Commit a693d31e23ab9047434414748b4d1a46be8644dd

Authored by YunaiV
1 parent 8bcd65d2

✨ feat(stock): 添加虚拟仓过滤功能

新增虚拟仓过滤模式,支持排除虚拟仓和仅查看虚拟仓的选项。更新相关请求对象和数据查询逻辑,以便后端处理虚拟仓的过滤需求。
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/vo/MesWmMaterialStockPageReqVO.java
@@ -12,6 +12,18 @@ import lombok.ToString; @@ -12,6 +12,18 @@ import lombok.ToString;
12 @ToString(callSuper = true) 12 @ToString(callSuper = true)
13 public class MesWmMaterialStockPageReqVO extends PageParam { 13 public class MesWmMaterialStockPageReqVO extends PageParam {
14 14
  15 + /**
  16 + * 虚拟仓过滤模式 - 排除虚拟仓
  17 + */
  18 + public static final String VIRTUAL_FILTER_EXCLUDE = "exclude";
  19 + /**
  20 + * 虚拟仓过滤模式 - 只看虚拟仓
  21 + */
  22 + public static final String VIRTUAL_FILTER_ONLY = "only";
  23 +
  24 + @Schema(description = "虚拟仓过滤模式", example = "exclude")
  25 + private String virtualFilter;
  26 +
15 @Schema(description = "物料分类编号", example = "1") 27 @Schema(description = "物料分类编号", example = "1")
16 private Long itemTypeId; 28 private Long itemTypeId;
17 29
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/materialstock/MesWmMaterialStockMapper.java
@@ -21,8 +21,9 @@ public interface MesWmMaterialStockMapper extends BaseMapperX<MesWmMaterialStock @@ -21,8 +21,9 @@ public interface MesWmMaterialStockMapper extends BaseMapperX<MesWmMaterialStock
21 21
22 default PageResult<MesWmMaterialStockDO> selectPage(MesWmMaterialStockPageReqVO reqVO, 22 default PageResult<MesWmMaterialStockDO> selectPage(MesWmMaterialStockPageReqVO reqVO,
23 Collection<Long> itemTypeIds, 23 Collection<Long> itemTypeIds,
24 - Collection<Long> itemIds) {  
25 - return selectPage(reqVO, new LambdaQueryWrapperX<MesWmMaterialStockDO>() 24 + Collection<Long> itemIds,
  25 + Long virtualWarehouseId) {
  26 + LambdaQueryWrapperX<MesWmMaterialStockDO> wrapper = new LambdaQueryWrapperX<MesWmMaterialStockDO>()
26 .inIfPresent(MesWmMaterialStockDO::getItemTypeId, itemTypeIds) 27 .inIfPresent(MesWmMaterialStockDO::getItemTypeId, itemTypeIds)
27 .inIfPresent(MesWmMaterialStockDO::getItemId, itemIds) 28 .inIfPresent(MesWmMaterialStockDO::getItemId, itemIds)
28 .likeIfPresent(MesWmMaterialStockDO::getBatchCode, reqVO.getBatchCode()) 29 .likeIfPresent(MesWmMaterialStockDO::getBatchCode, reqVO.getBatchCode())
@@ -31,9 +32,18 @@ public interface MesWmMaterialStockMapper extends BaseMapperX&lt;MesWmMaterialStock @@ -31,9 +32,18 @@ public interface MesWmMaterialStockMapper extends BaseMapperX&lt;MesWmMaterialStock
31 .eqIfPresent(MesWmMaterialStockDO::getLocationId, reqVO.getLocationId()) 32 .eqIfPresent(MesWmMaterialStockDO::getLocationId, reqVO.getLocationId())
32 .eqIfPresent(MesWmMaterialStockDO::getAreaId, reqVO.getAreaId()) 33 .eqIfPresent(MesWmMaterialStockDO::getAreaId, reqVO.getAreaId())
33 .eqIfPresent(MesWmMaterialStockDO::getVendorId, reqVO.getVendorId()) 34 .eqIfPresent(MesWmMaterialStockDO::getVendorId, reqVO.getVendorId())
34 - .eqIfPresent(MesWmMaterialStockDO::getFrozen, reqVO.getFrozen())  
35 - .ne(MesWmMaterialStockDO::getQuantity, BigDecimal.ZERO)  
36 - .orderByAsc(MesWmMaterialStockDO::getReceiptTime)); 35 + .eqIfPresent(MesWmMaterialStockDO::getFrozen, reqVO.getFrozen());
  36 + wrapper.ne(MesWmMaterialStockDO::getQuantity, BigDecimal.ZERO)
  37 + .orderByAsc(MesWmMaterialStockDO::getReceiptTime);
  38 + // 虚拟仓过滤(Service 层已将 virtualFilter 解析为 virtualWarehouseId)
  39 + if (virtualWarehouseId != null) {
  40 + if (MesWmMaterialStockPageReqVO.VIRTUAL_FILTER_ONLY.equals(reqVO.getVirtualFilter())) {
  41 + wrapper.eq(MesWmMaterialStockDO::getWarehouseId, virtualWarehouseId);
  42 + } else if (MesWmMaterialStockPageReqVO.VIRTUAL_FILTER_EXCLUDE.equals(reqVO.getVirtualFilter())) {
  43 + wrapper.ne(MesWmMaterialStockDO::getWarehouseId, virtualWarehouseId);
  44 + }
  45 + }
  46 + return selectPage(reqVO, wrapper);
37 } 47 }
38 48
39 default Long selectCountByWarehouseId(Long warehouseId) { 49 default Long selectCountByWarehouseId(Long warehouseId) {
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstock/MesWmMaterialStockServiceImpl.java
1 package cn.iocoder.yudao.module.mes.service.wm.materialstock; 1 package cn.iocoder.yudao.module.mes.service.wm.materialstock;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.lang.Assert;
4 import cn.hutool.core.util.ObjUtil; 5 import cn.hutool.core.util.ObjUtil;
5 import cn.iocoder.yudao.framework.common.pojo.PageResult; 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 import cn.iocoder.yudao.framework.common.util.collection.SetUtils; 7 import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
@@ -11,11 +12,14 @@ import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO; @@ -11,11 +12,14 @@ import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
11 import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemTypeDO; 12 import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemTypeDO;
12 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstock.MesWmMaterialStockDO; 13 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstock.MesWmMaterialStockDO;
13 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseAreaDO; 14 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseAreaDO;
  15 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseDO;
14 import cn.iocoder.yudao.module.mes.dal.mysql.wm.materialstock.MesWmMaterialStockMapper; 16 import cn.iocoder.yudao.module.mes.dal.mysql.wm.materialstock.MesWmMaterialStockMapper;
15 import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; 17 import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
16 import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemTypeService; 18 import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemTypeService;
17 import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService; 19 import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService;
  20 +import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseService;
18 import jakarta.annotation.Resource; 21 import jakarta.annotation.Resource;
  22 +import org.springframework.context.annotation.Lazy;
19 import org.springframework.stereotype.Service; 23 import org.springframework.stereotype.Service;
20 import org.springframework.validation.annotation.Validated; 24 import org.springframework.validation.annotation.Validated;
21 25
@@ -43,6 +47,9 @@ public class MesWmMaterialStockServiceImpl implements MesWmMaterialStockService @@ -43,6 +47,9 @@ public class MesWmMaterialStockServiceImpl implements MesWmMaterialStockService
43 private MesMdItemTypeService itemTypeService; 47 private MesMdItemTypeService itemTypeService;
44 @Resource 48 @Resource
45 private MesWmWarehouseAreaService areaService; 49 private MesWmWarehouseAreaService areaService;
  50 + @Resource
  51 + @Lazy
  52 + private MesWmWarehouseService warehouseService;
46 53
47 @Override 54 @Override
48 public MesWmMaterialStockDO getMaterialStock(Long id) { 55 public MesWmMaterialStockDO getMaterialStock(Long id) {
@@ -77,9 +84,18 @@ public class MesWmMaterialStockServiceImpl implements MesWmMaterialStockService @@ -77,9 +84,18 @@ public class MesWmMaterialStockServiceImpl implements MesWmMaterialStockService
77 if (pageReqVO.getItemId() != null) { 84 if (pageReqVO.getItemId() != null) {
78 itemIds = SetUtils.asSet(pageReqVO.getItemId()); 85 itemIds = SetUtils.asSet(pageReqVO.getItemId());
79 } 86 }
  87 + // 1.3 解析 virtualFilter:转换为虚拟仓 warehouseId
  88 + Long virtualWarehouseId = null;
  89 + String virtualFilter = pageReqVO.getVirtualFilter();
  90 + if (MesWmMaterialStockPageReqVO.VIRTUAL_FILTER_EXCLUDE.equals(virtualFilter)
  91 + || MesWmMaterialStockPageReqVO.VIRTUAL_FILTER_ONLY.equals(virtualFilter)) {
  92 + MesWmWarehouseDO virtualWarehouse = warehouseService.getWarehouseByCode(
  93 + MesWmWarehouseDO.WIP_VIRTUAL_WAREHOUSE);
  94 + Assert.notNull(virtualWarehouse, "虚拟仓库(WIP_VIRTUAL_WAREHOUSE)不存在");
  95 + }
80 96
81 // 2. 分页查询 97 // 2. 分页查询
82 - return materialStockMapper.selectPage(pageReqVO, itemTypeIds, itemIds); 98 + return materialStockMapper.selectPage(pageReqVO, itemTypeIds, itemIds, virtualWarehouseId);
83 } 99 }
84 100
85 @Override 101 @Override
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productsales/MesWmProductSalesDetailServiceImpl.java
@@ -10,6 +10,7 @@ import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; @@ -10,6 +10,7 @@ import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
10 import cn.iocoder.yudao.module.mes.service.wm.materialstock.MesWmMaterialStockService; 10 import cn.iocoder.yudao.module.mes.service.wm.materialstock.MesWmMaterialStockService;
11 import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService; 11 import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService;
12 import jakarta.annotation.Resource; 12 import jakarta.annotation.Resource;
  13 +import org.springframework.context.annotation.Lazy;
13 import org.springframework.stereotype.Service; 14 import org.springframework.stereotype.Service;
14 import org.springframework.validation.annotation.Validated; 15 import org.springframework.validation.annotation.Validated;
15 16
@@ -33,6 +34,7 @@ public class MesWmProductSalesDetailServiceImpl implements MesWmProductSalesDeta @@ -33,6 +34,7 @@ public class MesWmProductSalesDetailServiceImpl implements MesWmProductSalesDeta
33 @Resource 34 @Resource
34 private MesMdItemService itemService; 35 private MesMdItemService itemService;
35 @Resource 36 @Resource
  37 + @Lazy
36 private MesWmProductSalesLineService productSalesLineService; 38 private MesWmProductSalesLineService productSalesLineService;
37 @Resource 39 @Resource
38 private MesWmMaterialStockService materialStockService; 40 private MesWmMaterialStockService materialStockService;
yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/productsales/MesWmProductSalesDetailMapperTest.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mes.dal.mysql.wm.productsales;
  2 +
  3 +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
  4 +import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesDetailDO;
  5 +import jakarta.annotation.Resource;
  6 +import org.junit.jupiter.api.Test;
  7 +
  8 +import java.math.BigDecimal;
  9 +import java.util.List;
  10 +import java.util.function.Consumer;
  11 +
  12 +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
  13 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
  14 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
  15 +import static org.junit.jupiter.api.Assertions.*;
  16 +
  17 +/**
  18 + * {@link MesWmProductSalesDetailMapper} 的单元测试
  19 + * <p>
  20 + * 覆盖:selectListByLineId、selectListBySalesId、deleteByLineId、deleteBySalesId
  21 + */
  22 +public class MesWmProductSalesDetailMapperTest extends BaseDbUnitTest {
  23 +
  24 + @Resource
  25 + private MesWmProductSalesDetailMapper detailMapper;
  26 +
  27 + // ==================== 辅助方法 ====================
  28 +
  29 + /**
  30 + * 创建明细 DO,固定 BigDecimal 精度为 2 位,避免 H2 decimal(14,2) 精度不匹配
  31 + */
  32 + private MesWmProductSalesDetailDO createDetailPojo(Consumer<MesWmProductSalesDetailDO> consumer) {
  33 + return randomPojo(MesWmProductSalesDetailDO.class, o -> {
  34 + o.setQuantity(new BigDecimal("10.00"));
  35 + consumer.accept(o);
  36 + });
  37 + }
  38 +
  39 + // ==================== selectListByLineId ====================
  40 +
  41 + @Test
  42 + public void testSelectListByLineId_match() {
  43 + Long lineId = 100L;
  44 + MesWmProductSalesDetailDO match = createDetailPojo(o -> {
  45 + o.setLineId(lineId);
  46 + o.setSalesId(1L);
  47 + });
  48 + detailMapper.insert(match);
  49 + // 插入另一条不同 lineId 的记录,不应被返回
  50 + detailMapper.insert(cloneIgnoreId(match, o -> o.setLineId(999L)));
  51 +
  52 + List<MesWmProductSalesDetailDO> result = detailMapper.selectListByLineId(lineId);
  53 +
  54 + assertEquals(1, result.size());
  55 + assertPojoEquals(match, result.get(0));
  56 + }
  57 +
  58 + @Test
  59 + public void testSelectListByLineId_multipleRows() {
  60 + Long lineId = 200L;
  61 + MesWmProductSalesDetailDO detail1 = createDetailPojo(o -> {
  62 + o.setLineId(lineId);
  63 + o.setSalesId(2L);
  64 + });
  65 + MesWmProductSalesDetailDO detail2 = createDetailPojo(o -> {
  66 + o.setLineId(lineId);
  67 + o.setSalesId(2L);
  68 + });
  69 + detailMapper.insert(detail1);
  70 + detailMapper.insert(detail2);
  71 +
  72 + List<MesWmProductSalesDetailDO> result = detailMapper.selectListByLineId(lineId);
  73 +
  74 + assertEquals(2, result.size());
  75 + }
  76 +
  77 + @Test
  78 + public void testSelectListByLineId_noMatch() {
  79 + MesWmProductSalesDetailDO detail = createDetailPojo(o -> {
  80 + o.setLineId(300L);
  81 + o.setSalesId(3L);
  82 + });
  83 + detailMapper.insert(detail);
  84 +
  85 + List<MesWmProductSalesDetailDO> result = detailMapper.selectListByLineId(999L);
  86 +
  87 + assertTrue(result.isEmpty());
  88 + }
  89 +
  90 + // ==================== selectListBySalesId ====================
  91 +
  92 + @Test
  93 + public void testSelectListBySalesId_match() {
  94 + Long salesId = 10L;
  95 + MesWmProductSalesDetailDO match = createDetailPojo(o -> {
  96 + o.setSalesId(salesId);
  97 + o.setLineId(1L);
  98 + });
  99 + detailMapper.insert(match);
  100 + // 插入另一条不同 salesId 的记录
  101 + detailMapper.insert(cloneIgnoreId(match, o -> o.setSalesId(888L)));
  102 +
  103 + List<MesWmProductSalesDetailDO> result = detailMapper.selectListBySalesId(salesId);
  104 +
  105 + assertEquals(1, result.size());
  106 + assertPojoEquals(match, result.get(0));
  107 + }
  108 +
  109 + @Test
  110 + public void testSelectListBySalesId_multipleRows() {
  111 + Long salesId = 20L;
  112 + MesWmProductSalesDetailDO detail1 = createDetailPojo(o -> {
  113 + o.setSalesId(salesId);
  114 + o.setLineId(11L);
  115 + });
  116 + MesWmProductSalesDetailDO detail2 = createDetailPojo(o -> {
  117 + o.setSalesId(salesId);
  118 + o.setLineId(12L);
  119 + });
  120 + detailMapper.insert(detail1);
  121 + detailMapper.insert(detail2);
  122 +
  123 + List<MesWmProductSalesDetailDO> result = detailMapper.selectListBySalesId(salesId);
  124 +
  125 + assertEquals(2, result.size());
  126 + }
  127 +
  128 + @Test
  129 + public void testSelectListBySalesId_noMatch() {
  130 + MesWmProductSalesDetailDO detail = createDetailPojo(o -> {
  131 + o.setSalesId(30L);
  132 + o.setLineId(2L);
  133 + });
  134 + detailMapper.insert(detail);
  135 +
  136 + List<MesWmProductSalesDetailDO> result = detailMapper.selectListBySalesId(9999L);
  137 +
  138 + assertTrue(result.isEmpty());
  139 + }
  140 +
  141 + // ==================== deleteByLineId ====================
  142 +
  143 + @Test
  144 + public void testDeleteByLineId_deletesOnlyTargetLine() {
  145 + Long lineId = 400L;
  146 + MesWmProductSalesDetailDO toDelete = createDetailPojo(o -> {
  147 + o.setLineId(lineId);
  148 + o.setSalesId(4L);
  149 + });
  150 + MesWmProductSalesDetailDO toKeep = createDetailPojo(o -> {
  151 + o.setLineId(500L);
  152 + o.setSalesId(4L);
  153 + });
  154 + detailMapper.insert(toDelete);
  155 + detailMapper.insert(toKeep);
  156 +
  157 + detailMapper.deleteByLineId(lineId);
  158 +
  159 + // lineId=400 的记录已被逻辑删除
  160 + List<MesWmProductSalesDetailDO> deletedResult = detailMapper.selectListByLineId(lineId);
  161 + assertTrue(deletedResult.isEmpty(), "lineId=400 的记录应被删除");
  162 +
  163 + // lineId=500 的记录保留
  164 + List<MesWmProductSalesDetailDO> keptResult = detailMapper.selectListByLineId(500L);
  165 + assertEquals(1, keptResult.size());
  166 + }
  167 +
  168 + @Test
  169 + public void testDeleteByLineId_multipleRows() {
  170 + Long lineId = 600L;
  171 + detailMapper.insert(createDetailPojo(o -> { o.setLineId(lineId); o.setSalesId(5L); }));
  172 + detailMapper.insert(createDetailPojo(o -> { o.setLineId(lineId); o.setSalesId(5L); }));
  173 +
  174 + detailMapper.deleteByLineId(lineId);
  175 +
  176 + List<MesWmProductSalesDetailDO> result = detailMapper.selectListByLineId(lineId);
  177 + assertTrue(result.isEmpty(), "同 lineId 下的所有记录均应被删除");
  178 + }
  179 +
  180 + // ==================== deleteBySalesId ====================
  181 +
  182 + @Test
  183 + public void testDeleteBySalesId_deletesOnlyTargetSales() {
  184 + Long salesId = 700L;
  185 + MesWmProductSalesDetailDO toDelete = createDetailPojo(o -> {
  186 + o.setSalesId(salesId);
  187 + o.setLineId(6L);
  188 + });
  189 + MesWmProductSalesDetailDO toKeep = createDetailPojo(o -> {
  190 + o.setSalesId(800L);
  191 + o.setLineId(7L);
  192 + });
  193 + detailMapper.insert(toDelete);
  194 + detailMapper.insert(toKeep);
  195 +
  196 + detailMapper.deleteBySalesId(salesId);
  197 +
  198 + // salesId=700 的记录已被逻辑删除
  199 + List<MesWmProductSalesDetailDO> deletedResult = detailMapper.selectListBySalesId(salesId);
  200 + assertTrue(deletedResult.isEmpty(), "salesId=700 的记录应被删除");
  201 +
  202 + // salesId=800 的记录保留
  203 + List<MesWmProductSalesDetailDO> keptResult = detailMapper.selectListBySalesId(800L);
  204 + assertEquals(1, keptResult.size());
  205 + }
  206 +
  207 + @Test
  208 + public void testDeleteBySalesId_multipleRows() {
  209 + Long salesId = 900L;
  210 + detailMapper.insert(createDetailPojo(o -> { o.setSalesId(salesId); o.setLineId(8L); }));
  211 + detailMapper.insert(createDetailPojo(o -> { o.setSalesId(salesId); o.setLineId(9L); }));
  212 + detailMapper.insert(createDetailPojo(o -> { o.setSalesId(salesId); o.setLineId(10L); }));
  213 +
  214 + detailMapper.deleteBySalesId(salesId);
  215 +
  216 + List<MesWmProductSalesDetailDO> result = detailMapper.selectListBySalesId(salesId);
  217 + assertTrue(result.isEmpty(), "同 salesId 下的所有记录均应被删除");
  218 + }
  219 +
  220 +}
yudao-module-mes/src/test/resources/sql/create_tables.sql
@@ -678,3 +678,28 @@ CREATE TABLE IF NOT EXISTS &quot;mes_pro_route_process&quot; ( @@ -678,3 +678,28 @@ CREATE TABLE IF NOT EXISTS &quot;mes_pro_route_process&quot; (
678 "tenant_id" bigint NOT NULL DEFAULT 0, 678 "tenant_id" bigint NOT NULL DEFAULT 0,
679 PRIMARY KEY ("id") 679 PRIMARY KEY ("id")
680 ); 680 );
  681 +
  682 +-- ----------------------------
  683 +-- MES 销售出库明细
  684 +-- ----------------------------
  685 +CREATE TABLE IF NOT EXISTS "mes_wm_product_sales_detail" (
  686 + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  687 + "line_id" bigint NOT NULL,
  688 + "sales_id" bigint NOT NULL,
  689 + "item_id" bigint NOT NULL,
  690 + "quantity" decimal(14,2) DEFAULT NULL,
  691 + "material_stock_id" bigint DEFAULT NULL,
  692 + "batch_id" bigint DEFAULT NULL,
  693 + "batch_code" varchar(255) DEFAULT NULL,
  694 + "warehouse_id" bigint DEFAULT NULL,
  695 + "location_id" bigint DEFAULT NULL,
  696 + "area_id" bigint DEFAULT NULL,
  697 + "remark" varchar(500) DEFAULT NULL,
  698 + "creator" varchar(64) DEFAULT '',
  699 + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  700 + "updater" varchar(64) DEFAULT '',
  701 + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  702 + "deleted" bit NOT NULL DEFAULT FALSE,
  703 + "tenant_id" bigint NOT NULL DEFAULT 0,
  704 + PRIMARY KEY ("id")
  705 +);