feat(bpm): 新增工单模块设计和数据库结构

- 添加工单模块前端设计文档,详细描述了页面架构、权限设计和API调用流程
- 新增工单模块数据库表结构,包括工单主表、分配规则表和跟踪记录表
- 创建工单类型、状态和优先级的字典数据
- 添加示例分配规则数据
This commit is contained in:
aikai 2025-06-21 09:15:01 +08:00
parent 197605f4d2
commit 7ab86ea5cc
4 changed files with 283 additions and 43 deletions

View File

@ -4,8 +4,10 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.workorder.BpmOAWorkOrderPageReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.workorder.BpmOAWorkOrderRespVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAWorkOrderDO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
/**
* BPM OA 工单 Mapper
@ -64,4 +66,22 @@ public interface BpmOAWorkOrderMapper extends BaseMapperX<BpmOAWorkOrderDO> {
.in(BpmOAWorkOrderDO::getStatus, 1, 2)); // 待分配和处理中
}
/**
* 工单分页查询带关联信息- XML实现
*/
PageResult<BpmOAWorkOrderRespVO> selectWorkOrderPage(@Param("loginUserId") Long loginUserId,
@Param("req") BpmOAWorkOrderPageReqVO req);
/**
* 我发起的工单分页查询带关联信息- XML实现
*/
PageResult<BpmOAWorkOrderRespVO> selectMyWorkOrderPage(@Param("loginUserId") Long loginUserId,
@Param("req") BpmOAWorkOrderPageReqVO req);
/**
* 分配给我的工单分页查询带关联信息- XML实现
*/
PageResult<BpmOAWorkOrderRespVO> selectAssignedWorkOrderPage(@Param("loginUserId") Long loginUserId,
@Param("req") BpmOAWorkOrderPageReqVO req);
}

View File

@ -16,6 +16,8 @@ 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.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import cn.iocoder.yudao.module.system.api.dict.DictDataApi;
import cn.iocoder.yudao.module.system.api.dict.dto.DictDataRespDTO;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
@ -64,6 +66,9 @@ public class BpmOAWorkOrderServiceImpl extends BpmOABaseService implements BpmOA
@Resource
private DeptApi deptApi;
@Resource
private DictDataApi dictDataApi;
@Override
@Transactional(rollbackFor = Exception.class)
public Long createWorkOrder(Long userId, BpmOAWorkOrderCreateReqVO createReqVO) {
@ -186,20 +191,20 @@ public class BpmOAWorkOrderServiceImpl extends BpmOABaseService implements BpmOA
@Override
public PageResult<BpmOAWorkOrderRespVO> getWorkOrderPage(Long loginUserId, BpmOAWorkOrderPageReqVO pageVO) {
PageResult<BpmOAWorkOrderDO> pageResult = workOrderMapper.selectPage(loginUserId, pageVO);
return convertToPageResult(pageResult);
// 使用XML查询直接返回带关联信息的VO数据
return workOrderMapper.selectWorkOrderPage(loginUserId, pageVO);
}
@Override
public PageResult<BpmOAWorkOrderRespVO> getMyWorkOrderPage(Long loginUserId, BpmOAWorkOrderPageReqVO pageVO) {
PageResult<BpmOAWorkOrderDO> pageResult = workOrderMapper.selectMyPage(loginUserId, pageVO);
return convertToPageResult(pageResult);
// 使用XML查询直接返回带关联信息的VO数据
return workOrderMapper.selectMyWorkOrderPage(loginUserId, pageVO);
}
@Override
public PageResult<BpmOAWorkOrderRespVO> getAssignedWorkOrderPage(Long loginUserId, BpmOAWorkOrderPageReqVO pageVO) {
PageResult<BpmOAWorkOrderDO> pageResult = workOrderMapper.selectAssignedPage(loginUserId, pageVO);
return convertToPageResult(pageResult);
// 使用XML查询直接返回带关联信息的VO数据
return workOrderMapper.selectAssignedWorkOrderPage(loginUserId, pageVO);
}
@Override
@ -357,19 +362,15 @@ public class BpmOAWorkOrderServiceImpl extends BpmOABaseService implements BpmOA
* 获取工单类型名称
*/
private String getWorkOrderTypeName(String type) {
switch (type) {
case "it_support":
return "IT支持";
case "equipment_repair":
return "设备维修";
case "system_issue":
return "系统问题";
case "permission_request":
return "权限申请";
case "other":
return "其他";
default:
return type;
if (type == null) {
return "";
}
try {
DictDataRespDTO dictData = dictDataApi.getDictData("work_order_type", type).getCheckedData();
return dictData != null ? dictData.getLabel() : type;
} catch (Exception e) {
// 如果字典查询失败返回原值
return type;
}
}
@ -380,17 +381,12 @@ public class BpmOAWorkOrderServiceImpl extends BpmOABaseService implements BpmOA
if (level == null) {
return "";
}
switch (level) {
case 1:
return "";
case 2:
return "";
case 3:
return "";
case 4:
return "紧急";
default:
return String.valueOf(level);
try {
DictDataRespDTO dictData = dictDataApi.getDictData("work_order_level", String.valueOf(level)).getCheckedData();
return dictData != null ? dictData.getLabel() : String.valueOf(level);
} catch (Exception e) {
// 如果字典查询失败返回原值
return String.valueOf(level);
}
}
@ -401,19 +397,12 @@ public class BpmOAWorkOrderServiceImpl extends BpmOABaseService implements BpmOA
if (status == null) {
return "";
}
switch (status) {
case 1:
return "待分配";
case 2:
return "处理中";
case 3:
return "已完成";
case 4:
return "已取消";
case 5:
return "已关闭";
default:
return String.valueOf(status);
try {
DictDataRespDTO dictData = dictDataApi.getDictData("work_order_status", String.valueOf(status)).getCheckedData();
return dictData != null ? dictData.getLabel() : String.valueOf(status);
} catch (Exception e) {
// 如果字典查询失败返回原值
return String.valueOf(status);
}
}

View File

@ -0,0 +1,31 @@
package cn.iocoder.yudao.module.bpm.service.oa.listener;
import cn.iocoder.yudao.module.bpm.framework.bpm.core.event.BpmProcessInstanceResultEvent;
import cn.iocoder.yudao.module.bpm.framework.bpm.core.event.BpmProcessInstanceResultEventListener;
import cn.iocoder.yudao.module.bpm.service.oa.BpmOAWorkOrderService;
import cn.iocoder.yudao.module.bpm.service.oa.BpmOAWorkOrderServiceImpl;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* OA 工单的结果的监听器实现类
*
* @author 系统
*/
@Component
public class BpmOAWorkOrderResultListener extends BpmProcessInstanceResultEventListener {
@Resource
private BpmOAWorkOrderService workOrderService;
@Override
protected String getProcessDefinitionKey() {
return BpmOAWorkOrderServiceImpl.PROCESS_KEY;
}
@Override
protected void onEvent(BpmProcessInstanceResultEvent event) {
workOrderService.updateWorkOrderResult(Long.parseLong(event.getBusinessKey()), event.getResult());
}
}

View File

@ -0,0 +1,200 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.iocoder.yudao.module.bpm.dal.mysql.oa.BpmOAWorkOrderMapper">
<!-- 工单分页查询结果映射 -->
<resultMap id="WorkOrderPageResultMap" type="cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.workorder.BpmOAWorkOrderRespVO">
<id column="id" property="id"/>
<result column="title" property="title"/>
<result column="type" property="type"/>
<result column="level" property="level"/>
<result column="content" property="content"/>
<result column="from_user_id" property="fromUserId"/>
<result column="from_dept_id" property="fromDeptId"/>
<result column="assignee_user_id" property="assigneeUserId"/>
<result column="assignee_dept_id" property="assigneeDeptId"/>
<result column="status" property="status"/>
<result column="expected_time" property="expectedTime"/>
<result column="completed_time" property="completedTime"/>
<result column="result_description" property="resultDescription"/>
<result column="result" property="result"/>
<result column="process_instance_id" property="processInstanceId"/>
<result column="create_time" property="createTime"/>
<result column="update_time" property="updateTime"/>
<!-- 关联用户和部门名称 -->
<result column="from_user_name" property="fromUserName"/>
<result column="from_dept_name" property="fromDeptName"/>
<result column="assignee_user_name" property="assigneeUserName"/>
<result column="assignee_dept_name" property="assigneeDeptName"/>
<!-- 字典名称 -->
<result column="type_name" property="typeName"/>
<result column="level_name" property="levelName"/>
<result column="status_name" property="statusName"/>
</resultMap>
<!-- 工单分页查询SQL所有工单 -->
<select id="selectWorkOrderPage" resultMap="WorkOrderPageResultMap">
SELECT
w.id,
w.title,
w.type,
w.level,
w.content,
w.from_user_id,
w.from_dept_id,
w.assignee_user_id,
w.assignee_dept_id,
w.status,
w.expected_time,
w.completed_time,
w.result_description,
w.result,
w.process_instance_id,
w.create_time,
w.update_time,
fu.nickname AS from_user_name,
fd.name AS from_dept_name,
au.nickname AS assignee_user_name,
ad.name AS assignee_dept_name,
wt.label AS type_name,
wl.label AS level_name,
ws.label AS status_name
FROM bpm_oa_work_order w
LEFT JOIN system_users fu ON w.from_user_id = fu.id
LEFT JOIN system_dept fd ON w.from_dept_id = fd.id
LEFT JOIN system_users au ON w.assignee_user_id = au.id
LEFT JOIN system_dept ad ON w.assignee_dept_id = ad.id
LEFT JOIN system_dict_data wt ON wt.dict_type = 'work_order_type' AND wt.value = w.type
LEFT JOIN system_dict_data wl ON wl.dict_type = 'work_order_level' AND wl.value = CAST(w.level AS CHAR)
LEFT JOIN system_dict_data ws ON ws.dict_type = 'work_order_status' AND ws.value = CAST(w.status AS CHAR)
<where>
w.deleted = 0
<if test="req.title != null and req.title != ''">
AND w.title LIKE CONCAT('%', #{req.title}, '%')
</if>
<if test="req.type != null and req.type != ''">
AND w.type = #{req.type}
</if>
<if test="req.status != null">
AND w.status = #{req.status}
</if>
<if test="req.assigneeUserId != null">
AND w.assignee_user_id = #{req.assigneeUserId}
</if>
<if test="req.fromUserId != null">
AND w.from_user_id = #{req.fromUserId}
</if>
<if test="req.createTime != null and req.createTime.size() == 2">
AND w.create_time BETWEEN #{req.createTime[0]} AND #{req.createTime[1]}
</if>
</where>
ORDER BY w.id DESC
</select>
<!-- 我发起的工单分页查询SQL -->
<select id="selectMyWorkOrderPage" resultMap="WorkOrderPageResultMap">
SELECT
w.id,
w.title,
w.type,
w.level,
w.content,
w.from_user_id,
w.from_dept_id,
w.assignee_user_id,
w.assignee_dept_id,
w.status,
w.expected_time,
w.completed_time,
w.result_description,
w.result,
w.process_instance_id,
w.create_time,
w.update_time,
fu.nickname AS from_user_name,
fd.name AS from_dept_name,
au.nickname AS assignee_user_name,
ad.name AS assignee_dept_name,
wt.label AS type_name,
wl.label AS level_name,
ws.label AS status_name
FROM bpm_oa_work_order w
LEFT JOIN system_users fu ON w.from_user_id = fu.id
LEFT JOIN system_dept fd ON w.from_dept_id = fd.id
LEFT JOIN system_users au ON w.assignee_user_id = au.id
LEFT JOIN system_dept ad ON w.assignee_dept_id = ad.id
LEFT JOIN system_dict_data wt ON wt.dict_type = 'work_order_type' AND wt.value = w.type
LEFT JOIN system_dict_data wl ON wl.dict_type = 'work_order_level' AND wl.value = CAST(w.level AS CHAR)
LEFT JOIN system_dict_data ws ON ws.dict_type = 'work_order_status' AND ws.value = CAST(w.status AS CHAR)
<where>
w.deleted = 0 AND w.from_user_id = #{loginUserId}
<if test="req.title != null and req.title != ''">
AND w.title LIKE CONCAT('%', #{req.title}, '%')
</if>
<if test="req.type != null and req.type != ''">
AND w.type = #{req.type}
</if>
<if test="req.status != null">
AND w.status = #{req.status}
</if>
<if test="req.createTime != null and req.createTime.size() == 2">
AND w.create_time BETWEEN #{req.createTime[0]} AND #{req.createTime[1]}
</if>
</where>
ORDER BY w.id DESC
</select>
<!-- 分配给我的工单分页查询SQL -->
<select id="selectAssignedWorkOrderPage" resultMap="WorkOrderPageResultMap">
SELECT
w.id,
w.title,
w.type,
w.level,
w.content,
w.from_user_id,
w.from_dept_id,
w.assignee_user_id,
w.assignee_dept_id,
w.status,
w.expected_time,
w.completed_time,
w.result_description,
w.result,
w.process_instance_id,
w.create_time,
w.update_time,
fu.nickname AS from_user_name,
fd.name AS from_dept_name,
au.nickname AS assignee_user_name,
ad.name AS assignee_dept_name,
wt.label AS type_name,
wl.label AS level_name,
ws.label AS status_name
FROM bpm_oa_work_order w
LEFT JOIN system_users fu ON w.from_user_id = fu.id
LEFT JOIN system_dept fd ON w.from_dept_id = fd.id
LEFT JOIN system_users au ON w.assignee_user_id = au.id
LEFT JOIN system_dept ad ON w.assignee_dept_id = ad.id
LEFT JOIN system_dict_data wt ON wt.dict_type = 'work_order_type' AND wt.value = w.type
LEFT JOIN system_dict_data wl ON wl.dict_type = 'work_order_level' AND wl.value = CAST(w.level AS CHAR)
LEFT JOIN system_dict_data ws ON ws.dict_type = 'work_order_status' AND ws.value = CAST(w.status AS CHAR)
<where>
w.deleted = 0 AND w.assignee_user_id = #{loginUserId}
<if test="req.title != null and req.title != ''">
AND w.title LIKE CONCAT('%', #{req.title}, '%')
</if>
<if test="req.type != null and req.type != ''">
AND w.type = #{req.type}
</if>
<if test="req.status != null">
AND w.status = #{req.status}
</if>
<if test="req.createTime != null and req.createTime.size() == 2">
AND w.create_time BETWEEN #{req.createTime[0]} AND #{req.createTime[1]}
</if>
</where>
ORDER BY w.id DESC
</select>
</mapper>