From 3a0cae35bd5c2a69a05c6ac36438f025a389ca02 Mon Sep 17 00:00:00 2001
From: Echo <4759156@qq.com>
Date: Thu, 10 Apr 2025 17:32:16 +0800
Subject: [PATCH 1/2] =?UTF-8?q?replaceValues=20=E5=A4=84=E7=90=86=E4=B8=80?=
=?UTF-8?q?=E4=B8=8B=E3=80=82=20=E5=A2=9E=E5=BC=BA=E4=BA=86=E9=80=BB?=
=?UTF-8?q?=E8=BE=91=E3=80=82?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../module/bpm/service/task/BpmProcessInstanceServiceImpl.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
index 6208054c..0128f70e 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
@@ -1 +1 @@
-package cn.iocoder.yudao.module.bpm.service.task;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
import cn.iocoder.yudao.framework.flowable.core.util.FlowableUtils;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.cash.BpmOACashRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.payment.BpmOAPaymentRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.print.BpmOAPrintDataRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.print.BpmProcessInstancePrintDataReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.print.BpmProcessInstancePrintDataRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.reimbursement.BpmOAReimbursementRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskApproveReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;
import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessCcDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionExtDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmTaskAssignRuleDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmUserGroupDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOACashDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAImprestDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAPaymentDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAReimbursementDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceExtDO;
import cn.iocoder.yudao.module.bpm.dal.mysql.DynamicMapper;
import cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmTaskAssignRuleMapper;
import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmProcessInstanceExtMapper;
import cn.iocoder.yudao.module.bpm.enums.task.BpmConstants;
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceDeleteReasonEnum;
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum;
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum;
import cn.iocoder.yudao.module.bpm.framework.bpm.core.event.BpmProcessInstanceResultEventPublisher;
import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessCcService;
import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
import cn.iocoder.yudao.module.bpm.service.definition.BpmUserGroupService;
import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService;
import cn.iocoder.yudao.module.bpm.service.oa.*;
import cn.iocoder.yudao.module.infra.api.config.ConfigApi;
import cn.iocoder.yudao.module.infra.api.file.FileApi;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
import cn.iocoder.yudao.module.system.api.permission.dto.DeptDataPermissionRespDTO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import jodd.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.delegate.event.FlowableCancelledEvent;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.lang.reflect.Field;
import java.text.DecimalFormat;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
/**
* 流程实例 Service 实现类
*
* ProcessDefinition & ProcessInstance & Execution & Task 的关系:
* 1.
*
* HistoricProcessInstance & ProcessInstance 的关系:
* 1.
*
* 简单来说,前者 = 历史 + 运行中的流程实例,后者仅是运行中的流程实例
*/
@Service
@Validated
@Slf4j
public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService {
@Resource
private TaskService engineTaskService;
@Resource
private BpmTaskAssignRuleMapper taskRuleMapper;
@Resource
private BpmUserGroupService userGroupService;
@Resource
private RuntimeService runtimeService;
@Resource
private BpmProcessInstanceExtMapper processInstanceExtMapper;
@Resource
@Lazy // 解决循环依赖
private BpmTaskService taskService;
@Resource
private BpmProcessDefinitionService processDefinitionService;
@Resource
private HistoryService historyService;
@Resource
private AdminUserApi adminUserApi;
@Resource
private DeptApi deptApi;
@Resource
private BpmProcessInstanceResultEventPublisher processInstanceResultEventPublisher;
@Resource
@Lazy // 解决循环依赖
private BpmMessageService messageService;
@Resource
private ConfigApi configApi;
@Resource
private DynamicMapper dynamicMapper ;
@Resource
private BpmProcessCcService processCcService;
@Resource
@Lazy // 解决循环依赖
private StringRedisTemplate stringRedisTemplate;
@Override
public ProcessInstance getProcessInstance(String id) {
return runtimeService.createProcessInstanceQuery().processInstanceId(id).singleResult();
}
@Override
public List getProcessInstances(Set ids) {
return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).list();
}
public String transform(String input) {
if(StringUtil.isNotEmpty(input)) {
String[] parts = input.split(",");
StringBuilder result = new StringBuilder();
for (String part : parts) {
String[] keyValue = part.split(":");
if (keyValue.length > 1) {
result.append(keyValue[1]).append(",");
}
}
if (result.length() > 0) {
return result.substring(0, result.length() - 1);
}
}
return "";
}
public String replaceValues(String input, Map map) {
String[] parts = input.split(",");
StringBuilder result = new StringBuilder();
for (String part : parts) {
String[] keyValue = part.split(":");
if (keyValue.length > 1) {
String key = keyValue[1].trim(); // 去除可能的空格
if (map.containsKey(key)) {
Object value = map.get(key);
String stringValue = (value != null) ? value.toString() : ""; // 将值转换为字符串
result.append(keyValue[0].trim()).append(":").append(stringValue).append(",");
} else {
//result.append(part).append(",");
}
}
}
if (result.length() > 0) {
return result.substring(0, result.length() - 1);
}
return "";
}
@Override
@TenantIgnore
public PageResult getMyProcessInstancePage(Long userId,
BpmProcessInstanceMyPageReqVO pageReqVO) {
// 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页
PageResult pageResult = processInstanceExtMapper.selectPage(userId, pageReqVO);
List bpmProcessInstanceExtDOList = pageResult.getList() ;
for (int i = 0; i < bpmProcessInstanceExtDOList.size() ; i++) {
BpmProcessInstanceExtDO bpmProcessInstanceExtDO = bpmProcessInstanceExtDOList.get(i) ;
String input = bpmProcessInstanceExtDO.getFieldNames() ;
if(StringUtil.isNotEmpty(input)) {
String sql = "SELECT " + transform(bpmProcessInstanceExtDO.getFieldNames()) + " FROM " + bpmProcessInstanceExtDO.getTableName() + " WHERE id=" + bpmProcessInstanceExtDO.getBusinessKey();
log.info(sql);
Map map = dynamicMapper.selectListByTableName(sql) ;
String detailInfo = replaceValues(bpmProcessInstanceExtDO.getFieldNames(),map) ;
bpmProcessInstanceExtDO.setDetailInfo(detailInfo);
}
}
if (CollUtil.isEmpty(pageResult.getList())) {
return new PageResult<>(pageResult.getTotal());
}
// 获得流程 Task Map
List processInstanceIds = convertList(pageResult.getList(), BpmProcessInstanceExtDO::getProcessInstanceId);
Map> taskMap = taskService.getTaskMapByProcessInstanceIds(processInstanceIds);
List ids = taskMap.values().stream()
.flatMap(Collection::stream)
.map(Task::getAssignee)
.collect(Collectors.toList());
Iterator iterator = ids.iterator();
while (iterator.hasNext()) {
String id = iterator.next();
if (id == null) {
iterator.remove();
}
}
List longIds = ids.stream()
.map(Long::valueOf)
.collect(Collectors.toList());
// 获得 User Map
Map userMap = adminUserApi.getUserMap(longIds);
// 转换返回
return BpmProcessInstanceConvert.INSTANCE.convertPage(pageResult, taskMap, userMap);
}
@Override
@Transactional(rollbackFor = Exception.class)
public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO) {
// 获得流程定义
ProcessDefinition definition = processDefinitionService.getProcessDefinition(createReqVO.getProcessDefinitionId());
// 发起流程
if (MapUtil.isEmpty(createReqVO.getVariables())){
createReqVO.setVariables(new HashMap<>());
}
return createProcessInstance0(userId, definition, createReqVO.getVariables(), null);
}
@Override
@Transactional(rollbackFor = Exception.class)
public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) {
// 获得流程定义
ProcessDefinition definition = processDefinitionService.getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey());
// 发起流程
if (MapUtil.isEmpty(createReqDTO.getVariables())){
createReqDTO.setVariables(new HashMap<>());
}
return createProcessInstance0(userId, definition, createReqDTO.getVariables(), createReqDTO.getBusinessKey());
}
@Override
public BpmProcessInstanceRespVO getProcessInstanceVO(String id) {
// 获得流程实例
HistoricProcessInstance processInstance = getHistoricProcessInstance(id);
if (processInstance == null) {
return null;
}
BpmProcessInstanceExtDO processInstanceExt = processInstanceExtMapper.selectByProcessInstanceId(id);
Assert.notNull(processInstanceExt, "流程实例拓展({}) 不存在", id);
// 获得流程定义
ProcessDefinition processDefinition = processDefinitionService
.getProcessDefinition(processInstance.getProcessDefinitionId());
Assert.notNull(processDefinition, "流程定义({}) 不存在", processInstance.getProcessDefinitionId());
BpmProcessDefinitionExtDO processDefinitionExt = processDefinitionService.getProcessDefinitionExt(
processInstance.getProcessDefinitionId());
Assert.notNull(processDefinitionExt, "流程定义拓展({}) 不存在", id);
String bpmnXml = processDefinitionService.getProcessDefinitionBpmnXML(processInstance.getProcessDefinitionId());
// 获得 User
AdminUserRespDTO startUser = adminUserApi.getUser(NumberUtils.parseLong(processInstance.getStartUserId())).getCheckedData();
DeptRespDTO dept = null;
DeptRespDTO companyDept = null;
if (startUser != null) {
dept = deptApi.getDept(startUser.getDeptId()).getCheckedData();
companyDept = deptApi.getUserCompanyDept(startUser.getId()).getCheckedData();
}
// 拼接结果
return BpmProcessInstanceConvert.INSTANCE.convert2(processInstance, processInstanceExt,
processDefinition, processDefinitionExt, bpmnXml, startUser, dept, companyDept);
}
@Override
public void cancelProcessInstance(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) {
// 校验流程实例存在
ProcessInstance instance = getProcessInstance(cancelReqVO.getId());
if (instance == null) {
throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS);
}
// 只能取消自己的
if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) {
throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF);
}
// 通过删除流程实例,实现流程实例的取消,
// 删除流程实例,正则执行任务 ACT_RU_TASK. 任务会被删除。通过历史表查询
deleteProcessInstance(cancelReqVO.getId(),
BpmProcessInstanceDeleteReasonEnum.CANCEL_TASK.format(cancelReqVO.getReason()));
}
/**
* 获得历史的流程实例
*
* @param id 流程实例的编号
* @return 历史的流程实例
*/
@Override
public HistoricProcessInstance getHistoricProcessInstance(String id) {
return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).singleResult();
}
@Override
public List getHistoricProcessInstances(Set ids) {
return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).list();
}
@Override
public void createProcessInstanceExt(ProcessInstance instance) {
// 获得流程定义
ProcessDefinition definition = processDefinitionService.getProcessDefinition2(instance.getProcessDefinitionId());
// 插入 BpmProcessInstanceExtDO 对象
BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO()
.setProcessInstanceId(instance.getId())
.setProcessDefinitionId(definition.getId())
.setName(instance.getProcessDefinitionName())
.setStartUserId(Long.valueOf(instance.getStartUserId()))
.setCategory(definition.getCategory())
.setStatus(BpmProcessInstanceStatusEnum.RUNNING.getStatus())
.setResult(BpmProcessInstanceResultEnum.PROCESS.getResult());
processInstanceExtMapper.insert(instanceExtDO);
}
@Override
@DataPermission(enable = false)
public void updateProcessInstanceExtCancel(FlowableCancelledEvent event) {
// 判断是否为 Reject 不通过。如果是,则不进行更新.
// 因为,updateProcessInstanceExtReject 方法,已经进行更新了
if (BpmProcessInstanceDeleteReasonEnum.isRejectReason((String) event.getCause())) {
return;
}
// 需要主动查询,因为 instance 只有 id 属性
// 另外,此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance
HistoricProcessInstance processInstance = getHistoricProcessInstance(event.getProcessInstanceId());
// 更新拓展表
BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO()
.setProcessInstanceId(event.getProcessInstanceId())
.setEndTime(LocalDateTime.now()) // 由于 ProcessInstance 里没有办法拿到 endTime,所以这里设置
.setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus())
.setResult(BpmProcessInstanceResultEnum.CANCEL.getResult());
processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO);
// 删除 流程审批部门缓存
updateAssigneeDeptRedis(event.getProcessInstanceId());
// 发送流程实例的状态事件
processInstanceResultEventPublisher.sendProcessInstanceResultEvent(
BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult()));
}
@Override
@DataPermission(enable = false)
public void updateProcessInstanceExtComplete(ProcessInstance instance) {
// 需要主动查询,因为 instance 只有 id 属性
// 另外,此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance
HistoricProcessInstance processInstance = getHistoricProcessInstance(instance.getId());
// 更新拓展表
BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO()
.setProcessInstanceId(instance.getProcessInstanceId())
.setEndTime(LocalDateTime.now()) // 由于 ProcessInstance 里没有办法拿到 endTime,所以这里设置
.setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus())
.setResult(BpmProcessInstanceResultEnum.APPROVE.getResult()); // 如果正常完全,说明审批通过
processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO);
// 删除 流程审批部门缓存
updateAssigneeDeptRedis(instance.getProcessInstanceId());
Map processVariables = runtimeService.getVariables(instance.getProcessInstanceId());
String reason = (String) processVariables.get("approve_reason");
// 发送流程被通过的消息
messageService.sendMessageWhenProcessInstanceApprove(BpmProcessInstanceConvert.INSTANCE.convert2ApprovedReq(instance, reason));
// 发送流程实例的状态事件
processInstanceResultEventPublisher.sendProcessInstanceResultEvent(
BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult()));
}
@Override
@Transactional(rollbackFor = Exception.class)
@DataPermission(enable = false)
public void updateProcessInstanceExtReject(String id, String reason) {
// 需要主动查询,因为 instance 只有 id 属性
ProcessInstance processInstance = getProcessInstance(id);
// 删除流程实例,以实现驳回任务时,取消整个审批流程
deleteProcessInstance(id, StrUtil.format(BpmProcessInstanceDeleteReasonEnum.REJECT_TASK.format(reason)));
// 更新 status + result
// 注意,不能和上面的逻辑更换位置。因为 deleteProcessInstance 会触发流程的取消,进而调用 updateProcessInstanceExtCancel 方法,
// 设置 result 为 BpmProcessInstanceStatusEnum.CANCEL,显然和 result 不一定是一致的
BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO()
.setProcessInstanceId(id)
.setEndTime(LocalDateTime.now()) // 由于 ProcessInstance 里没有办法拿到 endTime,所以这里设置
.setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus())
.setResult(BpmProcessInstanceResultEnum.REJECT.getResult());
processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO);
// 删除 流程审批部门缓存
updateAssigneeDeptRedis(id);
// 发送流程被不通过的消息
messageService.sendMessageWhenProcessInstanceReject(BpmProcessInstanceConvert.INSTANCE.convert2RejectReq(processInstance, reason));
// 发送流程实例的状态事件
processInstanceResultEventPublisher.sendProcessInstanceResultEvent(
BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult()));
}
@Override
public void updateProcessInstanceResult(String id, Integer result) {
processInstanceExtMapper.update(null, new LambdaUpdateWrapper()
.set(BpmProcessInstanceExtDO::getResult, result)
.eq(BpmProcessInstanceExtDO::getProcessInstanceId, id));
}
/**
* 删除 流程审批部门缓存
* @param processInstanceId 流程实例id
*/
public void updateAssigneeDeptRedis(String processInstanceId) {
stringRedisTemplate.delete("assignee_dept_" + processInstanceId);
}
private void deleteProcessInstance(String id, String reason) {
runtimeService.deleteProcessInstance(id, reason);
}
private String createProcessInstance0(Long userId, ProcessDefinition definition,
Map variables, String businessKey) {
// 校验流程定义
if (definition == null) {
throw exception(PROCESS_DEFINITION_NOT_EXISTS);
}
if (definition.isSuspended()) {
throw exception(PROCESS_DEFINITION_IS_SUSPENDED);
}
// 设置当前用户
FlowableUtils.setAuthenticatedUserId(userId);
//获取流程发起人User
AdminUserRespDTO startUser = adminUserApi.getUser(userId).getCheckedData();
//获取流程发起人部门信息
DeptRespDTO startDeptInfo = deptApi.getDept(startUser.getDeptId()).getCheckedData();
// 获取岗位信息
Set postIds = startUser.getPostIds();
ArrayList list = new ArrayList<>(postIds);
//配置通用流程变量
variables.put("post_id", list.get(0).toString()); // 只获配置的首个岗位
variables.put("user_id", userId.toString()); // 配置发起人用户id
variables.put("dept_id", startUser.getDeptId().toString()); // 配置发起人部门id
variables.put("dept_flag", startDeptInfo.getFlag()); // 配置发起人部门flag
// 创建流程实例
ProcessInstance instance = runtimeService.createProcessInstanceBuilder()
.processDefinitionId(definition.getId())
.businessKey(businessKey)
.name(definition.getName().trim())
.variables(variables)
.start();
// 设置流程名字
runtimeService.setProcessInstanceName(instance.getId(), definition.getName());
// 设置流程审批部门缓存
stringRedisTemplate.opsForValue().set("assignee_dept_" + instance.getId(), startUser.getDeptId().toString());
// 在事务提交时,批量执行操作,所以直接查询会无法查询到 ProcessInstance,所以这里是通过监听事务的提交来实现。
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
@Override
public void afterCommit() {
// 创建流程后,添加抄送人
processCCToUsers(instance, variables);
}
});
return instance.getId();
// // 补全流程实例的拓展表
// processInstanceExtMapper.updateByProcessInstanceId(new BpmProcessInstanceExtDO().setProcessInstanceId(instance.getId())
// .setFormVariables(variables));
//
// /** 创建流程后,添加抄送人 End add by yj 2024.1.4 */
// processCCToUsers(definition, instance);
// /** 通过自己发起的流程 */
// approveSelfTask(instance.getId()) ;
}
private void approveSelfTask(String processInstanceId) {
List tasks = engineTaskService.createTaskQuery().processInstanceId(processInstanceId).list();
if (tasks != null && tasks.size() > 0) {
Task task = tasks.get(0);
String assigneeId = task.getAssignee();
//如果当前登陆用户是审批人,那么自动审批通过
if (assigneeId.equals(SecurityFrameworkUtils.getLoginUserId().toString())) {
BpmTaskApproveReqVO reqVO = new BpmTaskApproveReqVO();
reqVO.setId(task.getId());
reqVO.setReason(BpmConstants.AUTO_APPRAVAL);
taskService.approveTask(getLoginUserId(), reqVO);
}
}
}
/**
* 获得抄送我的流程实例的分页
*
* @param userId 用户编号
* @param pageReqVO 分页请求
* @return 流程实例的分页
*/
public PageResult getMyCCProcessInstancePage(Long userId,
BpmProcessInstanceMyPageReqVO pageReqVO) {
// 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页
PageResult pageResult = processInstanceExtMapper.selectCCPage(userId, pageReqVO);
if (CollUtil.isEmpty(pageResult.getList())) {
return new PageResult<>(pageResult.getTotal());
}
// 获得流程 Task Map
List processInstanceIds = convertList(pageResult.getList(), BpmProcessInstanceExtDO::getProcessInstanceId);
Map> taskMap = taskService.getTaskMapByProcessInstanceIds(processInstanceIds);
// 转换返回
return BpmProcessInstanceConvert.INSTANCE.convertPage(pageResult, taskMap, null);
}
@Override
public List getProcessInstancesGroupByModelName(BpmProcessInstanceStatisticsReqVO pageReqVO) {
pageReqVO = getUserids(pageReqVO);
return processInstanceExtMapper.getProcessInstancesGroupByModelName(pageReqVO);
}
@Override
public List getProcessInstancesGroupByResultStatus(BpmProcessInstanceStatisticsReqVO pageReqVO) {
pageReqVO = getUserids(pageReqVO);
return processInstanceExtMapper.getProcessInstancesGroupByResultStatus(pageReqVO);
}
@Override
public PageResult getStatisticsProcessInstancePage(@Valid BpmProcessInstanceMyPageReqVO pageReqVO) {
// 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页
PageResult pageResult = processInstanceExtMapper.selectStatisticePage(pageReqVO);
if (CollUtil.isEmpty(pageResult.getList())) {
return new PageResult<>(pageResult.getTotal());
}
// 获得流程 Task Map
List processInstanceIds = convertList(pageResult.getList(), BpmProcessInstanceExtDO::getProcessInstanceId);
Map> taskMap = taskService.getTaskMapByProcessInstanceIds(processInstanceIds);
List ids = taskMap.values().stream()
.flatMap(Collection::stream)
.map(Task::getAssignee)
.collect(Collectors.toList());
Iterator iterator = ids.iterator();
while (iterator.hasNext()) {
String id = iterator.next();
if (id == null) {
iterator.remove();
}
}
List longIds = ids.stream()
.map(Long::valueOf)
.collect(Collectors.toList());
//获得 审批人 User Map
Map assigneeUserMap = adminUserApi.getUserMap(longIds);
//获得 发起人 User Map
List bpieDOs = pageResult.getList();
longIds = new ArrayList<>();
for (BpmProcessInstanceExtDO bpieDO : bpieDOs) {
longIds.add(bpieDO.getStartUserId());
}
Map startUserMap = adminUserApi.getUserMap(longIds);
// 转换返回
return BpmProcessInstanceConvert.INSTANCE.convertStatisticsPage(pageResult, taskMap, assigneeUserMap, startUserMap);
}
@Resource
PermissionApi permissionApi;
/**
* 根据数据权限,查询关联操作的用户IDS
*
* @param pageReqVO
* @param
* @return
*/
private T getUserids(T pageReqVO) {
try {
Class clazz = (Class) pageReqVO.getClass();
Field idField = clazz.getDeclaredField("userIds");
idField.setAccessible(true); // 设置可访问性
Long[] userIds = null;
Long userId = WebFrameworkUtils.getLoginUserId();
DeptDataPermissionRespDTO deptDataPermission = permissionApi.getDeptDataPermission(userId).getCheckedData();
//查询全部
if (deptDataPermission.getAll()) {
//idField.set(pageReqVO, null); // 设置属性值
return pageReqVO;
}
// 情况二,即不能查看部门,又不能查看自己,则说明 100% 无权限
if (CollUtil.isEmpty(deptDataPermission.getDeptIds())
&& Boolean.FALSE.equals(deptDataPermission.getSelf())) {
//设置成0,一个不存在的用户Id,就查询不到数据了。
userIds = new Long[]{0L};
idField.set(pageReqVO, userIds);
return pageReqVO;
}
//情况三 至查询自己
if (deptDataPermission.getSelf()) {
userIds = new Long[]{userId};
idField.set(pageReqVO, userIds);
return pageReqVO;
}
Set deptIds = deptDataPermission.getDeptIds();
//查询部门关联的用户Id
List users = adminUserApi.getUserListByDeptIds(deptIds).getCheckedData();
List tempList = new ArrayList<>();
for (AdminUserRespDTO user : users) {
Long id = user.getId();
tempList.add(id);
}
tempList.add(userId);
userIds = tempList.stream().toArray(Long[]::new); //将临时的List集合转换成数组集合
idField.set(pageReqVO, userIds);
return pageReqVO;
} catch (Exception exception) {
exception.printStackTrace();
throw exception(BPM_SYSTEM_BUG);
}
}
/**
* /创建流程后,添加抄送人 Begin add by yj 2024.1.4
* 在设计流程的时候,需要添加一个任务块 名字必须叫Activity_cc 分配权限的时候,需要选择用户组。
*
* @param definition
* @param instance
*/
private void processCCToUsers(ProcessDefinition definition, ProcessInstance instance) {
//获取bpm_task_assign_reule (Bpm 任务规则表)的流程中有没有配置抄送节点 固定抄送名称为:Activity_cc
String processDefinitionId = definition.getId();
List rules = taskRuleMapper.selectListByProcessDefinitionId(processDefinitionId, null);
String BpmConstantsName = BpmConstants.CC_NAME;
// 获取发起人信息
AdminUserRespDTO userRespDTO = adminUserApi.getUser(Long.valueOf(instance.getStartUserId())).getCheckedData();
DeptRespDTO deptRespDTO = deptApi.getDept(userRespDTO.getDeptId()).getCheckedData();
//发起人是深圳分公司
if (deptRespDTO.getFlag().contains("136")) {
BpmConstantsName = BpmConstants.CCSZ_NAME;
}
for (BpmTaskAssignRuleDO rule : rules) {
String key = rule.getTaskDefinitionKey(); //任务名称
Integer type = rule.getType();
if (!key.isEmpty() && key.equals(BpmConstantsName) && type == 40) {
StringBuffer str = new StringBuffer();
Set options = rule.getOptions();
List list = new ArrayList(options);
for (Long groupId : list) {
//需要根据这个groupId,查询这个组中的用户id
BpmUserGroupDO userGroup = userGroupService.getUserGroup(groupId);
Set userIds = userGroup.getMemberUserIds();
List userIdList = new ArrayList(userIds);
for (Long user_id : userIdList) {
str.append("[").append(user_id).append("]");
}
}
//流程是采购计划时
if (definition.getKey().equals(BpmOAProcureServiceImpl.PROCESS_KEY)) {
//发起人部门为 生产部及以下时
if (deptRespDTO.getFlag().contains("130")) {
//添加 供应部抄送
BpmUserGroupDO userGroup = userGroupService.getUserGroup(121L);
Set userIds = userGroup.getMemberUserIds();
List userIdList = new ArrayList(userIds);
for (Long user_id : userIdList) {
str.append("[").append(user_id).append("]");
}
}
}
String ccids = str.toString();
//根据processDefinitionId 将ccids保存到bpm_process_instance_ext中的ccids字段
BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO().setProcessDefinitionId(processDefinitionId)
.setCcids(ccids).setProcessInstanceId(instance.getProcessInstanceId());
processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO);
break;
}
}
}
/**
* 创建流程后,添加抄送人
* @param instance 流程实例
*/
private void processCCToUsers(ProcessInstance instance, Map variables) {
// 根据流程名称、流程发起人 查询流程配置的抄送人信息
List processCcList = processCcService.getCCListByName(instance.getName(), Long.valueOf(instance.getStartUserId()));
// 提取抄送信息用中对应的用户组编号
Set userGroupIds = processCcList.stream()
.flatMap(data -> data.getUserGroupId().stream())
.collect(Collectors.toSet());
//根据processDefinitionId 将ccIds保存到bpm_process_instance_ext中的ccIds字段
BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO()
.setProcessDefinitionId(instance.getProcessDefinitionId())
.setFormVariables(variables)
.setProcessInstanceId(instance.getProcessInstanceId());
if (CollectionUtil.isNotEmpty(userGroupIds)) {
// 获取用户组信息
List userGroups = userGroupService.getUserGroupList(userGroupIds);
// 提取用户组中对应的用户ID
Set userIds = userGroups.stream()
.flatMap(data -> data.getMemberUserIds().stream())
.collect(Collectors.toSet());
// 将用户ID列表转换为字符串
String ccIds = userIds.stream()
.map(id -> "[" + id + "]") // 每个值用方括号包裹
.collect(Collectors.joining());
//根据processDefinitionId 将ccIds保存到bpm_process_instance_ext中的ccIds字段
instanceExtDO.setCcids(ccIds);
}
processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO);
}
@Override
public List getUserProcessTpo10(BpmProcessInstanceStatisticsReqVO pageReqVO) {
//按审核人分组查询,统计已完成流程数量及平均耗时间
List respVOS = processInstanceExtMapper.getUserProcessTpo10(pageReqVO);
if (respVOS == null || respVOS.size() == 0) {
return null;
}
List idList = respVOS.stream()
.map(BpmProcessFinishStatisticsRespVO::getUserId)
.collect(Collectors.toList());
//根据userId,查询UserMap
Map userMap = adminUserApi.getUserMap(idList);
Long[] idsArray = new Long[userMap.size()];
// 使用 map 的 keySet() 方法获取键集合
Set keys = userMap.keySet();
// 遍历键集合,将每个键添加到数组中
int index = 0;
for (Long key : keys) {
idsArray[index++] = key;
}
pageReqVO.setUserIds(idsArray);
//按审核人分组查询,未审批完成的流程数量
List bpmTaskExtDOs = processInstanceExtMapper.selectUnfinishProcessCount(pageReqVO);
//按审核人分组查询,未完成的记录数
Map unFinfishCountCountMap = new HashMap<>();
for (BpmProcessFinishStatisticsRespVO item : bpmTaskExtDOs) {
unFinfishCountCountMap.put(item.getUserId(), item.getUnFinfishCount());
}
respVOS.forEach(respVO -> respVO.setName(userMap.get(respVO.getUserId()).getNickname()));
respVOS.forEach(respVO -> respVO.setUnFinfishCount(unFinfishCountCountMap.get(respVO.getUserId())));
//获取排行榜第一记录的耗时,作为基础数据
double num = Double.parseDouble(respVOS.get(0).getUserTime()); // 先将字符串转换为双精度浮点数
int baseNumber = (int) num; // 再通过类型转换操作符将其转换为整数
DecimalFormat df = new DecimalFormat("#.00");
respVOS.forEach(respVO -> {
//格式化流程完成率
float finfishCount = (float) respVO.getFinfishCount();
float unFinfishCount = respVO.getUnFinfishCount() == null ? 0 : respVO.getUnFinfishCount();
float all = finfishCount + unFinfishCount;
float result = finfishCount / all;
float rate = result * 100;
respVO.setCompletionRate(df.format(rate) + "%");
double dValue = Double.parseDouble(respVO.getUserTime()); // 将字符串转换为double类型
int roundedValue = (int) Math.round(dValue); // 使用Math.round()进行四舍五入,并转换为int类型
respVO.setUserTime(roundedValue + "");
//格式化进度百度比, 参照最高的数据进行百分比显示
double percentage = ((int) Double.parseDouble(respVO.getUserTime()) / (double) baseNumber) * 100;
respVO.setPercentage((int) Math.round(percentage));
//设置未完成
respVO.setUnFinfishCount(Integer.valueOf((int) unFinfishCount));
});
return respVOS;
}
@Override
@DataPermission(enable = false)
public BpmProcessInstanceExtDO getProcessInstanceDO(String id) {
return processInstanceExtMapper.selectByProcessInstanceId(id);
}
@Resource
private FileApi fileApi;
@Resource
@Lazy // 解决循环依赖
private BpmOAReimbursementService reimbursementService;
@Resource
@Lazy // 解决循环依赖
private BpmOACashService cashService;
@Resource
@Lazy // 解决循环依赖
private BpmOAImprestService imprestService;
@Resource
@Lazy // 解决循环依赖
private BpmOAPaymentService paymentService;
@Override
public BpmProcessInstancePrintDataRespVO getOAReportPrintData(BpmProcessInstancePrintDataReqVO reqVO) {
BpmProcessInstancePrintDataRespVO bpmProcessInstancePrintDataRespVO = new BpmProcessInstancePrintDataRespVO();
String key = reqVO.getKey(); //流程标识
String processInstanceId = reqVO.getId(); //流程实例ID
bpmProcessInstancePrintDataRespVO.setId(processInstanceId);
bpmProcessInstancePrintDataRespVO.setKey(key);
BpmProcessInstanceRespVO bpmProcessInstance = getProcessInstanceVO(processInstanceId);
Long businessKey = Long.valueOf(bpmProcessInstance.getBusinessKey()); //流程业务表-主键ID
// 判断是否可见外勤人员信息
List userIds = new ArrayList<>();
String type = configApi.getConfigKey("sys.user.type").getCheckedData();
if ("1".equals(type)) { //不可见时
// 获取所有外勤人员 用户编号
userIds = adminUserApi.getUserIdsByUserNature(4).getCheckedData();
}
// 获得流程审批信息
List taskRespVOList = taskService.getTaskListByProcessInstanceId(processInstanceId);
// 移除外勤人员信息
List finalUserIds = userIds;
taskRespVOList.removeIf(data -> finalUserIds.contains(data.getAssigneeUser().getId()));
//给User添加签名地址
taskRespVOList.forEach(taskRespVO ->
taskRespVO.getAssigneeUser().setSignURL(
fileApi.getUserSignImgPath(
taskRespVO.getAssigneeUser().getId()
).getData()
)
);
//获取流程抄送用户编号
BpmProcessInstanceExtDO processInstanceExtDO = getProcessInstanceDO(processInstanceId);
//获取流程抄送用户信息
List ccUserIds = new ArrayList<>();
if (processInstanceExtDO.getCcids() != null && !processInstanceExtDO.getCcids().isEmpty()) {
Pattern pattern = Pattern.compile("\\[(\\d+)]");
Matcher matcher = pattern.matcher(processInstanceExtDO.getCcids());
while (matcher.find()) {
ccUserIds.add(Long.parseLong(matcher.group(1)));
}
// 移除 外勤人员信息
ccUserIds.removeAll(userIds);
}
List userRespDTOS = adminUserApi.getUserList(ccUserIds).getCheckedData();
//获取抄送用户部门信息
Map deptMapDTO = deptApi.getDeptMap(convertSet(userRespDTOS, AdminUserRespDTO::getDeptId));
// 设置抄送人信息
List ccUsers = userRespDTOS.stream().map(user -> {
BpmOAPrintDataRespVO.CCUser cc = new BpmOAPrintDataRespVO.CCUser();
cc.setCcUserId(user.getId());
cc.setCcUserName(user.getNickname());
cc.setCcDeptName(deptMapDTO.get(user.getDeptId()).getName());
return cc;
}).collect(Collectors.toList());
BpmOAPrintDataRespVO printData = new BpmOAPrintDataRespVO();
// 设置抄送人
printData.setCcUsers(ccUsers);
// 设置发起人信息
printData.setStartUser(BeanUtils.toBean(bpmProcessInstance.getStartUser(), BpmOAPrintDataRespVO.User.class));
switch (key) {
case "oa_reimbursement":
BpmOAReimbursementDO reimbursement = reimbursementService.getReimbursement(businessKey);
BpmOAReimbursementRespVO bpmOAReimbursementRespVO = reimbursementService.convert(reimbursement); //报销业务数据
// 移除自己得审批节点
taskRespVOList.removeIf(data -> data.getAssigneeUser().getId().equals(reimbursement.getUserId()));
//备用金信息查询
BpmOAImprestDO bpmOAImprestDO = imprestService.getImprest(reimbursement.getImprestId());
if (bpmOAImprestDO != null) {
//设置备用金 金额
bpmOAReimbursementRespVO.setAmount(bpmOAImprestDO.getAmount());
// 设置备用金 剩余金额
bpmOAReimbursementRespVO.setRemainingAmount(bpmOAImprestDO.getAmount().subtract(bpmOAImprestDO.getReimbursedAmount()));
}
printData.setBpmOAReimbursementRespVO(bpmOAReimbursementRespVO);
printData.setProcessTasks(taskRespVOList);
break;
case "oa_cash":
BpmOACashDO cashDO = cashService.getCash(businessKey);
BpmOACashRespVO cashRespVO = cashService.convertCash(cashDO);
// 移除自己得审批节点
taskRespVOList.removeIf(data -> data.getAssigneeUser().getId().equals(cashDO.getUserId()));
//备用金信息查询
BpmOAImprestDO cashImprestDO = imprestService.getImprest(cashDO.getImprestId());
if (cashImprestDO != null) {
//设置备用金 金额
cashRespVO.setAmount(cashImprestDO.getAmount());
// 设置备用金 剩余金额
cashRespVO.setRemainingAmount(cashImprestDO.getAmount().subtract(cashImprestDO.getReimbursedAmount()));
}
printData.setBpmOACashRespVO(cashRespVO);
printData.setProcessTasks(taskRespVOList);
break;
case "oa_payment_2":
BpmOAPaymentDO paymentDO = paymentService.getPayment(businessKey);
BpmOAPaymentRespVO paymentRespVO = paymentService.convertPayment(paymentDO);
// 移除自己得审批节点
taskRespVOList.removeIf(data -> data.getAssigneeUser().getId().equals(paymentDO.getUserId()));
printData.setBpmOAPaymentRespVO(paymentRespVO);
printData.setProcessTasks(taskRespVOList);
break;
}
bpmProcessInstancePrintDataRespVO.setPrintDataRespVO(printData);
return bpmProcessInstancePrintDataRespVO;
}
@Override
public Map> getProcessInstanceResultStatusStatisticsGroupTime(BpmProcessInstanceStatisticsReqVO pageReqVO) {
pageReqVO = getUserids(pageReqVO);
pageReqVO.setRecentDays(pageReqVO.getRecentDays() == null ? 7 : pageReqVO.getRecentDays());
List list = processInstanceExtMapper.getProcessInstanceResultStatusStatisticsGroupTime(pageReqVO);
// -- 获取到日期列表
LocalDateTime now = LocalDateTimeUtil.now();
LocalDateTime offset = LocalDateTimeUtil.offset(LocalDateTimeUtil.now(), -1L * pageReqVO.getRecentDays(), ChronoUnit.DAYS);
List dateList = DateUtils.betweenDayList(offset, now);
//根据时间分组
Map> map = list.stream().collect(Collectors.groupingBy(BpmProcessInstanceResultStatusStatisticsGroupTimeVO::getTime));
List keys = new ArrayList<>(CollectionUtil.subtract(dateList, new ArrayList<>(map.keySet())));
if (CollectionUtil.isNotEmpty(keys)) {
for (String key : keys) {
map.put(key, new ArrayList<>());
}
}
List statusList = Arrays.asList(1, 2, 3, 4);
for (Map.Entry> entry : map.entrySet()) {
List items = entry.getValue();
List results = items.stream().map(BpmProcessInstanceResultStatusStatisticsGroupTimeVO::getResult).collect(Collectors.toList());
List saveList = new ArrayList<>(CollectionUtil.subtract(statusList, results));
// -- 如果没有的话组装上缺少的
if (CollectionUtil.isNotEmpty(saveList)) {
for (Integer status : saveList) {
items.add(new BpmProcessInstanceResultStatusStatisticsGroupTimeVO()
.setTime(entry.getKey())
.setTotalCount(0)
.setResult(status)
.setName(""));
}
}
}
return map;
}
}
\ No newline at end of file
+package cn.iocoder.yudao.module.bpm.service.task;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
import cn.iocoder.yudao.framework.flowable.core.util.FlowableUtils;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.cash.BpmOACashRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.payment.BpmOAPaymentRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.print.BpmOAPrintDataRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.print.BpmProcessInstancePrintDataReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.print.BpmProcessInstancePrintDataRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.reimbursement.BpmOAReimbursementRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskApproveReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;
import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessCcDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionExtDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmTaskAssignRuleDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmUserGroupDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOACashDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAImprestDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAPaymentDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAReimbursementDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceExtDO;
import cn.iocoder.yudao.module.bpm.dal.mysql.DynamicMapper;
import cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmTaskAssignRuleMapper;
import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmProcessInstanceExtMapper;
import cn.iocoder.yudao.module.bpm.enums.task.BpmConstants;
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceDeleteReasonEnum;
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum;
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum;
import cn.iocoder.yudao.module.bpm.framework.bpm.core.event.BpmProcessInstanceResultEventPublisher;
import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessCcService;
import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
import cn.iocoder.yudao.module.bpm.service.definition.BpmUserGroupService;
import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService;
import cn.iocoder.yudao.module.bpm.service.oa.*;
import cn.iocoder.yudao.module.infra.api.config.ConfigApi;
import cn.iocoder.yudao.module.infra.api.file.FileApi;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
import cn.iocoder.yudao.module.system.api.permission.dto.DeptDataPermissionRespDTO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import jodd.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.delegate.event.FlowableCancelledEvent;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.lang.reflect.Field;
import java.text.DecimalFormat;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
/**
* 流程实例 Service 实现类
*
* ProcessDefinition & ProcessInstance & Execution & Task 的关系:
* 1.
*
* HistoricProcessInstance & ProcessInstance 的关系:
* 1.
*
* 简单来说,前者 = 历史 + 运行中的流程实例,后者仅是运行中的流程实例
*/
@Service
@Validated
@Slf4j
public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService {
@Resource
private TaskService engineTaskService;
@Resource
private BpmTaskAssignRuleMapper taskRuleMapper;
@Resource
private BpmUserGroupService userGroupService;
@Resource
private RuntimeService runtimeService;
@Resource
private BpmProcessInstanceExtMapper processInstanceExtMapper;
@Resource
@Lazy // 解决循环依赖
private BpmTaskService taskService;
@Resource
private BpmProcessDefinitionService processDefinitionService;
@Resource
private HistoryService historyService;
@Resource
private AdminUserApi adminUserApi;
@Resource
private DeptApi deptApi;
@Resource
private BpmProcessInstanceResultEventPublisher processInstanceResultEventPublisher;
@Resource
@Lazy // 解决循环依赖
private BpmMessageService messageService;
@Resource
private ConfigApi configApi;
@Resource
private DynamicMapper dynamicMapper ;
@Resource
private BpmProcessCcService processCcService;
@Resource
@Lazy // 解决循环依赖
private StringRedisTemplate stringRedisTemplate;
@Override
public ProcessInstance getProcessInstance(String id) {
return runtimeService.createProcessInstanceQuery().processInstanceId(id).singleResult();
}
@Override
public List getProcessInstances(Set ids) {
return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).list();
}
public String transform(String input) {
if(StringUtil.isNotEmpty(input)) {
String[] parts = input.split(",");
StringBuilder result = new StringBuilder();
for (String part : parts) {
String[] keyValue = part.split(":");
if (keyValue.length > 1) {
result.append(keyValue[1]).append(",");
}
}
if (result.length() > 0) {
return result.substring(0, result.length() - 1);
}
}
return "";
}
public String replaceValues(String input, Map map) {
String[] parts = input.split(",");
StringBuilder result = new StringBuilder();
for (String part : parts) {
String[] keyValue = part.split(":");
if (keyValue.length > 1) {
String key = keyValue[1].trim(); // 去除可能的空格
if (map.containsKey(key)) {
Object value = map.get(key);
String stringValue = (value != null) ? value.toString() : ""; // 将值转换为字符串
result.append(keyValue[0].trim()).append(":").append(stringValue).append(",");
} else {
//result.append(part).append(",");
}
}
}
if (result.length() > 0) {
return result.substring(0, result.length() - 1);
}
return "";
}
@Override
@TenantIgnore
public PageResult getMyProcessInstancePage(Long userId,
BpmProcessInstanceMyPageReqVO pageReqVO) {
// 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页
PageResult pageResult = processInstanceExtMapper.selectPage(userId, pageReqVO);
List bpmProcessInstanceExtDOList = pageResult.getList() ;
for (int i = 0; i < bpmProcessInstanceExtDOList.size() ; i++) {
BpmProcessInstanceExtDO bpmProcessInstanceExtDO = bpmProcessInstanceExtDOList.get(i) ;
String input = bpmProcessInstanceExtDO.getFieldNames() ;
if(StringUtil.isNotEmpty(input)) {
String sql = "SELECT " + transform(bpmProcessInstanceExtDO.getFieldNames()) + " FROM " + bpmProcessInstanceExtDO.getTableName() + " WHERE id=" + bpmProcessInstanceExtDO.getBusinessKey();
log.info(sql);
Map map = dynamicMapper.selectListByTableName(sql) ;
String detailInfo = "";
if(map != null) {
detailInfo = replaceValues(bpmProcessInstanceExtDO.getFieldNames(),map) ;
}
bpmProcessInstanceExtDO.setDetailInfo(detailInfo);
}
}
if (CollUtil.isEmpty(pageResult.getList())) {
return new PageResult<>(pageResult.getTotal());
}
// 获得流程 Task Map
List processInstanceIds = convertList(pageResult.getList(), BpmProcessInstanceExtDO::getProcessInstanceId);
Map> taskMap = taskService.getTaskMapByProcessInstanceIds(processInstanceIds);
List ids = taskMap.values().stream()
.flatMap(Collection::stream)
.map(Task::getAssignee)
.collect(Collectors.toList());
Iterator iterator = ids.iterator();
while (iterator.hasNext()) {
String id = iterator.next();
if (id == null) {
iterator.remove();
}
}
List longIds = ids.stream()
.map(Long::valueOf)
.collect(Collectors.toList());
// 获得 User Map
Map userMap = adminUserApi.getUserMap(longIds);
// 转换返回
return BpmProcessInstanceConvert.INSTANCE.convertPage(pageResult, taskMap, userMap);
}
@Override
@Transactional(rollbackFor = Exception.class)
public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO) {
// 获得流程定义
ProcessDefinition definition = processDefinitionService.getProcessDefinition(createReqVO.getProcessDefinitionId());
// 发起流程
if (MapUtil.isEmpty(createReqVO.getVariables())){
createReqVO.setVariables(new HashMap<>());
}
return createProcessInstance0(userId, definition, createReqVO.getVariables(), null);
}
@Override
@Transactional(rollbackFor = Exception.class)
public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) {
// 获得流程定义
ProcessDefinition definition = processDefinitionService.getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey());
// 发起流程
if (MapUtil.isEmpty(createReqDTO.getVariables())){
createReqDTO.setVariables(new HashMap<>());
}
return createProcessInstance0(userId, definition, createReqDTO.getVariables(), createReqDTO.getBusinessKey());
}
@Override
public BpmProcessInstanceRespVO getProcessInstanceVO(String id) {
// 获得流程实例
HistoricProcessInstance processInstance = getHistoricProcessInstance(id);
if (processInstance == null) {
return null;
}
BpmProcessInstanceExtDO processInstanceExt = processInstanceExtMapper.selectByProcessInstanceId(id);
Assert.notNull(processInstanceExt, "流程实例拓展({}) 不存在", id);
// 获得流程定义
ProcessDefinition processDefinition = processDefinitionService
.getProcessDefinition(processInstance.getProcessDefinitionId());
Assert.notNull(processDefinition, "流程定义({}) 不存在", processInstance.getProcessDefinitionId());
BpmProcessDefinitionExtDO processDefinitionExt = processDefinitionService.getProcessDefinitionExt(
processInstance.getProcessDefinitionId());
Assert.notNull(processDefinitionExt, "流程定义拓展({}) 不存在", id);
String bpmnXml = processDefinitionService.getProcessDefinitionBpmnXML(processInstance.getProcessDefinitionId());
// 获得 User
AdminUserRespDTO startUser = adminUserApi.getUser(NumberUtils.parseLong(processInstance.getStartUserId())).getCheckedData();
DeptRespDTO dept = null;
DeptRespDTO companyDept = null;
if (startUser != null) {
dept = deptApi.getDept(startUser.getDeptId()).getCheckedData();
companyDept = deptApi.getUserCompanyDept(startUser.getId()).getCheckedData();
}
// 拼接结果
return BpmProcessInstanceConvert.INSTANCE.convert2(processInstance, processInstanceExt,
processDefinition, processDefinitionExt, bpmnXml, startUser, dept, companyDept);
}
@Override
public void cancelProcessInstance(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) {
// 校验流程实例存在
ProcessInstance instance = getProcessInstance(cancelReqVO.getId());
if (instance == null) {
throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS);
}
// 只能取消自己的
if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) {
throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF);
}
// 通过删除流程实例,实现流程实例的取消,
// 删除流程实例,正则执行任务 ACT_RU_TASK. 任务会被删除。通过历史表查询
deleteProcessInstance(cancelReqVO.getId(),
BpmProcessInstanceDeleteReasonEnum.CANCEL_TASK.format(cancelReqVO.getReason()));
}
/**
* 获得历史的流程实例
*
* @param id 流程实例的编号
* @return 历史的流程实例
*/
@Override
public HistoricProcessInstance getHistoricProcessInstance(String id) {
return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).singleResult();
}
@Override
public List getHistoricProcessInstances(Set ids) {
return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).list();
}
@Override
public void createProcessInstanceExt(ProcessInstance instance) {
// 获得流程定义
ProcessDefinition definition = processDefinitionService.getProcessDefinition2(instance.getProcessDefinitionId());
// 插入 BpmProcessInstanceExtDO 对象
BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO()
.setProcessInstanceId(instance.getId())
.setProcessDefinitionId(definition.getId())
.setName(instance.getProcessDefinitionName())
.setStartUserId(Long.valueOf(instance.getStartUserId()))
.setCategory(definition.getCategory())
.setStatus(BpmProcessInstanceStatusEnum.RUNNING.getStatus())
.setResult(BpmProcessInstanceResultEnum.PROCESS.getResult());
processInstanceExtMapper.insert(instanceExtDO);
}
@Override
@DataPermission(enable = false)
public void updateProcessInstanceExtCancel(FlowableCancelledEvent event) {
// 判断是否为 Reject 不通过。如果是,则不进行更新.
// 因为,updateProcessInstanceExtReject 方法,已经进行更新了
if (BpmProcessInstanceDeleteReasonEnum.isRejectReason((String) event.getCause())) {
return;
}
// 需要主动查询,因为 instance 只有 id 属性
// 另外,此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance
HistoricProcessInstance processInstance = getHistoricProcessInstance(event.getProcessInstanceId());
// 更新拓展表
BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO()
.setProcessInstanceId(event.getProcessInstanceId())
.setEndTime(LocalDateTime.now()) // 由于 ProcessInstance 里没有办法拿到 endTime,所以这里设置
.setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus())
.setResult(BpmProcessInstanceResultEnum.CANCEL.getResult());
processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO);
// 删除 流程审批部门缓存
updateAssigneeDeptRedis(event.getProcessInstanceId());
// 发送流程实例的状态事件
processInstanceResultEventPublisher.sendProcessInstanceResultEvent(
BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult()));
}
@Override
@DataPermission(enable = false)
public void updateProcessInstanceExtComplete(ProcessInstance instance) {
// 需要主动查询,因为 instance 只有 id 属性
// 另外,此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance
HistoricProcessInstance processInstance = getHistoricProcessInstance(instance.getId());
// 更新拓展表
BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO()
.setProcessInstanceId(instance.getProcessInstanceId())
.setEndTime(LocalDateTime.now()) // 由于 ProcessInstance 里没有办法拿到 endTime,所以这里设置
.setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus())
.setResult(BpmProcessInstanceResultEnum.APPROVE.getResult()); // 如果正常完全,说明审批通过
processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO);
// 删除 流程审批部门缓存
updateAssigneeDeptRedis(instance.getProcessInstanceId());
Map processVariables = runtimeService.getVariables(instance.getProcessInstanceId());
String reason = (String) processVariables.get("approve_reason");
// 发送流程被通过的消息
messageService.sendMessageWhenProcessInstanceApprove(BpmProcessInstanceConvert.INSTANCE.convert2ApprovedReq(instance, reason));
// 发送流程实例的状态事件
processInstanceResultEventPublisher.sendProcessInstanceResultEvent(
BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult()));
}
@Override
@Transactional(rollbackFor = Exception.class)
@DataPermission(enable = false)
public void updateProcessInstanceExtReject(String id, String reason) {
// 需要主动查询,因为 instance 只有 id 属性
ProcessInstance processInstance = getProcessInstance(id);
// 删除流程实例,以实现驳回任务时,取消整个审批流程
deleteProcessInstance(id, StrUtil.format(BpmProcessInstanceDeleteReasonEnum.REJECT_TASK.format(reason)));
// 更新 status + result
// 注意,不能和上面的逻辑更换位置。因为 deleteProcessInstance 会触发流程的取消,进而调用 updateProcessInstanceExtCancel 方法,
// 设置 result 为 BpmProcessInstanceStatusEnum.CANCEL,显然和 result 不一定是一致的
BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO()
.setProcessInstanceId(id)
.setEndTime(LocalDateTime.now()) // 由于 ProcessInstance 里没有办法拿到 endTime,所以这里设置
.setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus())
.setResult(BpmProcessInstanceResultEnum.REJECT.getResult());
processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO);
// 删除 流程审批部门缓存
updateAssigneeDeptRedis(id);
// 发送流程被不通过的消息
messageService.sendMessageWhenProcessInstanceReject(BpmProcessInstanceConvert.INSTANCE.convert2RejectReq(processInstance, reason));
// 发送流程实例的状态事件
processInstanceResultEventPublisher.sendProcessInstanceResultEvent(
BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult()));
}
@Override
public void updateProcessInstanceResult(String id, Integer result) {
processInstanceExtMapper.update(null, new LambdaUpdateWrapper()
.set(BpmProcessInstanceExtDO::getResult, result)
.eq(BpmProcessInstanceExtDO::getProcessInstanceId, id));
}
/**
* 删除 流程审批部门缓存
* @param processInstanceId 流程实例id
*/
public void updateAssigneeDeptRedis(String processInstanceId) {
stringRedisTemplate.delete("assignee_dept_" + processInstanceId);
}
private void deleteProcessInstance(String id, String reason) {
runtimeService.deleteProcessInstance(id, reason);
}
private String createProcessInstance0(Long userId, ProcessDefinition definition,
Map variables, String businessKey) {
// 校验流程定义
if (definition == null) {
throw exception(PROCESS_DEFINITION_NOT_EXISTS);
}
if (definition.isSuspended()) {
throw exception(PROCESS_DEFINITION_IS_SUSPENDED);
}
// 设置当前用户
FlowableUtils.setAuthenticatedUserId(userId);
//获取流程发起人User
AdminUserRespDTO startUser = adminUserApi.getUser(userId).getCheckedData();
//获取流程发起人部门信息
DeptRespDTO startDeptInfo = deptApi.getDept(startUser.getDeptId()).getCheckedData();
// 获取岗位信息
Set postIds = startUser.getPostIds();
ArrayList list = new ArrayList<>(postIds);
//配置通用流程变量
variables.put("post_id", list.get(0).toString()); // 只获配置的首个岗位
variables.put("user_id", userId.toString()); // 配置发起人用户id
variables.put("dept_id", startUser.getDeptId().toString()); // 配置发起人部门id
variables.put("dept_flag", startDeptInfo.getFlag()); // 配置发起人部门flag
// 创建流程实例
ProcessInstance instance = runtimeService.createProcessInstanceBuilder()
.processDefinitionId(definition.getId())
.businessKey(businessKey)
.name(definition.getName().trim())
.variables(variables)
.start();
// 设置流程名字
runtimeService.setProcessInstanceName(instance.getId(), definition.getName());
// 设置流程审批部门缓存
stringRedisTemplate.opsForValue().set("assignee_dept_" + instance.getId(), startUser.getDeptId().toString());
// 在事务提交时,批量执行操作,所以直接查询会无法查询到 ProcessInstance,所以这里是通过监听事务的提交来实现。
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
@Override
public void afterCommit() {
// 创建流程后,添加抄送人
processCCToUsers(instance, variables);
}
});
return instance.getId();
// // 补全流程实例的拓展表
// processInstanceExtMapper.updateByProcessInstanceId(new BpmProcessInstanceExtDO().setProcessInstanceId(instance.getId())
// .setFormVariables(variables));
//
// /** 创建流程后,添加抄送人 End add by yj 2024.1.4 */
// processCCToUsers(definition, instance);
// /** 通过自己发起的流程 */
// approveSelfTask(instance.getId()) ;
}
private void approveSelfTask(String processInstanceId) {
List tasks = engineTaskService.createTaskQuery().processInstanceId(processInstanceId).list();
if (tasks != null && tasks.size() > 0) {
Task task = tasks.get(0);
String assigneeId = task.getAssignee();
//如果当前登陆用户是审批人,那么自动审批通过
if (assigneeId.equals(SecurityFrameworkUtils.getLoginUserId().toString())) {
BpmTaskApproveReqVO reqVO = new BpmTaskApproveReqVO();
reqVO.setId(task.getId());
reqVO.setReason(BpmConstants.AUTO_APPRAVAL);
taskService.approveTask(getLoginUserId(), reqVO);
}
}
}
/**
* 获得抄送我的流程实例的分页
*
* @param userId 用户编号
* @param pageReqVO 分页请求
* @return 流程实例的分页
*/
public PageResult getMyCCProcessInstancePage(Long userId,
BpmProcessInstanceMyPageReqVO pageReqVO) {
// 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页
PageResult pageResult = processInstanceExtMapper.selectCCPage(userId, pageReqVO);
if (CollUtil.isEmpty(pageResult.getList())) {
return new PageResult<>(pageResult.getTotal());
}
// 获得流程 Task Map
List processInstanceIds = convertList(pageResult.getList(), BpmProcessInstanceExtDO::getProcessInstanceId);
Map> taskMap = taskService.getTaskMapByProcessInstanceIds(processInstanceIds);
// 转换返回
return BpmProcessInstanceConvert.INSTANCE.convertPage(pageResult, taskMap, null);
}
@Override
public List getProcessInstancesGroupByModelName(BpmProcessInstanceStatisticsReqVO pageReqVO) {
pageReqVO = getUserids(pageReqVO);
return processInstanceExtMapper.getProcessInstancesGroupByModelName(pageReqVO);
}
@Override
public List getProcessInstancesGroupByResultStatus(BpmProcessInstanceStatisticsReqVO pageReqVO) {
pageReqVO = getUserids(pageReqVO);
return processInstanceExtMapper.getProcessInstancesGroupByResultStatus(pageReqVO);
}
@Override
public PageResult getStatisticsProcessInstancePage(@Valid BpmProcessInstanceMyPageReqVO pageReqVO) {
// 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页
PageResult pageResult = processInstanceExtMapper.selectStatisticePage(pageReqVO);
if (CollUtil.isEmpty(pageResult.getList())) {
return new PageResult<>(pageResult.getTotal());
}
// 获得流程 Task Map
List processInstanceIds = convertList(pageResult.getList(), BpmProcessInstanceExtDO::getProcessInstanceId);
Map> taskMap = taskService.getTaskMapByProcessInstanceIds(processInstanceIds);
List ids = taskMap.values().stream()
.flatMap(Collection::stream)
.map(Task::getAssignee)
.collect(Collectors.toList());
Iterator iterator = ids.iterator();
while (iterator.hasNext()) {
String id = iterator.next();
if (id == null) {
iterator.remove();
}
}
List longIds = ids.stream()
.map(Long::valueOf)
.collect(Collectors.toList());
//获得 审批人 User Map
Map assigneeUserMap = adminUserApi.getUserMap(longIds);
//获得 发起人 User Map
List bpieDOs = pageResult.getList();
longIds = new ArrayList<>();
for (BpmProcessInstanceExtDO bpieDO : bpieDOs) {
longIds.add(bpieDO.getStartUserId());
}
Map startUserMap = adminUserApi.getUserMap(longIds);
// 转换返回
return BpmProcessInstanceConvert.INSTANCE.convertStatisticsPage(pageResult, taskMap, assigneeUserMap, startUserMap);
}
@Resource
PermissionApi permissionApi;
/**
* 根据数据权限,查询关联操作的用户IDS
*
* @param pageReqVO
* @param
* @return
*/
private T getUserids(T pageReqVO) {
try {
Class clazz = (Class) pageReqVO.getClass();
Field idField = clazz.getDeclaredField("userIds");
idField.setAccessible(true); // 设置可访问性
Long[] userIds = null;
Long userId = WebFrameworkUtils.getLoginUserId();
DeptDataPermissionRespDTO deptDataPermission = permissionApi.getDeptDataPermission(userId).getCheckedData();
//查询全部
if (deptDataPermission.getAll()) {
//idField.set(pageReqVO, null); // 设置属性值
return pageReqVO;
}
// 情况二,即不能查看部门,又不能查看自己,则说明 100% 无权限
if (CollUtil.isEmpty(deptDataPermission.getDeptIds())
&& Boolean.FALSE.equals(deptDataPermission.getSelf())) {
//设置成0,一个不存在的用户Id,就查询不到数据了。
userIds = new Long[]{0L};
idField.set(pageReqVO, userIds);
return pageReqVO;
}
//情况三 至查询自己
if (deptDataPermission.getSelf()) {
userIds = new Long[]{userId};
idField.set(pageReqVO, userIds);
return pageReqVO;
}
Set deptIds = deptDataPermission.getDeptIds();
//查询部门关联的用户Id
List users = adminUserApi.getUserListByDeptIds(deptIds).getCheckedData();
List tempList = new ArrayList<>();
for (AdminUserRespDTO user : users) {
Long id = user.getId();
tempList.add(id);
}
tempList.add(userId);
userIds = tempList.stream().toArray(Long[]::new); //将临时的List集合转换成数组集合
idField.set(pageReqVO, userIds);
return pageReqVO;
} catch (Exception exception) {
exception.printStackTrace();
throw exception(BPM_SYSTEM_BUG);
}
}
/**
* /创建流程后,添加抄送人 Begin add by yj 2024.1.4
* 在设计流程的时候,需要添加一个任务块 名字必须叫Activity_cc 分配权限的时候,需要选择用户组。
*
* @param definition
* @param instance
*/
private void processCCToUsers(ProcessDefinition definition, ProcessInstance instance) {
//获取bpm_task_assign_reule (Bpm 任务规则表)的流程中有没有配置抄送节点 固定抄送名称为:Activity_cc
String processDefinitionId = definition.getId();
List rules = taskRuleMapper.selectListByProcessDefinitionId(processDefinitionId, null);
String BpmConstantsName = BpmConstants.CC_NAME;
// 获取发起人信息
AdminUserRespDTO userRespDTO = adminUserApi.getUser(Long.valueOf(instance.getStartUserId())).getCheckedData();
DeptRespDTO deptRespDTO = deptApi.getDept(userRespDTO.getDeptId()).getCheckedData();
//发起人是深圳分公司
if (deptRespDTO.getFlag().contains("136")) {
BpmConstantsName = BpmConstants.CCSZ_NAME;
}
for (BpmTaskAssignRuleDO rule : rules) {
String key = rule.getTaskDefinitionKey(); //任务名称
Integer type = rule.getType();
if (!key.isEmpty() && key.equals(BpmConstantsName) && type == 40) {
StringBuffer str = new StringBuffer();
Set options = rule.getOptions();
List list = new ArrayList(options);
for (Long groupId : list) {
//需要根据这个groupId,查询这个组中的用户id
BpmUserGroupDO userGroup = userGroupService.getUserGroup(groupId);
Set userIds = userGroup.getMemberUserIds();
List userIdList = new ArrayList(userIds);
for (Long user_id : userIdList) {
str.append("[").append(user_id).append("]");
}
}
//流程是采购计划时
if (definition.getKey().equals(BpmOAProcureServiceImpl.PROCESS_KEY)) {
//发起人部门为 生产部及以下时
if (deptRespDTO.getFlag().contains("130")) {
//添加 供应部抄送
BpmUserGroupDO userGroup = userGroupService.getUserGroup(121L);
Set userIds = userGroup.getMemberUserIds();
List userIdList = new ArrayList(userIds);
for (Long user_id : userIdList) {
str.append("[").append(user_id).append("]");
}
}
}
String ccids = str.toString();
//根据processDefinitionId 将ccids保存到bpm_process_instance_ext中的ccids字段
BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO().setProcessDefinitionId(processDefinitionId)
.setCcids(ccids).setProcessInstanceId(instance.getProcessInstanceId());
processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO);
break;
}
}
}
/**
* 创建流程后,添加抄送人
* @param instance 流程实例
*/
private void processCCToUsers(ProcessInstance instance, Map variables) {
// 根据流程名称、流程发起人 查询流程配置的抄送人信息
List processCcList = processCcService.getCCListByName(instance.getName(), Long.valueOf(instance.getStartUserId()));
// 提取抄送信息用中对应的用户组编号
Set userGroupIds = processCcList.stream()
.flatMap(data -> data.getUserGroupId().stream())
.collect(Collectors.toSet());
//根据processDefinitionId 将ccIds保存到bpm_process_instance_ext中的ccIds字段
BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO()
.setProcessDefinitionId(instance.getProcessDefinitionId())
.setFormVariables(variables)
.setProcessInstanceId(instance.getProcessInstanceId());
if (CollectionUtil.isNotEmpty(userGroupIds)) {
// 获取用户组信息
List userGroups = userGroupService.getUserGroupList(userGroupIds);
// 提取用户组中对应的用户ID
Set userIds = userGroups.stream()
.flatMap(data -> data.getMemberUserIds().stream())
.collect(Collectors.toSet());
// 将用户ID列表转换为字符串
String ccIds = userIds.stream()
.map(id -> "[" + id + "]") // 每个值用方括号包裹
.collect(Collectors.joining());
//根据processDefinitionId 将ccIds保存到bpm_process_instance_ext中的ccIds字段
instanceExtDO.setCcids(ccIds);
}
processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO);
}
@Override
public List getUserProcessTpo10(BpmProcessInstanceStatisticsReqVO pageReqVO) {
//按审核人分组查询,统计已完成流程数量及平均耗时间
List respVOS = processInstanceExtMapper.getUserProcessTpo10(pageReqVO);
if (respVOS == null || respVOS.size() == 0) {
return null;
}
List idList = respVOS.stream()
.map(BpmProcessFinishStatisticsRespVO::getUserId)
.collect(Collectors.toList());
//根据userId,查询UserMap
Map userMap = adminUserApi.getUserMap(idList);
Long[] idsArray = new Long[userMap.size()];
// 使用 map 的 keySet() 方法获取键集合
Set keys = userMap.keySet();
// 遍历键集合,将每个键添加到数组中
int index = 0;
for (Long key : keys) {
idsArray[index++] = key;
}
pageReqVO.setUserIds(idsArray);
//按审核人分组查询,未审批完成的流程数量
List bpmTaskExtDOs = processInstanceExtMapper.selectUnfinishProcessCount(pageReqVO);
//按审核人分组查询,未完成的记录数
Map unFinfishCountCountMap = new HashMap<>();
for (BpmProcessFinishStatisticsRespVO item : bpmTaskExtDOs) {
unFinfishCountCountMap.put(item.getUserId(), item.getUnFinfishCount());
}
respVOS.forEach(respVO -> respVO.setName(userMap.get(respVO.getUserId()).getNickname()));
respVOS.forEach(respVO -> respVO.setUnFinfishCount(unFinfishCountCountMap.get(respVO.getUserId())));
//获取排行榜第一记录的耗时,作为基础数据
double num = Double.parseDouble(respVOS.get(0).getUserTime()); // 先将字符串转换为双精度浮点数
int baseNumber = (int) num; // 再通过类型转换操作符将其转换为整数
DecimalFormat df = new DecimalFormat("#.00");
respVOS.forEach(respVO -> {
//格式化流程完成率
float finfishCount = (float) respVO.getFinfishCount();
float unFinfishCount = respVO.getUnFinfishCount() == null ? 0 : respVO.getUnFinfishCount();
float all = finfishCount + unFinfishCount;
float result = finfishCount / all;
float rate = result * 100;
respVO.setCompletionRate(df.format(rate) + "%");
double dValue = Double.parseDouble(respVO.getUserTime()); // 将字符串转换为double类型
int roundedValue = (int) Math.round(dValue); // 使用Math.round()进行四舍五入,并转换为int类型
respVO.setUserTime(roundedValue + "");
//格式化进度百度比, 参照最高的数据进行百分比显示
double percentage = ((int) Double.parseDouble(respVO.getUserTime()) / (double) baseNumber) * 100;
respVO.setPercentage((int) Math.round(percentage));
//设置未完成
respVO.setUnFinfishCount(Integer.valueOf((int) unFinfishCount));
});
return respVOS;
}
@Override
@DataPermission(enable = false)
public BpmProcessInstanceExtDO getProcessInstanceDO(String id) {
return processInstanceExtMapper.selectByProcessInstanceId(id);
}
@Resource
private FileApi fileApi;
@Resource
@Lazy // 解决循环依赖
private BpmOAReimbursementService reimbursementService;
@Resource
@Lazy // 解决循环依赖
private BpmOACashService cashService;
@Resource
@Lazy // 解决循环依赖
private BpmOAImprestService imprestService;
@Resource
@Lazy // 解决循环依赖
private BpmOAPaymentService paymentService;
@Override
public BpmProcessInstancePrintDataRespVO getOAReportPrintData(BpmProcessInstancePrintDataReqVO reqVO) {
BpmProcessInstancePrintDataRespVO bpmProcessInstancePrintDataRespVO = new BpmProcessInstancePrintDataRespVO();
String key = reqVO.getKey(); //流程标识
String processInstanceId = reqVO.getId(); //流程实例ID
bpmProcessInstancePrintDataRespVO.setId(processInstanceId);
bpmProcessInstancePrintDataRespVO.setKey(key);
BpmProcessInstanceRespVO bpmProcessInstance = getProcessInstanceVO(processInstanceId);
Long businessKey = Long.valueOf(bpmProcessInstance.getBusinessKey()); //流程业务表-主键ID
// 判断是否可见外勤人员信息
List userIds = new ArrayList<>();
String type = configApi.getConfigKey("sys.user.type").getCheckedData();
if ("1".equals(type)) { //不可见时
// 获取所有外勤人员 用户编号
userIds = adminUserApi.getUserIdsByUserNature(4).getCheckedData();
}
// 获得流程审批信息
List taskRespVOList = taskService.getTaskListByProcessInstanceId(processInstanceId);
// 移除外勤人员信息
List finalUserIds = userIds;
taskRespVOList.removeIf(data -> finalUserIds.contains(data.getAssigneeUser().getId()));
//给User添加签名地址
taskRespVOList.forEach(taskRespVO ->
taskRespVO.getAssigneeUser().setSignURL(
fileApi.getUserSignImgPath(
taskRespVO.getAssigneeUser().getId()
).getData()
)
);
//获取流程抄送用户编号
BpmProcessInstanceExtDO processInstanceExtDO = getProcessInstanceDO(processInstanceId);
//获取流程抄送用户信息
List ccUserIds = new ArrayList<>();
if (processInstanceExtDO.getCcids() != null && !processInstanceExtDO.getCcids().isEmpty()) {
Pattern pattern = Pattern.compile("\\[(\\d+)]");
Matcher matcher = pattern.matcher(processInstanceExtDO.getCcids());
while (matcher.find()) {
ccUserIds.add(Long.parseLong(matcher.group(1)));
}
// 移除 外勤人员信息
ccUserIds.removeAll(userIds);
}
List userRespDTOS = adminUserApi.getUserList(ccUserIds).getCheckedData();
//获取抄送用户部门信息
Map deptMapDTO = deptApi.getDeptMap(convertSet(userRespDTOS, AdminUserRespDTO::getDeptId));
// 设置抄送人信息
List ccUsers = userRespDTOS.stream().map(user -> {
BpmOAPrintDataRespVO.CCUser cc = new BpmOAPrintDataRespVO.CCUser();
cc.setCcUserId(user.getId());
cc.setCcUserName(user.getNickname());
cc.setCcDeptName(deptMapDTO.get(user.getDeptId()).getName());
return cc;
}).collect(Collectors.toList());
BpmOAPrintDataRespVO printData = new BpmOAPrintDataRespVO();
// 设置抄送人
printData.setCcUsers(ccUsers);
// 设置发起人信息
printData.setStartUser(BeanUtils.toBean(bpmProcessInstance.getStartUser(), BpmOAPrintDataRespVO.User.class));
switch (key) {
case "oa_reimbursement":
BpmOAReimbursementDO reimbursement = reimbursementService.getReimbursement(businessKey);
BpmOAReimbursementRespVO bpmOAReimbursementRespVO = reimbursementService.convert(reimbursement); //报销业务数据
// 移除自己得审批节点
taskRespVOList.removeIf(data -> data.getAssigneeUser().getId().equals(reimbursement.getUserId()));
//备用金信息查询
BpmOAImprestDO bpmOAImprestDO = imprestService.getImprest(reimbursement.getImprestId());
if (bpmOAImprestDO != null) {
//设置备用金 金额
bpmOAReimbursementRespVO.setAmount(bpmOAImprestDO.getAmount());
// 设置备用金 剩余金额
bpmOAReimbursementRespVO.setRemainingAmount(bpmOAImprestDO.getAmount().subtract(bpmOAImprestDO.getReimbursedAmount()));
}
printData.setBpmOAReimbursementRespVO(bpmOAReimbursementRespVO);
printData.setProcessTasks(taskRespVOList);
break;
case "oa_cash":
BpmOACashDO cashDO = cashService.getCash(businessKey);
BpmOACashRespVO cashRespVO = cashService.convertCash(cashDO);
// 移除自己得审批节点
taskRespVOList.removeIf(data -> data.getAssigneeUser().getId().equals(cashDO.getUserId()));
//备用金信息查询
BpmOAImprestDO cashImprestDO = imprestService.getImprest(cashDO.getImprestId());
if (cashImprestDO != null) {
//设置备用金 金额
cashRespVO.setAmount(cashImprestDO.getAmount());
// 设置备用金 剩余金额
cashRespVO.setRemainingAmount(cashImprestDO.getAmount().subtract(cashImprestDO.getReimbursedAmount()));
}
printData.setBpmOACashRespVO(cashRespVO);
printData.setProcessTasks(taskRespVOList);
break;
case "oa_payment_2":
BpmOAPaymentDO paymentDO = paymentService.getPayment(businessKey);
BpmOAPaymentRespVO paymentRespVO = paymentService.convertPayment(paymentDO);
// 移除自己得审批节点
taskRespVOList.removeIf(data -> data.getAssigneeUser().getId().equals(paymentDO.getUserId()));
printData.setBpmOAPaymentRespVO(paymentRespVO);
printData.setProcessTasks(taskRespVOList);
break;
}
bpmProcessInstancePrintDataRespVO.setPrintDataRespVO(printData);
return bpmProcessInstancePrintDataRespVO;
}
@Override
public Map> getProcessInstanceResultStatusStatisticsGroupTime(BpmProcessInstanceStatisticsReqVO pageReqVO) {
pageReqVO = getUserids(pageReqVO);
pageReqVO.setRecentDays(pageReqVO.getRecentDays() == null ? 7 : pageReqVO.getRecentDays());
List list = processInstanceExtMapper.getProcessInstanceResultStatusStatisticsGroupTime(pageReqVO);
// -- 获取到日期列表
LocalDateTime now = LocalDateTimeUtil.now();
LocalDateTime offset = LocalDateTimeUtil.offset(LocalDateTimeUtil.now(), -1L * pageReqVO.getRecentDays(), ChronoUnit.DAYS);
List dateList = DateUtils.betweenDayList(offset, now);
//根据时间分组
Map> map = list.stream().collect(Collectors.groupingBy(BpmProcessInstanceResultStatusStatisticsGroupTimeVO::getTime));
List keys = new ArrayList<>(CollectionUtil.subtract(dateList, new ArrayList<>(map.keySet())));
if (CollectionUtil.isNotEmpty(keys)) {
for (String key : keys) {
map.put(key, new ArrayList<>());
}
}
List statusList = Arrays.asList(1, 2, 3, 4);
for (Map.Entry> entry : map.entrySet()) {
List items = entry.getValue();
List results = items.stream().map(BpmProcessInstanceResultStatusStatisticsGroupTimeVO::getResult).collect(Collectors.toList());
List saveList = new ArrayList<>(CollectionUtil.subtract(statusList, results));
// -- 如果没有的话组装上缺少的
if (CollectionUtil.isNotEmpty(saveList)) {
for (Integer status : saveList) {
items.add(new BpmProcessInstanceResultStatusStatisticsGroupTimeVO()
.setTime(entry.getKey())
.setTotalCount(0)
.setResult(status)
.setName(""));
}
}
}
return map;
}
}
\ No newline at end of file
From fd7d5b20286290d1d966431b05d22bdd47605566 Mon Sep 17 00:00:00 2001
From: aikai
Date: Tue, 22 Apr 2025 17:59:32 +0800
Subject: [PATCH 2/2] =?UTF-8?q?feat(module):=20=E6=B7=BB=E5=8A=A0=20erp=20?=
=?UTF-8?q?=E6=A8=A1=E5=9D=97?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 在 pom.xml 中添加 yudao-module-erp 模块
- 新增 erp模块的 API 包结构- 添加多个与 erp 相关的枚举类
- 在网关配置中添加 erp服务路由
---
pom.xml | 1 +
.../framework/common/core/ArrayValuable.java | 15 +
.../common/enums/CommonStatusEnum.java | 8 +-
.../common/enums/SocialTypeEnum.java | 8 +-
.../framework/common/enums/TerminalEnum.java | 8 +-
.../framework/common/enums/UserTypeEnum.java | 8 +-
.../util/collection/CollectionUtils.java | 17 +-
.../common/util/date/LocalDateTimeUtils.java | 35 ++
.../common/util/number/MoneyUtils.java | 40 ++-
.../common/util/object/BeanUtils.java | 40 ++-
.../framework/common/validation/InEnum.java | 6 +-
.../validation/InEnumCollectionValidator.java | 18 +-
.../common/validation/InEnumValidator.java | 15 +-
.../framework/ip/core/enums/AreaTypeEnum.java | 8 +-
.../enums/transfer/PayTransferTypeEnum.java | 9 +-
.../src/main/resources/application.yaml | 10 +
yudao-module-erp/pom.xml | 24 ++
yudao-module-erp/yudao-module-erp-api/pom.xml | 33 ++
.../yudao/module/erp/api/package-info.java | 4 +
.../yudao/module/erp/enums/ApiConstants.java | 23 ++
.../module/erp/enums/DictTypeConstants.java | 13 +
.../module/erp/enums/ErpAuditStatus.java | 39 +++
.../module/erp/enums/ErrorCodeConstants.java | 168 ++++++++++
.../module/erp/enums/LogRecordConstants.java | 12 +
.../erp/enums/common/ErpBizTypeEnum.java | 43 +++
.../stock/ErpStockRecordBizTypeEnum.java | 63 ++++
.../yudao-module-erp-biz/Dockerfile | 19 ++
yudao-module-erp/yudao-module-erp-biz/pom.xml | 119 +++++++
.../module/erp/ErpServerApplication.java | 30 ++
.../admin/finance/ErpAccountController.java | 113 +++++++
.../finance/ErpFinancePaymentController.java | 150 +++++++++
.../finance/ErpFinanceReceiptController.java | 150 +++++++++
.../vo/account/ErpAccountPageReqVO.java | 24 ++
.../finance/vo/account/ErpAccountRespVO.java | 50 +++
.../vo/account/ErpAccountSaveReqVO.java | 36 ++
.../payment/ErpFinancePaymentPageReqVO.java | 48 +++
.../vo/payment/ErpFinancePaymentRespVO.java | 99 ++++++
.../payment/ErpFinancePaymentSaveReqVO.java | 74 ++++
.../receipt/ErpFinanceReceiptPageReqVO.java | 48 +++
.../vo/receipt/ErpFinanceReceiptRespVO.java | 99 ++++++
.../receipt/ErpFinanceReceiptSaveReqVO.java | 74 ++++
.../product/ErpProductCategoryController.java | 98 ++++++
.../admin/product/ErpProductController.java | 102 ++++++
.../product/ErpProductUnitController.java | 100 ++++++
.../category/ErpProductCategoryListReqVO.java | 16 +
.../vo/category/ErpProductCategoryRespVO.java | 47 +++
.../category/ErpProductCategorySaveReqVO.java | 35 ++
.../vo/product/ErpProductPageReqVO.java | 30 ++
.../product/vo/product/ErpProductRespVO.java | 76 +++++
.../product/vo/product/ProductSaveReqVO.java | 58 ++++
.../vo/unit/ErpProductUnitPageReqVO.java | 21 ++
.../product/vo/unit/ErpProductUnitRespVO.java | 34 ++
.../vo/unit/ErpProductUnitSaveReqVO.java | 26 ++
.../purchase/ErpPurchaseInController.java | 163 +++++++++
.../purchase/ErpPurchaseOrderController.java | 162 +++++++++
.../purchase/ErpPurchaseReturnController.java | 163 +++++++++
.../admin/purchase/ErpSupplierController.java | 100 ++++++
.../vo/in/ErpPurchaseInPageReqVO.java | 61 ++++
.../purchase/vo/in/ErpPurchaseInRespVO.java | 145 ++++++++
.../vo/in/ErpPurchaseInSaveReqVO.java | 81 +++++
.../vo/order/ErpPurchaseOrderPageReqVO.java | 80 +++++
.../vo/order/ErpPurchaseOrderRespVO.java | 152 +++++++++
.../vo/order/ErpPurchaseOrderSaveReqVO.java | 73 ++++
.../returns/ErpPurchaseReturnPageReqVO.java | 61 ++++
.../vo/returns/ErpPurchaseReturnRespVO.java | 145 ++++++++
.../returns/ErpPurchaseReturnSaveReqVO.java | 81 +++++
.../vo/supplier/ErpSupplierPageReqVO.java | 24 ++
.../vo/supplier/ErpSupplierRespVO.java | 84 +++++
.../vo/supplier/ErpSupplierSaveReqVO.java | 71 ++++
.../admin/sale/ErpCustomerController.java | 99 ++++++
.../admin/sale/ErpSaleOrderController.java | 161 +++++++++
.../admin/sale/ErpSaleOutController.java | 162 +++++++++
.../admin/sale/ErpSaleReturnController.java | 162 +++++++++
.../vo/customer/ErpCustomerPageReqVO.java | 24 ++
.../sale/vo/customer/ErpCustomerRespVO.java | 83 +++++
.../vo/customer/ErpCustomerSaveReqVO.java | 62 ++++
.../sale/vo/order/ErpSaleOrderPageReqVO.java | 80 +++++
.../sale/vo/order/ErpSaleOrderRespVO.java | 155 +++++++++
.../sale/vo/order/ErpSaleOrderSaveReqVO.java | 76 +++++
.../sale/vo/out/ErpSaleOutPageReqVO.java | 61 ++++
.../admin/sale/vo/out/ErpSaleOutRespVO.java | 148 ++++++++
.../sale/vo/out/ErpSaleOutSaveReqVO.java | 84 +++++
.../vo/returns/ErpSaleReturnPageReqVO.java | 61 ++++
.../sale/vo/returns/ErpSaleReturnRespVO.java | 148 ++++++++
.../vo/returns/ErpSaleReturnSaveReqVO.java | 84 +++++
.../ErpPurchaseStatisticsController.java | 69 ++++
.../ErpSaleStatisticsController.http | 11 +
.../ErpSaleStatisticsController.java | 69 ++++
.../vo/purchase/ErpPurchaseSummaryRespVO.java | 24 ++
.../ErpPurchaseTimeSummaryRespVO.java | 18 +
.../vo/sale/ErpSaleSummaryRespVO.java | 24 ++
.../vo/sale/ErpSaleTimeSummaryRespVO.java | 18 +
.../admin/stock/ErpStockCheckController.java | 147 ++++++++
.../admin/stock/ErpStockController.java | 110 ++++++
.../admin/stock/ErpStockInController.java | 163 +++++++++
.../admin/stock/ErpStockMoveController.java | 158 +++++++++
.../admin/stock/ErpStockOutController.java | 163 +++++++++
.../admin/stock/ErpStockRecordController.java | 103 ++++++
.../admin/stock/ErpWarehouseController.java | 114 +++++++
.../vo/check/ErpStockCheckPageReqVO.java | 45 +++
.../stock/vo/check/ErpStockCheckRespVO.java | 111 ++++++
.../vo/check/ErpStockCheckSaveReqVO.java | 69 ++++
.../stock/vo/in/ErpStockInPageReqVO.java | 48 +++
.../admin/stock/vo/in/ErpStockInRespVO.java | 110 ++++++
.../stock/vo/in/ErpStockInSaveReqVO.java | 64 ++++
.../stock/vo/move/ErpStockMovePageReqVO.java | 45 +++
.../stock/vo/move/ErpStockMoveRespVO.java | 107 ++++++
.../stock/vo/move/ErpStockMoveSaveReqVO.java | 77 +++++
.../stock/vo/out/ErpStockOutPageReqVO.java | 48 +++
.../admin/stock/vo/out/ErpStockOutRespVO.java | 110 ++++++
.../stock/vo/out/ErpStockOutSaveReqVO.java | 64 ++++
.../vo/record/ErpStockRecordPageReqVO.java | 36 ++
.../stock/vo/record/ErpStockRecordRespVO.java | 87 +++++
.../stock/vo/stock/ErpStockPageReqVO.java | 21 ++
.../admin/stock/vo/stock/ErpStockRespVO.java | 49 +++
.../vo/warehouse/ErpWarehousePageReqVO.java | 24 ++
.../vo/warehouse/ErpWarehouseRespVO.java | 64 ++++
.../vo/warehouse/ErpWarehouseSaveReqVO.java | 47 +++
.../module/erp/controller/package-info.java | 6 +
.../dal/dataobject/finance/ErpAccountDO.java | 56 ++++
.../finance/ErpFinancePaymentDO.java | 86 +++++
.../finance/ErpFinancePaymentItemDO.java | 75 +++++
.../finance/ErpFinanceReceiptDO.java | 86 +++++
.../finance/ErpFinanceReceiptItemDO.java | 75 +++++
.../product/ErpProductCategoryDO.java | 54 +++
.../dal/dataobject/product/ErpProductDO.java | 86 +++++
.../dataobject/product/ErpProductUnitDO.java | 38 +++
.../dataobject/purchase/ErpPurchaseInDO.java | 122 +++++++
.../purchase/ErpPurchaseInItemDO.java | 95 ++++++
.../purchase/ErpPurchaseOrderDO.java | 115 +++++++
.../purchase/ErpPurchaseOrderItemDO.java | 93 ++++++
.../purchase/ErpPurchaseReturnDO.java | 122 +++++++
.../purchase/ErpPurchaseReturnItemDO.java | 95 ++++++
.../dataobject/purchase/ErpSupplierDO.java | 90 +++++
.../dal/dataobject/sale/ErpCustomerDO.java | 90 +++++
.../dal/dataobject/sale/ErpSaleOrderDO.java | 121 +++++++
.../dataobject/sale/ErpSaleOrderItemDO.java | 93 ++++++
.../erp/dal/dataobject/sale/ErpSaleOutDO.java | 128 +++++++
.../dal/dataobject/sale/ErpSaleOutItemDO.java | 96 ++++++
.../dal/dataobject/sale/ErpSaleReturnDO.java | 128 +++++++
.../dataobject/sale/ErpSaleReturnItemDO.java | 95 ++++++
.../dal/dataobject/stock/ErpStockCheckDO.java | 63 ++++
.../dataobject/stock/ErpStockCheckItemDO.java | 83 +++++
.../erp/dal/dataobject/stock/ErpStockDO.java | 49 +++
.../dal/dataobject/stock/ErpStockInDO.java | 70 ++++
.../dataobject/stock/ErpStockInItemDO.java | 73 ++++
.../dal/dataobject/stock/ErpStockMoveDO.java | 63 ++++
.../dataobject/stock/ErpStockMoveItemDO.java | 79 +++++
.../dal/dataobject/stock/ErpStockOutDO.java | 69 ++++
.../dataobject/stock/ErpStockOutItemDO.java | 73 ++++
.../dataobject/stock/ErpStockRecordDO.java | 82 +++++
.../dal/dataobject/stock/ErpWarehouseDO.java | 70 ++++
.../dal/mysql/finance/ErpAccountMapper.java | 36 ++
.../finance/ErpFinancePaymentItemMapper.java | 44 +++
.../finance/ErpFinancePaymentMapper.java | 48 +++
.../finance/ErpFinanceReceiptItemMapper.java | 44 +++
.../finance/ErpFinanceReceiptMapper.java | 48 +++
.../product/ErpProductCategoryMapper.java | 34 ++
.../dal/mysql/product/ErpProductMapper.java | 40 +++
.../mysql/product/ErpProductUnitMapper.java | 35 ++
.../purchase/ErpPurchaseInItemMapper.java | 56 ++++
.../mysql/purchase/ErpPurchaseInMapper.java | 70 ++++
.../purchase/ErpPurchaseOrderItemMapper.java | 30 ++
.../purchase/ErpPurchaseOrderMapper.java | 75 +++++
.../purchase/ErpPurchaseReturnItemMapper.java | 56 ++++
.../purchase/ErpPurchaseReturnMapper.java | 70 ++++
.../dal/mysql/purchase/ErpSupplierMapper.java | 32 ++
.../erp/dal/mysql/sale/ErpCustomerMapper.java | 32 ++
.../mysql/sale/ErpSaleOrderItemMapper.java | 30 ++
.../dal/mysql/sale/ErpSaleOrderMapper.java | 76 +++++
.../dal/mysql/sale/ErpSaleOutItemMapper.java | 56 ++++
.../erp/dal/mysql/sale/ErpSaleOutMapper.java | 70 ++++
.../mysql/sale/ErpSaleReturnItemMapper.java | 56 ++++
.../dal/mysql/sale/ErpSaleReturnMapper.java | 71 ++++
.../ErpPurchaseStatisticsMapper.java | 20 ++
.../statistics/ErpSaleStatisticsMapper.java | 20 ++
.../mysql/stock/ErpStockCheckItemMapper.java | 30 ++
.../dal/mysql/stock/ErpStockCheckMapper.java | 46 +++
.../dal/mysql/stock/ErpStockInItemMapper.java | 30 ++
.../erp/dal/mysql/stock/ErpStockInMapper.java | 47 +++
.../erp/dal/mysql/stock/ErpStockMapper.java | 64 ++++
.../mysql/stock/ErpStockMoveItemMapper.java | 30 ++
.../dal/mysql/stock/ErpStockMoveMapper.java | 46 +++
.../mysql/stock/ErpStockOutItemMapper.java | 30 ++
.../dal/mysql/stock/ErpStockOutMapper.java | 47 +++
.../dal/mysql/stock/ErpStockRecordMapper.java | 28 ++
.../dal/mysql/stock/ErpWarehouseMapper.java | 35 ++
.../erp/dal/redis/RedisKeyConstants.java | 18 +
.../erp/dal/redis/no/ErpNoRedisDAO.java | 96 ++++++
.../module/erp/framework/package-info.java | 6 +
.../rpc/config/RpcConfiguration.java | 10 +
.../erp/framework/rpc/package-info.java | 4 +
.../config/SecurityConfiguration.java | 36 ++
.../framework/security/core/package-info.java | 4 +
.../yudao/module/erp/package-info.java | 10 +
.../service/finance/ErpAccountService.java | 102 ++++++
.../finance/ErpAccountServiceImpl.java | 113 +++++++
.../finance/ErpFinancePaymentService.java | 84 +++++
.../finance/ErpFinancePaymentServiceImpl.java | 273 +++++++++++++++
.../finance/ErpFinanceReceiptService.java | 84 +++++
.../finance/ErpFinanceReceiptServiceImpl.java | 273 +++++++++++++++
.../product/ErpProductCategoryService.java | 77 +++++
.../ErpProductCategoryServiceImpl.java | 149 +++++++++
.../service/product/ErpProductService.java | 111 ++++++
.../product/ErpProductServiceImpl.java | 152 +++++++++
.../product/ErpProductUnitService.java | 86 +++++
.../product/ErpProductUnitServiceImpl.java | 111 ++++++
.../purchase/ErpPurchaseInService.java | 101 ++++++
.../purchase/ErpPurchaseInServiceImpl.java | 308 +++++++++++++++++
.../purchase/ErpPurchaseOrderService.java | 110 ++++++
.../purchase/ErpPurchaseOrderServiceImpl.java | 295 ++++++++++++++++
.../purchase/ErpPurchaseReturnService.java | 101 ++++++
.../ErpPurchaseReturnServiceImpl.java | 304 +++++++++++++++++
.../service/purchase/ErpSupplierService.java | 94 ++++++
.../purchase/ErpSupplierServiceImpl.java | 95 ++++++
.../erp/service/sale/ErpCustomerService.java | 94 ++++++
.../service/sale/ErpCustomerServiceImpl.java | 97 ++++++
.../erp/service/sale/ErpSaleOrderService.java | 110 ++++++
.../service/sale/ErpSaleOrderServiceImpl.java | 307 +++++++++++++++++
.../erp/service/sale/ErpSaleOutService.java | 102 ++++++
.../service/sale/ErpSaleOutServiceImpl.java | 316 ++++++++++++++++++
.../service/sale/ErpSaleReturnService.java | 101 ++++++
.../sale/ErpSaleReturnServiceImpl.java | 316 ++++++++++++++++++
.../ErpPurchaseStatisticsService.java | 24 ++
.../ErpPurchaseStatisticsServiceImpl.java | 26 ++
.../statistics/ErpSaleStatisticsService.java | 24 ++
.../ErpSaleStatisticsServiceImpl.java | 26 ++
.../service/stock/ErpStockCheckService.java | 84 +++++
.../stock/ErpStockCheckServiceImpl.java | 232 +++++++++++++
.../erp/service/stock/ErpStockInService.java | 84 +++++
.../service/stock/ErpStockInServiceImpl.java | 228 +++++++++++++
.../service/stock/ErpStockMoveService.java | 84 +++++
.../stock/ErpStockMoveServiceImpl.java | 229 +++++++++++++
.../erp/service/stock/ErpStockOutService.java | 84 +++++
.../service/stock/ErpStockOutServiceImpl.java | 228 +++++++++++++
.../service/stock/ErpStockRecordService.java | 39 +++
.../stock/ErpStockRecordServiceImpl.java | 53 +++
.../erp/service/stock/ErpStockService.java | 61 ++++
.../service/stock/ErpStockServiceImpl.java | 89 +++++
.../service/stock/ErpWarehouseService.java | 102 ++++++
.../stock/ErpWarehouseServiceImpl.java | 126 +++++++
.../stock/bo/ErpStockRecordCreateReqBO.java | 59 ++++
.../src/main/resources/application-dev.yaml | 114 +++++++
.../src/main/resources/application-local.yaml | 128 +++++++
.../src/main/resources/application-prod.yaml | 125 +++++++
.../src/main/resources/application.yaml | 124 +++++++
.../src/main/resources/bootstrap-dev.yaml | 23 ++
.../src/main/resources/bootstrap-local.yaml | 23 ++
.../src/main/resources/bootstrap-prod.yaml | 23 ++
.../src/main/resources/bootstrap.yaml | 15 +
.../src/main/resources/logback-spring.xml | 76 +++++
.../ErpPurchaseStatisticsMapper.xml | 25 ++
.../statistics/ErpSaleStatisticsMapper.xml | 25 ++
.../ProductCommentAuditStatusEnum.java | 8 +-
.../comment/ProductCommentScoresEnum.java | 8 +-
.../enums/delivery/DeliveryTypeEnum.java | 8 +-
.../enums/spu/ProductSpuSpecTypeEnum.java | 8 +-
.../enums/spu/ProductSpuStatusEnum.java | 8 +-
.../enums/banner/BannerPositionEnum.java | 8 +-
.../bargain/BargainRecordStatusEnum.java | 8 +-
.../CombinationRecordStatusEnum.java | 8 +-
.../common/PromotionActivityStatusEnum.java | 10 +-
.../common/PromotionConditionTypeEnum.java | 8 +-
.../common/PromotionDiscountTypeEnum.java | 8 +-
.../common/PromotionProductScopeEnum.java | 8 +-
.../enums/common/PromotionTypeEnum.java | 8 +-
.../enums/coupon/CouponStatusEnum.java | 8 +-
.../enums/coupon/CouponTakeTypeEnum.java | 8 +-
.../CouponTemplateValidityTypeEnum.java | 10 +-
.../enums/decorate/DecoratePageEnum.java | 8 +-
.../statistics/enums/TimeRangeTypeEnum.java | 8 +-
.../enums/aftersale/AfterSaleStatusEnum.java | 8 +-
.../enums/aftersale/AfterSaleTypeEnum.java | 10 +-
.../enums/aftersale/AfterSaleWayEnum.java | 8 +-
.../brokerage/BrokerageBindModeEnum.java | 8 +-
.../BrokerageEnabledConditionEnum.java | 8 +-
.../brokerage/BrokerageRecordBizTypeEnum.java | 8 +-
.../brokerage/BrokerageRecordStatusEnum.java | 8 +-
.../BrokerageWithdrawStatusEnum.java | 9 +-
.../brokerage/BrokerageWithdrawTypeEnum.java | 8 +-
.../DeliveryExpressChargeModeEnum.java | 10 +-
.../enums/delivery/DeliveryTypeEnum.java | 13 +-
.../enums/order/TradeOrderCancelTypeEnum.java | 8 +-
.../TradeOrderItemAfterSaleStatusEnum.java | 10 +-
.../order/TradeOrderRefundStatusEnum.java | 8 +-
.../enums/order/TradeOrderStatusEnum.java | 8 +-
.../trade/enums/order/TradeOrderTypeEnum.java | 8 +-
.../enums/point/MemberPointBizTypeEnum.java | 10 +-
.../pay/enums/order/PayOrderStatusEnum.java | 10 +-
.../enums/transfer/PayTransferTypeEnum.java | 8 +-
.../enums/wallet/PayWalletBizTypeEnum.java | 10 +-
.../module/system/api/user/AdminUserApi.java | 10 +-
.../enums/errorcode/ErrorCodeTypeEnum.java | 8 +-
.../enums/permission/DataScopeEnum.java | 12 +-
.../module/system/enums/sms/SmsSceneEnum.java | 10 +-
.../src/main/resources/application-prod.yaml | 12 -
296 files changed, 19917 insertions(+), 252 deletions(-)
create mode 100644 yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/core/ArrayValuable.java
create mode 100644 yudao-module-erp/pom.xml
create mode 100644 yudao-module-erp/yudao-module-erp-api/pom.xml
create mode 100644 yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/api/package-info.java
create mode 100644 yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/ApiConstants.java
create mode 100644 yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/DictTypeConstants.java
create mode 100644 yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/ErpAuditStatus.java
create mode 100644 yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/ErrorCodeConstants.java
create mode 100644 yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/LogRecordConstants.java
create mode 100644 yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/common/ErpBizTypeEnum.java
create mode 100644 yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/stock/ErpStockRecordBizTypeEnum.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/Dockerfile
create mode 100644 yudao-module-erp/yudao-module-erp-biz/pom.xml
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/ErpServerApplication.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/ErpAccountController.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/ErpFinancePaymentController.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/ErpFinanceReceiptController.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/account/ErpAccountPageReqVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/account/ErpAccountRespVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/account/ErpAccountSaveReqVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/payment/ErpFinancePaymentPageReqVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/payment/ErpFinancePaymentRespVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/payment/ErpFinancePaymentSaveReqVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/receipt/ErpFinanceReceiptPageReqVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/receipt/ErpFinanceReceiptRespVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/receipt/ErpFinanceReceiptSaveReqVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/ErpProductCategoryController.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/ErpProductController.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/ErpProductUnitController.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/category/ErpProductCategoryListReqVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/category/ErpProductCategoryRespVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/category/ErpProductCategorySaveReqVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/product/ErpProductPageReqVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/product/ErpProductRespVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/product/ProductSaveReqVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/unit/ErpProductUnitPageReqVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/unit/ErpProductUnitRespVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/unit/ErpProductUnitSaveReqVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseInController.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseOrderController.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseReturnController.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpSupplierController.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/in/ErpPurchaseInPageReqVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/in/ErpPurchaseInRespVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/in/ErpPurchaseInSaveReqVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/order/ErpPurchaseOrderPageReqVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/order/ErpPurchaseOrderRespVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/order/ErpPurchaseOrderSaveReqVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/returns/ErpPurchaseReturnPageReqVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/returns/ErpPurchaseReturnRespVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/returns/ErpPurchaseReturnSaveReqVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/supplier/ErpSupplierPageReqVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/supplier/ErpSupplierRespVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/supplier/ErpSupplierSaveReqVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpCustomerController.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleOrderController.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleOutController.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleReturnController.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/customer/ErpCustomerPageReqVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/customer/ErpCustomerRespVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/customer/ErpCustomerSaveReqVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderPageReqVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderRespVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderSaveReqVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/out/ErpSaleOutPageReqVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/out/ErpSaleOutRespVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/out/ErpSaleOutSaveReqVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/returns/ErpSaleReturnPageReqVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/returns/ErpSaleReturnRespVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/returns/ErpSaleReturnSaveReqVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/statistics/ErpPurchaseStatisticsController.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/statistics/ErpSaleStatisticsController.http
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/statistics/ErpSaleStatisticsController.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/statistics/vo/purchase/ErpPurchaseSummaryRespVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/statistics/vo/purchase/ErpPurchaseTimeSummaryRespVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/statistics/vo/sale/ErpSaleSummaryRespVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/statistics/vo/sale/ErpSaleTimeSummaryRespVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockCheckController.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockController.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockInController.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockMoveController.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockOutController.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockRecordController.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpWarehouseController.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckPageReqVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckRespVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/check/ErpStockCheckSaveReqVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/in/ErpStockInPageReqVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/in/ErpStockInRespVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/in/ErpStockInSaveReqVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/move/ErpStockMovePageReqVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/move/ErpStockMoveRespVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/move/ErpStockMoveSaveReqVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/out/ErpStockOutPageReqVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/out/ErpStockOutRespVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/out/ErpStockOutSaveReqVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/record/ErpStockRecordPageReqVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/record/ErpStockRecordRespVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/stock/ErpStockPageReqVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/stock/ErpStockRespVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/warehouse/ErpWarehousePageReqVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/warehouse/ErpWarehouseRespVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/vo/warehouse/ErpWarehouseSaveReqVO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/package-info.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/finance/ErpAccountDO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/finance/ErpFinancePaymentDO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/finance/ErpFinancePaymentItemDO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/finance/ErpFinanceReceiptDO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/finance/ErpFinanceReceiptItemDO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/product/ErpProductCategoryDO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/product/ErpProductDO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/product/ErpProductUnitDO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseInDO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseInItemDO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseOrderDO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseOrderItemDO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseReturnDO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseReturnItemDO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpSupplierDO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/sale/ErpCustomerDO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/sale/ErpSaleOrderDO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/sale/ErpSaleOrderItemDO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/sale/ErpSaleOutDO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/sale/ErpSaleOutItemDO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/sale/ErpSaleReturnDO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/sale/ErpSaleReturnItemDO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockCheckDO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockCheckItemDO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockDO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockInDO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockInItemDO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockMoveDO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockMoveItemDO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockOutDO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockOutItemDO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockRecordDO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpWarehouseDO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/finance/ErpAccountMapper.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/finance/ErpFinancePaymentItemMapper.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/finance/ErpFinancePaymentMapper.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/finance/ErpFinanceReceiptItemMapper.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/finance/ErpFinanceReceiptMapper.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/product/ErpProductCategoryMapper.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/product/ErpProductMapper.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/product/ErpProductUnitMapper.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseInItemMapper.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseInMapper.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseOrderItemMapper.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseOrderMapper.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseReturnItemMapper.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseReturnMapper.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpSupplierMapper.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/sale/ErpCustomerMapper.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/sale/ErpSaleOrderItemMapper.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/sale/ErpSaleOrderMapper.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/sale/ErpSaleOutItemMapper.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/sale/ErpSaleOutMapper.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/sale/ErpSaleReturnItemMapper.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/sale/ErpSaleReturnMapper.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/statistics/ErpPurchaseStatisticsMapper.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/statistics/ErpSaleStatisticsMapper.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockCheckItemMapper.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockCheckMapper.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockInItemMapper.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockInMapper.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockMapper.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockMoveItemMapper.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockMoveMapper.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockOutItemMapper.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockOutMapper.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockRecordMapper.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpWarehouseMapper.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/redis/RedisKeyConstants.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/redis/no/ErpNoRedisDAO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/framework/package-info.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/framework/rpc/config/RpcConfiguration.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/framework/rpc/package-info.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/framework/security/config/SecurityConfiguration.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/framework/security/core/package-info.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/package-info.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/finance/ErpAccountService.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/finance/ErpAccountServiceImpl.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/finance/ErpFinancePaymentService.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/finance/ErpFinancePaymentServiceImpl.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/finance/ErpFinanceReceiptService.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/finance/ErpFinanceReceiptServiceImpl.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductCategoryService.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductCategoryServiceImpl.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductService.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductServiceImpl.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductUnitService.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductUnitServiceImpl.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseInService.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseInServiceImpl.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseOrderService.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseOrderServiceImpl.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseReturnService.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseReturnServiceImpl.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpSupplierService.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpSupplierServiceImpl.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpCustomerService.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpCustomerServiceImpl.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOrderService.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOrderServiceImpl.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOutService.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOutServiceImpl.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleReturnService.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleReturnServiceImpl.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/statistics/ErpPurchaseStatisticsService.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/statistics/ErpPurchaseStatisticsServiceImpl.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/statistics/ErpSaleStatisticsService.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/statistics/ErpSaleStatisticsServiceImpl.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockCheckService.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockCheckServiceImpl.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockInService.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockInServiceImpl.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockMoveService.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockMoveServiceImpl.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockOutService.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockOutServiceImpl.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockRecordService.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockRecordServiceImpl.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockService.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockServiceImpl.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpWarehouseService.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpWarehouseServiceImpl.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/bo/ErpStockRecordCreateReqBO.java
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/resources/application-dev.yaml
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/resources/application-local.yaml
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/resources/application-prod.yaml
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/resources/application.yaml
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/resources/bootstrap-dev.yaml
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/resources/bootstrap-local.yaml
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/resources/bootstrap-prod.yaml
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/resources/bootstrap.yaml
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/resources/logback-spring.xml
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/resources/mapper/statistics/ErpPurchaseStatisticsMapper.xml
create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/resources/mapper/statistics/ErpSaleStatisticsMapper.xml
diff --git a/pom.xml b/pom.xml
index 349db03d..27089f9d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -24,6 +24,7 @@
yudao-module-wms
yudao-module-hrm
yudao-module-crm
+ yudao-module-erp
${project.artifactId}
diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/core/ArrayValuable.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/core/ArrayValuable.java
new file mode 100644
index 00000000..b83451fd
--- /dev/null
+++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/core/ArrayValuable.java
@@ -0,0 +1,15 @@
+package cn.iocoder.yudao.framework.common.core;
+
+/**
+ * 可生成 T 数组的接口
+ *
+ * @author HUIHUI
+ */
+public interface ArrayValuable {
+
+ /**
+ * @return 数组
+ */
+ T[] array();
+
+}
\ No newline at end of file
diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/CommonStatusEnum.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/CommonStatusEnum.java
index 06a3539b..3d41029f 100644
--- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/CommonStatusEnum.java
+++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/CommonStatusEnum.java
@@ -1,7 +1,7 @@
package cn.iocoder.yudao.framework.common.enums;
import cn.hutool.core.util.ObjUtil;
-import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
+import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@@ -14,12 +14,12 @@ import java.util.Arrays;
*/
@Getter
@AllArgsConstructor
-public enum CommonStatusEnum implements IntArrayValuable {
+public enum CommonStatusEnum implements ArrayValuable {
ENABLE(0, "开启"),
DISABLE(1, "关闭");
- public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CommonStatusEnum::getStatus).toArray();
+ public static final Integer[] ARRAYS = Arrays.stream(values()).map(CommonStatusEnum::getStatus).toArray(Integer[]::new);
/**
* 状态值
@@ -31,7 +31,7 @@ public enum CommonStatusEnum implements IntArrayValuable {
private final String name;
@Override
- public int[] array() {
+ public Integer[] array() {
return ARRAYS;
}
diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/SocialTypeEnum.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/SocialTypeEnum.java
index 25b417ad..ac995353 100644
--- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/SocialTypeEnum.java
+++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/SocialTypeEnum.java
@@ -1,7 +1,7 @@
package cn.iocoder.yudao.framework.common.enums;
import cn.hutool.core.util.ArrayUtil;
-import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
+import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@@ -14,7 +14,7 @@ import java.util.Arrays;
*/
@Getter
@AllArgsConstructor
-public enum SocialTypeEnum implements IntArrayValuable {
+public enum SocialTypeEnum implements ArrayValuable {
/**
* Gitee
@@ -61,7 +61,7 @@ public enum SocialTypeEnum implements IntArrayValuable {
WECHAT_MINI_APP_CRM(35, "WECHAT_MINI_APP_CRM"),
;
- public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(SocialTypeEnum::getType).toArray();
+ public static final Integer[] ARRAYS = Arrays.stream(values()).map(SocialTypeEnum::getType).toArray(Integer[]::new);;
/**
* 类型
@@ -73,7 +73,7 @@ public enum SocialTypeEnum implements IntArrayValuable {
private final String source;
@Override
- public int[] array() {
+ public Integer[] array() {
return ARRAYS;
}
diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/TerminalEnum.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/TerminalEnum.java
index 0538ca55..1aead19a 100644
--- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/TerminalEnum.java
+++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/TerminalEnum.java
@@ -1,6 +1,6 @@
package cn.iocoder.yudao.framework.common.enums;
-import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
+import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@@ -13,7 +13,7 @@ import java.util.Arrays;
*/
@RequiredArgsConstructor
@Getter
-public enum TerminalEnum implements IntArrayValuable {
+public enum TerminalEnum implements ArrayValuable {
WECHAT_MINI_PROGRAM(10, "微信小程序"),
WECHAT_WAP(11, "微信公众号"),
@@ -21,7 +21,7 @@ public enum TerminalEnum implements IntArrayValuable {
APP(31, "手机 App"),
;
- public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(TerminalEnum::getTerminal).toArray();
+ public static final Integer[] ARRAYS = Arrays.stream(values()).map(TerminalEnum::getTerminal).toArray(Integer[]::new);
/**
* 终端
@@ -33,7 +33,7 @@ public enum TerminalEnum implements IntArrayValuable {
private final String name;
@Override
- public int[] array() {
+ public Integer[] array() {
return ARRAYS;
}
}
diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/UserTypeEnum.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/UserTypeEnum.java
index d124479f..a964b112 100644
--- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/UserTypeEnum.java
+++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/UserTypeEnum.java
@@ -1,7 +1,7 @@
package cn.iocoder.yudao.framework.common.enums;
import cn.hutool.core.util.ArrayUtil;
-import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
+import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@@ -12,13 +12,13 @@ import java.util.Arrays;
*/
@AllArgsConstructor
@Getter
-public enum UserTypeEnum implements IntArrayValuable {
+public enum UserTypeEnum implements ArrayValuable {
MEMBER(1, "会员"), // 面向 c 端,普通用户
ADMIN(2, "管理员"), // 面向 b 端,管理后台
CUSTOMER(3, "客户"); //客户
- public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(UserTypeEnum::getValue).toArray();
+ public static final Integer[] ARRAYS = Arrays.stream(values()).map(UserTypeEnum::getValue).toArray(Integer[]::new);
/**
* 类型
@@ -34,7 +34,7 @@ public enum UserTypeEnum implements IntArrayValuable {
}
@Override
- public int[] array() {
+ public Integer[] array() {
return ARRAYS;
}
}
diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java
index 69e9606b..39892f94 100644
--- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java
+++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java
@@ -304,13 +304,18 @@ public class CollectionUtils {
return valueFunc.apply(t);
}
- public static > V getSumValue(List from, Function valueFunc,
+ public static > V getSumValue(Collection from, Function valueFunc,
BinaryOperator accumulator) {
+ return getSumValue(from, valueFunc, accumulator, null);
+ }
+
+ public static > V getSumValue(Collection from, Function valueFunc,
+ BinaryOperator accumulator, V defaultValue) {
if (CollUtil.isEmpty(from)) {
- return null;
+ return defaultValue;
}
- assert from.size() > 0; // 断言,避免告警
- return from.stream().map(valueFunc).reduce(accumulator).get();
+ assert !from.isEmpty(); // 断言,避免告警
+ return from.stream().map(valueFunc).filter(Objects::nonNull).reduce(accumulator).orElse(defaultValue);
}
public static void addIfNotNull(Collection coll, T item) {
@@ -323,5 +328,7 @@ public class CollectionUtils {
public static Collection singleton(T deptId) {
return deptId == null ? Collections.emptyList() : Collections.singleton(deptId);
}
-
+ public static List newArrayList(List> list) {
+ return list.stream().filter(Objects::nonNull).flatMap(Collection::stream).collect(Collectors.toList());
+ }
}
diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java
index 4edfece5..793eb95a 100644
--- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java
+++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java
@@ -214,4 +214,39 @@ public class LocalDateTimeUtils {
return !end.isBefore(otherStart) && !otherEnd.isBefore(start);
}
+ /**
+ * 获取今天的开始时间
+ *
+ * @return 今天
+ */
+ public static LocalDateTime getToday() {
+ return LocalDateTimeUtil.beginOfDay(LocalDateTime.now());
+ }
+
+ /**
+ * 获取昨天的开始时间
+ *
+ * @return 昨天
+ */
+ public static LocalDateTime getYesterday() {
+ return LocalDateTimeUtil.beginOfDay(LocalDateTime.now().minusDays(1));
+ }
+
+ /**
+ * 获取本月的开始时间
+ *
+ * @return 本月
+ */
+ public static LocalDateTime getMonth() {
+ return beginOfMonth(LocalDateTime.now());
+ }
+
+ /**
+ * 获取本年的开始时间
+ *
+ * @return 本年
+ */
+ public static LocalDateTime getYear() {
+ return LocalDateTime.now().with(TemporalAdjusters.firstDayOfYear()).with(LocalTime.MIN);
+ }
}
diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/number/MoneyUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/number/MoneyUtils.java
index b0299c97..eba68669 100644
--- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/number/MoneyUtils.java
+++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/number/MoneyUtils.java
@@ -12,6 +12,15 @@ import java.math.RoundingMode;
*/
public class MoneyUtils {
+ /**
+ * 金额的小数位数
+ */
+ private static final int PRICE_SCALE = 2;
+
+ /**
+ * 百分比对应的 BigDecimal 对象
+ */
+ public static final BigDecimal PERCENT_100 = BigDecimal.valueOf(100);
/**
* 计算百分比金额,四舍五入
@@ -69,5 +78,34 @@ public class MoneyUtils {
public static String fenToYuanStr(int fen) {
return new Money(0, fen).toString();
}
-
+ /**
+ * 金额相乘,默认进行四舍五入
+ *
+ * 位数:{@link #PRICE_SCALE}
+ *
+ * @param price 金额
+ * @param count 数量
+ * @return 金额相乘结果
+ */
+ public static BigDecimal priceMultiply(BigDecimal price, BigDecimal count) {
+ if (price == null || count == null) {
+ return null;
+ }
+ return price.multiply(count).setScale(PRICE_SCALE, RoundingMode.HALF_UP);
+ }
+ /**
+ * 金额相乘(百分比),默认进行四舍五入
+ *
+ * 位数:{@link #PRICE_SCALE}
+ *
+ * @param price 金额
+ * @param percent 百分比
+ * @return 金额相乘结果
+ */
+ public static BigDecimal priceMultiplyPercent(BigDecimal price, BigDecimal percent) {
+ if (price == null || percent == null) {
+ return null;
+ }
+ return price.multiply(percent).divide(PERCENT_100, PRICE_SCALE, RoundingMode.HALF_UP);
+ }
}
diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/object/BeanUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/object/BeanUtils.java
index 21c5ad5b..cbfc5728 100644
--- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/object/BeanUtils.java
+++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/object/BeanUtils.java
@@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import java.util.List;
+import java.util.function.Consumer;
/**
* Bean 工具类
@@ -12,7 +13,7 @@ import java.util.List;
* 1. 默认使用 {@link cn.hutool.core.bean.BeanUtil} 作为实现类,虽然不同 bean 工具的性能有差别,但是对绝大多数同学的项目,不用在意这点性能
* 2. 针对复杂的对象转换,可以搜参考 AuthConvert 实现,通过 mapstruct + default 配合实现
*
-
+ * @author 芋道源码
*/
public class BeanUtils {
@@ -20,6 +21,14 @@ public class BeanUtils {
return BeanUtil.toBean(source, targetClass);
}
+ public static T toBean(Object source, Class targetClass, Consumer peek) {
+ T target = toBean(source, targetClass);
+ if (target != null) {
+ peek.accept(target);
+ }
+ return target;
+ }
+
public static List toBean(List source, Class targetType) {
if (source == null) {
return null;
@@ -27,11 +36,34 @@ public class BeanUtils {
return CollectionUtils.convertList(source, s -> toBean(s, targetType));
}
- public static PageResult toBean(PageResult source, Class targetType) {
+ public static List toBean(List source, Class targetType, Consumer peek) {
+ List list = toBean(source, targetType);
+ if (list != null) {
+ list.forEach(peek);
+ }
+ return list;
+ }
+
+ public static PageResult toBean(PageResult source, Class targetType) {
+ return toBean(source, targetType, null);
+ }
+
+ public static PageResult toBean(PageResult source, Class targetType, Consumer peek) {
if (source == null) {
return null;
}
- return new PageResult<>(toBean(source.getList(), targetType), source.getTotal());
+ List list = toBean(source.getList(), targetType);
+ if (peek != null) {
+ list.forEach(peek);
+ }
+ return new PageResult<>(list, source.getTotal());
}
-}
\ No newline at end of file
+ public static void copyProperties(Object source, Object target) {
+ if (source == null || target == null) {
+ return;
+ }
+ BeanUtil.copyProperties(source, target, false);
+ }
+
+}
diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/validation/InEnum.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/validation/InEnum.java
index 139b0896..d27a6431 100644
--- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/validation/InEnum.java
+++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/validation/InEnum.java
@@ -1,6 +1,6 @@
package cn.iocoder.yudao.framework.common.validation;
-import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
+import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import javax.validation.Constraint;
import javax.validation.Payload;
@@ -22,9 +22,9 @@ import java.lang.annotation.*;
public @interface InEnum {
/**
- * @return 实现 EnumValuable 接口的
+ * @return 实现 ArrayValuable 接口的类
*/
- Class extends IntArrayValuable> value();
+ Class extends ArrayValuable>> value();
String message() default "必须在指定范围 {value}";
diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/validation/InEnumCollectionValidator.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/validation/InEnumCollectionValidator.java
index d20a7170..b0551bd8 100644
--- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/validation/InEnumCollectionValidator.java
+++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/validation/InEnumCollectionValidator.java
@@ -1,7 +1,7 @@
package cn.iocoder.yudao.framework.common.validation;
import cn.hutool.core.collection.CollUtil;
-import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
+import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
@@ -9,29 +9,31 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
-import java.util.stream.Collectors;
-public class InEnumCollectionValidator implements ConstraintValidator> {
+public class InEnumCollectionValidator implements ConstraintValidator> {
- private List values;
+ private List> values;
@Override
public void initialize(InEnum annotation) {
- IntArrayValuable[] values = annotation.value().getEnumConstants();
+ ArrayValuable>[] values = annotation.value().getEnumConstants();
if (values.length == 0) {
this.values = Collections.emptyList();
} else {
- this.values = Arrays.stream(values[0].array()).boxed().collect(Collectors.toList());
+ this.values = Arrays.asList(values[0].array());
}
}
@Override
- public boolean isValid(Collection list, ConstraintValidatorContext context) {
+ public boolean isValid(Collection> list, ConstraintValidatorContext context) {
+ if (list == null) {
+ return true;
+ }
// 校验通过
if (CollUtil.containsAll(values, list)) {
return true;
}
- // 校验不通过,自定义提示语句(因为,注解上的 value 是枚举类,无法获得枚举类的实际值)
+ // 校验不通过,自定义提示语句
context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值
context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()
.replaceAll("\\{value}", CollUtil.join(list, ","))).addConstraintViolation(); // 重新添加错误提示语句
diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/validation/InEnumValidator.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/validation/InEnumValidator.java
index 6cd08caa..bd7e2878 100644
--- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/validation/InEnumValidator.java
+++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/validation/InEnumValidator.java
@@ -1,30 +1,29 @@
package cn.iocoder.yudao.framework.common.validation;
-import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
+import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
-import java.util.stream.Collectors;
-public class InEnumValidator implements ConstraintValidator {
+public class InEnumValidator implements ConstraintValidator {
- private List values;
+ private List> values;
@Override
public void initialize(InEnum annotation) {
- IntArrayValuable[] values = annotation.value().getEnumConstants();
+ ArrayValuable>[] values = annotation.value().getEnumConstants();
if (values.length == 0) {
this.values = Collections.emptyList();
} else {
- this.values = Arrays.stream(values[0].array()).boxed().collect(Collectors.toList());
+ this.values = Arrays.asList(values[0].array());
}
}
@Override
- public boolean isValid(Integer value, ConstraintValidatorContext context) {
+ public boolean isValid(Object value, ConstraintValidatorContext context) {
// 为空时,默认不校验,即认为通过
if (value == null) {
return true;
@@ -33,7 +32,7 @@ public class InEnumValidator implements ConstraintValidator {
if (values.contains(value)) {
return true;
}
- // 校验不通过,自定义提示语句(因为,注解上的 value 是枚举类,无法获得枚举类的实际值)
+ // 校验不通过,自定义提示语句
context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值
context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()
.replaceAll("\\{value}", values.toString())).addConstraintViolation(); // 重新添加错误提示语句
diff --git a/yudao-framework/yudao-spring-boot-starter-biz-ip/src/main/java/cn/iocoder/yudao/framework/ip/core/enums/AreaTypeEnum.java b/yudao-framework/yudao-spring-boot-starter-biz-ip/src/main/java/cn/iocoder/yudao/framework/ip/core/enums/AreaTypeEnum.java
index 7f453271..f7e94ae4 100644
--- a/yudao-framework/yudao-spring-boot-starter-biz-ip/src/main/java/cn/iocoder/yudao/framework/ip/core/enums/AreaTypeEnum.java
+++ b/yudao-framework/yudao-spring-boot-starter-biz-ip/src/main/java/cn/iocoder/yudao/framework/ip/core/enums/AreaTypeEnum.java
@@ -1,6 +1,6 @@
package cn.iocoder.yudao.framework.ip.core.enums;
-import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
+import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@@ -13,7 +13,7 @@ import java.util.Arrays;
*/
@AllArgsConstructor
@Getter
-public enum AreaTypeEnum implements IntArrayValuable {
+public enum AreaTypeEnum implements ArrayValuable {
COUNTRY(1, "国家"),
PROVINCE(2, "省份"),
@@ -21,7 +21,7 @@ public enum AreaTypeEnum implements IntArrayValuable {
DISTRICT(4, "地区"), // 县、镇、区等
;
- public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(AreaTypeEnum::getType).toArray();
+ public static final Integer[] ARRAYS = Arrays.stream(values()).map(AreaTypeEnum::getType).toArray(Integer[]::new);
/**
* 类型
@@ -33,7 +33,7 @@ public enum AreaTypeEnum implements IntArrayValuable {
private final String name;
@Override
- public int[] array() {
+ public Integer[] array() {
return ARRAYS;
}
}
diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/transfer/PayTransferTypeEnum.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/transfer/PayTransferTypeEnum.java
index 2de6fd21..e8cb12a2 100644
--- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/transfer/PayTransferTypeEnum.java
+++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/transfer/PayTransferTypeEnum.java
@@ -1,7 +1,8 @@
package cn.iocoder.yudao.framework.pay.core.enums.transfer;
import cn.hutool.core.util.ArrayUtil;
-import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
+import cn.iocoder.yudao.framework.common.core.ArrayValuable;
+import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import lombok.AllArgsConstructor;
import lombok.Getter;
@@ -14,7 +15,7 @@ import java.util.Arrays;
*/
@AllArgsConstructor
@Getter
-public enum PayTransferTypeEnum implements IntArrayValuable {
+public enum PayTransferTypeEnum implements ArrayValuable {
ALIPAY_BALANCE(1, "支付宝余额"),
WX_BALANCE(2, "微信余额"),
@@ -27,10 +28,10 @@ public enum PayTransferTypeEnum implements IntArrayValuable {
private final Integer type;
private final String name;
- public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(PayTransferTypeEnum::getType).toArray();
+ public static final Integer[] ARRAYS = Arrays.stream(values()).map(PayTransferTypeEnum::getType).toArray(Integer[]::new);
@Override
- public int[] array() {
+ public Integer[] array() {
return ARRAYS;
}
diff --git a/yudao-gateway/src/main/resources/application.yaml b/yudao-gateway/src/main/resources/application.yaml
index 3a0c78f4..aed4c1b5 100644
--- a/yudao-gateway/src/main/resources/application.yaml
+++ b/yudao-gateway/src/main/resources/application.yaml
@@ -209,6 +209,13 @@ spring:
- Path=/app-api/statistics/**
filters:
- RewritePath=/app-api/statistics/v3/api-docs, /v3/api-docs
+ ## erp-server 服务
+ - id: erp-admin-api # 路由的编号
+ uri: grayLb://erp-server
+ predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组
+ - Path=/admin-api/erp/**
+ filters:
+ - RewritePath=/admin-api/erp/v3/api-docs, /v3/api-docs # 配置,保证转发到 /v3/api-docs
x-forwarded:
prefix-enabled: false # 避免 Swagger 重复带上额外的 /admin-api/system 前缀
@@ -262,3 +269,6 @@ knife4j:
- name: statistics-server
service-name: statistics-server
url: /admin-api/statistics/v3/api-docs
+ - name: erp-server
+ service-name: erp-server
+ url: /admin-api/erp/v3/api-docs
diff --git a/yudao-module-erp/pom.xml b/yudao-module-erp/pom.xml
new file mode 100644
index 00000000..bed9d7d5
--- /dev/null
+++ b/yudao-module-erp/pom.xml
@@ -0,0 +1,24 @@
+
+
+
+ cn.iocoder.cloud
+ yudao
+ ${revision}
+
+
+ yudao-module-erp-api
+ yudao-module-erp-biz
+
+ 4.0.0
+ yudao-module-erp
+ pom
+
+ ${project.artifactId}
+
+ erp 包下,企业资源管理(Enterprise Resource Planning)。
+ 例如说:采购、销售、库存、财务、产品等等
+
+
+
\ No newline at end of file
diff --git a/yudao-module-erp/yudao-module-erp-api/pom.xml b/yudao-module-erp/yudao-module-erp-api/pom.xml
new file mode 100644
index 00000000..ef5394f1
--- /dev/null
+++ b/yudao-module-erp/yudao-module-erp-api/pom.xml
@@ -0,0 +1,33 @@
+
+
+
+ cn.iocoder.cloud
+ yudao-module-erp
+ ${revision}
+
+ 4.0.0
+ yudao-module-erp-api
+ jar
+
+ ${project.artifactId}
+
+ erp 模块 API,暴露给其它模块调用
+
+
+
+
+ cn.iocoder.cloud
+ yudao-common
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+ true
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/api/package-info.java b/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/api/package-info.java
new file mode 100644
index 00000000..540f18f0
--- /dev/null
+++ b/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/api/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * erp API 包,定义暴露给其它模块的 API
+ */
+package cn.iocoder.yudao.module.erp.api;
diff --git a/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/ApiConstants.java b/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/ApiConstants.java
new file mode 100644
index 00000000..1241dbc9
--- /dev/null
+++ b/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/ApiConstants.java
@@ -0,0 +1,23 @@
+package cn.iocoder.yudao.module.erp.enums;
+
+import cn.iocoder.yudao.framework.common.enums.RpcConstants;
+
+/**
+ * API 相关的枚举
+ *
+ * @author 芋道源码
+ */
+public class ApiConstants {
+
+ /**
+ * 服务名
+ *
+ * 注意,需要保证和 spring.application.name 保持一致
+ */
+ public static final String NAME = "erp-server";
+
+ public static final String PREFIX = RpcConstants.RPC_API_PREFIX + "/erp";
+
+ public static final String VERSION = "1.0.0";
+
+}
diff --git a/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/DictTypeConstants.java b/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/DictTypeConstants.java
new file mode 100644
index 00000000..36d4df85
--- /dev/null
+++ b/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/DictTypeConstants.java
@@ -0,0 +1,13 @@
+package cn.iocoder.yudao.module.erp.enums;
+
+/**
+ * ERP 字典类型的枚举类
+ *
+ * @author 芋道源码
+ */
+public interface DictTypeConstants {
+
+ String AUDIT_STATUS = "erp_audit_status"; // 审核状态
+ String STOCK_RECORD_BIZ_TYPE = "erp_stock_record_biz_type"; // 库存明细的业务类型
+
+}
diff --git a/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/ErpAuditStatus.java b/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/ErpAuditStatus.java
new file mode 100644
index 00000000..42c0174b
--- /dev/null
+++ b/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/ErpAuditStatus.java
@@ -0,0 +1,39 @@
+package cn.iocoder.yudao.module.erp.enums;
+
+import cn.iocoder.yudao.framework.common.core.ArrayValuable;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+import java.util.Arrays;
+
+/**
+ * ERP 审核状态枚举
+ *
+ * TODO 芋艿:目前只有待审批、已审批两个状态,未来接入工作流后,会丰富下:待提交(草稿)=》已提交(待审核)=》审核通过、审核不通过;另外,工作流需要支持“反审核”,把工作流退回到原点;
+ *
+ * @author 芋道源码
+ */
+@RequiredArgsConstructor
+@Getter
+public enum ErpAuditStatus implements ArrayValuable {
+
+ PROCESS(10, "未审核"), // 审核中
+ APPROVE(20, "已审核"); // 审核通过
+
+ public static final Integer[] ARRAYS = Arrays.stream(values()).map(ErpAuditStatus::getStatus).toArray(Integer[]::new);
+
+ /**
+ * 状态
+ */
+ private final Integer status;
+ /**
+ * 状态名
+ */
+ private final String name;
+
+ @Override
+ public Integer[] array() {
+ return ARRAYS;
+ }
+
+}
diff --git a/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/ErrorCodeConstants.java b/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/ErrorCodeConstants.java
new file mode 100644
index 00000000..65f64c2f
--- /dev/null
+++ b/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/ErrorCodeConstants.java
@@ -0,0 +1,168 @@
+package cn.iocoder.yudao.module.erp.enums;
+
+import cn.iocoder.yudao.framework.common.exception.ErrorCode;
+
+/**
+ * ERP 错误码枚举类
+ *
+ * erp 系统,使用 1-030-000-000 段
+ */
+public interface ErrorCodeConstants {
+
+ // ========== ERP 供应商(1-030-100-000) ==========
+ ErrorCode SUPPLIER_NOT_EXISTS = new ErrorCode(1_030_100_000, "供应商不存在");
+ ErrorCode SUPPLIER_NOT_ENABLE = new ErrorCode(1_030_100_000, "供应商({})未启用");
+
+ // ========== ERP 采购订单(1-030-101-000) ==========
+ ErrorCode PURCHASE_ORDER_NOT_EXISTS = new ErrorCode(1_030_101_000, "采购订单不存在");
+ ErrorCode PURCHASE_ORDER_DELETE_FAIL_APPROVE = new ErrorCode(1_030_101_001, "采购订单({})已审核,无法删除");
+ ErrorCode PURCHASE_ORDER_PROCESS_FAIL = new ErrorCode(1_030_101_002, "反审核失败,只有已审核的采购订单才能反审核");
+ ErrorCode PURCHASE_ORDER_APPROVE_FAIL = new ErrorCode(1_030_101_003, "审核失败,只有未审核的采购订单才能审核");
+ ErrorCode PURCHASE_ORDER_NO_EXISTS = new ErrorCode(1_030_101_004, "生成采购单号失败,请重新提交");
+ ErrorCode PURCHASE_ORDER_UPDATE_FAIL_APPROVE = new ErrorCode(1_030_101_005, "采购订单({})已审核,无法修改");
+ ErrorCode PURCHASE_ORDER_NOT_APPROVE = new ErrorCode(1_030_101_006, "采购订单未审核,无法操作");
+ ErrorCode PURCHASE_ORDER_ITEM_IN_FAIL_PRODUCT_EXCEED = new ErrorCode(1_030_101_007, "采购订单项({})超过最大允许入库数量({})");
+ ErrorCode PURCHASE_ORDER_PROCESS_FAIL_EXISTS_IN = new ErrorCode(1_030_101_008, "反审核失败,已存在对应的采购入库单");
+ErrorCode PURCHASE_ORDER_ITEM_RETURN_FAIL_IN_EXCEED = new ErrorCode(1_030_101_009, "采购订单项({})超过最大允许退货数量({})");
+ ErrorCode PURCHASE_ORDER_PROCESS_FAIL_EXISTS_RETURN = new ErrorCode(1_030_101_010, "反审核失败,已存在对应的采购退货单");
+
+ // ========== ERP 采购入库(1-030-102-000) ==========
+ ErrorCode PURCHASE_IN_NOT_EXISTS = new ErrorCode(1_030_102_000, "采购入库单不存在");
+ ErrorCode PURCHASE_IN_DELETE_FAIL_APPROVE = new ErrorCode(1_030_102_001, "采购入库单({})已审核,无法删除");
+ ErrorCode PURCHASE_IN_PROCESS_FAIL = new ErrorCode(1_030_102_002, "反审核失败,只有已审核的入库单才能反审核");
+ ErrorCode PURCHASE_IN_APPROVE_FAIL = new ErrorCode(1_030_102_003, "审核失败,只有未审核的入库单才能审核");
+ ErrorCode PURCHASE_IN_NO_EXISTS = new ErrorCode(1_030_102_004, "生成入库单失败,请重新提交");
+ ErrorCode PURCHASE_IN_UPDATE_FAIL_APPROVE = new ErrorCode(1_030_102_005, "采购入库单({})已审核,无法修改");
+ ErrorCode PURCHASE_IN_NOT_APPROVE = new ErrorCode(1_030_102_006, "采购入库单未审核,无法操作");
+ ErrorCode PURCHASE_IN_FAIL_PAYMENT_PRICE_EXCEED = new ErrorCode(1_030_102_007, "付款金额({})超过采购入库单总金额({})");
+ ErrorCode PURCHASE_IN_PROCESS_FAIL_EXISTS_PAYMENT = new ErrorCode(1_030_102_008, "反审核失败,已存在对应的付款单");
+
+ // ========== ERP 采购退货(1-030-103-000) ==========
+ ErrorCode PURCHASE_RETURN_NOT_EXISTS = new ErrorCode(1_030_103_000, "采购退货单不存在");
+ ErrorCode PURCHASE_RETURN_DELETE_FAIL_APPROVE = new ErrorCode(1_030_103_001, "采购退货单({})已审核,无法删除");
+ ErrorCode PURCHASE_RETURN_PROCESS_FAIL = new ErrorCode(1_030_103_002, "反审核失败,只有已审核的退货单才能反审核");
+ ErrorCode PURCHASE_RETURN_APPROVE_FAIL = new ErrorCode(1_030_103_003, "审核失败,只有未审核的退货单才能审核");
+ ErrorCode PURCHASE_RETURN_NO_EXISTS = new ErrorCode(1_030_103_004, "生成退货单失败,请重新提交");
+ ErrorCode PURCHASE_RETURN_UPDATE_FAIL_APPROVE = new ErrorCode(1_030_103_005, "采购退货单({})已审核,无法修改");
+ ErrorCode PURCHASE_RETURN_NOT_APPROVE = new ErrorCode(1_030_103_006, "采购退货单未审核,无法操作");
+ ErrorCode PURCHASE_RETURN_FAIL_REFUND_PRICE_EXCEED = new ErrorCode(1_030_103_007, "退款金额({})超过采购退货单总金额({})");
+ ErrorCode PURCHASE_RETURN_PROCESS_FAIL_EXISTS_REFUND = new ErrorCode(1_030_103_008, "反审核失败,已存在对应的退款单");
+
+ // ========== ERP 客户(1-030-200-000)==========
+ ErrorCode CUSTOMER_NOT_EXISTS = new ErrorCode(1_020_200_000, "客户不存在");
+ ErrorCode CUSTOMER_NOT_ENABLE = new ErrorCode(1_020_200_001, "客户({})未启用");
+
+ // ========== ERP 销售订单(1-030-201-000) ==========
+ ErrorCode SALE_ORDER_NOT_EXISTS = new ErrorCode(1_020_201_000, "销售订单不存在");
+ ErrorCode SALE_ORDER_DELETE_FAIL_APPROVE = new ErrorCode(1_020_201_001, "销售订单({})已审核,无法删除");
+ ErrorCode SALE_ORDER_PROCESS_FAIL = new ErrorCode(1_020_201_002, "反审核失败,只有已审核的销售订单才能反审核");
+ ErrorCode SALE_ORDER_APPROVE_FAIL = new ErrorCode(1_020_201_003, "审核失败,只有未审核的销售订单才能审核");
+ ErrorCode SALE_ORDER_NO_EXISTS = new ErrorCode(1_020_201_004, "生成销售单号失败,请重新提交");
+ ErrorCode SALE_ORDER_UPDATE_FAIL_APPROVE = new ErrorCode(1_020_201_005, "销售订单({})已审核,无法修改");
+ ErrorCode SALE_ORDER_NOT_APPROVE = new ErrorCode(1_020_201_006, "销售订单未审核,无法操作");
+ ErrorCode SALE_ORDER_ITEM_OUT_FAIL_PRODUCT_EXCEED = new ErrorCode(1_020_201_007, "销售订单项({})超过最大允许出库数量({})");
+ ErrorCode SALE_ORDER_PROCESS_FAIL_EXISTS_OUT = new ErrorCode(1_020_201_008, "反审核失败,已存在对应的销售出库单");
+ ErrorCode SALE_ORDER_ITEM_RETURN_FAIL_OUT_EXCEED = new ErrorCode(1_020_201_009, "销售订单项({})超过最大允许退货数量({})");
+ ErrorCode SALE_ORDER_PROCESS_FAIL_EXISTS_RETURN = new ErrorCode(1_020_201_010, "反审核失败,已存在对应的销售退货单");
+
+ // ========== ERP 销售出库(1-030-202-000) ==========
+ ErrorCode SALE_OUT_NOT_EXISTS = new ErrorCode(1_020_202_000, "销售出库单不存在");
+ ErrorCode SALE_OUT_DELETE_FAIL_APPROVE = new ErrorCode(1_020_202_001, "销售出库单({})已审核,无法删除");
+ ErrorCode SALE_OUT_PROCESS_FAIL = new ErrorCode(1_020_202_002, "反审核失败,只有已审核的出库单才能反审核");
+ ErrorCode SALE_OUT_APPROVE_FAIL = new ErrorCode(1_020_202_003, "审核失败,只有未审核的出库单才能审核");
+ ErrorCode SALE_OUT_NO_EXISTS = new ErrorCode(1_020_202_004, "生成出库单失败,请重新提交");
+ ErrorCode SALE_OUT_UPDATE_FAIL_APPROVE = new ErrorCode(1_020_202_005, "销售出库单({})已审核,无法修改");
+ ErrorCode SALE_OUT_NOT_APPROVE = new ErrorCode(1_020_202_006, "销售出库单未审核,无法操作");
+ ErrorCode SALE_OUT_FAIL_RECEIPT_PRICE_EXCEED = new ErrorCode(1_020_202_007, "收款金额({})超过销售出库单总金额({})");
+ ErrorCode SALE_OUT_PROCESS_FAIL_EXISTS_RECEIPT = new ErrorCode(1_020_202_008, "反审核失败,已存在对应的收款单");
+
+ // ========== ERP 销售退货(1-030-203-000) ==========
+ ErrorCode SALE_RETURN_NOT_EXISTS = new ErrorCode(1_020_203_000, "销售退货单不存在");
+ ErrorCode SALE_RETURN_DELETE_FAIL_APPROVE = new ErrorCode(1_020_203_001, "销售退货单({})已审核,无法删除");
+ ErrorCode SALE_RETURN_PROCESS_FAIL = new ErrorCode(1_020_203_002, "反审核失败,只有已审核的退货单才能反审核");
+ ErrorCode SALE_RETURN_APPROVE_FAIL = new ErrorCode(1_020_203_003, "审核失败,只有未审核的退货单才能审核");
+ ErrorCode SALE_RETURN_NO_EXISTS = new ErrorCode(1_020_203_004, "生成退货单失败,请重新提交");
+ ErrorCode SALE_RETURN_UPDATE_FAIL_APPROVE = new ErrorCode(1_020_203_005, "销售退货单({})已审核,无法修改");
+ ErrorCode SALE_RETURN_NOT_APPROVE = new ErrorCode(1_020_203_006, "销售退货单未审核,无法操作");
+ ErrorCode SALE_RETURN_FAIL_REFUND_PRICE_EXCEED = new ErrorCode(1_020_203_007, "退款金额({})超过销售退货单总金额({})");
+ ErrorCode SALE_RETURN_PROCESS_FAIL_EXISTS_REFUND = new ErrorCode(1_020_203_008, "反审核失败,已存在对应的退款单");
+
+ // ========== ERP 仓库 1-030-400-000 ==========
+ ErrorCode WAREHOUSE_NOT_EXISTS = new ErrorCode(1_030_400_000, "仓库不存在");
+ ErrorCode WAREHOUSE_NOT_ENABLE = new ErrorCode(1_030_400_001, "仓库({})未启用");
+
+ // ========== ERP 其它入库单 1-030-401-000 ==========
+ ErrorCode STOCK_IN_NOT_EXISTS = new ErrorCode(1_030_401_000, "其它入库单不存在");
+ ErrorCode STOCK_IN_DELETE_FAIL_APPROVE = new ErrorCode(1_030_401_001, "其它入库单({})已审核,无法删除");
+ ErrorCode STOCK_IN_PROCESS_FAIL = new ErrorCode(1_030_401_002, "反审核失败,只有已审核的入库单才能反审核");
+ ErrorCode STOCK_IN_APPROVE_FAIL = new ErrorCode(1_030_401_003, "审核失败,只有未审核的入库单才能审核");
+ ErrorCode STOCK_IN_NO_EXISTS = new ErrorCode(1_030_401_004, "生成入库单失败,请重新提交");
+ ErrorCode STOCK_IN_UPDATE_FAIL_APPROVE = new ErrorCode(1_030_401_005, "其它入库单({})已审核,无法修改");
+
+ // ========== ERP 其它出库单 1-030-402-000 ==========
+ ErrorCode STOCK_OUT_NOT_EXISTS = new ErrorCode(1_030_402_000, "其它出库单不存在");
+ ErrorCode STOCK_OUT_DELETE_FAIL_APPROVE = new ErrorCode(1_030_402_001, "其它出库单({})已审核,无法删除");
+ ErrorCode STOCK_OUT_PROCESS_FAIL = new ErrorCode(1_030_402_002, "反审核失败,只有已审核的出库单才能反审核");
+ ErrorCode STOCK_OUT_APPROVE_FAIL = new ErrorCode(1_030_402_003, "审核失败,只有未审核的出库单才能审核");
+ ErrorCode STOCK_OUT_NO_EXISTS = new ErrorCode(1_030_402_004, "生成出库单失败,请重新提交");
+ ErrorCode STOCK_OUT_UPDATE_FAIL_APPROVE = new ErrorCode(1_030_402_005, "其它出库单({})已审核,无法修改");
+
+ // ========== ERP 库存调拨单 1-030-403-000 ==========
+ ErrorCode STOCK_MOVE_NOT_EXISTS = new ErrorCode(1_030_402_000, "库存调拨单不存在");
+ ErrorCode STOCK_MOVE_DELETE_FAIL_APPROVE = new ErrorCode(1_030_402_001, "库存调拨单({})已审核,无法删除");
+ ErrorCode STOCK_MOVE_PROCESS_FAIL = new ErrorCode(1_030_402_002, "反审核失败,只有已审核的调拨单才能反审核");
+ ErrorCode STOCK_MOVE_APPROVE_FAIL = new ErrorCode(1_030_402_003, "审核失败,只有未审核的调拨单才能审核");
+ ErrorCode STOCK_MOVE_NO_EXISTS = new ErrorCode(1_030_402_004, "生成调拨号失败,请重新提交");
+ ErrorCode STOCK_MOVE_UPDATE_FAIL_APPROVE = new ErrorCode(1_030_402_005, "库存调拨单({})已审核,无法修改");
+
+ // ========== ERP 库存盘点单 1-030-403-000 ==========
+ ErrorCode STOCK_CHECK_NOT_EXISTS = new ErrorCode(1_030_403_000, "库存盘点单不存在");
+ ErrorCode STOCK_CHECK_DELETE_FAIL_APPROVE = new ErrorCode(1_030_403_001, "库存盘点单({})已审核,无法删除");
+ ErrorCode STOCK_CHECK_PROCESS_FAIL = new ErrorCode(1_030_403_002, "反审核失败,只有已审核的盘点单才能反审核");
+ ErrorCode STOCK_CHECK_APPROVE_FAIL = new ErrorCode(1_030_403_003, "审核失败,只有未审核的盘点单才能审核");
+ ErrorCode STOCK_CHECK_NO_EXISTS = new ErrorCode(1_030_403_004, "生成盘点号失败,请重新提交");
+ ErrorCode STOCK_CHECK_UPDATE_FAIL_APPROVE = new ErrorCode(1_030_403_005, "库存盘点单({})已审核,无法修改");
+
+ // ========== ERP 产品库存 1-030-404-000 ==========
+ ErrorCode STOCK_COUNT_NEGATIVE = new ErrorCode(1_030_404_000, "操作失败,产品({})所在仓库({})的库存:{},小于变更数量:{}");
+ ErrorCode STOCK_COUNT_NEGATIVE2 = new ErrorCode(1_030_404_001, "操作失败,产品({})所在仓库({})的库存不足");
+
+ // ========== ERP 产品 1-030-500-000 ==========
+ ErrorCode PRODUCT_NOT_EXISTS = new ErrorCode(1_030_500_000, "产品不存在");
+ ErrorCode PRODUCT_NOT_ENABLE = new ErrorCode(1_030_500_001, "产品({})未启用");
+
+ // ========== ERP 产品分类 1-030-501-000 ==========
+ ErrorCode PRODUCT_CATEGORY_NOT_EXISTS = new ErrorCode(1_030_501_000, "产品分类不存在");
+ ErrorCode PRODUCT_CATEGORY_EXITS_CHILDREN = new ErrorCode(1_030_501_001, "存在存在子产品分类,无法删除");
+ ErrorCode PRODUCT_CATEGORY_PARENT_NOT_EXITS = new ErrorCode(1_030_501_002,"父级产品分类不存在");
+ ErrorCode PRODUCT_CATEGORY_PARENT_ERROR = new ErrorCode(1_030_501_003, "不能设置自己为父产品分类");
+ ErrorCode PRODUCT_CATEGORY_NAME_DUPLICATE = new ErrorCode(1_030_501_004, "已经存在该分类名称的产品分类");
+ ErrorCode PRODUCT_CATEGORY_PARENT_IS_CHILD = new ErrorCode(1_030_501_005, "不能设置自己的子分类为父分类");
+ ErrorCode PRODUCT_CATEGORY_EXITS_PRODUCT = new ErrorCode(1_030_502_002, "存在产品使用该分类,无法删除");
+
+ // ========== ERP 产品单位 1-030-502-000 ==========
+ ErrorCode PRODUCT_UNIT_NOT_EXISTS = new ErrorCode(1_030_502_000, "产品单位不存在");
+ ErrorCode PRODUCT_UNIT_NAME_DUPLICATE = new ErrorCode(1_030_502_001, "已存在该名字的产品单位");
+ ErrorCode PRODUCT_UNIT_EXITS_PRODUCT = new ErrorCode(1_030_502_002, "存在产品使用该单位,无法删除");
+
+ // ========== ERP 结算账户 1-030-600-000 ==========
+ ErrorCode ACCOUNT_NOT_EXISTS = new ErrorCode(1_030_600_000, "结算账户不存在");
+ ErrorCode ACCOUNT_NOT_ENABLE = new ErrorCode(1_030_600_001, "结算账户({})未启用");
+
+ // ========== ERP 付款单 1-030-601-000 ==========
+ ErrorCode FINANCE_PAYMENT_NOT_EXISTS = new ErrorCode(1_030_601_000, "付款单不存在");
+ ErrorCode FINANCE_PAYMENT_DELETE_FAIL_APPROVE = new ErrorCode(1_030_601_001, "付款单({})已审核,无法删除");
+ ErrorCode FINANCE_PAYMENT_PROCESS_FAIL = new ErrorCode(1_030_601_002, "反审核失败,只有已审核的付款单才能反审核");
+ ErrorCode FINANCE_PAYMENT_APPROVE_FAIL = new ErrorCode(1_030_601_003, "审核失败,只有未审核的付款单才能审核");
+ ErrorCode FINANCE_PAYMENT_NO_EXISTS = new ErrorCode(1_030_601_004, "生成付款单号失败,请重新提交");
+ ErrorCode FINANCE_PAYMENT_UPDATE_FAIL_APPROVE = new ErrorCode(1_030_601_005, "付款单({})已审核,无法修改");
+
+ // ========== ERP 收款单 1-030-602-000 ==========
+ ErrorCode FINANCE_RECEIPT_NOT_EXISTS = new ErrorCode(1_030_602_000, "收款单不存在");
+ ErrorCode FINANCE_RECEIPT_DELETE_FAIL_APPROVE = new ErrorCode(1_030_602_001, "收款单({})已审核,无法删除");
+ ErrorCode FINANCE_RECEIPT_PROCESS_FAIL = new ErrorCode(1_030_602_002, "反审核失败,只有已审核的收款单才能反审核");
+ ErrorCode FINANCE_RECEIPT_APPROVE_FAIL = new ErrorCode(1_030_602_003, "审核失败,只有未审核的收款单才能审核");
+ ErrorCode FINANCE_RECEIPT_NO_EXISTS = new ErrorCode(1_030_602_004, "生成收款单号失败,请重新提交");
+ ErrorCode FINANCE_RECEIPT_UPDATE_FAIL_APPROVE = new ErrorCode(1_030_602_005, "收款单({})已审核,无法修改");
+
+}
diff --git a/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/LogRecordConstants.java b/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/LogRecordConstants.java
new file mode 100644
index 00000000..73b72c0a
--- /dev/null
+++ b/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/LogRecordConstants.java
@@ -0,0 +1,12 @@
+package cn.iocoder.yudao.module.erp.enums;
+
+/**
+ * ERP 操作日志枚举
+ * 目的:统一管理,也减少 Service 里各种“复杂”字符串
+ *
+ * @author 芋道源码
+ */
+public interface LogRecordConstants {
+
+
+}
diff --git a/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/common/ErpBizTypeEnum.java b/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/common/ErpBizTypeEnum.java
new file mode 100644
index 00000000..5b09756a
--- /dev/null
+++ b/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/common/ErpBizTypeEnum.java
@@ -0,0 +1,43 @@
+package cn.iocoder.yudao.module.erp.enums.common;
+
+import cn.iocoder.yudao.framework.common.core.ArrayValuable;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+import java.util.Arrays;
+
+/**
+ * ERP 业务类型枚举
+ *
+ * @author HUIHUI
+ */
+@RequiredArgsConstructor
+@Getter
+public enum ErpBizTypeEnum implements ArrayValuable {
+
+ PURCHASE_ORDER(10, "采购订单"),
+ PURCHASE_IN(11, "采购入库"),
+ PURCHASE_RETURN(12, "采购退货"),
+
+ SALE_ORDER(20, "销售订单"),
+ SALE_OUT(21, "销售出库"),
+ SALE_RETURN(22, "销售退货"),
+ ;
+
+ public static final Integer[] ARRAYS = Arrays.stream(values()).map(ErpBizTypeEnum::getType).toArray(Integer[]::new);
+
+ /**
+ * 类型
+ */
+ private final Integer type;
+ /**
+ * 名称
+ */
+ private final String name;
+
+ @Override
+ public Integer[] array() {
+ return ARRAYS;
+ }
+
+}
diff --git a/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/stock/ErpStockRecordBizTypeEnum.java b/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/stock/ErpStockRecordBizTypeEnum.java
new file mode 100644
index 00000000..9ed19c56
--- /dev/null
+++ b/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/stock/ErpStockRecordBizTypeEnum.java
@@ -0,0 +1,63 @@
+package cn.iocoder.yudao.module.erp.enums.stock;
+
+import cn.iocoder.yudao.framework.common.core.ArrayValuable;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+import java.util.Arrays;
+
+/**
+ * ERP 库存明细 - 业务类型枚举
+ *
+ * @author 芋道源码
+ */
+@RequiredArgsConstructor
+@Getter
+public enum ErpStockRecordBizTypeEnum implements ArrayValuable {
+
+ OTHER_IN(10, "其它入库"),
+ OTHER_IN_CANCEL(11, "其它入库(作废)"),
+
+ OTHER_OUT(20, "其它出库"),
+ OTHER_OUT_CANCEL(21, "其它出库(作废)"),
+
+ MOVE_IN(30, "调拨入库"),
+ MOVE_IN_CANCEL(31, "调拨入库(作废)"),
+ MOVE_OUT(32, "调拨出库"),
+ MOVE_OUT_CANCEL(33, "调拨出库(作废)"),
+
+ CHECK_MORE_IN(40, "盘盈入库"),
+ CHECK_MORE_IN_CANCEL(41, "盘盈入库(作废)"),
+ CHECK_LESS_OUT(42, "盘亏出库"),
+ CHECK_LESS_OUT_CANCEL(43, "盘亏出库(作废)"),
+
+ SALE_OUT(50, "销售出库"),
+ SALE_OUT_CANCEL(51, "销售出库(作废)"),
+
+ SALE_RETURN(60, "销售退货入库"),
+ SALE_RETURN_CANCEL(61, "销售退货入库(作废)"),
+
+ PURCHASE_IN(70, "采购入库"),
+ PURCHASE_IN_CANCEL(71, "采购入库(作废)"),
+
+ PURCHASE_RETURN(80, "采购退货出库"),
+ PURCHASE_RETURN_CANCEL(81, "采购退货出库(作废)"),
+ ;
+
+ public static final Integer[] ARRAYS = Arrays.stream(values()).map(ErpStockRecordBizTypeEnum::getType).toArray(Integer[]::new);
+
+ /**
+ * 类型
+ */
+ private final Integer type;
+ /**
+ * 名字
+ */
+ private final String name;
+
+ @Override
+ public Integer[] array() {
+ return ARRAYS;
+ }
+
+}
diff --git a/yudao-module-erp/yudao-module-erp-biz/Dockerfile b/yudao-module-erp/yudao-module-erp-biz/Dockerfile
new file mode 100644
index 00000000..f46f7c83
--- /dev/null
+++ b/yudao-module-erp/yudao-module-erp-biz/Dockerfile
@@ -0,0 +1,19 @@
+## AdoptOpenJDK 停止发布 OpenJDK 二进制,而 Eclipse Temurin 是它的延伸,提供更好的稳定性
+## 感谢复旦核博士的建议!灰子哥,牛皮!
+FROM eclipse-temurin:8-jre
+
+## 创建目录,并使用它作为工作目录
+RUN mkdir -p /yudao-module-erp-biz
+WORKDIR /yudao-module-erp-biz
+## 将后端项目的 Jar 文件,复制到镜像中
+COPY ./target/yudao-module-erp-biz.jar app.jar
+
+## 设置 TZ 时区
+## 设置 JAVA_OPTS 环境变量,可通过 docker run -e "JAVA_OPTS=" 进行覆盖
+ENV TZ=Asia/Shanghai JAVA_OPTS="-Xms512m -Xmx512m"
+
+## 暴露后端项目的 48088 端口
+EXPOSE 48088
+
+## 启动后端项目
+CMD java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar app.jar
diff --git a/yudao-module-erp/yudao-module-erp-biz/pom.xml b/yudao-module-erp/yudao-module-erp-biz/pom.xml
new file mode 100644
index 00000000..412052b8
--- /dev/null
+++ b/yudao-module-erp/yudao-module-erp-biz/pom.xml
@@ -0,0 +1,119 @@
+
+
+
+ cn.iocoder.cloud
+ yudao-module-erp
+ ${revision}
+
+ 4.0.0
+ yudao-module-erp-biz
+
+ ${project.artifactId}
+
+ erp 包下,企业资源管理(Enterprise Resource Planning)。
+ 例如说:采购、销售、库存、财务、产品等等
+
+
+
+
+
+ cn.iocoder.cloud
+ yudao-spring-boot-starter-env
+
+
+
+
+ cn.iocoder.cloud
+ yudao-module-system-api
+ ${revision}
+
+
+ cn.iocoder.cloud
+ yudao-module-erp-api
+ ${revision}
+
+
+
+
+ cn.iocoder.cloud
+ yudao-spring-boot-starter-biz-tenant
+
+
+
+
+ cn.iocoder.cloud
+ yudao-spring-boot-starter-security
+
+
+
+
+ cn.iocoder.cloud
+ yudao-spring-boot-starter-mybatis
+
+
+
+ cn.iocoder.cloud
+ yudao-spring-boot-starter-redis
+
+
+
+
+ cn.iocoder.cloud
+ yudao-spring-boot-starter-rpc
+
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-nacos-discovery
+
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-nacos-config
+
+
+
+
+ cn.iocoder.cloud
+ yudao-spring-boot-starter-excel
+
+
+
+
+ cn.iocoder.cloud
+ yudao-spring-boot-starter-monitor
+
+
+
+
+ cn.iocoder.cloud
+ yudao-spring-boot-starter-test
+
+
+
+
+
+
+ ${project.artifactId}
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ ${spring.boot.version}
+
+
+
+ repackage
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/ErpServerApplication.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/ErpServerApplication.java
new file mode 100644
index 00000000..07a3cd6e
--- /dev/null
+++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/ErpServerApplication.java
@@ -0,0 +1,30 @@
+package cn.iocoder.yudao.module.erp;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+/**
+ * 项目的启动类
+ *
+ * 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
+ * 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
+ * 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
+ *
+ * @author 芋道源码
+ */
+@SpringBootApplication
+public class ErpServerApplication {
+
+ public static void main(String[] args) {
+ // 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
+ // 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
+ // 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
+
+ SpringApplication.run(ErpServerApplication.class, args);
+
+ // 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
+ // 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
+ // 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
+ }
+
+}
diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/ErpAccountController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/ErpAccountController.java
new file mode 100644
index 00000000..84c4c661
--- /dev/null
+++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/ErpAccountController.java
@@ -0,0 +1,113 @@
+package cn.iocoder.yudao.module.erp.controller.admin.finance;
+
+import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.account.ErpAccountPageReqVO;
+import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.account.ErpAccountRespVO;
+import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.account.ErpAccountSaveReqVO;
+import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpAccountDO;
+import cn.iocoder.yudao.module.erp.service.finance.ErpAccountService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Parameters;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
+import javax.validation.Valid;
+import java.io.IOException;
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
+
+@Tag(name = "管理后台 - ERP 结算账户")
+@RestController
+@RequestMapping("/erp/account")
+@Validated
+public class ErpAccountController {
+
+ @Resource
+ private ErpAccountService accountService;
+
+ @PostMapping("/create")
+ @Operation(summary = "创建结算账户")
+ @PreAuthorize("@ss.hasPermission('erp:account:create')")
+ public CommonResult createAccount(@Valid @RequestBody ErpAccountSaveReqVO createReqVO) {
+ return success(accountService.createAccount(createReqVO));
+ }
+
+ @PutMapping("/update")
+ @Operation(summary = "更新结算账户")
+ @PreAuthorize("@ss.hasPermission('erp:account:update')")
+ public CommonResult updateAccount(@Valid @RequestBody ErpAccountSaveReqVO updateReqVO) {
+ accountService.updateAccount(updateReqVO);
+ return success(true);
+ }
+
+ @PutMapping("/update-default-status")
+ @Operation(summary = "更新结算账户默认状态")
+ @Parameters({
+ @Parameter(name = "id", description = "编号", required = true),
+ @Parameter(name = "status", description = "状态", required = true)
+ })
+ public CommonResult updateAccountDefaultStatus(@RequestParam("id") Long id,
+ @RequestParam("defaultStatus") Boolean defaultStatus) {
+ accountService.updateAccountDefaultStatus(id, defaultStatus);
+ return success(true);
+ }
+
+ @DeleteMapping("/delete")
+ @Operation(summary = "删除结算账户")
+ @Parameter(name = "id", description = "编号", required = true)
+ @PreAuthorize("@ss.hasPermission('erp:account:delete')")
+ public CommonResult deleteAccount(@RequestParam("id") Long id) {
+ accountService.deleteAccount(id);
+ return success(true);
+ }
+
+ @GetMapping("/get")
+ @Operation(summary = "获得结算账户")
+ @Parameter(name = "id", description = "编号", required = true, example = "1024")
+ @PreAuthorize("@ss.hasPermission('erp:account:query')")
+ public CommonResult getAccount(@RequestParam("id") Long id) {
+ ErpAccountDO account = accountService.getAccount(id);
+ return success(BeanUtils.toBean(account, ErpAccountRespVO.class));
+ }
+
+ @GetMapping("/simple-list")
+ @Operation(summary = "获得结算账户精简列表", description = "只包含被开启的结算账户,主要用于前端的下拉选项")
+ public CommonResult> getWarehouseSimpleList() {
+ List list = accountService.getAccountListByStatus(CommonStatusEnum.ENABLE.getStatus());
+ return success(convertList(list, account -> new ErpAccountRespVO().setId(account.getId())
+ .setName(account.getName()).setDefaultStatus(account.getDefaultStatus())));
+ }
+
+ @GetMapping("/page")
+ @Operation(summary = "获得结算账户分页")
+ @PreAuthorize("@ss.hasPermission('erp:account:query')")
+ public CommonResult> getAccountPage(@Valid ErpAccountPageReqVO pageReqVO) {
+ PageResult pageResult = accountService.getAccountPage(pageReqVO);
+ return success(BeanUtils.toBean(pageResult, ErpAccountRespVO.class));
+ }
+
+ @GetMapping("/export-excel")
+ @Operation(summary = "导出结算账户 Excel")
+ @PreAuthorize("@ss.hasPermission('erp:account:export')")
+ public void exportAccountExcel(@Valid ErpAccountPageReqVO pageReqVO,
+ HttpServletResponse response) throws IOException {
+ pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+ List list = accountService.getAccountPage(pageReqVO).getList();
+ // 导出 Excel
+ ExcelUtils.write(response, "结算账户.xls", "数据", ErpAccountRespVO.class,
+ BeanUtils.toBean(list, ErpAccountRespVO.class));
+ }
+
+}
diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/ErpFinancePaymentController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/ErpFinancePaymentController.java
new file mode 100644
index 00000000..c3de50a3
--- /dev/null
+++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/ErpFinancePaymentController.java
@@ -0,0 +1,150 @@
+package cn.iocoder.yudao.module.erp.controller.admin.finance;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
+import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.payment.ErpFinancePaymentPageReqVO;
+import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.payment.ErpFinancePaymentRespVO;
+import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.payment.ErpFinancePaymentSaveReqVO;
+import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpAccountDO;
+import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinancePaymentDO;
+import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinancePaymentItemDO;
+import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpSupplierDO;
+import cn.iocoder.yudao.module.erp.service.finance.ErpAccountService;
+import cn.iocoder.yudao.module.erp.service.finance.ErpFinancePaymentService;
+import cn.iocoder.yudao.module.erp.service.purchase.ErpSupplierService;
+import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
+import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
+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.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
+import javax.validation.Valid;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Stream;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
+
+@Tag(name = "管理后台 - ERP 付款单")
+@RestController
+@RequestMapping("/erp/finance-payment")
+@Validated
+public class ErpFinancePaymentController {
+
+ @Resource
+ private ErpFinancePaymentService financePaymentService;
+ @Resource
+ private ErpSupplierService supplierService;
+ @Resource
+ private ErpAccountService accountService;
+
+ @Resource
+ private AdminUserApi adminUserApi;
+
+ @PostMapping("/create")
+ @Operation(summary = "创建付款单")
+ @PreAuthorize("@ss.hasPermission('erp:finance-payment:create')")
+ public CommonResult createFinancePayment(@Valid @RequestBody ErpFinancePaymentSaveReqVO createReqVO) {
+ return success(financePaymentService.createFinancePayment(createReqVO));
+ }
+
+ @PutMapping("/update")
+ @Operation(summary = "更新付款单")
+ @PreAuthorize("@ss.hasPermission('erp:finance-payment:update')")
+ public CommonResult updateFinancePayment(@Valid @RequestBody ErpFinancePaymentSaveReqVO updateReqVO) {
+ financePaymentService.updateFinancePayment(updateReqVO);
+ return success(true);
+ }
+
+ @PutMapping("/update-status")
+ @Operation(summary = "更新付款单的状态")
+ @PreAuthorize("@ss.hasPermission('erp:finance-payment:update-status')")
+ public CommonResult updateFinancePaymentStatus(@RequestParam("id") Long id,
+ @RequestParam("status") Integer status) {
+ financePaymentService.updateFinancePaymentStatus(id, status);
+ return success(true);
+ }
+
+ @DeleteMapping("/delete")
+ @Operation(summary = "删除付款单")
+ @Parameter(name = "ids", description = "编号数组", required = true)
+ @PreAuthorize("@ss.hasPermission('erp:finance-payment:delete')")
+ public CommonResult deleteFinancePayment(@RequestParam("ids") List ids) {
+ financePaymentService.deleteFinancePayment(ids);
+ return success(true);
+ }
+
+ @GetMapping("/get")
+ @Operation(summary = "获得付款单")
+ @Parameter(name = "id", description = "编号", required = true, example = "1024")
+ @PreAuthorize("@ss.hasPermission('erp:finance-payment:query')")
+ public CommonResult getFinancePayment(@RequestParam("id") Long id) {
+ ErpFinancePaymentDO payment = financePaymentService.getFinancePayment(id);
+ if (payment == null) {
+ return success(null);
+ }
+ List paymentItemList = financePaymentService.getFinancePaymentItemListByPaymentId(id);
+ return success(BeanUtils.toBean(payment, ErpFinancePaymentRespVO.class, financePaymentVO ->
+ financePaymentVO.setItems(BeanUtils.toBean(paymentItemList, ErpFinancePaymentRespVO.Item.class))));
+ }
+
+ @GetMapping("/page")
+ @Operation(summary = "获得付款单分页")
+ @PreAuthorize("@ss.hasPermission('erp:finance-payment:query')")
+ public CommonResult> getFinancePaymentPage(@Valid ErpFinancePaymentPageReqVO pageReqVO) {
+ PageResult pageResult = financePaymentService.getFinancePaymentPage(pageReqVO);
+ return success(buildFinancePaymentVOPageResult(pageResult));
+ }
+
+ @GetMapping("/export-excel")
+ @Operation(summary = "导出付款单 Excel")
+ @PreAuthorize("@ss.hasPermission('erp:finance-payment:export')")
+ public void exportFinancePaymentExcel(@Valid ErpFinancePaymentPageReqVO pageReqVO,
+ HttpServletResponse response) throws IOException {
+ pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+ List list = buildFinancePaymentVOPageResult(financePaymentService.getFinancePaymentPage(pageReqVO)).getList();
+ // 导出 Excel
+ ExcelUtils.write(response, "付款单.xls", "数据", ErpFinancePaymentRespVO.class, list);
+ }
+
+ private PageResult buildFinancePaymentVOPageResult(PageResult pageResult) {
+ if (CollUtil.isEmpty(pageResult.getList())) {
+ return PageResult.empty(pageResult.getTotal());
+ }
+ // 1.1 付款项
+ List paymentItemList = financePaymentService.getFinancePaymentItemListByPaymentIds(
+ convertSet(pageResult.getList(), ErpFinancePaymentDO::getId));
+ Map> financePaymentItemMap = convertMultiMap(paymentItemList, ErpFinancePaymentItemDO::getPaymentId);
+ // 1.2 供应商信息
+ Map supplierMap = supplierService.getSupplierMap(
+ convertSet(pageResult.getList(), ErpFinancePaymentDO::getSupplierId));
+ // 1.3 结算账户信息
+ Map