From ae8ce7265954cb46c657df04e7cf93af301d0e4c Mon Sep 17 00:00:00 2001 From: canonical Date: Mon, 2 Dec 2024 23:31:41 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0batch-gen.task.xml=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E6=96=87=E4=BB=B6=EF=BC=8C=E5=9C=A8batch=20dsl?= =?UTF-8?q?=E4=B8=AD=E9=9B=86=E6=88=90BatchGenModel=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/dev-guide/report/excel-import.md | 29 ++- .../io/nop/batch/core/BatchTaskBuilder.java | 11 + .../loader/ResourceRecordLoaderProvider.java | 18 +- nop-batch/nop-batch-dsl/pom.xml | 5 + .../batch/dsl/manager/FileBatchSupport.java | 4 +- .../ModelBasedBatchTaskBuilderFactory.java | 17 ++ .../batch/dsl/model/BatchGeneratorModel.java | 9 + .../dsl/model/_gen/_BatchFileReaderModel.java | 22 +- .../dsl/model/_gen/_BatchGeneratorModel.java | 107 ++++++++ .../dsl/model/_gen/_BatchLoaderModel.java | 30 +++ .../batch/dsl/model/_gen/_BatchTaskModel.java | 28 +++ .../io/nop/batch/gen/BatchGenConstants.java | 8 +- ...oader.java => BatchGenLoaderProvider.java} | 17 +- .../io/nop/batch/gen/model/BatchGenModel.java | 44 +++- .../batch/gen/model/BatchGenModelParser.java | 44 +--- .../_vfs/nop/batch/imp/batch-gen.imp.xml | 234 +++++------------- nop-bom/pom.xml | 6 + nop-cli-core/pom.xml | 5 + .../src/test/java/io/nop/cli/TestNopCli.java | 19 +- nop-cli/demo/_vfs/app/demo/_module | 0 nop-cli/demo/_vfs/app/demo/orm/app.orm.xml | 5 +- nop-cli/demo/_vfs/app/demo/orm/demo.orm.xlsx | Bin 24599 -> 29282 bytes nop-cli/demo/_vfs/batch/batch-demo.task.xml | 28 +++ .../_vfs/batch/create-card.batch-gen.xlsx | Bin 12671 -> 12870 bytes nop-cli/demo/_vfs/batch/demo.orm.xlsx | Bin 24076 -> 0 bytes .../_vfs/nop/task/demo/create-card.task.xml | 47 ++++ .../io/nop/commons/util/StringHelper.java | 8 + .../io/nop/excel/imp/ImportExcelParser.java | 4 + .../excel/imp/model/_gen/_ImportModel.java | 28 +++ .../java/io/nop/orm/utils/OrmDaoHelper.java | 18 ++ .../main/resources/_vfs/nop/orm/xlib/dao.xlib | 9 + .../resources/_vfs/nop/orm/xlib/orm-gen.xlib | 16 ++ .../resources/_vfs/nop/schema/excel/imp.xdef | 2 + .../resources/_vfs/nop/schema/task/batch.xdef | 6 +- .../xdef/domain/SimpleStdDomainHandlers.java | 1 + .../java/io/nop/xlang/xdsl/XDslExtender.java | 3 +- 36 files changed, 574 insertions(+), 258 deletions(-) create mode 100644 nop-batch/nop-batch-dsl/src/main/java/io/nop/batch/dsl/model/BatchGeneratorModel.java create mode 100644 nop-batch/nop-batch-dsl/src/main/java/io/nop/batch/dsl/model/_gen/_BatchGeneratorModel.java rename nop-batch/nop-batch-gen/src/main/java/io/nop/batch/gen/loader/{BatchGenLoader.java => BatchGenLoaderProvider.java} (80%) create mode 100644 nop-cli/demo/_vfs/app/demo/_module create mode 100644 nop-cli/demo/_vfs/batch/batch-demo.task.xml delete mode 100644 nop-cli/demo/_vfs/batch/demo.orm.xlsx create mode 100644 nop-cli/demo/_vfs/nop/task/demo/create-card.task.xml create mode 100644 nop-orm/src/main/java/io/nop/orm/utils/OrmDaoHelper.java diff --git a/docs/dev-guide/report/excel-import.md b/docs/dev-guide/report/excel-import.md index e9c84a925..1ca4b4fcd 100644 --- a/docs/dev-guide/report/excel-import.md +++ b/docs/dev-guide/report/excel-import.md @@ -14,7 +14,7 @@ ## 配置说明 -## 如果解析列表 +## 如何解析列表 * sheet或者field上标注list=true,表示将会解析得到一个列表 * 列表的第一列必须是数字列,不要求编号唯一,也不要求编号列在字段列表中定义。它仅仅被用于确定列表行的范围。 @@ -23,7 +23,7 @@ ```xml - + ``` @@ -45,7 +45,7 @@ ExcelReportHelper中提供了根据导入的业务数据自动生成html或者xlsx文件的方法。 ```javascript - Object bean = ExcelHelper.loadXlsxObject("/nop/test/imp/test5.imp.xml", resource); + Object bean = ExcelHelper.loadXlsxObject("/nop/test/imp/test5.imp.xml", resource); String html = ExcelReportHelper.getHtmlForXlsxObject(impModelPath, bean, scope); ExcelReportHelper.saveXlsxObject(impModelPath, resource, bean); ``` @@ -66,7 +66,7 @@ ExcelReportHelper中提供了根据导入的业务数据自动生成html或者xl - + @@ -88,7 +88,7 @@ ExcelReportHelper中提供了根据导入的业务数据自动生成html或者xl _.findWhere(cell.rp.ev.indexValues,'year',cell.cp.ev.$toInt()).value - + @@ -201,17 +201,20 @@ ExcelReportHelper中提供了根据导入的业务数据自动生成html或者xl 如果要使用Nop平台的Excel导入导出功能,只需要在pom文件中引入如下模块 ```xml - - + + + + io.github.entropy-cloud nop-spring-core-starter - + - - -io.github.entropy-cloud -nop-report-core - + + + io.github.entropy-cloud + nop-report-core + + ``` diff --git a/nop-batch/nop-batch-core/src/main/java/io/nop/batch/core/BatchTaskBuilder.java b/nop-batch/nop-batch-core/src/main/java/io/nop/batch/core/BatchTaskBuilder.java index fe986e472..202da2b8b 100644 --- a/nop-batch/nop-batch-core/src/main/java/io/nop/batch/core/BatchTaskBuilder.java +++ b/nop-batch/nop-batch-core/src/main/java/io/nop/batch/core/BatchTaskBuilder.java @@ -23,6 +23,7 @@ import io.nop.batch.core.loader.ChunkSortBatchLoader; import io.nop.batch.core.loader.RetryBatchLoader; import io.nop.batch.core.processor.BatchChunkProcessor; +import io.nop.batch.core.processor.BatchSequentialProcessor; import io.nop.batch.core.processor.InvokerBatchChunkProcessor; import io.nop.commons.concurrent.executor.ExecutorHelper; import io.nop.commons.concurrent.ratelimit.DefaultRateLimiter; @@ -44,6 +45,7 @@ public class BatchTaskBuilder implements IBatchTaskBuilder { private Long taskVersion; private IBatchLoaderProvider loader; private IBatchConsumerProvider consumer; + private boolean useBatchRequestGenerator; private IBatchProcessorProvider processor; private int batchSize = 100; @@ -133,6 +135,12 @@ public BatchTaskBuilder taskVersion(Long taskVersion) { return this; } + @PropertySetter + public BatchTaskBuilder useBatchRequestGenerator(boolean b) { + this.useBatchRequestGenerator = b; + return this; + } + @PropertySetter public BatchTaskBuilder taskKeyExpr(IEvalFunction expr) { this.taskKeyExpr = expr; @@ -328,6 +336,9 @@ protected IBatchChunkProcessor buildChunkProcessor(IBatchTaskContext context) { if (this.processor != null) { // 如果设置了processor,则先执行processor再调用consumer,否则直接调用consumer IBatchProcessor processor = this.processor.setup(context); + if (useBatchRequestGenerator) { + processor = new BatchSequentialProcessor(processor); + } consumer = new BatchProcessorConsumer<>(processor, (IBatchConsumer) consumer); } diff --git a/nop-batch/nop-batch-core/src/main/java/io/nop/batch/core/loader/ResourceRecordLoaderProvider.java b/nop-batch/nop-batch-core/src/main/java/io/nop/batch/core/loader/ResourceRecordLoaderProvider.java index a7a0c8741..8ce67cd96 100644 --- a/nop-batch/nop-batch-core/src/main/java/io/nop/batch/core/loader/ResourceRecordLoaderProvider.java +++ b/nop-batch/nop-batch-core/src/main/java/io/nop/batch/core/loader/ResourceRecordLoaderProvider.java @@ -7,6 +7,7 @@ */ package io.nop.batch.core.loader; +import io.nop.api.core.convert.ConvertHelper; import io.nop.api.core.exceptions.NopException; import io.nop.batch.core.IBatchAggregator; import io.nop.batch.core.IBatchChunkContext; @@ -15,6 +16,7 @@ import io.nop.batch.core.IBatchTaskContext; import io.nop.batch.core.common.AbstractBatchResourceHandler; import io.nop.commons.util.IoHelper; +import io.nop.core.lang.eval.IEvalAction; import io.nop.core.resource.IResource; import io.nop.core.resource.record.IResourceRecordInputProvider; import io.nop.dataset.record.IRecordInput; @@ -50,7 +52,7 @@ public class ResourceRecordLoaderProvider extends AbstractBatchResourceHandle /** * 最多读取多少行数据(包含跳过的记录) */ - long maxCount; + IEvalAction maxCountExpr; /** * 跳过起始的多少行数据 @@ -111,12 +113,8 @@ public boolean isSaveState() { return saveState; } - public long getMaxCount() { - return maxCount; - } - - public void setMaxCount(long maxCount) { - this.maxCount = maxCount; + public void setMaxCountExpr(IEvalAction maxCountExpr) { + this.maxCountExpr = maxCountExpr; } public long getSkipCount() { @@ -164,8 +162,10 @@ LoaderState newLoaderState(IBatchTaskContext context) { skip(input, skipCount, state); } - if (maxCount > 0) { - input = input.limit(maxCount); + if (maxCountExpr != null) { + Long maxCount = ConvertHelper.toLong(maxCountExpr.invoke(context)); + if (maxCount != null) + input = input.limit(maxCount); } if (recordRowNumber) { diff --git a/nop-batch/nop-batch-dsl/pom.xml b/nop-batch/nop-batch-dsl/pom.xml index bb805fc74..ccd786d2f 100644 --- a/nop-batch/nop-batch-dsl/pom.xml +++ b/nop-batch/nop-batch-dsl/pom.xml @@ -36,6 +36,11 @@ nop-batch-dao + + io.github.entropy-cloud + nop-batch-gen + + io.github.entropy-cloud nop-task-core diff --git a/nop-batch/nop-batch-dsl/src/main/java/io/nop/batch/dsl/manager/FileBatchSupport.java b/nop-batch/nop-batch-dsl/src/main/java/io/nop/batch/dsl/manager/FileBatchSupport.java index 152bd9cb0..5c880a90f 100644 --- a/nop-batch/nop-batch-dsl/src/main/java/io/nop/batch/dsl/manager/FileBatchSupport.java +++ b/nop-batch/nop-batch-dsl/src/main/java/io/nop/batch/dsl/manager/FileBatchSupport.java @@ -34,8 +34,8 @@ public static IBatchLoaderProvider newFileReader(BatchFileReaderModel lo loader.setResourceLoader(resourceLoader); loader.setSaveState(Boolean.TRUE.equals(saveState)); - if (loaderModel.getMaxCount() != null) - loader.setMaxCount(loaderModel.getMaxCount()); + if (loaderModel.getMaxCountExpr() != null) + loader.setMaxCountExpr(loaderModel.getMaxCountExpr()); loader.setPathExpr(loaderModel.getFilePath()); loader.setEncoding(loaderModel.getEncoding()); loader.setAggregator(aggregator); diff --git a/nop-batch/nop-batch-dsl/src/main/java/io/nop/batch/dsl/manager/ModelBasedBatchTaskBuilderFactory.java b/nop-batch/nop-batch-dsl/src/main/java/io/nop/batch/dsl/manager/ModelBasedBatchTaskBuilderFactory.java index bfd661b46..06bac678b 100644 --- a/nop-batch/nop-batch-dsl/src/main/java/io/nop/batch/dsl/manager/ModelBasedBatchTaskBuilderFactory.java +++ b/nop-batch/nop-batch-dsl/src/main/java/io/nop/batch/dsl/manager/ModelBasedBatchTaskBuilderFactory.java @@ -22,17 +22,21 @@ import io.nop.batch.core.processor.MultiBatchProcessorProvider; import io.nop.batch.dsl.model.BatchChunkProcessorBuilderModel; import io.nop.batch.dsl.model.BatchConsumerModel; +import io.nop.batch.dsl.model.BatchGeneratorModel; import io.nop.batch.dsl.model.BatchListenersModel; import io.nop.batch.dsl.model.BatchLoaderModel; import io.nop.batch.dsl.model.BatchOrmWriterModel; import io.nop.batch.dsl.model.BatchProcessorModel; import io.nop.batch.dsl.model.BatchTaggerModel; import io.nop.batch.dsl.model.BatchTaskModel; +import io.nop.batch.gen.loader.BatchGenLoaderProvider; import io.nop.commons.collections.OrderByComparator; import io.nop.commons.util.CollectionHelper; import io.nop.commons.util.retry.IRetryPolicy; +import io.nop.core.lang.eval.IEvalAction; import io.nop.core.lang.eval.IEvalFunction; import io.nop.core.reflect.bean.BeanTool; +import io.nop.core.resource.VirtualFileSystem; import io.nop.dao.api.IDaoProvider; import io.nop.dao.api.INamedSqlBuilder; import io.nop.dao.jdbc.IJdbcTemplate; @@ -93,6 +97,7 @@ public IBatchTaskBuilder newTaskBuilder(IBeanProvider beanContainer) { builder.taskKeyExpr(batchTaskModel.getTaskKeyExpr()); builder.allowStartIfComplete(batchTaskModel.getAllowStartIfComplete()); builder.startLimit(batchTaskModel.getStartLimit()); + builder.useBatchRequestGenerator(batchTaskModel.isUseBatchRequestGenerator()); builder.batchSize(batchTaskModel.getBatchSize()); if (batchTaskModel.getJitterRatio() != null) @@ -257,6 +262,8 @@ private IBatchLoaderProvider buildLoader0(BatchLoaderModel loaderModel, return newJdbcReader(loaderModel.getJdbcReader(), beanProvider, jdbcTemplate, sqlLibManager); } else if (loaderModel.getOrmReader() != null) { return newOrmReader(loaderModel.getOrmReader(), daoProvider); + } else if (loaderModel.getGenerator() != null) { + return newGenerator(loaderModel.getGenerator()); } else if (loaderModel.getSource() != null) { return context -> (batchSize, ctx) -> (List) loaderModel.getSource().call2(null, batchSize, ctx, ctx.getEvalScope()); @@ -265,6 +272,16 @@ private IBatchLoaderProvider buildLoader0(BatchLoaderModel loaderModel, } } + private IBatchLoaderProvider newGenerator(BatchGeneratorModel generatorModel) { + String genModelPath = generatorModel.getGenModelPath(); + IEvalAction totalCountExpr = generatorModel.getTotalCountExpr(); + BatchGenLoaderProvider provider = new BatchGenLoaderProvider<>(); + provider.setTotalCount(totalCountExpr); + provider.setResourcePath(genModelPath); + provider.setResourceLoader(VirtualFileSystem.instance()); + return provider; + } + private IBatchAggregator> loadAggregator(String beanName, IBeanProvider beanContainer) { if (beanName == null) return null; diff --git a/nop-batch/nop-batch-dsl/src/main/java/io/nop/batch/dsl/model/BatchGeneratorModel.java b/nop-batch/nop-batch-dsl/src/main/java/io/nop/batch/dsl/model/BatchGeneratorModel.java new file mode 100644 index 000000000..39eadbc14 --- /dev/null +++ b/nop-batch/nop-batch-dsl/src/main/java/io/nop/batch/dsl/model/BatchGeneratorModel.java @@ -0,0 +1,9 @@ +package io.nop.batch.dsl.model; + +import io.nop.batch.dsl.model._gen._BatchGeneratorModel; + +public class BatchGeneratorModel extends _BatchGeneratorModel{ + public BatchGeneratorModel(){ + + } +} diff --git a/nop-batch/nop-batch-dsl/src/main/java/io/nop/batch/dsl/model/_gen/_BatchFileReaderModel.java b/nop-batch/nop-batch-dsl/src/main/java/io/nop/batch/dsl/model/_gen/_BatchFileReaderModel.java index ec7dfbd4e..41c955a38 100644 --- a/nop-batch/nop-batch-dsl/src/main/java/io/nop/batch/dsl/model/_gen/_BatchFileReaderModel.java +++ b/nop-batch/nop-batch-dsl/src/main/java/io/nop/batch/dsl/model/_gen/_BatchFileReaderModel.java @@ -53,10 +53,10 @@ public abstract class _BatchFileReaderModel extends io.nop.core.resource.compone /** * - * xml name: maxCount - * 读取的最大记录数,缺省值为-1,表示不限制 + * xml name: maxCountExpr + * */ - private java.lang.Long _maxCount ; + private io.nop.core.lang.eval.IEvalAction _maxCountExpr ; /** * @@ -176,19 +176,19 @@ public void setHeaders(java.util.Set value){ /** * - * xml name: maxCount - * 读取的最大记录数,缺省值为-1,表示不限制 + * xml name: maxCountExpr + * */ - public java.lang.Long getMaxCount(){ - return _maxCount; + public io.nop.core.lang.eval.IEvalAction getMaxCountExpr(){ + return _maxCountExpr; } - public void setMaxCount(java.lang.Long value){ + public void setMaxCountExpr(io.nop.core.lang.eval.IEvalAction value){ checkAllowChange(); - this._maxCount = value; + this._maxCountExpr = value; } @@ -270,7 +270,7 @@ protected void outputJson(IJsonHandler out){ out.putNotNull("filePath",this.getFilePath()); out.putNotNull("filter",this.getFilter()); out.putNotNull("headers",this.getHeaders()); - out.putNotNull("maxCount",this.getMaxCount()); + out.putNotNull("maxCountExpr",this.getMaxCountExpr()); out.putNotNull("newRecordInputProvider",this.getNewRecordInputProvider()); out.putNotNull("resourceIO",this.getResourceIO()); out.putNotNull("resourceLoader",this.getResourceLoader()); @@ -290,7 +290,7 @@ protected void copyTo(BatchFileReaderModel instance){ instance.setFilePath(this.getFilePath()); instance.setFilter(this.getFilter()); instance.setHeaders(this.getHeaders()); - instance.setMaxCount(this.getMaxCount()); + instance.setMaxCountExpr(this.getMaxCountExpr()); instance.setNewRecordInputProvider(this.getNewRecordInputProvider()); instance.setResourceIO(this.getResourceIO()); instance.setResourceLoader(this.getResourceLoader()); diff --git a/nop-batch/nop-batch-dsl/src/main/java/io/nop/batch/dsl/model/_gen/_BatchGeneratorModel.java b/nop-batch/nop-batch-dsl/src/main/java/io/nop/batch/dsl/model/_gen/_BatchGeneratorModel.java new file mode 100644 index 000000000..603b122ab --- /dev/null +++ b/nop-batch/nop-batch-dsl/src/main/java/io/nop/batch/dsl/model/_gen/_BatchGeneratorModel.java @@ -0,0 +1,107 @@ +package io.nop.batch.dsl.model._gen; + +import io.nop.commons.collections.KeyedList; //NOPMD NOSONAR - suppressed UnusedImports - Used for List Prop +import io.nop.core.lang.json.IJsonHandler; +import io.nop.batch.dsl.model.BatchGeneratorModel; +import io.nop.commons.util.ClassHelper; + + + +// tell cpd to start ignoring code - CPD-OFF +/** + * generate from /nop/schema/task/batch.xdef

+ * + */ +@SuppressWarnings({"PMD.UselessOverridingMethod","PMD.UnusedLocalVariable", + "PMD.UnnecessaryFullyQualifiedName","PMD.EmptyControlStatement","java:S116","java:S101","java:S1128","java:S1161"}) +public abstract class _BatchGeneratorModel extends io.nop.core.resource.component.AbstractComponentModel { + + /** + * + * xml name: genModelPath + * + */ + private java.lang.String _genModelPath ; + + /** + * + * xml name: totalCountExpr + * + */ + private io.nop.core.lang.eval.IEvalAction _totalCountExpr ; + + /** + * + * xml name: genModelPath + * + */ + + public java.lang.String getGenModelPath(){ + return _genModelPath; + } + + + public void setGenModelPath(java.lang.String value){ + checkAllowChange(); + + this._genModelPath = value; + + } + + + /** + * + * xml name: totalCountExpr + * + */ + + public io.nop.core.lang.eval.IEvalAction getTotalCountExpr(){ + return _totalCountExpr; + } + + + public void setTotalCountExpr(io.nop.core.lang.eval.IEvalAction value){ + checkAllowChange(); + + this._totalCountExpr = value; + + } + + + + @Override + public void freeze(boolean cascade){ + if(frozen()) return; + super.freeze(cascade); + + if(cascade){ //NOPMD - suppressed EmptyControlStatement - Auto Gen Code + + } + } + + @Override + protected void outputJson(IJsonHandler out){ + super.outputJson(out); + + out.putNotNull("genModelPath",this.getGenModelPath()); + out.putNotNull("totalCountExpr",this.getTotalCountExpr()); + } + + public BatchGeneratorModel cloneInstance(){ + BatchGeneratorModel instance = newInstance(); + this.copyTo(instance); + return instance; + } + + protected void copyTo(BatchGeneratorModel instance){ + super.copyTo(instance); + + instance.setGenModelPath(this.getGenModelPath()); + instance.setTotalCountExpr(this.getTotalCountExpr()); + } + + protected BatchGeneratorModel newInstance(){ + return (BatchGeneratorModel) ClassHelper.newInstance(getClass()); + } +} + // resume CPD analysis - CPD-ON diff --git a/nop-batch/nop-batch-dsl/src/main/java/io/nop/batch/dsl/model/_gen/_BatchLoaderModel.java b/nop-batch/nop-batch-dsl/src/main/java/io/nop/batch/dsl/model/_gen/_BatchLoaderModel.java index 62c7119dc..b6af4eb95 100644 --- a/nop-batch/nop-batch-dsl/src/main/java/io/nop/batch/dsl/model/_gen/_BatchLoaderModel.java +++ b/nop-batch/nop-batch-dsl/src/main/java/io/nop/batch/dsl/model/_gen/_BatchLoaderModel.java @@ -44,6 +44,13 @@ public abstract class _BatchLoaderModel extends io.nop.batch.dsl.model.BatchList */ private io.nop.batch.dsl.model.BatchFileReaderModel _fileReader ; + /** + * + * xml name: generator + * + */ + private io.nop.batch.dsl.model.BatchGeneratorModel _generator ; + /** * * xml name: jdbc-reader @@ -148,6 +155,25 @@ public void setFileReader(io.nop.batch.dsl.model.BatchFileReaderModel value){ } + /** + * + * xml name: generator + * + */ + + public io.nop.batch.dsl.model.BatchGeneratorModel getGenerator(){ + return _generator; + } + + + public void setGenerator(io.nop.batch.dsl.model.BatchGeneratorModel value){ + checkAllowChange(); + + this._generator = value; + + } + + /** * * xml name: jdbc-reader @@ -234,6 +260,8 @@ public void freeze(boolean cascade){ this._fileReader = io.nop.api.core.util.FreezeHelper.deepFreeze(this._fileReader); + this._generator = io.nop.api.core.util.FreezeHelper.deepFreeze(this._generator); + this._jdbcReader = io.nop.api.core.util.FreezeHelper.deepFreeze(this._jdbcReader); this._ormReader = io.nop.api.core.util.FreezeHelper.deepFreeze(this._ormReader); @@ -249,6 +277,7 @@ protected void outputJson(IJsonHandler out){ out.putNotNull("aggregator",this.getAggregator()); out.putNotNull("bean",this.getBean()); out.putNotNull("fileReader",this.getFileReader()); + out.putNotNull("generator",this.getGenerator()); out.putNotNull("jdbcReader",this.getJdbcReader()); out.putNotNull("ormReader",this.getOrmReader()); out.putNotNull("saveState",this.getSaveState()); @@ -268,6 +297,7 @@ protected void copyTo(BatchLoaderModel instance){ instance.setAggregator(this.getAggregator()); instance.setBean(this.getBean()); instance.setFileReader(this.getFileReader()); + instance.setGenerator(this.getGenerator()); instance.setJdbcReader(this.getJdbcReader()); instance.setOrmReader(this.getOrmReader()); instance.setSaveState(this.getSaveState()); diff --git a/nop-batch/nop-batch-dsl/src/main/java/io/nop/batch/dsl/model/_gen/_BatchTaskModel.java b/nop-batch/nop-batch-dsl/src/main/java/io/nop/batch/dsl/model/_gen/_BatchTaskModel.java index cd04d7b3a..8d4aee9cd 100644 --- a/nop-batch/nop-batch-dsl/src/main/java/io/nop/batch/dsl/model/_gen/_BatchTaskModel.java +++ b/nop-batch/nop-batch-dsl/src/main/java/io/nop/batch/dsl/model/_gen/_BatchTaskModel.java @@ -185,6 +185,13 @@ public abstract class _BatchTaskModel extends io.nop.core.resource.component.Abs */ private io.nop.batch.core.BatchTransactionScope _transactionScope ; + /** + * + * xml name: useBatchRequestGenerator + * + */ + private boolean _useBatchRequestGenerator = false; + /** * * xml name: allowStartIfComplete @@ -720,6 +727,25 @@ public void setTransactionScope(io.nop.batch.core.BatchTransactionScope value){ } + /** + * + * xml name: useBatchRequestGenerator + * + */ + + public boolean isUseBatchRequestGenerator(){ + return _useBatchRequestGenerator; + } + + + public void setUseBatchRequestGenerator(boolean value){ + checkAllowChange(); + + this._useBatchRequestGenerator = value; + + } + + @Override public void freeze(boolean cascade){ @@ -777,6 +803,7 @@ protected void outputJson(IJsonHandler out){ out.putNotNull("taskName",this.getTaskName()); out.putNotNull("taskVersion",this.getTaskVersion()); out.putNotNull("transactionScope",this.getTransactionScope()); + out.putNotNull("useBatchRequestGenerator",this.isUseBatchRequestGenerator()); } public BatchTaskModel cloneInstance(){ @@ -812,6 +839,7 @@ protected void copyTo(BatchTaskModel instance){ instance.setTaskName(this.getTaskName()); instance.setTaskVersion(this.getTaskVersion()); instance.setTransactionScope(this.getTransactionScope()); + instance.setUseBatchRequestGenerator(this.isUseBatchRequestGenerator()); } protected BatchTaskModel newInstance(){ diff --git a/nop-batch/nop-batch-gen/src/main/java/io/nop/batch/gen/BatchGenConstants.java b/nop-batch/nop-batch-gen/src/main/java/io/nop/batch/gen/BatchGenConstants.java index dfcd92dc7..7b7bfc450 100644 --- a/nop-batch/nop-batch-gen/src/main/java/io/nop/batch/gen/BatchGenConstants.java +++ b/nop-batch/nop-batch-gen/src/main/java/io/nop/batch/gen/BatchGenConstants.java @@ -7,12 +7,18 @@ */ package io.nop.batch.gen; +import io.nop.batch.core.BatchConstants; + public interface BatchGenConstants { String MODEL_TYPE_BATCH_GEN = "batch-gen"; String FILE_TYPE_GEN_JSON = "batch-gen.json"; String FILE_TYPE_GEN_JSON5 = "batch-gen.json5"; String FILE_TYPE_GEN_YAML = "batch-gen.yaml"; - String VAR_CHUNK_CONTEXT = "chunkContext"; + String POSTFIX_BATCH_GEN_XLSX = ".batch-gen.xlsx"; + + String XDSL_BATCH_GEN_IMP_PATH = "/nop/batch/imp/batch-gen.imp.xml"; + + String VAR_CHUNK_CONTEXT = BatchConstants.VAR_BATCH_CHUNK_CTX; String VAR_CHUNK_RESPONSE = "response"; } diff --git a/nop-batch/nop-batch-gen/src/main/java/io/nop/batch/gen/loader/BatchGenLoader.java b/nop-batch/nop-batch-gen/src/main/java/io/nop/batch/gen/loader/BatchGenLoaderProvider.java similarity index 80% rename from nop-batch/nop-batch-gen/src/main/java/io/nop/batch/gen/loader/BatchGenLoader.java rename to nop-batch/nop-batch-gen/src/main/java/io/nop/batch/gen/loader/BatchGenLoaderProvider.java index 013a857e4..988ecf6d2 100644 --- a/nop-batch/nop-batch-gen/src/main/java/io/nop/batch/gen/loader/BatchGenLoader.java +++ b/nop-batch/nop-batch-gen/src/main/java/io/nop/batch/gen/loader/BatchGenLoaderProvider.java @@ -7,6 +7,8 @@ */ package io.nop.batch.gen.loader; +import io.nop.api.core.convert.ConvertHelper; +import io.nop.api.core.exceptions.NopException; import io.nop.batch.core.IBatchChunkContext; import io.nop.batch.core.IBatchLoaderProvider; import io.nop.batch.core.IBatchTaskContext; @@ -16,24 +18,21 @@ import io.nop.batch.gen.generator.BatchGenState; import io.nop.batch.gen.model.BatchGenModel; import io.nop.batch.gen.model.BatchGenModelParser; +import io.nop.core.lang.eval.IEvalAction; import java.util.ArrayList; import java.util.List; -public class BatchGenLoader extends AbstractBatchResourceHandler +public class BatchGenLoaderProvider extends AbstractBatchResourceHandler implements IBatchLoaderProvider { /** * 总共生成多少条记录 */ - private long totalCount; + private IEvalAction totalCountExpr; - public long getTotalCount() { - return totalCount; - } - - public void setTotalCount(long totalCount) { - this.totalCount = totalCount; + public void setTotalCount(IEvalAction totalCountExpr) { + this.totalCountExpr = totalCountExpr; } static class LoaderState { @@ -44,6 +43,8 @@ static class LoaderState { @Override public IBatchLoader setup(IBatchTaskContext context) { + long totalCount = ConvertHelper.toPrimitiveLong(totalCountExpr.invoke(context), NopException::new); + LoaderState state = new LoaderState(); state.genModel = new BatchGenModelParser().parseFromResource(getResource(context)); state.genState = new BatchGenState(state.genModel, totalCount); diff --git a/nop-batch/nop-batch-gen/src/main/java/io/nop/batch/gen/model/BatchGenModel.java b/nop-batch/nop-batch-gen/src/main/java/io/nop/batch/gen/model/BatchGenModel.java index f3540cc6d..f05c85187 100644 --- a/nop-batch/nop-batch-gen/src/main/java/io/nop/batch/gen/model/BatchGenModel.java +++ b/nop-batch/nop-batch-gen/src/main/java/io/nop/batch/gen/model/BatchGenModel.java @@ -9,6 +9,8 @@ import io.nop.api.core.annotations.data.DataBean; import io.nop.api.core.beans.TreeBean; +import io.nop.api.core.util.INeedInit; +import io.nop.core.lang.json.delta.JsonMerger; import io.nop.core.resource.component.AbstractComponentModel; import io.nop.core.type.IGenericType; @@ -19,7 +21,7 @@ * 数据生成模型。用于按照指定的数据分布比例来批量生成一批测试数据。 */ @DataBean -public class BatchGenModel extends AbstractComponentModel implements IBatchGenCaseModel { +public class BatchGenModel extends AbstractComponentModel implements IBatchGenCaseModel, INeedInit { private String name; @@ -109,4 +111,44 @@ public List getSubCases() { public void setSubCases(List subCases) { this.subCases = subCases; } + + private boolean inited = false; + + @Override + public void init() { + if (inited) + return; + inited = true; + for (BatchGenCaseModel subCase : getSubCases()) { + mergeWithParent(subCase, this); + } + } + + void mergeWithParent(BatchGenCaseModel subCase, IBatchGenCaseModel parent) { + if (subCase.isInheritParent()) { + Map template = mergeMap(parent.getMergedTemplate(), subCase.getTemplate()); + subCase.setMergedTemplate(template); + + Map outputVars = mergeMap(parent.getMergedOutputVars(), subCase.getOutputVars()); + subCase.setMergedOutputVars(outputVars); + } else { + subCase.setMergedTemplate(subCase.getTemplate()); + subCase.setMergedOutputVars(subCase.getOutputVars()); + } + + if (subCase.getSubCases() != null) { + subCase.getSubCases().forEach(sub -> { + mergeWithParent(sub, subCase); + }); + } + } + + Map mergeMap(Map m1, Map m2) { + if (m1 == null || m1.isEmpty()) + return m2; + if (m2 == null || m1.isEmpty()) + return m1; + + return (Map) JsonMerger.instance().merge(m1, m2); + } } \ No newline at end of file diff --git a/nop-batch/nop-batch-gen/src/main/java/io/nop/batch/gen/model/BatchGenModelParser.java b/nop-batch/nop-batch-gen/src/main/java/io/nop/batch/gen/model/BatchGenModelParser.java index e124497c4..32dfa94cb 100644 --- a/nop-batch/nop-batch-gen/src/main/java/io/nop/batch/gen/model/BatchGenModelParser.java +++ b/nop-batch/nop-batch-gen/src/main/java/io/nop/batch/gen/model/BatchGenModelParser.java @@ -7,55 +7,31 @@ */ package io.nop.batch.gen.model; +import io.nop.batch.gen.BatchGenConstants; import io.nop.core.lang.eval.EvalExprProvider; import io.nop.core.lang.json.DeltaJsonOptions; import io.nop.core.lang.json.JsonTool; -import io.nop.core.lang.json.delta.JsonMerger; import io.nop.core.resource.IResource; import io.nop.core.resource.component.parse.AbstractResourceParser; - -import java.util.Map; +import io.nop.xlang.xdsl.DslModelHelper; public class BatchGenModelParser extends AbstractResourceParser { @Override protected BatchGenModel doParseResource(IResource resource) { + if (resource.getName().endsWith(BatchGenConstants.POSTFIX_BATCH_GEN_XLSX)) { + BatchGenModel model = (BatchGenModel) DslModelHelper.newExcelModelLoader( + BatchGenConstants.XDSL_BATCH_GEN_IMP_PATH).parseFromResource(resource); + model.init(); + return model; + } + DeltaJsonOptions options = new DeltaJsonOptions(); options.setIgnoreUnknownValueResolver(false); options.setEvalContext(EvalExprProvider.newEvalScope()); BatchGenModel model = JsonTool.loadDeltaBean(resource, BatchGenModel.class, options); - - for (BatchGenCaseModel subCase : model.getSubCases()) { - mergeWithParent(subCase, model); - } + model.init(); return model; } - void mergeWithParent(BatchGenCaseModel subCase, IBatchGenCaseModel parent) { - if (subCase.isInheritParent()) { - Map template = mergeMap(parent.getMergedTemplate(), subCase.getTemplate()); - subCase.setMergedTemplate(template); - - Map outputVars = mergeMap(parent.getMergedOutputVars(), subCase.getOutputVars()); - subCase.setMergedOutputVars(outputVars); - } else { - subCase.setMergedTemplate(subCase.getTemplate()); - subCase.setMergedOutputVars(subCase.getOutputVars()); - } - - if (subCase.getSubCases() != null) { - subCase.getSubCases().forEach(sub -> { - mergeWithParent(sub, subCase); - }); - } - } - - Map mergeMap(Map m1, Map m2) { - if (m1 == null || m1.isEmpty()) - return m2; - if (m2 == null || m1.isEmpty()) - return m1; - - return (Map) JsonMerger.instance().merge(m1, m2); - } } diff --git a/nop-batch/nop-batch-gen/src/main/resources/_vfs/nop/batch/imp/batch-gen.imp.xml b/nop-batch/nop-batch-gen/src/main/resources/_vfs/nop/batch/imp/batch-gen.imp.xml index e53bb9376..c4da72817 100644 --- a/nop-batch/nop-batch-gen/src/main/resources/_vfs/nop/batch/imp/batch-gen.imp.xml +++ b/nop-batch/nop-batch-gen/src/main/resources/_vfs/nop/batch/imp/batch-gen.imp.xml @@ -1,229 +1,115 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - value == 'M' || value == 'Y' || value == true || value == 'true' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - + function normalizeCase(subCase){ + subCase.subCases = subCase.subCases?.map(caseItem=>{ + caseItem.subCases?.map(caseName=>{ + return $.notNull(subCaseMap[caseName],"invalid caseName:"+caseName); + }); + caseItem.removeProp('subCases'); + }); + } - - - + rootRecord.subCases.$('sss')?.forEach(subCase=>{ + subCaseMap[subCase.name] = subCase; + }); - - - + rootRecord.subCaseMap?.forEach(normalizeCase); - - - - - - - + rootRecord.removeProp('subCaseMap'); - - - - ]]> - + + + - - + + - - + + - - + + - - + + - - + + - + - + - - - - - value == 'M' || value == 'Y' || value == true || value == 'true' - - + + - + - - - - - - - - - - - - - - - - - - - - - - - + - - + + - - + + - + - - + + - - + + - - + + + + + + - - - + - - - + - - - + + + - + + diff --git a/nop-bom/pom.xml b/nop-bom/pom.xml index 430c0c7d1..d342b998d 100644 --- a/nop-bom/pom.xml +++ b/nop-bom/pom.xml @@ -564,6 +564,12 @@ ${nop-entropy.version} + + io.github.entropy-cloud + nop-batch-dsl + ${nop-entropy.version} + + io.github.entropy-cloud nop-batch-orm diff --git a/nop-cli-core/pom.xml b/nop-cli-core/pom.xml index 7420d5500..a7d449276 100644 --- a/nop-cli-core/pom.xml +++ b/nop-cli-core/pom.xml @@ -100,6 +100,11 @@ nop-task-core + + io.github.entropy-cloud + nop-batch-dsl + + diff --git a/nop-cli-core/src/test/java/io/nop/cli/TestNopCli.java b/nop-cli-core/src/test/java/io/nop/cli/TestNopCli.java index 774fa1f91..b90b53b54 100644 --- a/nop-cli-core/src/test/java/io/nop/cli/TestNopCli.java +++ b/nop-cli-core/src/test/java/io/nop/cli/TestNopCli.java @@ -8,17 +8,21 @@ package io.nop.cli; import io.nop.api.core.config.AppConfig; +import io.nop.core.CoreConfigs; import io.nop.core.initialize.CoreInitialization; +import io.nop.core.unittest.BaseTestCase; import io.quarkus.test.junit.QuarkusTest; import jakarta.inject.Inject; import org.junit.jupiter.api.Test; import picocli.CommandLine; +import java.io.File; + import static io.nop.codegen.CodeGenConfigs.CFG_CODEGEN_TRACE_ENABLED; import static org.junit.jupiter.api.Assertions.assertEquals; @QuarkusTest -public class TestNopCli { +public class TestNopCli extends BaseTestCase { @Inject CommandLine.IFactory factory; @@ -76,4 +80,17 @@ public void testRunTask(){ int ret = app.run(args); assertEquals(0, ret); } + + @Test + public void testRunBatchDemo(){ + CoreInitialization.destroy(); + File file = new File(getModuleDir(),"../nop-cli/demo/_vfs"); + System.setProperty(CoreConfigs.CFG_RESOURCE_DIR_OVERRIDE_VFS.getName(), file.getAbsolutePath()); + String[] args = new String[]{"run-task", "v:/batch/batch-demo.task.xml","-i","{totalCount:250,taskKey:'abc'}"}; + NopCliApplication app = new NopCliApplication(); + app.setFactory(factory); + int ret = app.run(args); + assertEquals(0, ret); + System.getProperties().remove(CoreConfigs.CFG_RESOURCE_DIR_OVERRIDE_VFS.getName()); + } } diff --git a/nop-cli/demo/_vfs/app/demo/_module b/nop-cli/demo/_vfs/app/demo/_module new file mode 100644 index 000000000..e69de29bb diff --git a/nop-cli/demo/_vfs/app/demo/orm/app.orm.xml b/nop-cli/demo/_vfs/app/demo/orm/app.orm.xml index c9951e75d..46b1443f6 100644 --- a/nop-cli/demo/_vfs/app/demo/orm/app.orm.xml +++ b/nop-cli/demo/_vfs/app/demo/orm/app.orm.xml @@ -1,7 +1,8 @@ + orm:forceDynamicEntity="true" + xmlns:i18n-en="i18n-en" xmlns:orm-gen="orm-gen" xmlns:xpl="xpl" xmlns:orm="orm"> - + \ No newline at end of file diff --git a/nop-cli/demo/_vfs/app/demo/orm/demo.orm.xlsx b/nop-cli/demo/_vfs/app/demo/orm/demo.orm.xlsx index 60fa43035b75996981eaec933248ba8760ede955..58570f48c2f1b5888ce32bc49f94ab4c53c95300 100644 GIT binary patch delta 21741 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 delta 17065 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 + + + + + + + + taskKey + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/nop-cli/demo/_vfs/batch/create-card.batch-gen.xlsx b/nop-cli/demo/_vfs/batch/create-card.batch-gen.xlsx index 74b575e3057b49641c234a2f0e802bb2ef5f7f8e..10eb92f6471710bd5ca6bcb4bb63f9bd82117a5c 100644 GIT binary patch delta 6932 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 AyZ`_I delta 6800 zcmZXZWl)^Wwzda%7+`RB2{I7eEikx4fZ!I~2|oBR39iAN;6u;=f#4D}!CeLk5Q2w5 zkdJqtU3-70PXFreXVt3es($Wkt-FWp9&H+?F`-hwEFltT000vf06+u)00Lb3-*|Yt z*m!uj@CCTJ)*6nvuZV%xDBeSXpNy3bd6~Semk6ng=+NDk-SN?dUZ^Os>MH7fjAHzY z(s597MBbFk;PRKIE4@_eP_u%re_PR5O6q#uXS9ERrK6Fr{V>{9P>B%S7|?IWlyiaK z1(lX=w6*ig5y7al>woS$D0vxjKHSuwU_kMVi=PMZx&uGMw5e$WkNXvMXK}%|ISJzW}mbw4HPJ#FdlE2U^y6T*J(|_)m zG60&JSi1IQdkLA66dKl&dS{YDRL<0`y#t2TV!n)Uf9}UU>BW9<6GHt?-JGg$8b-|f z3AtA?x@Br`x2DxW?zST9?%y5TW)BI9fPycJC?3AOX2|?Ln^;yf}HL zDgNN{+H;xvaTsJ7A*%5wzO$81G^W%Ax^5QR1q;WUuw`N(cF%u$ukjiiG|ytxwkwuO5yu?gW+yLJn`}i3Nj8Y?R#`+h<8k%boe*PYIFvu|Z<0@>XP$M?@uGj| z;{4_G3b|6~c3YV9&EJ+*1hyvGE4tS8a(s5Vn?jRGl&TRYw*UO_%=bCv2K1dm7S*}u zw!H4~de`RmXY?V~cxHX4ufpSkzWEFAJAC# zQOdVoVmK>$2rF`hJYMduY%qMe|2wrX#jpty#X{h;s3(`}HDyTf#d#fXl~5i3S@Ol* zSmnp2=kbK?B&`UT3UE@*5SW-~V6V@SZX9Oi7tksYIg@RYpPdQy!99>MSb+(o3yGmq zEshVX=IGySx<}B`jD7OU9*2u1K$;Tk`my$^88U|H<~_&1a}YkBWZSZ0=u+~-EG~|( zgl|xq_;9sQabkHfi?uKTfRePoB-Ckz_*hW$q!KVF_C$2)Q7(wTIXqlB@5ki(d^>)T zCd-8ukW<;L+mW%z#m2P`h(n+=M|D8|^~S#Ru{bzTY-D-G%Prd3TPY-7Y8`AC=a5m# zut5=pA2{^AqNv3=ATe>Z2zD?`l-Bj#Mc>O^no)>dC8H(R3rK0?rJ8^c`5ck0=LA*d zT|G3k63LnAgWJ(XQ*uOf`T9?c=L<)g>$|V2xD^??4yq7FORRtyPI!lGKyhN=Cjh#Gf%|t6cQD z((_|_W!goF2T~I{jM@~~YnST2J$sg;g^NPw{$|(7V(+!`kmU#vV+QV0R2X?Ok^9l{ z;G6~_-bAZ`eZKtgio3;~LZ+VuAA%%rhJ3AEJk$PM?{leOh1hHJaT>NFzNCkG3_E^_ zIXXZrKMPQ8YYEl;JN2rT8*eCuh(F%tt(RBGPON-ZWIIF0Y<4vY>SF=u_z2TnnaI4O zq`@%JM)s`&E%D1=AL~A$^ z^m=^-De=TRAu+w&mL^%l-nm9 z_C_Z*lWe;!Lujwxz01mrwA)hM9Ze*(DUDu`vPV`@$Dx;mzh+F}u-=}J_){?%VME2* z#X^HeKJ>vV^UN{Q#ApIKZu-0_lJ))WC?Ahojpfjj|9vKcWO4{r*#}lt^SxC~i1|~D z#&(jU`D%j??J4{GfhyrB1Ip zGdJ|*Ri$pPR<+U~yE*I}H^kfX6#EP#u!hvkB&K9VywP%>A55u+l(bEkI1`fHx zXXRLdyQ(}{%cOezG)WMbwLXo1jz60OZzEejEOe!C(ig2dc)AwUDN1EPCnW-%5^`Nw zrM8YU{t2~g*N~ye*93x5;ly1RVxco-kwj`ZdID+*m+Hd!O4F7|GR4eHKt^XPY#of3 zDIC4vI!Z8;@Kz~O8Rc90rxTL_vnyFZ+Bz{gbdR_XJ2{+4p-8VyE^VurghS*{IF$KyR^-inOYV@jk|ZO4S4I zvx0PSxz**j>2tqlm{jMCq55krYnHzyx4F=>QV|rwWVeSn~-pb5EFIgr=K$EQ3MA8yv@yN?{yuy0lzK z4X$b3GJSr82;)l^s`EMIATnOBq1Y(()9!d=dD|xm z4I+BE910d{h!>nO;y9KdZBOvyw2`K+n=TW zjV!xndFyx+r-ehcOSI%G#P&i^y$N+{+%RjbAlhPGNRjBQPAf!#{6w-ulILDd@CquH2BQ}8EEXe-*~~5#8itd>rJ|S- zTPT5)Oky8+W-h%`1K!Jsb)_dC<-AS8n-*8BU`l)kjb}N}z5Xv@^0JP+|3#RXGc-9@ z&v1OOzN`BAE*s@nq5E%Tq_3B|L)l#XR??V|52JQ647}e{nmeJ(=7s!OW}zMVgq>{* z8%=GYnT}0IQ!`_RkJsrGOFy-{CGrcqAL{rQE_QcYr|9>d5aaL2@?bM-+CJ%UJ7$Qy zR_r$W%;5j-xoV*eHActJa1_8v$YK$^a3C z1x2wXVe&L*K-ab1w$No0a&Fpf!h+z}X(L1073?JKSW18tIZ*OE$^F8~b!xjK*_*Xe z#+2a9qG0vC^`?iV&v~*AO&5rx<11K4$fHQ(6k#<^8SK#foYXNmh;{V)%1dH+JR1oW z-v8wJhX-wCBmGrP=%HqzJ7(PvL`V%*e`a;4KXcbWAw>Nxa~_1bcNH@Z@DH1KlL;Z( zc4YBQWzk2bS{?PuT~mEdVy4NkG;@@yCvKnvagz7FCv7lk;tor+x zDjM%YTkjfX;9oLaOET^&XS6D1$8VMsu6c>DBGH`W3!-;}<#PlU63ori?RaQd4qk0m zCfHgBwE;vsnNZ;~9hapu(7{(@96@fV;)uYHiC-cXe%spMc4D>AoOJEUc{1`sx*UuZ zQ`paQAUiF1A;T_hfD=D-5`(`^Q-fnM4#DJz|`eM$GR|U}a6wp(RVHlxU4E zQSOL>NGCI~S22(l^4HH1g-u^DR&Q1~3AIZWQR_xQ7*pdwPk4dCDpiD7IfY~2M8Dba z%MjlUni%a&wBKKL=_8#;@Ky>}M%5Ki-5<~W_;F_HU3(a>hB}*}dv4y&=%}EtW{%(G zPID%&G0bpE^_TeUn!=mNNEtoVj8Daub zyv(kC`!Pem$@p$a6&{-;4K2}4gbPnzaPwUjyYU%vzxzpn7Zu7kM&~PeaKDesJ$P!` zn}X!P;2ju4i1D+PZ>f{TY;@to$c`~nkme=AIL(_w<>x&qB{)9L}I6lY* zn8VZ=C?_5H6{dXSiiImN*amDjmi7sNN>eNe>ZK}6ny<(IP+i$ORLklMxelLqtl5=_QA@4$1RZ5NRl@nBWaQjT` zI*b#YtjI5-8v^YfF2OCB6}pfC5+(8el=i-u8|SC>ooZXgIJOserwoKL*`CpO6RoNs zOZrGgraiTZRLFD3S4C79mE}jJi=1oSr}28V89&x}rK#W;`~lnux%!Ej^nm~x_t!9y zw=}AZH(*tFD@hB-<8m>bR1ds6Ncvys_!;M}2D$QhmWFZ#mRtr->aCaK`a`%D52zqJ>9E5P}T1)|9IMgg@<0-Mu6YXQyR zh;mtRBim_U0WB_gfwPd-jCb#oOBIhF`FnH6e6l{apk_jS>|09xAVlBAzAx#`+vTxY zt1;J{QPCcfhOB4v=CY{Lvq^^O#tCQGx`K{W4n5m1AtW`LOtI|sah*^NCfVg%?ql9T zi$}%@O#VMxG0rsnkHRCB7u+S; zmZ7|8W+gIqTzt!g+lkPao&bKGhj4YV$^+Z%P;6R1?vvB+_N?Y7HdcNwL&i+AMz+mb zJC%|@FgDz#pYmiLHw=2EA|Dr#obADRtm5qL`LP$*Wh)-rt8+^e+Vam-WIXdC4fxJ` zyzohGFcY2m>7m?kBKt1F#jLmiX{!Cf`z{U{@KUx%$?Xu~n@gxsRgpgD;;SD7hR}rZ zFS4Px`_~($u%4w<9ZoePQC~ymm2p6}>QXwL@0M0|Y9}AWwYg`gU=ze!6|(p_etho+ zD=*m|Q{z?AZHkn4)e9wui#wyaU#r~`_x-(p-B2(@|A=NrD(&&VNjp0I?S5#2<|x>3 zM^9J_e!prO0euc_@5+x&++V$0=Dw3H{fYRZ-?Q6)1;dC*LMyRwC3xsQcsFiT8i_dJ z19T=xU*1k0_4w5~O?mNtNy3~j+Q6%I+6-z7g?dwHM3MjA?3qGKJnoo-VPeQos-;@1 zEiN0o5VZvNs!rd1W&YX1@pF%5`j6$my54_Rzoh>uf8C1gvyGlS%q!fq2{sz2k-5ir z9m;NwE!n);08w6h25MAJElrL1oH)1zuV%1F&a}SkyOj5Ax%x(Hlmp^x6}j2pdI@YV zY?;9~0>ASbpFKvx=z-fY6a`*}>q+~e>uvj?TBQ{9hfb$}oJ@zJ_l)@jH~{8B9bq9- z(qF8I0SY`_M2Lg0k1wvD3UrYcP*uq0I{mO1II^E5Yx#Tq$SLN8e2@4wWl?F`( z-^wLIoW+!i^lNoSb7<&B%Kp9R#s_yvm@vh++@4Jkof5U!HN$ z=EDT6!J@z1yzQZ?scC$0XPW6k_@oHlmi5VV{u%J}(!`vmSH5|;k({uXSUAooAI z^cZLWPA)2@_uUoB;mJU#a!8tMwQ9#Z^LMk)chiMF^XxOrQQHcbj8$|1Ca_Pf0q-;Y zc=J1%j>6XScP6TKGFC>b=mA$Q753L__+;T!akG4}J5i`bRcXy)ydeU)bF*Wh0`!SVJ|B4w2rTf=hO3*c@ z32&12;hDj5QYq}mf|yH$wa&&UKGNXB@0eP|X}q0Y>!txSQ^F;E-yNPLwZyTT3>6Pu z*vF7bQG%gIpI=^JFersTU_#3O+`VgSUN&G?L7~J1eu_=G^J!ya>rf)h%(~cai)#{k z74e|gZCR(uUA_*5VXjXhOAYU8dss75nHA5q@pL}Ye!7#42$AebFn{HJdaZc$B2wp7_ELok<%K;ya|D zRoT5yk+HItNxLmlG9Q_^6j?aX8`>~u<+>u`JR=i3rG5JM{Gc2`ZB)HNhGno9sqY>x z?)Eh_QRRcXk8qRwLDuJbxm5=B$#f$!$Eyz~$Rmc){Pj-LBlCfl@v{>g<)o99ymbKa zpODMhy^_!6L`gLm+-YK}eE}pxqi@U7fjRaJ=0!C(d?yDA1-AFv;|dCWcCE}*nvUrj zDg$^$KYc78Q_g9cLU;nuaQ^c~lXlOqg~FAl0p_LskFoq@2v9!B;(v!Nekv*1lhuro zh5!R;|Kkk&pSXRJR8s1+D=;^Ta~h)nJI8;kI{<*-zv-T4H;n&!7xM89bhY?>;!ax^kU{NymPRkgj9JR}&!_(fx5%M) diff --git a/nop-cli/demo/_vfs/batch/demo.orm.xlsx b/nop-cli/demo/_vfs/batch/demo.orm.xlsx deleted file mode 100644 index 9d7b389906d6cb1f45f2184029b591922dc79256..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24076 zcmeEuV|ZoVvTi!Ila6iMwr$(C)3I&awv&!++Z~%7-t_nF{hWRFJ^TNCo^$7qxt=xF zm^EsSRaI}jHOH88l0d-70H6Ti0002^03z2vVw}GL0I-1p03ZQ?18NG|+Bg~8IO!_6 z+Zj7*)3{k%;pGDZlH~#be)a$V$N%CPm`ED8>7#=eeoA_Qi)&Ff$c0lz@e{@!M=iSt z+Hez~!dkyYgY?}Fb1yAdfCntIG9b=wdom)@eF}-tAa%ShtsGTnJ*x$l(l262$n=iX zo%2lAgBO9chB>f;7zqv-kN9vq0YExsNo}A2e)`Fdin0L6c=mHf#@w44uF z!w^PgY!^kJZtHaB{LQ-d(z9UJjTj#t0#`>^8qF*3cgn-OtYyinO&qF(W^y@zd4#$& z(#VWEJR;=}xihM2b?U@byQ}M%!IAwrZhEmhU-@Ug=&dw6tsIEq$uzr-Y>44aUVGp% z;+-4!H~BCIPgv04B2X@8E}>cPsibSX?SK+sUiP1|7ur~fwuH9W3Ui)pUXU4Nw(W|d z0y_cz)~PK)drNUPVFkmmliWo7&2D#YS1{dQKKc0x1R(d{ytPT0j`04=1f{;55$elZ zbsdbY9BFC(od3U<{x5dTf4TL_cxl;wI_TglvA2+sr^U@!cmYXQez6XGC7&PS8*oig z1w@#eJ)~IhN*Dn^qP|@|Z)5A5Tv4YZ_>X%`l@W-@oP^D;RlzAQ_Re4wBo4_U_LaK> z2ri2ci;rod67FO!T`?3Dt)+RAquYcc^EX0u@Y6J^7!XKBSiz{=X?_|*(&}3V&(+@+ z1r#r;f@@n?b59edGklkmi%(#9L)oRy=h9F{91ToYtG!07@E?9-C@Y$=o7Wm-J8%%V z>l#`8yc5dkL3;P1mdPAZAYej#W|$ToCeD5E)o5h88P9SbV1?+f7`YzxkH9V6`X8O7 zMI=1m9|!>8<_pr`zGlYFiq_TE!P3Ci*76UWRVvHcuCpNbz#Y57yQvCEG+RN$L46Ja zHVV!ZT@3Q^DLM%W5io~`V*_(v*7iqtScUimY%7@@%D*@i zEffI()Jw>jNTGY`3^M4_ifUFDRa>11ynfnl1K|fVG zJx5jcajQIkWd*JU2(c;!Nobi*QD*PlthiCp%LGSPE_2Ri>K{T-m}OY(>{Sz7D8VHV z7<{VM&jsJKQxY@|=+5B6aPe&$Q|`)K6uOI@ZKn7}sJVX%v9+?z#?T%nRi%`=A4g9))s<kkey-DVW3D+NP4vQ3sE_A zzMFKYlXA~XrnFFNJ{L>E`8DVZPiedagwv`g>iG1&fc;ZtxC&1;%4O%(=n>NYma)qp z-Vc866`IEnEp|tX0XyL?W6+77@B^!(|D^TmaVR2;6Dvf~l`kIx7SI<_<_3&TB!phE zk!98Up4!RZ1O7CiY}sjj3La~jz&qDI;L5k!I;V534Q@$K^JojAy9~$4pkwN>E4FNn zXoMK;^x$FtvN+9)Rw03y?58}y??%kmxQGa7v;;f4AN*sHt$U_UNx9w3y)9fwPK@f% z`S!eM?QZm#?Ol2}?XymZtx*22K)e`2f?PJw?%4j)-XQtXXCow}-EYpGPru>F4w@e| zQUq`2>bM1S3I{Y+wBM&|dVGI-t*c`@sl3XnZhODExxOB4=lGmi0Sxl&fXTY5>1Q*R z&(q+CZf470>AK1dDN#$iBAr-SI5b#_I7ZGYsVwz0Gh9wyT|!|Bx9Au$naTlL&5JP- zxT)ekFLjnr9HVKB@O+!a=jOuMDLvYA2Qa`E>E=8K>E^tcd~^4u%b6tgrd09d9{L<6 zo<;U+?Q*3akF;RTKG6>)UMN*(9yb!j2{7bICUk1Leomc=?S=u^BM_tppZ} z@O#UJs^gk3hG!qf4;o-!YUF*R!vOb8q~HzQUJxUyK{pYeV7J|%0iIMdeY7i+_#l16 zZ4(x#ek(7!vmpH?p41X`pduC2aMpSn+LAT~>(9Q>iV>-v{}BYXM`icDfdK%N5C8zs z{tE;g&5Vtm9BKdhp#OsgnF-pq>v+h)SELubc$e0Z0hskG_R@6G@;}}9 zc*@N`y@zEIRKi(=ldSFzpV*Inj^BTnF|A1+C0R>K?E@38LTZ+|DDGWttIVPi;<{20sFErbRKucEa_5>SQT6DEXts=F|PqHJ;w219FyxyefC#1Ilm zU=nvQXtlTrlWWyPYpza;m85hLY2F34SGWWC{VL-8A;q|dnz?J!aYmpo4HnuoN6`r_ z7a2*G%~Iv;Rh4&1Xsnf65I`E_>IBO;p)1TF!FFbiZGe}cIJJBzI}&JTkFUM#ixkYT zccAkC_8ct1n~XR~Q+W@=@`6Xz^Yi7xVNgby_#3eIl%Q`I=e}g zDIV;k4v$!sWM?ik*Slpd&0Ib%V3Jnt)$0*_MemR49kLBdgGd$SA7r% z8>TIN!zt%t(gI@3nEgl^C`IRewetwz2=VxNdrVRl-bdWB{ z;WrmC$a_&B1x9y-0fL8eZD%>x*T*dQbF6jkvTng)(dP_kFQEN52OizA8wak)D_dXH zt5^7biw{SytQ1w*jM|s)ti$v3SJMvJvPa9~Je9sh)@+d_Yu?p|-eUSq(7%Y^*$SCp zaYO9cHYrwBmg?zOheTM$SS>GG8K`!odUD?~yOYmAR4+?w*R8$>uRBXA73cEd!C!KL zH%WoN9IQdZmrj7gg9jdVzrP_{q`|+G>0H^`F!JRx5Po%Q#tGCQ0d~G}%=iYFg#HcC zul5dafdSuY(KK%oz;7%Pz>lx>=}*f!vcNyPT<`Fg8Vj_lf?I=vE13X;1NRkx&RZgFPs)vDus4(Y%k>2C5%6mj zM+zjZC2<+6rx)sdeeI4Un=s2}UW-L~dJTJ%O`kQMw9LT8S!*(a2aTQ54 zU3fe&2c!QOhz7PtXUZ2Sa3PcB46P$`AqpKYKoG`g5_ezYkCdz<iXSBcL2BE0dR6!| znGh#U5QijjXd6!y@1V;tVi4~YqH$fNH(xjY>zc+x!B5}YFSz-KSujoZLVgij;2B+r z*KaBaCq*ZN31E3W@axuy3t6@5$e&vj2Q0R*jDq6u&0MmlDxun_vS1F6WIv#w`73Bjq{1ZLBOXf;=gG7QzhwEbbg$`1r zAu=6JEU&WnQ?z)3Hn|3~#^~dZ+21Chu=61WDrU5m3B}a*STOX6)T~KXti&HT-pr|b z2V9nv#Zr_KUWl-~6Fk1@IFXy}BH%GUX4(;1w_HK+qge$Qzo*GG)+%6Dq=l4i!Y`}$ zmgCKZ&+O6>rjMfLKm>J#=$~ph)G>LL2%k;EQzGBV^;jAm(J)P3^_rWsblCiix&)|8 zmUvP8VNmFP(`X)LYzg9xyPs(9)OaoDmLQpa{YjFLyn$hAf%`2j)3V!|my})qO``qc zlSV2Nk@B&g_opiIyPEjT@mPLM?Lk28IpkT(I4Y-l(z9#!z^GSTNr00~5A>XJG7L$< zZ^YtJhfGuT7v=)rYby2uat zmEk$^`I`04gat0X|7rM72G{cglyqHAkN|8Q4%|E|h^9Mq8%AP?C52Q4DLNpf9u%nL$xr zHop)pO%Vy6VXr9cY?kg(As&RF&uCL#>LRIFz*I&@U}Ykel+Me;`xmnOb^3oKjyzv+ zmr#KM00d$E$2h|HcN~$9S)+pwzDjz*rP>Zl0PqtELPaeUc-^`I*n;Mhv;oTWav zhfe019#ZdzT%_=!N&$Wqees6CX5!c1lZw8!qbrPz1ofZ|z zzW8m*lxfib=4A%~Xn7`0{<$Mpo)ym>;0yV#*>HjmcelLq=h5{7D($R!dAcl;1TzkFMS{mA^M|v&YvWD8*)yQo4=AYs zGN};n70^n}@$TeL{8ZZA|H8Gzv@4r6I^;oJkQ)G?k{JQvI8@RbN4(%h*-fQ#8_(Cl zFz>b}EW%ycj45sw_K#OdkJgV)EuN0~qZ7C1=Lgf9n`Wo+!H1`lcaD#%2jjNqGnSIO zvl!paLyzuG-)`#V?evRVH#X6$Co`O=vqOVQEz#d+D@-Aa%Qv+gq_P=Y`@DPZI+`)G z?wJox#Bgsay0^M0vfSpN=n6NH4VMRVXfD?cyHE~1c z`=%E23+oQu#IgD$RkQZlDlT;+*uX@Ro#gRLPlHPf0};n)2vA4j>iUJ=c5q{=zG5`7{gd=PR{xkMRVtb(%W#_SZa2u0(bcR6{O4nSeZI!#xIcy)LJNlWJ`9(hrDn!Fbb{Iqon1Jz9= z0UXSN>`ZvpEQN>zMm&~+LU}rRQ9vkL*-W^pQUc;MSzP=*J8WT;=AeX3f4rcEgfbLj zXb=d}RlUbY^{^1W_E`Lx@OKGCVfCouN>Fl2Ja%^&Lav*Yk%E!2h($Zd9Hfr+h#OTEA zM8Cvbl-CjE#Cg1e#zHp*4h0VdPz8ULb3#@5@#iAfmu^a1p?~XpFpKx?-}=T01-QCM zhLaW%7Lgj`5t|wN94ubQ0(|~OaQ|OI7!k->SP^IdfIhDOh%iilAxxsC?Nh~-@B8`YrThxSs=kh?-AOhO7Hi7X(40@wfhRn%V>DE6P5kMe2Gdf?K6kdr@`WmPx(3Rk?3Vl@wx9`&v zO2nqDFx~|511sCHo2myqB6h~lN&QnBEi*VAyA^u}lj1{7!i}kRk5%oKUybFbRmXzS zbOqx3`26788AFseS9;7l)7bY9E=>_1maz!qaE(O@2@bz;^r_h*IS##Pi6_i2h(VBMxO&0Dw8!JC7c{Dsc%7 zqB?2qLgukV_KA9K8wH-$h0l&WcIGewXu(MS*n#HZ^JE?PfkT}5D>7`5_2-W|q)j?3YXJc`eWG=Z@2v zoGjn}Djoji1C<$@HXAJPLAu1(Km*Ax7b||&xg9UM;uWfNS_LP<2?Q?lj~A5GrX#!T z=Olm*re2;VLQ@Fj@$px>yj`80D6+P)F;BWZZ||39{lYFcmk-k=(=~UxxD6Q*uAfHh zB}XwmADW$=JUw1Mo~^AOIl8)BTfDS;x~LERgV0-(J)Kj}>-&rEhb_aeVNZTy_(Zq*K*7lb3_S2|06|!W&pn4J@RJNf+YJA)bU{ zh29H+;`dI^##td<8sg`TfC>p`n;xUgzvO2}U0VByPk=Te?wsFrA@0r=MqK^*WBW#vfq4hM?mPMKN*AkH}qrOoc>SU9HGT-^bO zjDr|a7z#Hf&p>2|FauJ3r)qsdt|O$05M2Q|NMnDhi}1|tUmm?_#-4wpo#XSv6mpN1=OG^^j+ z;su!MJn4Q&NGK8a=T12*04LdWj=*vIHZh`-C?tf)TVL<4DCU4#jEWtI$tT1HCX7cf zXVZD5KfSH~dhZ`K$m z)G~fQyz@s+-8dV9Vm<{rsH`6cd4Aq`y3>(q%98G6rmM9bhs=RVM!{a}xLQQ-(Sk>m zP_$CGxLDtzvFxX#mP-lHlyR!5>s>T!cJ`AHB!1mE9eEra^EOhAdzu-h7`s$fZmH*}_E*W8;pP z&PnEew(d2hw%k|xo$ej0^ol#Kw}mvdEG-~SElNBe(S*hngL1-26wg74pZ4&6xzg}#ff-ui5z-%(D|1(l?^Lr@%iOS$XZ=8=6aJGL`<&t5|3b0X^8I4Ofq#C z+z*!5?*180KZX97RK_^M5G3nnSV*KIm+{#BGM!8ua(>uGp}K0pxdVidG6jP408LyM znvHjcP&Ow<`$`lI!XVHhD&>(jH3_!Y+)kAXUZhfFI#GC6)^=#s%_C9EummT8=l|{d z0kO4t*kM@Y7Krhs6xk@5pTCGwzQ5fSlX+45n|e_|UcEgMt;yqG6*w zM1qPC77!MQ&z>;;ct%3n=<+SOwE3QH8*j^N(pd?|dgo8l3fM5LBm65@dB(vJjn$qr zxBU8}%|MBgp{!bea4zudw>O9BDRK}k7VO&N@6{bl4;ab}9btVl=&ikvBROe z0!%K6X&jUkf@*jrCT%W|l=+%Ulau0b@m-ld$f^&|zvLxDWj={=*m-v8wPJ%^$eO*C zj&R@?)yaTMu+`ao&l(SbUV;eOYeNVg(b*#)B}#G@h;{attFi_!50)Pu>X=}J1uP5`18So?5N_u0w|+in-Y@z zfCSIDhE!3-S67-q-7pc;C7HWkRb)E`+4S9~Np(Wza^#1Cg4;QR3CclF4H@PLzn zZ`!=lq8NmQm)x?uv&b7kXmrzQQHV1Ja?Y`nDs@!2hT07erhEJ?Ow?NzuTdQdX7rhP zXZ*V@=QS|8aQx+}Z)ZjI{AUMfcV1siQ6MQHrB{G3ZXhvXxx02gFF(A}D1k=n zov>D$eX`LdAg}a>y8?CnIr;QF-gAV>e7ayrM>*D@3UT`W9>~#1j`Rh<`@wnX_HP;1 z7)kP$%|6Bf2!K5Gelu2d*l&({x@z-@rQsxW^F7o}ci&E8CZ0Zi)|^B4kd>HF*4s|7 zy1$J*dl>Hg6vDxR=L8mhe;Hv4Dhu4vF>6y|0r2w`y}oqoRfi$-%NRWczAj~y;iM!*SW29a(FAB zT`PQ6sdjAIZku*=Yu1lmb!(=da0$o*4s=E^5eSXYX$r|u>y)w z;!S972eJ~*Tkwym3c(Kq{-pk^L8Y^Cd+;MfL_gTSJw<@@hHO&cR$_SlxJ}|m^ujX; zJmC3}mlTSK8_Fbrh#N@6bKVPDqGly#3wV7>l|O(^z#YdgwiiiQ#wd`_2r-hmTH`QO z-kXN>Cg@MZCO81@4n1EHNW=nf{&ixzg`abREoAM9Egbm6SH3=?3yDz?B{P3i4umKZ zH(QX|`)QMz2}FgV;fqHF5i$RR5ljiu{MG;X=vb^S23tgo$^o#RfL7X5xTNK@XrxX& zSzyomR%8IaEObE#gMd0NdL(TmNKDoUj{z|)bV8uo)l+4m$hn%VT$xswyNDVtT`)Ef zGbI#X0ii66Nl1eLJB~39>WhN$84)ocq$$QL|29`ns`4mvP&mMD1``I9&1OyJ!)28~ zbPLiKt_!laSo#@IuMfg?rm2Fpt3MttD&}A^DwutcPc-_)I?Yx~acd-b9&zG5Q*g-q7c?*~eFkW8p~;nuh~QXj{7L zRR%M2swtfjcjG&cQRp8x#G_u0NNNmwJN;^(K7JSG{q-XO4M7Txfz!@g%h-iBNPD)v zYHb~+f$F_!rW^qc%z-jb1qF0b174|SmT4wOqXuH=cU=7UOKy(gFxO)(6~yC8?rM{t z>XG0i&}rCuN8!dbwI&S~hg+Ox<%i5EU1%+3I+tMx(k9zLHZf%KRl&VI!1RMrGP+hn5yoAVw$NC#)XwLK*?)0 zc^;!ww^O$`qWscE{V;Gs`BFzCn>#IBz6n)0ySzn2{1>=8hp_9$T%jww>ggSoLVr-N>*qf8>S2Qfl&(~b1iA`)RT7l&PNq%U8}FFk56rDEh3QFrvmEQG3PHLhep zR0NZ+K64^nQ;rA)wD6m56>t8dJ2C zZ{?q5YUP(_5}k4S(#WLUdGjNsxTr7XIeUpMuua(ToorUE@`y@4yzY_BAgJ#0UO)!@ z7%8I%BTLu-Qgkl;dI_Y!f;!xVM#5yRwf2`#&hFcBOXeXL*h|J9g@?NE#yX6?_N;Uc zdwZC>NtnV$qbU^S#y~?@J>Qojg0m0G@)eee&;KFxiAPfDQ08BpUN}mKBs@KcOu6ax z14Y?+82E%VTtMzN%0EtDiM|}2IZ{IeyMVC(svvvzre7jOusf80ErKBL$E{H`uH5Oh81Q$zU9MV*Rj$z{shGip?k|2j6*Fm zPe5~jT^CD{Wc)EsprqsPEaRUqH zs~z;Ky|;KH6B+C17x-k{#Sw56hc#zNPw}`H&WIrvtu#FLZ#CWJwW3g6M%1|k;?IHF z$b*&u$MphBLEaZ(sg)|;`;SL_VYF9$UD0%3ri8r&gTmKrP^%iW=E6e{q(N>gI1l+& zY9Iru|9PcSksUGC2`m7Bw)B5YXjuMAXq471_UYigbys}pJYuX@!)ftt#9LrASAy!7 zO@)(@5XNsMBogY+d#=>F8|B0V@kgZyvK&o*MK*NujIMbetbs2ggTLb6y7eoOr(65^ ze5g$`9H2{P#4nZaf_UtKYU_Hx)pAjEv2spTfk8)+&rGekAOHtnv=$DjQrqqnDrhJ` z={~T~RsCvo!czy6M`2saWB^9eN~6UtSP+HzCzvtQU8I$SU;k zH8YhOMI~d&M|~WPBap2hd~cRdx3M^W4TLb>67Q$o#ujioy^Pa3( zpAO*4wKDM7{1j$N1y1J;pZaN9e(X)W`+ER|J7MsONRp)9M$5w<-q`OWo-1qE^|wXv z(W-^WrOoYXc&t}*^e=a}fC8ckmCu>QdgL9u<)jjKg2C_DufRct;6)( zq^pjj345sCf%>A)Zqoa}8OBfY+xDPMvZkxpCp%JLn0@#xP-7LY+mA<80lkw?wpALi zNj*gGv>1@e&3In3ngbN>g~tpIpI-(Y4RYe={YVGRM)EwH4*h!8{2MX9uxS;UV}F*A z3O~K|uFWPyh{~^N`-S2T%cB{#Ue*QFNqk(wT?1VC?IZ&UWc~=$LnN^U=tTz$WCLKt z4RO!RUJm%ijyCWucp7MD;TxU|h>3dq-Z@N>5r9BuB25`QmaBi(UH?GtFeBjv7{EYT zACt7nK2w&a^Undt{Ea z%v3mtP0qjgUSh_X%>fHykgo9#n40FMbqBT^=!(szW{WDC6zE9JIRTDQLW^#lx-jar ze&X>mbT&X%C_!aJU_vg$0UH^O!>@=M2RuB74Pu9y_KO|38k5}dM6Rqr3OpmX-?;AkUDAh9~L;BPfbp2w{B2BiILu3l^gsc(wz^>@;ZV-2%2P zEBzj4AN-XdARA=f3~&$=Yv3A_l8M(0?U6p^e#GA#_<_ zkH5YfC`f&!A&~Usj|0VOVp4VA#KUDf6wVi7_W(`vav|7B6d>u@Q4JiV{w}cI3;nIA zW2QbAd9-~n0%C#{U;Kgd+!p;j=R^7do48Kk!0UlTA<=*3F4-ed|Hxdzs01!uJ>wUg zPdDO}N^}xlOiH*?n*)e8T7@JCP~y_X1Ph7h5Kh82LZO932$15^MhwK`50|yoT~3+D zNk+jkaTpL+h_%!K@7&TL03}H&!%2@zOkk)IVJP*gY}LO-xDAJY2cK&=VDHM*BoQk| zQ~TD@PVd+@GnqKbCH70pU_OrueH33TzT>R(?9$T{lQy& zbmx7%QM{WUX6qi^pkim|t4Y_?pdSJ&JIsK&asi`4N}E(|+fgf6E3qw0oanLR3~tjj z0vJ7hYTf8wuXmTkn+{`#;gpNT1E$EA+&-obdh6Z@(^eI9c^4GiIQA6q1E+=XKp^9 zbM-1z>Xwl(TMd}J9o>DizCH<)JKA0=VzyMFC5AcC*Su*^3oyvEGq?@c1=OU!7e)lx z4Wzr| zR$!P@{X-X>_7|^^lRv0w z0WPkZ$T>2rBYerpH>-eku&jSbcUrL<-Y{GFs@S*Zgib72AjH>AWAmKXPUk4Qz1p=n zkl!yqs}Yb38zR{kHYsSP9R)(hYT;>cd*>E1YH+DL2D53m)>4PCPkpng#UwVvV+cN* zBa#s^v1kGxMLVD3c0oIj5g*W7TRyFs)gvnMoNlm*Ovn_(p_r{ycJdOw19o)Gf^hP0 zY8^G#+qGs)XUx_!0(Ht2PNt1~l4S9ordz|mM}(ZFYc^|s%r`1liHMC{z0@)YS^Yp0 zoG7lU`M{VIQ9#XBYml{@SIYm5*}&+h$0uFQsP?iJ`3uJCm;uP3fpI_Y1Y@2)aAa05 z3JFGpL9ya%qV!zPYy0&)q5VsQQvl(ZtA!-+!+P&&m|b;*WEpYSGIz+W9I|>BW=)t( zXPkB!nv2hT?Lv|FTD4JmC!HdZhfpVr1BfJU2#5@uIm=h{n{k|5XK~END|?1Y(8bl= z3?_#vs)g=YWgSW27X{H_k_0Q=3%*}^plg~qW+HJ!oaTV1~Z zTE+Cbws9-B!~cnior5V|h5zI?f5(VIgnvw|k@I$5FaOq2jj`%RQ)*_5+udP=r=0TenhZ;kC;~>{x9HNS_aKK@!iVrZFc?iUu_loMURrl4I`s~TY^Rn$fAjlSiY*F{Tkat41_%ObJt5B1DyLJd$zY+qz-&=W&R1eHB%&?5Ky>0@Gn6KL`CD5 z|4ChS;wC$wdF)Yi?$1^(1PLbN!5&b4jf-Py*cRqJh{z3Pct70n^8nRuxj9J;L5xo; zuVw9}N{FBoq|5i08ZXeFqNv;U?d&t+la-5Do8>^ruXZA7#N*6Y^>^1l?o^Z;xx_!+ zjE1n+Qs@+I&Te^v_~7B^Cv4Ka?hJ=o$hRe+-6h$3;IgiMn1L4TXg6bToOOlo<=XgX z7cd6oA5V;wi_1H?n=}&DDSSREmtSHsYiV zv$1@Vd5q_AKrU90`J7e_&@g@|$hxEUC=8c)LilH$eAw zq~;_)8q)+N&0n;j7>W>;tDNDCt3YH{r5EWH3ItB{6eVp?c(_~rx^$%SEj%8I%doeA zq5+Uz1Nmmmcf-}pE~VtvkUs+?#-h^qWFY7u5+vr)^gSWCct?Iwq_-fxk(0`; zwL>S)SxH8Q1q!Fj=&ZDHoyx5-i&<$cttzveA^4Irifzn1aCHn~jmAys%dq53GOA{) zyz}cH-5a&9?MkENYew3MXD^t@M`RKlE)7^*J;IhCMv?{c3oKcn|I`mAaPWr!=-xr&BP6gJ}x-P z)JTbMrwhD#311p_*!v~J6qLWF%&29no3P^{d5(*`gzk?G@Y5^R5Hw(>?0o(IMk(kD z(imfxL&7Mt`Q=i*4Oh^;+(^%2?up1;{#Lz$rSWQ{8O~*=f(95UgXc{f)ksE)RPSlX zP<7;}Mv7A9;;#+ls7cawQHNJA!_Os~pI=+?{!vDATC$A(S`z}YQzfL%hwlOy2_oh$ zrPEAo>z76kmlP&1rs@2|9Vzr$K>7)ubB^WUO}pyza&sW*+qG5jbZwaGazhgJwBDmD zTEKg0K)F0N>fHG(3)v=L;~-e**QmraAXf*^N{CKG_(XmP38BK&3ki|xq&BZO3oke* zun{R1S}04ZTqG5y#2^evf?-gq7Mc+U96tjL2mvgOrzBmJ1h~3_0Yk4uH6TA~obRA9 z52!G=@KuVR6Y!wGFfhsX8}L^1rXaV+C7~|NDKiJvCzT(EhImHT-jA&RFrQJUMuDOC zigJ^$a>Hj{{4*w2k^oJdwfL)Pn3WXM5*h{SUTGp=g20UUH}f*5*zEA&znYK@iX?h} z@q?U{nT6&V;73c~FRer=YYIZ6K-nuf_)Q^JVmji_s#o%W6e8ab2wG537nbDX2Zsl- zQ3U-wKyteMD*69Q(@5+OXTz_@4vk*}e03q_I7@O-i3Spo%}|1=kWL{RH*q6PEP4B{QgDH-p; zEHFeXzBK$!YR~lE{ac$WkO7+a-K{5Z7vb53&D|4CHf0w_`yIiScHH)i4THAi?(PoR zMt9r9{vNfM+>57X>f?HMcsME2zJ!jOw)SdI6|HA`dQ0~8@0GOwzA8Sy_7hI$%X}vB z{=A^zuiczZX2#aWw11ud+QW0Mt{H*JitIys&kKEb`OLCEj%a(syl%t_p-x&9qoMgo zR)LN#z7+-G7tfO^5vgfSNbnp;?(K908ip-fz+la9bKJCN5BvhdP>_1GI~tT1BErgI*2ZujwhJc*OOf|r%xyh*+Ng~cX#?XO@9nd9xBQ}wNRSF zErfh(++xgzef(}h?-&C7-H2q}IbI+8R|5oVV*w?bSmp!J7t(}r;Gqx{Zqgi_N)i}< zy3!`q7v>*h&RRWNH4m1Q-WWeb2-VCjBB~m6PC(I@0E%$2+dUt2nyhwV-WQoO*7IJP zt}zl%;g+rg_`i3W?J|=MhFCt=&Y#~~y<;TqSz11Olx;$tCi}pIlZnGF&lJ_FT+pa? z_)DQ}+V^GX)0L_+9I`{!>nCYjghNOk6l+DyS`UE%$nk{}iF383?m}SjGq@rUgJrsP z7-8`5P0IBFL#I#_@s~kWJhLR6w-4t%Ix%a3g2>hfbKJF>7CP+A_HWQTiURq^$QaEWJzRrMCc8&t)8f zslRmL6VDAtD4R9|5ts_Wp#UdQa4VG-s6Z+8_W9;5M`XLoX1^#4VlzlS?0!h9hD~SS zFT_EbBtc}w`P~jN5gNj$?_NfGF9HIEC8-QGyh8%5M%DIeH3;{VKj|)OuV#g<2-}E4 zXtFv&>5*0qLpH$&8A~RK!lceS_TUwe-IvpxdY@9e1Q!?wz7b>gdq?O|ZcLN-XYf@Q zi@W=m+Wy)=7?(_4eYNK&1@BNRo-~ZmUe9D}C}7OdDC>%#7alf?taH874gtYtY4FO% z^kxTCS?#SJXpcPCQl5rDU72^Dc$*zZaJg1gHmAv{Sg{o*emk*uq_m3k!D$@R z@q<0Uf4J&dSt3h1vEH%#=W5_9Trx8^lWesioA?s@S!X$Wjm6uiw{n9yZO$^oQPYGw z^}ffe2X&K_PB))%f~&jJH0jpLZ5q79)1m*Kc3uY$;Fj4C5Zb~8*=B-P6M!j#*93+X zs?Atj@(o+U6$1K&s2ONgUcHT;jBv3BMJGDa4Ero+PoNQ&qO>mCC6zuTyzPr6f?|c?6oilnJK1Gn%|EtN?PhNubVZn%b%qxBEFfwmDYXnxOEZ1qML&$f9^(N9MIULPWq z$f1=WGX_c@m+F}~K&?ydn*_r&i^|nO>WNudN?bJA)%4!HGa9bca-hc~*-ca2#W6=v^IxXL zgA}I=P6;PxoCh!!w}K1x6h$`gGSm*$mNad!h>PwLT>A^&(LlX))HxFj((0Wu{oxG_ z^6QO^RN&CE3oOsp&LV8wH}#~vBUw`#&U{p75?RkLzcxA-O~6|GxIFB9*-8|S<@&Oi zMdj{`SYyepf1$#1_T@&LU+TovY;F&aYo>qY&fn9zb7(F4AKtycUOUi(Tb|**O~}XL zYMI2}uy(!xIcec32kwz2lDAsr4B4g8vW0oyu3y03dgLjJfz?>CnY(0*Y;kDQ0>2J3Jm2RplZ6{6YE!bT+|**W8CACB9O#}mgV+Y;$Qn~LMwnu;JH8xx zmMcsVoGH7nmjQ4LlY1e~Qt?CUG?c|nGe(lI<0cLSTc>jq@)IpapudQ>juSzQ=}`a5 ziY0Lz7)c!ntQyd2PY!%Pnol$_!vb0;I?R>R)aaU6N>*K^I=r4)?b_3N?FZjWETmxg;34m!4hIJZbO|D9s}G2$<+dWS0#U^ z!Tvw(ocTMH`~SynbBqSrlPn{IoCr^R;KgAwr%LOZ!`&8x=XmlR+I-e2vF-@Arg3VgWkBicoOLH=SXAmc50`&Fm_CXWd} zt+}as0-+0kwVC~hQ-DW`n$otH+IatRf3c1%4 z{W#M`hNhq`S?u}8Dc@`3Tke#h(ea&d|J0is3{bL{0+}9Cb!|c|poJj1eXISzV2a4t zF4*GH!UcE14*>X~_2Bx%EWK_Qqpn;tbuJ4WY)%;~Q#?V@j$)>M6S1H^jHLS*A91#~ z+o$tHQZ5ef(90U#JZGyqlwK^jc(al1Ej!f;$@ODp=PU73CbP;QjlLIl8!LSi^}Ech z6w&3@q)stj2)zB3wXcNJT`k+*UOAy?sMcey=NXz#;Jr*w*TGp1Rrap&spOV>V`A-p zAZ#=&8*XS&1<|U|ydhISU^%(7G52HS1#M!~2BZ~BmL#N%B1+tGFCC0XC$z9dO@NzYdjb{X4Hb>@hHDr3e$V3Yk5h%GW1f9|uHBPspr{vfv_A&!VkpLhGwUf6w3h{kF|=x zHp^+Ov2!bJt*DUow5X;K`a5$u74tjx?KYA17e9%1AqPd(jIY#^wLJ~H1jc>#yLh9| zt~_slGr_5sq03lLiw~_054yK1LACI>JIBPNo+-uzv#?Zs%WAI|ONLF+0lF&El(*0e+$Yn4t_DPD+n!>M?_ZuWs=Pg$u*&X@uPp zwuBAs`hJvep_y$f1P#NhqBX9?84pxGvl|M4APP>lch?T_Gg4n`IzbF-=iLm_Te0ET zDzsPYp;GA4dPd9fkAv~s)VIj5b_NsL|jj>j9{b ze};=MP(IIjAJXWo=qp-%<#V=a*9v#V$DM{d&I^{iCWE+u#pJ0!{QL(SJcpl`6_Vve z`5z|n%JRGZiPcscFx6*hQhZMJ_+7NYiGS-x7-6ygZ#R_%gx>W!!7C_tg@lCZujSmu z&I|5ne6$ZIo`Q%Xz*h2**}=b=~!ywu+WB z+59^jwb_u_yrPF!2r9zmNSv`D#Z2&IwEO!&q)K`cL<=~ubd*6Jfjw?2 zyln%u6u<4$8uUrKW!z|FBuJfsVf`Ddah`)Z`?D>Y)^DtSBA9RaNmN@qud~Xi&(wzQ zGTS{zS?7wlo6NG(dS_QAaY8mHwnOV>EWk3GYPR(Ww|i>1@Tim(g_-Jy87hgBn!fC> zW)PL5UGZuSgKx~eL0&6&Mp1Pi6w5dAfwZtYCTwo*G-QD$b*o%pZ<*dPMHUD7uDqAN zmleuz^=|7H{>AqBGGK3~UHRceG2S;br6#B-r_DK*OL@wR$Y@SRU{S+5Ezm{B15t+A z@mnR4=u!-PQyK?UJuDfue$`%P0f6}U%~Kfv0CzYbFt5Rx)Rkyex|-1wj4-gJ9q944Gnv` z=w)Ggpuxx*&H{08mO>yMsN)q7;O4@lI;SVXcYbh)spe{ROQmV`wA)3ndK#+)-&yqr zWN#*WLab*+fFrX*2kK7cZOZ9M`RHVan}!bxJt=sZx%{|o3}cc&o9`46=80XlO~9zx zmnS>1=-1Hb=5_c^jA#rgXb9fi@E(ksv3Sy?Gmyu;G-P?Rh%^<4%D7^EA z4Gt;=8yiXlh-6Wq?e%?CwYI4k?K4{C^0l~?GeSsV1>)z^=6iB@BqNQxg+|T~O#3~O z&$(pL=Ut}g;a*XV$^NnBEMry))!z6r?`zRf){^)BgJo`tCm#+Bpk0GYhpY9+5Xlc_APe8%mlbQoFVn4)od#U2G+?RZ^ zDuNXP+v_+jv3;FO@JGY@t(wNcLeh6%9lZaNxVy=Q{fZRu(Giz-hVRylhE#4+4clNr zx(ImnuTO>&;<9U;w6sZ`=klv<@#-a<_a++eyK50%g?RXV%7jxPx%r|Q>mpL-w z-4e6Wz+07{+@1Yr(=W1_tfcF(uf3K=_&r=>uU?!#xLl8$9deUC7-_CHavs%tmB){Z z6Y-y;gMxm<_jDD~F|)?oMrzBb0MyTq?@ZmxO;wj!(li=J@9aJ%$4b#~=Sr=8a`jP| zMl@UFCh1@1T{6>4_CEYh9zjw}ssG4CFc~JzJ>Fc0>kCbZdvnxLkjv!&kg!(~ ziC^`3GJ#U|gnL^O62`7t8lsCrfh3~{(~$Rys7av#TONNQJ>QNfyCbPKgZ&-(`E(ES zTK}MkY27_o%rm-ueaWH=yrs@}Avb7mmb^!K+S_$1TfEtEgT*fRI_f}T2irCh#QJCi z>BucOQyDrO%pZOOFT9nNDK||@(p(%&1b05~l~a^n$zs$|k9&Th8ttH8mdPdXhYN$C zNS--|j<<~dg<8*cmSSAkBl8P1(`WAEW+w}^b`&ITAw2{!>6(Re(V(&-eHUKlv^K}o zcP}+7THlQZ*24!lL))wnV*vwH;VD=k49qRS9s$nv0Fp`cUl{^qdi&d^4bHgEMmn58 z--fmUlq|%m&MMWb&qh!o?N}{uiTA6nVlST&uT*;{D#Q}`J%*d_iHmEA72Q}36y^h~ zP+sAnRWD>HAq~7&99x)mbelY#eQhzew#N2lew|+>D9C@g+uz<+EB)jm*#q@Cwla|h z1Is`zK;6_$?*tXuvxWutGjrqEj@^Lw>C^l0k10C6_QN9=xq_9gd5)6u8^siKmW!&H zeA%o^p7zS)`IYISbs9bqJXCXQ??{+1F3J0Om(;#fGbP;2U+uPuj`|pLGCBACz#sO< z2_etozq?MJY|3K`f~yfp$ibi|L_RrsIOuzMAic#MJiOpP3kgD);s27J2-|URDom$? z;NZpsTGV=ia*A_0Fm;TWYY@6z!R8w`zSdPOp4D<1_wbm!Q>(Tq0a-fsdc!v{FIYMY zN)pBN(-BD86 z4*dv{6%l^=HYFQMjrU5Fa_|MFKM^(SK6mAvyvGuw4qf`%%*TC=p$)8xN^R@dCmGLf zoA>&eQR}C8K9D3I5i(+zRFJF9!It=5YFW^uI%*bT#b`fBGR<$kUx7!<`yw%hhw(xw zb9hEheC-OJgmXXG6~9>4q{qoQu4ByWnHs$*l9VQ$RYa%?S2Qmy+cytz~qsAxm%&D#c0DLsyJVA8HyNh?5 z2Gl7%+9t;!v>AwHr$w$G$9_)z(2STf!ZkP9IO4jZ1gZ5wXv+r zOLfkju@1;q2yPV^OLx&K1U1F@dCCe1YbHS?y2z$l`z9~pDgkKdT2hum2H9YeIa5hO z-u1WWxm11UOb$$*-FvxY;qS2F{`l-c>tgIXasdTt3SkU@pWeAKT1ixg4}cB}*)lwX z$n6mdsADRTT}_-af*OdF6`8EPg&r7(w^yFnrbc;nPdsZKr2lMpQ`s_O{+qCFI$^x%G)-SBMG zkIu=0PaExucEVbZf-un5ccim+TrccgJgstb#8=lAjq)paerz9q9B>NJpR}=dAJ4A{ z3z02xGS3_*bMtFCqna60e0ZTd(hvMu8h_rVnpP<`j8joYoeK3SMhwNhW94s9yhsi7 zFnbAG7>1blANl$`8DieY;j2t)dje=tu@mlt){hkX`*tgIljEY64Hi6}nSFHIJK+oa zWiS7E&O=HjN+<$;pA7x?5cvE0o1AE3z`w(4{}%qdJ|oPte?-?33yA|fPo=|zQiUka zlUV%kIKfjX2}vZuL;U}T3lejP1LRIg?+MQLzi#3`adN~I;!LMgN+Kbc?l+WQ2~Wfn z;zXBI$}sb9DZlbvh$+Ob{ZooGAt~lJ_wvibPfQ{9-<(n^367lKP=2{_h$+NPucwqq ziQiIwwZIZnh`$YFX1_O6W7ti zgnz#Uo$5(Qe2qy+{_{>mEdTeW^-np#>>u*KwlIAi0AcR@48%Q4GDDbttd>9j{U0`7 Bg2n&< diff --git a/nop-cli/demo/_vfs/nop/task/demo/create-card.task.xml b/nop-cli/demo/_vfs/nop/task/demo/create-card.task.xml new file mode 100644 index 000000000..e613a5e98 --- /dev/null +++ b/nop-cli/demo/_vfs/nop/task/demo/create-card.task.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + RESULT + + + + + + + RESULT + + + + + + \ No newline at end of file diff --git a/nop-commons/src/main/java/io/nop/commons/util/StringHelper.java b/nop-commons/src/main/java/io/nop/commons/util/StringHelper.java index 7cdfe9bac..185dd9ced 100644 --- a/nop-commons/src/main/java/io/nop/commons/util/StringHelper.java +++ b/nop-commons/src/main/java/io/nop/commons/util/StringHelper.java @@ -4456,4 +4456,12 @@ public static String filePathMd5(String filePath) { fileName = fileName.substring(0, 20); return md5Hash(filePath) + '-' + fileName; } + + @Deterministic + public static short shortHash(String str) { + if (str == null) + str = ""; + int hash = HashHelper.murmur3_32(str); + return (short) (hash % Short.MAX_VALUE); + } } \ No newline at end of file diff --git a/nop-excel/src/main/java/io/nop/excel/imp/ImportExcelParser.java b/nop-excel/src/main/java/io/nop/excel/imp/ImportExcelParser.java index fda75a10b..1f1473851 100644 --- a/nop-excel/src/main/java/io/nop/excel/imp/ImportExcelParser.java +++ b/nop-excel/src/main/java/io/nop/excel/imp/ImportExcelParser.java @@ -123,6 +123,10 @@ public Object parseFromWorkbook(ExcelWorkbook workbook) { } } + if (importModel.getNormalizeFieldsExpr() != null) { + importModel.getNormalizeFieldsExpr().invoke(scope); + } + if (importModel.getValidator() != null) { try { importModel.getValidator().invoke(scope); diff --git a/nop-excel/src/main/java/io/nop/excel/imp/model/_gen/_ImportModel.java b/nop-excel/src/main/java/io/nop/excel/imp/model/_gen/_ImportModel.java index 4e169fd18..1468da32e 100644 --- a/nop-excel/src/main/java/io/nop/excel/imp/model/_gen/_ImportModel.java +++ b/nop-excel/src/main/java/io/nop/excel/imp/model/_gen/_ImportModel.java @@ -51,6 +51,13 @@ public abstract class _ImportModel extends io.nop.core.resource.component.Abstra */ private boolean _ignoreUnknownSheet = false; + /** + * + * xml name: normalizeFieldsExpr + * + */ + private io.nop.core.lang.eval.IEvalAction _normalizeFieldsExpr ; + /** * * xml name: resultType @@ -181,6 +188,25 @@ public void setIgnoreUnknownSheet(boolean value){ } + /** + * + * xml name: normalizeFieldsExpr + * + */ + + public io.nop.core.lang.eval.IEvalAction getNormalizeFieldsExpr(){ + return _normalizeFieldsExpr; + } + + + public void setNormalizeFieldsExpr(io.nop.core.lang.eval.IEvalAction value){ + checkAllowChange(); + + this._normalizeFieldsExpr = value; + + } + + /** * * xml name: resultType @@ -324,6 +350,7 @@ protected void outputJson(IJsonHandler out){ out.putNotNull("defaultStripText",this.isDefaultStripText()); out.putNotNull("dump",this.isDump()); out.putNotNull("ignoreUnknownSheet",this.isIgnoreUnknownSheet()); + out.putNotNull("normalizeFieldsExpr",this.getNormalizeFieldsExpr()); out.putNotNull("resultType",this.getResultType()); out.putNotNull("sheets",this.getSheets()); out.putNotNull("templatePath",this.getTemplatePath()); @@ -345,6 +372,7 @@ protected void copyTo(ImportModel instance){ instance.setDefaultStripText(this.isDefaultStripText()); instance.setDump(this.isDump()); instance.setIgnoreUnknownSheet(this.isIgnoreUnknownSheet()); + instance.setNormalizeFieldsExpr(this.getNormalizeFieldsExpr()); instance.setResultType(this.getResultType()); instance.setSheets(this.getSheets()); instance.setTemplatePath(this.getTemplatePath()); diff --git a/nop-orm/src/main/java/io/nop/orm/utils/OrmDaoHelper.java b/nop-orm/src/main/java/io/nop/orm/utils/OrmDaoHelper.java new file mode 100644 index 000000000..f2072e126 --- /dev/null +++ b/nop-orm/src/main/java/io/nop/orm/utils/OrmDaoHelper.java @@ -0,0 +1,18 @@ +package io.nop.orm.utils; + +import io.nop.core.reflect.bean.BeanTool; +import io.nop.dao.api.DaoProvider; +import io.nop.dao.api.IEntityDao; +import io.nop.orm.IOrmEntity; + +import java.util.Map; + +public class OrmDaoHelper { + public static IOrmEntity saveEntity(String entityName, Map data) { + IEntityDao dao = DaoProvider.instance().dao(entityName); + IOrmEntity entity = dao.newEntity(); + BeanTool.instance().setProperties(entity, data); + dao.saveEntity(entity); + return entity; + } +} diff --git a/nop-orm/src/main/resources/_vfs/nop/orm/xlib/dao.xlib b/nop-orm/src/main/resources/_vfs/nop/orm/xlib/dao.xlib index 0ec16a7e4..2cb9981cb 100644 --- a/nop-orm/src/main/resources/_vfs/nop/orm/xlib/dao.xlib +++ b/nop-orm/src/main/resources/_vfs/nop/orm/xlib/dao.xlib @@ -112,5 +112,14 @@ ]]> + + + + + + import io.nop.orm.utils.OrmDaoHelper; + return OrmDaoHelper.saveEntity(entityName, data); + + \ No newline at end of file diff --git a/nop-orm/src/main/resources/_vfs/nop/orm/xlib/orm-gen.xlib b/nop-orm/src/main/resources/_vfs/nop/orm/xlib/orm-gen.xlib index c5e0bba11..10071c0b7 100644 --- a/nop-orm/src/main/resources/_vfs/nop/orm/xlib/orm-gen.xlib +++ b/nop-orm/src/main/resources/_vfs/nop/orm/xlib/orm-gen.xlib @@ -9,6 +9,7 @@ + @@ -18,6 +19,21 @@ + + + { + if(entityNode.attrCsvSet('tagSet')?.contains('kv-table')){ + entityNode.setAttr('className', 'io.nop.orm.support.DynamicOrmKeyValueTable'); + }else{ + entityNode.setAttr('className','io.nop.orm.support.DynamicOrmEntity'); + } + }); + } + ]]> + + diff --git a/nop-xdefs/src/main/resources/_vfs/nop/schema/excel/imp.xdef b/nop-xdefs/src/main/resources/_vfs/nop/schema/excel/imp.xdef index fcb96828d..7ca397486 100644 --- a/nop-xdefs/src/main/resources/_vfs/nop/schema/excel/imp.xdef +++ b/nop-xdefs/src/main/resources/_vfs/nop/schema/excel/imp.xdef @@ -18,6 +18,8 @@ + + diff --git a/nop-xdefs/src/main/resources/_vfs/nop/schema/task/batch.xdef b/nop-xdefs/src/main/resources/_vfs/nop/schema/task/batch.xdef index 9be2b8915..6e3342b1e 100644 --- a/nop-xdefs/src/main/resources/_vfs/nop/schema/task/batch.xdef +++ b/nop-xdefs/src/main/resources/_vfs/nop/schema/task/batch.xdef @@ -10,6 +10,7 @@ singleMode="boolean=false" singleSession="boolean" saveState="boolean" transactionScope="enum:io.nop.batch.core.BatchTransactionScope" rateLimit="double" jitterRatio="double" allowStartIfComplete="boolean" startLimit="!int=0" + useBatchRequestGenerator="!boolean=false" executor="bean-name" xdef:name="BatchTaskModel" xdef:bean-package="io.nop.batch.dsl.model" x:schema="/nop/schema/xdef.xdef" xmlns:x="/nop/schema/xdsl.xdef" xdef:model-name-prop="taskName" xdef:model-version-prop="taskVersion" @@ -61,7 +62,7 @@ @fileModelPath 文件模型路径。当没有指定resourceIO和newRecordInputProvider时,根据fileModelPath自动生成resourceIO --> + resourceLoader="bean-name" resourceIO="bean-name" maxCountExpr="expr" fileModelPath="v-path"> @@ -87,6 +88,9 @@ + + + diff --git a/nop-xlang/src/main/java/io/nop/xlang/xdef/domain/SimpleStdDomainHandlers.java b/nop-xlang/src/main/java/io/nop/xlang/xdef/domain/SimpleStdDomainHandlers.java index 6e1008805..ff3012f5e 100644 --- a/nop-xlang/src/main/java/io/nop/xlang/xdef/domain/SimpleStdDomainHandlers.java +++ b/nop-xlang/src/main/java/io/nop/xlang/xdef/domain/SimpleStdDomainHandlers.java @@ -654,6 +654,7 @@ public Object parseProp(IStdDomainOptions options, SourceLocation loc, String pr JsonParseOptions opts = new JsonParseOptions(); opts.setKeepLocation(true); + opts.setStrictMode(false); try { return JsonTool.instance().parseFromText(loc, text.toString(), opts); } catch (Exception e) { diff --git a/nop-xlang/src/main/java/io/nop/xlang/xdsl/XDslExtender.java b/nop-xlang/src/main/java/io/nop/xlang/xdsl/XDslExtender.java index 7fe7e4090..296b7acdc 100644 --- a/nop-xlang/src/main/java/io/nop/xlang/xdsl/XDslExtender.java +++ b/nop-xlang/src/main/java/io/nop/xlang/xdsl/XDslExtender.java @@ -351,7 +351,8 @@ private List genCpExtends(IXDefinition def, XNode root, XNode node, if (AppConfig.isDebugMode()) addPathRef(extendsNode, currentPath); - List genExtends = extendsNode.detachChildren(); + List genExtends = !extendsNode.isDummyNode() ? + Collections.singletonList(extendsNode) : extendsNode.detachChildren(); List ret = new ArrayList<>(genExtends.size()); for (XNode gen : genExtends) { if (XplParseHelper.getAttrBool(gen, keys.DUMP, false)) {