jiaxing.zhou

feat(exception-workorder): 新增异常工单数据导出功能

- 在API文档中添加异常工单数据导出接口说明
- 后端增加导出接口及对应的数据查询方法
- 前端实现异常工单导出功能并与后端对接
- 完善Excel导出服务集成与调用逻辑
- 更新相关权限控制与响应处理逻辑
- 补充前后端交互所需的类型定义和请求封装
......@@ -2423,8 +2423,418 @@ Authorization: Bearer <JWT_TOKEN>
**响应:** 返回Excel文件流,文件名格式:`发票数据_yyyyMMdd_HHmmss.xlsx`
### 4. 异常工单数据导出
**接口路径:** `POST /api/exception-workorder/export`
**请求方法:** POST
**权限要求:** `exception:workorder:export`
**请求参数:** 同异常工单查询接口参数
**响应:** 返回Excel文件流,文件名格式:`异常工单数据_yyyyMMdd_HHmmss.xlsx`
## 12. 异常工单管理 (ExceptionWorkorderController)
### 12.1 分页查询异常工单列表
**接口路径:** `GET /api/exception-workorder/list`
**功能描述:** 根据条件分页查询异常工单列表
**请求头:**
```
Authorization: Bearer <JWT_TOKEN>
```
**请求参数:**
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| workorderNo | String | 否 | 工单编号(模糊查询) |
| orderNo | String | 否 | 关联订单号(模糊查询) |
| dealerCode | String | 否 | 经销商编码 |
| dealerName | String | 否 | 经销商名称(模糊查询) |
| workorderStatus | Integer | 否 | 工单状态(1-待处理/2-处理中/3-已解决/4-已关闭) |
| exceptionType | Integer | 否 | 异常类型(1-逻辑验证异常/2-源头验证异常/3-交叉验证异常) |
| severityLevel | Integer | 否 | 严重程度(1-高/2-中/3-低) |
| handlerUser | String | 否 | 处理人(模糊查询) |
| startTime | String | 否 | 创建开始时间(yyyy-MM-dd HH:mm:ss) |
| endTime | String | 否 | 创建结束时间(yyyy-MM-dd HH:mm:ss) |
| pageNum | Integer | 是 | 页码,默认1 |
| pageSize | Integer | 是 | 每页大小,默认10 |
**权限要求:** `exception:workorder:list`
**响应示例:**
```json
{
"code": 200,
"message": "操作成功",
"data": {
"records": [
{
"workorderId": 1,
"workorderNo": "EW202501290001",
"orderNo": "ORD202501290001",
"dealerCode": "D001",
"dealerName": "北京经销商",
"exceptionType": 1,
"exceptionTypeName": "逻辑验证异常",
"severityLevel": 1,
"severityLevelName": "高",
"workorderStatus": 1,
"workorderStatusName": "待处理",
"createTime": "2025-01-29 10:00:00",
"expectCompleteTime": "2025-01-30 18:00:00",
"handlerUser": "张三",
"exceptionDesc": "订单金额与产品单价不匹配",
"handleSuggest": "请核实订单明细",
"dataSource": "系统自动生成",
"createBy": "system",
"updateBy": null,
"updateTime": null,
"workorderLogs": []
}
],
"total": 1,
"size": 10,
"current": 1,
"pages": 1
}
}
```
### 12.2 获取异常工单详情
**接口路径:** `GET /api/exception-workorder/{workorderId}`
**功能描述:** 根据工单ID获取异常工单详细信息,包含处理日志
**请求头:**
```
Authorization: Bearer <JWT_TOKEN>
```
**路径参数:**
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| workorderId | Long | 是 | 工单ID |
**权限要求:** `exception:workorder:detail`
**响应示例:**
```json
{
"code": 200,
"message": "操作成功",
"data": {
"workorderId": 1,
"workorderNo": "EW202501290001",
"orderNo": "ORD202501290001",
"dealerCode": "D001",
"dealerName": "北京经销商",
"exceptionType": 1,
"exceptionTypeName": "逻辑验证异常",
"severityLevel": 1,
"severityLevelName": "高",
"workorderStatus": 2,
"workorderStatusName": "处理中",
"createTime": "2025-01-29 10:00:00",
"expectCompleteTime": "2025-01-30 18:00:00",
"handlerUser": "张三",
"exceptionDesc": "订单金额与产品单价不匹配",
"handleSuggest": "请核实订单明细",
"dataSource": "系统自动生成",
"createBy": "system",
"updateBy": "张三",
"updateTime": "2025-01-29 11:00:00",
"workorderLogs": [
{
"logId": 1,
"workorderId": 1,
"workorderNo": "EW202501290001",
"handleUser": "张三",
"handleTime": "2025-01-29 11:00:00",
"beforeStatus": 1,
"beforeStatusName": "待处理",
"afterStatus": 2,
"afterStatusName": "处理中",
"handleOpinion": "开始处理此工单",
"attachUrl": null,
"createBy": "张三",
"createTime": "2025-01-29 11:00:00"
}
]
}
}
```
### 12.3 新增异常工单
**接口路径:** `POST /api/exception-workorder`
**功能描述:** 创建新的异常工单
**请求头:**
```
Authorization: Bearer <JWT_TOKEN>
Content-Type: application/json
```
**请求体:**
```json
{
"workorderNo": "EW202501290002",
"orderNo": "ORD202501290002",
"dealerCode": "D002",
"dealerName": "上海经销商",
"exceptionType": 2,
"severityLevel": 2,
"workorderStatus": 1,
"expectCompleteTime": "2025-01-30 18:00:00",
"handlerUser": "李四",
"exceptionDesc": "经销商信息不完整",
"handleSuggest": "请补充经销商详细信息",
"dataSource": "手动创建"
}
```
**权限要求:** `exception:workorder:add`
**响应示例:**
```json
{
"code": 200,
"message": "新增异常工单成功",
"data": null
}
```
### 12.4 更新异常工单
**接口路径:** `PUT /api/exception-workorder`
**功能描述:** 更新异常工单信息
**请求头:**
```
Authorization: Bearer <JWT_TOKEN>
Content-Type: application/json
```
**请求体:**
```json
{
"workorderId": 1,
"workorderNo": "EW202501290001",
"orderNo": "ORD202501290001",
"dealerCode": "D001",
"dealerName": "北京经销商",
"exceptionType": 1,
"severityLevel": 1,
"workorderStatus": 2,
"expectCompleteTime": "2025-01-30 18:00:00",
"handlerUser": "张三",
"exceptionDesc": "订单金额与产品单价不匹配",
"handleSuggest": "请核实订单明细并联系客户确认",
"dataSource": "系统自动生成"
}
```
**权限要求:** `exception:workorder:edit`
**响应示例:**
```json
{
"code": 200,
"message": "更新异常工单成功",
"data": null
}
```
### 12.5 更新工单状态
**接口路径:** `POST /api/exception-workorder/status`
**功能描述:** 更新异常工单状态并记录处理日志
**请求头:**
```
Authorization: Bearer <JWT_TOKEN>
Content-Type: application/json
```
**请求体:**
```json
{
"workorderId": 1,
"workorderStatus": 3,
"handlerUser": "张三",
"handleOpinion": "问题已解决,客户确认无误",
"attachUrl": "http://example.com/attachment.pdf"
}
```
**权限要求:** `exception:workorder:edit`
**响应示例:**
```json
{
"code": 200,
"message": "更新工单状态成功",
"data": null
}
```
### 12.6 删除异常工单
**接口路径:** `DELETE /api/exception-workorder/{workorderId}`
**功能描述:** 根据工单ID逻辑删除异常工单
**请求头:**
```
Authorization: Bearer <JWT_TOKEN>
```
**路径参数:**
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| workorderId | Long | 是 | 工单ID |
**权限要求:** `exception:workorder:delete`
**响应示例:**
```json
{
"code": 200,
"message": "删除异常工单成功",
"data": null
}
```
### 12.7 批量删除异常工单
**接口路径:** `DELETE /api/exception-workorder/batch`
**功能描述:** 根据工单ID列表批量逻辑删除异常工单
**请求头:**
```
Authorization: Bearer <JWT_TOKEN>
Content-Type: application/json
```
**请求体:**
```json
[1, 2, 3]
```
**权限要求:** `exception:workorder:delete`
**响应示例:**
```json
{
"code": 200,
"message": "批量删除异常工单成功",
"data": null
}
```
### 12.8 批量更新工单状态
**接口路径:** `POST /api/exception-workorder/batch-status`
**功能描述:** 批量更新异常工单状态
**请求头:**
```
Authorization: Bearer <JWT_TOKEN>
```
**请求参数:**
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| workorderIds | String | 是 | 工单ID列表,逗号分隔 |
| workorderStatus | Integer | 是 | 工单状态 |
| handlerUser | String | 是 | 处理人 |
**权限要求:** `exception:workorder:edit`
**响应示例:**
```json
{
"code": 200,
"message": "批量更新工单状态成功",
"data": null
}
```
### 12.9 获取异常工单统计信息
**接口路径:** `GET /api/exception-workorder/stats`
**功能描述:** 获取异常工单的统计信息
**请求头:**
```
Authorization: Bearer <JWT_TOKEN>
```
**权限要求:** `exception:workorder:list`
**响应示例:**
```json
{
"code": 200,
"message": "操作成功",
"data": [
{
"workorderNo": "total",
"workorderId": 10,
"exceptionType": 3,
"severityLevel": 2,
"workorderStatus": 4,
"handlerUser": 1,
"exceptionDesc": 5,
"handleSuggest": 3,
"dataSource": 2
}
]
}
```
### 12.10 导出异常工单数据
**接口路径:** `POST /api/exception-workorder/export`
**功能描述:** 根据查询条件导出异常工单数据到Excel
**请求头:**
```
Authorization: Bearer <JWT_TOKEN>
Content-Type: application/json
```
**请求体:** 同异常工单查询接口参数(可选,为空时导出所有数据)
**权限要求:** `exception:workorder:export`
**响应:** 返回Excel文件流,文件名格式:`异常工单数据_yyyyMMdd_HHmmss.xlsx`
**导出字段:**
- 工单ID
- 工单编号
- 工单类型(异常类型名称)
- 严重程度(严重程度名称)
- 工单状态(工单状态名称)
- 问题描述
- 处理人
- 创建人
- 创建时间
- 更新时间
---
**文档版本:** 1.0.0
**最后更新:** 2025-01-27
**最后更新:** 2025-01-29
**维护人员:** Apple ERP Team
......
......@@ -6,12 +6,14 @@ import com.apple.erp.dto.ExceptionWorkorderRes;
import com.apple.erp.dto.ExceptionWorkorderStatusUpdateReq;
import com.apple.erp.dto.ExceptionWorkorderUpdateReq;
import com.apple.erp.service.ExceptionWorkorderService;
import com.apple.erp.service.ExcelExportService;
import com.apple.erp.dto.response.ApiRes;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
......@@ -34,6 +36,9 @@ public class ExceptionWorkorderController {
@Autowired
private ExceptionWorkorderService exceptionWorkorderService;
@Autowired
private ExcelExportService excelExportService;
@Operation(summary = "分页查询异常工单列表", description = "根据条件分页查询异常工单列表")
@GetMapping("/list")
@PreAuthorize("hasAuthority('exception:workorder:list')")
......@@ -168,4 +173,22 @@ public class ExceptionWorkorderController {
List<ExceptionWorkorderRes> stats = exceptionWorkorderService.getExceptionWorkorderStats();
return ApiRes.success(stats);
}
@Operation(summary = "导出异常工单数据", description = "根据查询条件导出异常工单数据到Excel")
@PostMapping("/export")
@PreAuthorize("hasAuthority('exception:workorder:export')")
public ResponseEntity<byte[]> exportExceptionWorkorders(@RequestBody(required = false) ExceptionWorkorderQueryReq queryReq) {
try {
// 如果请求体为空,创建默认查询条件
if (queryReq == null) {
queryReq = new ExceptionWorkorderQueryReq();
}
// 获取异常工单数据
List<ExceptionWorkorderRes> workorders = exceptionWorkorderService.getExceptionWorkorderListForExport(queryReq);
// 导出Excel
return excelExportService.exportExceptionWorkorders(workorders);
} catch (Exception e) {
throw new RuntimeException("导出异常工单数据失败: " + e.getMessage());
}
}
}
......
......@@ -54,4 +54,12 @@ public interface ExceptionWorkorderMapper extends BaseMapper<ExceptionWorkorder>
int batchUpdateWorkorderStatus(@Param("workorderIds") List<Long> workorderIds,
@Param("workorderStatus") Integer workorderStatus,
@Param("handlerUser") String handlerUser);
/**
* 获取异常工单列表用于导出
*
* @param queryReq 查询条件
* @return 异常工单列表(不分页)
*/
List<ExceptionWorkorderRes> selectExceptionWorkorderListForExport(@Param("query") ExceptionWorkorderQueryReq queryReq);
}
......
......@@ -91,4 +91,12 @@ public interface ExceptionWorkorderService extends IService<ExceptionWorkorder>
* @return 统计信息
*/
List<ExceptionWorkorderRes> getExceptionWorkorderStats();
/**
* 获取异常工单列表用于导出
*
* @param queryReq 查询条件
* @return 异常工单列表(不分页)
*/
List<ExceptionWorkorderRes> getExceptionWorkorderListForExport(ExceptionWorkorderQueryReq queryReq);
}
......
......@@ -18,11 +18,9 @@ import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;
/**
* 异常工单Service实现类
......@@ -200,4 +198,10 @@ public class ExceptionWorkorderServiceImpl extends ServiceImpl<ExceptionWorkorde
public List<ExceptionWorkorderRes> getExceptionWorkorderStats() {
return exceptionWorkorderMapper.selectExceptionWorkorderStats();
}
@Override
public List<ExceptionWorkorderRes> getExceptionWorkorderListForExport(ExceptionWorkorderQueryReq queryReq) {
// 不分页查询所有数据用于导出
return exceptionWorkorderMapper.selectExceptionWorkorderListForExport(queryReq);
}
}
......
......@@ -150,4 +150,76 @@
AND del_flag = '0'
</update>
<!-- 获取异常工单列表用于导出 -->
<select id="selectExceptionWorkorderListForExport" resultType="com.apple.erp.dto.ExceptionWorkorderRes">
SELECT
ew.workorder_id,
ew.workorder_no,
ew.order_no,
ew.dealer_code,
ew.dealer_name,
ew.exception_type,
CASE ew.exception_type
WHEN 1 THEN '逻辑验证异常'
WHEN 2 THEN '源头验证异常'
WHEN 3 THEN '交叉验证异常'
ELSE '未知类型'
END AS exception_type_name,
ew.severity_level,
CASE ew.severity_level
WHEN 1 THEN '高'
WHEN 2 THEN '中'
WHEN 3 THEN '低'
ELSE '未知'
END AS severity_level_name,
ew.workorder_status,
CASE ew.workorder_status
WHEN 1 THEN '待处理'
WHEN 2 THEN '处理中'
WHEN 3 THEN '已解决'
WHEN 4 THEN '已关闭'
ELSE '未知状态'
END AS workorder_status_name,
ew.exception_desc,
ew.handler_user,
ew.create_by,
ew.create_time,
ew.update_time
FROM t_exception_workorder ew
<where>
ew.del_flag = '0'
<if test="query.workorderNo != null and query.workorderNo != ''">
AND ew.workorder_no LIKE CONCAT('%', #{query.workorderNo}, '%')
</if>
<if test="query.orderNo != null and query.orderNo != ''">
AND ew.order_no LIKE CONCAT('%', #{query.orderNo}, '%')
</if>
<if test="query.dealerCode != null and query.dealerCode != ''">
AND ew.dealer_code = #{query.dealerCode}
</if>
<if test="query.dealerName != null and query.dealerName != ''">
AND ew.dealer_name LIKE CONCAT('%', #{query.dealerName}, '%')
</if>
<if test="query.workorderStatus != null">
AND ew.workorder_status = #{query.workorderStatus}
</if>
<if test="query.exceptionType != null">
AND ew.exception_type = #{query.exceptionType}
</if>
<if test="query.severityLevel != null">
AND ew.severity_level = #{query.severityLevel}
</if>
<if test="query.handlerUser != null and query.handlerUser != ''">
AND ew.handler_user LIKE CONCAT('%', #{query.handlerUser}, '%')
</if>
<if test="query.startTime != null">
AND ew.create_time >= #{query.startTime}
</if>
<if test="query.endTime != null">
AND ew.create_time &lt;= #{query.endTime}
</if>
</where>
ORDER BY ew.create_time DESC
</select>
</mapper>
......
......@@ -146,6 +146,13 @@ export const exceptionWorkorderApi = {
// 获取异常工单统计信息
getExceptionWorkorderStats: () => {
return request.get('/api/exception-workorder/stats')
},
// 导出异常工单数据
exportExceptionWorkorders: (params: ExceptionWorkorderQueryReq) => {
return request.post('/api/exception-workorder/export', params, {
responseType: 'blob'
})
}
}
......
......@@ -734,8 +734,38 @@ const handleBatchProcess = () => {
}
// 导出
const handleExport = () => {
ElMessage.info('导出功能开发中...')
const handleExport = async () => {
try {
await ElMessageBox.confirm('确定要导出异常工单数据吗?', '确认导出', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
ElMessage.info('正在导出数据,请稍候...')
const response = await exceptionWorkorderApi.exportExceptionWorkorders(searchParams.value)
// 创建下载链接
const blob = new Blob([response as unknown as BlobPart], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
})
const url = window.URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = `异常工单数据_${new Date().toISOString().slice(0, 19).replace(/:/g, '-')}.xlsx`
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
window.URL.revokeObjectURL(url)
ElMessage.success('导出成功')
} catch (error: any) {
if (error !== 'cancel') {
console.error('导出失败:', error)
ElMessage.error('导出失败: ' + (error?.message || '未知错误'))
}
}
}
// 提交新增
......