parent
fc3de4123a
commit
37f6b62975
@ -0,0 +1,122 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2020 OPSLI 快速开发平台 https://www.opsli.com
|
||||||
|
* <p>
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy of
|
||||||
|
* the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
package org.opsli.modulars.creater.importable;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ClassUtil;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.opsli.common.utils.Props;
|
||||||
|
import org.opsli.core.creater.strategy.sync.SyncStrategy;
|
||||||
|
import org.opsli.core.utils.SpringContextHolder;
|
||||||
|
import org.opsli.modulars.creater.importable.entity.DatabaseColumn;
|
||||||
|
import org.opsli.modulars.creater.importable.entity.DatabaseTable;
|
||||||
|
import org.opsli.modulars.creater.importable.service.DatabaseTableService;
|
||||||
|
import org.opsli.modulars.creater.table.wrapper.CreaterTableAndColumnModel;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @BelongsProject: opsli-boot
|
||||||
|
* @Author: Parker
|
||||||
|
* @CreateTime: 2020-09-15 14:50
|
||||||
|
* @Description: 数据库同步策略 工具类
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Configuration
|
||||||
|
public class ImportTableUtil{
|
||||||
|
|
||||||
|
/** 数据库名 */
|
||||||
|
public static final String DB_NAME;
|
||||||
|
/** 数据库类型 */
|
||||||
|
public static final String DB_TYPE;
|
||||||
|
/** 处理方法集合 */
|
||||||
|
private static final ConcurrentMap<String, DatabaseTableService> HANDLER_MAP = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
Props props = new Props("creater.yaml");
|
||||||
|
DB_NAME = props.getStr("opsli.db-name","opsli-boot");
|
||||||
|
DB_TYPE = props.getStr("opsli.db-type");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得当前数据库中表
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static List<DatabaseTable> findTables() {
|
||||||
|
DatabaseTableService databaseTableService = HANDLER_MAP.get(DB_TYPE);
|
||||||
|
if(databaseTableService == null){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return databaseTableService.findTables(DB_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得当前数据库中表
|
||||||
|
* @param tableName
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static List<DatabaseTable> findTables(String tableName) {
|
||||||
|
DatabaseTableService databaseTableService = HANDLER_MAP.get(DB_TYPE);
|
||||||
|
if(databaseTableService == null){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return databaseTableService.findTables(DB_NAME, tableName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得表字段
|
||||||
|
* @param tableName
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static List<DatabaseColumn> findColumns(String tableName) {
|
||||||
|
DatabaseTableService databaseTableService = HANDLER_MAP.get(DB_TYPE);
|
||||||
|
if(databaseTableService == null){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return databaseTableService.findColumns(DB_NAME, tableName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ====================================
|
||||||
|
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public void initImportTable(){
|
||||||
|
|
||||||
|
// 拿到state包下 实现了 SystemEventState 接口的,所有子类
|
||||||
|
Set<Class<?>> clazzSet = ClassUtil.scanPackageBySuper(DatabaseTableService.class.getPackage().getName()
|
||||||
|
, DatabaseTableService.class
|
||||||
|
);
|
||||||
|
|
||||||
|
for (Class<?> aClass : clazzSet) {
|
||||||
|
// 位运算 去除抽象类
|
||||||
|
if((aClass.getModifiers() & Modifier.ABSTRACT) != 0){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
DatabaseTableService handler = (DatabaseTableService) SpringContextHolder.getBean(aClass);
|
||||||
|
// 加入集合
|
||||||
|
HANDLER_MAP.put(handler.getType(),handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package org.opsli.modulars.creater.importable.constants;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @BelongsProject: opsli-boot
|
||||||
|
* @BelongsPackage: org.opsli.modulars.creater.importable.constants
|
||||||
|
* @Author: Parker
|
||||||
|
* @CreateTime: 2020-11-19 15:27
|
||||||
|
* @Description: 数据库类型
|
||||||
|
*/
|
||||||
|
public interface DbType {
|
||||||
|
|
||||||
|
String DB_MYSQL = "mysql";
|
||||||
|
|
||||||
|
String DB_ORACLE = "oracle";
|
||||||
|
|
||||||
|
String DB_DB2 = "db2";
|
||||||
|
|
||||||
|
String DB_SQLSERVER = "sqlserver";
|
||||||
|
}
|
@ -0,0 +1,64 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2020 OPSLI 快速开发平台 https://www.opsli.com
|
||||||
|
* <p>
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy of
|
||||||
|
* the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
package org.opsli.modulars.creater.importable.entity;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @BelongsProject: opsli-boot
|
||||||
|
* @Author: Parker
|
||||||
|
* @CreateTime: 2020-11-15 17:33
|
||||||
|
* @Description: 代码生成器 - 数据库表字段
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = false)
|
||||||
|
public class DatabaseColumn {
|
||||||
|
|
||||||
|
|
||||||
|
/** 数据库 */
|
||||||
|
private String dbName;
|
||||||
|
|
||||||
|
/** 表名称 */
|
||||||
|
private String tableName;
|
||||||
|
|
||||||
|
/** 字段名称 */
|
||||||
|
private String columnName;
|
||||||
|
|
||||||
|
/** 字段类型 */
|
||||||
|
private String columnType;
|
||||||
|
|
||||||
|
/** 字段长度 */
|
||||||
|
private Integer columnLength;
|
||||||
|
|
||||||
|
/** 字段精度 = 位数 */
|
||||||
|
private Integer columnPrecision;
|
||||||
|
|
||||||
|
/** 字段位数 = 精度 */
|
||||||
|
private Integer columnScale;
|
||||||
|
|
||||||
|
/** 字段描述 */
|
||||||
|
private String columnComment;
|
||||||
|
|
||||||
|
/** 是否主键 */
|
||||||
|
private Character izPk;
|
||||||
|
|
||||||
|
/** 是否可为空 */
|
||||||
|
private Character izNull;
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2020 OPSLI 快速开发平台 https://www.opsli.com
|
||||||
|
* <p>
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy of
|
||||||
|
* the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
package org.opsli.modulars.creater.importable.entity;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @BelongsProject: opsli-boot
|
||||||
|
* @Author: Parker
|
||||||
|
* @CreateTime: 2020-11-15 17:33
|
||||||
|
* @Description: 代码生成器 - 数据库表
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = false)
|
||||||
|
public class DatabaseTable {
|
||||||
|
|
||||||
|
|
||||||
|
/** 数据库 */
|
||||||
|
private String dbName;
|
||||||
|
|
||||||
|
/** 表名称 */
|
||||||
|
private String tableName;
|
||||||
|
|
||||||
|
/** 描述 */
|
||||||
|
private String tableComments;
|
||||||
|
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2020 OPSLI 快速开发平台 https://www.opsli.com
|
||||||
|
* <p>
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy of
|
||||||
|
* the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
package org.opsli.modulars.creater.importable.mapper;
|
||||||
|
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.opsli.modulars.creater.importable.entity.DatabaseColumn;
|
||||||
|
import org.opsli.modulars.creater.importable.entity.DatabaseTable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @BelongsProject: opsli-boot
|
||||||
|
* @Author: Parker
|
||||||
|
* @CreateTime: 2020-09-17 13:01
|
||||||
|
* @Description: 代码生成器 - 数据库表 Mapper
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface MySQLDatabaseTableMapper {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得当前库中 所有表
|
||||||
|
* @param table
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
List<DatabaseTable> findTables(DatabaseTable table);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得当前表中 所有字段
|
||||||
|
* @param column
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
List<DatabaseColumn> findColumns(DatabaseColumn column);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="org.opsli.modulars.creater.importable.mapper.MySQLDatabaseTableMapper">
|
||||||
|
|
||||||
|
|
||||||
|
<select id="findTables" resultType="org.opsli.modulars.creater.importable.entity.DatabaseTable">
|
||||||
|
SELECT
|
||||||
|
TB.TABLE_SCHEMA AS dbName,
|
||||||
|
TB.TABLE_NAME AS tableName,
|
||||||
|
TB.TABLE_COMMENT AS tableComments
|
||||||
|
FROM
|
||||||
|
INFORMATION_SCHEMA.TABLES TB
|
||||||
|
WHERE
|
||||||
|
TB.TABLE_SCHEMA = #{dbName}
|
||||||
|
<if test="tableName != null and tableName != ''">
|
||||||
|
AND TB.TABLE_NAME = #{tableName}
|
||||||
|
</if>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="findColumns" resultType="org.opsli.modulars.creater.importable.entity.DatabaseColumn">
|
||||||
|
SELECT
|
||||||
|
TABLE_SCHEMA AS dbName,
|
||||||
|
TABLE_NAME AS tableName,
|
||||||
|
COLUMN_NAME AS columnName,
|
||||||
|
data_type AS columnType,
|
||||||
|
CHARACTER_MAXIMUM_LENGTH AS columnLength,
|
||||||
|
NUMERIC_PRECISION AS columnPrecision,
|
||||||
|
NUMERIC_SCALE AS columnScale,
|
||||||
|
COLUMN_COMMENT AS columnComment,
|
||||||
|
IF( IS_NULLABLE = 'NO', '1', '0' ) AS izNull,
|
||||||
|
IF( COLUMN_KEY = 'PRI', '1', '0' ) AS izPk
|
||||||
|
FROM
|
||||||
|
INFORMATION_SCHEMA.COLUMNS
|
||||||
|
WHERE
|
||||||
|
TABLE_NAME = #{tableName}
|
||||||
|
AND TABLE_SCHEMA = #{dbName}
|
||||||
|
ORDER BY ORDINAL_POSITION
|
||||||
|
</select>
|
||||||
|
|
||||||
|
</mapper>
|
@ -0,0 +1,60 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2020 OPSLI 快速开发平台 https://www.opsli.com
|
||||||
|
* <p>
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy of
|
||||||
|
* the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
package org.opsli.modulars.creater.importable.service;
|
||||||
|
|
||||||
|
import org.opsli.modulars.creater.importable.entity.DatabaseColumn;
|
||||||
|
import org.opsli.modulars.creater.importable.entity.DatabaseTable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @BelongsProject: opsli-boot
|
||||||
|
* @Author: Parker
|
||||||
|
* @CreateTime: 2020-09-17 13:07
|
||||||
|
* @Description: 代码生成器 - 数据库表
|
||||||
|
*/
|
||||||
|
public interface DatabaseTableService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得类型
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
String getType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得当前库中 所有表
|
||||||
|
* @param dbName
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
List<DatabaseTable> findTables(String dbName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得当前库中 所有表
|
||||||
|
* @param dbName
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
List<DatabaseTable> findTables(String dbName, String tableName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得当前表中 所有字段
|
||||||
|
* @param dbName
|
||||||
|
* @param tableName
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
List<DatabaseColumn> findColumns(String dbName, String tableName);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,116 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2020 OPSLI 快速开发平台 https://www.opsli.com
|
||||||
|
* <p>
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy of
|
||||||
|
* the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
package org.opsli.modulars.creater.importable.service;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.opsli.common.utils.Props;
|
||||||
|
import org.opsli.modulars.creater.importable.constants.DbType;
|
||||||
|
import org.opsli.modulars.creater.importable.entity.DatabaseColumn;
|
||||||
|
import org.opsli.modulars.creater.importable.entity.DatabaseTable;
|
||||||
|
import org.opsli.modulars.creater.importable.mapper.MySQLDatabaseTableMapper;
|
||||||
|
import org.opsli.modulars.creater.table.service.ITableService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @BelongsProject: opsli-boot
|
||||||
|
* @Author: Parker
|
||||||
|
* @CreateTime: 2020-09-16 17:34
|
||||||
|
* @Description: 代码生成器 - 表 接口实现类
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class MySQLDatabaseTableServiceImpl implements DatabaseTableService {
|
||||||
|
|
||||||
|
/** 排除表 */
|
||||||
|
private static final List<String> EXCLUDE_TABLES;
|
||||||
|
|
||||||
|
static {
|
||||||
|
Props props = new Props("creater.yaml");
|
||||||
|
EXCLUDE_TABLES = props.getList("opsli.exclude-tables");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Autowired(required = false)
|
||||||
|
private MySQLDatabaseTableMapper mapper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ITableService iTableService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getType() {
|
||||||
|
return DbType.DB_MYSQL;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<DatabaseTable> findTables(String dbName) {
|
||||||
|
return this.findTables(dbName, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<DatabaseTable> findTables(String dbName, String tableName) {
|
||||||
|
DatabaseTable table = new DatabaseTable();
|
||||||
|
table.setDbName(dbName);
|
||||||
|
|
||||||
|
if(StringUtils.isNotEmpty(tableName)){
|
||||||
|
table.setTableName(tableName);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<DatabaseTable> tables = mapper.findTables(table);
|
||||||
|
|
||||||
|
// 表去重复
|
||||||
|
List<String> currTableNames = iTableService.findAllByTableName();
|
||||||
|
if(currTableNames != null && !currTableNames.isEmpty()){
|
||||||
|
//遍历删除
|
||||||
|
tables.removeIf(tmp -> currTableNames.contains(tmp.getTableName()));
|
||||||
|
//遍历删除
|
||||||
|
tables.removeIf(tmp -> EXCLUDE_TABLES.contains(tmp.getTableName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return tables;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<DatabaseColumn> findColumns(String dbName, String tableName) {
|
||||||
|
DatabaseColumn entity = new DatabaseColumn();
|
||||||
|
entity.setDbName(dbName);
|
||||||
|
entity.setTableName(tableName);
|
||||||
|
|
||||||
|
List<DatabaseColumn> columns = mapper.findColumns(entity);
|
||||||
|
|
||||||
|
// 设置字段长度
|
||||||
|
for (DatabaseColumn column : columns) {
|
||||||
|
// MySQL 中 这两个 有一个会代表为当前字段长度
|
||||||
|
Integer len1 = column.getColumnLength();
|
||||||
|
Integer len2 = column.getColumnPrecision();
|
||||||
|
if(len1 == null && len2 != null){
|
||||||
|
column.setColumnLength(len2);
|
||||||
|
}
|
||||||
|
// 如果小数位不为空 则需要 减掉小数位置
|
||||||
|
if(column.getColumnScale() != null){
|
||||||
|
column.setColumnLength(
|
||||||
|
column.getColumnLength() - column.getColumnScale()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return columns;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in new issue