From b85966c53591876870d9320b82b84a848501e569 Mon Sep 17 00:00:00 2001 From: iqudoo Date: Wed, 15 Apr 2026 00:46:34 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=B7=BB=E5=8A=A0=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=8F=98=E6=9B=B4=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 61 ++- ...-mybatis-generator-plugin-1.0-SNAPSHOT.jar | Bin 35962 -> 38004 bytes .../TapeRepositoryGeneratorPlugin.java | 423 +++++++++++++----- .../mybatis/TapeRepoviewGeneratorPlugin.java | 20 +- 4 files changed, 352 insertions(+), 152 deletions(-) diff --git a/README.md b/README.md index 9dc06f0..f2f8772 100644 --- a/README.md +++ b/README.md @@ -128,6 +128,7 @@ ### 2. 在 `mybatis.generator.xml` 中配置插件 ```xml + @@ -135,10 +136,13 @@ - - - - + + + + + + + @@ -157,22 +161,41 @@ ### 3. 配置参数说明 -| 参数名 | 说明 | 默认值 | 必需 | -|----------------------------|-------------------------------|-------------------------------------------------------|------| -| `targetProject` | 生成代码的目标项目路径 | `src/main/java` | 否 | -| `modelPackage` | Model 类的包路径 | `com.iqudoo.platform.application.database.model` | 是 | -| `mapperPackage` | Mapper 接口的包路径 | `com.iqudoo.platform.application.database.mapper` | 是 | -| `facadeRepositoryPackage` | Repository 接口的包路径 | `com.iqudoo.platform.application.facade.repository` | 否 | -| `domainRepositoryPackage` | Repository 实现类的包路径 | `com.iqudoo.platform.application.domain.repository` | 否 | -| `facadeRepoviewPackage` | RepoView 接口的包路径 | `com.iqudoo.platform.application.facade.repoview` | 否 | +| 参数名 | 说明 | 默认值 | 必需 | +|----------------------------|-------------------------------|---------------------------------------------------------|------| +| `targetProject` | 生成代码的目标项目路径 | `src/main/java` | 否 | +| `modelPackage` | Model 类的包路径 | `com.iqudoo.platform.application.database.model` | 是 | +| `mapperPackage` | Mapper 接口的包路径 | `com.iqudoo.platform.application.database.mapper` | 是 | +| `facadeRepositoryPackage` | Repository 接口的包路径 | `com.iqudoo.platform.application.facade.repository` | 否 | +| `domainRepositoryPackage` | Repository 实现类的包路径 | `com.iqudoo.platform.application.domain.repository` | 否 | +| `facadeRepoviewPackage` | RepoView 接口的包路径 | `com.iqudoo.platform.application.facade.repoview` | 否 | | `snowflakeUtilClass` | 雪花算法ID生成工具类 | `com.iqudoo.framework.tape.modules.utils.SnowflakeUtil` | 否 | -| `snowflakeUtilGenId` | 雪花算法ID生成方法 | `SnowflakeUtil.nextId()` | 否 | -| `slowQueryLoggerTime` | 慢查询日志时间阈值 | `300` | 否 | -| `slowQueryLoggerLevel` | 慢查询日志类型:error,warn,debug,info | `error` | 否 | -| `priorityPrimaryKeyOffset` | 优先查询主键偏移阈值 | `0` | 否 | -| `ignorePageSize` | 忽略分页阈值 | `10000` | 否 | -| `startPageNum` | 分页开始页码 | `1` | 否 | -| `maxPageSize` | 最大每页数量 | `100` | 否 | +| `snowflakeUtilGenId` | 雪花算法ID生成方法 | `SnowflakeUtil.nextId()` | 否 | +| `changeLogContextClassPackage` | 变更日志上下文包路径 | `com.iqudoo.platform.application.domain.changeLog` | 否 | +| `changeLogContextClassName` | 变更日志上下文类 | `ChangeLogContext` | 否 | +| `changeLogEnable` | 变更日志监听开关 | `false` | 否 | +| `slowQueryLoggerTime` | 慢查询日志时间阈值 | `300` | 否 | +| `slowQueryLoggerLevel` | 慢查询日志类型:error,warn,debug,info | `error` | 否 | +| `priorityPrimaryKeyOffset` | 优先查询主键偏移阈值 | `0` | 否 | +| `ignorePageSize` | 忽略分页阈值 | `10000` | 否 | +| `startPageNum` | 分页开始页码 | `1` | 否 | +| `maxPageSize` | 最大每页数量 | `100` | 否 | + +## 变更日志监听 + +ChangeLogContext应该提供以下实现的静态方法,供Repository实现中调用 +```java +public class ChangeLogContext { + + /** + * 添加一条变更 + */ + public static void addLog(String tableName, String eventType, Long dataGuid, Map fieldChanges) { + // your thing code + } + +} +``` ## 数据库表结构要求 diff --git a/releases/tape-mybatis-generator-plugin-1.0-SNAPSHOT.jar b/releases/tape-mybatis-generator-plugin-1.0-SNAPSHOT.jar index 456df450c15fbe8c539f9d25ccc65cdee36a2b6a..8732b42f1df778e8f3a4b6a828722243fb94ee80 100644 GIT binary patch delta 22171 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%0AaA zyk)dENE8^ZoGb#k7+or^XNmT?)L-aDe~3BenH2K+g4AY2O3DcL>lOWyt&zavV40iE z&qhB^rYj4_|NVL>yX@!o9j-weN!axzq{RT60!FO8Cd2N-*JOGoo^GE^w@;@-)kz*` z*Ro~sVU%1KQKEH&~JSO z@2#e;#HqeDv>5x=AV)>&Muftt?Tg*Nl04dC>bLJ^FSXv6BIId9ncQ=+14N0jk@Jq0 zpB92de211B5uah_+Or@oik7Z*a?l?ZNE10PcNq`&?l8Z5gv_e+t{~FSug}$%EOt|G zhNb5I7PA4*GFCLl!yG4IXYLX3tf~7M`nEBsv)jo5Y$mPkvc*pf%+GBjBt_|xpqr$> zbnLeRwD6s@58sO@TU_fmSM_3Dm6Yb z3RA@gg7H7El}#+XP4S;pZpNV^&)5X<^^_li$SceX#?FA$Qi zPvtBcpL(&hs~T7Y9n_1|Ynuve{r}26|7VOn!40#S{hwp6ad3c4O+rT=UWQ56 ziby*M1*x{GC3X|%F2c|vf!ezLx}JXdL7|}ex~*4ZB7IGf=hujDMmmf71S6HTDK|e3 zCTb>$2JxS9i8TjxQUJw_hdn@mw=nXZd61LHgt+KQ_0zgu4iEd^0*V6x7VQH-kra-Z zdd;&wl6^JJ-!?DBqP(Rrx0hiL*6Gt26yjc? zzh>9KGvhQFgQ*hGZh~C5-|mP>$@AGJHSPVk4Pm>mJz+$ZvII#UI9^6t>u&)Vxe5}lcw9w?SzZ`W#3*UJd*MGog9clIE2PH1=9F&A2*GcD1DX5>sW za-mV2Y2f|pLL)iTNG`PA_xXc&)fclUurxUCb^koW zA^!?{xy-c&y{n{-J@$VyEc=O-s`rZ^L>46fGeGhUu{}R-2V$Ylp zmWX^Nafqyb08<;)K8f>v#(naCvpxWT4&dLCALDKIf8;eT95_Y(z)V$n_&S{%0O*hc z03iQ*0r@dgp16P%{R7kD@6m|~HUL0`9RS#exc#@}$Ml5ko8)m^NIsk=?$7p%KvtdM zQ2;<)bie)A3fNz9C-lIq86&W7Rz8&R2v?q9gqy(qxwvJV#G$CT{i*A?Q4A30nEDsz J<;VZT{|5yr8D; recordList"); + method.addBodyLine(" = " + mapperFieldName + "." + selectByExampleMethod + "(example);"); + method.addBodyLine("if (recordList != null && !recordList.isEmpty()) {"); + method.addBodyLine("aDo = recordList.get(0);"); method.addBodyLine("}"); method.addBodyLine("return aDo;"); implClass.addMethod(method); } - private void generateFindValidByIdMethod(TopLevelClass implClass, String modelClassName, String mapperFieldName, String exampleClassName) { + private void generateFindValidByIdMethod(TopLevelClass implClass, String modelClassName, String mapperFieldName, String exampleClassName, boolean hasBLOBColumns) { Method method = new Method("findValidById"); method.addAnnotation("@Override"); method.setVisibility(JavaVisibility.PUBLIC); method.setReturnType(new FullyQualifiedJavaType(modelClassName)); - method.addParameter(new Parameter(new FullyQualifiedJavaType("long"), "id")); + method.addParameter(new Parameter(new FullyQualifiedJavaType("long"), "guid")); method.addException(new FullyQualifiedJavaType("Throwable")); - method.addBodyLine(modelClassName + " aDo = " + mapperFieldName + ".selectByPrimaryKey(id);"); - method.addBodyLine("if (aDo != null && (aDo.getIsDelete() == 1 || aDo.getIsHidden() == 1)) {"); - method.addBodyLine("return null;"); + method.addBodyLine(modelClassName + " aDo = null;"); + method.addBodyLine(exampleClassName + " example"); + method.addBodyLine(" = new " + exampleClassName + "();"); + method.addBodyLine("example.createCriteria()"); + method.addBodyLine(" .andIsHiddenEqualTo(0)"); + method.addBodyLine(" .andIsDeleteEqualTo(0)"); + method.addBodyLine(" .andGuidEqualTo(guid);"); + String selectByExampleMethod = hasBLOBColumns ? "selectByExampleWithBLOBs" : "selectByExample"; + method.addBodyLine("List<" + modelClassName + "> recordList"); + method.addBodyLine(" = " + mapperFieldName + "." + selectByExampleMethod + "(example);"); + method.addBodyLine("if (recordList != null && !recordList.isEmpty()) {"); + method.addBodyLine("aDo = recordList.get(0);"); method.addBodyLine("}"); method.addBodyLine("return aDo;"); implClass.addMethod(method); } - private void generateFindTrashByIdMethod(TopLevelClass implClass, String modelClassName, String mapperFieldName, String exampleClassName) { + private void generateFindTrashByIdMethod(TopLevelClass implClass, String modelClassName, String mapperFieldName, String exampleClassName, boolean hasBLOBColumns) { Method method = new Method("findTrashById"); method.addAnnotation("@Override"); method.setVisibility(JavaVisibility.PUBLIC); method.setReturnType(new FullyQualifiedJavaType(modelClassName)); - method.addParameter(new Parameter(new FullyQualifiedJavaType("long"), "id")); + method.addParameter(new Parameter(new FullyQualifiedJavaType("long"), "guid")); method.addException(new FullyQualifiedJavaType("Throwable")); - method.addBodyLine(modelClassName + " aDo = " + mapperFieldName + ".selectByPrimaryKey(id);"); - method.addBodyLine("if (aDo != null && (aDo.getIsDelete() == 1 || aDo.getIsHidden() == 0)) {"); - method.addBodyLine("return null;"); + method.addBodyLine(modelClassName + " aDo = null;"); + method.addBodyLine(exampleClassName + " example"); + method.addBodyLine(" = new " + exampleClassName + "();"); + method.addBodyLine("example.createCriteria()"); + method.addBodyLine(" .andIsHiddenEqualTo(1)"); + method.addBodyLine(" .andIsDeleteEqualTo(0)"); + method.addBodyLine(" .andGuidEqualTo(guid);"); + String selectByExampleMethod = hasBLOBColumns ? "selectByExampleWithBLOBs" : "selectByExample"; + method.addBodyLine("List<" + modelClassName + "> recordList"); + method.addBodyLine(" = " + mapperFieldName + "." + selectByExampleMethod + "(example);"); + method.addBodyLine("if (recordList != null && !recordList.isEmpty()) {"); + method.addBodyLine("aDo = recordList.get(0);"); method.addBodyLine("}"); method.addBodyLine("return aDo;"); @@ -584,7 +563,7 @@ public class TapeRepositoryGeneratorPlugin extends PluginAdapter { method.addBodyLine("if (record.getGuid() != null) {"); method.addBodyLine("aDo.setGuid(record.getGuid());"); method.addBodyLine("} else {"); - method.addBodyLine("Long guid = " + snowflakeUtilGenId + ";"); + method.addBodyLine("Long guid = " + guidGeneratorCode + ";"); method.addBodyLine("aDo.setGuid(guid);"); method.addBodyLine("}"); @@ -610,16 +589,18 @@ public class TapeRepositoryGeneratorPlugin extends PluginAdapter { method.addBodyLine("int count = " + mapperFieldName + ".insert(aDo);"); method.addBodyLine("if (count > 0) {"); - method.addBodyLine("return aDo;"); - method.addBodyLine("}"); - method.addBodyLine("// optimistic locking with data version and guid"); method.addBodyLine("record.setGuid(aDo.getGuid());"); method.addBodyLine("record.setDataVersion(aDo.getDataVersion());"); method.addBodyLine("record.setCreateTime(aDo.getCreateTime());"); method.addBodyLine("record.setUpdateTime(aDo.getUpdateTime());"); - method.addBodyLine("throw new Throwable(\"Database insert failed, " + modelClassName + ": \" + aDo);"); - + if (isChangeLogEnable()) { + method.addBodyLine(changeLogContextClassName + ".addLog(\"" + modelClassName + "\","); + method.addBodyLine(" \"insert\", aDo.getGuid(), new HashMap<>());"); + } + method.addBodyLine("return aDo;"); + method.addBodyLine("}"); + method.addBodyLine("throw new Throwable(\"Insert failed, " + modelClassName + ": \" + aDo);"); implClass.addMethod(method); } @@ -640,7 +621,7 @@ public class TapeRepositoryGeneratorPlugin extends PluginAdapter { method.addBodyLine("if (record.getGuid() != null) {"); method.addBodyLine("aDo.setGuid(record.getGuid());"); method.addBodyLine("} else {"); - method.addBodyLine("Long guid = " + snowflakeUtilGenId + ";"); + method.addBodyLine("Long guid = " + guidGeneratorCode + ";"); method.addBodyLine("aDo.setGuid(guid);"); method.addBodyLine("}"); @@ -648,7 +629,6 @@ public class TapeRepositoryGeneratorPlugin extends PluginAdapter { String fieldName = column.getJavaProperty(); String setterMethod = "set" + upperFirst(fieldName); String getterMethod = "get" + upperFirst(fieldName); - if ("guid".equals(fieldName) || "isDelete".equals(fieldName) || "isHidden".equals(fieldName) || "deleteToken".equals(fieldName) || "dataVersion".equals(fieldName) || "createTime".equals(fieldName) || "updateTime".equals(fieldName)) { @@ -668,9 +648,15 @@ public class TapeRepositoryGeneratorPlugin extends PluginAdapter { method.addBodyLine("int count = " + mapperFieldName + ".batchInsert(batch);"); method.addBodyLine("if (count == batch.size()) {"); + if (isChangeLogEnable()) { + method.addBodyLine("for (" + modelClassName + " aDo : batch) {"); + method.addBodyLine(changeLogContextClassName + ".addLog(\"" + modelClassName + "\","); + method.addBodyLine(" \"batchInsert\", aDo.getGuid(), new HashMap<>());"); + method.addBodyLine("}"); + } method.addBodyLine("return batch;"); method.addBodyLine("}"); - method.addBodyLine("throw new Throwable(\"Database batchInsert failed, " + modelClassName + " affected: \" + count + \", expected: \" + batch.size());"); + method.addBodyLine("throw new Throwable(\"Batch insert failed, " + modelClassName + " affected: \" + count + \", expected: \" + batch.size());"); implClass.addMethod(method); } @@ -684,26 +670,32 @@ public class TapeRepositoryGeneratorPlugin extends PluginAdapter { method.addParameter(new Parameter(new FullyQualifiedJavaType(modelClassName), "record")); method.addException(new FullyQualifiedJavaType("Throwable")); - method.addBodyLine(modelClassName + " aDo = findValidById(record.getGuid());"); - method.addBodyLine("if (aDo == null) {"); - method.addBodyLine("throw new Throwable(\"Database record not found, " + modelClassName + " GUID:\" + record.getGuid());"); + method.addBodyLine(modelClassName + " aDo"); + method.addBodyLine(" = " + mapperFieldName + ".selectByPrimaryKey(record.getGuid());"); + method.addBodyLine("if (aDo == null || aDo.getIsDelete() == 1) {"); + method.addBodyLine("throw new Throwable(\"Record not found, " + modelClassName + " GUID:\" + record.getGuid());"); method.addBodyLine("}"); - + if (isChangeLogEnable()) { + method.addBodyLine("Map changeDiff = new HashMap<>();"); + } for (IntrospectedColumn column : introspectedTable.getAllColumns()) { String fieldName = column.getJavaProperty(); String setterMethod = "set" + upperFirst(fieldName); String getterMethod = "get" + upperFirst(fieldName); - if ("guid".equals(fieldName) || "isDelete".equals(fieldName) || "isHidden".equals(fieldName) || "deleteToken".equals(fieldName) || "dataVersion".equals(fieldName) || "createTime".equals(fieldName) || "updateTime".equals(fieldName)) { continue; } method.addBodyLine("if (record." + getterMethod + "() != null) {"); + if (isChangeLogEnable()) { + method.addBodyLine("if (!Objects.equals(record." + getterMethod + "(), aDo." + getterMethod + "())) {"); + method.addBodyLine("changeDiff.put(\"" + fieldName + "\", new Object[]{aDo." + getterMethod + "(), record." + getterMethod + "()});"); + method.addBodyLine("}"); + } method.addBodyLine("aDo." + setterMethod + "(record." + getterMethod + "());"); method.addBodyLine("}"); } - method.addBodyLine(exampleClassName + " updateWhere = new " + exampleClassName + "();"); method.addBodyLine("Integer lockDataVersion = record.getDataVersion();"); method.addBodyLine("if (lockDataVersion == null) {"); @@ -717,14 +709,22 @@ public class TapeRepositoryGeneratorPlugin extends PluginAdapter { method.addBodyLine("// update data version"); method.addBodyLine("record.setDataVersion(aDo.getDataVersion());"); method.addBodyLine("record.setUpdateTime(aDo.getUpdateTime());"); - String updateMethod = hasBLOBColumns ? "updateByExampleWithBLOBs" : "updateByExample"; - method.addBodyLine("return " + mapperFieldName + "." + updateMethod + "(aDo, updateWhere);"); - + if (isChangeLogEnable()) { + method.addBodyLine("int update = " + mapperFieldName + "." + updateMethod + "(aDo, updateWhere);"); + method.addBodyLine("if (update > 0 && !changeDiff.isEmpty()) {"); + method.addBodyLine(changeLogContextClassName + ".addLog(\"" + modelClassName + "\","); + method.addBodyLine(" \"update\", aDo.getGuid(), changeDiff);"); + method.addBodyLine("}"); + method.addBodyLine("return update;"); + } else { + method.addBodyLine("return " + mapperFieldName + "." + updateMethod + "(aDo, updateWhere);"); + } implClass.addMethod(method); } - private void generateUpdateByExampleSelectiveMethod(TopLevelClass implClass, String modelClassName, String exampleClassName, String mapperFieldName) { + private void generateUpdateByExampleSelectiveMethod(TopLevelClass implClass, String modelClassName, String exampleClassName, + String mapperFieldName, IntrospectedTable introspectedTable, boolean hasBLOBColumns) { Method method = new Method("updateByExampleSelective"); method.addAnnotation("@Override"); method.setVisibility(JavaVisibility.PUBLIC); @@ -737,15 +737,62 @@ public class TapeRepositoryGeneratorPlugin extends PluginAdapter { method.addBodyLine("for (" + exampleClassName + ".Criteria criteria : example.getOredCriteria()) {"); method.addBodyLine("criteria.andIsDeleteEqualTo(0).andIsHiddenEqualTo(0);"); method.addBodyLine("}"); - method.addBodyLine("record.setUpdateTime(new Date());"); + + method.addBodyLine("List guidList = " + mapperFieldName); + method.addBodyLine(" .selectPrimaryKeyByExample(example);"); + method.addBodyLine("if (guidList.isEmpty()) {"); + method.addBodyLine("return 0;"); + method.addBodyLine("}"); + method.addBodyLine("example = new " + exampleClassName + "();"); + method.addBodyLine("example.createCriteria()"); + method.addBodyLine(" .andIsDeleteEqualTo(0)"); + method.addBodyLine(" .andIsHiddenEqualTo(0)"); + method.addBodyLine(" .andGuidIn(guidList);"); + if (isChangeLogEnable()) { + String selectByExampleMethod = hasBLOBColumns ? "selectByExampleWithBLOBs" : "selectByExample"; + method.addBodyLine("List<" + modelClassName + "> recordList"); + method.addBodyLine(" = " + mapperFieldName + "." + selectByExampleMethod + "(example);"); + method.addBodyLine("Map> diffGroup = new HashMap<>();"); + method.addBodyLine("for (" + modelClassName + " aDo : recordList) {"); + method.addBodyLine("Map changeDiff = new HashMap<>();"); + for (IntrospectedColumn column : introspectedTable.getAllColumns()) { + String fieldName = column.getJavaProperty(); + String setterMethod = "set" + upperFirst(fieldName); + String getterMethod = "get" + upperFirst(fieldName); + if ("guid".equals(fieldName) || "isDelete".equals(fieldName) || "isHidden".equals(fieldName) + || "deleteToken".equals(fieldName) || "dataVersion".equals(fieldName) + || "createTime".equals(fieldName) || "updateTime".equals(fieldName)) { + continue; + } + method.addBodyLine("if (record." + getterMethod + "() != null && !Objects.equals(record." + getterMethod + "(), aDo." + getterMethod + "())) {"); + method.addBodyLine("changeDiff.put(\"" + fieldName + "\", new Object[]{aDo." + getterMethod + "(), record." + getterMethod + "()});"); + method.addBodyLine("}"); + } + method.addBodyLine("diffGroup.put(aDo.getGuid(), changeDiff);"); + method.addBodyLine("}"); + } + method.addBodyLine("// reset data version to 100, with optimistic locking"); + method.addBodyLine("record.setDataVersion(100);"); method.addBodyLine("// It is not supported to directly modify the following columns"); + method.addBodyLine("record.setUpdateTime(new Date());"); method.addBodyLine("record.setIsHidden(null);"); method.addBodyLine("record.setIsDelete(null);"); method.addBodyLine("record.setDeleteToken(null);"); - method.addBodyLine("record.setDataVersion(null);"); method.addBodyLine("record.setCreateTime(null);"); - method.addBodyLine("return " + mapperFieldName + ".updateByExampleSelective(record, example);"); - + if (isChangeLogEnable()) { + method.addBodyLine("int update = " + mapperFieldName + ".updateByExampleSelective(record, example);"); + method.addBodyLine("if (update > 0) {"); + method.addBodyLine("for (Map.Entry> diffEntry : diffGroup.entrySet()) {"); + method.addBodyLine("if (diffEntry.getValue() != null && !diffEntry.getValue().isEmpty()) {"); + method.addBodyLine(changeLogContextClassName + ".addLog(\"" + modelClassName + "\","); + method.addBodyLine(" \"updateByExampleSelective\", diffEntry.getKey(), diffEntry.getValue());"); + method.addBodyLine("}"); + method.addBodyLine("}"); + method.addBodyLine("}"); + method.addBodyLine("return update;"); + } else { + method.addBodyLine("return " + mapperFieldName + ".updateByExampleSelective(record, example);"); + } implClass.addMethod(method); } @@ -757,7 +804,6 @@ public class TapeRepositoryGeneratorPlugin extends PluginAdapter { method.addParameter(new Parameter(new FullyQualifiedJavaType("long"), "id")); method.addParameter(new Parameter(new FullyQualifiedJavaType("boolean"), "release")); method.addException(new FullyQualifiedJavaType("Throwable")); - method.addBodyLine(modelClassName + " aDo = findValidById(id);"); method.addBodyLine("if (aDo == null) {"); method.addBodyLine("return 0;"); @@ -767,8 +813,14 @@ public class TapeRepositoryGeneratorPlugin extends PluginAdapter { method.addBodyLine("}"); method.addBodyLine("aDo.setIsDelete(1);"); method.addBodyLine("aDo.setUpdateTime(new Date());"); - method.addBodyLine("return " + mapperFieldName + ".updateByPrimaryKey(aDo);"); - + method.addBodyLine("int update = " + mapperFieldName + ".updateByPrimaryKey(aDo);"); + if (isChangeLogEnable()) { + method.addBodyLine("if (update > 0) {"); + method.addBodyLine(changeLogContextClassName + ".addLog(\"" + modelClassName + "\","); + method.addBodyLine(" \"deleteById\", aDo.getGuid(), new HashMap<>());"); + method.addBodyLine("}"); + } + method.addBodyLine("return update;"); implClass.addMethod(method); } @@ -787,8 +839,16 @@ public class TapeRepositoryGeneratorPlugin extends PluginAdapter { method.addBodyLine("aDo.setIsHidden(1);"); method.addBodyLine("aDo.setDeleteToken(aDo.getGuid() + \"\");"); method.addBodyLine("aDo.setUpdateTime(new Date());"); - method.addBodyLine("return " + mapperFieldName + ".updateByPrimaryKey(aDo);"); - + if (isChangeLogEnable()) { + method.addBodyLine("int update = " + mapperFieldName + ".updateByPrimaryKey(aDo);"); + method.addBodyLine("if (update > 0) {"); + method.addBodyLine(changeLogContextClassName + ".addLog(\"" + modelClassName + "\","); + method.addBodyLine(" \"trashById\", aDo.getGuid(), new HashMap<>());"); + method.addBodyLine("}"); + method.addBodyLine("return update;"); + } else { + method.addBodyLine("return " + mapperFieldName + ".updateByPrimaryKey(aDo);"); + } implClass.addMethod(method); } @@ -810,7 +870,124 @@ public class TapeRepositoryGeneratorPlugin extends PluginAdapter { method.addBodyLine("aDo.setIsHidden(0);"); method.addBodyLine("aDo.setDeleteToken(\"VALID\");"); method.addBodyLine("aDo.setUpdateTime(new Date());"); - method.addBodyLine("return " + mapperFieldName + ".updateByPrimaryKey(aDo);"); + if (isChangeLogEnable()) { + method.addBodyLine("int update = " + mapperFieldName + ".updateByPrimaryKey(aDo);"); + method.addBodyLine("if (update > 0) {"); + method.addBodyLine(changeLogContextClassName + ".addLog(\"" + modelClassName + "\","); + method.addBodyLine(" \"recoverById\", aDo.getGuid(), new HashMap<>());"); + method.addBodyLine("}"); + method.addBodyLine("return update;"); + } else { + method.addBodyLine("return " + mapperFieldName + ".updateByPrimaryKey(aDo);"); + } + implClass.addMethod(method); + } + + private void generateDeleteAllMethod(TopLevelClass implClass, String modelClassName, String exampleClassName, String mapperFieldName) { + Method method = new Method("deleteAll"); + method.addAnnotation("@Override"); + method.setVisibility(JavaVisibility.PUBLIC); + method.setReturnType(new FullyQualifiedJavaType("int")); + method.addParameter(new Parameter(new FullyQualifiedJavaType(exampleClassName), "example")); + method.addParameter(new Parameter(new FullyQualifiedJavaType("boolean"), "release")); + method.addException(new FullyQualifiedJavaType("Throwable")); + + // 方法体 + method.addBodyLine("if (release) {"); + method.addBodyLine("return " + mapperFieldName + ".deleteByExample(example);"); + method.addBodyLine("}"); + method.addBodyLine("for (" + exampleClassName + ".Criteria criteria : example.getOredCriteria()) {"); + method.addBodyLine("criteria.andIsDeleteEqualTo(0).andIsHiddenEqualTo(1);"); + method.addBodyLine("}"); + method.addBodyLine("List guidList = " + mapperFieldName + ".selectPrimaryKeyByExample(example);"); + method.addBodyLine("if (guidList.isEmpty()) {"); + method.addBodyLine("return 0;"); + method.addBodyLine("}"); + method.addBodyLine(modelClassName + " " + lowerFirst(modelClassName) + " = new " + modelClassName + "();"); + method.addBodyLine(lowerFirst(modelClassName) + ".setIsDelete(1);"); + method.addBodyLine(lowerFirst(modelClassName) + ".setUpdateTime(new Date());"); + if (isChangeLogEnable()) { + method.addBodyLine("int update = " + mapperFieldName + ".updateByExampleSelective(" + lowerFirst(modelClassName) + ", example);"); + method.addBodyLine("if (update > 0) {"); + method.addBodyLine("for (Long guid : guidList) {"); + method.addBodyLine(changeLogContextClassName + ".addLog(\"" + modelClassName + "\","); + method.addBodyLine(" \"deleteAll\", guid, new HashMap<>());"); + method.addBodyLine("}"); + method.addBodyLine("}"); + method.addBodyLine("return update;"); + } else { + method.addBodyLine("return " + mapperFieldName + ".updateByExampleSelective(" + lowerFirst(modelClassName) + ", example);"); + } + implClass.addMethod(method); + } + + private void generateTrashAllMethod(TopLevelClass implClass, String modelClassName, String exampleClassName, String mapperFieldName) { + Method method = new Method("trashAll"); + method.addAnnotation("@Override"); + method.setVisibility(JavaVisibility.PUBLIC); + method.setReturnType(new FullyQualifiedJavaType("int")); + method.addParameter(new Parameter(new FullyQualifiedJavaType(exampleClassName), "example")); + method.addException(new FullyQualifiedJavaType("Throwable")); + + // 方法体 + method.addBodyLine("for (" + exampleClassName + ".Criteria criteria : example.getOredCriteria()) {"); + method.addBodyLine("criteria.andIsDeleteEqualTo(0).andIsHiddenEqualTo(0);"); + method.addBodyLine("}"); + method.addBodyLine("List guidList = " + mapperFieldName + ".selectPrimaryKeyByExample(example);"); + method.addBodyLine("if (guidList.isEmpty()) {"); + method.addBodyLine("return 0;"); + method.addBodyLine("}"); + method.addBodyLine(modelClassName + " " + lowerFirst(modelClassName) + " = new " + modelClassName + "();"); + method.addBodyLine(lowerFirst(modelClassName) + ".setIsHidden(1);"); + method.addBodyLine(lowerFirst(modelClassName) + ".setDeleteToken(" + guidGeneratorCode + " + \"\");"); + method.addBodyLine(lowerFirst(modelClassName) + ".setUpdateTime(new Date());"); + if (isChangeLogEnable()) { + method.addBodyLine("int update = " + mapperFieldName + ".updateByExampleSelective(" + lowerFirst(modelClassName) + ", example);"); + method.addBodyLine("if (update > 0) {"); + method.addBodyLine("for (Long guid : guidList) {"); + method.addBodyLine(changeLogContextClassName + ".addLog(\"" + modelClassName + "\","); + method.addBodyLine(" \"trashAll\", guid, new HashMap<>());"); + method.addBodyLine("}"); + method.addBodyLine("}"); + method.addBodyLine("return update;"); + } else { + method.addBodyLine("return " + mapperFieldName + ".updateByExampleSelective(" + lowerFirst(modelClassName) + ", example);"); + } + implClass.addMethod(method); + } + + private void generateRecoverAllMethod(TopLevelClass implClass, String modelClassName, String exampleClassName, String mapperFieldName) { + Method method = new Method("recoverAll"); + method.addAnnotation("@Override"); + method.setVisibility(JavaVisibility.PUBLIC); + method.setReturnType(new FullyQualifiedJavaType("int")); + method.addParameter(new Parameter(new FullyQualifiedJavaType(exampleClassName), "example")); + method.addException(new FullyQualifiedJavaType("Throwable")); + + // 方法体 + method.addBodyLine("for (" + exampleClassName + ".Criteria criteria : example.getOredCriteria()) {"); + method.addBodyLine("criteria.andIsDeleteEqualTo(0).andIsHiddenEqualTo(1);"); + method.addBodyLine("}"); + method.addBodyLine("List guidList = " + mapperFieldName + ".selectPrimaryKeyByExample(example);"); + method.addBodyLine("if (guidList.isEmpty()) {"); + method.addBodyLine("return 0;"); + method.addBodyLine("}"); + method.addBodyLine(modelClassName + " " + lowerFirst(modelClassName) + " = new " + modelClassName + "();"); + method.addBodyLine(lowerFirst(modelClassName) + ".setIsHidden(0);"); + method.addBodyLine(lowerFirst(modelClassName) + ".setDeleteToken(\"VALID\");"); + method.addBodyLine(lowerFirst(modelClassName) + ".setUpdateTime(new Date());"); + if (isChangeLogEnable()) { + method.addBodyLine("int update = " + mapperFieldName + ".updateByExampleSelective(" + lowerFirst(modelClassName) + ", example);"); + method.addBodyLine("if (update > 0) {"); + method.addBodyLine("for (Long guid : guidList) {"); + method.addBodyLine(changeLogContextClassName + ".addLog(\"" + modelClassName + "\","); + method.addBodyLine(" \"recoverAll\", guid, new HashMap<>());"); + method.addBodyLine("}"); + method.addBodyLine("}"); + method.addBodyLine("return update;"); + } else { + method.addBodyLine("return " + mapperFieldName + ".updateByExampleSelective(" + lowerFirst(modelClassName) + ", example);"); + } implClass.addMethod(method); } diff --git a/src/main/java/com/iqudoo/framework/mybatis/TapeRepoviewGeneratorPlugin.java b/src/main/java/com/iqudoo/framework/mybatis/TapeRepoviewGeneratorPlugin.java index 7bcd3e7..214f505 100644 --- a/src/main/java/com/iqudoo/framework/mybatis/TapeRepoviewGeneratorPlugin.java +++ b/src/main/java/com/iqudoo/framework/mybatis/TapeRepoviewGeneratorPlugin.java @@ -22,10 +22,10 @@ public class TapeRepoviewGeneratorPlugin extends PluginAdapter { // 视图Repo包配置(可通过配置文件自定义) private String slowQueryLoggerTime = "300"; private String slowQueryLoggerLevel = "error"; - private String facadeRepoviewPackage = "com.iqudoo.platform.application.facade.repoview"; - private String domainRepoviewPackage = "com.iqudoo.platform.application.domain.repoview"; private String modelPackage = "com.iqudoo.platform.application.database.model"; private String mapperPackage = "com.iqudoo.platform.application.database.mapper"; + private String facadeViewRepositoryPackage = "com.iqudoo.platform.application.facade.repoview"; + private String domainViewRepositoryPackage = "com.iqudoo.platform.application.domain.repoview"; private String targetProject = "src/main/java"; // 1.4.1版本专用格式化器 @@ -50,8 +50,8 @@ public class TapeRepoviewGeneratorPlugin extends PluginAdapter { if (!UtilTools.inArray(new String[]{"error", "warn", "debug", "info"}, slowQueryLoggerLevel)) { slowQueryLoggerLevel = "error"; } - facadeRepoviewPackage = stringConfig("facadeRepoviewPackage", facadeRepoviewPackage); - domainRepoviewPackage = stringConfig("domainRepoviewPackage", domainRepoviewPackage); + facadeViewRepositoryPackage = stringConfig("facadeRepoviewPackage", facadeViewRepositoryPackage); + domainViewRepositoryPackage = stringConfig("domainRepoviewPackage", domainViewRepositoryPackage); modelPackage = stringConfig("modelPackage", modelPackage); mapperPackage = stringConfig("mapperPackage", mapperPackage); targetProject = stringConfig("targetProject", targetProject); @@ -118,8 +118,8 @@ public class TapeRepoviewGeneratorPlugin extends PluginAdapter { generatedJavaFiles.add(implFile); // 4. 手动写入磁盘(兼容1.4.1) - generateJavaFileToDisk(repoInterface, facadeRepoviewPackage); - generateJavaFileToDisk(repoImpl, domainRepoviewPackage); + generateJavaFileToDisk(repoInterface, facadeViewRepositoryPackage); + generateJavaFileToDisk(repoImpl, domainViewRepositoryPackage); return generatedJavaFiles; } @@ -128,7 +128,7 @@ public class TapeRepoviewGeneratorPlugin extends PluginAdapter { * 核心修改:生成视图Repo接口(移除继承,手动添加指定方法) */ private Interface generateRepoViewInterface(String interfaceName, String modelClassName, String exampleClassName) { - Interface repoInterface = new Interface(facadeRepoviewPackage + "." + interfaceName); + Interface repoInterface = new Interface(facadeViewRepositoryPackage + "." + interfaceName); repoInterface.setVisibility(JavaVisibility.PUBLIC); // 添加必要的导入包(仅保留model、example、List) @@ -187,7 +187,7 @@ public class TapeRepoviewGeneratorPlugin extends PluginAdapter { boolean hasBLOBColumns ) { - TopLevelClass implClass = new TopLevelClass(domainRepoviewPackage + "." + implClassName); + TopLevelClass implClass = new TopLevelClass(domainViewRepositoryPackage + "." + implClassName); implClass.setVisibility(JavaVisibility.PUBLIC); implClass.addAnnotation("@SuppressWarnings(\"DuplicatedCode\")"); implClass.addAnnotation("@Repository"); @@ -196,7 +196,7 @@ public class TapeRepoviewGeneratorPlugin extends PluginAdapter { addImportPackages(implClass, modelClassName, exampleClassName, mapperClassName, interfaceName); // 实现Repo接口 - FullyQualifiedJavaType superInterface = new FullyQualifiedJavaType(facadeRepoviewPackage + "." + interfaceName); + FullyQualifiedJavaType superInterface = new FullyQualifiedJavaType(facadeViewRepositoryPackage + "." + interfaceName); implClass.addSuperInterface(superInterface); // slow query logger @@ -232,7 +232,7 @@ public class TapeRepoviewGeneratorPlugin extends PluginAdapter { implClass.addImportedType(new FullyQualifiedJavaType(modelPackage + "." + modelClassName)); implClass.addImportedType(new FullyQualifiedJavaType(modelPackage + "." + exampleClassName)); // Repo接口 - implClass.addImportedType(new FullyQualifiedJavaType(facadeRepoviewPackage + "." + interfaceName)); + implClass.addImportedType(new FullyQualifiedJavaType(facadeViewRepositoryPackage + "." + interfaceName)); // 注解&工具类 implClass.addImportedType(new FullyQualifiedJavaType("org.springframework.stereotype.Repository")); implClass.addImportedType(new FullyQualifiedJavaType("javax.annotation.Resource"));