feat(bpm): 添加 OA 回款申请功能

- 实现了 OA 回款申请的创建、查询、统计等功能
- 添加了回款申请相关的数据结构和接口定义
- 实现了回款申请的流程控制和状态更新- 添加了回款申请的附件上传和处理逻辑
This commit is contained in:
furongxin 2024-12-20 15:38:08 +08:00
parent 1c07abebb0
commit f9b0c8cf7d
12 changed files with 829 additions and 0 deletions

View File

@ -0,0 +1,50 @@
package cn.iocoder.yudao.module.bpm.api.oa;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.bpm.api.oa.vo.receipt.BpmOAReceiptDTO;
import cn.iocoder.yudao.module.bpm.api.oa.vo.receipt.BpmOAReceiptVO;
import cn.iocoder.yudao.module.bpm.api.oa.vo.receipt.ReceiptStatisticsDTO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.contract.ContractStatisticsRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.receipt.ReceiptStatisticsVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAReceiptDO;
import cn.iocoder.yudao.module.bpm.service.oa.BpmOAReceiptService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
/**
* Flowable 流程实例 Api 实现类
*/
@RestController
@Validated
public class BpmOAReceiptApiImpl implements BpmOAReceiptApi{
@Resource
private BpmOAReceiptService receiptService;
@Override
public CommonResult<ReceiptStatisticsDTO> getReceiptCount(String relation) {
ReceiptStatisticsVO respVO = receiptService.getStatistics(getLoginUserId(), relation);
return success(BeanUtils.toBean(respVO, ReceiptStatisticsDTO.class));
}
@Override
public CommonResult<List<BpmOAReceiptDTO>> getReceiptList(BpmOAReceiptVO respVO) {
List<BpmOAReceiptDO> receiptDO = receiptService.getReceiptList(respVO);
return success(BeanUtils.toBean(receiptDO, BpmOAReceiptDTO.class));
}
@Override
public CommonResult<List<ReceiptStatisticsDTO>> getContractStatistics(List<Long> userIds, LocalDateTime[] createTime) {
List<ReceiptStatisticsDTO> respVOS = receiptService.getReceiptStatisticsListByUserIds(userIds, createTime);
return success(BeanUtils.toBean(respVOS, ReceiptStatisticsDTO.class));
}
}

View File

@ -0,0 +1,107 @@
package cn.iocoder.yudao.module.bpm.controller.admin.oa;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.receipt.BpmOAReceiptCreateReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.receipt.BpmOAReceiptPageReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.receipt.BpmOAReceiptRespVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAContractDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAReceiptDO;
import cn.iocoder.yudao.module.bpm.service.oa.BpmOAContractService;
import cn.iocoder.yudao.module.bpm.service.oa.BpmOAReceiptService;
import cn.iocoder.yudao.module.hrm.api.crmcustomer.CrmCustomerApi;
import cn.iocoder.yudao.module.hrm.api.crmcustomer.dto.CrmCustomerDTO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
/**
* OA 回款申请 Controller
*
* @author 符溶馨
*/
@Tag(name = "管理后台 - OA 回款申请")
@RestController
@RequestMapping("/bpm/oa/receipt")
@Validated
public class BpmOAReceiptController {
@Resource
private BpmOAReceiptService receiptService;
@Resource
private CrmCustomerApi customerApi;
@Resource
private BpmOAContractService contractService;
@PostMapping("/create")
@Operation(summary = "创建请求申请")
public CommonResult<Long> createReceipt(@Valid @RequestBody BpmOAReceiptCreateReqVO createReqVO) {
return success(receiptService.createReceipt(getLoginUserId(), createReqVO));
}
@GetMapping("/get")
@Operation(summary = "获得回款申请")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
public CommonResult<BpmOAReceiptRespVO> getReceipt(@RequestParam("id") Long id) {
BpmOAReceiptDO receipt = receiptService.getReceipt(id);
BpmOAReceiptRespVO respVO = BeanUtils.toBean(receipt, BpmOAReceiptRespVO.class);
if (respVO != null) {
// 获取客户信息
CrmCustomerDTO crmCustomerDTO = customerApi.getCustomer(receipt.getCustomerId()).getCheckedData();
// 获取合同信息
BpmOAContractDO contractDO = contractService.getContract(receipt.getContractId());
// 设置客户名称
respVO.setCustomerName(crmCustomerDTO != null ? crmCustomerDTO.getName() : null);
// 设置合同名称
respVO.setContractName(contractDO != null ? contractDO.getContractName() : null);
}
return success(respVO);
}
@GetMapping("/getByProcessInstanceId")
@Operation(summary = "获得回款申请")
@Parameter(name = "processInstanceId", description = "流程实例编号", required = true, example = "1024")
public CommonResult<BpmOAReceiptRespVO> getByProcessInstanceId(@RequestParam("processInstanceId") String processInstanceId) {
BpmOAReceiptDO receipt = receiptService.getByProcessInstanceId(processInstanceId);
BpmOAReceiptRespVO respVO = BeanUtils.toBean(receipt, BpmOAReceiptRespVO.class);
if (respVO != null) {
// 获取客户信息
CrmCustomerDTO crmCustomerDTO = customerApi.getCustomer(receipt.getCustomerId()).getCheckedData();
// 获取合同信息
BpmOAContractDO contractDO = contractService.getContract(receipt.getContractId());
// 设置客户名称
respVO.setCustomerName(crmCustomerDTO != null ? crmCustomerDTO.getName() : null);
// 设置合同名称
respVO.setContractName(contractDO != null ? contractDO.getContractName() : null);
}
return success(respVO);
}
@GetMapping("/page")
@Operation(summary = "获得回款分页列表")
public CommonResult<PageResult<BpmOAReceiptRespVO>> getPage(@Valid BpmOAReceiptPageReqVO pageReqVO) {
return success(receiptService.getReceiptPage(pageReqVO));
}
}

View File

@ -0,0 +1,55 @@
package cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.receipt;
import cn.iocoder.yudao.framework.common.pojo.UploadUserFile;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.List;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
/**
* 回款申请 创建 Request VO
*
* @author 符溶馨
*/
@Schema(description = "管理后台 - 回款申请创建 Request VO")
@Data
@EqualsAndHashCode()
@ToString(callSuper = true)
public class BpmOAReceiptCreateReqVO {
@Schema(description = "客户ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "客户ID不能为空")
private Long customerId;
@Schema(description = "合同ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "合同ID不能为空")
private Long contractId;
@Schema(description = "回款日期", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
private LocalDate returnTime;
@Schema(description = "回款金额", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "回款金额不能为空")
private BigDecimal money;
@Schema(description = "备注", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String remark;
@Schema(description = "流程实例编号")
private String processInstanceId;
@Schema(description = "状态-参见 bpm_process_instance_result 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer result;
@Schema(description = "上传文件", requiredMode = Schema.RequiredMode.REQUIRED)
private List<UploadUserFile> fileItems;
}

View File

@ -0,0 +1,31 @@
package cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.receipt;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@Schema(description = "管理后台 - 回款申请分页 Request VO")
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class BpmOAReceiptPageReqVO extends PageParam {
@Schema(description = "客户名称,模糊匹配")
private String customerName;
@Schema(description = "合同名称,模糊匹配")
private String contractName;
@Schema(description = "审批结果")
private Integer result;
@Schema(description = "创建人名称")
private String createName;
@Schema(description = "查询模式")
private String relation;
}

View File

@ -0,0 +1,56 @@
package cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.receipt;
import cn.iocoder.yudao.framework.common.pojo.UploadUserFile;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.BpmOABaseRespVO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.List;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
/**
* @author 符溶馨
*/
@Schema(description = "管理后台 - 退款申请 请求Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class BpmOAReceiptRespVO extends BpmOABaseRespVO {
@Schema(description = "客户ID")
private Long customerId;
@Schema(description = "客户名称")
private String customerName;
@Schema(description = "合同ID")
private Long contractId;
@Schema(description = "合同编号")
private String contractNo;
@Schema(description = "合同名称")
private String contractName;
@Schema(description = "回款日期")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
private LocalDate returnTime;
@Schema(description = "回款金额")
private BigDecimal money;
@Schema(description = "备注")
private String remark;
@Schema(description = "创建人")
private String createName;
@Schema(description = "上传文件", requiredMode = Schema.RequiredMode.REQUIRED)
private List<UploadUserFile> fileItems;
}

View File

@ -0,0 +1,26 @@
package cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.receipt;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
@Schema(description = "管理后台 - 合同审批 请求Request VO")
@Data
public class ReceiptStatisticsVO {
@Schema(description = "用户编号")
private Long userId;
@Schema(description = "今日新增回款金额")
private BigDecimal todayAddMoney;
@Schema(description = "昨日新增回款金额")
private BigDecimal yesterdayAddMoney;
@Schema(description = "较昨日百分比 | 金额")
private BigDecimal moneyPercentage;
@Schema(description = "回款总金额")
private BigDecimal money;
}

View File

@ -0,0 +1,86 @@
package cn.iocoder.yudao.module.bpm.dal.dataobject.oa;
import cn.iocoder.yudao.framework.common.pojo.UploadUserFile;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.refund.BpmOARefundItems;
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import lombok.*;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.List;
/**
* OA 回款申请 DO
*
* @author 符溶馨
*/
@TableName(value ="bpm_oa_receipt", autoResultMap = true)
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class BpmOAReceiptDO extends BaseDO {
/**
* 出差表单主键
*/
@TableId
private Long id;
/**
* 申请人的用户编号
* 关联 AdminUserDO id 属性
*/
private Long userId;
/**
* 客户ID
*/
private Long customerId;
/**
* 合同ID
*/
private Long contractId;
/**
* 回款日期
*/
private LocalDate returnTime;
/**
* 回款金额
*/
private BigDecimal money;
/**
* 备注
*/
private String remark;
/**
* 结果
* 枚举 {@link BpmProcessInstanceResultEnum}
* 考虑到简单所以直接复用了 BpmProcessInstanceResultEnum 枚举也可以自己定义一个枚举哈
*/
private Integer result;
/**
* 对应的流程编号
* 关联 ProcessInstance id 属性
*/
private String processInstanceId;
/**
* 附件基本信息
*/
@TableField(typeHandler = JacksonTypeHandler.class)
private List<UploadUserFile> fileItems ;
}

View File

@ -0,0 +1,64 @@
package cn.iocoder.yudao.module.bpm.dal.mysql.oa;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;
import cn.iocoder.yudao.module.bpm.api.oa.vo.receipt.ReceiptStatisticsDTO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.contract.BpmOAContractPageReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.contract.BpmOAContractRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.receipt.BpmOAReceiptPageReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.receipt.BpmOAReceiptRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.receipt.ReceiptStatisticsVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAContractDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAReceiptDO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
@Mapper
public interface BpmOAReceiptMapper extends BaseMapperX<BpmOAReceiptDO> {
default PageResult<BpmOAReceiptRespVO> selectReceiptPage(BpmOAReceiptPageReqVO pageReqVO, Long userId) {
MPJLambdaWrapperX<BpmOAReceiptDO> query = new MPJLambdaWrapperX<>();
query.selectAll(BpmOAReceiptDO.class);
query.selectAs(BpmOAContractDO::getContractName, BpmOAReceiptRespVO::getContractName);
query.selectAs(BpmOAContractDO::getCustomerName, BpmOAReceiptRespVO::getCustomerName);
query.selectAs(BpmOAContractDO::getContractNo, BpmOAReceiptRespVO::getContractNo);
query.selectAs("u.nickname", BpmOAReceiptRespVO::getCreateName);
query.leftJoin(BpmOAContractDO.class, "c", BpmOAContractDO::getId, BpmOAReceiptDO::getContractId);
query.leftJoin("system_users u on u.id = t.creator");
query.eqIfPresent(BpmOAReceiptDO::getResult, pageReqVO.getResult());
query.apply(Objects.nonNull(pageReqVO.getCustomerName()), "c.customer_name LIKE CONCAT('%', {0}, '%')", pageReqVO.getCustomerName());
query.apply(Objects.nonNull(pageReqVO.getContractName()), "c.contract_name LIKE CONCAT('%', {0}, '%')", pageReqVO.getContractName());
query.apply(Objects.nonNull(pageReqVO.getCreateName()),"u.nickname Like CONCAT('%', {0}, '%')", pageReqVO.getCreateName());
if ("my".equals(pageReqVO.getRelation())) {
query.eq(BpmOAContractDO::getUserId, userId);
}else if ("sub".equals(pageReqVO.getRelation())){
query.innerJoin("(" +
"\tSELECT DISTINCT\n" +
"\t\tu.id \n" +
"\tFROM\n" +
"\t\tsystem_users u\n" +
"\t\tINNER JOIN system_dept d ON d.leader_user_id = "+ userId +"\n" +
"\t\tINNER JOIN system_dept d1 ON d1.flag LIKE CONCAT( '%', d.id, '-' ) \n" +
"\t\tOR d1.flag LIKE CONCAT( '%-', d.id, '-%' ) \n" +
"\t\tOR d1.flag LIKE CONCAT( '-', d.id, '%' ) \n" +
"\tWHERE\n" +
"\t\tu.dept_id = d1.id \n" +
"\t\tAND u.STATUS = 0 \n" +
"\t) subordinate ON subordinate.id = t.user_id");
}
query.orderByDesc(BpmOAReceiptDO::getCreateTime);
return selectJoinPage(pageReqVO, BpmOAReceiptRespVO.class, query);
}
ReceiptStatisticsVO selectStatistics(@Param("userId") Long userId,
@Param("relation") String relation);
List<ReceiptStatisticsDTO> selectReceiptStatisticsListByUserIds(@Param("userIds") List<Long> userIds,
@Param("createTime") LocalDateTime[] createTime);
}

View File

@ -0,0 +1,80 @@
package cn.iocoder.yudao.module.bpm.service.oa;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.bpm.api.oa.vo.receipt.BpmOAReceiptVO;
import cn.iocoder.yudao.module.bpm.api.oa.vo.receipt.ReceiptStatisticsDTO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.contract.BpmOAContractPageReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.receipt.BpmOAReceiptCreateReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.receipt.BpmOAReceiptPageReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.receipt.BpmOAReceiptRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.receipt.ReceiptStatisticsVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAReceiptDO;
import javax.validation.Valid;
import java.time.LocalDateTime;
import java.util.List;
public interface BpmOAReceiptService {
/**
* 创建回款申请
*
* @param userId 用户编号
* @param createReqVO 创建信息
* @return 编号
*/
Long createReceipt(Long userId, @Valid BpmOAReceiptCreateReqVO createReqVO);
/**
* 更新回款申请的状态
*
* @param id 编号
* @param result 结果
*/
void updateReceiptResult(String processInstanceId, Long id, Integer result);
/**
* 获得回款申请
*
* @param id 编号
* @return 退款申请
*/
BpmOAReceiptDO getReceipt(Long id);
/**
* 获得指定的回款申请
* @param processInstanceId 流程实例编号
* @return 退款申请
*/
BpmOAReceiptDO getByProcessInstanceId(String processInstanceId);
/**
* 获得回款申请分页列表
* @param pageReqVO 分页信息
* @return 分页列表
*/
PageResult<BpmOAReceiptRespVO> getReceiptPage(BpmOAReceiptPageReqVO pageReqVO);
/**
* 获得回款统计信息
* @param userId 用户编号
* @param relation 查询类型
* @return 回款统计信息
*/
ReceiptStatisticsVO getStatistics(Long userId, String relation);
/**
* 获得回款列表
* @param respVO 参数信息
* @return 回款信息列表
*/
List<BpmOAReceiptDO> getReceiptList(BpmOAReceiptVO respVO);
/**
* 获得指定用户回款统计信息
* @param userIds 用户编号
* @param createTime 创建时间
* @return 回款统计信息
*/
List<ReceiptStatisticsDTO> getReceiptStatisticsListByUserIds(List<Long> userIds, LocalDateTime[] createTime);
}

View File

@ -0,0 +1,180 @@
package cn.iocoder.yudao.module.bpm.service.oa;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.UploadUserFile;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.bpm.api.oa.vo.receipt.BpmOAReceiptVO;
import cn.iocoder.yudao.module.bpm.api.oa.vo.receipt.ReceiptStatisticsDTO;
import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.receipt.BpmOAReceiptCreateReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.receipt.BpmOAReceiptPageReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.receipt.BpmOAReceiptRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.receipt.ReceiptStatisticsVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAReceiptDO;
import cn.iocoder.yudao.module.bpm.dal.mysql.oa.BpmOAReceiptMapper;
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum;
import cn.iocoder.yudao.module.bpm.service.task.BpmHistoryProcessInstanceService;
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.OA_RECEIPT_NOT_EXISTS;
/**
* OA 回款申请 Service 实现类
*
* @author 符溶馨
*/
@Service
@Validated
public class BpmOAReceiptServiceImpl extends BpmOABaseService implements BpmOAReceiptService{
/**
* OA 退款对应的流程定义 KEY
*/
public static final String PROCESS_KEY = "oa_receipt_2";
@Resource
private BpmOAReceiptMapper receiptMapper;
@Resource
private BpmProcessInstanceApi processInstanceApi;
@Resource
private BpmHistoryProcessInstanceService historyProcessInstanceService;
@Resource
private BpmProcessInstanceService bpmProcessInstanceService;
@Override
public Long createReceipt(Long userId, BpmOAReceiptCreateReqVO createReqVO) {
//插入OA 回款申请
BpmOAReceiptDO receipt = BeanUtils.toBean(createReqVO, BpmOAReceiptDO.class)
.setUserId(userId)
.setResult(BpmProcessInstanceResultEnum.PROCESS.getResult());
receiptMapper.insert(receipt) ;
// 发起 BPM 流程
Map<String, Object> processInstanceVariables = new HashMap<>();
String processInstanceId = processInstanceApi.createProcessInstance(userId,
new BpmProcessInstanceCreateReqDTO().setProcessDefinitionKey(PROCESS_KEY)
.setVariables(processInstanceVariables).setBusinessKey(String.valueOf(receipt.getId()))).getCheckedData();
// 将工作流的编号更新到 OA 回款申请单中
receiptMapper.updateById(new BpmOAReceiptDO().setId(receipt.getId()).setProcessInstanceId(processInstanceId));
// 判断是否为重新发起的流程
if (createReqVO.getProcessInstanceId() != null && createReqVO.getResult() == 3) {
historyProcessInstanceService.createHistoryProcessInstance(processInstanceId, createReqVO.getProcessInstanceId());
}
List<UploadUserFile> fileItems = createReqVO.getFileItems() ;
//这里的逻辑如果fileItems不为空且有数据那么说明是上传了附件的则需要更工作流文件表对应的实例Id
if (fileItems != null && !fileItems.isEmpty()) {
uploadBpmFileProcessInstanceId(processInstanceId,fileItems) ;
}
return receipt.getId();
}
@Override
public void updateReceiptResult(String processInstanceId, Long id, Integer result) {
BpmOAReceiptDO receiptDO = validateLeaveExists(id);
//审核通过 最后节点
if (BpmProcessInstanceResultEnum.APPROVE.getResult().equals(result)) {
ProcessInstance instance = bpmProcessInstanceService.getProcessInstance(processInstanceId);
if (instance.isEnded()) {
}
}
receiptMapper.updateById(new BpmOAReceiptDO().setId(id).setResult(result));
}
private BpmOAReceiptDO validateLeaveExists(Long id) {
BpmOAReceiptDO receiptDO = receiptMapper.selectById(id);
if (receiptDO == null) {
throw exception(OA_RECEIPT_NOT_EXISTS);
}
return receiptDO;
}
@Override
public BpmOAReceiptDO getReceipt(Long id) {
return receiptMapper.selectById(id);
}
@Override
public BpmOAReceiptDO getByProcessInstanceId(String processInstanceId) {
return receiptMapper.selectOne(BpmOAReceiptDO::getProcessInstanceId, processInstanceId);
}
@Override
public PageResult<BpmOAReceiptRespVO> getReceiptPage(BpmOAReceiptPageReqVO pageReqVO) {
return receiptMapper.selectReceiptPage(pageReqVO, getLoginUserId());
}
@Override
public ReceiptStatisticsVO getStatistics(Long userId, String relation) {
ReceiptStatisticsVO respVO = receiptMapper.selectStatistics(userId, relation);
// 设置数量金额百分比
respVO.setMoneyPercentage(calculatePercentageChange(respVO.getTodayAddMoney(), respVO.getYesterdayAddMoney()));
return respVO;
}
@Override
public List<BpmOAReceiptDO> getReceiptList(BpmOAReceiptVO respVO) {
return receiptMapper.selectList(new LambdaQueryWrapperX<BpmOAReceiptDO>()
.inIfPresent(BpmOAReceiptDO::getUserId, respVO.getUserId())
.eq(BpmOAReceiptDO::getResult, BpmProcessInstanceResultEnum.APPROVE.getResult())
.betweenIfPresent(BpmOAReceiptDO::getReturnTime, respVO.getStarTime(), respVO.getEndTime()));
}
@Override
public List<ReceiptStatisticsDTO> getReceiptStatisticsListByUserIds(List<Long> userIds, LocalDateTime[] createTime) {
return receiptMapper.selectReceiptStatisticsListByUserIds(userIds, createTime);
}
public static BigDecimal calculatePercentageChange(Object today, Object yesterday) {
// 转换为 BigDecimal 进行高精度计算
BigDecimal bigToday = new BigDecimal(today.toString());
BigDecimal bigYesterday = new BigDecimal(yesterday.toString());
if (bigYesterday.compareTo(BigDecimal.ZERO) == 0) {
return BigDecimal.ZERO;
}
// 计算百分比变化
BigDecimal difference = bigToday.subtract(bigYesterday); // a - b
BigDecimal percentageChange = difference.divide(bigYesterday, 2, RoundingMode.HALF_UP); // (a - b) / b
// 转为百分比并返回
return percentageChange.multiply(new BigDecimal(100)); // (result) * 100
}
}

View File

@ -0,0 +1,32 @@
package cn.iocoder.yudao.module.bpm.service.oa.listener;
import cn.iocoder.yudao.module.bpm.framework.bpm.core.event.BpmProcessInstanceResultEvent;
import cn.iocoder.yudao.module.bpm.framework.bpm.core.event.BpmProcessInstanceResultEventListener;
import cn.iocoder.yudao.module.bpm.service.oa.BpmOAReceiptService;
import cn.iocoder.yudao.module.bpm.service.oa.BpmOAReceiptServiceImpl;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* OA 回款单的结果的监听器实现类
*
* @author 符溶馨
*/
@Component
public class BpmOAReceiptResultListener extends BpmProcessInstanceResultEventListener {
@Resource
private BpmOAReceiptService receiptService;
@Override
protected String getProcessDefinitionKey() {
return BpmOAReceiptServiceImpl.PROCESS_KEY;
}
@Override
protected void onEvent(BpmProcessInstanceResultEvent event) {
receiptService.updateReceiptResult(event.getId(), Long.parseLong(event.getBusinessKey()), event.getResult());
}
}

View File

@ -0,0 +1,62 @@
<?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="cn.iocoder.yudao.module.bpm.dal.mysql.oa.BpmOAReceiptMapper">
<!--
一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
文档可见https://www.iocoder.cn/MyBatis/x-plugins/
-->
<select id="selectStatistics" resultType="cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.receipt.ReceiptStatisticsVO">
SELECT
COALESCE ( SUM( CASE WHEN DATE( a.create_time ) = CURDATE() THEN a.money END ), 0 ) AS todayAddMoney,
COALESCE ( SUM( CASE WHEN DATE( a.create_time ) = CURDATE() - INTERVAL 1 DAY THEN a.money END ), 0 ) AS yesterdayAddMoney
FROM
bpm_oa_receipt a
<if test="relation == 'sub'">
INNER JOIN (
SELECT DISTINCT
u.id
FROM
system_users u
INNER JOIN system_dept d ON d.leader_user_id = #{userId}
INNER JOIN system_dept d1 ON d1.flag LIKE CONCAT( '%', d.id, '-' )
OR d1.flag LIKE CONCAT( '%-', d.id, '-%' )
OR d1.flag LIKE CONCAT( '-', d.id, '%' )
WHERE
u.dept_id = d1.id
AND u.STATUS = 0
) subordinate ON subordinate.id = a.user_id
</if>
WHERE
a.result = 2
<if test="relation == 'my'">
AND a.user_id = #{userId}
</if>
</select>
<select id="selectReceiptStatisticsListByUserIds" resultType="cn.iocoder.yudao.module.bpm.api.oa.vo.receipt.ReceiptStatisticsDTO">
SELECT
user_id AS userId,
SUM(money) AS money
FROM
bpm_oa_receipt
WHERE
deleted = 0
AND result = 2
AND user_id IN
<foreach item="userId" collection="userIds" separator="," open="(" close=")" index="">
#{userId}
</foreach>
<if test="createTime != null and createTime.length > 0">
<if test="createTime[0] != null">
and create_time &gt;= #{createTime[0]}
</if>
<if test="createTime[1] != null">
and create_time &lt;= #{createTime[1]}
</if>
</if>
GROUP BY user_id
</select>
</mapper>