feat(incoming-inspection): 新增不合格记录项图片获取接口并使用该接口

- 在IncomingInspectionTaskControllerService添加getUnqualifiedRecordItemDataImages方法
- 在IQmsIncomingInspectionTaskRecordItemService接口及实现中添加对应方法实现
- 在QmsIncomingInspectionTaskRecordItemMapper.xml中添加新SQL映射,查询不合格记录项相关图片ID
- 在QmsIssueTicketControllerService保存质检工单时获取并拼接不合格记录项图片ID
- 修正部分代码格式和空格问题
- 新增ExternalInventoryInspectionApplyApiTest测试类,覆盖库存检测申请接口的正常及异常场景验证
This commit is contained in:
曹鹏飞 2026-04-28 11:42:23 +08:00
parent 70baa07783
commit 8f30243721
7 changed files with 412 additions and 4 deletions

View File

@ -1085,4 +1085,8 @@ public class IncomingInspectionTaskControllerService {
public List<QmsIssueTicketInspectionRecordItemSimpleVO> getUnqualifiedRecordItems(Long taskId) {
return incomingInspectionTaskRecordItemService.getUnqualifiedRecordItems(taskId);
}
public List<String> getUnqualifiedRecordItemDataImages(Long taskId) {
return incomingInspectionTaskRecordItemService.getUnqualifiedRecordItemDataImages(taskId);
}
}

View File

@ -172,7 +172,9 @@ public class QmsIssueTicketControllerService {
+ "\n"
);
});
List<String> images = incomingInspectionTaskControllerService.getUnqualifiedRecordItemDataImages(taskId);
images.removeIf(StrUtil::isBlank);
entity.setImageIds(StrUtil.join(",", images));
issueTicketService.save(entity);
// 添加待办
@ -183,13 +185,13 @@ public class QmsIssueTicketControllerService {
.list();
List<QmsTodoItem> todoItems = users.stream().map(user -> new QmsTodoItem()
.setCode(basdeSerialNumberControllerService.generateSerialNumber(32))
.setSourceTypeId(dictionaryItemService.getIdByCode("MessageType","IncomingMaterialInspectionApproval"))
.setSourceTypeId(dictionaryItemService.getIdByCode("MessageType", "IncomingMaterialInspectionApproval"))
.setSourceId(entity.getId())
.setCreateUserId(user.getId())
.setCreateUserName(user.getUserName())
.setCreateTime(LocalDateTime.now())
).toList();
if (CollectionUtil.isNotEmpty(todoItems)){
if (CollectionUtil.isNotEmpty(todoItems)) {
todoItemService.saveBatch(todoItems);
}
}

View File

@ -0,0 +1,382 @@
package com.nflg.qms.admin;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONUtil;
import com.nflg.wms.common.pojo.ApiResult;
import com.nflg.wms.common.pojo.qo.ExternalInventoryInspectionApplyQO;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
/**
* 对外接口-库存检测申请 接口测试
* <p>
* 测试前提
* 1. qms-admin 服务已启动默认 http://localhost:8105
* 2. 测试账号已登录将有效的 token 填入 TOKEN 常量
* 3. 数据库中已存在物料编号为 VALID_MATERIAL_NO 的质检物料且该物料有对应的已发布检验标准
* 4. 该物料或其所属物料类别层级已绑定 IQE 质检人员inspection_type = 1
* </p>
*/
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class ExternalInventoryInspectionApplyApiTest {
// ===================== 配置区 =====================
/** 服务地址 */
private static final String BASE_URL = "http://localhost:8105";
/** 当前登录用户的 token需手动配置 */
private static final String TOKEN = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJsb2dpblR5cGUiOiJsb2dpbiIsImxvZ2luSWQiOjEsInJuU3RyIjoidVFwSWM2R3RJeUoxcFNSczBadzJzb1hvMUZLZXB3czkiLCJuYW1lIjoi6LaF57qn566h55CG5ZGYIiwiY29kZSI6ImFkbWluIiwicm9sZXMiOlsiU3VwZXJBZG1pbiJdLCJ0eXBlIjoxfQ.FtQ2uVwvuxsjAFbXnB006hV1pODtRhZT0z_9nfuR0So";
/** 有效的物料编号数据库中已存在且有已发布检验标准和绑定的IQE */
private static final String VALID_MATERIAL_NO = "2200052100";
/** 有效的所属工厂 */
private static final String VALID_FACTORY = "1010";
/** 有效的所属仓库 */
private static final String VALID_WAREHOUSE = "0007";
/** 有效的所属储位 */
private static final String VALID_STORAGE_LOCATION = "SL01";
/** 有效的存储时长(天) */
private static final int VALID_STORAGE_DAYS = 30;
// ===================== 配置区结束 =====================
private static final String API_PATH = "/external/incoming-inspection-task/inventory-apply";
// ==================== HTTP 工具方法 ====================
private static <T> ApiResult<T> post(String path, Object body, TypeReference<ApiResult<T>> typeRef) {
String url = BASE_URL + path;
String reqBody = body == null ? "" : JSONUtil.toJsonStr(body);
HttpResponse resp = HttpRequest.post(url)
.header("authorization", TOKEN)
.header("Content-Type", "application/json")
.body(reqBody)
.execute();
String raw = resp.body();
System.out.println("[POST] " + path);
System.out.println(" 请求: " + reqBody);
System.out.println(" 响应: " + raw);
return JSONUtil.toBean(raw, typeRef, false);
}
private static <T> void assertSuccess(ApiResult<T> result, String msg) {
Assert.isTrue(result != null && result.getCode() == 200,
() -> new RuntimeException(msg + " => " + JSONUtil.toJsonStr(result)));
}
private static void assertFailed(ApiResult<?> result, String msg) {
Assert.isTrue(result != null && result.getCode() != 200,
() -> new RuntimeException(msg + " => 预期失败但实际成功"));
}
// ==================== 正常场景测试 ====================
/**
* 库存检测申请正常申请必填字段完整
* 验证点
* 1. 接口返回成功
* 2. 物料编号有效存在对应的已发布检验标准
* 3. 能正常分配到质检人员
* 4. 任务 dataType=1, inspectionType=1
*/
@Test
@Order(1)
public void test01_inventoryApply_Success() {
ExternalInventoryInspectionApplyQO request = buildValidRequest();
ApiResult<Void> result = post(API_PATH, request, new TypeReference<>() {});
assertSuccess(result, "库存检测申请失败");
System.out.println(" ✅ 库存检测申请成功");
}
/**
* 库存检测申请最小检验数量inspectionQty=1
* 验证点边界值最小合法检验数量
*/
@Test
@Order(2)
public void test02_inventoryApply_MinInspectionQty() {
ExternalInventoryInspectionApplyQO request = buildValidRequest();
request.setInspectionQty(1);
ApiResult<Void> result = post(API_PATH, request, new TypeReference<>() {});
assertSuccess(result, "最小检验数量库存检测申请失败");
System.out.println(" ✅ 最小检验数量库存检测申请成功");
}
/**
* 库存检测申请最小存储时长storageDays=1
* 验证点边界值最小合法存储时长
*/
@Test
@Order(3)
public void test03_inventoryApply_MinStorageDays() {
ExternalInventoryInspectionApplyQO request = buildValidRequest();
request.setStorageDays(1);
ApiResult<Void> result = post(API_PATH, request, new TypeReference<>() {});
assertSuccess(result, "最小存储时长库存检测申请失败");
System.out.println(" ✅ 最小存储时长库存检测申请成功");
}
/**
* 库存检测申请大数量检验inspectionQty=99999
* 验证点大数量时能正常处理
*/
@Test
@Order(4)
public void test04_inventoryApply_LargeInspectionQty() {
ExternalInventoryInspectionApplyQO request = buildValidRequest();
request.setInspectionQty(99999);
ApiResult<Void> result = post(API_PATH, request, new TypeReference<>() {});
assertSuccess(result, "大数量检验库存检测申请失败");
System.out.println(" ✅ 大数量检验库存检测申请成功");
}
// ==================== 参数校验异常测试 ====================
/**
* 库存检测申请物料编号为空
* 验证点@NotBlank 校验接口返回参数校验错误
*/
@Test
@Order(10)
public void test10_inventoryApply_MaterialNoBlank() {
ExternalInventoryInspectionApplyQO request = buildValidRequest();
request.setMaterialNo(null);
ApiResult<Void> result = post(API_PATH, request, new TypeReference<>() {});
assertFailed(result, "物料编号为空应返回失败");
System.out.println(" ✅ 物料编号为空校验通过");
}
/**
* 库存检测申请物料编号为空字符串
* 验证点@NotBlank 校验空字符串也应返回失败
*/
@Test
@Order(11)
public void test11_inventoryApply_MaterialNoEmpty() {
ExternalInventoryInspectionApplyQO request = buildValidRequest();
request.setMaterialNo("");
ApiResult<Void> result = post(API_PATH, request, new TypeReference<>() {});
assertFailed(result, "物料编号为空字符串应返回失败");
System.out.println(" ✅ 物料编号为空字符串校验通过");
}
/**
* 库存检测申请所属工厂为空
* 验证点@NotBlank 校验接口返回参数校验错误
*/
@Test
@Order(12)
public void test12_inventoryApply_FactoryBlank() {
ExternalInventoryInspectionApplyQO request = buildValidRequest();
request.setFactory(null);
ApiResult<Void> result = post(API_PATH, request, new TypeReference<>() {});
assertFailed(result, "所属工厂为空应返回失败");
System.out.println(" ✅ 所属工厂为空校验通过");
}
/**
* 库存检测申请检验数量为空
* 验证点@NotNull 校验接口返回参数校验错误
*/
@Test
@Order(13)
public void test13_inventoryApply_InspectionQtyNull() {
ExternalInventoryInspectionApplyQO request = buildValidRequest();
request.setInspectionQty(null);
ApiResult<Void> result = post(API_PATH, request, new TypeReference<>() {});
assertFailed(result, "检验数量为空应返回失败");
System.out.println(" ✅ 检验数量为空校验通过");
}
/**
* 库存检测申请检验数量为零
* 验证点@Min(1) 校验检验数量必须大于0
*/
@Test
@Order(14)
public void test14_inventoryApply_InspectionQtyZero() {
ExternalInventoryInspectionApplyQO request = buildValidRequest();
request.setInspectionQty(0);
ApiResult<Void> result = post(API_PATH, request, new TypeReference<>() {});
assertFailed(result, "检验数量为零应返回失败");
System.out.println(" ✅ 检验数量为零校验通过");
}
/**
* 库存检测申请检验数量为负数
* 验证点@Min(1) 校验负数应返回失败
*/
@Test
@Order(15)
public void test15_inventoryApply_InspectionQtyNegative() {
ExternalInventoryInspectionApplyQO request = buildValidRequest();
request.setInspectionQty(-1);
ApiResult<Void> result = post(API_PATH, request, new TypeReference<>() {});
assertFailed(result, "检验数量为负数应返回失败");
System.out.println(" ✅ 检验数量为负数校验通过");
}
/**
* 库存检测申请所属仓库为空
* 验证点@NotBlank 校验库存检测必填仓库
*/
@Test
@Order(16)
public void test16_inventoryApply_WarehouseBlank() {
ExternalInventoryInspectionApplyQO request = buildValidRequest();
request.setWarehouse(null);
ApiResult<Void> result = post(API_PATH, request, new TypeReference<>() {});
assertFailed(result, "所属仓库为空应返回失败");
System.out.println(" ✅ 所属仓库为空校验通过");
}
/**
* 库存检测申请所属储位为空
* 验证点@NotBlank 校验库存检测必填储位
*/
@Test
@Order(17)
public void test17_inventoryApply_StorageLocationBlank() {
ExternalInventoryInspectionApplyQO request = buildValidRequest();
request.setStorageLocation(null);
ApiResult<Void> result = post(API_PATH, request, new TypeReference<>() {});
assertFailed(result, "所属储位为空应返回失败");
System.out.println(" ✅ 所属储位为空校验通过");
}
/**
* 库存检测申请存储时长为空
* 验证点@NotNull 校验库存检测必填存储时长
*/
@Test
@Order(18)
public void test18_inventoryApply_StorageDaysNull() {
ExternalInventoryInspectionApplyQO request = buildValidRequest();
request.setStorageDays(null);
ApiResult<Void> result = post(API_PATH, request, new TypeReference<>() {});
assertFailed(result, "存储时长为空应返回失败");
System.out.println(" ✅ 存储时长为空校验通过");
}
/**
* 库存检测申请存储时长为零
* 验证点@Min(1) 校验存储时长必须大于0
*/
@Test
@Order(19)
public void test19_inventoryApply_StorageDaysZero() {
ExternalInventoryInspectionApplyQO request = buildValidRequest();
request.setStorageDays(0);
ApiResult<Void> result = post(API_PATH, request, new TypeReference<>() {});
assertFailed(result, "存储时长为零应返回失败");
System.out.println(" ✅ 存储时长为零校验通过");
}
/**
* 库存检测申请存储时长为负数
* 验证点@Min(1) 校验负数应返回失败
*/
@Test
@Order(20)
public void test20_inventoryApply_StorageDaysNegative() {
ExternalInventoryInspectionApplyQO request = buildValidRequest();
request.setStorageDays(-5);
ApiResult<Void> result = post(API_PATH, request, new TypeReference<>() {});
assertFailed(result, "存储时长为负数应返回失败");
System.out.println(" ✅ 存储时长为负数校验通过");
}
// ==================== 业务校验异常测试 ====================
/**
* 库存检测申请物料编号不存在
* 验证点接口返回业务错误物料编号不存在于质检物料表中
*/
@Test
@Order(30)
public void test30_inventoryApply_MaterialNotExist() {
ExternalInventoryInspectionApplyQO request = buildValidRequest();
request.setMaterialNo("NOT_EXIST_MATERIAL_999");
ApiResult<Void> result = post(API_PATH, request, new TypeReference<>() {});
assertFailed(result, "物料编号不存在应返回失败");
System.out.println(" ✅ 物料编号不存在校验通过");
}
/**
* 库存检测申请物料存在但无对应的检验标准
* 验证点接口返回业务错误该物料不存在对应的检验标准
* 注意需要一个存在于质检物料表但没有已发布检验标准的物料编号
*/
@Test
@Order(31)
public void test31_inventoryApply_NoInspectionStandard() {
ExternalInventoryInspectionApplyQO request = buildValidRequest();
request.setMaterialNo("MATERIAL_NO_STANDARD_999");
ApiResult<Void> result = post(API_PATH, request, new TypeReference<>() {});
assertFailed(result, "物料无检验标准应返回失败");
System.out.println(" ✅ 物料无检验标准校验通过");
}
/**
* 库存检测申请物料及标准均存在但无绑定的质检人员
* 验证点接口返回业务错误该物料未绑定质检人员无法自动分配
* 注意需要一个有物料和标准但物料/类别层级均未绑定IQE的物料编号
*/
@Test
@Order(32)
public void test32_inventoryApply_NoInspectorBound() {
ExternalInventoryInspectionApplyQO request = buildValidRequest();
request.setMaterialNo("MATERIAL_NO_INSPECTOR_999");
ApiResult<Void> result = post(API_PATH, request, new TypeReference<>() {});
assertFailed(result, "物料未绑定质检人员应返回失败");
System.out.println(" ✅ 物料未绑定质检人员校验通过");
}
/**
* 库存检测申请请求体为空
* 验证点传入空 body 时接口返回失败
*/
@Test
@Order(33)
public void test33_inventoryApply_EmptyBody() {
ApiResult<Void> result = post(API_PATH, null, new TypeReference<>() {});
assertFailed(result, "空请求体应返回失败");
System.out.println(" ✅ 空请求体校验通过");
}
// ==================== 辅助方法 ====================
/**
* 构建一个有效的库存检测申请请求参数所有必填字段
*/
private ExternalInventoryInspectionApplyQO buildValidRequest() {
ExternalInventoryInspectionApplyQO request = new ExternalInventoryInspectionApplyQO();
request.setMaterialNo(VALID_MATERIAL_NO);
request.setFactory(VALID_FACTORY);
request.setInspectionQty(100);
request.setWarehouse(VALID_WAREHOUSE);
request.setStorageLocation(VALID_STORAGE_LOCATION);
request.setStorageDays(VALID_STORAGE_DAYS);
return request;
}
}

View File

@ -20,4 +20,6 @@ public interface QmsIncomingInspectionTaskRecordItemMapper extends BaseMapper<Qm
List<QmsIssueTicketInspectionRecordItemVO> getNonconformanceDataGroups(Long recordId);
List<QmsIssueTicketInspectionRecordItemSimpleVO> getUnqualifiedRecordItems(Long taskId);
List<String> getUnqualifiedRecordItemDataImages(Long taskId);
}

View File

@ -20,4 +20,6 @@ public interface IQmsIncomingInspectionTaskRecordItemService extends IService<Qm
List<QmsIssueTicketInspectionRecordItemVO> getNonconformanceDataGroups(Long recordId);
List<QmsIssueTicketInspectionRecordItemSimpleVO> getUnqualifiedRecordItems(Long taskId);
List<String> getUnqualifiedRecordItemDataImages(Long taskId);
}

View File

@ -21,6 +21,7 @@ import java.util.List;
@Service
public class QmsIncomingInspectionTaskRecordItemServiceImpl extends ServiceImpl<QmsIncomingInspectionTaskRecordItemMapper, QmsIncomingInspectionTaskRecordItem>
implements IQmsIncomingInspectionTaskRecordItemService {
@Override
public List<QmsIssueTicketInspectionRecordItemVO> getNonconformanceDataGroups(Long recordId) {
return baseMapper.getNonconformanceDataGroups(recordId);
@ -30,4 +31,9 @@ public class QmsIncomingInspectionTaskRecordItemServiceImpl extends ServiceImpl<
public List<QmsIssueTicketInspectionRecordItemSimpleVO> getUnqualifiedRecordItems(Long taskId) {
return baseMapper.getUnqualifiedRecordItems(taskId);
}
@Override
public List<String> getUnqualifiedRecordItemDataImages(Long taskId) {
return baseMapper.getUnqualifiedRecordItemDataImages(taskId);
}
}

View File

@ -17,6 +17,16 @@
INNER JOIN qms_incoming_inspection_task_record_item iitri ON iitr."id"=iitri.record_id
INNER JOIN qms_inspection_standard_item_content isic ON isic."id"=iitri.inspection_standard_item_content_id
INNER JOIN qms_inspection_standard_item isi ON isic.inspection_standard_item_id=isi."id"
WHERE iitr.task_id=#{taskId}
WHERE iitr.task_id=#{taskId} and iitri.unqualified_qty>0
</select>
<select id="getUnqualifiedRecordItemDataImages" resultType="java.lang.String">
SELECT iitrid.image_ids
FROM qms_incoming_inspection_task_record iitr
INNER JOIN qms_incoming_inspection_task_record_item iitri ON iitr."id"=iitri.record_id
INNER JOIN qms_incoming_inspection_task_record_item_data iitrid ON iitrid.item_id=iitri."id"
INNER JOIN qms_inspection_standard_item_content isic ON isic."id"=iitri.inspection_standard_item_content_id
INNER JOIN qms_inspection_standard_item isi ON isic.inspection_standard_item_id=isi."id"
WHERE iitr.task_id=#{taskId} and iitri.unqualified_qty>0
</select>
</mapper>