jiaxing.zhou

feat(exception): 实现异常工单管理模块

- 创建异常工单表和处理日志表结构
- 添加相关索引和外键约束
- 初始化菜单权限及管理员角色访问控制
- 实现后端异常工单实体类与数据传输对象
- 开发前后端API接口用于工单增删改查及状态更新
- 提供工单处理日志记录与查询功能
- 支持工单统计信息展示
- 实现工单状态变更历史追踪
- 添加工单批量操作支持
- 配置MyBatis映射文件实现复杂查询逻辑
- 建立前端异常工单类型定义和HTTP请求封装
- 提供枚举转换和状态颜色标识工具函数
1 +package com.apple.erp.controller;
2 +
3 +import com.apple.erp.dto.ExceptionWorkorderAddReq;
4 +import com.apple.erp.dto.ExceptionWorkorderQueryReq;
5 +import com.apple.erp.dto.ExceptionWorkorderRes;
6 +import com.apple.erp.dto.ExceptionWorkorderStatusUpdateReq;
7 +import com.apple.erp.dto.ExceptionWorkorderUpdateReq;
8 +import com.apple.erp.service.ExceptionWorkorderService;
9 +import com.apple.erp.dto.response.ApiRes;
10 +import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
11 +import io.swagger.v3.oas.annotations.Operation;
12 +import io.swagger.v3.oas.annotations.Parameter;
13 +import io.swagger.v3.oas.annotations.tags.Tag;
14 +import org.springframework.beans.factory.annotation.Autowired;
15 +import org.springframework.security.access.prepost.PreAuthorize;
16 +import org.springframework.validation.annotation.Validated;
17 +import org.springframework.web.bind.annotation.*;
18 +
19 +import javax.validation.Valid;
20 +import java.util.List;
21 +
22 +/**
23 + * 异常工单Controller
24 + *
25 + * @author Apple ERP System
26 + * @since 2025-01-27
27 + */
28 +@Tag(name = "异常工单管理", description = "异常工单相关接口")
29 +@RestController
30 +@RequestMapping("/api/exception-workorder")
31 +@Validated
32 +public class ExceptionWorkorderController {
33 +
34 + @Autowired
35 + private ExceptionWorkorderService exceptionWorkorderService;
36 +
37 + @Operation(summary = "分页查询异常工单列表", description = "根据条件分页查询异常工单列表")
38 + @GetMapping("/list")
39 + @PreAuthorize("hasAuthority('exception:workorder:list')")
40 + public ApiRes<Page<ExceptionWorkorderRes>> getExceptionWorkorderList(@Valid ExceptionWorkorderQueryReq queryReq) {
41 + Page<ExceptionWorkorderRes> page = exceptionWorkorderService.getExceptionWorkorderList(queryReq);
42 + return ApiRes.success(page);
43 + }
44 +
45 + @Operation(summary = "获取异常工单详情", description = "根据工单ID获取异常工单详细信息,包含处理日志")
46 + @GetMapping("/{workorderId}")
47 + @PreAuthorize("hasAuthority('exception:workorder:detail')")
48 + public ApiRes<ExceptionWorkorderRes> getExceptionWorkorderDetail(
49 + @Parameter(description = "工单ID", required = true) @PathVariable Long workorderId) {
50 + ExceptionWorkorderRes workorderRes = exceptionWorkorderService.getExceptionWorkorderDetail(workorderId);
51 + if (workorderRes != null) {
52 + return ApiRes.success(workorderRes);
53 + }
54 + return ApiRes.error("异常工单不存在或已删除");
55 + }
56 +
57 + @Operation(summary = "新增异常工单", description = "创建新的异常工单")
58 + @PostMapping
59 + @PreAuthorize("hasAuthority('exception:workorder:add')")
60 + public ApiRes<Void> addExceptionWorkorder(@Valid @RequestBody ExceptionWorkorderAddReq addReq) {
61 + try {
62 + boolean result = exceptionWorkorderService.addExceptionWorkorder(addReq);
63 + if (result) {
64 + return ApiRes.success("新增异常工单成功", null);
65 + } else {
66 + return ApiRes.error("新增异常工单失败");
67 + }
68 + } catch (Exception e) {
69 + return ApiRes.error("新增异常工单失败: " + e.getMessage());
70 + }
71 + }
72 +
73 + @Operation(summary = "更新异常工单", description = "更新异常工单信息")
74 + @PutMapping
75 + @PreAuthorize("hasAuthority('exception:workorder:edit')")
76 + public ApiRes<Void> updateExceptionWorkorder(@Valid @RequestBody ExceptionWorkorderUpdateReq updateReq) {
77 + try {
78 + boolean result = exceptionWorkorderService.updateExceptionWorkorder(updateReq);
79 + if (result) {
80 + return ApiRes.success("更新异常工单成功", null);
81 + } else {
82 + return ApiRes.error("更新异常工单失败");
83 + }
84 + } catch (Exception e) {
85 + return ApiRes.error("更新异常工单失败: " + e.getMessage());
86 + }
87 + }
88 +
89 + @Operation(summary = "更新工单状态", description = "更新异常工单状态并记录处理日志")
90 + @PostMapping("/status")
91 + @PreAuthorize("hasAuthority('exception:workorder:edit')")
92 + public ApiRes<Void> updateWorkorderStatus(@Valid @RequestBody ExceptionWorkorderStatusUpdateReq statusUpdateReq) {
93 + try {
94 + boolean result = exceptionWorkorderService.updateWorkorderStatus(statusUpdateReq);
95 + if (result) {
96 + return ApiRes.success("更新工单状态成功", null);
97 + } else {
98 + return ApiRes.error("更新工单状态失败");
99 + }
100 + } catch (Exception e) {
101 + return ApiRes.error("更新工单状态失败: " + e.getMessage());
102 + }
103 + }
104 +
105 + @Operation(summary = "删除异常工单", description = "根据工单ID逻辑删除异常工单")
106 + @DeleteMapping("/{workorderId}")
107 + @PreAuthorize("hasAuthority('exception:workorder:delete')")
108 + public ApiRes<Void> deleteExceptionWorkorder(
109 + @Parameter(description = "工单ID", required = true) @PathVariable Long workorderId) {
110 + try {
111 + boolean result = exceptionWorkorderService.deleteExceptionWorkorder(workorderId);
112 + if (result) {
113 + return ApiRes.success("删除异常工单成功", null);
114 + } else {
115 + return ApiRes.error("删除异常工单失败");
116 + }
117 + } catch (Exception e) {
118 + return ApiRes.error("删除异常工单失败: " + e.getMessage());
119 + }
120 + }
121 +
122 + @Operation(summary = "批量删除异常工单", description = "根据工单ID列表批量逻辑删除异常工单")
123 + @DeleteMapping("/batch")
124 + @PreAuthorize("hasAuthority('exception:workorder:delete')")
125 + public ApiRes<Void> batchDeleteExceptionWorkorders(
126 + @Parameter(description = "工单ID列表", required = true) @RequestBody List<Long> workorderIds) {
127 + try {
128 + if (workorderIds == null || workorderIds.isEmpty()) {
129 + return ApiRes.error("工单ID列表不能为空");
130 + }
131 + boolean result = exceptionWorkorderService.batchDeleteExceptionWorkorders(workorderIds);
132 + if (result) {
133 + return ApiRes.success("批量删除异常工单成功", null);
134 + } else {
135 + return ApiRes.error("批量删除异常工单失败");
136 + }
137 + } catch (Exception e) {
138 + return ApiRes.error("批量删除异常工单失败: " + e.getMessage());
139 + }
140 + }
141 +
142 + @Operation(summary = "批量更新工单状态", description = "批量更新异常工单状态")
143 + @PostMapping("/batch-status")
144 + @PreAuthorize("hasAuthority('exception:workorder:edit')")
145 + public ApiRes<Void> batchUpdateWorkorderStatus(
146 + @Parameter(description = "工单ID列表", required = true) @RequestParam List<Long> workorderIds,
147 + @Parameter(description = "工单状态", required = true) @RequestParam Integer workorderStatus,
148 + @Parameter(description = "处理人", required = true) @RequestParam String handlerUser) {
149 + try {
150 + if (workorderIds == null || workorderIds.isEmpty()) {
151 + return ApiRes.error("工单ID列表不能为空");
152 + }
153 + boolean result = exceptionWorkorderService.batchUpdateWorkorderStatus(workorderIds, workorderStatus, handlerUser);
154 + if (result) {
155 + return ApiRes.success("批量更新工单状态成功", null);
156 + } else {
157 + return ApiRes.error("批量更新工单状态失败");
158 + }
159 + } catch (Exception e) {
160 + return ApiRes.error("批量更新工单状态失败: " + e.getMessage());
161 + }
162 + }
163 +
164 + @Operation(summary = "获取异常工单统计信息", description = "获取异常工单的统计信息")
165 + @GetMapping("/stats")
166 + @PreAuthorize("hasAuthority('exception:workorder:list')")
167 + public ApiRes<List<ExceptionWorkorderRes>> getExceptionWorkorderStats() {
168 + List<ExceptionWorkorderRes> stats = exceptionWorkorderService.getExceptionWorkorderStats();
169 + return ApiRes.success(stats);
170 + }
171 +}
1 +package com.apple.erp.dto;
2 +
3 +import io.swagger.v3.oas.annotations.media.Schema;
4 +import lombok.Data;
5 +
6 +import javax.validation.constraints.NotBlank;
7 +import javax.validation.constraints.NotNull;
8 +import java.time.LocalDateTime;
9 +
10 +/**
11 + * 异常工单新增请求DTO
12 + *
13 + * @author Apple ERP System
14 + * @since 2025-01-27
15 + */
16 +@Data
17 +@Schema(description = "异常工单新增请求")
18 +public class ExceptionWorkorderAddReq {
19 +
20 + @NotBlank(message = "工单编号不能为空")
21 + @Schema(description = "工单编号", required = true)
22 + private String workorderNo;
23 +
24 + @Schema(description = "关联订单编号")
25 + private String orderNo;
26 +
27 + @Schema(description = "经销商编码")
28 + private String dealerCode;
29 +
30 + @Schema(description = "经销商名称")
31 + private String dealerName;
32 +
33 + @NotNull(message = "异常类型不能为空")
34 + @Schema(description = "异常类型(1-逻辑验证异常/2-源头验证异常/3-交叉验证异常)", required = true)
35 + private Integer exceptionType;
36 +
37 + @NotNull(message = "严重程度不能为空")
38 + @Schema(description = "严重程度(1-高/2-中/3-低)", required = true)
39 + private Integer severityLevel;
40 +
41 + @Schema(description = "工单状态(1-待处理/2-处理中/3-已解决/4-已关闭)")
42 + private Integer workorderStatus = 1;
43 +
44 + @Schema(description = "预计处理完成时间")
45 + private LocalDateTime expectCompleteTime;
46 +
47 + @Schema(description = "处理人")
48 + private String handlerUser;
49 +
50 + @Schema(description = "异常描述")
51 + private String exceptionDesc;
52 +
53 + @Schema(description = "处理建议")
54 + private String handleSuggest;
55 +
56 + @Schema(description = "数据来源")
57 + private String dataSource;
58 +}
1 +package com.apple.erp.dto;
2 +
3 +import com.fasterxml.jackson.annotation.JsonFormat;
4 +import io.swagger.v3.oas.annotations.media.Schema;
5 +import lombok.Data;
6 +
7 +import java.time.LocalDateTime;
8 +
9 +/**
10 + * 异常工单处理日志响应DTO
11 + *
12 + * @author Apple ERP System
13 + * @since 2025-01-27
14 + */
15 +@Data
16 +@Schema(description = "异常工单处理日志响应")
17 +public class ExceptionWorkorderLogRes {
18 +
19 + @Schema(description = "日志ID")
20 + private Long logId;
21 +
22 + @Schema(description = "关联工单ID")
23 + private Long workorderId;
24 +
25 + @Schema(description = "关联工单编号")
26 + private String workorderNo;
27 +
28 + @Schema(description = "处理人")
29 + private String handleUser;
30 +
31 + @Schema(description = "处理时间")
32 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
33 + private LocalDateTime handleTime;
34 +
35 + @Schema(description = "处理前状态")
36 + private Integer beforeStatus;
37 +
38 + @Schema(description = "处理前状态名称")
39 + private String beforeStatusName;
40 +
41 + @Schema(description = "处理后状态")
42 + private Integer afterStatus;
43 +
44 + @Schema(description = "处理后状态名称")
45 + private String afterStatusName;
46 +
47 + @Schema(description = "处理意见")
48 + private String handleOpinion;
49 +
50 + @Schema(description = "附件URL")
51 + private String attachUrl;
52 +
53 + @Schema(description = "创建者")
54 + private String createBy;
55 +
56 + @Schema(description = "创建时间")
57 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
58 + private LocalDateTime createTime;
59 +}
1 +package com.apple.erp.dto;
2 +
3 +import io.swagger.v3.oas.annotations.media.Schema;
4 +import lombok.Data;
5 +
6 +import java.time.LocalDateTime;
7 +
8 +/**
9 + * 异常工单查询请求DTO
10 + *
11 + * @author Apple ERP System
12 + * @since 2025-01-27
13 + */
14 +@Data
15 +@Schema(description = "异常工单查询请求")
16 +public class ExceptionWorkorderQueryReq {
17 +
18 + @Schema(description = "工单编号")
19 + private String workorderNo;
20 +
21 + @Schema(description = "关联订单编号")
22 + private String orderNo;
23 +
24 + @Schema(description = "经销商编码")
25 + private String dealerCode;
26 +
27 + @Schema(description = "经销商名称")
28 + private String dealerName;
29 +
30 + @Schema(description = "工单状态(1-待处理/2-处理中/3-已解决/4-已关闭)")
31 + private Integer workorderStatus;
32 +
33 + @Schema(description = "异常类型(1-逻辑验证异常/2-源头验证异常/3-交叉验证异常)")
34 + private Integer exceptionType;
35 +
36 + @Schema(description = "严重程度(1-高/2-中/3-低)")
37 + private Integer severityLevel;
38 +
39 + @Schema(description = "处理人")
40 + private String handlerUser;
41 +
42 + @Schema(description = "开始时间")
43 + private LocalDateTime startTime;
44 +
45 + @Schema(description = "结束时间")
46 + private LocalDateTime endTime;
47 +
48 + @Schema(description = "页码")
49 + private Integer pageNum = 1;
50 +
51 + @Schema(description = "每页大小")
52 + private Integer pageSize = 10;
53 +}
1 +package com.apple.erp.dto;
2 +
3 +import com.fasterxml.jackson.annotation.JsonFormat;
4 +import io.swagger.v3.oas.annotations.media.Schema;
5 +import lombok.Data;
6 +
7 +import java.time.LocalDateTime;
8 +import java.util.List;
9 +
10 +/**
11 + * 异常工单响应DTO
12 + *
13 + * @author Apple ERP System
14 + * @since 2025-01-27
15 + */
16 +@Data
17 +@Schema(description = "异常工单响应")
18 +public class ExceptionWorkorderRes {
19 +
20 + @Schema(description = "工单ID")
21 + private Long workorderId;
22 +
23 + @Schema(description = "工单编号")
24 + private String workorderNo;
25 +
26 + @Schema(description = "关联订单编号")
27 + private String orderNo;
28 +
29 + @Schema(description = "经销商编码")
30 + private String dealerCode;
31 +
32 + @Schema(description = "经销商名称")
33 + private String dealerName;
34 +
35 + @Schema(description = "异常类型(1-逻辑验证异常/2-源头验证异常/3-交叉验证异常)")
36 + private Integer exceptionType;
37 +
38 + @Schema(description = "异常类型名称")
39 + private String exceptionTypeName;
40 +
41 + @Schema(description = "严重程度(1-高/2-中/3-低)")
42 + private Integer severityLevel;
43 +
44 + @Schema(description = "严重程度名称")
45 + private String severityLevelName;
46 +
47 + @Schema(description = "工单状态(1-待处理/2-处理中/3-已解决/4-已关闭)")
48 + private Integer workorderStatus;
49 +
50 + @Schema(description = "工单状态名称")
51 + private String workorderStatusName;
52 +
53 + @Schema(description = "工单创建时间")
54 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
55 + private LocalDateTime createTime;
56 +
57 + @Schema(description = "预计处理完成时间")
58 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
59 + private LocalDateTime expectCompleteTime;
60 +
61 + @Schema(description = "处理人")
62 + private String handlerUser;
63 +
64 + @Schema(description = "异常描述")
65 + private String exceptionDesc;
66 +
67 + @Schema(description = "处理建议")
68 + private String handleSuggest;
69 +
70 + @Schema(description = "数据来源")
71 + private String dataSource;
72 +
73 + @Schema(description = "创建者")
74 + private String createBy;
75 +
76 + @Schema(description = "更新者")
77 + private String updateBy;
78 +
79 + @Schema(description = "更新时间")
80 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
81 + private LocalDateTime updateTime;
82 +
83 + @Schema(description = "处理日志列表")
84 + private List<ExceptionWorkorderLogRes> workorderLogs;
85 +}
1 +package com.apple.erp.dto;
2 +
3 +import io.swagger.v3.oas.annotations.media.Schema;
4 +import lombok.Data;
5 +
6 +import javax.validation.constraints.NotNull;
7 +
8 +/**
9 + * 异常工单状态更新请求DTO
10 + *
11 + * @author Apple ERP System
12 + * @since 2025-01-27
13 + */
14 +@Data
15 +@Schema(description = "异常工单状态更新请求")
16 +public class ExceptionWorkorderStatusUpdateReq {
17 +
18 + @NotNull(message = "工单ID不能为空")
19 + @Schema(description = "工单ID", required = true)
20 + private Long workorderId;
21 +
22 + @NotNull(message = "工单状态不能为空")
23 + @Schema(description = "工单状态(1-待处理/2-处理中/3-已解决/4-已关闭)", required = true)
24 + private Integer workorderStatus;
25 +
26 + @Schema(description = "处理人")
27 + private String handlerUser;
28 +
29 + @Schema(description = "处理意见")
30 + private String handleOpinion;
31 +
32 + @Schema(description = "附件URL")
33 + private String attachUrl;
34 +}
1 +package com.apple.erp.dto;
2 +
3 +import io.swagger.v3.oas.annotations.media.Schema;
4 +import lombok.Data;
5 +
6 +import javax.validation.constraints.NotNull;
7 +import java.time.LocalDateTime;
8 +
9 +/**
10 + * 异常工单更新请求DTO
11 + *
12 + * @author Apple ERP System
13 + * @since 2025-01-27
14 + */
15 +@Data
16 +@Schema(description = "异常工单更新请求")
17 +public class ExceptionWorkorderUpdateReq {
18 +
19 + @NotNull(message = "工单ID不能为空")
20 + @Schema(description = "工单ID", required = true)
21 + private Long workorderId;
22 +
23 + @Schema(description = "工单编号")
24 + private String workorderNo;
25 +
26 + @Schema(description = "关联订单编号")
27 + private String orderNo;
28 +
29 + @Schema(description = "经销商编码")
30 + private String dealerCode;
31 +
32 + @Schema(description = "经销商名称")
33 + private String dealerName;
34 +
35 + @Schema(description = "异常类型(1-逻辑验证异常/2-源头验证异常/3-交叉验证异常)")
36 + private Integer exceptionType;
37 +
38 + @Schema(description = "严重程度(1-高/2-中/3-低)")
39 + private Integer severityLevel;
40 +
41 + @Schema(description = "工单状态(1-待处理/2-处理中/3-已解决/4-已关闭)")
42 + private Integer workorderStatus;
43 +
44 + @Schema(description = "预计处理完成时间")
45 + private LocalDateTime expectCompleteTime;
46 +
47 + @Schema(description = "处理人")
48 + private String handlerUser;
49 +
50 + @Schema(description = "异常描述")
51 + private String exceptionDesc;
52 +
53 + @Schema(description = "处理建议")
54 + private String handleSuggest;
55 +
56 + @Schema(description = "数据来源")
57 + private String dataSource;
58 +}
1 +package com.apple.erp.entity;
2 +
3 +import com.baomidou.mybatisplus.annotation.IdType;
4 +import com.baomidou.mybatisplus.annotation.TableId;
5 +import com.baomidou.mybatisplus.annotation.TableName;
6 +import io.swagger.v3.oas.annotations.media.Schema;
7 +import lombok.Data;
8 +import lombok.EqualsAndHashCode;
9 +
10 +import java.time.LocalDateTime;
11 +
12 +/**
13 + * 异常工单表实体类
14 + *
15 + * @author Apple ERP System
16 + * @since 2025-01-27
17 + */
18 +@Data
19 +@EqualsAndHashCode(callSuper = false)
20 +@TableName("t_exception_workorder")
21 +@Schema(description = "异常工单表")
22 +public class ExceptionWorkorder {
23 +
24 + @TableId(value = "workorder_id", type = IdType.AUTO)
25 + @Schema(description = "工单ID")
26 + private Long workorderId;
27 +
28 + @Schema(description = "工单编号")
29 + private String workorderNo;
30 +
31 + @Schema(description = "关联订单编号")
32 + private String orderNo;
33 +
34 + @Schema(description = "经销商编码")
35 + private String dealerCode;
36 +
37 + @Schema(description = "经销商名称")
38 + private String dealerName;
39 +
40 + @Schema(description = "异常类型(1-逻辑验证异常/2-源头验证异常/3-交叉验证异常)")
41 + private Integer exceptionType;
42 +
43 + @Schema(description = "严重程度(1-高/2-中/3-低)")
44 + private Integer severityLevel;
45 +
46 + @Schema(description = "工单状态(1-待处理/2-处理中/3-已解决/4-已关闭)")
47 + private Integer workorderStatus;
48 +
49 + @Schema(description = "工单创建时间")
50 + private LocalDateTime createTime;
51 +
52 + @Schema(description = "预计处理完成时间")
53 + private LocalDateTime expectCompleteTime;
54 +
55 + @Schema(description = "处理人")
56 + private String handlerUser;
57 +
58 + @Schema(description = "异常描述")
59 + private String exceptionDesc;
60 +
61 + @Schema(description = "处理建议")
62 + private String handleSuggest;
63 +
64 + @Schema(description = "数据来源")
65 + private String dataSource;
66 +
67 + @Schema(description = "创建者")
68 + private String createBy;
69 +
70 + @Schema(description = "更新者")
71 + private String updateBy;
72 +
73 + @Schema(description = "更新时间")
74 + private LocalDateTime updateTime;
75 +
76 + @Schema(description = "删除标志(0代表存在 2代表删除)")
77 + private String delFlag;
78 +}
1 +package com.apple.erp.entity;
2 +
3 +import com.baomidou.mybatisplus.annotation.IdType;
4 +import com.baomidou.mybatisplus.annotation.TableId;
5 +import com.baomidou.mybatisplus.annotation.TableName;
6 +import io.swagger.v3.oas.annotations.media.Schema;
7 +import lombok.Data;
8 +import lombok.EqualsAndHashCode;
9 +
10 +import java.time.LocalDateTime;
11 +
12 +/**
13 + * 异常工单处理日志表实体类
14 + *
15 + * @author Apple ERP System
16 + * @since 2025-01-27
17 + */
18 +@Data
19 +@EqualsAndHashCode(callSuper = false)
20 +@TableName("t_exception_workorder_log")
21 +@Schema(description = "异常工单处理日志表")
22 +public class ExceptionWorkorderLog {
23 +
24 + @TableId(value = "log_id", type = IdType.AUTO)
25 + @Schema(description = "日志ID")
26 + private Long logId;
27 +
28 + @Schema(description = "关联工单ID")
29 + private Long workorderId;
30 +
31 + @Schema(description = "关联工单编号")
32 + private String workorderNo;
33 +
34 + @Schema(description = "处理人")
35 + private String handleUser;
36 +
37 + @Schema(description = "处理时间")
38 + private LocalDateTime handleTime;
39 +
40 + @Schema(description = "处理前状态")
41 + private Integer beforeStatus;
42 +
43 + @Schema(description = "处理后状态")
44 + private Integer afterStatus;
45 +
46 + @Schema(description = "处理意见")
47 + private String handleOpinion;
48 +
49 + @Schema(description = "附件URL")
50 + private String attachUrl;
51 +
52 + @Schema(description = "创建者")
53 + private String createBy;
54 +
55 + @Schema(description = "创建时间")
56 + private LocalDateTime createTime;
57 +
58 + @Schema(description = "更新者")
59 + private String updateBy;
60 +
61 + @Schema(description = "更新时间")
62 + private LocalDateTime updateTime;
63 +
64 + @Schema(description = "删除标志(0代表存在 2代表删除)")
65 + private String delFlag;
66 +}
1 +package com.apple.erp.mapper;
2 +
3 +import com.apple.erp.dto.ExceptionWorkorderLogRes;
4 +import com.apple.erp.entity.ExceptionWorkorderLog;
5 +import com.baomidou.mybatisplus.core.mapper.BaseMapper;
6 +import org.apache.ibatis.annotations.Mapper;
7 +import org.apache.ibatis.annotations.Param;
8 +
9 +import java.util.List;
10 +
11 +/**
12 + * 异常工单处理日志Mapper接口
13 + *
14 + * @author Apple ERP System
15 + * @since 2025-01-27
16 + */
17 +@Mapper
18 +public interface ExceptionWorkorderLogMapper extends BaseMapper<ExceptionWorkorderLog> {
19 +
20 + /**
21 + * 根据工单ID获取处理日志列表
22 + *
23 + * @param workorderId 工单ID
24 + * @return 处理日志列表
25 + */
26 + List<ExceptionWorkorderLogRes> selectWorkorderLogsByWorkorderId(@Param("workorderId") Long workorderId);
27 +
28 + /**
29 + * 根据工单ID列表批量获取处理日志
30 + *
31 + * @param workorderIds 工单ID列表
32 + * @return 处理日志列表
33 + */
34 + List<ExceptionWorkorderLogRes> selectWorkorderLogsByWorkorderIds(@Param("workorderIds") List<Long> workorderIds);
35 +}
1 +package com.apple.erp.mapper;
2 +
3 +import com.apple.erp.dto.ExceptionWorkorderQueryReq;
4 +import com.apple.erp.dto.ExceptionWorkorderRes;
5 +import com.apple.erp.entity.ExceptionWorkorder;
6 +import com.baomidou.mybatisplus.core.mapper.BaseMapper;
7 +import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
8 +import org.apache.ibatis.annotations.Mapper;
9 +import org.apache.ibatis.annotations.Param;
10 +
11 +import java.util.List;
12 +
13 +/**
14 + * 异常工单Mapper接口
15 + *
16 + * @author Apple ERP System
17 + * @since 2025-01-27
18 + */
19 +@Mapper
20 +public interface ExceptionWorkorderMapper extends BaseMapper<ExceptionWorkorder> {
21 +
22 + /**
23 + * 分页查询异常工单列表
24 + *
25 + * @param page 分页参数
26 + * @param queryReq 查询条件
27 + * @return 异常工单列表
28 + */
29 + Page<ExceptionWorkorderRes> selectExceptionWorkorderPage(Page<ExceptionWorkorderRes> page, @Param("query") ExceptionWorkorderQueryReq queryReq);
30 +
31 + /**
32 + * 根据工单ID获取异常工单详情
33 + *
34 + * @param workorderId 工单ID
35 + * @return 异常工单详情
36 + */
37 + ExceptionWorkorderRes selectExceptionWorkorderDetail(@Param("workorderId") Long workorderId);
38 +
39 + /**
40 + * 获取异常工单统计信息
41 + *
42 + * @return 统计信息
43 + */
44 + List<ExceptionWorkorderRes> selectExceptionWorkorderStats();
45 +
46 + /**
47 + * 批量更新工单状态
48 + *
49 + * @param workorderIds 工单ID列表
50 + * @param workorderStatus 工单状态
51 + * @param handlerUser 处理人
52 + * @return 更新数量
53 + */
54 + int batchUpdateWorkorderStatus(@Param("workorderIds") List<Long> workorderIds,
55 + @Param("workorderStatus") Integer workorderStatus,
56 + @Param("handlerUser") String handlerUser);
57 +}
1 +package com.apple.erp.service;
2 +
3 +import com.apple.erp.dto.ExceptionWorkorderAddReq;
4 +import com.apple.erp.dto.ExceptionWorkorderQueryReq;
5 +import com.apple.erp.dto.ExceptionWorkorderRes;
6 +import com.apple.erp.dto.ExceptionWorkorderStatusUpdateReq;
7 +import com.apple.erp.dto.ExceptionWorkorderUpdateReq;
8 +import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
9 +import com.baomidou.mybatisplus.extension.service.IService;
10 +import com.apple.erp.entity.ExceptionWorkorder;
11 +
12 +import java.util.List;
13 +
14 +/**
15 + * 异常工单Service接口
16 + *
17 + * @author Apple ERP System
18 + * @since 2025-01-27
19 + */
20 +public interface ExceptionWorkorderService extends IService<ExceptionWorkorder> {
21 +
22 + /**
23 + * 分页查询异常工单列表
24 + *
25 + * @param queryReq 查询条件
26 + * @return 异常工单列表(带分页信息)
27 + */
28 + Page<ExceptionWorkorderRes> getExceptionWorkorderList(ExceptionWorkorderQueryReq queryReq);
29 +
30 + /**
31 + * 获取异常工单详情
32 + *
33 + * @param workorderId 工单ID
34 + * @return 异常工单详情(包含处理日志)
35 + */
36 + ExceptionWorkorderRes getExceptionWorkorderDetail(Long workorderId);
37 +
38 + /**
39 + * 新增异常工单
40 + *
41 + * @param addReq 异常工单新增请求
42 + * @return 是否成功
43 + */
44 + boolean addExceptionWorkorder(ExceptionWorkorderAddReq addReq);
45 +
46 + /**
47 + * 更新异常工单
48 + *
49 + * @param updateReq 异常工单更新请求
50 + * @return 是否成功
51 + */
52 + boolean updateExceptionWorkorder(ExceptionWorkorderUpdateReq updateReq);
53 +
54 + /**
55 + * 更新工单状态
56 + *
57 + * @param statusUpdateReq 状态更新请求
58 + * @return 是否成功
59 + */
60 + boolean updateWorkorderStatus(ExceptionWorkorderStatusUpdateReq statusUpdateReq);
61 +
62 + /**
63 + * 删除异常工单
64 + *
65 + * @param workorderId 工单ID
66 + * @return 是否成功
67 + */
68 + boolean deleteExceptionWorkorder(Long workorderId);
69 +
70 + /**
71 + * 批量删除异常工单
72 + *
73 + * @param workorderIds 工单ID列表
74 + * @return 是否成功
75 + */
76 + boolean batchDeleteExceptionWorkorders(List<Long> workorderIds);
77 +
78 + /**
79 + * 批量更新工单状态
80 + *
81 + * @param workorderIds 工单ID列表
82 + * @param workorderStatus 工单状态
83 + * @param handlerUser 处理人
84 + * @return 是否成功
85 + */
86 + boolean batchUpdateWorkorderStatus(List<Long> workorderIds, Integer workorderStatus, String handlerUser);
87 +
88 + /**
89 + * 获取异常工单统计信息
90 + *
91 + * @return 统计信息
92 + */
93 + List<ExceptionWorkorderRes> getExceptionWorkorderStats();
94 +}
1 +package com.apple.erp.service.impl;
2 +
3 +import com.apple.erp.dto.ExceptionWorkorderAddReq;
4 +import com.apple.erp.dto.ExceptionWorkorderLogRes;
5 +import com.apple.erp.dto.ExceptionWorkorderQueryReq;
6 +import com.apple.erp.dto.ExceptionWorkorderRes;
7 +import com.apple.erp.dto.ExceptionWorkorderStatusUpdateReq;
8 +import com.apple.erp.dto.ExceptionWorkorderUpdateReq;
9 +import com.apple.erp.entity.ExceptionWorkorder;
10 +import com.apple.erp.entity.ExceptionWorkorderLog;
11 +import com.apple.erp.mapper.ExceptionWorkorderLogMapper;
12 +import com.apple.erp.mapper.ExceptionWorkorderMapper;
13 +import com.apple.erp.service.ExceptionWorkorderService;
14 +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
15 +import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
16 +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
17 +import org.springframework.beans.BeanUtils;
18 +import org.springframework.beans.factory.annotation.Autowired;
19 +import org.springframework.stereotype.Service;
20 +import org.springframework.transaction.annotation.Transactional;
21 +import org.springframework.util.StringUtils;
22 +
23 +import java.time.LocalDateTime;
24 +import java.util.List;
25 +import java.util.stream.Collectors;
26 +
27 +/**
28 + * 异常工单Service实现类
29 + *
30 + * @author Apple ERP System
31 + * @since 2025-01-27
32 + */
33 +@Service
34 +public class ExceptionWorkorderServiceImpl extends ServiceImpl<ExceptionWorkorderMapper, ExceptionWorkorder> implements ExceptionWorkorderService {
35 +
36 + @Autowired
37 + private ExceptionWorkorderMapper exceptionWorkorderMapper;
38 +
39 + @Autowired
40 + private ExceptionWorkorderLogMapper exceptionWorkorderLogMapper;
41 +
42 + @Override
43 + public Page<ExceptionWorkorderRes> getExceptionWorkorderList(ExceptionWorkorderQueryReq queryReq) {
44 + Page<ExceptionWorkorderRes> page = new Page<>(queryReq.getPageNum(), queryReq.getPageSize());
45 + return exceptionWorkorderMapper.selectExceptionWorkorderPage(page, queryReq);
46 + }
47 +
48 + @Override
49 + public ExceptionWorkorderRes getExceptionWorkorderDetail(Long workorderId) {
50 + ExceptionWorkorderRes workorderRes = exceptionWorkorderMapper.selectExceptionWorkorderDetail(workorderId);
51 + if (workorderRes != null) {
52 + // 获取处理日志
53 + List<ExceptionWorkorderLogRes> logs = exceptionWorkorderLogMapper.selectWorkorderLogsByWorkorderId(workorderId);
54 + workorderRes.setWorkorderLogs(logs);
55 + }
56 + return workorderRes;
57 + }
58 +
59 + @Override
60 + @Transactional
61 + public boolean addExceptionWorkorder(ExceptionWorkorderAddReq addReq) {
62 + ExceptionWorkorder workorder = new ExceptionWorkorder();
63 + BeanUtils.copyProperties(addReq, workorder);
64 + workorder.setCreateTime(LocalDateTime.now());
65 + workorder.setDelFlag("0");
66 +
67 + int result = exceptionWorkorderMapper.insert(workorder);
68 +
69 + // 记录处理日志
70 + if (result > 0) {
71 + ExceptionWorkorderLog log = new ExceptionWorkorderLog();
72 + log.setWorkorderId(workorder.getWorkorderId());
73 + log.setWorkorderNo(workorder.getWorkorderNo());
74 + log.setHandleUser(addReq.getHandlerUser());
75 + log.setHandleTime(LocalDateTime.now());
76 + log.setBeforeStatus(0);
77 + log.setAfterStatus(workorder.getWorkorderStatus());
78 + log.setHandleOpinion("工单创建");
79 + log.setCreateTime(LocalDateTime.now());
80 + log.setDelFlag("0");
81 + exceptionWorkorderLogMapper.insert(log);
82 + }
83 +
84 + return result > 0;
85 + }
86 +
87 + @Override
88 + @Transactional
89 + public boolean updateExceptionWorkorder(ExceptionWorkorderUpdateReq updateReq) {
90 + ExceptionWorkorder workorder = exceptionWorkorderMapper.selectById(updateReq.getWorkorderId());
91 + if (workorder == null || "2".equals(workorder.getDelFlag())) {
92 + return false;
93 + }
94 +
95 + BeanUtils.copyProperties(updateReq, workorder);
96 + workorder.setUpdateTime(LocalDateTime.now());
97 +
98 + return exceptionWorkorderMapper.updateById(workorder) > 0;
99 + }
100 +
101 + @Override
102 + @Transactional
103 + public boolean updateWorkorderStatus(ExceptionWorkorderStatusUpdateReq statusUpdateReq) {
104 + ExceptionWorkorder workorder = exceptionWorkorderMapper.selectById(statusUpdateReq.getWorkorderId());
105 + if (workorder == null || "2".equals(workorder.getDelFlag())) {
106 + return false;
107 + }
108 +
109 + Integer beforeStatus = workorder.getWorkorderStatus();
110 + workorder.setWorkorderStatus(statusUpdateReq.getWorkorderStatus());
111 + workorder.setHandlerUser(statusUpdateReq.getHandlerUser());
112 + workorder.setUpdateTime(LocalDateTime.now());
113 +
114 + int result = exceptionWorkorderMapper.updateById(workorder);
115 +
116 + // 记录处理日志
117 + if (result > 0) {
118 + ExceptionWorkorderLog log = new ExceptionWorkorderLog();
119 + log.setWorkorderId(workorder.getWorkorderId());
120 + log.setWorkorderNo(workorder.getWorkorderNo());
121 + log.setHandleUser(statusUpdateReq.getHandlerUser());
122 + log.setHandleTime(LocalDateTime.now());
123 + log.setBeforeStatus(beforeStatus);
124 + log.setAfterStatus(statusUpdateReq.getWorkorderStatus());
125 + log.setHandleOpinion(statusUpdateReq.getHandleOpinion());
126 + log.setAttachUrl(statusUpdateReq.getAttachUrl());
127 + log.setCreateTime(LocalDateTime.now());
128 + log.setDelFlag("0");
129 + exceptionWorkorderLogMapper.insert(log);
130 + }
131 +
132 + return result > 0;
133 + }
134 +
135 + @Override
136 + @Transactional
137 + public boolean deleteExceptionWorkorder(Long workorderId) {
138 + ExceptionWorkorder workorder = exceptionWorkorderMapper.selectById(workorderId);
139 + if (workorder == null || "2".equals(workorder.getDelFlag())) {
140 + return false;
141 + }
142 +
143 + workorder.setDelFlag("2");
144 + workorder.setUpdateTime(LocalDateTime.now());
145 +
146 + return exceptionWorkorderMapper.updateById(workorder) > 0;
147 + }
148 +
149 + @Override
150 + @Transactional
151 + public boolean batchDeleteExceptionWorkorders(List<Long> workorderIds) {
152 + if (workorderIds == null || workorderIds.isEmpty()) {
153 + return false;
154 + }
155 +
156 + ExceptionWorkorder workorder = new ExceptionWorkorder();
157 + workorder.setDelFlag("2");
158 + workorder.setUpdateTime(LocalDateTime.now());
159 +
160 + LambdaQueryWrapper<ExceptionWorkorder> queryWrapper = new LambdaQueryWrapper<>();
161 + queryWrapper.in(ExceptionWorkorder::getWorkorderId, workorderIds);
162 + queryWrapper.eq(ExceptionWorkorder::getDelFlag, "0");
163 +
164 + return exceptionWorkorderMapper.update(workorder, queryWrapper) > 0;
165 + }
166 +
167 + @Override
168 + @Transactional
169 + public boolean batchUpdateWorkorderStatus(List<Long> workorderIds, Integer workorderStatus, String handlerUser) {
170 + if (workorderIds == null || workorderIds.isEmpty()) {
171 + return false;
172 + }
173 +
174 + int result = exceptionWorkorderMapper.batchUpdateWorkorderStatus(workorderIds, workorderStatus, handlerUser);
175 +
176 + // 记录批量处理日志
177 + if (result > 0) {
178 + for (Long workorderId : workorderIds) {
179 + ExceptionWorkorder workorder = exceptionWorkorderMapper.selectById(workorderId);
180 + if (workorder != null && "0".equals(workorder.getDelFlag())) {
181 + ExceptionWorkorderLog log = new ExceptionWorkorderLog();
182 + log.setWorkorderId(workorderId);
183 + log.setWorkorderNo(workorder.getWorkorderNo());
184 + log.setHandleUser(handlerUser);
185 + log.setHandleTime(LocalDateTime.now());
186 + log.setBeforeStatus(workorder.getWorkorderStatus());
187 + log.setAfterStatus(workorderStatus);
188 + log.setHandleOpinion("批量状态更新");
189 + log.setCreateTime(LocalDateTime.now());
190 + log.setDelFlag("0");
191 + exceptionWorkorderLogMapper.insert(log);
192 + }
193 + }
194 + }
195 +
196 + return result > 0;
197 + }
198 +
199 + @Override
200 + public List<ExceptionWorkorderRes> getExceptionWorkorderStats() {
201 + return exceptionWorkorderMapper.selectExceptionWorkorderStats();
202 + }
203 +}
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
3 +<mapper namespace="com.apple.erp.mapper.ExceptionWorkorderLogMapper">
4 +
5 + <!-- 根据工单ID获取处理日志列表 -->
6 + <select id="selectWorkorderLogsByWorkorderId" resultType="com.apple.erp.dto.ExceptionWorkorderLogRes">
7 + SELECT
8 + ewl.log_id,
9 + ewl.workorder_id,
10 + ewl.workorder_no,
11 + ewl.handle_user,
12 + ewl.handle_time,
13 + ewl.before_status,
14 + CASE ewl.before_status
15 + WHEN 1 THEN '待处理'
16 + WHEN 2 THEN '处理中'
17 + WHEN 3 THEN '已解决'
18 + WHEN 4 THEN '已关闭'
19 + ELSE '未知状态'
20 + END AS before_status_name,
21 + ewl.after_status,
22 + CASE ewl.after_status
23 + WHEN 1 THEN '待处理'
24 + WHEN 2 THEN '处理中'
25 + WHEN 3 THEN '已解决'
26 + WHEN 4 THEN '已关闭'
27 + ELSE '未知状态'
28 + END AS after_status_name,
29 + ewl.handle_opinion,
30 + ewl.attach_url,
31 + ewl.create_by,
32 + ewl.create_time
33 + FROM t_exception_workorder_log ewl
34 + WHERE ewl.workorder_id = #{workorderId} AND ewl.del_flag = '0'
35 + ORDER BY ewl.handle_time DESC
36 + </select>
37 +
38 + <!-- 根据工单ID列表批量获取处理日志 -->
39 + <select id="selectWorkorderLogsByWorkorderIds" resultType="com.apple.erp.dto.ExceptionWorkorderLogRes">
40 + SELECT
41 + ewl.log_id,
42 + ewl.workorder_id,
43 + ewl.workorder_no,
44 + ewl.handle_user,
45 + ewl.handle_time,
46 + ewl.before_status,
47 + CASE ewl.before_status
48 + WHEN 1 THEN '待处理'
49 + WHEN 2 THEN '处理中'
50 + WHEN 3 THEN '已解决'
51 + WHEN 4 THEN '已关闭'
52 + ELSE '未知状态'
53 + END AS before_status_name,
54 + ewl.after_status,
55 + CASE ewl.after_status
56 + WHEN 1 THEN '待处理'
57 + WHEN 2 THEN '处理中'
58 + WHEN 3 THEN '已解决'
59 + WHEN 4 THEN '已关闭'
60 + ELSE '未知状态'
61 + END AS after_status_name,
62 + ewl.handle_opinion,
63 + ewl.attach_url,
64 + ewl.create_by,
65 + ewl.create_time
66 + FROM t_exception_workorder_log ewl
67 + WHERE ewl.workorder_id IN
68 + <foreach collection="workorderIds" item="workorderId" open="(" separator="," close=")">
69 + #{workorderId}
70 + </foreach>
71 + AND ewl.del_flag = '0'
72 + ORDER BY ewl.workorder_id, ewl.handle_time DESC
73 + </select>
74 +
75 +</mapper>
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
3 +<mapper namespace="com.apple.erp.mapper.ExceptionWorkorderMapper">
4 +
5 + <!-- 分页查询异常工单列表 -->
6 + <select id="selectExceptionWorkorderPage" resultType="com.apple.erp.dto.ExceptionWorkorderRes">
7 + SELECT
8 + ew.workorder_id,
9 + ew.workorder_no,
10 + ew.order_no,
11 + ew.dealer_code,
12 + ew.dealer_name,
13 + ew.exception_type,
14 + CASE ew.exception_type
15 + WHEN 1 THEN '逻辑验证异常'
16 + WHEN 2 THEN '源头验证异常'
17 + WHEN 3 THEN '交叉验证异常'
18 + ELSE '未知类型'
19 + END AS exception_type_name,
20 + ew.severity_level,
21 + CASE ew.severity_level
22 + WHEN 1 THEN '高'
23 + WHEN 2 THEN '中'
24 + WHEN 3 THEN '低'
25 + ELSE '未知'
26 + END AS severity_level_name,
27 + ew.workorder_status,
28 + CASE ew.workorder_status
29 + WHEN 1 THEN '待处理'
30 + WHEN 2 THEN '处理中'
31 + WHEN 3 THEN '已解决'
32 + WHEN 4 THEN '已关闭'
33 + ELSE '未知状态'
34 + END AS workorder_status_name,
35 + ew.create_time,
36 + ew.expect_complete_time,
37 + ew.handler_user,
38 + ew.exception_desc,
39 + ew.handle_suggest,
40 + ew.data_source,
41 + ew.create_by,
42 + ew.update_by,
43 + ew.update_time
44 + FROM t_exception_workorder ew
45 + <where>
46 + ew.del_flag = '0'
47 + <if test="query.workorderNo != null and query.workorderNo != ''">
48 + AND ew.workorder_no LIKE CONCAT('%', #{query.workorderNo}, '%')
49 + </if>
50 + <if test="query.orderNo != null and query.orderNo != ''">
51 + AND ew.order_no LIKE CONCAT('%', #{query.orderNo}, '%')
52 + </if>
53 + <if test="query.dealerCode != null and query.dealerCode != ''">
54 + AND ew.dealer_code = #{query.dealerCode}
55 + </if>
56 + <if test="query.dealerName != null and query.dealerName != ''">
57 + AND ew.dealer_name LIKE CONCAT('%', #{query.dealerName}, '%')
58 + </if>
59 + <if test="query.workorderStatus != null">
60 + AND ew.workorder_status = #{query.workorderStatus}
61 + </if>
62 + <if test="query.exceptionType != null">
63 + AND ew.exception_type = #{query.exceptionType}
64 + </if>
65 + <if test="query.severityLevel != null">
66 + AND ew.severity_level = #{query.severityLevel}
67 + </if>
68 + <if test="query.handlerUser != null and query.handlerUser != ''">
69 + AND ew.handler_user LIKE CONCAT('%', #{query.handlerUser}, '%')
70 + </if>
71 + <if test="query.startTime != null">
72 + AND ew.create_time >= #{query.startTime}
73 + </if>
74 + <if test="query.endTime != null">
75 + AND ew.create_time &lt;= #{query.endTime}
76 + </if>
77 + </where>
78 + ORDER BY ew.create_time DESC
79 + </select>
80 +
81 + <!-- 根据工单ID获取异常工单详情 -->
82 + <select id="selectExceptionWorkorderDetail" resultType="com.apple.erp.dto.ExceptionWorkorderRes">
83 + SELECT
84 + ew.workorder_id,
85 + ew.workorder_no,
86 + ew.order_no,
87 + ew.dealer_code,
88 + ew.dealer_name,
89 + ew.exception_type,
90 + CASE ew.exception_type
91 + WHEN 1 THEN '逻辑验证异常'
92 + WHEN 2 THEN '源头验证异常'
93 + WHEN 3 THEN '交叉验证异常'
94 + ELSE '未知类型'
95 + END AS exception_type_name,
96 + ew.severity_level,
97 + CASE ew.severity_level
98 + WHEN 1 THEN '高'
99 + WHEN 2 THEN '中'
100 + WHEN 3 THEN '低'
101 + ELSE '未知'
102 + END AS severity_level_name,
103 + ew.workorder_status,
104 + CASE ew.workorder_status
105 + WHEN 1 THEN '待处理'
106 + WHEN 2 THEN '处理中'
107 + WHEN 3 THEN '已解决'
108 + WHEN 4 THEN '已关闭'
109 + ELSE '未知状态'
110 + END AS workorder_status_name,
111 + ew.create_time,
112 + ew.expect_complete_time,
113 + ew.handler_user,
114 + ew.exception_desc,
115 + ew.handle_suggest,
116 + ew.data_source,
117 + ew.create_by,
118 + ew.update_by,
119 + ew.update_time
120 + FROM t_exception_workorder ew
121 + WHERE ew.workorder_id = #{workorderId} AND ew.del_flag = '0'
122 + </select>
123 +
124 + <!-- 获取异常工单统计信息 -->
125 + <select id="selectExceptionWorkorderStats" resultType="com.apple.erp.dto.ExceptionWorkorderRes">
126 + SELECT
127 + 'total' AS workorder_no,
128 + COUNT(*) AS workorder_id,
129 + SUM(CASE WHEN workorder_status = 1 THEN 1 ELSE 0 END) AS exception_type,
130 + SUM(CASE WHEN workorder_status = 2 THEN 1 ELSE 0 END) AS severity_level,
131 + SUM(CASE WHEN workorder_status = 3 THEN 1 ELSE 0 END) AS workorder_status,
132 + SUM(CASE WHEN workorder_status = 4 THEN 1 ELSE 0 END) AS handler_user,
133 + SUM(CASE WHEN severity_level = 1 THEN 1 ELSE 0 END) AS exception_desc,
134 + SUM(CASE WHEN severity_level = 2 THEN 1 ELSE 0 END) AS handle_suggest,
135 + SUM(CASE WHEN severity_level = 3 THEN 1 ELSE 0 END) AS data_source
136 + FROM t_exception_workorder
137 + WHERE del_flag = '0'
138 + </select>
139 +
140 + <!-- 批量更新工单状态 -->
141 + <update id="batchUpdateWorkorderStatus">
142 + UPDATE t_exception_workorder
143 + SET workorder_status = #{workorderStatus},
144 + handler_user = #{handlerUser},
145 + update_time = NOW()
146 + WHERE workorder_id IN
147 + <foreach collection="workorderIds" item="workorderId" open="(" separator="," close=")">
148 + #{workorderId}
149 + </foreach>
150 + AND del_flag = '0'
151 + </update>
152 +
153 +</mapper>
1 +-- 异常工单模块完整初始化脚本
2 +-- 包含表结构创建、权限配置、测试数据插入
3 +
4 +-- 1. 创建异常工单表
5 +CREATE TABLE IF NOT EXISTS t_exception_workorder (
6 + workorder_id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '工单ID',
7 + workorder_no VARCHAR(30) NOT NULL COMMENT '工单编号',
8 + order_no VARCHAR(30) COMMENT '关联订单编号',
9 + dealer_code VARCHAR(20) COMMENT '经销商编码',
10 + dealer_name VARCHAR(100) COMMENT '经销商名称',
11 + exception_type TINYINT NOT NULL COMMENT '异常类型(1-逻辑验证异常/2-源头验证异常/3-交叉验证异常)',
12 + severity_level TINYINT NOT NULL COMMENT '严重程度(1-高/2-中/3-低)',
13 + workorder_status TINYINT DEFAULT 1 COMMENT '工单状态(1-待处理/2-处理中/3-已解决/4-已关闭)',
14 + create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '工单创建时间',
15 + expect_complete_time DATETIME COMMENT '预计处理完成时间',
16 + handler_user VARCHAR(20) COMMENT '处理人',
17 + exception_desc VARCHAR(500) COMMENT '异常描述',
18 + handle_suggest VARCHAR(500) COMMENT '处理建议',
19 + data_source VARCHAR(20) COMMENT '数据来源',
20 + create_by VARCHAR(64) DEFAULT '' COMMENT '创建者',
21 + update_by VARCHAR(64) DEFAULT '' COMMENT '更新者',
22 + update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
23 + del_flag CHAR(1) DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)'
24 +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='异常工单表';
25 +
26 +-- 2. 创建异常工单处理日志表
27 +CREATE TABLE IF NOT EXISTS t_exception_workorder_log (
28 + log_id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '日志ID',
29 + workorder_id BIGINT NOT NULL COMMENT '关联工单ID',
30 + workorder_no VARCHAR(30) NOT NULL COMMENT '关联工单编号',
31 + handle_user VARCHAR(20) NOT NULL COMMENT '处理人',
32 + handle_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '处理时间',
33 + before_status TINYINT NOT NULL COMMENT '处理前状态',
34 + after_status TINYINT NOT NULL COMMENT '处理后状态',
35 + handle_opinion VARCHAR(500) COMMENT '处理意见',
36 + attach_url VARCHAR(200) COMMENT '附件URL',
37 + create_by VARCHAR(64) DEFAULT '' COMMENT '创建者',
38 + create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
39 + update_by VARCHAR(64) DEFAULT '' COMMENT '更新者',
40 + update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
41 + del_flag CHAR(1) DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)'
42 +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='异常工单处理日志表';
43 +
44 +-- 3. 创建索引
45 +-- 异常工单表索引
46 +CREATE UNIQUE INDEX IF NOT EXISTS uk_workorder_no ON t_exception_workorder(workorder_no);
47 +CREATE INDEX IF NOT EXISTS idx_workorder_dealer_code ON t_exception_workorder(dealer_code);
48 +CREATE INDEX IF NOT EXISTS idx_workorder_order_no ON t_exception_workorder(order_no);
49 +CREATE INDEX IF NOT EXISTS idx_workorder_status ON t_exception_workorder(workorder_status);
50 +CREATE INDEX IF NOT EXISTS idx_workorder_exception_type ON t_exception_workorder(exception_type);
51 +CREATE INDEX IF NOT EXISTS idx_workorder_severity_level ON t_exception_workorder(severity_level);
52 +CREATE INDEX IF NOT EXISTS idx_workorder_create_time ON t_exception_workorder(create_time);
53 +CREATE INDEX IF NOT EXISTS idx_workorder_handler_user ON t_exception_workorder(handler_user);
54 +
55 +-- 异常工单处理日志表索引
56 +CREATE INDEX IF NOT EXISTS idx_log_workorder_id ON t_exception_workorder_log(workorder_id);
57 +CREATE INDEX IF NOT EXISTS idx_log_workorder_no ON t_exception_workorder_log(workorder_no);
58 +CREATE INDEX IF NOT EXISTS idx_log_handle_user ON t_exception_workorder_log(handle_user);
59 +CREATE INDEX IF NOT EXISTS idx_log_handle_time ON t_exception_workorder_log(handle_time);
60 +
61 +-- 4. 创建外键约束
62 +ALTER TABLE t_exception_workorder_log
63 +ADD CONSTRAINT IF NOT EXISTS fk_workorder_log_workorder_id
64 +FOREIGN KEY (workorder_id) REFERENCES t_exception_workorder(workorder_id)
65 +ON DELETE CASCADE ON UPDATE CASCADE;
66 +
67 +-- 5. 插入异常工单菜单
68 +INSERT IGNORE INTO t_sys_menu (parent_id, menu_name, menu_type, path, icon, sort, status, perms, create_by, create_time) VALUES
69 +(0, '异常工单', 1, '/main/exception-workorder', '⚠️', 7, 1, 'exception:workorder:view', 'admin', NOW());
70 +
71 +-- 获取异常工单菜单ID
72 +SET @exception_menu_id = LAST_INSERT_ID();
73 +
74 +-- 6. 插入异常工单子菜单
75 +INSERT IGNORE INTO t_sys_menu (parent_id, menu_name, menu_type, path, icon, sort, status, perms, create_by, create_time) VALUES
76 +(@exception_menu_id, '工单查询', 2, '', '', 1, 1, 'exception:workorder:list', 'admin', NOW()),
77 +(@exception_menu_id, '工单详情', 2, '', '', 2, 1, 'exception:workorder:detail', 'admin', NOW()),
78 +(@exception_menu_id, '工单新增', 2, '', '', 3, 1, 'exception:workorder:add', 'admin', NOW()),
79 +(@exception_menu_id, '工单编辑', 2, '', '', 4, 1, 'exception:workorder:edit', 'admin', NOW()),
80 +(@exception_menu_id, '工单删除', 2, '', '', 5, 1, 'exception:workorder:delete', 'admin', NOW()),
81 +(@exception_menu_id, '状态更新', 2, '', '', 6, 1, 'exception:workorder:status', 'admin', NOW()),
82 +(@exception_menu_id, '批量处理', 2, '', '', 7, 1, 'exception:workorder:batch', 'admin', NOW()),
83 +(@exception_menu_id, '统计查看', 2, '', '', 8, 1, 'exception:workorder:stats', 'admin', NOW());
84 +
85 +
86 +-- 12. 为管理员角色分配异常工单权限
87 +INSERT IGNORE INTO t_sys_role_menu (role_id, menu_id, create_by, create_time)
88 +SELECT r.role_id, @exception_menu_id, 'admin', NOW()
89 +FROM t_sys_role r
90 +WHERE r.role_code = 'ADMIN';
91 +
92 +-- 13. 为管理员角色分配异常工单子菜单权限
93 +INSERT IGNORE INTO t_sys_role_menu (role_id, menu_id, create_by, create_time)
94 +SELECT r.role_id, m.menu_id, 'admin', NOW()
95 +FROM t_sys_role r, t_sys_menu m
96 +WHERE r.role_code = 'ADMIN'
97 +AND m.parent_id = @exception_menu_id;
98 +
99 +
100 +-- 完成初始化
101 +SELECT '异常工单模块初始化完成' AS message;
1 +import { request } from '../utils/request'
2 +
3 +// 异常工单相关类型定义
4 +export interface ExceptionWorkorderInfo {
5 + workorderId: number
6 + workorderNo: string
7 + orderNo: string
8 + dealerCode: string
9 + dealerName: string
10 + exceptionType: number
11 + exceptionTypeName: string
12 + severityLevel: number
13 + severityLevelName: string
14 + workorderStatus: number
15 + workorderStatusName: string
16 + createTime: string
17 + expectCompleteTime?: string
18 + handlerUser?: string
19 + exceptionDesc?: string
20 + handleSuggest?: string
21 + dataSource?: string
22 + createBy?: string
23 + updateBy?: string
24 + updateTime?: string
25 + workorderLogs?: ExceptionWorkorderLogInfo[]
26 +}
27 +
28 +export interface ExceptionWorkorderLogInfo {
29 + logId: number
30 + workorderId: number
31 + workorderNo: string
32 + handleUser: string
33 + handleTime: string
34 + beforeStatus: number
35 + beforeStatusName: string
36 + afterStatus: number
37 + afterStatusName: string
38 + handleOpinion?: string
39 + attachUrl?: string
40 + createBy?: string
41 + createTime: string
42 +}
43 +
44 +export interface ExceptionWorkorderQueryReq {
45 + workorderNo?: string
46 + orderNo?: string
47 + dealerCode?: string
48 + dealerName?: string
49 + workorderStatus?: number
50 + exceptionType?: number
51 + severityLevel?: number
52 + handlerUser?: string
53 + startTime?: string
54 + endTime?: string
55 + pageNum?: number
56 + pageSize?: number
57 +}
58 +
59 +export interface ExceptionWorkorderAddReq {
60 + workorderNo: string
61 + orderNo?: string
62 + dealerCode?: string
63 + dealerName?: string
64 + exceptionType: number
65 + severityLevel: number
66 + workorderStatus?: number
67 + expectCompleteTime?: string
68 + handlerUser?: string
69 + exceptionDesc?: string
70 + handleSuggest?: string
71 + dataSource?: string
72 +}
73 +
74 +export interface ExceptionWorkorderUpdateReq {
75 + workorderId: number
76 + workorderNo?: string
77 + orderNo?: string
78 + dealerCode?: string
79 + dealerName?: string
80 + exceptionType?: number
81 + severityLevel?: number
82 + workorderStatus?: number
83 + expectCompleteTime?: string
84 + handlerUser?: string
85 + exceptionDesc?: string
86 + handleSuggest?: string
87 + dataSource?: string
88 +}
89 +
90 +export interface ExceptionWorkorderStatusUpdateReq {
91 + workorderId: number
92 + workorderStatus: number
93 + handlerUser?: string
94 + handleOpinion?: string
95 + attachUrl?: string
96 +}
97 +
98 +// 异常工单API接口
99 +export const exceptionWorkorderApi = {
100 + // 分页查询异常工单列表
101 + getExceptionWorkorderList: (params: ExceptionWorkorderQueryReq) => {
102 + return request.get('/api/exception-workorder/list', params)
103 + },
104 +
105 + // 获取异常工单详情
106 + getExceptionWorkorderDetail: (workorderId: number) => {
107 + return request.get(`/api/exception-workorder/${workorderId}`)
108 + },
109 +
110 + // 新增异常工单
111 + addExceptionWorkorder: (data: ExceptionWorkorderAddReq) => {
112 + return request.post('/api/exception-workorder', data)
113 + },
114 +
115 + // 更新异常工单
116 + updateExceptionWorkorder: (data: ExceptionWorkorderUpdateReq) => {
117 + return request.put('/api/exception-workorder', data)
118 + },
119 +
120 + // 更新工单状态
121 + updateWorkorderStatus: (data: ExceptionWorkorderStatusUpdateReq) => {
122 + return request.post('/api/exception-workorder/status', data)
123 + },
124 +
125 + // 删除异常工单
126 + deleteExceptionWorkorder: (workorderId: number) => {
127 + return request.delete(`/api/exception-workorder/${workorderId}`)
128 + },
129 +
130 + // 批量删除异常工单
131 + batchDeleteExceptionWorkorders: (workorderIds: number[]) => {
132 + return request.delete('/api/exception-workorder/batch', workorderIds)
133 + },
134 +
135 + // 批量更新工单状态
136 + batchUpdateWorkorderStatus: (workorderIds: number[], workorderStatus: number, handlerUser: string) => {
137 + return request.post('/api/exception-workorder/batch-status', null, {
138 + params: {
139 + workorderIds: workorderIds.join(','),
140 + workorderStatus,
141 + handlerUser
142 + }
143 + })
144 + },
145 +
146 + // 获取异常工单统计信息
147 + getExceptionWorkorderStats: () => {
148 + return request.get('/api/exception-workorder/stats')
149 + }
150 +}
151 +
152 +// 异常类型枚举
153 +export const EXCEPTION_TYPE = {
154 + 1: '逻辑验证异常',
155 + 2: '源头验证异常',
156 + 3: '交叉验证异常'
157 +}
158 +
159 +// 严重程度枚举
160 +export const SEVERITY_LEVEL = {
161 + 1: '高',
162 + 2: '中',
163 + 3: '低'
164 +}
165 +
166 +// 工单状态枚举
167 +export const WORKORDER_STATUS = {
168 + 1: '待处理',
169 + 2: '处理中',
170 + 3: '已解决',
171 + 4: '已关闭'
172 +}
173 +
174 +// 获取异常类型名称
175 +export const getExceptionTypeName = (type: number): string => {
176 + return EXCEPTION_TYPE[type as keyof typeof EXCEPTION_TYPE] || '未知类型'
177 +}
178 +
179 +// 获取严重程度名称
180 +export const getSeverityLevelName = (level: number): string => {
181 + return SEVERITY_LEVEL[level as keyof typeof SEVERITY_LEVEL] || '未知'
182 +}
183 +
184 +// 获取工单状态名称
185 +export const getWorkorderStatusName = (status: number): string => {
186 + return WORKORDER_STATUS[status as keyof typeof WORKORDER_STATUS] || '未知状态'
187 +}
188 +
189 +// 获取严重程度颜色
190 +export const getSeverityLevelColor = (level: number): string => {
191 + const colors = {
192 + 1: '#f56c6c', // 高 - 红色
193 + 2: '#e6a23c', // 中 - 橙色
194 + 3: '#409eff' // 低 - 蓝色
195 + }
196 + return colors[level as keyof typeof colors] || '#909399'
197 +}
198 +
199 +// 获取工单状态颜色
200 +export const getWorkorderStatusColor = (status: number): string => {
201 + const colors = {
202 + 1: '#909399', // 待处理 - 灰色
203 + 2: '#e6a23c', // 处理中 - 橙色
204 + 3: '#67c23a', // 已解决 - 绿色
205 + 4: '#f56c6c' // 已关闭 - 红色
206 + }
207 + return colors[status as keyof typeof colors] || '#909399'
208 +}
1 +<template>
2 + <div class="exception-workorder-container">
3 + <!-- 搜索区域 -->
4 + <div class="search-section">
5 + <div class="search-form">
6 + <div class="form-row">
7 + <div class="form-item">
8 + <label>工单编号:</label>
9 + <el-input
10 + v-model="searchParams.workorderNo"
11 + placeholder="请输入工单编号"
12 + clearable
13 + class="search-input"
14 + />
15 + </div>
16 + <div class="form-item">
17 + <label>关联订单号:</label>
18 + <el-input
19 + v-model="searchParams.orderNo"
20 + placeholder="请输入关联订单号"
21 + clearable
22 + class="search-input"
23 + />
24 + </div>
25 + <div class="form-item">
26 + <label>经销商编码:</label>
27 + <el-input
28 + v-model="searchParams.dealerCode"
29 + placeholder="请输入经销商编码"
30 + clearable
31 + class="search-input"
32 + />
33 + </div>
34 + <div class="form-item">
35 + <label>经销商名称:</label>
36 + <el-input
37 + v-model="searchParams.dealerName"
38 + placeholder="请输入经销商名称"
39 + clearable
40 + class="search-input"
41 + />
42 + </div>
43 + </div>
44 + <div class="form-row">
45 + <div class="form-item">
46 + <label>工单状态:</label>
47 + <el-select
48 + v-model="searchParams.workorderStatus"
49 + placeholder="请选择工单状态"
50 + clearable
51 + class="search-select"
52 + >
53 + <el-option
54 + v-for="(name, value) in WORKORDER_STATUS"
55 + :key="value"
56 + :label="name"
57 + :value="Number(value)"
58 + />
59 + </el-select>
60 + </div>
61 + <div class="form-item">
62 + <label>异常类型:</label>
63 + <el-select
64 + v-model="searchParams.exceptionType"
65 + placeholder="请选择异常类型"
66 + clearable
67 + class="search-select"
68 + >
69 + <el-option
70 + v-for="(name, value) in EXCEPTION_TYPE"
71 + :key="value"
72 + :label="name"
73 + :value="Number(value)"
74 + />
75 + </el-select>
76 + </div>
77 + <div class="form-item">
78 + <label>严重程度:</label>
79 + <el-select
80 + v-model="searchParams.severityLevel"
81 + placeholder="请选择严重程度"
82 + clearable
83 + class="search-select"
84 + >
85 + <el-option
86 + v-for="(name, value) in SEVERITY_LEVEL"
87 + :key="value"
88 + :label="name"
89 + :value="Number(value)"
90 + />
91 + </el-select>
92 + </div>
93 + <div class="form-item">
94 + <label>处理人:</label>
95 + <el-input
96 + v-model="searchParams.handlerUser"
97 + placeholder="请输入处理人"
98 + clearable
99 + class="search-input"
100 + />
101 + </div>
102 + </div>
103 + <div class="form-row">
104 + <div class="form-item">
105 + <label>开始日期:</label>
106 + <el-date-picker
107 + v-model="searchParams.startTime"
108 + type="datetime"
109 + placeholder="选择开始日期"
110 + format="YYYY-MM-DD HH:mm:ss"
111 + value-format="YYYY-MM-DD HH:mm:ss"
112 + class="search-date"
113 + />
114 + </div>
115 + <div class="form-item">
116 + <label>结束日期:</label>
117 + <el-date-picker
118 + v-model="searchParams.endTime"
119 + type="datetime"
120 + placeholder="选择结束日期"
121 + format="YYYY-MM-DD HH:mm:ss"
122 + value-format="YYYY-MM-DD HH:mm:ss"
123 + class="search-date"
124 + />
125 + </div>
126 + <div class="form-item">
127 + <el-button type="primary" @click="handleSearch" class="search-btn">
128 + <el-icon><Search /></el-icon>
129 + 搜索
130 + </el-button>
131 + <el-button @click="handleReset" class="reset-btn">
132 + <el-icon><Refresh /></el-icon>
133 + 重置
134 + </el-button>
135 + </div>
136 + </div>
137 + </div>
138 + </div>
139 +
140 + <!-- 操作按钮区域 -->
141 + <div class="action-section">
142 + <div class="action-buttons">
143 + <el-button type="warning" @click="handleAdd" class="add-btn">
144 + <el-icon><Plus /></el-icon>
145 + 新增数据
146 + </el-button>
147 + <el-button type="success" @click="handleBatchProcess" class="batch-btn">
148 + <el-icon><Operation /></el-icon>
149 + 批量处理
150 + </el-button>
151 + <el-button @click="handleExport" class="export-btn">
152 + <el-icon><Download /></el-icon>
153 + 导出
154 + </el-button>
155 + </div>
156 + </div>
157 +
158 + <!-- 数据表格 -->
159 + <div class="table-section">
160 + <el-table
161 + :data="workorderList"
162 + v-loading="loading"
163 + @selection-change="handleSelectionChange"
164 + class="data-table"
165 + stripe
166 + border
167 + >
168 + <el-table-column type="selection" width="55" />
169 + <el-table-column prop="workorderId" label="工单ID" width="120" />
170 + <el-table-column prop="workorderNo" label="工单编号" width="180" />
171 + <el-table-column prop="orderNo" label="关联订单号" width="180" />
172 + <el-table-column prop="dealerName" label="经销商名称" width="150" />
173 + <el-table-column prop="workorderStatusName" label="工单状态" width="100">
174 + <template #default="{ row }">
175 + <el-tag :color="getWorkorderStatusColor(row.workorderStatus)">
176 + {{ row.workorderStatusName }}
177 + </el-tag>
178 + </template>
179 + </el-table-column>
180 + <el-table-column prop="exceptionTypeName" label="异常类型" width="120" />
181 + <el-table-column prop="severityLevelName" label="严重程度" width="100">
182 + <template #default="{ row }">
183 + <el-tag :color="getSeverityLevelColor(row.severityLevel)">
184 + {{ row.severityLevelName }}
185 + </el-tag>
186 + </template>
187 + </el-table-column>
188 + <el-table-column prop="createTime" label="日期" width="180" sortable>
189 + <template #default="{ row }">
190 + {{ formatDateTime(row.createTime) }}
191 + </template>
192 + </el-table-column>
193 + <el-table-column label="操作" width="200" fixed="right">
194 + <template #default="{ row }">
195 + <el-button
196 + type="primary"
197 + size="small"
198 + @click="handleEditStatus(row)"
199 + class="action-btn"
200 + >
201 + 编辑状态
202 + </el-button>
203 + <el-button
204 + type="info"
205 + size="small"
206 + @click="handleViewLogs(row)"
207 + class="action-btn"
208 + >
209 + 处理日志
210 + </el-button>
211 + <el-button
212 + type="success"
213 + size="small"
214 + @click="handleViewDetail(row)"
215 + class="action-btn"
216 + >
217 + 详情
218 + </el-button>
219 + </template>
220 + </el-table-column>
221 + </el-table>
222 + </div>
223 +
224 + <!-- 分页 -->
225 + <div class="pagination-section">
226 + <el-pagination
227 + v-model:current-page="pagination.pageNum"
228 + v-model:page-size="pagination.pageSize"
229 + :page-sizes="[10, 20, 50, 100]"
230 + :total="pagination.total"
231 + layout="total, sizes, prev, pager, next, jumper"
232 + @size-change="handleSizeChange"
233 + @current-change="handleCurrentChange"
234 + class="pagination"
235 + />
236 + </div>
237 +
238 + <!-- 新增/编辑对话框 -->
239 + <el-dialog
240 + v-model="addDialogVisible"
241 + :title="isEdit ? '编辑异常工单' : '新增异常工单'"
242 + width="600px"
243 + class="add-dialog"
244 + >
245 + <el-form
246 + ref="addFormRef"
247 + :model="addFormData"
248 + :rules="addFormRules"
249 + label-width="120px"
250 + class="add-form"
251 + >
252 + <el-form-item label="工单编号" prop="workorderNo">
253 + <el-input v-model="addFormData.workorderNo" placeholder="请输入工单编号" />
254 + </el-form-item>
255 + <el-form-item label="关联订单号" prop="orderNo">
256 + <el-input v-model="addFormData.orderNo" placeholder="请输入关联订单号" />
257 + </el-form-item>
258 + <el-form-item label="经销商编码" prop="dealerCode">
259 + <el-input v-model="addFormData.dealerCode" placeholder="请输入经销商编码" />
260 + </el-form-item>
261 + <el-form-item label="经销商名称" prop="dealerName">
262 + <el-input v-model="addFormData.dealerName" placeholder="请输入经销商名称" />
263 + </el-form-item>
264 + <el-form-item label="异常类型" prop="exceptionType">
265 + <el-select v-model="addFormData.exceptionType" placeholder="请选择异常类型">
266 + <el-option
267 + v-for="(name, value) in EXCEPTION_TYPE"
268 + :key="value"
269 + :label="name"
270 + :value="Number(value)"
271 + />
272 + </el-select>
273 + </el-form-item>
274 + <el-form-item label="严重程度" prop="severityLevel">
275 + <el-select v-model="addFormData.severityLevel" placeholder="请选择严重程度">
276 + <el-option
277 + v-for="(name, value) in SEVERITY_LEVEL"
278 + :key="value"
279 + :label="name"
280 + :value="Number(value)"
281 + />
282 + </el-select>
283 + </el-form-item>
284 + <el-form-item label="工单状态" prop="workorderStatus">
285 + <el-select v-model="addFormData.workorderStatus" placeholder="请选择工单状态">
286 + <el-option
287 + v-for="(name, value) in WORKORDER_STATUS"
288 + :key="value"
289 + :label="name"
290 + :value="Number(value)"
291 + />
292 + </el-select>
293 + </el-form-item>
294 + <el-form-item label="处理人" prop="handlerUser">
295 + <el-input v-model="addFormData.handlerUser" placeholder="请输入处理人" />
296 + </el-form-item>
297 + <el-form-item label="预计完成时间" prop="expectCompleteTime">
298 + <el-date-picker
299 + v-model="addFormData.expectCompleteTime"
300 + type="datetime"
301 + placeholder="选择预计完成时间"
302 + format="YYYY-MM-DD HH:mm:ss"
303 + value-format="YYYY-MM-DD HH:mm:ss"
304 + style="width: 100%"
305 + />
306 + </el-form-item>
307 + <el-form-item label="异常描述" prop="exceptionDesc">
308 + <el-input
309 + v-model="addFormData.exceptionDesc"
310 + type="textarea"
311 + :rows="3"
312 + placeholder="请输入异常描述"
313 + />
314 + </el-form-item>
315 + <el-form-item label="处理建议" prop="handleSuggest">
316 + <el-input
317 + v-model="addFormData.handleSuggest"
318 + type="textarea"
319 + :rows="3"
320 + placeholder="请输入处理建议"
321 + />
322 + </el-form-item>
323 + <el-form-item label="数据来源" prop="dataSource">
324 + <el-input v-model="addFormData.dataSource" placeholder="请输入数据来源" />
325 + </el-form-item>
326 + </el-form>
327 + <template #footer>
328 + <div class="dialog-footer">
329 + <el-button @click="closeAddDialog">取消</el-button>
330 + <el-button type="primary" @click="handleSubmitAdd">确定</el-button>
331 + </div>
332 + </template>
333 + </el-dialog>
334 +
335 + <!-- 状态更新对话框 -->
336 + <el-dialog
337 + v-model="statusDialogVisible"
338 + title="编辑状态"
339 + width="500px"
340 + class="status-dialog"
341 + >
342 + <el-form
343 + ref="statusFormRef"
344 + :model="statusFormData"
345 + :rules="statusFormRules"
346 + label-width="100px"
347 + class="status-form"
348 + >
349 + <el-form-item label="工单状态" prop="workorderStatus">
350 + <el-select v-model="statusFormData.workorderStatus" placeholder="请选择工单状态">
351 + <el-option
352 + v-for="(name, value) in WORKORDER_STATUS"
353 + :key="value"
354 + :label="name"
355 + :value="Number(value)"
356 + />
357 + </el-select>
358 + </el-form-item>
359 + <el-form-item label="处理人" prop="handlerUser">
360 + <el-input v-model="statusFormData.handlerUser" placeholder="请输入处理人" />
361 + </el-form-item>
362 + <el-form-item label="处理意见" prop="handleOpinion">
363 + <el-input
364 + v-model="statusFormData.handleOpinion"
365 + type="textarea"
366 + :rows="3"
367 + placeholder="请输入处理意见"
368 + />
369 + </el-form-item>
370 + <el-form-item label="附件URL" prop="attachUrl">
371 + <el-input v-model="statusFormData.attachUrl" placeholder="请输入附件URL" />
372 + </el-form-item>
373 + </el-form>
374 + <template #footer>
375 + <div class="dialog-footer">
376 + <el-button @click="closeStatusDialog">取消</el-button>
377 + <el-button type="primary" @click="handleSubmitStatus">确定</el-button>
378 + </div>
379 + </template>
380 + </el-dialog>
381 +
382 + <!-- 详情对话框 -->
383 + <el-dialog
384 + v-model="detailDialogVisible"
385 + title="异常工单详情"
386 + width="800px"
387 + class="detail-dialog"
388 + >
389 + <div v-if="workorderDetail" class="detail-content">
390 + <div class="detail-section">
391 + <h4>工单基本信息</h4>
392 + <div class="detail-grid">
393 + <div class="detail-item">
394 + <label>工单编号:</label>
395 + <span>{{ workorderDetail.workorderNo }}</span>
396 + </div>
397 + <div class="detail-item">
398 + <label>关联订单号:</label>
399 + <span>{{ workorderDetail.orderNo }}</span>
400 + </div>
401 + <div class="detail-item">
402 + <label>经销商编码:</label>
403 + <span>{{ workorderDetail.dealerCode }}</span>
404 + </div>
405 + <div class="detail-item">
406 + <label>经销商名称:</label>
407 + <span>{{ workorderDetail.dealerName }}</span>
408 + </div>
409 + <div class="detail-item">
410 + <label>异常类型:</label>
411 + <span>{{ workorderDetail.exceptionTypeName }}</span>
412 + </div>
413 + <div class="detail-item">
414 + <label>严重程度:</label>
415 + <span>{{ workorderDetail.severityLevelName }}</span>
416 + </div>
417 + <div class="detail-item">
418 + <label>工单状态:</label>
419 + <span>{{ workorderDetail.workorderStatusName }}</span>
420 + </div>
421 + <div class="detail-item">
422 + <label>处理人:</label>
423 + <span>{{ workorderDetail.handlerUser || '未分配' }}</span>
424 + </div>
425 + <div class="detail-item">
426 + <label>创建时间:</label>
427 + <span>{{ formatDateTime(workorderDetail.createTime) }}</span>
428 + </div>
429 + <div class="detail-item">
430 + <label>预计完成时间:</label>
431 + <span>{{ workorderDetail.expectCompleteTime ? formatDateTime(workorderDetail.expectCompleteTime) : '未设置' }}</span>
432 + </div>
433 + </div>
434 + </div>
435 +
436 + <div class="detail-section" v-if="workorderDetail.exceptionDesc">
437 + <h4>异常描述</h4>
438 + <p class="detail-text">{{ workorderDetail.exceptionDesc }}</p>
439 + </div>
440 +
441 + <div class="detail-section" v-if="workorderDetail.handleSuggest">
442 + <h4>处理建议</h4>
443 + <p class="detail-text">{{ workorderDetail.handleSuggest }}</p>
444 + </div>
445 +
446 + <div class="detail-section" v-if="workorderDetail.workorderLogs && workorderDetail.workorderLogs.length > 0">
447 + <h4>处理日志</h4>
448 + <el-table :data="workorderDetail.workorderLogs" class="log-table">
449 + <el-table-column prop="handleUser" label="处理人" width="120" />
450 + <el-table-column prop="handleTime" label="处理时间" width="180">
451 + <template #default="{ row }">
452 + {{ formatDateTime(row.handleTime) }}
453 + </template>
454 + </el-table-column>
455 + <el-table-column prop="beforeStatusName" label="处理前状态" width="120" />
456 + <el-table-column prop="afterStatusName" label="处理后状态" width="120" />
457 + <el-table-column prop="handleOpinion" label="处理意见" />
458 + </el-table>
459 + </div>
460 + </div>
461 + </el-dialog>
462 +
463 + <!-- 处理日志对话框 -->
464 + <el-dialog
465 + v-model="logsDialogVisible"
466 + title="处理日志"
467 + width="900px"
468 + class="logs-dialog"
469 + >
470 + <el-table :data="workorderLogs" class="logs-table">
471 + <el-table-column prop="handleUser" label="处理人" width="120" />
472 + <el-table-column prop="handleTime" label="处理时间" width="180">
473 + <template #default="{ row }">
474 + {{ formatDateTime(row.handleTime) }}
475 + </template>
476 + </el-table-column>
477 + <el-table-column prop="beforeStatusName" label="处理前状态" width="120" />
478 + <el-table-column prop="afterStatusName" label="处理后状态" width="120" />
479 + <el-table-column prop="handleOpinion" label="处理意见" />
480 + <el-table-column prop="attachUrl" label="附件" width="100">
481 + <template #default="{ row }">
482 + <el-button v-if="row.attachUrl" type="primary" size="small" @click="handleDownload(row.attachUrl)">
483 + 下载
484 + </el-button>
485 + </template>
486 + </el-table-column>
487 + </el-table>
488 + </el-dialog>
489 + </div>
490 +</template>
491 +
492 +<script setup lang="ts">
493 +import { ref, reactive, computed, onMounted } from 'vue'
494 +import { ElMessage, ElMessageBox } from 'element-plus'
495 +import { Search, Refresh, Plus, Operation, Download } from '@element-plus/icons-vue'
496 +import {
497 + exceptionWorkorderApi,
498 + type ExceptionWorkorderInfo,
499 + type ExceptionWorkorderQueryReq,
500 + type ExceptionWorkorderAddReq,
501 + type ExceptionWorkorderStatusUpdateReq,
502 + EXCEPTION_TYPE,
503 + SEVERITY_LEVEL,
504 + WORKORDER_STATUS,
505 + getSeverityLevelColor,
506 + getWorkorderStatusColor
507 +} from '../../api/exceptionWorkorder'
508 +import dayjs from 'dayjs'
509 +
510 +// 响应式数据
511 +const loading = ref(false)
512 +const workorderList = ref<ExceptionWorkorderInfo[]>([])
513 +const selectedWorkorders = ref<ExceptionWorkorderInfo[]>([])
514 +const workorderDetail = ref<ExceptionWorkorderInfo | null>(null)
515 +const workorderLogs = ref<any[]>([])
516 +
517 +// 分页数据
518 +const pagination = reactive({
519 + pageNum: 1,
520 + pageSize: 10,
521 + total: 0
522 +})
523 +
524 +// 搜索参数
525 +const searchParams = reactive<ExceptionWorkorderQueryReq>({
526 + workorderNo: '',
527 + orderNo: '',
528 + dealerCode: '',
529 + dealerName: '',
530 + workorderStatus: undefined,
531 + exceptionType: undefined,
532 + severityLevel: undefined,
533 + handlerUser: '',
534 + startTime: '',
535 + endTime: '',
536 + pageNum: 1,
537 + pageSize: 10
538 +})
539 +
540 +// 对话框状态
541 +const addDialogVisible = ref(false)
542 +const statusDialogVisible = ref(false)
543 +const detailDialogVisible = ref(false)
544 +const logsDialogVisible = ref(false)
545 +const isEdit = ref(false)
546 +
547 +// 新增表单数据
548 +const addFormData = reactive<ExceptionWorkorderAddReq>({
549 + workorderNo: '',
550 + orderNo: '',
551 + dealerCode: '',
552 + dealerName: '',
553 + exceptionType: 1,
554 + severityLevel: 1,
555 + workorderStatus: 1,
556 + expectCompleteTime: '',
557 + handlerUser: '',
558 + exceptionDesc: '',
559 + handleSuggest: '',
560 + dataSource: ''
561 +})
562 +
563 +// 状态更新表单数据
564 +const statusFormData = reactive<ExceptionWorkorderStatusUpdateReq>({
565 + workorderId: 0,
566 + workorderStatus: 1,
567 + handlerUser: '',
568 + handleOpinion: '',
569 + attachUrl: ''
570 +})
571 +
572 +// 表单验证规则
573 +const addFormRules = {
574 + workorderNo: [
575 + { required: true, message: '请输入工单编号', trigger: 'blur' }
576 + ],
577 + exceptionType: [
578 + { required: true, message: '请选择异常类型', trigger: 'change' }
579 + ],
580 + severityLevel: [
581 + { required: true, message: '请选择严重程度', trigger: 'change' }
582 + ]
583 +}
584 +
585 +const statusFormRules = {
586 + workorderStatus: [
587 + { required: true, message: '请选择工单状态', trigger: 'change' }
588 + ]
589 +}
590 +
591 +// 获取异常工单列表
592 +const fetchWorkorders = async () => {
593 + try {
594 + loading.value = true
595 + const params = {
596 + ...searchParams,
597 + pageNum: pagination.pageNum,
598 + pageSize: pagination.pageSize
599 + }
600 + const response = await exceptionWorkorderApi.getExceptionWorkorderList(params) as any
601 + console.log('异常工单列表API响应:', response)
602 +
603 + if (response && response.records) {
604 + workorderList.value = response.records
605 + pagination.total = response.total
606 + } else {
607 + workorderList.value = []
608 + pagination.total = 0
609 + }
610 + } catch (error) {
611 + console.error('获取异常工单列表失败:', error)
612 + ElMessage.error('获取异常工单列表失败')
613 + } finally {
614 + loading.value = false
615 + }
616 +}
617 +
618 +// 搜索
619 +const handleSearch = () => {
620 + pagination.pageNum = 1
621 + fetchWorkorders()
622 +}
623 +
624 +// 重置
625 +const handleReset = () => {
626 + Object.assign(searchParams, {
627 + workorderNo: '',
628 + orderNo: '',
629 + dealerCode: '',
630 + dealerName: '',
631 + workorderStatus: undefined,
632 + exceptionType: undefined,
633 + severityLevel: undefined,
634 + handlerUser: '',
635 + startTime: '',
636 + endTime: ''
637 + })
638 + pagination.pageNum = 1
639 + fetchWorkorders()
640 +}
641 +
642 +// 新增
643 +const handleAdd = () => {
644 + isEdit.value = false
645 + Object.assign(addFormData, {
646 + workorderNo: '',
647 + orderNo: '',
648 + dealerCode: '',
649 + dealerName: '',
650 + exceptionType: 1,
651 + severityLevel: 1,
652 + workorderStatus: 1,
653 + expectCompleteTime: '',
654 + handlerUser: '',
655 + exceptionDesc: '',
656 + handleSuggest: '',
657 + dataSource: ''
658 + })
659 + addDialogVisible.value = true
660 +}
661 +
662 +// 编辑状态
663 +const handleEditStatus = (row: ExceptionWorkorderInfo) => {
664 + statusFormData.workorderId = row.workorderId
665 + statusFormData.workorderStatus = row.workorderStatus
666 + statusFormData.handlerUser = row.handlerUser || ''
667 + statusFormData.handleOpinion = ''
668 + statusFormData.attachUrl = ''
669 + statusDialogVisible.value = true
670 +}
671 +
672 +// 查看详情
673 +const handleViewDetail = async (row: ExceptionWorkorderInfo) => {
674 + try {
675 + const response = await exceptionWorkorderApi.getExceptionWorkorderDetail(row.workorderId) as any
676 + console.log('异常工单详情API响应:', response)
677 +
678 + if (response && response.workorderId) {
679 + workorderDetail.value = response
680 + detailDialogVisible.value = true
681 + } else {
682 + ElMessage.error('获取异常工单详情失败')
683 + }
684 + } catch (error) {
685 + console.error('获取异常工单详情失败:', error)
686 + ElMessage.error('获取异常工单详情失败, 请重试')
687 + }
688 +}
689 +
690 +// 查看处理日志
691 +const handleViewLogs = async (row: ExceptionWorkorderInfo) => {
692 + try {
693 + const response = await exceptionWorkorderApi.getExceptionWorkorderDetail(row.workorderId) as any
694 + console.log('处理日志API响应:', response)
695 +
696 + if (response && response.workorderLogs) {
697 + workorderLogs.value = response.workorderLogs
698 + logsDialogVisible.value = true
699 + } else {
700 + ElMessage.error('获取处理日志失败')
701 + }
702 + } catch (error) {
703 + console.error('获取处理日志失败:', error)
704 + ElMessage.error('获取处理日志失败, 请重试')
705 + }
706 +}
707 +
708 +// 批量处理
709 +const handleBatchProcess = () => {
710 + if (selectedWorkorders.value.length === 0) {
711 + ElMessage.warning('请选择要处理的工单')
712 + return
713 + }
714 +
715 + ElMessageBox.confirm(
716 + `确定要批量处理选中的 ${selectedWorkorders.value.length} 个工单吗?`,
717 + '批量处理确认',
718 + {
719 + confirmButtonText: '确定',
720 + cancelButtonText: '取消',
721 + type: 'warning'
722 + }
723 + ).then(async () => {
724 + try {
725 + const workorderIds = selectedWorkorders.value.map(item => item.workorderId)
726 + await exceptionWorkorderApi.batchUpdateWorkorderStatus(workorderIds, 2, '系统管理员')
727 + ElMessage.success('批量处理成功')
728 + fetchWorkorders()
729 + } catch (error) {
730 + console.error('批量处理失败:', error)
731 + ElMessage.error('批量处理失败')
732 + }
733 + })
734 +}
735 +
736 +// 导出
737 +const handleExport = () => {
738 + ElMessage.info('导出功能开发中...')
739 +}
740 +
741 +// 提交新增
742 +const handleSubmitAdd = async () => {
743 + try {
744 + await exceptionWorkorderApi.addExceptionWorkorder(addFormData)
745 + ElMessage.success('新增异常工单成功')
746 + closeAddDialog()
747 + fetchWorkorders()
748 + } catch (error) {
749 + console.error('新增异常工单失败:', error)
750 + ElMessage.error('新增异常工单失败')
751 + }
752 +}
753 +
754 +// 提交状态更新
755 +const handleSubmitStatus = async () => {
756 + try {
757 + await exceptionWorkorderApi.updateWorkorderStatus(statusFormData)
758 + ElMessage.success('更新工单状态成功')
759 + closeStatusDialog()
760 + fetchWorkorders()
761 + } catch (error) {
762 + console.error('更新工单状态失败:', error)
763 + ElMessage.error('更新工单状态失败')
764 + }
765 +}
766 +
767 +// 关闭对话框
768 +const closeAddDialog = () => {
769 + addDialogVisible.value = false
770 +}
771 +
772 +const closeStatusDialog = () => {
773 + statusDialogVisible.value = false
774 +}
775 +
776 +// 选择变化
777 +const handleSelectionChange = (selection: ExceptionWorkorderInfo[]) => {
778 + selectedWorkorders.value = selection
779 +}
780 +
781 +// 分页变化
782 +const handleSizeChange = (size: number) => {
783 + pagination.pageSize = size
784 + pagination.pageNum = 1
785 + fetchWorkorders()
786 +}
787 +
788 +const handleCurrentChange = (page: number) => {
789 + pagination.pageNum = page
790 + fetchWorkorders()
791 +}
792 +
793 +// 下载附件
794 +const handleDownload = (url: string) => {
795 + window.open(url, '_blank')
796 +}
797 +
798 +// 格式化日期时间
799 +const formatDateTime = (dateTime: string) => {
800 + return dayjs(dateTime).format('YYYY-MM-DD HH:mm:ss')
801 +}
802 +
803 +// 组件挂载
804 +onMounted(() => {
805 + fetchWorkorders()
806 +})
807 +</script>
808 +
809 +<style scoped>
810 +.exception-workorder-container {
811 + padding: 20px;
812 + background: #f5f5f5;
813 + min-height: 100vh;
814 +}
815 +
816 +.search-section {
817 + background: white;
818 + padding: 20px;
819 + border-radius: 8px;
820 + margin-bottom: 20px;
821 + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
822 +}
823 +
824 +.search-form {
825 + display: flex;
826 + flex-direction: column;
827 + gap: 15px;
828 +}
829 +
830 +.form-row {
831 + display: flex;
832 + gap: 20px;
833 + align-items: center;
834 + flex-wrap: wrap;
835 +}
836 +
837 +.form-item {
838 + display: flex;
839 + align-items: center;
840 + gap: 8px;
841 + min-width: 200px;
842 +}
843 +
844 +.form-item label {
845 + font-weight: 500;
846 + color: #333;
847 + white-space: nowrap;
848 + min-width: 80px;
849 +}
850 +
851 +.search-input,
852 +.search-select,
853 +.search-date {
854 + width: 200px;
855 +}
856 +
857 +.search-btn,
858 +.reset-btn {
859 + margin-left: 10px;
860 +}
861 +
862 +.action-section {
863 + background: white;
864 + padding: 15px 20px;
865 + border-radius: 8px;
866 + margin-bottom: 20px;
867 + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
868 +}
869 +
870 +.action-buttons {
871 + display: flex;
872 + gap: 10px;
873 +}
874 +
875 +.table-section {
876 + background: white;
877 + border-radius: 8px;
878 + overflow: hidden;
879 + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
880 + margin-bottom: 20px;
881 +}
882 +
883 +.data-table {
884 + width: 100%;
885 +}
886 +
887 +.action-btn {
888 + margin-right: 5px;
889 +}
890 +
891 +.pagination-section {
892 + display: flex;
893 + justify-content: center;
894 + background: white;
895 + padding: 20px;
896 + border-radius: 8px;
897 + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
898 +}
899 +
900 +.add-dialog,
901 +.status-dialog,
902 +.detail-dialog,
903 +.logs-dialog {
904 + .el-dialog__body {
905 + padding: 20px;
906 + }
907 +}
908 +
909 +.add-form,
910 +.status-form {
911 + .el-form-item {
912 + margin-bottom: 20px;
913 + }
914 +}
915 +
916 +.detail-content {
917 + .detail-section {
918 + margin-bottom: 30px;
919 + }
920 +
921 + .detail-section h4 {
922 + color: #333;
923 + margin-bottom: 15px;
924 + padding-bottom: 8px;
925 + border-bottom: 2px solid #409eff;
926 + }
927 +
928 + .detail-grid {
929 + display: grid;
930 + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
931 + gap: 15px;
932 + }
933 +
934 + .detail-item {
935 + display: flex;
936 + align-items: center;
937 + gap: 10px;
938 + }
939 +
940 + .detail-item label {
941 + font-weight: 500;
942 + color: #666;
943 + min-width: 120px;
944 + }
945 +
946 + .detail-item span {
947 + color: #333;
948 + }
949 +
950 + .detail-text {
951 + color: #333;
952 + line-height: 1.6;
953 + background: #f8f9fa;
954 + padding: 15px;
955 + border-radius: 4px;
956 + border-left: 4px solid #409eff;
957 + }
958 +
959 + .log-table,
960 + .logs-table {
961 + margin-top: 15px;
962 + }
963 +}
964 +
965 +.dialog-footer {
966 + text-align: right;
967 +}
968 +
969 +.dialog-footer .el-button {
970 + margin-left: 10px;
971 +}
972 +</style>