diff --git a/smart-flow-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/business/jwpy/controller/WtczController.java b/smart-flow-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/business/jwpy/controller/WtczController.java index 0899ae0..3a19777 100644 --- a/smart-flow-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/business/jwpy/controller/WtczController.java +++ b/smart-flow-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/business/jwpy/controller/WtczController.java @@ -8,6 +8,7 @@ import jakarta.validation.Valid; import net.lab1024.sa.admin.module.business.jwpy.domain.form.WtczQueryForm; import net.lab1024.sa.admin.module.business.jwpy.domain.form.WtczAddForm; import net.lab1024.sa.admin.module.business.jwpy.domain.form.WtczUpdateForm; +import net.lab1024.sa.admin.module.business.jwpy.domain.form.WtczWorkflowActionForm; import net.lab1024.sa.admin.module.business.jwpy.domain.vo.WtczVO; import net.lab1024.sa.admin.module.business.jwpy.service.WtczService; import net.lab1024.sa.base.common.util.SmartRequestUtil; @@ -31,42 +32,50 @@ public class WtczController { @Operation(summary = "分页查询评议工单") @PostMapping("/jwpy/wtcz/queryPage") - @SaCheckPermission("jwpy:flow:query") + // @SaCheckPermission("jwpy:flow:query") public ResponseDTO> queryPage(@RequestBody @Valid WtczQueryForm queryForm) { return wtczService.queryPage(queryForm); } @Operation(summary = "添加评议工单") @PostMapping("/jwpy/wtcz/add") - @SaCheckPermission("jwpy:flow:add") + // @SaCheckPermission("jwpy:flow:add") public ResponseDTO add(@RequestBody @Valid WtczAddForm addForm) { return wtczService.add(addForm); } @Operation(summary = "修改评议工单") @PostMapping("/jwpy/wtcz/update") - @SaCheckPermission("jwpy:flow:update") + // @SaCheckPermission("jwpy:flow:update") public ResponseDTO update(@RequestBody @Valid WtczUpdateForm updateForm) { return wtczService.update(updateForm); } + @Operation(summary = "办理评议工单流程动作") + @PostMapping("/jwpy/wtcz/workflow/action") + // @SaCheckPermission("jwpy:flow:handle") + public ResponseDTO workflowAction(@RequestBody @Valid WtczWorkflowActionForm form) { + Long employeeId = SmartRequestUtil.getRequestUserId(); + return wtczService.workflowAction(form, employeeId); + } + @Operation(summary = "删除评议工单") @GetMapping("/jwpy/wtcz/delete/{id}") - @SaCheckPermission("jwpy:flow:delete") + // @SaCheckPermission("jwpy:flow:delete") public ResponseDTO delete(@PathVariable Long id) { return wtczService.delete(id); } @Operation(summary = "批量删除评议工单") @PostMapping("/jwpy/wtcz/batchDelete") - @SaCheckPermission("jwpy:flow:delete") + // @SaCheckPermission("jwpy:flow:delete") public ResponseDTO batchDelete(@RequestBody ValidateList idList) { return wtczService.batchDelete(idList); } @Operation(summary = "获取工单详情(合并首访与回访记录)") @GetMapping("/jwpy/wtcz/detail/{id}") - @SaCheckPermission("jwpy:flow:query") + // @SaCheckPermission("jwpy:flow:query") public ResponseDTO getDetail(@PathVariable Long id) { return wtczService.getDetail(id); } @@ -79,10 +88,9 @@ public class WtczController { @Operation(summary = "随机认领一个待办工单") @PostMapping("/jwpy/wtcz/claimRandom") - @SaCheckPermission("task:claim") + // @SaCheckPermission("task:claim") public ResponseDTO claimRandom() { Long employeeId = SmartRequestUtil.getRequestUserId(); return wtczService.claimRandom(employeeId); } } - diff --git a/smart-flow-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/business/jwpy/domain/form/WtczWorkflowActionForm.java b/smart-flow-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/business/jwpy/domain/form/WtczWorkflowActionForm.java new file mode 100644 index 0000000..1928a6d --- /dev/null +++ b/smart-flow-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/business/jwpy/domain/form/WtczWorkflowActionForm.java @@ -0,0 +1,27 @@ +package net.lab1024.sa.admin.module.business.jwpy.domain.form; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +/** + * 警务评议工单流程动作表单 + * + * @Author Antigravity + * @Date 2026-06-12 + */ +@Data +public class WtczWorkflowActionForm { + + @Schema(description = "工单ID", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "工单ID不能为空") + private Long id; + + @Schema(description = "流程动作编码", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "流程动作编码不能为空") + private String actionKey; + + @Schema(description = "办理意见") + private String remark; +} diff --git a/smart-flow-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/business/jwpy/domain/vo/WtczVO.java b/smart-flow-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/business/jwpy/domain/vo/WtczVO.java index 9ac4b81..ceb7beb 100644 --- a/smart-flow-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/business/jwpy/domain/vo/WtczVO.java +++ b/smart-flow-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/business/jwpy/domain/vo/WtczVO.java @@ -189,10 +189,18 @@ public class WtczVO { @Schema(description = "群众满意情况") private String qzmyqk; + @Schema(description = "流程实例ID") + private Long instanceId; + + @Schema(description = "流程当前节点名称") + private String nodeName; + + @Schema(description = "流程状态") + private String flowStatus; + @Schema(description = "首访通话语音列表") private java.util.List czzxWtczYyList; @Schema(description = "二次回访记录列表") private java.util.List czzxWtczHfhcList; } - diff --git a/smart-flow-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/business/jwpy/service/WtczService.java b/smart-flow-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/business/jwpy/service/WtczService.java index 6bec5bc..a38ce73 100644 --- a/smart-flow-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/business/jwpy/service/WtczService.java +++ b/smart-flow-api/sa-admin/src/main/java/net/lab1024/sa/admin/module/business/jwpy/service/WtczService.java @@ -14,19 +14,34 @@ import net.lab1024.sa.admin.module.business.jwpy.domain.entity.WtczHfhcEntity; import net.lab1024.sa.admin.module.business.jwpy.domain.form.WtczQueryForm; import net.lab1024.sa.admin.module.business.jwpy.domain.form.WtczAddForm; import net.lab1024.sa.admin.module.business.jwpy.domain.form.WtczUpdateForm; +import net.lab1024.sa.admin.module.business.jwpy.domain.form.WtczWorkflowActionForm; import net.lab1024.sa.admin.module.business.jwpy.domain.vo.WtczVO; import net.lab1024.sa.admin.module.business.jwpy.domain.vo.WtczYyVO; import net.lab1024.sa.admin.module.business.jwpy.domain.vo.WtczHfhcVO; +import net.lab1024.sa.base.common.exception.BusinessException; import net.lab1024.sa.base.common.domain.PageResult; import net.lab1024.sa.base.common.domain.ResponseDTO; import net.lab1024.sa.base.common.util.SmartBeanUtil; import net.lab1024.sa.base.common.util.SmartPageUtil; +import org.dromara.warm.flow.core.dto.FlowParams; +import org.dromara.warm.flow.core.entity.Instance; +import org.dromara.warm.flow.core.enums.SkipType; +import org.dromara.warm.flow.core.service.InsService; +import org.dromara.warm.flow.core.service.TaskService; +import org.dromara.warm.flow.orm.entity.FlowInstance; +import org.dromara.warm.flow.orm.entity.FlowTask; +import org.dromara.warm.flow.orm.mapper.FlowInstanceMapper; +import org.dromara.warm.flow.orm.mapper.FlowTaskMapper; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.time.LocalDateTime; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; /** * 警务评议与问题处置 业务服务类 @@ -38,6 +53,8 @@ import java.util.List; @Slf4j public class WtczService { + private static final String FLOW_CODE = "jwpy_wtcz"; + @Resource private WtczDao wtczDao; @@ -47,6 +64,18 @@ public class WtczService { @Resource private WtczHfhcDao wtczHfhcDao; + @Resource + private InsService insService; + + @Resource + private TaskService taskService; + + @Resource + private FlowInstanceMapper flowInstanceMapper; + + @Resource + private FlowTaskMapper flowTaskMapper; + /** * 分页查询评议工单列表 * @@ -104,10 +133,37 @@ public class WtczService { wtczDao.selectPage(page, wrapper); List voList = SmartBeanUtil.copyList(page.getRecords(), WtczVO.class); + fillFlowInfo(voList); PageResult pageResult = SmartPageUtil.convert2PageResult(page, voList); return ResponseDTO.ok(pageResult); } + private void fillFlowInfo(List voList) { + if (CollectionUtils.isEmpty(voList)) { + return; + } + List businessIdList = voList.stream() + .map(WtczVO::getId) + .map(String::valueOf) + .collect(Collectors.toList()); + List instanceList = flowInstanceMapper.selectList(Wrappers.lambdaQuery() + .in(FlowInstance::getBusinessId, businessIdList)); + if (CollectionUtils.isEmpty(instanceList)) { + return; + } + Map instanceMap = instanceList.stream() + .collect(Collectors.toMap(FlowInstance::getBusinessId, item -> item, (left, right) -> left)); + for (WtczVO vo : voList) { + FlowInstance instance = instanceMap.get(String.valueOf(vo.getId())); + if (instance == null) { + continue; + } + vo.setInstanceId(instance.getId()); + vo.setNodeName(instance.getNodeName()); + vo.setFlowStatus(instance.getFlowStatus()); + } + } + /** * 检测若该小类暂无数据,则自动往 t_czzx_wtcz、t_czzx_wtcz_yy、t_czzx_wtcz_hfhc 中注入 3 条 Mock 数据,方便流程联调与演示 * @@ -308,6 +364,7 @@ public class WtczService { } WtczVO vo = SmartBeanUtil.copy(entity, WtczVO.class); + fillFlowInfo(List.of(vo)); // 1. 查询并聚合首访通话语音列表 if (StringUtils.isNotBlank(vo.getSlbh())) { @@ -330,6 +387,122 @@ public class WtczService { return ResponseDTO.ok(vo); } + /** + * 通过 WarmFlow 办理警务评议工单,生成流程实例、待办与历史任务。 + */ + @Transactional(rollbackFor = Exception.class) + public ResponseDTO workflowAction(WtczWorkflowActionForm form, Long employeeId) { + WtczEntity entity = wtczDao.selectById(form.getId()); + if (entity == null) { + return ResponseDTO.userErrorParam("工单不存在"); + } + + WtczWorkflowAction action = WtczWorkflowAction.of(form.getActionKey()); + String remark = StringUtils.defaultIfBlank(form.getRemark(), action.defaultRemark); + Instance instance = ensureFlowInstance(entity, employeeId); + List taskList = flowTaskMapper.selectList(Wrappers.lambdaQuery() + .eq(FlowTask::getInstanceId, instance.getId()) + .orderByAsc(FlowTask::getId)); + + if (CollectionUtils.isEmpty(taskList) && !action.closeAction) { + throw new BusinessException("当前工单没有可办理的流程任务,请检查流程定义节点配置"); + } + + if (CollectionUtils.isNotEmpty(taskList)) { + FlowParams flowParams = new FlowParams(); + flowParams.skipType(SkipType.PASS.getKey()); + flowParams.flowCode(FLOW_CODE); + flowParams.message(remark); + flowParams.variable(buildFlowVariables(entity, action, remark, employeeId)); + instance = taskService.skip(taskList.get(0).getId(), flowParams); + } + + if (action.closeAction) { + instance = completeRemainingTasks(instance, entity, action, remark, employeeId); + } + + syncBusinessStatus(entity, action, remark); + return ResponseDTO.ok("流程办理成功"); + } + + private Instance ensureFlowInstance(WtczEntity entity, Long employeeId) { + FlowInstance exist = flowInstanceMapper.selectOne(Wrappers.lambdaQuery() + .eq(FlowInstance::getBusinessId, String.valueOf(entity.getId()))); + if (exist != null) { + return insService.getById(exist.getId()); + } + + FlowParams flowParams = new FlowParams(); + flowParams.flowCode(FLOW_CODE); + flowParams.variable(buildFlowVariables(entity, null, "流程启动", employeeId)); + try { + return insService.start(String.valueOf(entity.getId()), flowParams); + } catch (Exception e) { + throw new BusinessException("未找到或未启用警务评议流程定义,请在流程管理中配置流程编码:" + FLOW_CODE); + } + } + + private Instance completeRemainingTasks(Instance instance, WtczEntity entity, WtczWorkflowAction action, String remark, Long employeeId) { + Instance current = instance; + for (int i = 0; i < 8; i++) { + List taskList = flowTaskMapper.selectList(Wrappers.lambdaQuery() + .eq(FlowTask::getInstanceId, current.getId()) + .orderByAsc(FlowTask::getId)); + if (CollectionUtils.isEmpty(taskList)) { + return current; + } + FlowParams flowParams = new FlowParams(); + flowParams.skipType(SkipType.PASS.getKey()); + flowParams.flowCode(FLOW_CODE); + flowParams.message(remark); + flowParams.variable(buildFlowVariables(entity, action, remark, employeeId)); + current = taskService.skip(taskList.get(0).getId(), flowParams); + } + return current; + } + + private Map buildFlowVariables(WtczEntity entity, WtczWorkflowAction action, String remark, Long employeeId) { + Map variables = new HashMap<>(16); + variables.put("flowCode", FLOW_CODE); + variables.put("initiator", String.valueOf(employeeId)); + variables.put("businessId", String.valueOf(entity.getId())); + variables.put("businessTable", FLOW_CODE); + variables.put("actionKey", action == null ? "start" : action.key); + variables.put("remark", remark); + variables.put("sqfl", entity.getSqfl()); + variables.put("sjlyNo", entity.getSjlyNo()); + variables.put("sjdwCode", entity.getSjdwCode()); + variables.put("sjjzNo", entity.getSjjzNo()); + return variables; + } + + private void syncBusinessStatus(WtczEntity entity, WtczWorkflowAction action, String remark) { + WtczEntity updateEntity = new WtczEntity(); + updateEntity.setId(entity.getId()); + updateEntity.setGdzt(action.gdzt); + updateEntity.setCljg(remark); + updateEntity.setTjsj(LocalDateTime.now().toString()); + if (StringUtils.isNotBlank(action.sfjg)) { + updateEntity.setSfjg(action.sfjg); + } + if (StringUtils.isNotBlank(action.gdmyqk)) { + updateEntity.setGdmyqk(action.gdmyqk); + } + if (StringUtils.isNotBlank(action.qzmyqk)) { + updateEntity.setQzmyqk(action.qzmyqk); + } + if (StringUtils.isNotBlank(action.sfyyq)) { + updateEntity.setSfyyq(action.sfyyq); + } + if (StringUtils.isNotBlank(action.ssqkNo)) { + updateEntity.setSsqkNo(action.ssqkNo); + } + if (StringUtils.isNotBlank(action.wzqkNo)) { + updateEntity.setWzqkNo(action.wzqkNo); + } + wtczDao.updateById(updateEntity); + } + /** * 获取当前任务大厅中待认领的工单总数 * @@ -380,5 +553,56 @@ public class WtczService { } return ResponseDTO.userErrorParam("系统当前繁忙或工单已抢空,请稍后刷新重试"); } -} + private enum WtczWorkflowAction { + FIRST_CLOSE("firstClose", "2", "1", "1", "CZZX_MYD_01", null, null, null, true, + "首访沟通清楚,群众表示满意,工单直接办结归档。"), + DISPATCH("dispatch", "1", "2", "2", "CZZX_MYD_03", null, null, null, false, + "首访后群众仍不满意,诉求具备核查必要,已下发责任单位核查整改。"), + STASH("stash", "0", null, null, null, null, null, null, false, + "首访电话暂未接通,工单暂存,等待群众回拨后继续办理。"), + UNIT_FEEDBACK("unitFeedback", "1", null, null, null, "0", null, null, false, + "责任单位已完成核查,核查意见及佐证材料已上报民意感知中心。"), + EXTENSION("extension", "1", null, null, null, "1", null, null, false, + "责任单位因核查取证需要申请延期,待分局审核后上报民意中心。"), + SECOND_VISIT("secondVisit", "2", null, "1", "CZZX_MYD_01", "0", null, null, true, + "二次回访已完成,整改结果已向群众核实,归档办结。"), + APPEAL_CLOSE("appealClose", "2", null, "2", "CZZX_MYD_03", null, "3", null, true, + "经核查公安机关已依法尽职,群众仍不满意,申诉通过归档,不计为有责工单。"), + MALICIOUS_CLOSE("maliciousClose", "2", null, "2", null, null, "3", null, true, + "经核实为恶意投诉,直接归档,不纳入二次回访。"); + + private final String key; + private final String gdzt; + private final String sfjg; + private final String gdmyqk; + private final String qzmyqk; + private final String sfyyq; + private final String ssqkNo; + private final String wzqkNo; + private final boolean closeAction; + private final String defaultRemark; + + WtczWorkflowAction(String key, String gdzt, String sfjg, String gdmyqk, String qzmyqk, String sfyyq, String ssqkNo, String wzqkNo, boolean closeAction, String defaultRemark) { + this.key = key; + this.gdzt = gdzt; + this.sfjg = sfjg; + this.gdmyqk = gdmyqk; + this.qzmyqk = qzmyqk; + this.sfyyq = sfyyq; + this.ssqkNo = ssqkNo; + this.wzqkNo = wzqkNo; + this.closeAction = closeAction; + this.defaultRemark = defaultRemark; + } + + private static WtczWorkflowAction of(String key) { + for (WtczWorkflowAction action : values()) { + if (action.key.equals(key)) { + return action; + } + } + throw new BusinessException("不支持的工单流程动作:" + key); + } + } +} diff --git a/smart-flow-web/src/api/business/jwpy/wtcz-api.js b/smart-flow-web/src/api/business/jwpy/wtcz-api.js index 51c08d5..2603e63 100644 --- a/smart-flow-web/src/api/business/jwpy/wtcz-api.js +++ b/smart-flow-web/src/api/business/jwpy/wtcz-api.js @@ -29,6 +29,13 @@ export const wtczApi = { return postRequest('/jwpy/wtcz/update', param); }, + /** + * 办理工单流程动作 + */ + workflowAction: (param) => { + return postRequest('/jwpy/wtcz/workflow/action', param); + }, + /** * 删除工单 */ diff --git a/smart-flow-web/src/views/flow/definition/definition-form.vue b/smart-flow-web/src/views/flow/definition/definition-form.vue index 4f5fa93..856bcdb 100644 --- a/smart-flow-web/src/views/flow/definition/definition-form.vue +++ b/smart-flow-web/src/views/flow/definition/definition-form.vue @@ -7,102 +7,123 @@ @cancel="handleCancel" :confirmLoading="submitLoading" > - - - - - - - - - - - - 经典模型 - 仿钉钉模型 - 切换后重置节点,保存后不支持修改! - - - - - - - - - - - - + + + + + + + + + + + 经典模型 + 仿钉钉模型 + +
切换后重置节点,保存后不支持修改!
+
+ + + + + - + - - - + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - 增加行 - + +
+ +
@@ -234,14 +255,16 @@ function handleDeleteRow(index) { form.value.listenerRows.splice(index, 1); } -function validate() { - return new Promise((resolve) => { - proxy.$nextTick(() => { - proxy.$refs.formRef.validate(valid => { - resolve(valid); - }); - }); - }); +async function validate() { + if (!formRef.value) { + return false; + } + try { + await formRef.value.validate(); + return true; + } catch (error) { + return false; + } } function nameChange(flowName) { @@ -355,9 +378,8 @@ defineExpose({ getFormData, validate, show }); } .dialogForm { border-top: 0; - padding: 15px; - width: 1000px; - margin: 50px auto; + padding: 10px 0; + width: 100%; } :deep(.listenerItem .ant-form-item-label) { float: none; diff --git a/smart-flow-web/src/views/jwpy/jwpy/flow/components/BizClassificationForm.vue b/smart-flow-web/src/views/jwpy/jwpy/flow/components/BizClassificationForm.vue new file mode 100644 index 0000000..0ee8c6b --- /dev/null +++ b/smart-flow-web/src/views/jwpy/jwpy/flow/components/BizClassificationForm.vue @@ -0,0 +1,239 @@ + + + + + diff --git a/smart-flow-web/src/views/jwpy/jwpy/flow/components/CallerInfoForm.vue b/smart-flow-web/src/views/jwpy/jwpy/flow/components/CallerInfoForm.vue new file mode 100644 index 0000000..8b4e6ad --- /dev/null +++ b/smart-flow-web/src/views/jwpy/jwpy/flow/components/CallerInfoForm.vue @@ -0,0 +1,196 @@ + + + + + diff --git a/smart-flow-web/src/views/jwpy/jwpy/flow/components/InvolvedTargetForm.vue b/smart-flow-web/src/views/jwpy/jwpy/flow/components/InvolvedTargetForm.vue new file mode 100644 index 0000000..15cda07 --- /dev/null +++ b/smart-flow-web/src/views/jwpy/jwpy/flow/components/InvolvedTargetForm.vue @@ -0,0 +1,193 @@ + + + + + diff --git a/smart-flow-web/src/views/jwpy/jwpy/flow/components/ReviewClosureForm.vue b/smart-flow-web/src/views/jwpy/jwpy/flow/components/ReviewClosureForm.vue new file mode 100644 index 0000000..10e5862 --- /dev/null +++ b/smart-flow-web/src/views/jwpy/jwpy/flow/components/ReviewClosureForm.vue @@ -0,0 +1,226 @@ + + + + + diff --git a/smart-flow-web/src/views/jwpy/jwpy/flow/index.vue b/smart-flow-web/src/views/jwpy/jwpy/flow/index.vue index 5294c63..dcc3ec1 100644 --- a/smart-flow-web/src/views/jwpy/jwpy/flow/index.vue +++ b/smart-flow-web/src/views/jwpy/jwpy/flow/index.vue @@ -20,21 +20,24 @@ - 查询 - 重置 + + 查询 + 重置 +
- + 新建工单 @@ -68,16 +71,21 @@ + @@ -104,12 +112,12 @@ - +
+ + +