fix: 显式生成出库单聚合主键

This commit is contained in:
zhouz
2026-07-22 14:55:49 +08:00
parent e562715dd8
commit 3ab5af0eb5
3 changed files with 38 additions and 21 deletions

View File

@@ -1,6 +1,7 @@
package cn.code.nl.module.wms.service.iostorinv; package cn.code.nl.module.wms.service.iostorinv;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.code.nl.framework.common.pojo.CommonResult; import cn.code.nl.framework.common.pojo.CommonResult;
import cn.code.nl.module.base.api.codegen.CodeGenApi; import cn.code.nl.module.base.api.codegen.CodeGenApi;
import cn.code.nl.module.base.api.codegen.dto.CodeGenerateReqDTO; import cn.code.nl.module.base.api.codegen.dto.CodeGenerateReqDTO;
@@ -65,6 +66,7 @@ public class IostorInvServiceImpl implements IostorInvService {
.map(IostorInvCreateReqVO.Detail::getPlanQty) .map(IostorInvCreateReqVO.Detail::getPlanQty)
.reduce(BigDecimal.ZERO, BigDecimal::add); .reduce(BigDecimal.ZERO, BigDecimal::add);
IostorInvDO iostorInv = new IostorInvDO(); IostorInvDO iostorInv = new IostorInvDO();
iostorInv.setIostorinvId(IdUtil.fastSimpleUUID());
iostorInv.setBillCode(codeResult.getData()); iostorInv.setBillCode(codeResult.getData());
iostorInv.setIoType("OUT"); iostorInv.setIoType("OUT");
iostorInv.setBillType(reqVO.getBillType()); iostorInv.setBillType(reqVO.getBillType());
@@ -79,6 +81,7 @@ public class IostorInvServiceImpl implements IostorInvService {
for (int index = 0; index < reqVO.getDetails().size(); index++) { for (int index = 0; index < reqVO.getDetails().size(); index++) {
IostorInvCreateReqVO.Detail detail = reqVO.getDetails().get(index); IostorInvCreateReqVO.Detail detail = reqVO.getDetails().get(index);
IostorinvDtlDO detailDO = new IostorinvDtlDO(); IostorinvDtlDO detailDO = new IostorinvDtlDO();
detailDO.setIostorinvdtlId(IdUtil.fastSimpleUUID());
detailDO.setIostorinvId(iostorInv.getIostorinvId()); detailDO.setIostorinvId(iostorInv.getIostorinvId());
detailDO.setSeqNo(index + 1); detailDO.setSeqNo(index + 1);
detailDO.setMaterialCode(detail.getMaterialCode()); detailDO.setMaterialCode(detail.getMaterialCode());

View File

@@ -5,8 +5,6 @@ import cn.code.nl.framework.common.pojo.CommonResult;
import cn.code.nl.module.base.api.codegen.CodeGenApi; import cn.code.nl.module.base.api.codegen.CodeGenApi;
import cn.code.nl.module.base.api.codegen.dto.CodeGenerateReqDTO; import cn.code.nl.module.base.api.codegen.dto.CodeGenerateReqDTO;
import cn.code.nl.module.wms.controller.admin.iostorinv.vo.IostorInvCreateReqVO; import cn.code.nl.module.wms.controller.admin.iostorinv.vo.IostorInvCreateReqVO;
import com.baomidou.mybatisplus.core.config.GlobalConfig;
import com.baomidou.mybatisplus.extension.incrementer.H2KeyGenerator;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean; import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactory;
@@ -31,7 +29,9 @@ import java.time.LocalDateTime;
import java.util.List; import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrows;
@SpringJUnitConfig @SpringJUnitConfig
@@ -56,10 +56,13 @@ class IostorInvServiceLocalSpringTest {
String id = iostorInvService.createOutbound(buildRequest()); String id = iostorInvService.createOutbound(buildRequest());
assertNotNull(id); assertNotNull(id);
assertFalse(id.isBlank());
jdbcTemplate.queryForObject("SELECT * FROM wms_iostorinv WHERE iostorinv_id = ?", (rs, rowNum) -> { jdbcTemplate.queryForObject("SELECT * FROM wms_iostorinv WHERE iostorinv_id = ?", (rs, rowNum) -> {
assertEquals("OUT-TEST-0001", rs.getString("bill_code")); assertEquals("OUT-TEST-0001", rs.getString("bill_code"));
assertEquals("OUT", rs.getString("io_type")); assertEquals("OUT", rs.getString("io_type"));
assertEquals("销售出库", rs.getString("bill_type")); assertEquals("销售出库", rs.getString("bill_type"));
assertEquals(LocalDateTime.of(2026, 7, 22, 0, 0),
rs.getTimestamp("biz_date").toLocalDateTime());
assertEquals("STOR-01", rs.getString("stor_id")); assertEquals("STOR-01", rs.getString("stor_id"));
assertEquals("生成", rs.getString("bill_status")); assertEquals("生成", rs.getString("bill_status"));
assertEquals("主表备注", rs.getString("remark")); assertEquals("主表备注", rs.getString("remark"));
@@ -70,19 +73,27 @@ class IostorInvServiceLocalSpringTest {
List<DetailRow> details = jdbcTemplate.query( List<DetailRow> details = jdbcTemplate.query(
"SELECT * FROM wms_iostorinvdtl WHERE iostorinv_id = ? ORDER BY seq_no", "SELECT * FROM wms_iostorinvdtl WHERE iostorinv_id = ? ORDER BY seq_no",
(rs, rowNum) -> new DetailRow(rs.getInt("seq_no"), rs.getString("material_code"), (rs, rowNum) -> new DetailRow(rs.getString("iostorinvdtl_id"), rs.getString("iostorinv_id"),
rs.getInt("seq_no"), rs.getString("material_code"),
rs.getString("material_id"), rs.getString("pcsn"), rs.getBigDecimal("plan_qty"), rs.getString("material_id"), rs.getString("pcsn"), rs.getBigDecimal("plan_qty"),
rs.getBigDecimal("assign_qty"), rs.getBigDecimal("unassign_qty"), rs.getBigDecimal("assign_qty"), rs.getBigDecimal("unassign_qty"),
rs.getString("qty_unit_id"), rs.getString("qty_unit_name"), rs.getString("qty_unit_id"), rs.getString("qty_unit_name"),
rs.getString("source_bill_code"), rs.getString("source_bill_type"), rs.getString("source_bill_code"), rs.getString("source_bill_type"),
rs.getString("source_billdtl_id"), rs.getString("remark")), id); rs.getString("source_billdtl_id"), rs.getString("remark")), id);
assertEquals(List.of( assertEquals(2, details.size());
new DetailRow(1, "MAT-01", "MID-01", "PCSN-01", new BigDecimal("12.500"), assertNotNull(details.get(0).detailId());
BigDecimal.ZERO.setScale(3), new BigDecimal("12.500"), "KG", "千克", assertNotNull(details.get(1).detailId());
"SRC-01", "ORDER", "SRC-DTL-01", "明细一"), assertFalse(details.get(0).detailId().isBlank());
new DetailRow(2, "MAT-02", "MID-02", "PCSN-02", new BigDecimal("7.250"), assertFalse(details.get(1).detailId().isBlank());
BigDecimal.ZERO.setScale(3), new BigDecimal("7.250"), "KG", "千克", assertNotEquals(details.get(0).detailId(), details.get(1).detailId());
"SRC-02", "ORDER", "SRC-DTL-02", "明细二")), details); assertEquals(id, details.get(0).headerId());
assertEquals(id, details.get(1).headerId());
assertEquals(new DetailRow(details.get(0).detailId(), id, 1, "MAT-01", "MID-01", "PCSN-01",
new BigDecimal("12.500"), BigDecimal.ZERO.setScale(3), new BigDecimal("12.500"),
"KG", "千克", "SRC-01", "ORDER", "SRC-DTL-01", "明细一"), details.get(0));
assertEquals(new DetailRow(details.get(1).detailId(), id, 2, "MAT-02", "MID-02", "PCSN-02",
new BigDecimal("7.250"), BigDecimal.ZERO.setScale(3), new BigDecimal("7.250"),
"KG", "千克", "SRC-02", "ORDER", "SRC-DTL-02", "明细二"), details.get(1));
} }
@Test @Test
@@ -97,6 +108,17 @@ class IostorInvServiceLocalSpringTest {
assertEquals(0, jdbcTemplate.queryForObject("SELECT COUNT(*) FROM wms_iostorinvdtl", Integer.class)); assertEquals(0, jdbcTemplate.queryForObject("SELECT COUNT(*) FROM wms_iostorinvdtl", Integer.class));
} }
@Test
void shouldRollbackHeaderAndFirstDetailWhenSecondDetailInsertFails() {
IostorInvCreateReqVO request = buildRequest();
request.getDetails().get(1).setSourceBillCode(null);
assertThrows(RuntimeException.class, () -> iostorInvService.createOutbound(request));
assertEquals(0, jdbcTemplate.queryForObject("SELECT COUNT(*) FROM wms_iostorinv", Integer.class));
assertEquals(0, jdbcTemplate.queryForObject("SELECT COUNT(*) FROM wms_iostorinvdtl", Integer.class));
}
private IostorInvCreateReqVO buildRequest() { private IostorInvCreateReqVO buildRequest() {
IostorInvCreateReqVO request = new IostorInvCreateReqVO(); IostorInvCreateReqVO request = new IostorInvCreateReqVO();
request.setBillType("销售出库"); request.setBillType("销售出库");
@@ -123,7 +145,8 @@ class IostorInvServiceLocalSpringTest {
return detail; return detail;
} }
private record DetailRow(Integer seqNo, String materialCode, String materialId, String pcsn, private record DetailRow(String detailId, String headerId, Integer seqNo, String materialCode,
String materialId, String pcsn,
BigDecimal planQty, BigDecimal assignQty, BigDecimal unassignQty, BigDecimal planQty, BigDecimal assignQty, BigDecimal unassignQty,
String qtyUnitId, String qtyUnitName, String sourceBillCode, String qtyUnitId, String qtyUnitName, String sourceBillCode,
String sourceBillType, String sourceBilldtlId, String remark) { String sourceBillType, String sourceBilldtlId, String remark) {
@@ -144,9 +167,6 @@ class IostorInvServiceLocalSpringTest {
SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean(); MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();
factory.setDataSource(dataSource); factory.setDataSource(dataSource);
GlobalConfig.DbConfig dbConfig = new GlobalConfig.DbConfig();
dbConfig.setKeyGenerators(List.of(new H2KeyGenerator()));
factory.setGlobalConfig(new GlobalConfig().setDbConfig(dbConfig));
org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration(); org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
configuration.setMapUnderscoreToCamelCase(true); configuration.setMapUnderscoreToCamelCase(true);
factory.setConfiguration(configuration); factory.setConfiguration(configuration);

View File

@@ -1,11 +1,5 @@
DROP TABLE IF EXISTS wms_iostorinvdtl; DROP TABLE IF EXISTS wms_iostorinvdtl;
DROP TABLE IF EXISTS wms_iostorinv; DROP TABLE IF EXISTS wms_iostorinv;
DROP SEQUENCE IF EXISTS wms_iostorinvdtl_seq;
DROP SEQUENCE IF EXISTS wms_iostorinv_seq;
CREATE SEQUENCE wms_iostorinv_seq START WITH 1;
CREATE SEQUENCE wms_iostorinvdtl_seq START WITH 1;
CREATE TABLE wms_iostorinv ( CREATE TABLE wms_iostorinv (
iostorinv_id VARCHAR(64) PRIMARY KEY, iostorinv_id VARCHAR(64) PRIMARY KEY,
bill_code VARCHAR(64), io_type VARCHAR(32), bill_type VARCHAR(64), biz_date TIMESTAMP, bill_code VARCHAR(64), io_type VARCHAR(32), bill_type VARCHAR(64), biz_date TIMESTAMP,
@@ -22,7 +16,7 @@ CREATE TABLE wms_iostorinvdtl (
iostorinvdtl_id VARCHAR(64) PRIMARY KEY, iostorinv_id VARCHAR(64), seq_no INT, iostorinvdtl_id VARCHAR(64) PRIMARY KEY, iostorinv_id VARCHAR(64), seq_no INT,
pcsn VARCHAR(128), bill_status VARCHAR(32), qty_unit_id VARCHAR(64), qty_unit_name VARCHAR(64), pcsn VARCHAR(128), bill_status VARCHAR(32), qty_unit_id VARCHAR(64), qty_unit_name VARCHAR(64),
plan_qty DECIMAL(18, 3), real_qty DECIMAL(18, 3), source_billdtl_id VARCHAR(64), plan_qty DECIMAL(18, 3), real_qty DECIMAL(18, 3), source_billdtl_id VARCHAR(64),
source_bill_type VARCHAR(64), source_bill_code VARCHAR(64), source_bill_table VARCHAR(128), source_bill_type VARCHAR(64), source_bill_code VARCHAR(64) NOT NULL, source_bill_table VARCHAR(128),
remark VARCHAR(255), assign_qty DECIMAL(18, 3), unassign_qty DECIMAL(18, 3), remark VARCHAR(255), assign_qty DECIMAL(18, 3), unassign_qty DECIMAL(18, 3),
material_code VARCHAR(64), source_load_port VARCHAR(64), callback_strategy VARCHAR(255), material_code VARCHAR(64), source_load_port VARCHAR(64), callback_strategy VARCHAR(255),
material_id VARCHAR(64), create_time TIMESTAMP, update_time TIMESTAMP, material_id VARCHAR(64), create_time TIMESTAMP, update_time TIMESTAMP,