Commit a693d31e23ab9047434414748b4d1a46be8644dd
1 parent
8bcd65d2
✨ feat(stock): 添加虚拟仓过滤功能
新增虚拟仓过滤模式,支持排除虚拟仓和仅查看虚拟仓的选项。更新相关请求对象和数据查询逻辑,以便后端处理虚拟仓的过滤需求。
Showing
6 changed files
with
291 additions
and
6 deletions
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 | 12 | @ToString(callSuper = true) |
| 13 | 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 | 27 | @Schema(description = "物料分类编号", example = "1") |
| 16 | 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 | 21 | |
| 22 | 22 | default PageResult<MesWmMaterialStockDO> selectPage(MesWmMaterialStockPageReqVO reqVO, |
| 23 | 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 | 27 | .inIfPresent(MesWmMaterialStockDO::getItemTypeId, itemTypeIds) |
| 27 | 28 | .inIfPresent(MesWmMaterialStockDO::getItemId, itemIds) |
| 28 | 29 | .likeIfPresent(MesWmMaterialStockDO::getBatchCode, reqVO.getBatchCode()) |
| ... | ... | @@ -31,9 +32,18 @@ public interface MesWmMaterialStockMapper extends BaseMapperX<MesWmMaterialStock |
| 31 | 32 | .eqIfPresent(MesWmMaterialStockDO::getLocationId, reqVO.getLocationId()) |
| 32 | 33 | .eqIfPresent(MesWmMaterialStockDO::getAreaId, reqVO.getAreaId()) |
| 33 | 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 | 49 | default Long selectCountByWarehouseId(Long warehouseId) { | ... | ... |
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstock/MesWmMaterialStockServiceImpl.java
| 1 | 1 | package cn.iocoder.yudao.module.mes.service.wm.materialstock; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | +import cn.hutool.core.lang.Assert; | |
| 4 | 5 | import cn.hutool.core.util.ObjUtil; |
| 5 | 6 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 6 | 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 | 12 | import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemTypeDO; |
| 12 | 13 | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstock.MesWmMaterialStockDO; |
| 13 | 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 | 16 | import cn.iocoder.yudao.module.mes.dal.mysql.wm.materialstock.MesWmMaterialStockMapper; |
| 15 | 17 | import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; |
| 16 | 18 | import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemTypeService; |
| 17 | 19 | import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService; |
| 20 | +import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseService; | |
| 18 | 21 | import jakarta.annotation.Resource; |
| 22 | +import org.springframework.context.annotation.Lazy; | |
| 19 | 23 | import org.springframework.stereotype.Service; |
| 20 | 24 | import org.springframework.validation.annotation.Validated; |
| 21 | 25 | |
| ... | ... | @@ -43,6 +47,9 @@ public class MesWmMaterialStockServiceImpl implements MesWmMaterialStockService |
| 43 | 47 | private MesMdItemTypeService itemTypeService; |
| 44 | 48 | @Resource |
| 45 | 49 | private MesWmWarehouseAreaService areaService; |
| 50 | + @Resource | |
| 51 | + @Lazy | |
| 52 | + private MesWmWarehouseService warehouseService; | |
| 46 | 53 | |
| 47 | 54 | @Override |
| 48 | 55 | public MesWmMaterialStockDO getMaterialStock(Long id) { |
| ... | ... | @@ -77,9 +84,18 @@ public class MesWmMaterialStockServiceImpl implements MesWmMaterialStockService |
| 77 | 84 | if (pageReqVO.getItemId() != null) { |
| 78 | 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 | 97 | // 2. 分页查询 |
| 82 | - return materialStockMapper.selectPage(pageReqVO, itemTypeIds, itemIds); | |
| 98 | + return materialStockMapper.selectPage(pageReqVO, itemTypeIds, itemIds, virtualWarehouseId); | |
| 83 | 99 | } |
| 84 | 100 | |
| 85 | 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 | 10 | import cn.iocoder.yudao.module.mes.service.wm.materialstock.MesWmMaterialStockService; |
| 11 | 11 | import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService; |
| 12 | 12 | import jakarta.annotation.Resource; |
| 13 | +import org.springframework.context.annotation.Lazy; | |
| 13 | 14 | import org.springframework.stereotype.Service; |
| 14 | 15 | import org.springframework.validation.annotation.Validated; |
| 15 | 16 | |
| ... | ... | @@ -33,6 +34,7 @@ public class MesWmProductSalesDetailServiceImpl implements MesWmProductSalesDeta |
| 33 | 34 | @Resource |
| 34 | 35 | private MesMdItemService itemService; |
| 35 | 36 | @Resource |
| 37 | + @Lazy | |
| 36 | 38 | private MesWmProductSalesLineService productSalesLineService; |
| 37 | 39 | @Resource |
| 38 | 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 "mes_pro_route_process" ( |
| 678 | 678 | "tenant_id" bigint NOT NULL DEFAULT 0, |
| 679 | 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 | +); | ... | ... |