# 售后工单模块 - 前端联调方案 ## 涉及页面(全部新建) | 页面 | 路由建议 | 说明 | |------|---------|------| | 售后工单列表 | `/aftersales/ticket` | 工单列表、搜索、状态筛选 | | 售后工单详情/编辑 | `/aftersales/ticket/detail?id=:id` | 新建/编辑工单、查看详情 | | 退货申请列表 | `/aftersales/return` | 退货申请列表、搜索 | | 退货申请详情 | `/aftersales/return/detail?id=:id` | 退货详情、审核操作 | ## 业务流程与数据带入 ### 流程一:一般问题工单 ``` 创建工单 → 分配处理人 → 问题判定(一般问题)→ 处理 → 完结 → 关闭 ``` ### 流程二:退货问题工单 ``` 创建工单 → 分配处理人 → 问题判定(退货问题) → 创建退货申请 → 销售审核 → [MES退货流程自动执行] → 退款/换货完成 → 完结 → 关闭 ``` ### 流程三:维修问题工单 ``` 创建工单 → 分配处理人 → 问题判定(维修问题) → 创建维修记录 → 维修完成 → 完结 → 关闭 ``` ### 数据带入关系 | 步骤 | 来源 | 带入字段 | |------|------|----------| | 创建退货申请 | 工单详情 | `ticketId`、`customerId`、`saleOrderId`、工单明细行(`ticketItemId`、`itemId`、`itemCode`、`itemName`) | | 退货申请审批 | 退货申请 | `id`(通过 URL 参数或按钮传入) | **重点说明:** - 创建退货申请时,从工单详情页的明细行中筛选 `needReturn = true` 的行作为退货明细 - 退货明细中的 `ticketItemId` 必须对应工单明细行的 `id` - 物料信息(`itemId`、`itemCode`、`itemName`)从工单明细行带入,不允许修改 - 销售单价(`unitPrice`)需要从销售订单行查询获取,前端可通过已有的 ERP 销售订单接口查询 --- ## API 说明 ### 一、售后工单 | 方法 | 路径 | 权限 | 说明 | |------|------|------|------| | POST | `/aftersales/ticket/create` | `aftersales:ticket:create` | 创建工单 | | PUT | `/aftersales/ticket/update` | `aftersales:ticket:update` | 更新工单(仅草稿状态可更新) | | DELETE | `/aftersales/ticket/delete` | `aftersales:ticket:delete` | 删除工单(仅草稿/已取消状态可删除) | | GET | `/aftersales/ticket/get` | `aftersales:ticket:query` | 获取工单详情 | | GET | `/aftersales/ticket/page` | `aftersales:ticket:query` | 分页查询工单 | | POST | `/aftersales/ticket/assign` | `aftersales:ticket:update` | 分配处理人 | | POST | `/aftersales/ticket/classify` | `aftersales:ticket:update` | 问题判定 | | PUT | `/aftersales/ticket/resolve` | `aftersales:ticket:update` | 完结工单 | | PUT | `/aftersales/ticket/close` | `aftersales:ticket:update` | 关闭工单 | | PUT | `/aftersales/ticket/cancel` | `aftersales:ticket:update` | 取消工单 | #### 1.1 创建工单 `POST /aftersales/ticket/create` **请求参数:** | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | name | String | 是 | 工单主题 | | customerId | Long | 是 | 客户编号 | | contactId | Long | 否 | 联系人编号 | | saleOrderId | Long | 否 | 销售订单编号 | | saleOrderNo | String | 否 | 销售订单号(冗余,与 saleOrderId 配套传入) | | contractId | Long | 否 | 合同编号 | | issueDescription | String | 否 | 问题描述 | | remark | String | 否 | 备注 | | items | List\ | 否 | 明细行列表 | **Item 结构:** | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | saleOrderItemId | Long | 否 | 销售订单行编号 | | itemId | Long | 是 | MDM 物料编号 | | itemCode | String | 否 | 物料编码 | | itemName | String | 否 | 物料名称 | | batchId | Long | 否 | 生产批次编号 | | batchCode | String | 否 | 生产批次号 | | saleQuantity | BigDecimal | 否 | 销售数量 | | returnQuantity | BigDecimal | 否 | 退货数量 | | needRepair | Boolean | 否 | 是否需要维修 | | needReturn | Boolean | 否 | 是否需要退货 | | remark | String | 否 | 备注 | **请求示例:** ```json { "name": "产品外观缺陷投诉", "customerId": 1, "contactId": 2, "saleOrderId": 100, "saleOrderNo": "SO202401010001", "contractId": 50, "issueDescription": "收到货后发现产品表面有明显划痕", "remark": "客户要求尽快处理", "items": [ { "saleOrderItemId": 10, "itemId": 200, "itemCode": "MAT-001", "itemName": "电子元器件", "batchId": 5, "batchCode": "BATCH-2024-001", "saleQuantity": 100.000, "returnQuantity": 10.000, "needRepair": false, "needReturn": true, "remark": "外观缺陷" } ] } ``` **响应:** `{ "code": 0, "data": 1 }` — data 为新建工单 ID #### 1.2 更新工单 `PUT /aftersales/ticket/update` 请求参数与创建相同,额外需要 `id` 字段。仅草稿状态可更新。 #### 1.3 删除工单 `DELETE /aftersales/ticket/delete` | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | id | Long | 是 | 工单编号(Query 参数) | **响应:** `{ "code": 0, "data": true }` #### 1.4 获取工单详情 `GET /aftersales/ticket/get` | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | id | Long | 是 | 工单编号(Query 参数) | **响应字段:** | 字段 | 类型 | 说明 | |------|------|------| | id | Long | 工单编号 | | no | String | 工单单号(格式:AS + yyyyMMdd + 6位序号) | | name | String | 工单主题 | | customerId | Long | 客户编号 | | customerName | String | 客户名称 | | contactId | Long | 联系人编号 | | contactName | String | 联系人名称 | | saleOrderId | Long | 销售订单编号 | | saleOrderNo | String | 销售订单号 | | contractId | Long | 合同编号 | | issueType | Integer | 问题类型:1-一般问题 2-维修问题 3-退货问题 | | issueCategory | String | 问题分类 | | issueDescription | String | 问题描述 | | severityLevel | Integer | 严重程度:0-轻微 1-一般 2-严重 3-致命 | | status | Integer | 状态(见状态枚举) | | ownerUserId | Long | 负责人用户编号 | | ownerUserName | String | 负责人名称 | | handlerUserId | Long | 处理人用户编号 | | handlerUserName | String | 处理人名称 | | processInstanceId | String | BPM 工作流实例编号 | | returnId | Long | 关联退货申请编号 | | repairId | Long | 关联维修记录编号 | | resolvedTime | DateTime | 解决时间 | | closedTime | DateTime | 关闭时间 | | remark | String | 备注 | | creator | String | 创建者 | | createTime | DateTime | 创建时间 | | updater | String | 更新者 | | updateTime | DateTime | 更新时间 | | items | List\ | 明细行列表 | **响应示例:** ```json { "code": 0, "data": { "id": 1, "no": "AS20260730000001", "name": "产品外观缺陷投诉", "customerId": 1, "customerName": "XX科技有限公司", "contactId": 2, "contactName": "张三", "saleOrderId": 100, "saleOrderNo": "SO202401010001", "contractId": 50, "issueType": 3, "issueCategory": "外观缺陷", "issueDescription": "收到货后发现产品表面有明显划痕", "severityLevel": 1, "status": 30, "ownerUserId": 1, "ownerUserName": "管理员", "handlerUserId": 2, "handlerUserName": "售后专员", "returnId": 1, "items": [ { "id": 1, "ticketId": 1, "saleOrderItemId": 10, "itemId": 200, "itemCode": "MAT-001", "itemName": "电子元器件", "batchId": 5, "batchCode": "BATCH-2024-001", "saleQuantity": 100.000, "returnQuantity": 10.000, "needRepair": false, "needReturn": true } ] } } ``` #### 1.5 分页查询工单 `GET /aftersales/ticket/page` | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | pageNo | Integer | 否 | 页码,默认 1 | | pageSize | Integer | 否 | 每页大小,默认 10 | | no | String | 否 | 工单单号 | | name | String | 否 | 工单主题 | | customerId | Long | 否 | 客户编号 | | saleOrderId | Long | 否 | 销售订单编号 | | status | Integer | 否 | 工单状态 | | issueType | Integer | 否 | 问题类型 | | sceneType | Integer | 否 | 场景类型:1-我负责的 2-我参与的 3-下属负责的 4-全部 | **响应:** ```json { "code": 0, "data": { "list": [ /* AfterSaleTicketRespVO 数组 */ ], "total": 100 } } ``` **注意:** 分页列表响应中的每条记录结构与详情接口一致(含 `items` 和关联名称字段),但列表场景下 `items` 可为空。 #### 1.6 分配处理人 `POST /aftersales/ticket/assign` | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | id | Long | 是 | 工单编号(Query 参数) | | handlerUserId | Long | 是 | 处理人用户编号(Query 参数) | **业务规则:** 仅草稿、处理中状态可分配。分配后状态自动变为"处理中"。 #### 1.7 问题判定 `POST /aftersales/ticket/classify` | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | id | Long | 是 | 工单编号(Query 参数) | | issueType | Integer | 是 | 问题类型:1-一般问题 2-维修问题 3-退货问题 | | issueCategory | String | 否 | 问题分类(如"外观缺陷"、"功能故障"等) | | severityLevel | Integer | 否 | 严重程度:0-轻微 1-一般 2-严重 3-致命 | **业务规则:** 仅处理中状态可判定。判定为退货问题后,应引导用户创建退货申请。 #### 1.8 完结工单 `PUT /aftersales/ticket/resolve` | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | id | Long | 是 | 工单编号(Query 参数) | **业务规则:** 仅处理中/待退货/待退款状态可完结。 #### 1.9 关闭工单 `PUT /aftersales/ticket/close` | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | id | Long | 是 | 工单编号(Query 参数) | **业务规则:** 仅已完结状态可关闭。 #### 1.10 取消工单 `PUT /aftersales/ticket/cancel` | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | id | Long | 是 | 工单编号(Query 参数) | **业务规则:** 仅草稿、处理中状态可取消。 --- ### 二、售后退货申请 | 方法 | 路径 | 权限 | 说明 | |------|------|------|------| | POST | `/aftersales/return/create` | `aftersales:return:create` | 创建退货申请 | | PUT | `/aftersales/return/approve` | `aftersales:return:approve` | 审核通过(触发 MES 退货单) | | GET | `/aftersales/return/get` | `aftersales:return:query` | 获取退货详情 | | GET | `/aftersales/return/page` | `aftersales:return:query` | 分页查询退货申请 | #### 2.1 创建退货申请 `POST /aftersales/return/create` **请求参数:** | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | ticketId | Long | 是 | 关联工单编号 | | customerId | Long | 是 | 客户编号(从工单带入) | | saleOrderId | Long | 否 | 关联销售订单编号(从工单带入) | | returnType | Integer | 是 | 退货类型:1-退款退货 2-换货 3-仅退款 | | returnReason | String | 是 | 退货原因 | | defectCategory | String | 否 | 缺陷分类 | | remark | String | 否 | 备注 | | items | List\ | 是 | 退货明细行列表 | **Item 结构:** | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | ticketItemId | Long | 是 | 工单明细行编号(关联 after_sale_ticket_item.id) | | itemId | Long | 是 | MDM 物料编号(从工单明细行带入) | | itemCode | String | 否 | 物料编码 | | itemName | String | 否 | 物料名称 | | batchId | Long | 否 | 批次编号 | | batchCode | String | 否 | 批次号 | | returnQuantity | BigDecimal | 是 | 退货数量 | | unitPrice | BigDecimal | 是 | 销售单价(需从销售订单行查询获取) | | returnPrice | BigDecimal | 否 | 退货金额(= returnQuantity × unitPrice) | | remark | String | 否 | 备注 | **请求示例:** ```json { "ticketId": 1, "customerId": 1, "saleOrderId": 100, "returnType": 1, "returnReason": "产品外观缺陷", "defectCategory": "外观缺陷", "remark": "客户要求退货退款", "items": [ { "ticketItemId": 1, "itemId": 200, "itemCode": "MAT-001", "itemName": "电子元器件", "batchId": 5, "batchCode": "BATCH-2024-001", "returnQuantity": 10.000, "unitPrice": 100.00, "returnPrice": 1000.00 } ] } ``` **响应:** `{ "code": 0, "data": 1 }` — data 为新建退货申请 ID **业务规则:** - 创建退货申请后,关联工单状态自动变为"待退货(30)" - `returnPrice` 后端会自动计算(`returnQuantity × unitPrice` 求和),前端可不传 #### 2.2 审核通过 `PUT /aftersales/return/approve` | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | id | Long | 是 | 退货申请编号(Query 参数) | **业务规则:** - 仅草稿状态(0)可审核 - 审核通过后,后端自动调用 MES 创建销售退货单,状态变为"审核通过-已同步MES(20)" - 后续 MES 退货流程(质检→入库→退款)由后端回调自动完成,前端无需操作 #### 2.3 获取退货详情 `GET /aftersales/return/get` | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | id | Long | 是 | 退货申请编号(Query 参数) | **响应字段(新增/重要字段):** | 字段 | 类型 | 说明 | |------|------|------| | id | Long | 退货申请编号 | | no | String | 退货单号(格式:RT + yyyyMMdd + 6位序号) | | ticketId | Long | 关联工单编号 | | customerId | Long | 客户编号 | | customerName | String | 客户名称 | | saleOrderId | Long | 销售订单编号 | | returnType | Integer | 退货类型 | | returnReason | String | 退货原因 | | defectCategory | String | 缺陷分类 | | totalReturnPrice | BigDecimal | 应退总金额 | | actualRefundPrice | BigDecimal | 实退金额 | | status | Integer | 状态(见退货状态枚举) | | auditUserId | Long | 审核人 | | auditTime | DateTime | 审核时间 | | mesReturnId | Long | MES退货单编号 | | mesReturnCode | String | MES退货单号 | | qualityResult | String | 质检结论 | | qualityReportUrl | String | 质检报告附件URL | | returnInboundTime | DateTime | 退货入库完成时间 | | refundTime | DateTime | 退款完成时间 | | items | List\ | 退货明细行 | #### 2.4 分页查询退货申请 `GET /aftersales/return/page` | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | pageNo | Integer | 否 | 页码 | | pageSize | Integer | 否 | 每页大小 | | no | String | 否 | 退货单号 | | ticketId | Long | 否 | 关联工单编号 | | customerId | Long | 否 | 客户编号 | | status | Integer | 否 | 退货状态 | | sceneType | Integer | 否 | 场景类型 | --- ## 枚举值速查 ### 工单状态 (AfterSaleTicketStatusEnum) | 值 | 名称 | 说明 | |----|------|------| | 0 | 草稿 | 可编辑、可删除、可取消 | | 10 | 处理中 | 可分配、可判定、可取消 | | 30 | 待退货 | 退货流程进行中 | | 40 | 待退款/换货 | 等待退款或换货完成 | | 50 | 已完结 | 可关闭 | | 60 | 已关闭 | 终态 | | 99 | 已取消 | 终态 | ### 问题类型 (AfterSaleIssueTypeEnum) | 值 | 名称 | 后续操作 | |----|------|----------| | 0 | 未判定 | 创建工单时的初始值 | | 1 | 一般问题 | 直接处理→完结 | | 2 | 维修问题 | 创建维修记录→维修完成→完结 | | 3 | 退货问题 | 创建退货申请→审核→MES退货→完结 | ### 退货状态 (AfterSaleReturnStatusEnum) | 值 | 名称 | 说明 | |----|------|------| | 0 | 草稿 | 可审核 | | 10 | 销售审核中 | — | | 20 | 审核通过-已同步MES | 等待MES处理 | | 30 | 检验中 | MES质检进行中 | | 40 | 检验完成 | 等待入库 | | 50 | 已入库 | 等待退款 | | 60 | 退款/换货完成 | — | | 70 | 已关闭 | 终态 | | 99 | 已取消 | 终态 | ### 退货类型 | 值 | 名称 | |----|------| | 1 | 退款退货 | | 2 | 换货 | | 3 | 仅退款 | ### 严重程度 | 值 | 名称 | 建议颜色 | |----|------|----------| | 0 | 轻微 | 绿色 | | 1 | 一般 | 蓝色 | | 2 | 严重 | 橙色 | | 3 | 致命 | 红色 | ### 场景类型 (sceneType) | 值 | 名称 | 说明 | |----|------|------| | 1 | 我负责的 | 当前用户为负责人 | | 2 | 我参与的 | 当前用户为处理人或被授权 | | 3 | 下属负责的 | 下属为负责人 | | 4 | 全部 | 管理员查看所有 | --- ## 字段展示规则 ### 工单列表页 | 字段 | 展示位置 | 说明 | |------|----------|------| | no | 列表 | 单号,可点击进入详情 | | name | 列表 | 工单主题 | | customerName | 列表 | 客户名称 | | saleOrderNo | 列表 | 销售订单号 | | issueType | 列表 | 问题类型,用标签区分颜色 | | severityLevel | 列表 | 严重程度,用标签区分颜色 | | status | 列表 | 状态,用标签区分颜色 | | ownerUserName | 列表 | 负责人 | | handlerUserName | 列表 | 处理人 | | createTime | 列表 | 创建时间 | | — | 搜索区 | 单号、主题、客户、订单、状态、问题类型 | | — | 搜索区 | 场景类型 Tab:我负责的/我参与的/下属负责的/全部 | ### 工单详情页 | 字段 | 展示区域 | 说明 | |------|----------|------| | no | 头部 | 单号 | | name | 头部 | 工单主题 | | status | 头部 | 状态标签 + 状态流转按钮 | | customerId / customerName | 基本信息 | 客户选择器(创建时可选,创建后只读) | | contactId / contactName | 基本信息 | 联系人选择器 | | saleOrderId / saleOrderNo | 基本信息 | 销售订单选择器 | | contractId | 基本信息 | 合同选择器 | | issueType | 基本信息 | 问题类型(判定后显示) | | issueCategory | 基本信息 | 问题分类(判定后显示) | | issueDescription | 基本信息 | 问题描述 | | severityLevel | 基本信息 | 严重程度(判定后显示) | | ownerUserId / ownerUserName | 基本信息 | 负责人(创建时自动设为当前用户) | | handlerUserId / handlerUserName | 基本信息 | 处理人(分配后显示) | | returnId | 基本信息 | 关联退货申请(可点击跳转) | | repairId | 基本信息 | 关联维修记录(可点击跳转) | | resolvedTime | 基本信息 | 解决时间(完结后显示) | | closedTime | 基本信息 | 关闭时间(关闭后显示) | | remark | 基本信息 | 备注 | | items | 明细表格 | 物料、批次、销售数量、退货数量、是否维修、是否退货 | | createTime / creator | 底部信息 | 创建信息 | | updateTime / updater | 底部信息 | 更新信息 | ### 退货申请列表页 | 字段 | 展示位置 | 说明 | |------|----------|------| | no | 列表 | 退货单号 | | ticketId | 列表 | 关联工单编号(可点击跳转) | | customerName | 列表 | 客户名称 | | returnType | 列表 | 退货类型标签 | | totalReturnPrice | 列表 | 应退总金额 | | actualRefundPrice | 列表 | 实退金额 | | status | 列表 | 状态标签 | | mesReturnCode | 列表 | MES退货单号 | | qualityResult | 列表 | 质检结论 | | createTime | 列表 | 创建时间 | ### 退货申请详情页 | 字段 | 展示区域 | 说明 | |------|----------|------| | no | 头部 | 退货单号 | | status | 头部 | 状态标签 + 审核按钮(草稿状态显示) | | ticketId | 基本信息 | 关联工单(可点击跳转) | | customerName | 基本信息 | 客户名称 | | saleOrderId | 基本信息 | 销售订单 | | returnType | 基本信息 | 退货类型 | | returnReason | 基本信息 | 退货原因 | | defectCategory | 基本信息 | 缺陷分类 | | totalReturnPrice | 基本信息 | 应退总金额 | | actualRefundPrice | 基本信息 | 实退金额 | | mesReturnId / mesReturnCode | MES信息 | MES退货单 | | qualityResult | MES信息 | 质检结论 | | qualityReportUrl | MES信息 | 质检报告链接 | | returnInboundTime | MES信息 | 入库时间 | | refundTime | 退款信息 | 退款时间 | | items | 明细表格 | 物料、批次、退货数量、单价、退货金额 | | createTime / creator | 底部信息 | 创建信息 | --- ## 业务规则说明 ### 工单操作按钮显隐规则 | 操作 | 草稿(0) | 处理中(10) | 待退货(30) | 待退款(40) | 已完结(50) | 已关闭(60) | 已取消(99) | |------|---------|------------|------------|------------|------------|------------|------------| | 编辑 | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | | 删除 | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | | 分配处理人 | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | | 问题判定 | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | | 创建退货申请 | ❌ | ✅(判定为退货后) | ❌ | ❌ | ❌ | ❌ | ❌ | | 完结 | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | | 关闭 | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | | 取消 | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ### 退货申请操作按钮显隐规则 | 操作 | 草稿(0) | 审核中(10) | 已同步MES(20) | 检验中(30) | 检验完成(40) | 已入库(50) | 退款完成(60) | |------|---------|------------|---------------|------------|--------------|------------|--------------| | 审核通过 | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | | 查看详情 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ### 分页查询数据权限说明 - **sceneType=1(我负责的)**:显示当前用户为 `ownerUserId` 的工单 + 数据交接的工单 - **sceneType=2(我参与的)**:显示当前用户有读/写权限的工单 - **sceneType=3(下属负责的)**:显示当前用户的下属负责的工单 - **sceneType=4(全部)**:需要管理员权限(`aftersales:admin`) --- ## 文件上传 售后模块使用统一的文件上传机制,不自行实现上传接口。 **上传流程:** 1. 调用 `POST /system/storage-blob/upload` 上传文件 → 获得 `blobId` 2. 调用 `POST /system/storage-attachment/bind` 绑定到业务记录 **recordType 取值:** | 业务对象 | recordType | |----------|------------| | 售后工单 | `aftersales_ticket` | | 售后退货申请 | `aftersales_return` | **查询附件:** `GET /system/storage-attachment/list?recordType=aftersales_ticket&recordId=1` --- ## 注意事项 1. **明细行更新策略**:更新工单时,明细行采用"先删后增"策略,前端必须传入完整的明细行列表(包括未修改的行),否则会导致明细行丢失。 2. **单号生成**:工单单号(AS前缀)和退货单号(RT前缀)由后端自动生成,前端不需要传入。 3. **状态流转**:所有状态变更操作(分配、判定、完结、关闭、取消、审核)后端都会校验当前状态是否允许,前端应根据按钮显隐规则控制操作入口。 4. **退货审核后自动同步 MES**:审核通过退货申请后,后端自动调用 MES 创建销售退货单,后续 MES 流程(质检→入库→退款)由回调自动推进状态,前端只需轮询刷新查看最新状态。 5. **数据带入**:创建退货申请时,必须从工单详情页跳转并带入 `ticketId`、`customerId`、`saleOrderId` 以及工单明细行中标记为"需要退货"的行。 6. **权限控制**:所有接口都有 `@PreAuthorize` 权限校验,前端菜单和按钮需要对应配置权限标识。 7. **路由权限**:需在 `src/router/routes/modules/` 下新建 `aftersales.ts` 路由模块文件,并在菜单中注册"售后服务"一级菜单。 8. **API 模块**:需在 `src/api/` 下新建 `aftersales/` 目录,创建 `ticket.ts` 和 `return.ts` 两个 API 文件。