Browse Source

增加请假接口

master
WendyYang 1 year ago
parent
commit
45edbe63ba
25 changed files with 1322 additions and 19 deletions
  1. +28
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/common/model/entity/KeyValDTO.java
  2. +25
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/expert/model/dto/ModifyApplyExtraInfoDTO.java
  3. +70
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/leave/controller/ExpertLeaveController.java
  4. +83
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/leave/entity/domain/ExpertLeave.java
  5. +49
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/leave/entity/domain/ExpertLeaveDetail.java
  6. +45
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/leave/entity/enumeration/LeaveStatusEnum.java
  7. +41
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/leave/entity/enumeration/LeaveTypeEnum.java
  8. +73
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/leave/entity/po/LeaveCreateReq.java
  9. +43
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/leave/entity/po/LeaveListByCreatorReq.java
  10. +46
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/leave/entity/vo/LeaveDetailVO.java
  11. +61
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/leave/entity/vo/LeaveListItemVO.java
  12. +514
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/leave/manage/LeaveManage.java
  13. +46
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/leave/mapper/ExpertLeaveDetailMapper.java
  14. +29
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/leave/mapper/ExpertLeaveDetailMapper.xml
  15. +16
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/leave/mapper/ExpertLeaveMapper.java
  16. +5
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/leave/mapper/ExpertLeaveMapper.xml
  17. +25
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/leave/service/IExpertLeaveDetailService.java
  18. +26
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/leave/service/IExpertLeaveService.java
  19. +35
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/leave/service/impl/ExpertLeaveDetailServiceImpl.java
  20. +41
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/leave/service/impl/ExpertLeaveServiceImpl.java
  21. +1
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/enumeration/ExpertAttendStatusEnum.java
  22. +1
    -2
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/MeetingListReq.java
  23. +3
    -3
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/MeetingByManagerVO.java
  24. +5
    -4
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/manage/MeetingManage.java
  25. +11
    -10
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/task/ExpertInviteTask.java

+ 28
- 0
pmapi/src/main/java/com/ningdatech/pmapi/common/model/entity/KeyValDTO.java View File

@@ -0,0 +1,28 @@
package com.ningdatech.pmapi.common.model.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
* <p>
* KeyValueDTO
* </p>
*
* @author WendyYang
* @since 16:40 2022/8/31
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class KeyValDTO<K, V> {

private K key;

private V value;

public static <K, V> KeyValDTO<K, V> of(K k, V v) {
return new KeyValDTO<>(k, v);
}

}

+ 25
- 0
pmapi/src/main/java/com/ningdatech/pmapi/expert/model/dto/ModifyApplyExtraInfoDTO.java View File

@@ -0,0 +1,25 @@
package com.ningdatech.pmapi.expert.model.dto;

import com.ningdatech.pmapi.common.model.FileBasicInfo;
import lombok.Data;

import javax.validation.constraints.NotBlank;
import java.util.List;

/**
* @author liuxinxin
* @date 2022/7/28 下午3:48
*/
@Data
public class ModifyApplyExtraInfoDTO {
/**
* 情况说明
*/
@NotBlank
private String factSheet;

/**
* 证明材料
*/
private List<FileBasicInfo> evidenceList;
}

+ 70
- 0
pmapi/src/main/java/com/ningdatech/pmapi/leave/controller/ExpertLeaveController.java View File

@@ -0,0 +1,70 @@
package com.ningdatech.pmapi.leave.controller;


import com.ningdatech.basic.exception.BizException;
import com.ningdatech.basic.model.IdVo;
import com.ningdatech.basic.model.PageVo;
import com.ningdatech.pmapi.leave.entity.po.LeaveCreateReq;
import com.ningdatech.pmapi.leave.entity.po.LeaveListByCreatorReq;
import com.ningdatech.pmapi.leave.entity.vo.LeaveDetailVO;
import com.ningdatech.pmapi.leave.entity.vo.LeaveListItemVO;
import com.ningdatech.pmapi.leave.manage.LeaveManage;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;

/**
* <p>
* 前端控制器
* </p>
*
* @author WendyYang
* @since 2022-08-11
*/
@Api(tags = "请假管理控制器")
@RestController
@AllArgsConstructor
@RequestMapping("/api/v1/leave/")
public class ExpertLeaveController {

private final LeaveManage leaveManage;

@ApiOperation("请假")
@PostMapping("/save")
public IdVo<Long> askForLeave(@Valid @RequestBody LeaveCreateReq po) {
return leaveManage.askForLeave(po);
}

@ApiOperation("请假详情")
@GetMapping(value = {"/detail/{leaveId}", "/detail/{auditId}/byApply"})
public LeaveDetailVO detail(@PathVariable(required = false) Long leaveId,
@PathVariable(required = false) Long auditId) {
if (ObjectUtils.allNull(leaveId, auditId)) {
throw BizException.wrap("请假ID或申请ID不能为空");
}
return leaveManage.leaveDetail(leaveId, auditId);
}

@ApiOperation("我的请假列表(发起人)")
@GetMapping("/leaveListByCreator")
public PageVo<LeaveListItemVO> leaveListByCreator(LeaveListByCreatorReq po) {
return leaveManage.leaveListByCreator(po);
}

@ApiOperation("销假")
@GetMapping("/endLeave")
public void endLeave(Long leaveId) {
leaveManage.endLeave(leaveId);
}

@ApiOperation("撤销")
@GetMapping("/cancel")
public void cancelLeave(Long leaveId) {
leaveManage.cancel(leaveId);
}

}

+ 83
- 0
pmapi/src/main/java/com/ningdatech/pmapi/leave/entity/domain/ExpertLeave.java View File

@@ -0,0 +1,83 @@
package com.ningdatech.pmapi.leave.entity.domain;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.io.Serializable;
import java.time.LocalDateTime;

/**
* <p>
* 请假记录表
* </p>
*
* @author WendyYang
* @since 2022-08-11
*/
@Data
@TableName("expert_leave")
@ApiModel(value = "ExpertLeave对象", description = "请假记录表")
public class ExpertLeave implements Serializable {

private static final long serialVersionUID = 1L;

@ApiModelProperty("主键")
@TableId(type = IdType.AUTO)
private Long id;

@ApiModelProperty("请假类型")
private Integer type;

@ApiModelProperty("请假说明")
private String remark;

@ApiModelProperty("附件ID")
private String attachment;

@ApiModelProperty("审核表ID")
private Long auditId;

@ApiModelProperty("请假人ID")
private Long leaveUserId;

@ApiModelProperty("创建人名称")
private String creator;

@ApiModelProperty("创建人")
private Long createBy;

@ApiModelProperty("创建时间")
private LocalDateTime createOn;

@ApiModelProperty("修改人")
private Long updateBy;

@ApiModelProperty("修改时间")
private LocalDateTime updateOn;

@ApiModelProperty("请假开始时间")
private LocalDateTime startTime;

@ApiModelProperty("请假结束时间")
private LocalDateTime endTime;

@ApiModelProperty("销假时间")
private LocalDateTime cancelTime;

@ApiModelProperty("固定时段模式")
private String fixedType;

private Long meetingId;

private Integer status;

public void setUpdateAndCreate(Long createBy, LocalDateTime createOn) {
this.updateBy = this.createBy = createBy;
this.updateOn = this.createOn = createOn;
}

}

+ 49
- 0
pmapi/src/main/java/com/ningdatech/pmapi/leave/entity/domain/ExpertLeaveDetail.java View File

@@ -0,0 +1,49 @@
package com.ningdatech.pmapi.leave.entity.domain;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;
import java.time.LocalDateTime;

/**
* <p>
* 专家请假详情表(维度:天)
* </p>
*
* @author WendyYang
* @since 2022-08-11
*/
@Data
@NoArgsConstructor
@TableName("expert_leave_detail")
@ApiModel(value = "ExpertLeaveDetail对象", description = "专家请假详情表(维度:天)")
public class ExpertLeaveDetail implements Serializable {

private static final long serialVersionUID = 1L;

@ApiModelProperty("主键")
@TableId(type = IdType.AUTO)
private Long id;

@ApiModelProperty("专家请假ID")
private Long expertLeaveId;

@ApiModelProperty("开始时间")
private LocalDateTime startTime;

@ApiModelProperty("结束时间")
private LocalDateTime endTime;

public ExpertLeaveDetail(Long expertLeaveId, LocalDateTime startTime, LocalDateTime endTime) {
this.expertLeaveId = expertLeaveId;
this.startTime = startTime;
this.endTime = endTime;
}

}

+ 45
- 0
pmapi/src/main/java/com/ningdatech/pmapi/leave/entity/enumeration/LeaveStatusEnum.java View File

@@ -0,0 +1,45 @@
package com.ningdatech.pmapi.leave.entity.enumeration;

import lombok.Getter;

import java.util.Arrays;

/**
* <p>
* LeaveStatus
* </p>
*
* @author WendyYang
* @since 09:39 2022/8/12
*/
@Getter
public enum LeaveStatusEnum {

/**
* 请假状态
*/
APPLYING(1, "审核中"),
PASSED(2, "审核通过"),
PASSED_END(3, "审核通过且销假"),
UN_PASSED(4, "审核不通过"),
CANCELED(5, "发起人撤销");
private final Integer code;
private final String name;

public boolean eq(Integer code) {
return this.getCode().equals(code);
}

LeaveStatusEnum(Integer code, String name) {
this.code = code;
this.name = name;
}

public static LeaveStatusEnum getByCode(Integer code) {
return Arrays.stream(values())
.filter(w -> w.getCode().equals(code))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("无效的请假状态"));
}

}

+ 41
- 0
pmapi/src/main/java/com/ningdatech/pmapi/leave/entity/enumeration/LeaveTypeEnum.java View File

@@ -0,0 +1,41 @@
package com.ningdatech.pmapi.leave.entity.enumeration;

import lombok.Getter;

import java.util.Arrays;

/**
* <p>
* LeaveType-请假类型
* </p>
*
* @author WendyYang
* @since 10:23 2022/8/11
*/
@Getter
public enum LeaveTypeEnum {

TEMPORARY(3, "临时请假"),
LONG_TERM(1, "长期请假"),
FIXED_TERM(2, "固定时段请假");

private final Integer code;
private final String name;

public boolean eq(Integer code) {
return this.getCode().equals(code);
}

LeaveTypeEnum(Integer code, String name) {
this.code = code;
this.name = name;
}

public static LeaveTypeEnum getByCode(Integer code) {
return Arrays.stream(values())
.filter(w -> w.getCode().equals(code))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("无效的请假类型"));
}

}

+ 73
- 0
pmapi/src/main/java/com/ningdatech/pmapi/leave/entity/po/LeaveCreateReq.java View File

@@ -0,0 +1,73 @@
package com.ningdatech.pmapi.leave.entity.po;

import com.ningdatech.basic.exception.BizException;
import com.ningdatech.pmapi.leave.entity.enumeration.LeaveTypeEnum;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.apache.commons.lang3.ObjectUtils;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
import java.util.List;

/**
* <p>
* LeaveCreatePo
* </p>
*
* @author WendyYang
* @since 10:30 2022/8/11
*/
@Data
@ApiModel("请假创建实体")
public class LeaveCreateReq {

@ApiModelProperty("请假类型:1 长期请假、2 固定时段请假、3 临时请假")
@NotNull(message = "请假类型不能为空")
private Integer type;

@ApiModelProperty("请假开始时间")
private LocalDateTime startTime;

@ApiModelProperty("请假结束时间")
private LocalDateTime endTime;

@ApiModelProperty("请假说明")
@NotBlank(message = "请假说明不能为空")
private String postscript;

@ApiModelProperty("附件ID")
private List<Long> attachments;

@ApiModelProperty("固定时段")
private List<Integer> fixedType;

@ApiModelProperty("会议ID")
private Long meetingId;

@ApiModelProperty("专家ID:管理员替专家请假时传参")
private Long expertId;

public void paramCheck(LeaveTypeEnum leaveTypeEnum) {
if (leaveTypeEnum.equals(LeaveTypeEnum.TEMPORARY)) {
if (this.getMeetingId() == null) {
throw new BizException("会议ID不能为空");
}
} else {
if (ObjectUtils.anyNull(this.getStartTime(), this.getEndTime())) {
throw new BizException("开始时间或结束时间不能为空");
}
if (!this.getStartTime().isBefore(this.getEndTime())) {
throw new BizException("无效的请假时间");
}
if (leaveTypeEnum.equals(LeaveTypeEnum.FIXED_TERM)) {
if (this.getFixedType() == null || this.getFixedType().isEmpty()) {
throw new BizException("固定时段不能为空");
}
}
}
}

}

+ 43
- 0
pmapi/src/main/java/com/ningdatech/pmapi/leave/entity/po/LeaveListByCreatorReq.java View File

@@ -0,0 +1,43 @@
package com.ningdatech.pmapi.leave.entity.po;

import com.ningdatech.basic.model.PagePo;
import com.ningdatech.pmapi.sms.constant.DatePattern;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.format.annotation.DateTimeFormat;

import java.time.LocalDateTime;

/**
* <p>
* LeaveListByCreatorPo
* </p>
*
* @author WendyYang
* @since 19:32 2022/8/11
*/
@Data
@ApiModel("我的请假列表参数实体")
@EqualsAndHashCode(callSuper = true)
public class LeaveListByCreatorReq extends PagePo {

@ApiModelProperty("开始时间")
@DateTimeFormat(pattern = DatePattern.YMD_HMS)
private LocalDateTime startTime;

@ApiModelProperty("结束时间")
@DateTimeFormat(pattern = DatePattern.YMD_HMS)
private LocalDateTime endTime;

@ApiModelProperty("请假类型")
private Integer leaveType;

@ApiModelProperty("审核状态")
private String auditStatus;

@ApiModelProperty("专家ID")
private Long expertId;

}

+ 46
- 0
pmapi/src/main/java/com/ningdatech/pmapi/leave/entity/vo/LeaveDetailVO.java View File

@@ -0,0 +1,46 @@
package com.ningdatech.pmapi.leave.entity.vo;

import com.ningdatech.file.entity.vo.result.AttachFileVo;
import lombok.Builder;
import lombok.Data;

import java.time.LocalDateTime;
import java.util.List;

/**
* <p>
* LeaveDetailVo
* </p>
*
* @author WendyYang
* @since 11:48 2022/8/12
*/
@Data
@Builder
public class LeaveDetailVO {

private Long id;

private String postscript;

private LocalDateTime startTime;

private LocalDateTime endTime;

private LocalDateTime actualEndTime;

private Integer status;

private List<AttachFileVo> attachments;

private Integer type;

private List<Integer> fixedType;

private LocalDateTime createOn;

private String createBy;

private Long auditId;

}

+ 61
- 0
pmapi/src/main/java/com/ningdatech/pmapi/leave/entity/vo/LeaveListItemVO.java View File

@@ -0,0 +1,61 @@
package com.ningdatech.pmapi.leave.entity.vo;

import com.ningdatech.file.entity.vo.result.AttachFileVo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.time.LocalDateTime;
import java.util.List;

/**
* <p>
* LeaveListItemByCreatorVo
* </p>
*
* @author WendyYang
* @since 20:53 2022/8/11
*/
@Data
@ApiModel("我得请假列表实体(发起人)")
public class LeaveListItemVO {

@ApiModelProperty("请假ID")
private Long leaveId;

@ApiModelProperty("审核ID")
private Long auditId;

@ApiModelProperty("开始时间")
private LocalDateTime startTime;

@ApiModelProperty("结束时间")
private LocalDateTime endTime;

@ApiModelProperty("实际结束时间")
private LocalDateTime actualEndTime;

@ApiModelProperty("请假说明")
private String postscript;

@ApiModelProperty("说明材料")
private List<AttachFileVo> attachments;

@ApiModelProperty("请假类型")
private Integer leaveType;

@ApiModelProperty("相关会议")
private String meetingName;

@ApiModelProperty("审核状态")
private String auditStatus;

@ApiModelProperty("状态")
private Integer status;

@ApiModelProperty("审核意见")
private String auditOption;

private LocalDateTime createOn;

}

+ 514
- 0
pmapi/src/main/java/com/ningdatech/pmapi/leave/manage/LeaveManage.java View File

@@ -0,0 +1,514 @@
package com.ningdatech.pmapi.leave.manage;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ningdatech.basic.exception.BizException;
import com.ningdatech.basic.model.IdVo;
import com.ningdatech.basic.model.PagePo;
import com.ningdatech.basic.model.PageVo;
import com.ningdatech.basic.util.CollUtils;
import com.ningdatech.file.entity.vo.result.AttachFileVo;
import com.ningdatech.file.service.FileService;
import com.ningdatech.pmapi.common.model.FileBasicInfo;
import com.ningdatech.pmapi.common.model.entity.KeyValDTO;
import com.ningdatech.pmapi.common.util.BizUtils;
import com.ningdatech.pmapi.common.util.StrUtils;
import com.ningdatech.pmapi.expert.constant.ExpertApplyStatusEnum;
import com.ningdatech.pmapi.expert.constant.ExpertApplyTypeEnum;
import com.ningdatech.pmapi.expert.entity.ExpertMetaApply;
import com.ningdatech.pmapi.expert.entity.ExpertUserFullInfo;
import com.ningdatech.pmapi.expert.model.dto.ModifyApplyExtraInfoDTO;
import com.ningdatech.pmapi.expert.service.IExpertMetaApplyService;
import com.ningdatech.pmapi.expert.service.IExpertUserFullInfoService;
import com.ningdatech.pmapi.leave.entity.domain.ExpertLeave;
import com.ningdatech.pmapi.leave.entity.domain.ExpertLeaveDetail;
import com.ningdatech.pmapi.leave.entity.enumeration.LeaveStatusEnum;
import com.ningdatech.pmapi.leave.entity.enumeration.LeaveTypeEnum;
import com.ningdatech.pmapi.leave.entity.po.LeaveCreateReq;
import com.ningdatech.pmapi.leave.entity.po.LeaveListByCreatorReq;
import com.ningdatech.pmapi.leave.entity.vo.LeaveDetailVO;
import com.ningdatech.pmapi.leave.entity.vo.LeaveListItemVO;
import com.ningdatech.pmapi.leave.service.IExpertLeaveDetailService;
import com.ningdatech.pmapi.leave.service.IExpertLeaveService;
import com.ningdatech.pmapi.meeting.entity.domain.Meeting;
import com.ningdatech.pmapi.meeting.entity.domain.MeetingExpert;
import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertAttendStatusEnum;
import com.ningdatech.pmapi.meeting.entity.enumeration.MeetingStatusEnum;
import com.ningdatech.pmapi.meeting.helper.YxtCallOrSmsHelper;
import com.ningdatech.pmapi.meeting.service.IMeetingExpertService;
import com.ningdatech.pmapi.meeting.service.IMeetingService;
import com.ningdatech.pmapi.meeting.task.ExpertInviteTask;
import com.ningdatech.pmapi.sms.utils.DateUtil;
import com.ningdatech.pmapi.user.service.IUserInfoService;
import com.ningdatech.pmapi.user.util.LoginUserUtil;
import lombok.AllArgsConstructor;
import org.springframework.aop.framework.AopContext;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;


/**
* <p>
* LeaveManage
* </p>
*
* @author WendyYang
* @since 10:38 2022/8/11
*/
@Component
@AllArgsConstructor
public class LeaveManage {

private final ExpertInviteTask inviteTask;
private final IMeetingService meetingService;
private final IMeetingExpertService meetingExpertService;
private final IExpertLeaveService leaveService;
private final IExpertLeaveDetailService leaveDetailService;
private final IExpertMetaApplyService metaApplyService;
private final FileService fileService;
private final IExpertUserFullInfoService userFullInfoService;
private final YxtCallOrSmsHelper yxtCallOrSmsHelper;
private final IUserInfoService userInfoService;

private static final int HOURS_BEFORE_MEETING = 12;

public void cancelMeetingExpertByLeave(Long meetingExpertId, ExpertAttendStatusEnum status) {
MeetingExpert update = buildMeetingExpertUpdate(meetingExpertId, status);
meetingExpertService.updateById(update);
}

private MeetingExpert buildMeetingExpertUpdate(Long meetingExpertId, ExpertAttendStatusEnum status) {
MeetingExpert expert = new MeetingExpert();
expert.setId(meetingExpertId);
expert.setStatus(status.getCode());
return expert;
}

private List<MeetingExpert> listMeetingExpert(Long expertId, List<KeyValDTO<LocalDateTime, LocalDateTime>> leaveTimes) {
// 先查询专家参与的所有会议
LambdaQueryWrapper<MeetingExpert> meetingExpertQuery = Wrappers.lambdaQuery(MeetingExpert.class)
.eq(MeetingExpert::getExpertId, expertId)
.in(MeetingExpert::getStatus, Arrays.asList(ExpertAttendStatusEnum.NOTICING.getCode(),
ExpertAttendStatusEnum.AGREED.getCode()));
List<MeetingExpert> meetingExperts = meetingExpertService.list(meetingExpertQuery);
if (meetingExperts.isEmpty()) {
return Collections.emptyList();
}
// 只需要查询待确认的或者是同意参加的
Map<Long, MeetingExpert> expertMap = meetingExperts.stream()
.collect(Collectors.groupingBy(MeetingExpert::getMeetingId,
Collectors.collectingAndThen(Collectors.toList(), w -> {
w.sort(Comparator.comparing(MeetingExpert::getUpdateOn).reversed());
return w.get(0);
})));
List<Long> meetingIds = CollUtils.fieldList(expertMap.values(), MeetingExpert::getMeetingId);
LocalDateTime startTime = leaveTimes.get(0).getKey(), endTime = leaveTimes.get(leaveTimes.size() - 1).getValue();
LambdaQueryWrapper<Meeting> meetingQuery = Wrappers.lambdaQuery(Meeting.class)
.in(Meeting::getId, meetingIds)
.ne(Meeting::getStatus, MeetingStatusEnum.CANCELED.getCode())
.and(w -> w.between(Meeting::getStartTime, startTime, endTime)
.or(or -> or.between(Meeting::getEndTime, startTime, endTime)));
List<Meeting> meetingList = meetingService.list(meetingQuery);
List<Long> matchMeetingIds = meetingList.stream()
.filter(w -> leaveTimes.stream()
.anyMatch(tp -> DateUtil.intersect(w.getStartTime(), w.getEndTime(), tp.getKey(), tp.getValue())))
.map(Meeting::getId).collect(Collectors.toList());
if (matchMeetingIds.isEmpty()) {
return Collections.emptyList();
}
return matchMeetingIds.stream().map(expertMap::get).collect(Collectors.toList());
}

@Transactional(rollbackFor = Exception.class)
public IdVo<Long> askForLeave(LeaveCreateReq po) {
Long leaveUserId, applyUserId = Objects.requireNonNull(LoginUserUtil.getUserId());
leaveUserId = po.getExpertId() != null ? po.getExpertId() : applyUserId;
boolean leaveForSelf = leaveUserId.equals(applyUserId);
if (!leaveForSelf) {
Assert.isFalse(leaveService.existsToBeReviewed(leaveUserId), "该专家存在待审核请假申请,请及时审核");
}
LeaveTypeEnum type = LeaveTypeEnum.getByCode(po.getType());
// 校验参数是否合法
po.paramCheck(type);
ExpertLeave leave = new ExpertLeave();
if (po.getAttachments() != null) {
leave.setAttachment(CollUtils.joinByComma(po.getAttachments()));
}
LocalDateTime now = LocalDateTime.now();
leave.setType(po.getType());
leave.setRemark(po.getPostscript());
leave.setLeaveUserId(leaveUserId);
leave.setUpdateAndCreate(applyUserId, now);
List<KeyValDTO<LocalDateTime, LocalDateTime>> leaveDetailTimes = new ArrayList<>();
if (type.equals(LeaveTypeEnum.FIXED_TERM) || type.equals(LeaveTypeEnum.LONG_TERM)) {
boolean fixedTerm = CollUtil.isNotEmpty(po.getFixedType());
if (fixedTerm) {
// 固定时段请假
leave.setFixedType(CollUtils.joinByComma(po.getFixedType()));
}
LocalDateTime tempStart = po.getStartTime();
while (!tempStart.isAfter(po.getEndTime())) {
boolean add = true;
if (fixedTerm) {
int weekday = tempStart.getDayOfWeek().getValue();
add = po.getFixedType().contains(weekday);
}
if (add) {
LocalDateTime tempEnd = tempStart.toLocalDate().atTime(DateUtil.LOCAL_TIME_3D);
leaveDetailTimes.add(KeyValDTO.of(tempStart, DateUtil.min(tempEnd, po.getEndTime())));
}
tempStart = tempStart.plusDays(1).toLocalDate().atStartOfDay();
}
if (leaveDetailTimes.isEmpty()) {
throw BizException.wrap("请假时间无效");
}
if (leaveDetailService.existsLeaveByUserIdAndTime(leaveUserId, leaveDetailTimes)) {
throw BizException.wrap("该请假时段内已存在请假");
}
List<MeetingExpert> meetingExperts = listMeetingExpert(leaveUserId, leaveDetailTimes);
if (!meetingExperts.isEmpty()) {
if (meetingExperts.stream().anyMatch(w -> ExpertAttendStatusEnum.AGREED.eq(w.getStatus()))) {
throw BizException.wrap("有待参加会议,请先处理完会议,再发起请假");
}
List<MeetingExpert> expertsUpdate = meetingExperts.stream()
.map(w -> buildMeetingExpertUpdate(w.getExpertId(), ExpertAttendStatusEnum.ON_LEAVE))
.collect(Collectors.toList());
meetingExpertService.updateBatchById(expertsUpdate);
}
leave.setStatus(LeaveStatusEnum.APPLYING.getCode());
} else if (type.equals(LeaveTypeEnum.TEMPORARY)) {
// 临时请假
Meeting meeting = meetingService.getById(po.getMeetingId());
if (meeting.getStatus().equals(MeetingStatusEnum.CANCELED.getCode())) {
throw BizException.wrap("该会议已取消");
}
po.setStartTime(meeting.getStartTime());
po.setEndTime(meeting.getEndTime());
if (now.plusHours(HOURS_BEFORE_MEETING).isAfter(po.getStartTime())) {
throw BizException.wrap("会议临期" + HOURS_BEFORE_MEETING + "小时内,不能在线请假,请电话该主题事务的联系人。");
}
MeetingExpert expert = meetingExpertService.getByMeetingIdAndExpertId(po.getMeetingId(), leaveUserId);
if (!expert.getStatus().equals(ExpertAttendStatusEnum.AGREED.getCode())) {
// 非确认参加状态无法临时请假
throw BizException.wrap("未同意参加该会议,无法临时请假");
}
LocalDateTime tempStart = po.getStartTime();
while (!tempStart.isAfter(po.getEndTime())) {
LocalDateTime tempEnd = tempStart.toLocalDate().atTime(DateUtil.LOCAL_TIME_3D);
leaveDetailTimes.add(KeyValDTO.of(tempStart, DateUtil.min(tempEnd, po.getEndTime())));
tempStart = tempStart.plusDays(1).toLocalDate().atStartOfDay();
}
LeaveManage proxy = (LeaveManage) AopContext.currentProxy();
proxy.cancelMeetingExpertByLeave(expert.getId(), ExpertAttendStatusEnum.ON_LEAVE);
inviteTask.notifyInviteTask(meeting.getId(), Boolean.FALSE);
// 临时请假无需审核
leave.setAuditId(0L);
leave.setStatus(LeaveStatusEnum.PASSED.getCode());
// 临时请假需通知专家管理进行专家补抽
}
if (type != LeaveTypeEnum.TEMPORARY) {
ExpertMetaApply apply = getExpertMetaApply(po, leaveUserId, applyUserId, now);
leave.setAuditId(apply.getId());
if (!leaveForSelf) {
leave.setStatus(LeaveStatusEnum.PASSED.getCode());
}
}
leave.setStartTime(po.getStartTime());
leave.setEndTime(po.getEndTime());
leave.setCancelTime(po.getEndTime());
leaveService.save(leave);
List<ExpertLeaveDetail> detailList = CollUtils.convert(leaveDetailTimes, w -> new ExpertLeaveDetail(leave.getId(), w.getKey(), w.getValue()));
leaveDetailService.saveBatch(detailList);
return IdVo.of(leave.getId());
}

private ExpertMetaApply getExpertMetaApply(LeaveCreateReq po, Long leaveUserId, Long applyUserId, LocalDateTime now) {
// 非临时请假需要审批
ExpertMetaApply apply = new ExpertMetaApply();
apply.setCreateOn(now);
apply.setUpdateOn(now);
apply.setApplyType(ExpertApplyTypeEnum.LONG_TERM_LEAVE.getKey());
if (applyUserId.equals(leaveUserId)) {
apply.setApplyStatus(ExpertApplyStatusEnum.PENDING_REVIEW.getKey());
} else {
// 专家管理员发起请假自动通过
apply.setReviewTime(now);
apply.setApprover(LoginUserUtil.getUsername());
apply.setApproverUserId(applyUserId);
apply.setApplyStatus(ExpertApplyStatusEnum.PASSED.getKey());
}
apply.setUserId(leaveUserId);
apply.setApplicantId(applyUserId);
ExpertUserFullInfo userInfo = userFullInfoService.getByUserId(apply.getUserId());
apply.setRegionCode(userInfo.getRegionCode());
apply.setRegionLevel(userInfo.getRegionLevel());
ModifyApplyExtraInfoDTO extraInfo = new ModifyApplyExtraInfoDTO();
extraInfo.setFactSheet(po.getPostscript());
if (po.getAttachments() != null) {
List<FileBasicInfo> infoList = CollUtils.convert(po.getAttachments(), w -> {
FileBasicInfo info = new FileBasicInfo();
info.setFileId(w);
return info;
});
extraInfo.setEvidenceList(infoList);
}
apply.setExtraMaterial(JSONUtil.toJsonStr(extraInfo));
metaApplyService.save(apply);
return apply;
}

public PageVo<LeaveListItemVO> leaveListByCreator(LeaveListByCreatorReq po) {
if (po.getExpertId() == null) {
po.setExpertId(LoginUserUtil.getUserId());
}
LambdaQueryWrapper<ExpertLeave> query = Wrappers.lambdaQuery(ExpertLeave.class)
.orderByDesc(ExpertLeave::getCreateOn);
if (po.getLeaveType() != null && po.getLeaveType().equals(LeaveTypeEnum.TEMPORARY.getCode())) {
// 临时请假
if (po.getAuditStatus() != null && !po.getAuditStatus().equals(ExpertApplyStatusEnum.PASSED.getKey())) {
return PageVo.empty();
}
query.eq(ExpertLeave::getAuditId, 0L);
query.eq(ExpertLeave::getLeaveUserId, po.getExpertId());
} else {
if (StrUtils.isNotBlank(po.getAuditStatus())) {
LambdaQueryWrapper<ExpertMetaApply> queryApply = Wrappers.lambdaQuery(ExpertMetaApply.class)
.select(ExpertMetaApply::getId)
.eq(ExpertMetaApply::getUserId, po.getExpertId())
.eq(ExpertMetaApply::getApplyStatus, po.getAuditStatus());
List<ExpertMetaApply> applyList = metaApplyService.list(queryApply);
List<Long> auditIds = CollUtils.fieldList(applyList, ExpertMetaApply::getId);
if (po.getAuditStatus().equals(ExpertApplyStatusEnum.PASSED.getKey())) {
query.eq(ExpertLeave::getLeaveUserId, po.getExpertId());
auditIds.add(0L);
}
if (auditIds.isEmpty()) {
return PageVo.empty();
}
query.in(ExpertLeave::getAuditId, auditIds);
} else {
query.eq(ExpertLeave::getLeaveUserId, po.getExpertId());
}
query.eq(po.getLeaveType() != null, ExpertLeave::getType, po.getLeaveType());
}
if (po.getStartTime() != null && po.getEndTime() != null) {
query.and(wrapper -> wrapper.between(ExpertLeave::getStartTime, po.getStartTime(), po.getEndTime())
.or(or -> or.between(ExpertLeave::getEndTime, po.getStartTime(), po.getEndTime())));
}
Page<ExpertLeave> page = leaveService.page(po.page(), query);
if (page.getTotal() == 0) {
return PageVo.empty();
}
List<Long> meetingIds = new ArrayList<>(), auditIds = new ArrayList<>(), fileIds = new ArrayList<>();
page.getRecords().forEach(w -> {
if (w.getMeetingId() != 0) {
meetingIds.add(w.getMeetingId());
}
if (w.getAuditId() != 0) {
auditIds.add(w.getAuditId());
}
if (!w.getAttachment().isEmpty()) {
List<Long> tempFileIds = BizUtils.splitToLong(w.getAttachment());
fileIds.addAll(tempFileIds);
}
});
Map<Long, Meeting> meetingMap = new HashMap<>(16);
if (!meetingIds.isEmpty()) {
meetingMap.putAll(CollUtils.listToMap(meetingService.listByIds(meetingIds), Meeting::getId));
}
Map<Long, ExpertMetaApply> applyMap = new HashMap<>(16);
if (!auditIds.isEmpty()) {
applyMap.putAll(CollUtils.listToMap(metaApplyService.listByIds(auditIds), ExpertMetaApply::getId));
}
Map<Long, AttachFileVo> fileMap = new HashMap<>(fileIds.size());
if (!fileIds.isEmpty()) {
fileMap.putAll(CollUtils.listToMap(fileService.getByIds(fileIds), AttachFileVo::getFileId));
}
List<LeaveListItemVO> dataList = CollUtils.convert(page.getRecords(), w -> {
LeaveListItemVO item = new LeaveListItemVO();
item.setLeaveType(w.getType());
item.setPostscript(w.getRemark());
item.setAuditId(w.getAuditId());
item.setLeaveId(w.getId());
item.setStartTime(w.getStartTime());
item.setEndTime(w.getEndTime());
ExpertMetaApply apply = applyMap.get(w.getAuditId());
item.setAuditOption(apply != null ? apply.getAuditOpinion() : StrUtil.EMPTY);
item.setStatus(w.getStatus());
item.setAuditStatus(apply != null ? apply.getApplyStatus() : ExpertApplyStatusEnum.PASSED.getKey());
item.setActualEndTime(w.getCancelTime());
Meeting meeting = meetingMap.get(w.getMeetingId());
item.setMeetingName(meeting != null ? meeting.getName() : StrUtil.EMPTY);
item.setAttachments(new ArrayList<>());
if (!w.getAttachment().isEmpty()) {
Arrays.stream(w.getAttachment().split(",")).forEach(fileId -> item.getAttachments().add(fileMap.get(Long.parseLong(fileId))));
}
return item;
});
return PageVo.of(dataList, page.getTotal());
}

public void endLeave(Long leaveId) {
ExpertLeave leave = leaveService.getById(leaveId);
if (LeaveTypeEnum.TEMPORARY.eq(leave.getType())) {
throw BizException.wrap("临时请假不支持销假");
}
LeaveStatusEnum status = LeaveStatusEnum.getByCode(leave.getStatus());
if (!status.equals(LeaveStatusEnum.PASSED) || LocalDateTime.now().isAfter(leave.getEndTime())) {
throw BizException.wrap("该请假不支持销假");
}
LocalDateTime actualEndTime = LocalDateTime.now();
LambdaUpdateWrapper<ExpertLeave> updater = Wrappers.lambdaUpdate(ExpertLeave.class)
.set(ExpertLeave::getCancelTime, actualEndTime)
.set(ExpertLeave::getUpdateOn, actualEndTime)
.set(ExpertLeave::getUpdateBy, LoginUserUtil.getUserId())
.set(ExpertLeave::getStatus, LeaveStatusEnum.PASSED_END.getCode())
.eq(ExpertLeave::getId, leave.getId());
leaveService.update(updater);
LambdaQueryWrapper<ExpertLeaveDetail> deleter = Wrappers.lambdaQuery(ExpertLeaveDetail.class)
.ge(ExpertLeaveDetail::getStartTime, actualEndTime.toLocalDate())
.eq(ExpertLeaveDetail::getExpertLeaveId, leaveId);
leaveDetailService.remove(deleter);
}

public void cancel(Long leaveId) {
ExpertLeave leave = leaveService.getById(leaveId);
if (LeaveTypeEnum.TEMPORARY.eq(leave.getType())) {
throw BizException.wrap("临时请假不支持撤销");
}
if (LeaveStatusEnum.APPLYING.eq(leave.getStatus())) {
throw BizException.wrap("该请假不支持撤销");
}
LocalDateTime actualEndTime = LocalDateTime.now();
// 修改请假状态
LambdaUpdateWrapper<ExpertLeave> updater = Wrappers.lambdaUpdate(ExpertLeave.class)
.set(ExpertLeave::getUpdateOn, actualEndTime)
.set(ExpertLeave::getUpdateBy, LoginUserUtil.getUserId())
.set(ExpertLeave::getStatus, LeaveStatusEnum.CANCELED.getCode())
.eq(ExpertLeave::getId, leave.getId());
leaveService.update(updater);
// 删除请假详情
LambdaQueryWrapper<ExpertLeaveDetail> deleter = Wrappers.lambdaQuery(ExpertLeaveDetail.class)
.eq(ExpertLeaveDetail::getExpertLeaveId, leaveId);
leaveDetailService.remove(deleter);
// 修改审核表
LambdaUpdateWrapper<ExpertMetaApply> updaterApply = Wrappers.lambdaUpdate(ExpertMetaApply.class)
.set(ExpertMetaApply::getApplyStatus, ExpertApplyStatusEnum.REVOKED.getKey())
.set(ExpertMetaApply::getUpdateOn, LocalDateTime.now())
.set(ExpertMetaApply::getAuditOpinion, "发起人撤销")
.eq(ExpertMetaApply::getId, leave.getAuditId());
metaApplyService.update(updaterApply);
}

public LeaveDetailVO leaveDetail(Long leaveId, Long auditId) {
ExpertLeave leave;
if (leaveId != null) {
leave = leaveService.getById(leaveId);
} else {
leave = leaveService.getByAuditId(auditId);
}
LeaveDetailVO detail = LeaveDetailVO.builder()
.createOn(leave.getCreateOn())
.createBy(leave.getCreator())
.postscript(leave.getRemark())
.id(leave.getId())
.status(leave.getStatus())
.startTime(leave.getStartTime())
.endTime(leave.getEndTime())
.attachments(new ArrayList<>())
.type(leave.getType())
.actualEndTime(leave.getCancelTime())
.auditId(leave.getAuditId())
.build();
if (LeaveTypeEnum.FIXED_TERM.eq(leave.getType())) {
detail.setFixedType(BizUtils.splitToNum(leave.getFixedType(), Integer.class));
}
if (StrUtils.isNotBlank(leave.getAttachment())) {
List<Long> fileIds = BizUtils.splitToLong(leave.getAttachment());
List<AttachFileVo> files = fileService.getByIds(fileIds);
detail.getAttachments().addAll(files);
}
return detail;
}

public List<Long> listLeaveUserIdByTime(LocalDateTime start, LocalDateTime end) {
LambdaQueryWrapper<ExpertLeaveDetail> leaveDetailQuery = Wrappers.lambdaQuery(ExpertLeaveDetail.class)
.select(ExpertLeaveDetail::getExpertLeaveId)
.and(wrapper -> wrapper.between(ExpertLeaveDetail::getStartTime, start, end)
.or(wrapper1 -> wrapper1.between(ExpertLeaveDetail::getEndTime, start, end)));
List<ExpertLeaveDetail> detailList = leaveDetailService.list(leaveDetailQuery);
if (detailList.isEmpty()) {
return new ArrayList<>();
}
List<Long> leaveIdList = CollUtils.fieldList(detailList, ExpertLeaveDetail::getExpertLeaveId);
LambdaQueryWrapper<ExpertLeave> leaveQuery = Wrappers.lambdaQuery(ExpertLeave.class)
.select(ExpertLeave::getCreateBy)
.in(ExpertLeave::getId, leaveIdList)
.in(ExpertLeave::getStatus,
LeaveStatusEnum.APPLYING.getCode(),
LeaveStatusEnum.PASSED.getCode(),
LeaveStatusEnum.PASSED_END.getCode());
List<ExpertLeave> leaveList = leaveService.list(leaveQuery);
return CollUtils.fieldList(leaveList, ExpertLeave::getCreateBy);
}

@Transactional(rollbackFor = Exception.class)
public void leaveAuditCallback(boolean status, Long applyId) {
LambdaUpdateWrapper<ExpertLeave> update = Wrappers.lambdaUpdate(ExpertLeave.class)
.set(ExpertLeave::getUpdateBy, LoginUserUtil.getUserId())
.set(ExpertLeave::getUpdateOn, LocalDateTime.now())
.eq(ExpertLeave::getAuditId, applyId);
ExpertLeave leave = leaveService.getByAuditId(applyId);
ExpertUserFullInfo leaveUser = userFullInfoService.getByUserId(leave.getLeaveUserId());
if (status) {
update.set(ExpertLeave::getStatus, LeaveStatusEnum.PASSED.getCode());
} else {
ExpertMetaApply metaApply = metaApplyService.getById(applyId);
update.set(ExpertLeave::getStatus, LeaveStatusEnum.UN_PASSED.getCode());
LambdaQueryWrapper<ExpertLeaveDetail> delete = Wrappers.lambdaQuery(ExpertLeaveDetail.class)
.eq(ExpertLeaveDetail::getExpertLeaveId, leave.getId());
leaveDetailService.remove(delete);
}
leaveService.update(update);
}

public Map<LocalDate, Set<Long>> listLeaveIdByDate(LocalDate startDate, LocalDate endDate, Long expertId) {
List<ExpertLeaveDetail> leaveDetails = leaveDetailService.listByDateAndLeaveUserId(startDate, endDate, expertId);
return leaveDetails.stream().collect(Collectors.groupingBy(w -> w.getStartTime().toLocalDate(),
Collectors.mapping(ExpertLeaveDetail::getExpertLeaveId, Collectors.toSet())));
}

public PageVo<LeaveListItemVO> listLeaveApplyingAndCouldBeStop(PagePo po) {
LambdaQueryWrapper<ExpertLeave> query = Wrappers.lambdaQuery(ExpertLeave.class)
.eq(ExpertLeave::getLeaveUserId, LoginUserUtil.getUserId())
.and(wrapper -> wrapper.eq(ExpertLeave::getStatus, LeaveStatusEnum.APPLYING.getCode())
.or(wrapper1 -> wrapper1.eq(ExpertLeave::getStatus, LeaveStatusEnum.PASSED.getCode())
.gt(ExpertLeave::getCancelTime, LocalDateTime.now())));
Page<ExpertLeave> page = leaveService.page(new Page<>(po.getPageNumber(), po.getPageSize()), query);
if (page.getTotal() == 0) {
return PageVo.empty();
}
List<LeaveListItemVO> result = CollUtils.convert(page.getRecords(), w -> {
LeaveListItemVO item = new LeaveListItemVO();
item.setStartTime(w.getStartTime());
item.setEndTime(w.getEndTime());
item.setStatus(w.getStatus());
item.setLeaveType(w.getType());
item.setLeaveId(w.getId());
return item;
});
return PageVo.of(result, page.getTotal());
}

}

+ 46
- 0
pmapi/src/main/java/com/ningdatech/pmapi/leave/mapper/ExpertLeaveDetailMapper.java View File

@@ -0,0 +1,46 @@
package com.ningdatech.pmapi.leave.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ningdatech.pmapi.common.model.entity.KeyValDTO;
import com.ningdatech.pmapi.leave.entity.domain.ExpertLeaveDetail;
import org.apache.ibatis.annotations.Param;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;

/**
* <p>
* 专家请假详情表(维度:天) Mapper 接口
* </p>
*
* @author WendyYang
* @since 2022-08-11
*/
public interface ExpertLeaveDetailMapper extends BaseMapper<ExpertLeaveDetail> {

/**
* 查询时间段之内的请假信息(请假状态:已通过、销假)
*
* @param leaveUserId 请假人ID
* @param startDate 开始时间
* @param endDate 结束时间
* @return 请假详情
* @author WendyYang
**/
List<ExpertLeaveDetail> selectLeaveIdByDateAndExpertId(@Param("leaveUserId") Long leaveUserId,
@Param("startDate") LocalDate startDate,
@Param("endDate") LocalDate endDate);

/**
* 查询用户在指定时间之内是否有请假
*
* @param leaveUserId 用户ID
* @param times 时间集合
* @return 请假天数
* @author WendyYang
**/
Integer existsLeaveByLeaveUserIdAndTime(@Param("leaveUserId") Long leaveUserId,
@Param("times") List<KeyValDTO<LocalDateTime, LocalDateTime>> times);

}

+ 29
- 0
pmapi/src/main/java/com/ningdatech/pmapi/leave/mapper/ExpertLeaveDetailMapper.xml View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ningdatech.pmapi.leave.mapper.ExpertLeaveDetailMapper">

<select id="selectLeaveIdByDateAndExpertId"
resultType="com.ningdatech.pmapi.leave.entity.domain.ExpertLeaveDetail">
select eld.expert_leave_id, eld.start_time, eld.end_time
from expert_leave el
inner join expert_leave_detail eld on el.id = eld.expert_leave_id
where leave_user_id = #{leaveUserId}
and status in (2, 3)
and ((eld.start_time &gt;= #{startDate} and eld.start_time &lt; #{endDate})
or (eld.end_time &gt;= #{startDate} and eld.end_time &lt; #{endDate}))
</select>

<select id="existsLeaveByLeaveUserIdAndTime" resultType="integer">
select 1
from expert_leave el
inner join expert_leave_detail eld on el.id = eld.expert_leave_id
where leave_user_id = #{leaveUserId}
and status not in (4, 5)
<foreach collection="times" item="item" open="and (" close=")" separator="or">
((eld.start_time &gt;= #{item.key} and eld.start_time &lt; #{item.value})
or (eld.end_time &gt;= #{item.key} and eld.end_time &lt; #{item.value}))
</foreach>
limit 1
</select>

</mapper>

+ 16
- 0
pmapi/src/main/java/com/ningdatech/pmapi/leave/mapper/ExpertLeaveMapper.java View File

@@ -0,0 +1,16 @@
package com.ningdatech.pmapi.leave.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ningdatech.pmapi.leave.entity.domain.ExpertLeave;

/**
* <p>
* Mapper 接口
* </p>
*
* @author WendyYang
* @since 2022-08-11
*/
public interface ExpertLeaveMapper extends BaseMapper<ExpertLeave> {

}

+ 5
- 0
pmapi/src/main/java/com/ningdatech/pmapi/leave/mapper/ExpertLeaveMapper.xml View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ningdatech.pmapi.leave.mapper.ExpertLeaveMapper">

</mapper>

+ 25
- 0
pmapi/src/main/java/com/ningdatech/pmapi/leave/service/IExpertLeaveDetailService.java View File

@@ -0,0 +1,25 @@
package com.ningdatech.pmapi.leave.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.ningdatech.pmapi.common.model.entity.KeyValDTO;
import com.ningdatech.pmapi.leave.entity.domain.ExpertLeaveDetail;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;

/**
* <p>
* 专家请假详情表(维度:天) 服务类
* </p>
*
* @author WendyYang
* @since 2022-08-11
*/
public interface IExpertLeaveDetailService extends IService<ExpertLeaveDetail> {

List<ExpertLeaveDetail> listByDateAndLeaveUserId(LocalDate startDate, LocalDate endDate, Long leaveUserId);

boolean existsLeaveByUserIdAndTime(Long userId, List<KeyValDTO<LocalDateTime, LocalDateTime>> times);

}

+ 26
- 0
pmapi/src/main/java/com/ningdatech/pmapi/leave/service/IExpertLeaveService.java View File

@@ -0,0 +1,26 @@
package com.ningdatech.pmapi.leave.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.ningdatech.pmapi.leave.entity.domain.ExpertLeave;

/**
* <p>
* 服务类
* </p>
*
* @author WendyYang
* @since 2022-08-11
*/
public interface IExpertLeaveService extends IService<ExpertLeave> {

ExpertLeave getByAuditId(Long auditId);

/**
* 是否存在待审核请假记录
*
* @param expertId 专家ID
* @return boolean
*/
boolean existsToBeReviewed(Long expertId);

}

+ 35
- 0
pmapi/src/main/java/com/ningdatech/pmapi/leave/service/impl/ExpertLeaveDetailServiceImpl.java View File

@@ -0,0 +1,35 @@
package com.ningdatech.pmapi.leave.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ningdatech.pmapi.common.model.entity.KeyValDTO;
import com.ningdatech.pmapi.leave.entity.domain.ExpertLeaveDetail;
import com.ningdatech.pmapi.leave.mapper.ExpertLeaveDetailMapper;
import com.ningdatech.pmapi.leave.service.IExpertLeaveDetailService;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;

/**
* <p>
* 专家请假详情表(维度:天) 服务实现类
* </p>
*
* @author WendyYang
* @since 2022-08-11
*/
@Service
public class ExpertLeaveDetailServiceImpl extends ServiceImpl<ExpertLeaveDetailMapper, ExpertLeaveDetail> implements IExpertLeaveDetailService {

@Override
public List<ExpertLeaveDetail> listByDateAndLeaveUserId(LocalDate startDate, LocalDate endDate, Long leaveUserId) {
return baseMapper.selectLeaveIdByDateAndExpertId(leaveUserId, startDate, endDate.plusDays(1));
}

@Override
public boolean existsLeaveByUserIdAndTime(Long userId, List<KeyValDTO<LocalDateTime, LocalDateTime>> times) {
return baseMapper.existsLeaveByLeaveUserIdAndTime(userId, times) != null;
}

}

+ 41
- 0
pmapi/src/main/java/com/ningdatech/pmapi/leave/service/impl/ExpertLeaveServiceImpl.java View File

@@ -0,0 +1,41 @@
package com.ningdatech.pmapi.leave.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ningdatech.pmapi.leave.entity.domain.ExpertLeave;
import com.ningdatech.pmapi.leave.entity.enumeration.LeaveStatusEnum;
import com.ningdatech.pmapi.leave.mapper.ExpertLeaveMapper;
import com.ningdatech.pmapi.leave.service.IExpertLeaveService;
import org.springframework.stereotype.Service;

/**
* <p>
* 服务实现类
* </p>
*
* @author WendyYang
* @since 2022-08-11
*/
@Service
public class ExpertLeaveServiceImpl extends ServiceImpl<ExpertLeaveMapper, ExpertLeave> implements IExpertLeaveService {

@Override
public ExpertLeave getByAuditId(Long auditId) {
if (auditId == 0) {
return null;
}
LambdaQueryWrapper<ExpertLeave> query = Wrappers.lambdaQuery(ExpertLeave.class)
.eq(ExpertLeave::getAuditId, auditId);
return getOne(query);
}

@Override
public boolean existsToBeReviewed(Long expertId) {
LambdaQueryWrapper<ExpertLeave> query = Wrappers.lambdaQuery(ExpertLeave.class)
.eq(ExpertLeave::getLeaveUserId, expertId)
.eq(ExpertLeave::getStatus, LeaveStatusEnum.APPLYING.getCode());
return baseMapper.exists(query);
}

}

+ 1
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/enumeration/ExpertAttendStatusEnum.java View File

@@ -21,6 +21,7 @@ public enum ExpertAttendStatusEnum {
UNANSWERED("未应答", 1), UNANSWERED("未应答", 1),
AGREED("同意参加", 3), AGREED("同意参加", 3),
REFUSED("拒绝参加", 4), REFUSED("拒绝参加", 4),
ON_LEAVE("已请假", 5),
RELEASED("已释放", 7); RELEASED("已释放", 7);


private final String value; private final String value;


+ 1
- 2
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/MeetingListReq.java View File

@@ -33,8 +33,7 @@ public class MeetingListReq extends PagePo {
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime endTime; private LocalDateTime endTime;


@ApiModelProperty("事务状态:1 未完成、2 已完成、3 已取消\n" +
"专家参与状态:1 待参加、2 已参加、3 已请假")
@ApiModelProperty("事务状态")
private Integer status; private Integer status;


@ApiModelProperty("会议类型") @ApiModelProperty("会议类型")


+ 3
- 3
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/MeetingByManagerVO.java View File

@@ -49,9 +49,6 @@ public class MeetingByManagerVO {
@ApiModelProperty("名单确认状态") @ApiModelProperty("名单确认状态")
private Boolean confirmedRoster; private Boolean confirmedRoster;


@ApiModelProperty("会议参加状态:1 待参加、2 已参加、3 已请假")
private Integer attendStatus;

@ApiModelProperty("会议名称") @ApiModelProperty("会议名称")
private String meetingName; private String meetingName;


@@ -61,4 +58,7 @@ public class MeetingByManagerVO {
@ApiModelProperty("会议类型名称") @ApiModelProperty("会议类型名称")
private String meetingTypeName; private String meetingTypeName;


@ApiModelProperty("专家状态")
private Integer expertStatus;

} }

+ 5
- 4
pmapi/src/main/java/com/ningdatech/pmapi/meeting/manage/MeetingManage.java View File

@@ -284,15 +284,16 @@ public class MeetingManage {
**/ **/
public PageVo<MeetingByManagerVO> meetingListByExpert(MeetingListReq req) { public PageVo<MeetingByManagerVO> meetingListByExpert(MeetingListReq req) {
Long expertId = req.getExpertId() != null ? req.getExpertId() : LoginUserUtil.getUserId(); Long expertId = req.getExpertId() != null ? req.getExpertId() : LoginUserUtil.getUserId();
List<MeetingAndAttendStatusDTO> expertDtoList = meetingExpertService.listByExpertIdAndStatus(expertId, req.getStatus(), null);
if (expertDtoList.isEmpty()) {
List<MeetingAndAttendStatusDTO> meetings = meetingExpertService.listByExpertIdAndStatus(expertId, null, null);
if (meetings.isEmpty()) {
return PageVo.empty(); return PageVo.empty();
} }
Map<Long, MeetingAndAttendStatusDTO> mapByMeetingId = new HashMap<>(16); Map<Long, MeetingAndAttendStatusDTO> mapByMeetingId = new HashMap<>(16);
expertDtoList.forEach(w -> mapByMeetingId.put(w.getMeetingId(), w));
meetings.forEach(w -> mapByMeetingId.put(w.getMeetingId(), w));
LambdaQueryWrapper<Meeting> query = new LambdaQueryWrapper<Meeting>() LambdaQueryWrapper<Meeting> query = new LambdaQueryWrapper<Meeting>()
.orderByDesc(Meeting::getCreateOn) .orderByDesc(Meeting::getCreateOn)
.in(Meeting::getId, mapByMeetingId.keySet()) .in(Meeting::getId, mapByMeetingId.keySet())
.eq(Meeting::getConfirmedRoster, Boolean.TRUE)
.ne(Meeting::getStatus, MeetingStatusEnum.CANCELED.getCode()); .ne(Meeting::getStatus, MeetingStatusEnum.CANCELED.getCode());
if (req.getExpertId() == null) { if (req.getExpertId() == null) {
meetingManageHelper.buildMeetingQuery(query, req); meetingManageHelper.buildMeetingQuery(query, req);
@@ -305,7 +306,7 @@ public class MeetingManage {
page.getRecords().forEach(meeting -> { page.getRecords().forEach(meeting -> {
MeetingByManagerVO item = meetingManageHelper.buildByMeeting(meeting); MeetingByManagerVO item = meetingManageHelper.buildByMeeting(meeting);
MeetingAndAttendStatusDTO info = mapByMeetingId.get(meeting.getId()); MeetingAndAttendStatusDTO info = mapByMeetingId.get(meeting.getId());
item.setAttendStatus(meetingManageHelper.getExpertAttendStatus(info));
item.setExpertStatus(info.getStatus());
result.getRecords().add(item); result.getRecords().add(item);
}); });
return result; return result;


+ 11
- 10
pmapi/src/main/java/com/ningdatech/pmapi/meeting/task/ExpertInviteTask.java View File

@@ -1,5 +1,6 @@
package com.ningdatech.pmapi.meeting.task; package com.ningdatech.pmapi.meeting.task;


import cn.hutool.core.util.ArrayUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.core.toolkit.Wrappers;
@@ -21,7 +22,6 @@ import com.ningdatech.pmapi.meeting.service.IExpertInviteAvoidRuleService;
import com.ningdatech.pmapi.meeting.service.IExpertInviteRuleService; import com.ningdatech.pmapi.meeting.service.IExpertInviteRuleService;
import com.ningdatech.pmapi.meeting.service.IMeetingExpertService; import com.ningdatech.pmapi.meeting.service.IMeetingExpertService;
import com.ningdatech.pmapi.meeting.service.IMeetingService; import com.ningdatech.pmapi.meeting.service.IMeetingService;
import com.ningdatech.pmapi.user.util.LoginUserUtil;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -146,18 +146,23 @@ public class ExpertInviteTask {
/** /**
* 唤醒某个会议的抽取任务 * 唤醒某个会议的抽取任务
* *
* @param meetingId 会议ID
* @param meetingId 会议ID
* @param invitedRefused 是否可邀请已拒绝的专家
* @author WendyYang * @author WendyYang
**/ **/
public void notifyInviteTask(Long meetingId) {
public void notifyInviteTask(Long meetingId, boolean... invitedRefused) {
boolean tmpInvitedRefused = true;
if (ArrayUtil.isNotEmpty(invitedRefused)) {
tmpInvitedRefused = invitedRefused[0];
}
if (!INVITE_MAP.containsKey(meetingId)) { if (!INVITE_MAP.containsKey(meetingId)) {
addInviteExpertTask(meetingId, false, properties.getInviteDelay(), true);
addInviteExpertTask(meetingId, false, properties.getInviteDelay(), tmpInvitedRefused);
log.info("重置会议的随机抽取状态:{}", meetingId); log.info("重置会议的随机抽取状态:{}", meetingId);
LambdaUpdateWrapper<Meeting> update = Wrappers.lambdaUpdate(Meeting.class); LambdaUpdateWrapper<Meeting> update = Wrappers.lambdaUpdate(Meeting.class);
update.set(Meeting::getInviteStatus, false); update.set(Meeting::getInviteStatus, false);
update.eq(Meeting::getId, meetingId); update.eq(Meeting::getId, meetingId);
meetingService.update(update); meetingService.update(update);
InviteCacheDTO cacheVal = InviteCacheDTO.of(meetingId, true);
InviteCacheDTO cacheVal = InviteCacheDTO.of(meetingId, tmpInvitedRefused);
cachePlusOps.hSet(getCacheKey(meetingId), cacheVal); cachePlusOps.hSet(getCacheKey(meetingId), cacheVal);
} }
} }
@@ -252,11 +257,7 @@ public class ExpertInviteTask {
}); });
if (notIgnoreCnt.get() == 0 || notIgnoreCnt.get() == notSupportCnt.get()) { if (notIgnoreCnt.get() == 0 || notIgnoreCnt.get() == notSupportCnt.get()) {
if (notSupportCnt.get() > 0) { if (notSupportCnt.get() > 0) {
// TODO
/*UserInfo inviterBasic = userInfoService.getById(meeting.getCreateBy());
UserInfo inviter = userInfoService.getById(inviterBasic.getId());
SendSmsContext context = YxtSmsContextBuilder.smsByRandomInviteStop(inviter.getNickname(), meeting.getName(), inviterBasic.getPhoneNo());
yxtCallOrSmsHelper.sendSms(context);*/
// TODO 发送邀请停止短信
} }
log.info("停止会议随机邀请:{} 未完成抽取规则数量 {} 无可抽取专家规则数量 {}", meetingId, notIgnoreCnt, notSupportCnt); log.info("停止会议随机邀请:{} 未完成抽取规则数量 {} 无可抽取专家规则数量 {}", meetingId, notIgnoreCnt, notSupportCnt);
currProxy().cancelByMeetingId(meetingId); currProxy().cancelByMeetingId(meetingId);


Loading…
Cancel
Save