From 27807c14eba195ded0bcc2aaa0d655f46a4312c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E6=98=8A=E5=A4=A9?= <15984976+n2319_0@user.noreply.gitee.com> Date: Wed, 17 Jun 2026 19:31:10 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AE=A2=E5=8D=95=E5=BC=82=E8=AE=AE=E5=BC=80?= =?UTF-8?q?=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gear-oa/pom.xml | 4 + .../OrderDisputeFlowController.java | 52 ++ .../domain/bo/OrderDisputeFlowConfirmBo.java | 12 + .../oa/domain/bo/OrderDisputeFlowStartBo.java | 26 + .../oa/domain/vo/OrderDisputeFlowStepVo.java | 22 + .../gear/oa/domain/vo/OrderDisputeFlowVo.java | 38 + .../oa/domain/vo/OrderDisputeTaskListVo.java | 31 + .../oa/domain/vo/OrderDisputeTodoTaskVo.java | 28 + .../oa/service/IOrderDisputeFlowService.java | 22 + .../impl/OrderDisputeFlowServiceImpl.java | 410 +++++++++++ .../workflow/OrderDisputeProcessDeployer.java | 47 ++ .../processes/oa_order_dispute.bpmn20.xml | 27 + gear-ui3/src/api/oa/orderDisputeFlow.js | 40 ++ gear-ui3/src/components/UserSelect/index.vue | 17 +- gear-ui3/src/views/oms/customer/index.vue | 14 +- gear-ui3/src/views/oms/dispute/index.vue | 473 +++++++++++++ .../views/oms/order/panels/disputeFlow.vue | 432 ++++++++++++ .../oms/returnExchange/summary/index.vue | 121 ++++ index.html | 649 ++++++++++++++++++ 19 files changed, 2455 insertions(+), 10 deletions(-) create mode 100644 gear-oa/src/main/java/com/gear/oa/controller/OrderDisputeFlowController.java create mode 100644 gear-oa/src/main/java/com/gear/oa/domain/bo/OrderDisputeFlowConfirmBo.java create mode 100644 gear-oa/src/main/java/com/gear/oa/domain/bo/OrderDisputeFlowStartBo.java create mode 100644 gear-oa/src/main/java/com/gear/oa/domain/vo/OrderDisputeFlowStepVo.java create mode 100644 gear-oa/src/main/java/com/gear/oa/domain/vo/OrderDisputeFlowVo.java create mode 100644 gear-oa/src/main/java/com/gear/oa/domain/vo/OrderDisputeTaskListVo.java create mode 100644 gear-oa/src/main/java/com/gear/oa/domain/vo/OrderDisputeTodoTaskVo.java create mode 100644 gear-oa/src/main/java/com/gear/oa/service/IOrderDisputeFlowService.java create mode 100644 gear-oa/src/main/java/com/gear/oa/service/impl/OrderDisputeFlowServiceImpl.java create mode 100644 gear-oa/src/main/java/com/gear/oa/workflow/OrderDisputeProcessDeployer.java create mode 100644 gear-oa/src/main/resources/processes/oa_order_dispute.bpmn20.xml create mode 100644 gear-ui3/src/api/oa/orderDisputeFlow.js create mode 100644 gear-ui3/src/views/oms/dispute/index.vue create mode 100644 gear-ui3/src/views/oms/order/panels/disputeFlow.vue create mode 100644 index.html diff --git a/gear-oa/pom.xml b/gear-oa/pom.xml index d7d0cd8..755dcbe 100644 --- a/gear-oa/pom.xml +++ b/gear-oa/pom.xml @@ -21,6 +21,10 @@ com.gear gear-common + + com.gear + gear-flowable + org.springframework.boot diff --git a/gear-oa/src/main/java/com/gear/oa/controller/OrderDisputeFlowController.java b/gear-oa/src/main/java/com/gear/oa/controller/OrderDisputeFlowController.java new file mode 100644 index 0000000..4b77d92 --- /dev/null +++ b/gear-oa/src/main/java/com/gear/oa/controller/OrderDisputeFlowController.java @@ -0,0 +1,52 @@ +package com.gear.oa.controller; + +import com.gear.common.core.controller.BaseController; +import com.gear.common.core.domain.R; +import com.gear.oa.domain.bo.OrderDisputeFlowConfirmBo; +import com.gear.oa.domain.bo.OrderDisputeFlowStartBo; +import com.gear.oa.domain.vo.OrderDisputeFlowVo; +import com.gear.oa.domain.vo.OrderDisputeTaskListVo; +import com.gear.oa.domain.vo.OrderDisputeTodoTaskVo; +import com.gear.oa.service.IOrderDisputeFlowService; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/oa/orderDisputeFlow") +public class OrderDisputeFlowController extends BaseController { + + private final IOrderDisputeFlowService orderDisputeFlowService; + + @GetMapping("/byOrder/{orderId}") + public R queryByOrderId(@PathVariable Long orderId) { + return R.ok(orderDisputeFlowService.queryByOrderId(orderId)); + } + + @PostMapping("/start") + public R start(@RequestBody OrderDisputeFlowStartBo bo) { + return R.ok(orderDisputeFlowService.start(bo)); + } + + @PostMapping("/confirm") + public R confirm(@RequestBody OrderDisputeFlowConfirmBo bo) { + orderDisputeFlowService.confirm(bo); + return R.ok(); + } + + @GetMapping("/todoList") + public R> todoList(@RequestParam(required = false) String stepKey) { + return R.ok(orderDisputeFlowService.todoList(stepKey)); + } + + @GetMapping("/taskList") + public R> taskList(@RequestParam(required = false) String stepKey, + @RequestParam(required = false) String status, + @RequestParam(required = false) Integer limit) { + return R.ok(orderDisputeFlowService.taskList(stepKey, status, limit)); + } +} diff --git a/gear-oa/src/main/java/com/gear/oa/domain/bo/OrderDisputeFlowConfirmBo.java b/gear-oa/src/main/java/com/gear/oa/domain/bo/OrderDisputeFlowConfirmBo.java new file mode 100644 index 0000000..8d8d8b7 --- /dev/null +++ b/gear-oa/src/main/java/com/gear/oa/domain/bo/OrderDisputeFlowConfirmBo.java @@ -0,0 +1,12 @@ +package com.gear.oa.domain.bo; + +import lombok.Data; + +@Data +public class OrderDisputeFlowConfirmBo { + + private Long orderId; + + private String comment; +} + diff --git a/gear-oa/src/main/java/com/gear/oa/domain/bo/OrderDisputeFlowStartBo.java b/gear-oa/src/main/java/com/gear/oa/domain/bo/OrderDisputeFlowStartBo.java new file mode 100644 index 0000000..666e493 --- /dev/null +++ b/gear-oa/src/main/java/com/gear/oa/domain/bo/OrderDisputeFlowStartBo.java @@ -0,0 +1,26 @@ +package com.gear.oa.domain.bo; + +import lombok.Data; + +@Data +public class OrderDisputeFlowStartBo { + + private Long orderId; + + private String orderCode; + + private Long customerId; + + private String customerName; + + private Long acceptUserId; + + private Long physicalUserId; + + private Long analysisUserId; + + private Long planUserId; + + private Long visitUserId; +} + diff --git a/gear-oa/src/main/java/com/gear/oa/domain/vo/OrderDisputeFlowStepVo.java b/gear-oa/src/main/java/com/gear/oa/domain/vo/OrderDisputeFlowStepVo.java new file mode 100644 index 0000000..0450a41 --- /dev/null +++ b/gear-oa/src/main/java/com/gear/oa/domain/vo/OrderDisputeFlowStepVo.java @@ -0,0 +1,22 @@ +package com.gear.oa.domain.vo; + +import lombok.Data; + +import java.util.Date; + +@Data +public class OrderDisputeFlowStepVo { + + private String stepKey; + + private String stepName; + + private String assignee; + + private Date startTime; + + private Date endTime; + + private String comment; +} + diff --git a/gear-oa/src/main/java/com/gear/oa/domain/vo/OrderDisputeFlowVo.java b/gear-oa/src/main/java/com/gear/oa/domain/vo/OrderDisputeFlowVo.java new file mode 100644 index 0000000..e789a70 --- /dev/null +++ b/gear-oa/src/main/java/com/gear/oa/domain/vo/OrderDisputeFlowVo.java @@ -0,0 +1,38 @@ +package com.gear.oa.domain.vo; + +import lombok.Data; + +import java.util.Date; +import java.util.List; +import java.util.Map; + +@Data +public class OrderDisputeFlowVo { + + private Long orderId; + + private String orderCode; + + private String customerName; + + private String procInsId; + + private String status; + + private String currentTaskId; + + private String currentTaskKey; + + private String currentTaskName; + + private String currentAssignee; + + private Date startTime; + + private Date endTime; + + private Map variables; + + private List steps; +} + diff --git a/gear-oa/src/main/java/com/gear/oa/domain/vo/OrderDisputeTaskListVo.java b/gear-oa/src/main/java/com/gear/oa/domain/vo/OrderDisputeTaskListVo.java new file mode 100644 index 0000000..ecb40c7 --- /dev/null +++ b/gear-oa/src/main/java/com/gear/oa/domain/vo/OrderDisputeTaskListVo.java @@ -0,0 +1,31 @@ +package com.gear.oa.domain.vo; + +import lombok.Data; + +import java.util.Date; + +@Data +public class OrderDisputeTaskListVo { + + private String taskId; + + private String taskKey; + + private String taskName; + + private String procInsId; + + private Long orderId; + + private String orderCode; + + private Long customerId; + + private String customerName; + + private Date createTime; + + private Date endTime; + + private String status; +} diff --git a/gear-oa/src/main/java/com/gear/oa/domain/vo/OrderDisputeTodoTaskVo.java b/gear-oa/src/main/java/com/gear/oa/domain/vo/OrderDisputeTodoTaskVo.java new file mode 100644 index 0000000..40cf201 --- /dev/null +++ b/gear-oa/src/main/java/com/gear/oa/domain/vo/OrderDisputeTodoTaskVo.java @@ -0,0 +1,28 @@ +package com.gear.oa.domain.vo; + +import lombok.Data; + +import java.util.Date; + +@Data +public class OrderDisputeTodoTaskVo { + + private String taskId; + + private String taskKey; + + private String taskName; + + private String procInsId; + + private Long orderId; + + private String orderCode; + + private Long customerId; + + private String customerName; + + private Date createTime; +} + diff --git a/gear-oa/src/main/java/com/gear/oa/service/IOrderDisputeFlowService.java b/gear-oa/src/main/java/com/gear/oa/service/IOrderDisputeFlowService.java new file mode 100644 index 0000000..ba5dfd3 --- /dev/null +++ b/gear-oa/src/main/java/com/gear/oa/service/IOrderDisputeFlowService.java @@ -0,0 +1,22 @@ +package com.gear.oa.service; + +import com.gear.oa.domain.bo.OrderDisputeFlowConfirmBo; +import com.gear.oa.domain.bo.OrderDisputeFlowStartBo; +import com.gear.oa.domain.vo.OrderDisputeFlowVo; +import com.gear.oa.domain.vo.OrderDisputeTaskListVo; +import com.gear.oa.domain.vo.OrderDisputeTodoTaskVo; + +import java.util.List; + +public interface IOrderDisputeFlowService { + + OrderDisputeFlowVo queryByOrderId(Long orderId); + + String start(OrderDisputeFlowStartBo bo); + + void confirm(OrderDisputeFlowConfirmBo bo); + + List todoList(String stepKey); + + List taskList(String stepKey, String status, Integer limit); +} diff --git a/gear-oa/src/main/java/com/gear/oa/service/impl/OrderDisputeFlowServiceImpl.java b/gear-oa/src/main/java/com/gear/oa/service/impl/OrderDisputeFlowServiceImpl.java new file mode 100644 index 0000000..50668c7 --- /dev/null +++ b/gear-oa/src/main/java/com/gear/oa/service/impl/OrderDisputeFlowServiceImpl.java @@ -0,0 +1,410 @@ +package com.gear.oa.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.map.MapUtil; +import com.gear.common.exception.ServiceException; +import com.gear.common.helper.LoginHelper; +import com.gear.common.utils.StringUtils; +import com.gear.flowable.common.enums.FlowComment; +import com.gear.oa.domain.bo.OrderDisputeFlowConfirmBo; +import com.gear.oa.domain.bo.OrderDisputeFlowStartBo; +import com.gear.oa.domain.vo.OrderDisputeFlowStepVo; +import com.gear.oa.domain.vo.OrderDisputeFlowVo; +import com.gear.oa.domain.vo.OrderDisputeTaskListVo; +import com.gear.oa.domain.vo.OrderDisputeTodoTaskVo; +import com.gear.oa.service.IOrderDisputeFlowService; +import com.gear.oa.workflow.OrderDisputeProcessDeployer; +import lombok.RequiredArgsConstructor; +import org.flowable.engine.HistoryService; +import org.flowable.engine.IdentityService; +import org.flowable.engine.RepositoryService; +import org.flowable.engine.RuntimeService; +import org.flowable.engine.TaskService; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.task.api.Task; +import org.flowable.task.api.TaskQuery; +import org.flowable.task.api.history.HistoricTaskInstance; +import org.flowable.task.api.history.HistoricTaskInstanceQuery; +import org.flowable.engine.task.Comment; +import org.springframework.stereotype.Service; + +import java.util.*; +import java.util.stream.Collectors; + +@RequiredArgsConstructor +@Service +public class OrderDisputeFlowServiceImpl implements IOrderDisputeFlowService { + + private final RepositoryService repositoryService; + private final RuntimeService runtimeService; + private final TaskService taskService; + private final HistoryService historyService; + private final IdentityService identityService; + + @Override + public OrderDisputeFlowVo queryByOrderId(Long orderId) { + if (orderId == null) { + return null; + } + + ProcessInstance active = runtimeService.createProcessInstanceQuery() + .processDefinitionKey(OrderDisputeProcessDeployer.PROCESS_KEY) + .variableValueEquals("orderId", orderId) + .active() + .singleResult(); + if (active != null) { + Map runtimeVars = runtimeService.getVariables(active.getProcessInstanceId()); + return buildRunningFlowVo(orderId, active.getProcessInstanceId(), runtimeVars); + } + + HistoricProcessInstance historic = historyService.createHistoricProcessInstanceQuery() + .processDefinitionKey(OrderDisputeProcessDeployer.PROCESS_KEY) + .variableValueEquals("orderId", orderId) + .includeProcessVariables() + .orderByProcessInstanceStartTime() + .desc() + .listPage(0, 1) + .stream() + .findFirst() + .orElse(null); + if (historic == null) { + return null; + } + return buildFlowVo(orderId, historic); + } + + @Override + public String start(OrderDisputeFlowStartBo bo) { + if (bo == null || bo.getOrderId() == null) { + throw new ServiceException("orderId不能为空"); + } + + Long orderId = bo.getOrderId(); + ProcessInstance active = runtimeService.createProcessInstanceQuery() + .processDefinitionKey(OrderDisputeProcessDeployer.PROCESS_KEY) + .variableValueEquals("orderId", orderId) + .active() + .singleResult(); + if (active != null) { + return active.getProcessInstanceId(); + } + + Long userId = LoginHelper.getUserId(); + if (userId == null) { + throw new ServiceException("未登录"); + } + String userIdStr = String.valueOf(userId); + + Map variables = new HashMap<>(); + variables.put("orderId", orderId); + if (StringUtils.isNotBlank(bo.getOrderCode())) { + variables.put("orderCode", bo.getOrderCode()); + } + if (bo.getCustomerId() != null) { + variables.put("customerId", bo.getCustomerId()); + } + if (StringUtils.isNotBlank(bo.getCustomerName())) { + variables.put("customerName", bo.getCustomerName()); + } + + variables.put("acceptUserId", String.valueOf(bo.getAcceptUserId() != null ? bo.getAcceptUserId() : userId)); + variables.put("physicalUserId", String.valueOf(bo.getPhysicalUserId() != null ? bo.getPhysicalUserId() : userId)); + variables.put("analysisUserId", String.valueOf(bo.getAnalysisUserId() != null ? bo.getAnalysisUserId() : userId)); + variables.put("planUserId", String.valueOf(bo.getPlanUserId() != null ? bo.getPlanUserId() : userId)); + variables.put("visitUserId", String.valueOf(bo.getVisitUserId() != null ? bo.getVisitUserId() : userId)); + + identityService.setAuthenticatedUserId(userIdStr); + ProcessInstance instance = runtimeService.startProcessInstanceByKey(OrderDisputeProcessDeployer.PROCESS_KEY, variables); + return instance.getProcessInstanceId(); + } + + @Override + public void confirm(OrderDisputeFlowConfirmBo bo) { + if (bo == null || bo.getOrderId() == null) { + throw new ServiceException("orderId不能为空"); + } + + Long userId = LoginHelper.getUserId(); + if (userId == null) { + throw new ServiceException("未登录"); + } + String userIdStr = String.valueOf(userId); + + ProcessInstance active = runtimeService.createProcessInstanceQuery() + .processDefinitionKey(OrderDisputeProcessDeployer.PROCESS_KEY) + .variableValueEquals("orderId", bo.getOrderId()) + .active() + .singleResult(); + if (active == null) { + throw new ServiceException("异议流程不存在或已结束"); + } + + Task task = taskService.createTaskQuery() + .processInstanceId(active.getProcessInstanceId()) + .active() + .singleResult(); + if (task == null) { + throw new ServiceException("当前没有可处理的节点"); + } + if (!StringUtils.equals(task.getAssignee(), userIdStr)) { + throw new ServiceException("当前节点不是你的待办"); + } + + identityService.setAuthenticatedUserId(userIdStr); + String comment = bo.getComment() != null ? String.valueOf(bo.getComment()) : ""; + taskService.addComment(task.getId(), active.getProcessInstanceId(), FlowComment.NORMAL.getType(), comment); + taskService.complete(task.getId()); + } + + @Override + public List todoList(String stepKey) { + Long userId = LoginHelper.getUserId(); + if (userId == null) { + throw new ServiceException("未登录"); + } + String userIdStr = String.valueOf(userId); + + TaskQuery query = taskService.createTaskQuery() + .processDefinitionKey(OrderDisputeProcessDeployer.PROCESS_KEY) + .active() + .includeProcessVariables() + .taskAssignee(userIdStr) + .orderByTaskCreateTime() + .desc(); + if (StringUtils.isNotBlank(stepKey)) { + query.taskDefinitionKey(stepKey); + } + + List list = query.list(); + if (CollUtil.isEmpty(list)) { + return Collections.emptyList(); + } + + return list.stream().map(task -> { + Map vars = task.getProcessVariables(); + OrderDisputeTodoTaskVo vo = new OrderDisputeTodoTaskVo(); + vo.setTaskId(task.getId()); + vo.setTaskKey(task.getTaskDefinitionKey()); + vo.setTaskName(task.getName()); + vo.setProcInsId(task.getProcessInstanceId()); + vo.setCreateTime(task.getCreateTime()); + vo.setOrderId(MapUtil.getLong(vars, "orderId")); + vo.setOrderCode(MapUtil.getStr(vars, "orderCode")); + vo.setCustomerId(MapUtil.getLong(vars, "customerId")); + vo.setCustomerName(MapUtil.getStr(vars, "customerName")); + return vo; + }).collect(Collectors.toList()); + } + + @Override + public List taskList(String stepKey, String status, Integer limit) { + Long userId = LoginHelper.getUserId(); + if (userId == null) { + throw new ServiceException("未登录"); + } + String userIdStr = String.valueOf(userId); + + String statusVal = StringUtils.isNotBlank(status) ? status.trim().toLowerCase() : "todo"; + boolean includeTodo = "todo".equals(statusVal) || "all".equals(statusVal); + boolean includeDone = "done".equals(statusVal) || "all".equals(statusVal); + + int limitVal = limit != null ? limit : 200; + if (limitVal <= 0) { + limitVal = 200; + } + if (limitVal > 500) { + limitVal = 500; + } + + List result = new ArrayList<>(); + + if (includeTodo) { + TaskQuery query = taskService.createTaskQuery() + .processDefinitionKey(OrderDisputeProcessDeployer.PROCESS_KEY) + .active() + .includeProcessVariables() + .taskAssignee(userIdStr) + .orderByTaskCreateTime() + .desc(); + if (StringUtils.isNotBlank(stepKey)) { + query.taskDefinitionKey(stepKey); + } + List todo = query.listPage(0, limitVal); + if (CollUtil.isNotEmpty(todo)) { + result.addAll(todo.stream().map(task -> { + Map vars = task.getProcessVariables(); + OrderDisputeTaskListVo vo = new OrderDisputeTaskListVo(); + vo.setTaskId(task.getId()); + vo.setTaskKey(task.getTaskDefinitionKey()); + vo.setTaskName(task.getName()); + vo.setProcInsId(task.getProcessInstanceId()); + vo.setCreateTime(task.getCreateTime()); + vo.setOrderId(MapUtil.getLong(vars, "orderId")); + vo.setOrderCode(MapUtil.getStr(vars, "orderCode")); + vo.setCustomerId(MapUtil.getLong(vars, "customerId")); + vo.setCustomerName(MapUtil.getStr(vars, "customerName")); + vo.setStatus("TODO"); + return vo; + }).collect(Collectors.toList())); + } + } + + if (includeDone) { + HistoricTaskInstanceQuery query = historyService.createHistoricTaskInstanceQuery() + .processDefinitionKey(OrderDisputeProcessDeployer.PROCESS_KEY) + .finished() + .taskAssignee(userIdStr) + .includeProcessVariables() + .orderByHistoricTaskInstanceEndTime() + .desc(); + if (StringUtils.isNotBlank(stepKey)) { + query.taskDefinitionKey(stepKey); + } + + List done = query.listPage(0, limitVal); + if (CollUtil.isNotEmpty(done)) { + result.addAll(done.stream().map(task -> { + Map vars = task.getProcessVariables(); + OrderDisputeTaskListVo vo = new OrderDisputeTaskListVo(); + vo.setTaskId(task.getId()); + vo.setTaskKey(task.getTaskDefinitionKey()); + vo.setTaskName(task.getName()); + vo.setProcInsId(task.getProcessInstanceId()); + vo.setCreateTime(task.getStartTime()); + vo.setEndTime(task.getEndTime()); + vo.setOrderId(MapUtil.getLong(vars, "orderId")); + vo.setOrderCode(MapUtil.getStr(vars, "orderCode")); + vo.setCustomerId(MapUtil.getLong(vars, "customerId")); + vo.setCustomerName(MapUtil.getStr(vars, "customerName")); + vo.setStatus("DONE"); + return vo; + }).collect(Collectors.toList())); + } + } + + if (CollUtil.isEmpty(result)) { + return Collections.emptyList(); + } + + result.sort((a, b) -> { + if (a == null || b == null) { + return 0; + } + int statusCompare = 0; + if ("TODO".equals(a.getStatus()) && "DONE".equals(b.getStatus())) { + statusCompare = -1; + } else if ("DONE".equals(a.getStatus()) && "TODO".equals(b.getStatus())) { + statusCompare = 1; + } + if (statusCompare != 0) { + return statusCompare; + } + Date at = a.getCreateTime(); + Date bt = b.getCreateTime(); + if (at == null && bt == null) { + return 0; + } + if (at == null) { + return 1; + } + if (bt == null) { + return -1; + } + return bt.compareTo(at); + }); + return result; + } + + private OrderDisputeFlowVo buildFlowVo(Long orderId, HistoricProcessInstance historic) { + OrderDisputeFlowVo vo = new OrderDisputeFlowVo(); + vo.setOrderId(orderId); + vo.setProcInsId(historic.getId()); + vo.setStartTime(historic.getStartTime()); + vo.setEndTime(historic.getEndTime()); + vo.setStatus(historic.getEndTime() == null ? "RUNNING" : "FINISHED"); + + Map variables = historic.getProcessVariables() != null ? historic.getProcessVariables() : Collections.emptyMap(); + applyVariables(vo, variables); + + Task currentTask = taskService.createTaskQuery().processInstanceId(historic.getId()).active().singleResult(); + if (currentTask != null) { + vo.setCurrentTaskId(currentTask.getId()); + vo.setCurrentTaskKey(currentTask.getTaskDefinitionKey()); + vo.setCurrentTaskName(currentTask.getName()); + vo.setCurrentAssignee(currentTask.getAssignee()); + } + + vo.setSteps(buildSteps(historic.getId())); + return vo; + } + + private OrderDisputeFlowVo buildRunningFlowVo(Long orderId, String procInsId, Map runtimeVars) { + OrderDisputeFlowVo vo = new OrderDisputeFlowVo(); + vo.setOrderId(orderId); + vo.setProcInsId(procInsId); + vo.setStatus("RUNNING"); + + Map variables = runtimeVars != null ? runtimeVars : Collections.emptyMap(); + applyVariables(vo, variables); + + HistoricProcessInstance his = historyService.createHistoricProcessInstanceQuery() + .processInstanceId(procInsId) + .singleResult(); + if (his != null) { + vo.setStartTime(his.getStartTime()); + vo.setEndTime(his.getEndTime()); + } + + Task currentTask = taskService.createTaskQuery().processInstanceId(procInsId).active().singleResult(); + if (currentTask != null) { + vo.setCurrentTaskId(currentTask.getId()); + vo.setCurrentTaskKey(currentTask.getTaskDefinitionKey()); + vo.setCurrentTaskName(currentTask.getName()); + vo.setCurrentAssignee(currentTask.getAssignee()); + } + vo.setSteps(buildSteps(procInsId)); + return vo; + } + + private void applyVariables(OrderDisputeFlowVo vo, Map variables) { + Map safe = variables != null ? variables : Collections.emptyMap(); + vo.setVariables(safe); + vo.setOrderCode(MapUtil.getStr(safe, "orderCode")); + vo.setCustomerName(MapUtil.getStr(safe, "customerName")); + } + + private List buildSteps(String procInsId) { + List comments = taskService.getProcessInstanceComments(procInsId); + Map lastCommentByTaskId = new HashMap<>(); + if (CollUtil.isNotEmpty(comments)) { + for (Comment c : comments) { + if (StringUtils.isNotBlank(c.getTaskId())) { + lastCommentByTaskId.put(c.getTaskId(), c.getFullMessage()); + } + } + } + + HistoricTaskInstanceQuery taskQuery = historyService.createHistoricTaskInstanceQuery() + .processInstanceId(procInsId) + .orderByHistoricTaskInstanceStartTime() + .asc(); + List taskInstances = taskQuery.list(); + if (CollUtil.isEmpty(taskInstances)) { + return Collections.emptyList(); + } + + List stepVos = new ArrayList<>(); + for (HistoricTaskInstance ti : taskInstances) { + OrderDisputeFlowStepVo stepVo = new OrderDisputeFlowStepVo(); + stepVo.setStepKey(ti.getTaskDefinitionKey()); + stepVo.setStepName(ti.getName()); + stepVo.setAssignee(ti.getAssignee()); + stepVo.setStartTime(ti.getStartTime()); + stepVo.setEndTime(ti.getEndTime()); + stepVo.setComment(lastCommentByTaskId.get(ti.getId())); + stepVos.add(stepVo); + } + return stepVos; + } +} diff --git a/gear-oa/src/main/java/com/gear/oa/workflow/OrderDisputeProcessDeployer.java b/gear-oa/src/main/java/com/gear/oa/workflow/OrderDisputeProcessDeployer.java new file mode 100644 index 0000000..f248e44 --- /dev/null +++ b/gear-oa/src/main/java/com/gear/oa/workflow/OrderDisputeProcessDeployer.java @@ -0,0 +1,47 @@ +package com.gear.oa.workflow; + +import lombok.RequiredArgsConstructor; +import org.flowable.engine.RepositoryService; +import org.flowable.engine.repository.Deployment; +import org.flowable.engine.repository.ProcessDefinition; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.core.io.ClassPathResource; +import org.springframework.stereotype.Component; + +import java.io.InputStream; + +@RequiredArgsConstructor +@Component +public class OrderDisputeProcessDeployer implements ApplicationRunner { + + public static final String PROCESS_KEY = "oa_order_dispute"; + private static final String BPMN_RESOURCE = "processes/oa_order_dispute.bpmn20.xml"; + + private final RepositoryService repositoryService; + + @Override + public void run(ApplicationArguments args) throws Exception { + ProcessDefinition existing = repositoryService.createProcessDefinitionQuery() + .processDefinitionKey(PROCESS_KEY) + .latestVersion() + .singleResult(); + if (existing != null) { + return; + } + + ClassPathResource resource = new ClassPathResource(BPMN_RESOURCE); + try (InputStream inputStream = resource.getInputStream()) { + Deployment deployment = repositoryService.createDeployment() + .name("订单异议处理") + .key(PROCESS_KEY) + .addInputStream(BPMN_RESOURCE, inputStream) + .deploy(); + repositoryService.createProcessDefinitionQuery() + .deploymentId(deployment.getId()) + .latestVersion() + .singleResult(); + } + } +} + diff --git a/gear-oa/src/main/resources/processes/oa_order_dispute.bpmn20.xml b/gear-oa/src/main/resources/processes/oa_order_dispute.bpmn20.xml new file mode 100644 index 0000000..42735ce --- /dev/null +++ b/gear-oa/src/main/resources/processes/oa_order_dispute.bpmn20.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gear-ui3/src/api/oa/orderDisputeFlow.js b/gear-ui3/src/api/oa/orderDisputeFlow.js new file mode 100644 index 0000000..ec88306 --- /dev/null +++ b/gear-ui3/src/api/oa/orderDisputeFlow.js @@ -0,0 +1,40 @@ +import request from '@/utils/request' + +export function getOrderDisputeFlowByOrder(orderId) { + return request({ + url: `/oa/orderDisputeFlow/byOrder/${orderId}`, + method: 'get' + }) +} + +export function startOrderDisputeFlow(data) { + return request({ + url: '/oa/orderDisputeFlow/start', + method: 'post', + data + }) +} + +export function confirmOrderDisputeFlow(data) { + return request({ + url: '/oa/orderDisputeFlow/confirm', + method: 'post', + data + }) +} + +export function listOrderDisputeTodo(stepKey) { + return request({ + url: '/oa/orderDisputeFlow/todoList', + method: 'get', + params: { stepKey } + }) +} + +export function listOrderDisputeTaskList(stepKey, status, limit) { + return request({ + url: '/oa/orderDisputeFlow/taskList', + method: 'get', + params: { stepKey, status, limit } + }) +} diff --git a/gear-ui3/src/components/UserSelect/index.vue b/gear-ui3/src/components/UserSelect/index.vue index 6bb2909..72c167c 100644 --- a/gear-ui3/src/components/UserSelect/index.vue +++ b/gear-ui3/src/components/UserSelect/index.vue @@ -71,13 +71,18 @@ const loadData = async () => { type: 'user' })) - // 如果只选择用户,过滤掉部门节点 - let allNodes = props.userOnly - ? transformedUserList - : [...transformedDeptList, ...transformedUserList] + if (props.userOnly) { + // 仅选择用户时直接平铺用户节点,避免因父部门节点被过滤导致整棵树为空 + treeData.value = transformedUserList.map(user => ({ + ...user, + parentId: null, + children: undefined + })) + return + } - // 构建树形结构 - treeData.value = buildTree(allNodes) + // 构建部门 + 用户树形结构 + treeData.value = buildTree([...transformedDeptList, ...transformedUserList]) } catch (error) { console.error('加载数据失败:', error) } diff --git a/gear-ui3/src/views/oms/customer/index.vue b/gear-ui3/src/views/oms/customer/index.vue index 993cec7..57dc7f2 100644 --- a/gear-ui3/src/views/oms/customer/index.vue +++ b/gear-ui3/src/views/oms/customer/index.vue @@ -100,7 +100,7 @@ - + @@ -305,7 +305,13 @@ - + 关闭 @@ -385,12 +391,12 @@ import { listShippingOrder } from "@/api/oms/shippingOrder"; import { listOrderDetail } from "@/api/oms/orderDetail"; import { listReceivable } from "@/api/finance/receivable"; import request from "@/utils/request"; -import ReturnExchange from "@/views/oms/order/panels/return.vue"; +import DisputeFlow from "@/views/oms/order/panels/disputeFlow.vue"; import * as XLSX from "xlsx"; export default { name: "Customer", - components: { ReturnExchange }, + components: { DisputeFlow }, setup() { const { proxy } = getCurrentInstance(); const { customer_from } = proxy.useDict("customer_from"); diff --git a/gear-ui3/src/views/oms/dispute/index.vue b/gear-ui3/src/views/oms/dispute/index.vue new file mode 100644 index 0000000..25533d2 --- /dev/null +++ b/gear-ui3/src/views/oms/dispute/index.vue @@ -0,0 +1,473 @@ + + + + + + + {{ item.label }} + + + + + + + 待办 + 已处理 + 全部 + + + + + + + 刷新 + + + + + + + + {{ leftTitle }} + 刷新 + + + + + + {{ item.orderCode || '-' }} + + + {{ item.status === 'DONE' ? '已处理' : '待办' }} + + {{ item.taskName || '-' }} + + + + 客户:{{ item.customerName || '-' }} + + 创建:{{ formatTime(item.createTime) }} + 完成:{{ formatTime(item.endTime) }} + + + + + + + + + + + + + {{ rightTitle }} + + 当前:{{ currentRow.orderCode }} + 请选择左侧异议单据 + + + + 刷新 + + + + + + + + + + + + + + + + + diff --git a/gear-ui3/src/views/oms/order/panels/disputeFlow.vue b/gear-ui3/src/views/oms/order/panels/disputeFlow.vue new file mode 100644 index 0000000..0f15f0e --- /dev/null +++ b/gear-ui3/src/views/oms/order/panels/disputeFlow.vue @@ -0,0 +1,432 @@ + + + + + + 订单异议流程 + + 订单:{{ orderCode }} + 流程:{{ flowInfo.procInsId }} + + + + 刷新 + + + + + + 客户 + {{ customerName || '-' }} + + + 当前状态 + + + {{ !flowLoaded ? '加载中' : flowInfo && flowInfo.status === 'FINISHED' ? '已闭环' : flowInfo ? '处理中' : '未发起' }} + + + + + 当前负责人 + {{ !flowLoaded ? '—' : flowInfo ? userLabel(flowInfo.currentAssignee) : '-' }} + + + + + + + + + 指定各节点负责人 + + + + + + + + + + + + + + + + + + + 发起流程 + + + + + + + + 流程进度 + + + + + + + 当前操作 + + + + + + + + + {{ flowInfo.currentTaskName }} + - + + + {{ userLabel(flowInfo.currentAssignee) }} + + + + + + + 确认 + 清空 + + 仅当前节点负责人可执行确认操作。 + + + + + + + + + + + + + diff --git a/gear-ui3/src/views/oms/returnExchange/summary/index.vue b/gear-ui3/src/views/oms/returnExchange/summary/index.vue index 8f6cc5a..7af6cf3 100644 --- a/gear-ui3/src/views/oms/returnExchange/summary/index.vue +++ b/gear-ui3/src/views/oms/returnExchange/summary/index.vue @@ -108,6 +108,12 @@ + + + 发起流程 + 查看 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 取消 + 确定发起 + + + + + + + + 关闭 + + + + + + + 嘉祥科伦普重工有限公司 + 投诉受理 + 投诉确认 + 投诉分析 + 投诉处理 + 回访确认 + 数据分析 + + + + + 投诉受理单+ 新建 + ← 请选择或新建投诉受理单 + + + + 投诉确认单+ 新建 + ← 请选择或新建投诉确认单 + + + + 投诉分析单+ 新建 + ← 请选择或新建投诉分析单 + + + + 投诉处理单+ 新建 + ← 请选择或新建投诉处理单 + + + + 回访确认单+ 新建 + ← 请选择或新建回访确认单 + + + + + 投诉数据分析 + + 分析时段: + + 至 + + 重置 + (默认:当月1日至今) + + 一、基本统计 + + 二、日趋势分析 + 投诉量 & 业务员日趋势投诉金额日趋势 + 各环节单据日新增趋势 + 三、月均分析(近6个月) + 月度投诉量 & 业务员数月度投诉金额 + 各环节单据月新增趋势 + 四、占比分析 + + 投诉原因分类占比产品类型投诉占比 + 各环节单据处理率业务员投诉分布 + 审核通过率 + + + + +选择合同×合同编号合同名称销售员签订时间 +选择钢卷×入场卷号规格材质重量(t)厂家 +投诉审核×审核结果:已通过未通过审核意见: +选择已审核通过的投诉受理单×投诉编号投诉日期投诉情况审核状态 +选择已处理的投诉确认单×确认单号来源受理单处理人状态 +选择已处理的投诉分析单×分析单号来源确认单处理人状态 +选择已处理的投诉处理单×处理单号来源分析单处理人状态 + + + +