listByGroupName(String groupName) {
+ return templateConfigMapper.listByGroupName(groupName);
+ }
+
+
+ public synchronized void save(TemplateConfig templateConfig) {
+ handleContent(templateConfig);
+ String name = templateConfig.getName();
+ TemplateConfig existObj = templateConfigMapper.getByName(name, templateConfig.getGroupId());
+ if(existObj == null) {
+ this.insert(templateConfig);
+ } else {
+ templateConfig.setId(existObj.getId());
+ this.update(templateConfig);
+ }
+ }
+
+ /**
+ * 复制模板
+ * @param templateConfig
+ */
+ public void copy(TemplateConfig templateConfig) {
+ Integer id = templateConfig.getId();
+ TemplateConfig templateConfigById = this.getById(id);
+ if(templateConfigById == null){
+ throw new RuntimeException("要复制的模板不存在");
+ }
+ templateConfigById.setId(null);
+ templateConfigById.setName(templateConfig.getName());
+ this.save(templateConfigById);
+ }
+
+ /**
+ * 解析模板元信息, 即开始第一行是注释时
+ *
+ * 格式: ## filename=#{xxx}.java, folder=entity
+ */
+ private void handleContent(TemplateConfig template) {
+ String content = StringUtil.trimLeadingWhitespace(template.getContent());
+ // 解析元信息
+ Map data = TemplateMetaUtils.parseMetaContent(content);
+ if (StringUtils.isEmpty(template.getFileName())) {
+ template.setFileName(data.get("filename"));
+ }
+ if (StringUtils.isEmpty(template.getFolder())) {
+ template.setFolder(data.get("folder"));
+ }
+ // 设置默认值
+ if (StringUtils.isEmpty(template.getFileName())) {
+ template.setFileName(template.getName());
+ }
+ if (StringUtils.isEmpty(template.getFolder())) {
+ template.setFolder(template.getName());
+ }
+ }
+}
diff --git a/sop-admin/sop-code-gen/src/main/java/com/gitee/gen/service/TemplateGroupService.java b/sop-admin/sop-code-gen/src/main/java/com/gitee/gen/service/TemplateGroupService.java
new file mode 100644
index 00000000..dfa46f75
--- /dev/null
+++ b/sop-admin/sop-code-gen/src/main/java/com/gitee/gen/service/TemplateGroupService.java
@@ -0,0 +1,109 @@
+package com.gitee.gen.service;
+
+import com.gitee.gen.entity.TemplateGroup;
+import com.gitee.gen.mapper.TemplateConfigMapper;
+import com.gitee.gen.mapper.TemplateGroupMapper;
+import org.apache.ibatis.solon.annotation.Db;
+import org.noear.solon.annotation.Component;
+
+import java.util.List;
+
+/**
+ * @author : zsljava
+ * @date Date : 2020-12-15 9:50
+ * @Description: TODO
+ */
+@Component
+public class TemplateGroupService {
+
+ @Db
+ private TemplateGroupMapper templateGroupMapper;
+
+ @Db
+ private TemplateConfigMapper templateConfigMapper;
+
+ /**
+ * 查询所有记录
+ *
+ * @return 返回集合,没有返回空List
+ */
+ public List listAll() {
+ return templateGroupMapper.listAll();
+ }
+
+
+ /**
+ * 根据主键查询
+ *
+ * @param id 主键
+ * @return 返回记录,没有返回null
+ */
+ public TemplateGroup getById(Integer id) {
+ return templateGroupMapper.getById(id);
+ }
+
+ /**
+ * 新增,插入所有字段
+ *
+ * @param templateGroup 新增的记录
+ * @return 返回影响行数
+ */
+ public int insert(TemplateGroup templateGroup) {
+ return templateGroupMapper.insert(templateGroup);
+ }
+
+ /**
+ * 新增,忽略null字段
+ *
+ * @param templateGroup 新增的记录
+ * @return 返回影响行数
+ */
+ public int insertIgnoreNull(TemplateGroup templateGroup) {
+ templateGroup.setIsDeleted(0);
+ return templateGroupMapper.insertIgnoreNull(templateGroup);
+ }
+
+ /**
+ * 修改,修改所有字段
+ *
+ * @param templateGroup 修改的记录
+ * @return 返回影响行数
+ */
+ public int update(TemplateGroup templateGroup) {
+ return templateGroupMapper.update(templateGroup);
+ }
+
+ /**
+ * 修改,忽略null字段
+ *
+ * @param templateGroup 修改的记录
+ * @return 返回影响行数
+ */
+ public int updateIgnoreNull(TemplateGroup templateGroup) {
+ return templateGroupMapper.updateIgnoreNull(templateGroup);
+ }
+
+ /**
+ * 删除记录
+ *
+ * @param templateGroup 待删除的记录
+ * @return 返回影响行数
+ */
+ public int delete(TemplateGroup templateGroup) {
+ return templateGroupMapper.delete(templateGroup);
+ }
+
+ public int deleteGroup(TemplateGroup templateGroup) {
+ List templateGroups = this.listAll();
+ if (templateGroups.size() == 1) {
+ throw new RuntimeException("无法删除,必须要有一个模板组");
+ }
+ int delete = templateGroupMapper.delete(templateGroup);
+ templateConfigMapper.deleteByGroupId(templateGroup.getId());
+ return delete;
+ }
+
+ public TemplateGroup getByName(String name) {
+ return templateGroupMapper.getByName(name);
+ }
+}
diff --git a/sop-admin/sop-code-gen/src/main/java/com/gitee/gen/service/TypeConfigService.java b/sop-admin/sop-code-gen/src/main/java/com/gitee/gen/service/TypeConfigService.java
new file mode 100644
index 00000000..42064c71
--- /dev/null
+++ b/sop-admin/sop-code-gen/src/main/java/com/gitee/gen/service/TypeConfigService.java
@@ -0,0 +1,97 @@
+package com.gitee.gen.service;
+
+import com.gitee.gen.entity.TypeConfig;
+import com.gitee.gen.gen.converter.ColumnTypeConverter;
+import com.gitee.gen.gen.converter.DbColumnTypeConverter;
+import com.gitee.gen.mapper.TypeConfigMapper;
+import org.apache.ibatis.solon.annotation.Db;
+import org.noear.solon.annotation.Component;
+
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+@Component
+public class TypeConfigService {
+
+ @Db
+ private TypeConfigMapper typeConfigMapper;
+
+ /**
+ * 查询所有记录
+ *
+ * @return 返回集合,没有返回空List
+ */
+ public List listAll() {
+ return typeConfigMapper.listAll();
+ }
+
+
+ /**
+ * 根据主键查询
+ *
+ * @param id 主键
+ * @return 返回记录,没有返回null
+ */
+ public TypeConfig getById(Integer id) {
+ return typeConfigMapper.getById(id);
+ }
+
+ /**
+ * 新增,插入所有字段
+ *
+ * @param typeConfig 新增的记录
+ * @return 返回影响行数
+ */
+ public int insert(TypeConfig typeConfig) {
+ return typeConfigMapper.insert(typeConfig);
+ }
+
+ /**
+ * 新增,忽略null字段
+ *
+ * @param typeConfig 新增的记录
+ * @return 返回影响行数
+ */
+ public int insertIgnoreNull(TypeConfig typeConfig) {
+ return typeConfigMapper.insertIgnoreNull(typeConfig);
+ }
+
+ /**
+ * 修改,修改所有字段
+ *
+ * @param typeConfig 修改的记录
+ * @return 返回影响行数
+ */
+ public int update(TypeConfig typeConfig) {
+ return typeConfigMapper.update(typeConfig);
+ }
+
+ /**
+ * 修改,忽略null字段
+ *
+ * @param typeConfig 修改的记录
+ * @return 返回影响行数
+ */
+ public int updateIgnoreNull(TypeConfig typeConfig) {
+ return typeConfigMapper.updateIgnoreNull(typeConfig);
+ }
+
+ /**
+ * 删除记录
+ *
+ * @param typeConfig 待删除的记录
+ * @return 返回影响行数
+ */
+ public int delete(TypeConfig typeConfig) {
+ return typeConfigMapper.delete(typeConfig);
+ }
+
+ public ColumnTypeConverter buildColumnTypeConverter() {
+ List typeConfigs = typeConfigMapper.listAll();
+ Map map = typeConfigs.stream()
+ .collect(Collectors.toMap(TypeConfig::getDbType, Function.identity(), (v1, v2) -> v2));
+ return new DbColumnTypeConverter(map);
+ }
+}
diff --git a/sop-admin/sop-code-gen/src/main/java/com/gitee/gen/service/UpgradeService.java b/sop-admin/sop-code-gen/src/main/java/com/gitee/gen/service/UpgradeService.java
new file mode 100644
index 00000000..be0e0488
--- /dev/null
+++ b/sop-admin/sop-code-gen/src/main/java/com/gitee/gen/service/UpgradeService.java
@@ -0,0 +1,323 @@
+package com.gitee.gen.service;
+
+import com.gitee.gen.entity.ColumnInfo;
+import com.gitee.gen.entity.SystemConfig;
+import com.gitee.gen.mapper.UpgradeMapper;
+import com.gitee.gen.util.SystemUtil;
+import org.apache.ibatis.solon.annotation.Db;
+import org.noear.solon.Solon;
+import org.noear.solon.Utils;
+import org.noear.solon.annotation.Component;
+import org.noear.solon.annotation.Inject;
+import org.noear.solon.core.AppClassLoader;
+import org.noear.solon.core.util.IoUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.Objects;
+
+
+/**
+ * 升级
+ *
+ * @author tanghc
+ */
+@Component
+public class UpgradeService {
+
+ private static final Logger log = LoggerFactory.getLogger(UpgradeService.class);
+
+ public static final String TABLE_DATASOURCE_CONFIG = "datasource_config";
+ public static final String TABLE_TEMPLATE_CONFIG = "template_config";
+ public static final String TABLE_TEMPLATE_GROUP = "template_group";
+ public static final String TABLE_GENERATE_HISTORY = "generate_history";
+
+ // 版本号,格式:xx.yy.zz,如:1.16.0,1.16.11
+ private static final int CURRENT_VERSION = 200000;
+ public static final String GEN_VERSION = "GEN_VERSION";
+ private static final String DB_FILE_NAME = "gen.db";
+
+ @Db
+ private UpgradeMapper upgradeMapper;
+
+ @Inject
+ private SystemConfigService systemConfigService;
+
+ @Inject("${dbms.enable:false}")
+ private boolean useDbms;
+
+ @Inject("${gen.db2.driverClassName}")
+ private String driverClassName;
+
+ @Inject("${dbms.database:gen}")
+ private String dbName;
+
+ @Inject("${gen.init-file:gen_init.db}")
+ private String initFileName;
+
+ public void init() {
+ this.createTable("system_config");
+ int oldVersion = getOldVersion();
+ upgrade(oldVersion);
+ }
+
+ public void initDatabase() {
+ if (useDbms) {
+ log.info("使用DBMS,跳过sqlit3文件初始化");
+ return;
+ }
+ File dbFile = getDbFile();
+ if (!dbFile.exists()) {
+ try {
+ dbFile.createNewFile();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ try (InputStream inputStream = AppClassLoader.getSystemResourceAsStream(initFileName)) {
+ Path path = dbFile.toPath();
+ log.info("初始化数据库文件, path={}", path);
+ OutputStream outputStream = Files.newOutputStream(path);
+ IoUtil.transferTo(inputStream, outputStream);
+ outputStream.close();
+ } catch (IOException e) {
+ throw new RuntimeException("初始化数据库失败", e);
+ }
+ }
+ }
+
+ private File getDbFile() {
+ String localDbPath = getLocalDbPath();
+ log.info("SQLite3 Database file:{}", localDbPath);
+ return new File(localDbPath);
+ }
+
+ public static String getLocalDbPath() {
+ String dbPath = Solon.cfg().get("LOCAL_DB", "");
+ if (Utils.isNotBlank(dbPath)) {
+ return dbPath.replace("${user.home}", SystemUtil.getUserHome());
+ } else {
+ return SystemUtil.getUserDir() + "/" + DB_FILE_NAME;
+ }
+ }
+
+ private int getOldVersion() {
+ return systemConfigService.getValue(GEN_VERSION)
+ .map(Integer::parseInt)
+ .orElse(0);
+ }
+
+ private void writeNewVersion() {
+ SystemConfig systemConfig = new SystemConfig();
+ systemConfig.setConfigKey(GEN_VERSION);
+ systemConfig.setConfigValue(String.valueOf(CURRENT_VERSION));
+ systemConfig.setRemark("版本号");
+ systemConfigService.saveOrUpdate(systemConfig);
+ }
+
+ /**
+ * 升级
+ */
+ public void upgrade(int oldVersion) {
+ if (oldVersion == 0) {
+ upgradeV1_4_0();
+ upgradeV1_4_12();
+ upgradeV1_4_17();
+ upgradeV1_5_2();
+ }
+ doUpgrade(oldVersion);
+ writeNewVersion();
+ }
+
+ private void doUpgrade(int oldVersion) {
+ upgradeV1_6_0(oldVersion);
+ upgradeV2_0_0(oldVersion);
+ }
+
+ private void upgradeV2_0_0(int oldVersion) {
+ if (oldVersion < 200000) {
+ createTable("type_config", () -> {
+ runSql("insert into type_config(db_type, base_type, box_type) values\n" +
+ "('bit', 'boolean', 'Boolean')\n" +
+ ",('boolean', 'boolean', 'Boolean')\n" +
+ ",('tinyint', 'int', 'Integer')\n" +
+ ",('smallint', 'int', 'Integer')\n" +
+ ",('int', 'int', 'Integer')\n" +
+ ",('bigint', 'long', 'Long')\n" +
+ ",('float', 'float', 'Float')\n" +
+ ",('double', 'double', 'Double')\n" +
+ ",('decimal', 'BigDecimal', 'BigDecimal')\n" +
+ ",('varchar', 'String', 'String')\n" +
+ ",('datetime', 'Date', 'Date')\n" +
+ ",('date', 'Date', 'Date')\n" +
+ ",('blob', 'String', 'String')\n" +
+ ",('jsonb', 'String', 'String')");
+ });
+ }
+ }
+
+ private void upgradeV1_6_0(int oldVersion) {
+ if (oldVersion < 101600) {
+ this.addColumn(TABLE_DATASOURCE_CONFIG, "db_desc", "varchar(64)");
+ this.addColumn(TABLE_DATASOURCE_CONFIG, "db_group_name", "varchar(64)");
+ }
+ }
+
+ private void upgradeV1_4_17() {
+ this.addColumn(TABLE_TEMPLATE_CONFIG, "folder", "varchar(64)");
+ }
+
+ private void upgradeV1_4_12() {
+ this.addColumn(TABLE_DATASOURCE_CONFIG, "schema_name", "varchar(100)");
+ }
+
+ /**
+ * 升级v1.4.0
+ */
+ private void upgradeV1_4_0() {
+ this.createTable(TABLE_GENERATE_HISTORY);
+ boolean isCreate = this.createTable(TABLE_TEMPLATE_GROUP);
+ if (isCreate) {
+ runSql("INSERT INTO `template_group` (`id`, `group_name`, `is_deleted`) VALUES (1,'default',0)");
+ }
+
+ this.addColumn(TABLE_DATASOURCE_CONFIG, "package_name", "varchar(100)");
+ this.addColumn(TABLE_DATASOURCE_CONFIG, "del_prefix", "varchar(200)");
+ this.addColumn(TABLE_DATASOURCE_CONFIG, "group_id", "int");
+
+ this.addColumn(TABLE_TEMPLATE_CONFIG, "group_id", "int");
+ this.addColumn(TABLE_TEMPLATE_CONFIG, "group_name", "varchar(100)");
+ runSql("update template_config set group_id=1,group_name='default' where group_id IS NULL");
+ }
+
+ /**
+ * 升级v1.5.2
+ * 1、前端:修复修改数据源时候不管什么数据库都带出oracle数据库
+ * 2、前端:新增oracle类型数据库 数据库角色可以为空
+ * 3、前端:修复数据库类型为oracle数据库时候测试连接服务器字段展示undefined问题
+ * 4、前后端:新增author作者名配置,方便模板中插入作者
+ * 5、后端:修复postgresql数据库表如果没有设置主键无法获取列数组问题
+ */
+ private void upgradeV1_5_2() {
+ this.addColumn(TABLE_DATASOURCE_CONFIG, "author", "varchar(255)");
+ }
+
+ private void runSql(String sql) {
+ upgradeMapper.runSql(sql);
+ }
+
+ /**
+ * 添加表字段
+ *
+ * @param tableName 表名
+ * @param columnName 字段名
+ * @param type 字段类型,如:varchar(128),text,integer
+ * @return 返回true,插入成功
+ */
+ public boolean addColumn(String tableName, String columnName, String type) {
+ if (!isColumnExist(tableName, columnName)) {
+ try {
+ if (isMysql()) {
+ upgradeMapper.addColumnMysql(tableName, columnName, type);
+ } else if (isDm()) {
+ upgradeMapper.addColumnDm(tableName, columnName, type);
+ } else {
+ upgradeMapper.addColumn(tableName, columnName, type);
+ }
+ } catch (Exception e) {
+ log.error("add column error, tableName={}, columnName={}, type={}",
+ tableName, columnName, type, e);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * 创建表
+ *
+ * @param tableName 表名
+ * @return 创建成功返回true
+ */
+ public boolean createTable(String tableName, Runnable afterCreated) {
+ if (!isTableExist(tableName)) {
+ String sql = this.loadDDL(tableName);
+ upgradeMapper.runSql(sql);
+ if (afterCreated != null) {
+ afterCreated.run();
+ }
+ return true;
+ }
+ return false;
+ }
+
+ public boolean createTable(String tableName) {
+ return createTable(tableName, null);
+ }
+
+ private String loadDDL(String tableName) {
+ String tmp_dm = "ddl_%s_dm.txt";
+ String tmp_mysql = "ddl_%s_mysql.txt";
+ String tmp_sqlite = "ddl_%s_sqlite.txt";
+ String tmp = isDm() ? tmp_dm : (isMysql() ? tmp_mysql : tmp_sqlite);
+ String filename = "upgrade/" + String.format(tmp, tableName);
+ InputStream inputStream = AppClassLoader.getSystemResourceAsStream(filename);
+ if (inputStream == null) {
+ throw new RuntimeException("找不到文件:" + filename);
+ }
+ try {
+ return IoUtil.transferToString(inputStream);
+ } catch (IOException e) {
+ e.printStackTrace();
+ throw new RuntimeException("打开文件出错", e);
+ }
+ }
+
+ /**
+ * 判断列是否存在
+ *
+ * @param tableName 表名
+ * @param columnName 列名
+ * @return true:存在
+ */
+ public boolean isColumnExist(String tableName, String columnName) {
+ List columnInfoList = isDm() ? upgradeMapper.listColumnInfoDm(tableName) :
+ (isMysql() ? upgradeMapper.listColumnInfoMysql(tableName, dbName) : upgradeMapper.listColumnInfo(tableName));
+ return columnInfoList
+ .stream()
+ .anyMatch(columnInfo -> Objects.equals(columnInfo.getName(), columnName));
+ }
+
+ /**
+ * 表是否存在
+ *
+ * @param tableName
+ * @return
+ */
+ public boolean isTableExist(String tableName) {
+ List tableNameList;
+ if (isMysql()) {
+ tableNameList = upgradeMapper.listTableNameMysql();
+ } else if (isDm()) {
+ tableNameList = upgradeMapper.listTableNameDm();
+ } else {
+ tableNameList = upgradeMapper.listTableName();
+ }
+ return tableNameList != null && tableNameList.contains(tableName);
+ }
+
+ private boolean isMysql() {
+ return useDbms && this.driverClassName.contains("mysql");
+ }
+
+ private boolean isDm() {
+ return useDbms && this.driverClassName.contains("dm");
+ }
+
+}
diff --git a/sop-admin/sop-code-gen/src/main/java/com/gitee/gen/util/BeanUtil.java b/sop-admin/sop-code-gen/src/main/java/com/gitee/gen/util/BeanUtil.java
new file mode 100644
index 00000000..38c2ae36
--- /dev/null
+++ b/sop-admin/sop-code-gen/src/main/java/com/gitee/gen/util/BeanUtil.java
@@ -0,0 +1,372 @@
+package com.gitee.gen.util;
+
+import org.apache.ibatis.logging.Log;
+import org.apache.ibatis.logging.LogFactory;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.sql.Timestamp;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.stream.Collectors;
+
+/**
+ * @author thc
+ */
+public class BeanUtil {
+ private static final Log logger = LogFactory.getLog(BeanUtil.class);
+
+ private static final String PREFIX_GET = "get";
+ private static final String PREFIX_SET = "set";
+ private static final String PREFIX_IS = "is";
+ private static final String GET_CLASS_NAME = "getClass";
+
+ /**
+ * 属性拷贝,第一个参数中的属性值拷贝到第二个参数中
+ * 注意:当第一个参数中的属性有null值时,不会拷贝进去
+ *
+ * @param source 源对象
+ * @param target 目标对象
+ */
+ public static void copyPropertiesIgnoreNull(Object source, Object target) {
+ copyProperties(source, target, true);
+ }
+
+ /**
+ * 属性拷贝,第一个参数中的属性值拷贝到第二个参数中
+ * 注意:当第一个参数中的属性有null值时,不会拷贝进去
+ *
+ * @param source 源对象
+ * @param target 目标对象
+ * @param ignoreNullValue 是否忽略null值
+ */
+ private static void copyPropertiesForMap(Map source, Object target, boolean ignoreNullValue) {
+ Objects.requireNonNull(source, "Source must not be null");
+ Objects.requireNonNull(target, "Target must not be null");
+ Method[] targetMethods = target.getClass().getMethods();
+ Map setMethodMap = buildSetMethodMap(targetMethods);
+ Set keys = source.keySet();
+ try {
+ for (String column : keys) {
+ Object sourceFieldValue = source.get(column);
+ if (ignoreNullValue && sourceFieldValue == null) {
+ continue;
+ }
+ MethodBean methodBean = setMethodMap.get(column);
+ // 如果为null,转成驼峰再获取一次
+ if (methodBean == null) {
+ String fieldName = FieldUtil.underlineFilter(column);
+ methodBean = setMethodMap.get(fieldName);
+ }
+ if (methodBean == null) {
+ continue;
+ }
+ Class> paramType = methodBean.getParamType();
+ Object targetVal = parseValue(sourceFieldValue, paramType);
+ Method targetMethod = methodBean.getMethod();
+ setValue(targetMethod, target, targetVal);
+ }
+ } catch (Exception e) {
+ logger.error("copyPropertiesForMap error, source=" + source, e);
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * 属性拷贝,第一个参数中的属性值拷贝到第二个参数中
+ * 注意:当第一个参数中的属性有null值时,不会拷贝进去
+ *
+ * @param source 源对象
+ * @param target 目标对象
+ * @param ignoreNullValue 是否忽略null值
+ */
+ private static void copyProperties(Object source, Object target, boolean ignoreNullValue) {
+ Objects.requireNonNull(source, "Source must not be null");
+ Objects.requireNonNull(target, "Target must not be null");
+ if (source instanceof Map) {
+ copyPropertiesForMap((Map) source, target, ignoreNullValue);
+ return;
+ }
+ Method[] sourceMethods = source.getClass().getMethods();
+ Method[] targetMethods = target.getClass().getMethods();
+ Map setMethodMap = buildSetMethodMap(targetMethods);
+ try {
+ for (Method method : sourceMethods) {
+ if (isGetMethod(method)) {
+ String methodName = method.getName();
+ Class> returnType = method.getReturnType();
+ String sourceFieldName = buildFieldName(methodName);
+ Object sourceFieldValue = method.invoke(source);
+ if (ignoreNullValue && sourceFieldValue == null) {
+ continue;
+ }
+ MethodBean methodBean = setMethodMap.get(sourceFieldName);
+ if (methodBean == null) {
+ continue;
+ }
+ Class> paramType = methodBean.getParamType();
+ Method targetMethod = methodBean.getMethod();
+ if (paramType == returnType) {
+ setValue(targetMethod, target, sourceFieldValue);
+ }
+ }
+ }
+ } catch (Exception e) {
+ throw new RuntimeException("copyProperties error", e);
+ }
+ }
+
+ /**
+ * 返回所有的setXX方法,key为对应的字段名称
+ *
+ * @param methods 方法列表
+ * @return 返回setXx方法对应关系,key为对应的字段名称
+ */
+ private static Map buildSetMethodMap(Method[] methods) {
+ return Arrays.stream(methods)
+ .filter(BeanUtil::isSetMethod)
+ .collect(Collectors.toMap(method -> buildFieldName(method.getName()), method -> {
+ Class> parameterType = method.getParameterTypes()[0];
+ return new MethodBean(method, parameterType);
+ }));
+ }
+
+ private static void setValue(Method method, Object obj, Object value) throws InvocationTargetException, IllegalAccessException {
+ method.invoke(obj, value);
+ }
+
+ /**
+ * 属性拷贝,第一个参数中的属性值拷贝到第二个参数中
+ * 注意:当第一个参数中的属性有null值时,不会拷贝进去
+ *
+ * @param source 源对象
+ * @param target 目标对象
+ */
+ public static void copyProperties(Object source, Object target) {
+ Objects.requireNonNull(source, "Source must not be null");
+ Objects.requireNonNull(target, "Target must not be null");
+ copyProperties(source, target, false);
+ }
+
+
+ /**
+ * 深层次拷贝
+ *
+ * @param from 待转换的集合类
+ * @param toClass 目标类class
+ * @param 目标类
+ * @return 返回目标类
+ */
+ public static List copyBean(List> from, Class toClass) {
+ if (from == null || from.isEmpty()) {
+ return new ArrayList<>();
+ }
+ return from.stream()
+ .filter(Objects::nonNull)
+ .map(source -> copyBean(source, toClass))
+ .collect(Collectors.toList());
+ }
+
+
+ /**
+ * 深层次拷贝,通过json转换的方式实现
+ *
+ * @param from 待转换的类
+ * @param toClass 目标类class
+ * @param 目标类
+ * @return 返回目标类
+ */
+ public static T copyBean(Object from, Class toClass) {
+ if (from == null) {
+ return null;
+ }
+ T target = newInstance(toClass);
+ copyProperties(from, target);
+ return target;
+ }
+
+ private static T newInstance(Class clazz) {
+ try {
+ return clazz.newInstance();
+ } catch (Exception e) {
+ throw new RuntimeException("create instance error", e);
+ }
+ }
+
+ /**
+ * 将实体对象转换成Map, key:实体类中的字段名称
+ *
+ * @param pojo 实体类
+ * @return 返回map, key:实体类中的字段名称, value:实体类中的字段值
+ */
+ public static Map pojoToMap(Object pojo) {
+ if (pojo == null) {
+ return Collections.emptyMap();
+ }
+ Method[] methods = pojo.getClass().getMethods();
+ Map map = new HashMap<>(methods.length * 2);
+ try {
+ for (Method method : methods) {
+ if (isGetMethod(method)) {
+ String methodName = method.getName();
+ String fieldName = buildFieldName(methodName);
+ Object value = method.invoke(pojo);
+ map.put(fieldName, value);
+ }
+ }
+ } catch (Exception e) {
+ throw new RuntimeException("pojoToMap失败", e);
+ }
+ return map;
+ }
+
+ // 构建列名
+ private static String buildFieldName(String methodName) {
+ if (methodName.startsWith(PREFIX_IS)) {
+ return methodName.substring(2, 3).toLowerCase() + methodName.substring(3);
+ }
+ return methodName.substring(3, 4).toLowerCase() + methodName.substring(4);
+ }
+
+ /**
+ * 是否是get方法
+ */
+ private static boolean isGetMethod(Method method) {
+ Class> returnType = method.getReturnType();
+ if (returnType == Void.TYPE) {
+ return false;
+ }
+ String methodName = method.getName();
+ if (GET_CLASS_NAME.equals(methodName)) {
+ return false;
+ }
+ boolean isBooleanType = returnType == boolean.class || returnType == Boolean.class;
+ return methodName.startsWith(PREFIX_GET) || (isBooleanType && methodName.startsWith(PREFIX_IS));
+ }
+
+ /**
+ * 是否是set方法
+ *
+ * @param method
+ * @return
+ */
+ private static boolean isSetMethod(Method method) {
+ return method.getName().startsWith(PREFIX_SET) && method.getParameterTypes().length == 1;
+ }
+
+ /**
+ * 单值转换
+ * @param value 单值
+ * @param valClass 转换的class类型
+ * @param 类型
+ * @return 转换后的值
+ */
+ public static T parseValue(Object value, Class valClass) {
+ if (value == null) {
+ return null;
+ }
+ if (valClass == Object.class) {
+ return (T) value;
+ }
+ if (valClass == String.class) {
+ return (T) String.valueOf(value);
+ }
+ // 处理时间类型
+ if (value.getClass() == Timestamp.class) {
+ Timestamp timestamp = (Timestamp) value;
+ if (valClass == Date.class) {
+ return (T) timestamp;
+ } else if (valClass == java.sql.Date.class) {
+ return (T) new java.sql.Date(timestamp.getTime());
+ } else if (valClass == LocalDateTime.class) {
+ return (T) timestamp.toLocalDateTime();
+ }
+ }
+ if ( valClass == Date.class && value.getClass() == LocalDateTime.class) {
+ LocalDateTime localDateTime = (LocalDateTime) value;
+ return (T) Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
+ }
+ T t = null;
+ // 转换单值:byte(位)、short(短整数)、int(整数)、long(长整数)、float(单精度)、double(双精度)、char(字符)和boolean(布尔值)。
+ String strValue = String.valueOf(value);
+
+ if (valClass == Byte.class || valClass == byte.class) {
+ t = (T) Byte.valueOf(strValue);
+ }
+ if (valClass == Short.class || valClass == short.class) {
+ t = (T) Short.valueOf(strValue);
+ }
+ if (valClass == Integer.class || valClass == int.class) {
+ t = (T) Integer.valueOf(strValue);
+ }
+ if (valClass == Long.class || valClass == long.class) {
+ t = (T) Long.valueOf(strValue);
+ }
+ if (valClass == Float.class || valClass == float.class) {
+ t = (T) Float.valueOf(strValue);
+ }
+ if (valClass == Double.class || valClass == double.class) {
+ t = (T) Double.valueOf(strValue);
+ }
+ if (valClass == Character.class || valClass == char.class) {
+ t = (T) Character.valueOf(strValue.charAt(0));
+ }
+ if (valClass == BigDecimal.class) {
+ t = (T) (value.getClass() == BigDecimal.class ? value : new BigDecimal(strValue));
+ }
+ if (valClass == BigInteger.class) {
+ t = (T) (value.getClass() == BigInteger.class ? value : new BigInteger(strValue));
+ }
+ if (valClass == AtomicInteger.class) {
+ t = (T) (value.getClass() == AtomicInteger.class ? value : new AtomicInteger(Integer.valueOf(strValue)));
+ }
+ if (valClass == AtomicLong.class) {
+ t = (T) (value.getClass() == AtomicLong.class ? value : new AtomicLong(Long.valueOf(strValue)));
+ }
+ if (valClass == Boolean.class || valClass == boolean.class) {
+ String temp = strValue;
+ if ("0".equals(strValue)) {
+ temp = "false";
+ } else if ("1".equals(strValue)) {
+ temp = "true";
+ }
+ t = (T) Boolean.valueOf(temp);
+ }
+ if (t != null) {
+ return t;
+ }
+ // 转换其它单值
+ return (T) value;
+ }
+
+ static class MethodBean {
+ private final Method method;
+ private final Class> paramType;
+
+ public MethodBean(Method method, Class> paramType) {
+ this.method = method;
+ this.paramType = paramType;
+ }
+
+ public Method getMethod() {
+ return method;
+ }
+
+ public Class> getParamType() {
+ return paramType;
+ }
+ }
+}
diff --git a/sop-admin/sop-code-gen/src/main/java/com/gitee/gen/util/FieldUtil.java b/sop-admin/sop-code-gen/src/main/java/com/gitee/gen/util/FieldUtil.java
new file mode 100644
index 00000000..f1865e90
--- /dev/null
+++ b/sop-admin/sop-code-gen/src/main/java/com/gitee/gen/util/FieldUtil.java
@@ -0,0 +1,164 @@
+package com.gitee.gen.util;
+
+import org.apache.commons.lang.StringUtils;
+
+import java.util.Objects;
+
+public class FieldUtil {
+
+ public static final String UNDER_LINE = "_";
+
+ /**
+ * 下划线字段转驼峰
+ *
+ *
+ * user_age -> userAge
+ * user_address_detail -> userAddressDetail
+ * user__age -> userAge
+ * name -> name
+ * _name -> _name
+ * _name_ -> _name_
+ * _user_age -> _userAge
+ * _user__age -> _userAge
+ * user_age_ -> userAge_
+ * _user_age_ -> _userAge_
+ * name__ -> name__
+ * __name -> __name
+ *
+ *
+ *
+ * @param field 字段
+ * @return 返回转换后的字段
+ */
+ public static String underlineFilter(String field) {
+ if (StringUtils.isNotBlank(field)) {
+ char underLine = '_';
+ int underLineCountLeading = findCharCount(field, underLine, false);
+ int underLineCountTailing = findCharCount(field, underLine, true);
+ // 去除首尾'_'
+ field = StringUtil.trimLeadingCharacter(field, underLine);
+ field = StringUtil.trimTrailingCharacter(field, underLine);
+ if (isSingleAllUpper(field)) {
+ return field.toLowerCase();
+ }
+ if (field.contains(UNDER_LINE)) {
+ field = field.toLowerCase();
+ }
+ String[] arr = field.split("_+");
+ return join(arr, underLineCountLeading, underLineCountTailing);
+ }
+ return "";
+ }
+
+ /**
+ * 是不是全部大写的单词,如:NAME, ADDRESS
+ * @param name 单词
+ * @return true:是
+ */
+ private static boolean isSingleAllUpper(String name) {
+ if (name.contains(UNDER_LINE)) {
+ return false;
+ }
+ return Objects.equals(name, name.toUpperCase());
+ }
+
+ private static String join(String[] arr, int underLineCountLeading, int underLineCountTailing) {
+ if (arr.length > 1) {
+ for (int i = 1; i < arr.length; i++) {
+ arr[i] = upperFirstLetter(arr[i]);
+ }
+ }
+ StringBuilder ret = new StringBuilder();
+ char underLine = '_';
+ for (int i = 0; i < underLineCountLeading; i++) {
+ ret.append(underLine);
+ }
+ ret.append(String.join("", arr));
+ for (int i = 0; i < underLineCountTailing; i++) {
+ ret.append(underLine);
+ }
+ return ret.toString();
+ }
+
+ private static int findCharCount(String str, char searchChar, boolean reverse) {
+ if (StringUtils.isEmpty(str)) {
+ return 0;
+ }
+ int count = 0;
+ char[] chars = str.toCharArray();
+
+ if (reverse) {
+ for (int i = chars.length - 1; i >= 0; i--) {
+ if (chars[i] == searchChar) {
+ count++;
+ } else {
+ break;
+ }
+ }
+ } else {
+ for (char aChar : chars) {
+ if (aChar == searchChar) {
+ count++;
+ } else {
+ break;
+ }
+ }
+ }
+ return count;
+ }
+
+ public static String convertString(Object object) {
+ if (object == null) {
+ return "";
+ }
+ return String.valueOf(object);
+ }
+
+ /**
+ * 过滤"."
+ *
+ * @param field 字段
+ * @return 返回新字段
+ */
+ public static String dotFilter(String field) {
+ if (StringUtils.isNotBlank(field)) {
+ if (field.contains(".")) {
+ String[] words = field.split("\\.");
+ StringBuilder ret = new StringBuilder();
+ for (String str : words) {
+ ret.append(upperFirstLetter(str));
+ }
+ return ret.toString();
+ }
+ }
+ return field;
+ }
+
+ /**
+ * 将第一个字母转换成大写。 name -> Name
+ *
+ * @param str 字符串
+ * @return 返回新字段
+ */
+ public static String upperFirstLetter(String str) {
+ if (StringUtils.isNotBlank(str)) {
+ return str.substring(0, 1).toUpperCase() + str.substring(1);
+ }
+ return str;
+ }
+
+ /**
+ * 将第一个字母转换成小写。Name -> name
+ *
+ * @param str 字符串
+ * @return 返回新内容
+ */
+ public static String lowerFirstLetter(String str) {
+ if (StringUtils.isNotBlank(str)) {
+ return str.substring(0, 1).toLowerCase() + str.substring(1);
+ }
+ return str;
+ }
+
+
+}
diff --git a/sop-admin/sop-code-gen/src/main/java/com/gitee/gen/util/FileUtil.java b/sop-admin/sop-code-gen/src/main/java/com/gitee/gen/util/FileUtil.java
new file mode 100644
index 00000000..bbb7a0de
--- /dev/null
+++ b/sop-admin/sop-code-gen/src/main/java/com/gitee/gen/util/FileUtil.java
@@ -0,0 +1,10 @@
+package com.gitee.gen.util;
+
+/**
+ * @author 六如
+ */
+public class FileUtil {
+
+
+
+}
diff --git a/sop-admin/sop-code-gen/src/main/java/com/gitee/gen/util/FormatUtil.java b/sop-admin/sop-code-gen/src/main/java/com/gitee/gen/util/FormatUtil.java
new file mode 100644
index 00000000..13660e06
--- /dev/null
+++ b/sop-admin/sop-code-gen/src/main/java/com/gitee/gen/util/FormatUtil.java
@@ -0,0 +1,50 @@
+package com.gitee.gen.util;
+
+import org.dom4j.Document;
+import org.dom4j.io.OutputFormat;
+import org.dom4j.io.SAXReader;
+import org.dom4j.io.XMLWriter;
+
+import java.io.StringReader;
+import java.io.StringWriter;
+
+/**
+ * 代码格式化
+ * @author tanghc
+ */
+public class FormatUtil {
+
+ public static String formatXml(String input) {
+ try{
+ SAXReader reader = new SAXReader();
+ // System.out.println(reader);
+ // 注释:创建一个串的字符输入流
+ StringReader in = new StringReader(input);
+ Document doc = reader.read(in);
+ // System.out.println(doc.getRootElement());
+ // 注释:创建输出格式
+ OutputFormat formater = OutputFormat.createPrettyPrint();
+ // formater=OutputFormat.createCompactFormat();
+ // 注释:设置xml的输出编码
+ formater.setEncoding("utf-8");
+ // TAB缩进
+ formater.setIndent(" ");
+ // 注释:创建输出(目标)
+ StringWriter out = new StringWriter();
+ // 注释:创建输出流
+ XMLWriter writer = new XMLWriter(out, formater);
+ // 注释:输出格式化的串到目标中,执行后。格式化后的串保存在out中。
+ writer.write(doc);
+
+ writer.close();
+ // 注释:返回我们格式化后的结果
+ return out.toString();
+ }catch (Exception e) {
+ e.printStackTrace();
+ return input;
+ }
+ }
+
+
+
+}
diff --git a/sop-admin/sop-code-gen/src/main/java/com/gitee/gen/util/SqlTypeUtil.java b/sop-admin/sop-code-gen/src/main/java/com/gitee/gen/util/SqlTypeUtil.java
new file mode 100644
index 00000000..69350661
--- /dev/null
+++ b/sop-admin/sop-code-gen/src/main/java/com/gitee/gen/util/SqlTypeUtil.java
@@ -0,0 +1,161 @@
+package com.gitee.gen.util;
+
+import com.gitee.gen.gen.TypeEnum;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 字段类型转换
+ */
+public class SqlTypeUtil {
+ private static final Map javaTypeMap = new HashMap<>();
+ private static final Map javaBoxTypeMap = new HashMap<>();
+ private static final Map mybatisTypeMap = new HashMap<>();
+
+ static {
+ javaTypeMap.put("bigint", "long");
+ javaTypeMap.put("binary", "byte[]");
+ javaTypeMap.put("bit", "boolean");
+ javaTypeMap.put("boolean", "boolean");
+ javaTypeMap.put("blob", "byte[]");
+ javaTypeMap.put("char", "String");
+ javaTypeMap.put("date", "Date");
+ javaTypeMap.put("datetime", "Date");
+ javaTypeMap.put("decimal", "BigDecimal");
+ javaTypeMap.put("double", "double");
+ javaTypeMap.put("float", "float");
+ javaTypeMap.put("int", "int");
+ javaTypeMap.put("integer", "int");
+ javaTypeMap.put("image", "byte[]");
+ javaTypeMap.put("money", "BigDecimal");
+ javaTypeMap.put("nchar", "String");
+ javaTypeMap.put("ntext", "byte");
+ javaTypeMap.put("numeric", "BigDecimal");
+ javaTypeMap.put("nvarchar", "String");
+ javaTypeMap.put("real", "float");
+ javaTypeMap.put("smalldatetime", "Date");
+ javaTypeMap.put("smallint", "int");
+ javaTypeMap.put("smallmoney", "BigDecimal");
+ javaTypeMap.put("sql_variant", "String");
+ javaTypeMap.put("text", "String");
+ javaTypeMap.put("tinyint", "int");
+ javaTypeMap.put("timestamp", "Date");
+ javaTypeMap.put("uniqueidentifier", "String");
+ javaTypeMap.put("varbinary", "byte[]");
+ javaTypeMap.put("varchar", "String");
+
+
+ javaBoxTypeMap.put("bigint", "Long");
+ javaBoxTypeMap.put("binary", "Byte[]");
+ javaBoxTypeMap.put("bit", "Boolean");
+ javaBoxTypeMap.put("bool", "Boolean");
+ javaBoxTypeMap.put("boolean", "Boolean");
+ javaBoxTypeMap.put("blob", "Byte[]");
+ javaBoxTypeMap.put("char", "String");
+ javaBoxTypeMap.put("date", "Date");
+ javaBoxTypeMap.put("datetime", "Date");
+ javaBoxTypeMap.put("decimal", "BigDecimal");
+ javaBoxTypeMap.put("double", "Double");
+ javaBoxTypeMap.put("float", "Float");
+ javaBoxTypeMap.put("int", "Integer");
+ javaBoxTypeMap.put("integer", "Integer");
+ javaBoxTypeMap.put("image", "Byte[]");
+ javaBoxTypeMap.put("money", "BigDecimal");
+ javaBoxTypeMap.put("nchar", "String");
+ javaBoxTypeMap.put("ntext", "String");
+ javaBoxTypeMap.put("numeric", "BigDecimal");
+ javaBoxTypeMap.put("nvarchar", "String");
+ javaBoxTypeMap.put("real", "Float");
+ javaBoxTypeMap.put("smalldatetime", "Date");
+ javaBoxTypeMap.put("smallint", "Integer");
+ javaBoxTypeMap.put("smallmoney", "BigDecimal");
+ javaBoxTypeMap.put("sql_variant", "String");
+ javaBoxTypeMap.put("text", "String");
+ javaBoxTypeMap.put("tinyint", "Integer");
+ javaBoxTypeMap.put("timestamp", "Date");
+ javaBoxTypeMap.put("uniqueidentifier", "String");
+ javaBoxTypeMap.put("varbinary", "Byte[]");
+ javaBoxTypeMap.put("varchar", "String");
+
+
+ mybatisTypeMap.put("bigint", "BIGINT");
+ mybatisTypeMap.put("binary", "BLOB");
+ mybatisTypeMap.put("bit", "BOOLEAN");
+ mybatisTypeMap.put("boolean", "BOOLEAN");
+ mybatisTypeMap.put("blob", "BLOB");
+ mybatisTypeMap.put("char", "CHAR");
+ mybatisTypeMap.put("date", "TIMESTAMP");
+ mybatisTypeMap.put("datetime", "TIMESTAMP");
+ mybatisTypeMap.put("decimal", "DECIMAL");
+ mybatisTypeMap.put("double", "DOUBLE");
+ mybatisTypeMap.put("float", "FLOAT");
+ mybatisTypeMap.put("int", "INTEGER");
+ mybatisTypeMap.put("integer", "INTEGER");
+ mybatisTypeMap.put("image", "BLOB");
+ mybatisTypeMap.put("money", "DECIMAL");
+ mybatisTypeMap.put("nchar", "NCHAR");
+ mybatisTypeMap.put("ntext", "VARCHAR");
+ mybatisTypeMap.put("numeric", "DECIMAL");
+ mybatisTypeMap.put("nvarchar", "NVARCHAR");
+ mybatisTypeMap.put("real", "FLOAT");
+ mybatisTypeMap.put("smalldatetime", "TIMESTAMP");
+ mybatisTypeMap.put("smallint", "INTEGER");
+ mybatisTypeMap.put("smallmoney", "DECIMAL");
+ mybatisTypeMap.put("sql_variant", "VARCHAR");
+ mybatisTypeMap.put("text", "VARCHAR");
+ mybatisTypeMap.put("tinyint", "TINYINT");
+ mybatisTypeMap.put("timestamp", "TIMESTAMP");
+ mybatisTypeMap.put("uniqueidentifier", "VARCHAR");
+ mybatisTypeMap.put("varbinary", "BLOB");
+ mybatisTypeMap.put("varchar", "VARCHAR");
+ }
+
+ public static final Map TYPE_MYBATIS_MAP = new HashMap<>(64);
+
+ static {
+ TYPE_MYBATIS_MAP.put(TypeEnum.BIT.getType(), "BOOLEAN");
+ TYPE_MYBATIS_MAP.put(TypeEnum.BOOLEAN.getType(), "BOOLEAN");
+ TYPE_MYBATIS_MAP.put(TypeEnum.TINYINT.getType(), "TINYINT");
+ TYPE_MYBATIS_MAP.put(TypeEnum.SMALLINT.getType(), "INTEGER");
+ TYPE_MYBATIS_MAP.put(TypeEnum.INT.getType(), "INTEGER");
+ TYPE_MYBATIS_MAP.put(TypeEnum.BIGINT.getType(), "BIGINT");
+ TYPE_MYBATIS_MAP.put(TypeEnum.FLOAT.getType(), "FLOAT");
+ TYPE_MYBATIS_MAP.put(TypeEnum.DOUBLE.getType(), "DOUBLE");
+ TYPE_MYBATIS_MAP.put(TypeEnum.DECIMAL.getType(), "DECIMAL");
+ TYPE_MYBATIS_MAP.put(TypeEnum.VARCHAR.getType(), "VARCHAR");
+ TYPE_MYBATIS_MAP.put(TypeEnum.DATETIME.getType(), "TIMESTAMP");
+ TYPE_MYBATIS_MAP.put(TypeEnum.BLOB.getType(), "BLOB");
+ }
+
+ /**
+ * 将sql字段类型转换为java字段类型
+ * @param sqlType 数据库字段类型
+ * @return 找不到类型默认返回String
+ */
+ public static String convertToJavaType(String sqlType){
+ String javaType = javaTypeMap.get(sqlType);
+ return javaType == null ? "String" : javaType;
+ }
+
+ /**
+ * 将sql字段类型转换为java装箱字段类型
+ * @param sqlType 数据库字段类型
+ * @return 找不到类型默认返回String
+ */
+ public static String convertToJavaBoxType(String sqlType){
+ String javaType = javaBoxTypeMap.get(sqlType);
+ return javaType == null ? "String" : javaType;
+ }
+
+ /**
+ * 将sql字段类型转换为mybatis的jdbcType
+ * @param sqlType 数据库字段类型
+ * @return 找不到类型默认返回VARCHAR
+ */
+ public static String convertToMyBatisJdbcType(String sqlType){
+ String javaType = mybatisTypeMap.get(sqlType);
+ return javaType == null ? "VARCHAR" : javaType;
+ }
+
+}
diff --git a/sop-admin/sop-code-gen/src/main/java/com/gitee/gen/util/StringUtil.java b/sop-admin/sop-code-gen/src/main/java/com/gitee/gen/util/StringUtil.java
new file mode 100644
index 00000000..64d1d6a9
--- /dev/null
+++ b/sop-admin/sop-code-gen/src/main/java/com/gitee/gen/util/StringUtil.java
@@ -0,0 +1,51 @@
+package com.gitee.gen.util;
+
+import org.apache.commons.lang.StringUtils;
+
+/**
+ * @author thc
+ */
+public class StringUtil {
+
+ /**
+ * Trim leading whitespace from the given {@code String}.
+ * @param str the {@code String} to check
+ * @return the trimmed {@code String}
+ * @see Character#isWhitespace
+ */
+ public static String trimLeadingWhitespace(String str) {
+ if (StringUtils.isBlank(str)) {
+ return str;
+ }
+
+ int beginIdx = 0;
+ while (beginIdx < str.length() && Character.isWhitespace(str.charAt(beginIdx))) {
+ beginIdx++;
+ }
+ return str.substring(beginIdx);
+ }
+
+ public static String trimLeadingCharacter(String str, char leadingCharacter) {
+ if (StringUtils.isBlank(str)) {
+ return str;
+ }
+
+ int beginIdx = 0;
+ while (beginIdx < str.length() && leadingCharacter == str.charAt(beginIdx)) {
+ beginIdx++;
+ }
+ return str.substring(beginIdx);
+ }
+
+ public static String trimTrailingCharacter(String str, char trailingCharacter) {
+ if (StringUtils.isBlank(str)) {
+ return str;
+ }
+
+ int endIdx = str.length() - 1;
+ while (endIdx >= 0 && trailingCharacter == str.charAt(endIdx)) {
+ endIdx--;
+ }
+ return str.substring(0, endIdx + 1);
+ }
+}
diff --git a/sop-admin/sop-code-gen/src/main/java/com/gitee/gen/util/SystemUtil.java b/sop-admin/sop-code-gen/src/main/java/com/gitee/gen/util/SystemUtil.java
new file mode 100644
index 00000000..2f84fd3f
--- /dev/null
+++ b/sop-admin/sop-code-gen/src/main/java/com/gitee/gen/util/SystemUtil.java
@@ -0,0 +1,45 @@
+package com.gitee.gen.util;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+
+/**
+ * @author tanghc
+ */
+public class SystemUtil {
+
+ private static volatile String binPath;
+
+ /**
+ * 获取程序执行目录,即jar包所在的目录。此方法只在部署后有用,开发模式下,这里返回target路径
+ *
+ * @return 返回路径
+ */
+ public static String getBinPath() {
+ if (binPath == null) {
+ synchronized (SystemUtil.class) {
+ if (binPath == null) {
+ String path = SystemUtil.class.getProtectionDomain().getCodeSource().getLocation().getPath();
+ try {
+ path = URLDecoder.decode(path, "UTF-8");
+ path = StringUtil.trimTrailingCharacter(path, '/');
+ int index = path.lastIndexOf('/');
+ path = path.substring(0, index);
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+ }
+ binPath = path;
+ }
+ }
+ }
+ return binPath;
+ }
+
+ public static String getUserDir() {
+ return System.getProperty("user.dir");
+ }
+
+ public static String getUserHome() {
+ return System.getProperty("user.home");
+ }
+}
diff --git a/sop-admin/sop-code-gen/src/main/java/com/gitee/gen/util/TemplateMetaUtils.java b/sop-admin/sop-code-gen/src/main/java/com/gitee/gen/util/TemplateMetaUtils.java
new file mode 100644
index 00000000..feba0d4f
--- /dev/null
+++ b/sop-admin/sop-code-gen/src/main/java/com/gitee/gen/util/TemplateMetaUtils.java
@@ -0,0 +1,106 @@
+package com.gitee.gen.util;
+
+import com.gitee.gen.entity.TemplateConfig;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 模板元信息工具类
+ */
+public final class TemplateMetaUtils {
+
+ private TemplateMetaUtils() {
+ }
+
+ /**
+ * 解析模板内容中的元信息
+ */
+ public static Map parseMetaContent(String content) {
+ content = StringUtil.trimLeadingWhitespace(content);
+ if (content != null && content.startsWith("##")) {
+ int rowIdx = content.indexOf('\n');
+ String metaInfo = content.substring(2, rowIdx);
+ return parseMetaRow(metaInfo);
+ }
+ return Collections.emptyMap();
+ }
+
+ /**
+ * 解析元数据信息
+ */
+ public static Map parseMetaRow(String row) {
+ char[] charArray = row.toCharArray();
+
+ Map data = new HashMap<>();
+
+ StringBuilder kvBuilder = new StringBuilder();
+ // 剩余未闭合表达式数量
+ int leftExpr = 0;
+ for (int i = 0, len = charArray.length, end = len - 1; i < len; i++) {
+ char c = charArray[i];
+ boolean kvEnd = false;
+ if (i == end) {
+ kvBuilder.append(c);
+ kvEnd = true;
+ } else if (',' == c && leftExpr == 0) {
+ kvEnd = true;
+ }
+
+ if (kvEnd) {
+ String[] kv = kvBuilder.toString().trim().split("=");
+ data.put(kv[0].trim(), kv.length == 1 ? null : kv[1].trim());
+ kvBuilder = new StringBuilder();
+ continue;
+ }
+
+ kvBuilder.append(c);
+ switch (c) {
+ case '{':
+ case '(':
+ leftExpr++;
+ break;
+ case '}':
+ case ')':
+ leftExpr--;
+ break;
+ default:
+ break;
+ }
+ }
+ if (leftExpr > 0) {
+ throw new RuntimeException("读取元数据失败,有" + leftExpr + "个表达式未闭合");
+ }
+ return data;
+ }
+
+ /**
+ * 生成元数据的模板内容
+ */
+ public static String generateMetaContent(TemplateConfig template) {
+ String content = StringUtil.trimLeadingWhitespace(template.getContent());
+ String metaRow = generateMetaRow(template);
+ if (content.startsWith("##")) {
+ int rowIdx = content.indexOf('\n');
+ if(rowIdx == -1) {
+ content = metaRow;
+ } else {
+ content = metaRow + content.substring(rowIdx);
+ }
+ } else {
+ content = metaRow + "\n" + content;
+ }
+ return content;
+ }
+
+ /**
+ * 解析元数据信息
+ */
+ public static String generateMetaRow(TemplateConfig template) {
+ String format = "## filename=%s, folder=%s";
+ String filename = template.getFileName() != null ? template.getFileName() : "";
+ String folder = template.getFolder() != null ? template.getFolder() : "";
+ return String.format(format, filename, folder);
+ }
+}
diff --git a/sop-admin/sop-code-gen/src/main/java/com/gitee/gen/util/VelocityUtil.java b/sop-admin/sop-code-gen/src/main/java/com/gitee/gen/util/VelocityUtil.java
new file mode 100644
index 00000000..db1a958d
--- /dev/null
+++ b/sop-admin/sop-code-gen/src/main/java/com/gitee/gen/util/VelocityUtil.java
@@ -0,0 +1,42 @@
+package com.gitee.gen.util;
+
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.runtime.log.NullLogChute;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
+
+/**
+ * Velocity工具类,根据模板内容生成文件
+ */
+public class VelocityUtil {
+ private VelocityUtil() {
+ super();
+ }
+
+ static {
+ // 禁止输出日志
+ Velocity.setProperty(Velocity.RUNTIME_LOG_LOGSYSTEM, new NullLogChute());
+ Velocity.init();
+ }
+
+ private final static String LOG_TAG = "velocity_log";
+
+ public static String generate(VelocityContext context, String template) {
+ StringReader reader = new StringReader(template);
+ StringWriter writer = new StringWriter();
+ // 不用vm文件
+ Velocity.evaluate(context, writer, LOG_TAG, reader);
+
+ try {
+ writer.close();
+ reader.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ return writer.toString();
+ }
+}
diff --git a/sop-admin/sop-code-gen/src/main/resources/app.yml b/sop-admin/sop-code-gen/src/main/resources/app.yml
new file mode 100644
index 00000000..48d0f584
--- /dev/null
+++ b/sop-admin/sop-code-gen/src/main/resources/app.yml
@@ -0,0 +1,88 @@
+solon.app:
+ name: gen
+server:
+ port: 6969
+
+
+# sqlite3
+#LOCAL_DB: ${user.home}/gen.db
+gen.db1:
+ url: "jdbc:sqlite:"
+ driverClassName: org.sqlite.JDBC
+ userName:
+ password:
+
+# mysql
+dbms:
+ enable: false
+# host: localhost:3306
+# database: gen
+# username: root
+# password: root
+
+gen.db2:
+ url: jdbc:mysql://${dbms.host:localhost:3306}/${dbms.database:gen}?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai
+ driverClassName: com.mysql.cj.jdbc.Driver
+ userName: ${dbms.username:gen}
+ password: ${dbms.password:}
+
+# 配置数据源对应的 mybatis 信息(要与 DataSource bean 的名字对上)
+mybatis.db1:
+ mappers: #支持包名 或 类名(大写开头 或 *)或 xml(.xml结尾)//支持 ** 或 * 占位符
+ - "com.gitee.gen.mapper.*" #这个表达式同上效果
+ - "classpath:mybatis/**/*.xml"
+ configuration: #扩展配置(要与 Configuration 类的属性一一对应)
+ cacheEnabled: false
+ mapperVerifyEnabled: true #如果为 true,则要求所有 mapper 有 @Mapper 主解
+ mapUnderscoreToCamelCase: true
+
+gen:
+ format-xml: ${GEN_FORMAT_XML:true}
+ db-name: ${GEN_DBNAME:gen}
+
+# log
+solon.logging.appender:
+ console:
+ level: INFO
+ enable: true #是否启用
+ file:
+ name: "logs/${solon.app.name}"
+ level: INFO
+ enable: false #是否启用
+ extension: ".log" #v2.2.18 后支持(例:.log, .log.gz, .log.zip)
+ maxFileSize: "10 MB"
+ maxHistory: "7"
+ cloud:
+ level: INFO
+ enable: false #是否启用
+
+
+# 2.0.3新增
+# 连接方式管理, {HOST},{PORT},{DB_NAME} 表示占位符
+connect:
+ 1:
+ name: MySQL
+ driver: com.mysql.cj.jdbc.Driver
+ url: jdbc:mysql://{HOST}:{PORT}/{DB_NAME}?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
+ serviceName: com.gitee.gen.gen.mysql.MySqlService
+ 2:
+ name: Oracle
+ driver: oracle.jdbc.driver.OracleDriver
+ url: jdbc:oracle:thin:@{HOST}:{PORT}{DB_NAME}
+ serviceName: com.gitee.gen.gen.oracle.OracleService
+ 3:
+ name: "SQL Server"
+ driver: com.microsoft.sqlserver.jdbc.SQLServerDriver
+ url: jdbc:sqlserver://{HOST}:{PORT};DatabaseName={DB_NAME};trustServerCertificate=true
+ serviceName: com.gitee.gen.gen.sqlserver.SqlServerService
+ 4:
+ name: PostgreSQL
+ driver: org.postgresql.Driver
+ url: jdbc:postgresql://{HOST}:{PORT}/{DB_NAME}
+ serviceName: com.gitee.gen.gen.postgresql.PostgreSqlService
+ 5:
+ name: "达梦"
+ driver: dm.jdbc.driver.DmDriver
+ url: jdbc:dm://{HOST}:{PORT}/{DB_NAME}
+ serviceName: com.gitee.gen.gen.dm.DmService
+
diff --git a/sop-admin/sop-code-gen/src/main/resources/gen_init.db b/sop-admin/sop-code-gen/src/main/resources/gen_init.db
new file mode 100644
index 0000000000000000000000000000000000000000..bf04d3e4c76baaa5dddfc439cb6bb9e2d4f046a2
GIT binary patch
literal 98304
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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ t.id
+ , t.db_type
+ , t.db_desc
+ , t.driver_class
+ , t.db_name
+ , t.schema_name
+ , t.host
+ , t.port
+ , t.username
+ , t.password
+ , t.is_deleted
+ , t.package_name
+ , t.del_prefix
+ , t.group_id
+ , t.author
+ , t.db_group_name
+
+
+
+
+
+
+
+
+
+
+ INSERT INTO datasource_config
+
+ db_type,
+ db_desc,
+ driver_class,
+ db_name,
+ schema_name,
+ host,
+ port,
+ username,
+ password,
+ is_deleted,
+ package_name,
+ del_prefix,
+ group_id,
+ author,
+ db_group_name,
+
+
+ #{dbType},
+ #{dbDesc},
+ #{driverClass},
+ #{dbName},
+ #{schemaName},
+ #{host},
+ #{port},
+ #{username},
+ #{password},
+ #{isDeleted},
+ #{packageName},
+ #{delPrefix},
+ #{groupId},
+ #{author},
+ #{dbGroupName},
+
+
+
+
+
+ INSERT INTO datasource_config
+
+
+
+ db_type,
+
+
+ db_desc,
+
+
+ driver_class,
+
+
+ db_name,
+
+
+ schema_name,
+
+
+ host,
+
+
+ port,
+
+
+ username,
+
+
+ password,
+
+
+ is_deleted,
+
+
+ package_name,
+
+
+ del_prefix,
+
+
+ group_id,
+
+
+ author,
+
+
+ db_group_name,
+
+
+
+
+ #{dbType},
+
+
+ #{dbDesc},
+
+
+ #{driverClass},
+
+
+ #{dbName},
+
+
+ #{schemaName},
+
+
+ #{host},
+
+
+ #{port},
+
+
+ #{username},
+
+
+ #{password},
+
+
+ #{isDeleted},
+
+
+ #{packageName},
+
+
+ #{delPrefix},
+
+
+ #{groupId},
+
+
+ #{author},
+
+
+ #{dbGroupName},
+
+
+
+
+
+
+ UPDATE datasource_config
+
+ db_type=#{dbType},
+ db_desc=#{dbDesc},
+ driver_class=#{driverClass},
+ db_name=#{dbName},
+ schema_name=#{schemaName},
+ host=#{host},
+ port=#{port},
+ username=#{username},
+ password=#{password},
+ is_deleted=#{isDeleted},
+ package_name=#{packageName},
+ del_prefix=#{delPrefix},
+ group_id=#{groupId},
+ author=#{author},
+ db_group_name=#{dbGroupName},
+
+ WHERE id = #{id}
+
+
+
+
+
+ UPDATE datasource_config
+
+
+ db_type=#{dbType},
+
+
+ db_desc=#{dbDesc},
+
+
+ driver_class=#{driverClass},
+
+
+ db_name=#{dbName},
+
+
+ schema_name=#{schemaName},
+
+
+ host=#{host},
+
+
+ port=#{port},
+
+
+ username=#{username},
+
+
+ password=#{password},
+
+
+ is_deleted=#{isDeleted},
+
+
+ package_name=#{packageName},
+
+
+ del_prefix=#{delPrefix},
+
+
+ group_id=#{groupId},
+
+
+ author=#{author},
+
+
+ db_group_name=#{dbGroupName},
+
+
+ WHERE id = #{id}
+
+
+
+
+
+ UPDATE datasource_config
+ SET is_deleted=1
+ WHERE id = #{id}
+
+
+
+
\ No newline at end of file
diff --git a/sop-admin/sop-code-gen/src/main/resources/mybatis/GenerateHistoryMapper.xml b/sop-admin/sop-code-gen/src/main/resources/mybatis/GenerateHistoryMapper.xml
new file mode 100644
index 00000000..1cc11f09
--- /dev/null
+++ b/sop-admin/sop-code-gen/src/main/resources/mybatis/GenerateHistoryMapper.xml
@@ -0,0 +1,127 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ t.id
+ , t.config_content
+ , t.md5_value
+ , t.generate_time
+
+
+
+
+
+
+
+
+
+
+ INSERT INTO generate_history
+
+ config_content,
+ md5_value,
+ generate_time,
+
+
+ #{configContent},
+ #{md5Value},
+ #{generateTime},
+
+
+
+
+
+ INSERT INTO generate_history
+
+
+
+ config_content,
+
+
+ md5_value,
+
+
+ generate_time,
+
+
+
+
+ #{configContent},
+
+
+ #{md5Value},
+
+
+ #{generateTime},
+
+
+
+
+
+
+ UPDATE generate_history
+
+ config_content=#{configContent},
+ md5_value=#{md5Value},
+ generate_time=#{generateTime},
+
+ WHERE id = #{id}
+
+
+
+
+
+ UPDATE generate_history
+
+
+ config_content=#{configContent},
+
+
+ md5_value=#{md5Value},
+
+
+ generate_time=#{generateTime},
+
+
+ WHERE id = #{id}
+
+
+
+
+
+ UPDATE generate_history
+ SET is_deleted=1
+ WHERE id = #{id}
+
+
+
+
+
\ No newline at end of file
diff --git a/sop-admin/sop-code-gen/src/main/resources/mybatis/SystemConfigMapper.xml b/sop-admin/sop-code-gen/src/main/resources/mybatis/SystemConfigMapper.xml
new file mode 100644
index 00000000..773c8322
--- /dev/null
+++ b/sop-admin/sop-code-gen/src/main/resources/mybatis/SystemConfigMapper.xml
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+ t.id, t.config_key, t.config_value, t.remark
+
+
+
+
+
+
+
+ INSERT INTO system_config
+ config_key, config_value, remark,
+ #{configKey}, #{configValue}, #{remark},
+
+
+ INSERT INTO system_config
+
+ config_key,
+ config_value,
+ remark,
+
+
+ #{configKey},
+ #{configValue},
+ #{remark},
+
+
+
+ UPDATE system_config
+ config_key=#{configKey}, config_value=#{configValue}, remark=#{remark}, WHERE id = #{id}
+
+
+ UPDATE system_config
+
+ config_key=#{configKey},
+ config_value=#{configValue},
+ remark=#{remark},
+ WHERE id = #{id}
+
+
+ UPDATE system_config SET is_deleted=1 WHERE id = #{id}
+
diff --git a/sop-admin/sop-code-gen/src/main/resources/mybatis/TemplateConfigMapper.xml b/sop-admin/sop-code-gen/src/main/resources/mybatis/TemplateConfigMapper.xml
new file mode 100644
index 00000000..84561e7b
--- /dev/null
+++ b/sop-admin/sop-code-gen/src/main/resources/mybatis/TemplateConfigMapper.xml
@@ -0,0 +1,213 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ t.id
+ , t.group_id
+ , t.group_name
+ , t.name
+ , t.folder
+ , t.file_name
+ , t.content
+ , t.is_deleted
+
+
+
+
+
+
+
+
+
+
+
+
+ INSERT INTO template_config
+
+ group_id,
+ group_name,
+ name,
+ folder,
+ file_name,
+ content,
+ is_deleted,
+
+
+ #{groupId},
+ #{groupName},
+ #{name},
+ #{folder},
+ #{fileName},
+ #{content},
+ #{isDeleted},
+
+
+
+
+
+ INSERT INTO template_config
+
+
+
+ group_id,
+
+
+ group_name,
+
+
+ name,
+
+
+ file_name,
+
+
+ content,
+
+
+ is_deleted,
+
+
+
+
+ #{groupId},
+
+
+ #{groupName},
+
+
+ #{name},
+
+
+ #{fileName},
+
+
+ #{content},
+
+
+ #{isDeleted},
+
+
+
+
+
+
+ UPDATE template_config
+
+ group_id=#{groupId},
+ group_name=#{groupName},
+ name=#{name},
+ file_name=#{fileName},
+ content=#{content},
+ is_deleted=#{isDeleted},
+
+ WHERE id = #{id}
+
+
+
+
+
+ UPDATE template_config
+
+
+ group_id=#{groupId},
+
+
+ group_name=#{groupName},
+
+
+ name=#{name},
+
+
+ folder=#{folder},
+
+
+ file_name=#{fileName},
+
+
+ content=#{content},
+
+
+ is_deleted=#{isDeleted},
+
+
+ WHERE id = #{id}
+
+
+
+
+
+ UPDATE template_config
+ SET is_deleted=1
+ WHERE id = #{id}
+
+
+
+
+
+
+
+ UPDATE template_config
+ SET group_name = #{groupName}
+ WHERE group_id = #{groupId}
+
+
+
+ UPDATE template_config
+ SET is_deleted=1
+ WHERE group_id = #{groupId}
+
+
+
+
+
diff --git a/sop-admin/sop-code-gen/src/main/resources/mybatis/TemplateGroupMapper.xml b/sop-admin/sop-code-gen/src/main/resources/mybatis/TemplateGroupMapper.xml
new file mode 100644
index 00000000..69be989b
--- /dev/null
+++ b/sop-admin/sop-code-gen/src/main/resources/mybatis/TemplateGroupMapper.xml
@@ -0,0 +1,115 @@
+
+
+
+
+
+
+
+
+
+
+
+ t.id
+ , t.group_name
+ , t.is_deleted
+
+
+
+
+
+
+
+
+
+
+
+
+ INSERT INTO template_group
+
+ group_name,
+ is_deleted,
+
+
+ #{groupName},
+ #{isDeleted},
+
+
+
+
+
+ INSERT INTO template_group
+
+
+
+ group_name,
+
+
+ is_deleted,
+
+
+
+
+ #{groupName},
+
+
+ #{isDeleted},
+
+
+
+
+
+
+ UPDATE template_group
+
+ group_name=#{groupName},
+ is_deleted=#{isDeleted},
+
+ WHERE id = #{id}
+
+
+
+
+
+ UPDATE template_group
+
+
+ group_name=#{groupName},
+
+
+ is_deleted=#{isDeleted},
+
+
+ WHERE id = #{id}
+
+
+
+
+
+ UPDATE template_group
+ SET is_deleted = 1
+ WHERE id = #{id}
+
+
+
+
\ No newline at end of file
diff --git a/sop-admin/sop-code-gen/src/main/resources/mybatis/TypeConfigMapper.xml b/sop-admin/sop-code-gen/src/main/resources/mybatis/TypeConfigMapper.xml
new file mode 100644
index 00000000..d39bee2c
--- /dev/null
+++ b/sop-admin/sop-code-gen/src/main/resources/mybatis/TypeConfigMapper.xml
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+ t.id , t.db_type , t.base_type , t.box_type
+
+
+
+
+
+ INSERT INTO type_config
+ db_type, base_type, box_type,
+ #{dbType}, #{baseType}, #{boxType},
+
+
+ INSERT INTO type_config
+
+ db_type,
+ base_type,
+ box_type,
+
+
+ #{dbType},
+ #{baseType},
+ #{boxType},
+
+
+
+ UPDATE type_config
+ db_type=#{dbType}, base_type=#{baseType}, box_type=#{boxType}, WHERE id = #{id}
+
+
+ UPDATE type_config
+
+ db_type=#{dbType},
+ base_type=#{baseType},
+ box_type=#{boxType},
+ WHERE id = #{id}
+
+
+ UPDATE type_config SET is_deleted=1 WHERE id = #{id}
+
diff --git a/sop-admin/sop-code-gen/src/main/resources/mybatis/UpgradeMapper.xml b/sop-admin/sop-code-gen/src/main/resources/mybatis/UpgradeMapper.xml
new file mode 100644
index 00000000..2a7d9f56
--- /dev/null
+++ b/sop-admin/sop-code-gen/src/main/resources/mybatis/UpgradeMapper.xml
@@ -0,0 +1,48 @@
+
+
+
+
+
+ ${sql}
+
+
+
+ ALTER TABLE ${tableName} ADD ${columnName} ${type}
+
+
+
+ alter table ${tableName} add column(${columnName} ${type});
+
+
+
+ ALTER TABLE ${tableName} ADD COLUMN ${columnName} ${type}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sop-admin/sop-code-gen/src/main/resources/static/favicon.ico b/sop-admin/sop-code-gen/src/main/resources/static/favicon.ico
new file mode 100644
index 0000000000000000000000000000000000000000..34b63ac63a87ee3ea8e7a0f3f5b5406c437e2112
GIT binary patch
literal 67646
zcmeHQ33wdEm2O$a2gEi66S8a^!){2(X2Y@}*zze`)?rJU(MUFiMYeol`2frK#KsuO
zmSjtF$~K2NVhlNfB)}R7SkfFaCWOr{`-RN#4YIW(H-
zo|aUj@2RJ{r>m>p|LWDNSFfrSr2u~|Eeif$u3R;tK>3)WC|7}SiYLW+e&x3_lNCW)
zTK<C+
zR1SCpF}#VnD^NAY(^oSQ_)Y=-Q*rK69RC8x%Ygl*pi6-NM{#|U$L|<_q}x%@6|58T
zLYZ(QKeyk8bhWAVkPjO?2iki|(6)h5$e}pLQj0&7qVaE>Z7c&zo=1K1Eq%6
zPiCH&1BdlEaj^n`p2H2XH-4&`R
zaKVOzju8668Ca6LijVS~I{X!|<*Jo?14|3sA^XR{_hyjUxF?dKKcgH!1N|%L9(TY|
z2K#cdCuAS%3oX;eA1Jr{WXAz_z@{ANU04A7>+jL-p9Zi007`^MKF@uCGW`m47X8B(
z*pZ(BUEm4TVO(ifyuDHmh9U*zoy%WY2;57-+wX(^1j;AQ>7GrUKw0=Ps1sD|_1iAy
z_{598q3GG6aAEY`)74uI>}zU4Z-CPA-ip`%8|8iwRO1cVF6jz!jA|RbfM!s07yWm^
znsU&OKvu*ylQR7rb@)2yMo*~vLfA1%M@ZU;iNwK!@f7@aw4c5Jew`fw*z4tl`rs7s
zU)>q3yb!iRLCeb(&=m7g`Yy$q5yZX>4Kn_O3$Axhb>N*GY|4&dlK8^J2zXK1R2Q`8xQ`s+)
zM)Jqyw-=zk$3cGtjdXkmTfbj}7ta9uTF^vKpqe~V=o24FANcs-=d*tvb^G^WgMFYz
zbtfUr4-XdK!Tyfp#z!&VGy{FZcwcC#LVY<>K41Nh-SqYB0?{5GEKlRk_1
zsQd0NA4tc_#kJ07Z7SMqF3U*U;2qF@#Jv1f+lysmFz1AJQ#b6z0ArphsMnLj0eg3^
zUEOh@L|s`qLr?>bdRn<;hPq~Hk&ppzUrzCl$P)X!^eHMa-
zg^#}j?RxuN3)PM5=c=n~W~wVHAP2x)Pb(^}Q*Yb5Ky^N!a~%+o0Umz~dKj@u^ST0c
zV-S-vEJeWCEByT1Q5VKu>FZ~?@9gh)9a^Mr**0Ho{aU4ZM^~A;sd28_V4K+w|CNhp
zsH)B}$$R-xOBuE)Q@hj=^iXI!h&)?GHNko8#fO=uCw^Vie4acA5pxyKg@Go)ttH&OFu~s>PSmsmRw_08n
z;OC&-|FY%rN88_a$AT!%TW*`Lc0OlQ+n%UWH{Lu~Wt-Rc0ZcdTC^ote(CZfZ5#NE1
z+5x&`f8Qd@+XX(v*CS?X3ChiJuD&kT`i{0wtT%0#8}08`*=MSat7og5HqKMm-7qUE
z7kU|3v#dz;6DHS<@llT>Hu~D5y>*4qTb9)UzFylHjAz%I%%6;9i+23MuPjm5ub8dj
zkG8#{{5o|7j+BGdj-rJ4H&o3~ceO84Ic6}qj?@LOf$A}rK8bdL6+gb9?E;kXu*tm1
z91H0F-LU%_s$u5=bGGZPyNlKJD`yYX`#KwdW5lMN#j2~vbo?WAWPEBn@SldGa=*o5
z44|7XMY$fcGX8A)?Bh4AofFmlYwC)`7=X6mfPP<(KfbT~hgoVn?$2W#e~)KB0s6Zy
zbuTM$`K=RY0=c{b<>LHzKKYaK*>1mGy}fhMK>ZFqpzrIGePwzM
zakDRBook6Z=oo92IYspSSD{>%jql^w{m_%N@6q-LiFYD7*t~gOzVVO9${WzR%YpxR
z#uw!K1MC^=!Sz*<@>%Lg-On*UeL0D69n?9<4ef+O-$ox`yf0`l{QD{J-w6CC4J5Z5c^O$`A1nIMi;l9e;B
z=c)TS=1YY=$No3pG%KZya6W+Z1)Mjuhz=lM!!lrl9>~x5(9-y0%*Q$2RBfL=Uity=
zyr*2)`N{MEf45=H95D_sVLl*}HuDQ$-vpZ2b*2h?UYNVTftB&6?%(4oO^g3ljPE<1
zsaCh&xiI-}(Lb!h9MSF$_<|r;7SnhCg|%t*DAU-^fVr^*R>q(H-p!ll4IKCB#`kR3
zP1}oA#(K1zT&%9bd~mY9g1@Ezn0BDWbO7b?Cx|5|+0(t=R9nEx_}_VNx#ri^;T?}H
z`*n`(ou}>40XB8*^~uKx#J_RX9JTcum8wB;A=&DcvzUwd8#mUfn)R_V{`B`!XUAh6
zO+%+M&S(Gea?ze$-FEndF+Z3>UYOq