Merge branch 'main' of github.com:dataease/dataease into main

This commit is contained in:
taojinlong 2021-05-11 19:10:03 +08:00
commit defdc1123c
34 changed files with 2749 additions and 65 deletions

View File

@ -13,6 +13,9 @@ import io.dataease.auth.util.RsaUtil;
import io.dataease.commons.utils.BeanUtils;
import io.dataease.commons.utils.CodingUtil;
import io.dataease.commons.utils.ServletUtils;
/*import io.dataease.plugins.config.SpringContextUtil;
import io.dataease.plugins.xpack.dto.response.SysSettingDto;
import io.dataease.plugins.xpack.service.DePluginXpackService;*/
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
@ -105,6 +108,14 @@ public class AuthServer implements AuthApi {
SysUserEntity userById = authUserService.getUserById(4L);
String nickName = userById.getNickName();
System.out.println(nickName);
/* Map<String, DePluginXpackService> beansOfType = SpringContextUtil.getApplicationContext().getBeansOfType(DePluginXpackService.class);
for (Map.Entry entry : beansOfType.entrySet()) {
Object key = entry.getKey();
DePluginXpackService value = (DePluginXpackService)entry.getValue();
List<SysSettingDto> sysSettingDtos = value.systemSettings();
String name = entry.getValue().getClass().getName();
System.out.println("key: "+ key + ", value: "+ name);
}*/
return "apple";
}
}

View File

@ -46,7 +46,12 @@ public class ShiroServiceImpl implements ShiroService {
filterChainDefinitionMap.put("/chart/view/getData/**", ANON);
filterChainDefinitionMap.put("/system/ui/**", ANON);
filterChainDefinitionMap.put("/PluginDemo.js", ANON);
filterChainDefinitionMap.put("/DeXPack.js", ANON);
filterChainDefinitionMap.put("/api/auth/test", ANON);
filterChainDefinitionMap.put("/api/xpack/test", ANON);
filterChainDefinitionMap.put("/api/auth/login", ANON);
filterChainDefinitionMap.put("/api/auth/validateName", ANON);

View File

@ -0,0 +1,35 @@
package io.dataease.base.domain;
import java.io.Serializable;
import lombok.Data;
@Data
public class MyPlugin implements Serializable {
private Long pluginId;
private String name;
private Boolean free;
private Integer cost;
private String descript;
private String version;
private Integer installType;
private String creator;
private Long releaseTime;
private Long installTime;
private String moduleName;
private String beanName;
private String icon;
private static final long serialVersionUID = 1L;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,30 @@
package io.dataease.base.mapper;
import io.dataease.base.domain.MyPlugin;
import io.dataease.base.domain.MyPluginExample;
import java.util.List;
import org.apache.ibatis.annotations.Param;
public interface MyPluginMapper {
long countByExample(MyPluginExample example);
int deleteByExample(MyPluginExample example);
int deleteByPrimaryKey(Long pluginId);
int insert(MyPlugin record);
int insertSelective(MyPlugin record);
List<MyPlugin> selectByExample(MyPluginExample example);
MyPlugin selectByPrimaryKey(Long pluginId);
int updateByExampleSelective(@Param("record") MyPlugin record, @Param("example") MyPluginExample example);
int updateByExample(@Param("record") MyPlugin record, @Param("example") MyPluginExample example);
int updateByPrimaryKeySelective(MyPlugin record);
int updateByPrimaryKey(MyPlugin record);
}

View File

@ -0,0 +1,338 @@
<?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="io.dataease.base.mapper.MyPluginMapper">
<resultMap id="BaseResultMap" type="io.dataease.base.domain.MyPlugin">
<id column="plugin_id" jdbcType="BIGINT" property="pluginId" />
<result column="name" jdbcType="VARCHAR" property="name" />
<result column="free" jdbcType="BIT" property="free" />
<result column="cost" jdbcType="INTEGER" property="cost" />
<result column="descript" jdbcType="VARCHAR" property="descript" />
<result column="version" jdbcType="VARCHAR" property="version" />
<result column="install_type" jdbcType="INTEGER" property="installType" />
<result column="creator" jdbcType="VARCHAR" property="creator" />
<result column="release_time" jdbcType="BIGINT" property="releaseTime" />
<result column="install_time" jdbcType="BIGINT" property="installTime" />
<result column="module_name" jdbcType="VARCHAR" property="moduleName" />
<result column="bean_name" jdbcType="VARCHAR" property="beanName" />
<result column="icon" jdbcType="VARCHAR" property="icon" />
</resultMap>
<sql id="Example_Where_Clause">
<where>
<foreach collection="oredCriteria" item="criteria" separator="or">
<if test="criteria.valid">
<trim prefix="(" prefixOverrides="and" suffix=")">
<foreach collection="criteria.criteria" item="criterion">
<choose>
<when test="criterion.noValue">
and ${criterion.condition}
</when>
<when test="criterion.singleValue">
and ${criterion.condition} #{criterion.value}
</when>
<when test="criterion.betweenValue">
and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
</when>
<when test="criterion.listValue">
and ${criterion.condition}
<foreach close=")" collection="criterion.value" item="listItem" open="(" separator=",">
#{listItem}
</foreach>
</when>
</choose>
</foreach>
</trim>
</if>
</foreach>
</where>
</sql>
<sql id="Update_By_Example_Where_Clause">
<where>
<foreach collection="example.oredCriteria" item="criteria" separator="or">
<if test="criteria.valid">
<trim prefix="(" prefixOverrides="and" suffix=")">
<foreach collection="criteria.criteria" item="criterion">
<choose>
<when test="criterion.noValue">
and ${criterion.condition}
</when>
<when test="criterion.singleValue">
and ${criterion.condition} #{criterion.value}
</when>
<when test="criterion.betweenValue">
and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
</when>
<when test="criterion.listValue">
and ${criterion.condition}
<foreach close=")" collection="criterion.value" item="listItem" open="(" separator=",">
#{listItem}
</foreach>
</when>
</choose>
</foreach>
</trim>
</if>
</foreach>
</where>
</sql>
<sql id="Base_Column_List">
plugin_id, `name`, `free`, cost, descript, version, install_type, creator, release_time,
install_time, module_name, bean_name, icon
</sql>
<select id="selectByExample" parameterType="io.dataease.base.domain.MyPluginExample" resultMap="BaseResultMap">
select
<if test="distinct">
distinct
</if>
<include refid="Base_Column_List" />
from my_plugin
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
<if test="orderByClause != null">
order by ${orderByClause}
</if>
</select>
<select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from my_plugin
where plugin_id = #{pluginId,jdbcType=BIGINT}
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.Long">
delete from my_plugin
where plugin_id = #{pluginId,jdbcType=BIGINT}
</delete>
<delete id="deleteByExample" parameterType="io.dataease.base.domain.MyPluginExample">
delete from my_plugin
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
</delete>
<insert id="insert" parameterType="io.dataease.base.domain.MyPlugin">
insert into my_plugin (plugin_id, `name`, `free`,
cost, descript, version,
install_type, creator, release_time,
install_time, module_name, bean_name,
icon)
values (#{pluginId,jdbcType=BIGINT}, #{name,jdbcType=VARCHAR}, #{free,jdbcType=BIT},
#{cost,jdbcType=INTEGER}, #{descript,jdbcType=VARCHAR}, #{version,jdbcType=VARCHAR},
#{installType,jdbcType=INTEGER}, #{creator,jdbcType=VARCHAR}, #{releaseTime,jdbcType=BIGINT},
#{installTime,jdbcType=BIGINT}, #{moduleName,jdbcType=VARCHAR}, #{beanName,jdbcType=VARCHAR},
#{icon,jdbcType=VARCHAR})
</insert>
<insert id="insertSelective" parameterType="io.dataease.base.domain.MyPlugin">
insert into my_plugin
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="pluginId != null">
plugin_id,
</if>
<if test="name != null">
`name`,
</if>
<if test="free != null">
`free`,
</if>
<if test="cost != null">
cost,
</if>
<if test="descript != null">
descript,
</if>
<if test="version != null">
version,
</if>
<if test="installType != null">
install_type,
</if>
<if test="creator != null">
creator,
</if>
<if test="releaseTime != null">
release_time,
</if>
<if test="installTime != null">
install_time,
</if>
<if test="moduleName != null">
module_name,
</if>
<if test="beanName != null">
bean_name,
</if>
<if test="icon != null">
icon,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="pluginId != null">
#{pluginId,jdbcType=BIGINT},
</if>
<if test="name != null">
#{name,jdbcType=VARCHAR},
</if>
<if test="free != null">
#{free,jdbcType=BIT},
</if>
<if test="cost != null">
#{cost,jdbcType=INTEGER},
</if>
<if test="descript != null">
#{descript,jdbcType=VARCHAR},
</if>
<if test="version != null">
#{version,jdbcType=VARCHAR},
</if>
<if test="installType != null">
#{installType,jdbcType=INTEGER},
</if>
<if test="creator != null">
#{creator,jdbcType=VARCHAR},
</if>
<if test="releaseTime != null">
#{releaseTime,jdbcType=BIGINT},
</if>
<if test="installTime != null">
#{installTime,jdbcType=BIGINT},
</if>
<if test="moduleName != null">
#{moduleName,jdbcType=VARCHAR},
</if>
<if test="beanName != null">
#{beanName,jdbcType=VARCHAR},
</if>
<if test="icon != null">
#{icon,jdbcType=VARCHAR},
</if>
</trim>
</insert>
<select id="countByExample" parameterType="io.dataease.base.domain.MyPluginExample" resultType="java.lang.Long">
select count(*) from my_plugin
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
</select>
<update id="updateByExampleSelective" parameterType="map">
update my_plugin
<set>
<if test="record.pluginId != null">
plugin_id = #{record.pluginId,jdbcType=BIGINT},
</if>
<if test="record.name != null">
`name` = #{record.name,jdbcType=VARCHAR},
</if>
<if test="record.free != null">
`free` = #{record.free,jdbcType=BIT},
</if>
<if test="record.cost != null">
cost = #{record.cost,jdbcType=INTEGER},
</if>
<if test="record.descript != null">
descript = #{record.descript,jdbcType=VARCHAR},
</if>
<if test="record.version != null">
version = #{record.version,jdbcType=VARCHAR},
</if>
<if test="record.installType != null">
install_type = #{record.installType,jdbcType=INTEGER},
</if>
<if test="record.creator != null">
creator = #{record.creator,jdbcType=VARCHAR},
</if>
<if test="record.releaseTime != null">
release_time = #{record.releaseTime,jdbcType=BIGINT},
</if>
<if test="record.installTime != null">
install_time = #{record.installTime,jdbcType=BIGINT},
</if>
<if test="record.moduleName != null">
module_name = #{record.moduleName,jdbcType=VARCHAR},
</if>
<if test="record.beanName != null">
bean_name = #{record.beanName,jdbcType=VARCHAR},
</if>
<if test="record.icon != null">
icon = #{record.icon,jdbcType=VARCHAR},
</if>
</set>
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
</if>
</update>
<update id="updateByExample" parameterType="map">
update my_plugin
set plugin_id = #{record.pluginId,jdbcType=BIGINT},
`name` = #{record.name,jdbcType=VARCHAR},
`free` = #{record.free,jdbcType=BIT},
cost = #{record.cost,jdbcType=INTEGER},
descript = #{record.descript,jdbcType=VARCHAR},
version = #{record.version,jdbcType=VARCHAR},
install_type = #{record.installType,jdbcType=INTEGER},
creator = #{record.creator,jdbcType=VARCHAR},
release_time = #{record.releaseTime,jdbcType=BIGINT},
install_time = #{record.installTime,jdbcType=BIGINT},
module_name = #{record.moduleName,jdbcType=VARCHAR},
bean_name = #{record.beanName,jdbcType=VARCHAR},
icon = #{record.icon,jdbcType=VARCHAR}
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
</if>
</update>
<update id="updateByPrimaryKeySelective" parameterType="io.dataease.base.domain.MyPlugin">
update my_plugin
<set>
<if test="name != null">
`name` = #{name,jdbcType=VARCHAR},
</if>
<if test="free != null">
`free` = #{free,jdbcType=BIT},
</if>
<if test="cost != null">
cost = #{cost,jdbcType=INTEGER},
</if>
<if test="descript != null">
descript = #{descript,jdbcType=VARCHAR},
</if>
<if test="version != null">
version = #{version,jdbcType=VARCHAR},
</if>
<if test="installType != null">
install_type = #{installType,jdbcType=INTEGER},
</if>
<if test="creator != null">
creator = #{creator,jdbcType=VARCHAR},
</if>
<if test="releaseTime != null">
release_time = #{releaseTime,jdbcType=BIGINT},
</if>
<if test="installTime != null">
install_time = #{installTime,jdbcType=BIGINT},
</if>
<if test="moduleName != null">
module_name = #{moduleName,jdbcType=VARCHAR},
</if>
<if test="beanName != null">
bean_name = #{beanName,jdbcType=VARCHAR},
</if>
<if test="icon != null">
icon = #{icon,jdbcType=VARCHAR},
</if>
</set>
where plugin_id = #{pluginId,jdbcType=BIGINT}
</update>
<update id="updateByPrimaryKey" parameterType="io.dataease.base.domain.MyPlugin">
update my_plugin
set `name` = #{name,jdbcType=VARCHAR},
`free` = #{free,jdbcType=BIT},
cost = #{cost,jdbcType=INTEGER},
descript = #{descript,jdbcType=VARCHAR},
version = #{version,jdbcType=VARCHAR},
install_type = #{installType,jdbcType=INTEGER},
creator = #{creator,jdbcType=VARCHAR},
release_time = #{releaseTime,jdbcType=BIGINT},
install_time = #{installTime,jdbcType=BIGINT},
module_name = #{moduleName,jdbcType=VARCHAR},
bean_name = #{beanName,jdbcType=VARCHAR},
icon = #{icon,jdbcType=VARCHAR}
where plugin_id = #{pluginId,jdbcType=BIGINT}
</update>
</mapper>

View File

@ -0,0 +1,11 @@
package io.dataease.base.mapper.ext;
import io.dataease.base.domain.MyPlugin;
import io.dataease.base.mapper.ext.query.GridExample;
import java.util.List;
public interface ExtSysPluginMapper {
List<MyPlugin> query(GridExample example);
}

View File

@ -0,0 +1,27 @@
<?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="io.dataease.base.mapper.ext.ExtSysPluginMapper">
<select id="query" parameterType="io.dataease.base.mapper.ext.query.GridExample" resultMap="io.dataease.base.mapper.MyPluginMapper.BaseResultMap">
select *
from my_plugin
<if test="_parameter != null">
<include refid="io.dataease.base.mapper.ext.query.GridSql.gridCondition" />
</if>
<if test="orderByClause != null">
order by ${orderByClause}
</if>
<if test="orderByClause == null">
order by install_time desc
</if>
</select>
</mapper>

View File

@ -0,0 +1,115 @@
package io.dataease.commons.utils;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.nio.channels.FileChannel;
public class DeFileUtils {
/**
* Java文件操作 获取不带扩展名的文件名
*/
public static String getFileNameNoEx(String filename) {
if ((filename != null) && (filename.length() > 0)) {
int dot = filename.lastIndexOf('.');
if ((dot > -1) && (dot < (filename.length()))) {
return filename.substring(0, dot);
}
}
return filename;
}
/**
* 获取文件扩展名不带 .
*/
public static String getExtensionName(String filename) {
if ((filename != null) && (filename.length() > 0)) {
int dot = filename.lastIndexOf('.');
if ((dot > -1) && (dot < (filename.length() - 1))) {
return filename.substring(dot + 1);
}
}
return filename;
}
/**
* 将文件名解析成文件的上传路径
*/
public static File upload(MultipartFile file, String filePath) {
String name = getFileNameNoEx(file.getOriginalFilename());
String suffix = getExtensionName(file.getOriginalFilename());
try {
String fileName = name + "." + suffix;
String path = filePath + fileName;
// getCanonicalFile 可解析正确各种路径
File dest = new File(path).getCanonicalFile();
// 检测是否存在目录
if (!dest.getParentFile().exists()) {
if (!dest.getParentFile().mkdirs()) {
System.out.println("was not successful.");
}
}
// 文件写入
// file.transferTo(dest);
FileOutputStream fileOutputStream = new FileOutputStream(dest);
fileOutputStream.write(file.getBytes());
fileOutputStream.flush();
fileOutputStream.close();
return dest;
} catch (Exception e) {
LogUtil.error(e.getMessage(), e);
}
return null;
}
public static String copy(File source, String targetDir) throws IOException{
String name = source.getName();
String destPath = null;
if (targetDir.endsWith("/") || targetDir.endsWith("\\")){
destPath = targetDir + name;
}else{
destPath = targetDir + "/" + name;
}
File DestFile = new File(destPath);
if (!DestFile.getParentFile().exists()) {
DestFile.getParentFile().mkdirs();
}
copyFileUsingFileChannels(source, DestFile);
return destPath;
}
private static void copyFileUsingFileChannels(File source, File dest) throws IOException {
FileChannel inputChannel = null;
FileChannel outputChannel = null;
try {
inputChannel = new FileInputStream(source).getChannel();
outputChannel = new FileOutputStream(dest).getChannel();
outputChannel.transferFrom(inputChannel, 0, inputChannel.size());
} finally {
inputChannel.close();
outputChannel.close();
}
}
public static String readJson(File file) {
String str = null;
try {
FileReader fileReader = new FileReader(file);
Reader reader = new InputStreamReader(new FileInputStream(file), "utf-8");
int ch=0;
StringBuffer sb = new StringBuffer();
while ((ch = reader.read()) != -1) {
sb.append((char) ch);
}
fileReader.close();
reader.close();
str = sb.toString();
return str;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}

View File

@ -0,0 +1,141 @@
package io.dataease.commons.utils;
import java.io.*;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
public class ZipUtils {
/**
* 解压文件
*
* @param zipFilePath 解压文件路径
* @param outputFolder 输出解压文件路径
*/
public static void unZipIt(String zipFilePath, String outputFolder) {
byte[] buffer = new byte[1024];
File folder = new File(outputFolder);
if (!folder.exists()) {
folder.mkdir();
}
try {
//get the zip file content
ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFilePath));
ZipEntry ze = zis.getNextEntry();
while (ze != null) {
String fileName = ze.getName();
File newFile = new File(outputFolder + File.separator + fileName);
System.out.println("file unzip : " + newFile.getAbsoluteFile());
//大部分网络上的源码这里没有判断子目录
if (ze.isDirectory()) {
if (!newFile.mkdirs()) {
System.out.println("was not successful.");
}
} else {
if (!new File(newFile.getParent()).mkdirs()) {
System.out.println("was not successful.");
}
FileOutputStream fos = new FileOutputStream(newFile);
int len;
while ((len = zis.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
fos.close();
}
ze = zis.getNextEntry();
}
zis.closeEntry();
zis.close();
System.out.println("Done");
} catch (IOException e) {
e.printStackTrace();
}
}
public static void unzip(File source, String out) throws IOException {
try (ZipInputStream zis = new ZipInputStream(new FileInputStream(source))) {
ZipEntry entry = zis.getNextEntry();
while (entry != null) {
File file = new File(out, entry.getName());
if (entry.isDirectory()) {
if (!file.mkdirs()) {
System.out.println("was not successful.");
}
} else {
File parent = file.getParentFile();
if (!parent.exists()) {
if (!parent.mkdirs()) {
System.out.println("was not successful.");
}
}
try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file))) {
byte[] buffer = new byte[Math.toIntExact(entry.getSize())];
int location;
while ((location = zis.read(buffer)) != -1) {
bos.write(buffer, 0, location);
}
}
}
entry = zis.getNextEntry();
}
}
}
/**
* 把所有文件都直接解压到指定目录(忽略子文件夹)
*
* @param zipFile
* @param folderPath
* @throws ZipException
* @throws IOException
*/
public static void upZipFile(File zipFile, String folderPath) throws ZipException, IOException {
File desDir = new File(folderPath);
if (!desDir.exists()) {
if (!desDir.mkdirs()) {
System.out.println("was not successful.");
}
}
ZipFile zf = new ZipFile(zipFile);
for (Enumeration<?> entries = zf.entries(); entries.hasMoreElements(); ) {
ZipEntry entry = ((ZipEntry) entries.nextElement());
InputStream in = zf.getInputStream(entry);
File desFile = new File(folderPath, java.net.URLEncoder.encode(entry.getName(), "UTF-8"));
if (!desFile.exists()) {
File fileParentDir = desFile.getParentFile();
if (!fileParentDir.exists()) {
if (!fileParentDir.mkdirs()) {
System.out.println("was not successful.");
}
}
}
OutputStream out = new FileOutputStream(desFile);
byte[] buffer = new byte[1024 * 1024];
int realLength = in.read(buffer);
while (realLength != -1) {
out.write(buffer, 0, realLength);
realLength = in.read(buffer);
}
out.close();
in.close();
}
}
}

View File

@ -0,0 +1,39 @@
package io.dataease.controller.sys;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import io.dataease.base.domain.MyPlugin;
import io.dataease.commons.utils.PageUtils;
import io.dataease.commons.utils.Pager;
import io.dataease.controller.sys.base.BaseGridRequest;
import io.dataease.service.sys.PluginService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
import java.util.Map;
@RestController
@Api(tags = "系统:插件管理")
@RequestMapping("/api/plugin")
public class SysPluginController {
@Autowired
private PluginService pluginService;
@ApiOperation("查询已安装插件")
@PostMapping("/pluginGrid/{goPage}/{pageSize}")
public Pager<List<MyPlugin>> pluginGrid(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody BaseGridRequest request) {
Page<Object> page = PageHelper.startPage(goPage, pageSize, true);
return PageUtils.setPageInfo(page, pluginService.query(request));
}
@PostMapping("upload")
public Map<String, Object> localUpload(@RequestParam("file") MultipartFile file) throws Exception {
return pluginService.localInstall(file);
}
}

View File

@ -17,6 +17,8 @@ public class ChartViewFieldDTO implements Serializable {
private String originName;
private String dataeaseName;
private String name;
private String type;

View File

@ -0,0 +1,51 @@
package io.dataease.plugins.config;
import io.dataease.plugins.loader.ClassloaderResponsity;
import io.dataease.plugins.loader.ModuleClassLoader;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.List;
import java.util.Map;
@Component
public class LoadjarUtil {
public List<?> loadJar(String jarPath){
File jar = new File(jarPath);
URI uri = jar.toURI();
String moduleName = jarPath.substring(jarPath.lastIndexOf("/")+1,jarPath.lastIndexOf("."));
try {
if(ClassloaderResponsity.getInstance().containsClassLoader(moduleName)){
ClassloaderResponsity.getInstance().removeClassLoader(moduleName);
}
ModuleClassLoader classLoader = new ModuleClassLoader(new URL[]{uri.toURL()}, Thread.currentThread().getContextClassLoader());
SpringContextUtil.getBeanFactory().setBeanClassLoader(classLoader);
Thread.currentThread().setContextClassLoader(classLoader);
classLoader.initBean();
ClassloaderResponsity.getInstance().addClassLoader(moduleName,classLoader);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return SpringContextUtil.getAllBean();
}
public List<Map<String, Object>> deleteModule(String moduleName){
if(ClassloaderResponsity.getInstance().containsClassLoader(moduleName)){
ClassloaderResponsity.getInstance().removeClassLoader(moduleName);
}
return beans();
}
public List<Map<String, Object>> beans(){
return SpringContextUtil.getAllBean();
}
}

View File

@ -0,0 +1,85 @@
package io.dataease.plugins.config;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Component
public class SpringContextUtil implements ApplicationContextAware {
//获取bean工厂用来实现动态注入bean
//不能使用其他类加载器加载bean
//否则会出现异常:类未找到类未定义
public static DefaultListableBeanFactory getBeanFactory(){
return (DefaultListableBeanFactory) getApplicationContext().getAutowireCapableBeanFactory();
}
public static List<Map<String, Object>> getAllBean() {
List<Map<String, Object>> list = new ArrayList<>();
String[] beans = getApplicationContext()
.getBeanDefinitionNames();
for (String beanName : beans) {
Class<?> beanType = getApplicationContext()
.getType(beanName);
Map<String, Object> map = new HashMap<>();
map.put("BeanName", beanName);
map.put("beanType", beanType);
map.put("package", beanType.getPackage());
list.add(map);
}
return list;
}
/**
* 上下文对象实例
*/
private static ApplicationContext applicationContext;
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
/**
* 获取applicationContext
*
* @return
*/
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
public static Object getBean(String name) {
return getApplicationContext().getBean(name);
}
public static <T> T getBean(Class<T> clazz) {
return getApplicationContext().getBean(clazz);
}
public static <T> T getBean(String name, Class<T> clazz) {
return getApplicationContext().getBean(name, clazz);
}
}

View File

@ -0,0 +1,65 @@
package io.dataease.plugins.loader;
import io.dataease.plugins.config.SpringContextUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class ClassloaderResponsity {
private static Logger logger = LoggerFactory.getLogger(ClassloaderResponsity.class);
private ClassloaderResponsity(){}
private Map<String,ModuleClassLoader> responsityMap = new ConcurrentHashMap<>();
public void addClassLoader(String moduleName,ModuleClassLoader moduleClassLoader){
responsityMap.put(moduleName,moduleClassLoader);
}
public boolean containsClassLoader(String key){
return responsityMap.containsKey(key);
}
public ModuleClassLoader getClassLoader(String key){
return responsityMap.get(key);
}
public void removeClassLoader(String moduleName){
ModuleClassLoader moduleClassLoader = responsityMap.get(moduleName);
try {
List<String> registeredBean = moduleClassLoader.getRegisteredBean();
for (String beanName : registeredBean) {
logger.info("删除bean:"+beanName);
SpringContextUtil.getBeanFactory().removeBeanDefinition(beanName);
}
moduleClassLoader.close();
responsityMap.remove(moduleName);
} catch (IOException e) {
logger.error("删除"+moduleName+"模块发生错误");
}
}
private static class ClassloaderResponsityHodler{
private static ClassloaderResponsity instamce = new ClassloaderResponsity();
}
public static ClassloaderResponsity getInstance(){
return ClassloaderResponsityHodler.instamce;
}
}

View File

@ -0,0 +1,189 @@
package io.dataease.plugins.loader;
import io.dataease.plugins.config.SpringContextUtil;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public class ModuleClassLoader extends URLClassLoader {
//属于本类加载器加载的jar包
private JarFile jarFile;
//保存已经加载过的Class对象
private Map<String,Class> cacheClassMap = new HashMap<>();
//保存本类加载器加载的class字节码
private Map<String,byte[]> classBytesMap = new HashMap<>();
//需要注册的spring bean的name集合
private List<String> registeredBean = new ArrayList<>();
//构造
public ModuleClassLoader(URL[] urls, ClassLoader parent) {
super(urls, parent);
URL url = urls[0];
String path = url.getPath();
try {
jarFile = new JarFile(path);
} catch (IOException e) {
e.printStackTrace();
}
//初始化类加载器执行类加载
init();
}
//重写loadClass方法
//改写loadClass方式
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
if(findLoadedClass(name)==null){
return super.loadClass(name);
}else{
return cacheClassMap.get(name);
}
}
/**
* 方法描述 初始化类加载器保存字节码
* @method init
*/
private void init() {
//解析jar包每一项
Enumeration<JarEntry> en = jarFile.entries();
InputStream input = null;
try{
while (en.hasMoreElements()) {
JarEntry je = en.nextElement();
String name = je.getName();
//这里添加了路径扫描限制
if (name.endsWith(".class")) {
String className = name.replace(".class", "").replaceAll("/", ".");
input = jarFile.getInputStream(je);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int bufferSize = 4096;
byte[] buffer = new byte[bufferSize];
int bytesNumRead = 0;
while ((bytesNumRead = input.read(buffer)) != -1) {
baos.write(buffer, 0, bytesNumRead);
}
byte[] classBytes = baos.toByteArray();
classBytesMap.put(className,classBytes);
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if(input!=null){
try {
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//将jar中的每一个class字节码进行Class载入
for (Map.Entry<String, byte[]> entry : classBytesMap.entrySet()) {
String key = entry.getKey();
Class<?> aClass = null;
try {
aClass = loadClass(key);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
cacheClassMap.put(key,aClass);
}
}
/**
* 方法描述 初始化spring bean
* @method initBean
*/
public void initBean(){
for (Map.Entry<String, Class> entry : cacheClassMap.entrySet()) {
String className = entry.getKey();
Class<?> cla = entry.getValue();
if(isSpringBeanClass(cla)){
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(cla);
BeanDefinition beanDefinition = beanDefinitionBuilder.getRawBeanDefinition();
//设置当前bean定义对象是单利的
beanDefinition.setScope("singleton");
//将变量首字母置小写
String beanName = StringUtils.uncapitalize(className);
beanName = beanName.substring(beanName.lastIndexOf(".")+1);
beanName = StringUtils.uncapitalize(beanName);
SpringContextUtil.getBeanFactory().registerBeanDefinition(beanName,beanDefinition);
registeredBean.add(beanName);
System.out.println("注册bean:"+beanName);
}
}
}
//获取当前类加载器注册的bean
//在移除当前类加载器的时候需要手动删除这些注册的bean
public List<String> getRegisteredBean() {
return registeredBean;
}
/**
* 方法描述 判断class对象是否带有spring的注解
* @method isSpringBeanClass
* @param cla jar中的每一个class
* @return true 是spring bean false 不是spring bean
*/
public boolean isSpringBeanClass(Class<?> cla){
if(cla==null){
return false;
}
//是否是接口
if(cla.isInterface()){
return false;
}
//是否是抽象类
if( Modifier.isAbstract(cla.getModifiers())){
return false;
}
if(cla.getAnnotation(Component.class)!=null){
return true;
}
if(cla.getAnnotation(Repository.class)!=null){
return true;
}
if(cla.getAnnotation(Service.class)!=null){
return true;
}
return false;
}
}

View File

@ -150,7 +150,7 @@ public class ChartViewService {
// data = sparkCalc.getData(table.getId(), fields, xAxis, yAxis, "tmp_" + view.getId().split("-")[0], extFilterList);
// 连接doris构建doris数据源查询
Datasource ds = (Datasource) CommonBeanFactory.getBean("DorisDatasource");
Datasource ds = (Datasource) CommonBeanFactory.getBean("DorisDatasource");
DatasourceProvider datasourceProvider = ProviderFactory.getProvider(ds.getType());
DatasourceRequest datasourceRequest = new DatasourceRequest();
datasourceRequest.setDatasource(ds);
@ -199,9 +199,9 @@ public class ChartViewService {
for (int i = 0; i < fields.size(); i++) {
ChartViewFieldDTO chartViewFieldDTO = fields.get(i);
if (chartViewFieldDTO.getDeType() == 0 || chartViewFieldDTO.getDeType() == 1) {
d.put(fields.get(i).getOriginName(), ele[i]);
d.put(fields.get(i).getDataeaseName(), ele[i]);
} else if (chartViewFieldDTO.getDeType() == 2 || chartViewFieldDTO.getDeType() == 3) {
d.put(fields.get(i).getOriginName(), new BigDecimal(ele[i]).setScale(2, RoundingMode.HALF_UP));
d.put(fields.get(i).getDataeaseName(), new BigDecimal(ele[i]).setScale(2, RoundingMode.HALF_UP));
}
}
tableRow.add(d);
@ -231,7 +231,7 @@ public class ChartViewService {
}
DatasetTableField field = request.getDatasetTableField();
filter.append(" AND ")
.append(field.getOriginName())
.append(field.getDataeaseName())
.append(" ")
.append(transMysqlFilterTerm(request.getOperator()))
.append(" ");
@ -259,12 +259,12 @@ public class ChartViewService {
public String transMysqlSQL(String table, List<ChartViewFieldDTO> xAxis, List<ChartViewFieldDTO> yAxis, List<ChartExtFilterRequest> extFilterRequestList) {
// 字段汇总 排序等
String[] field = yAxis.stream().map(y -> "CAST(" + y.getSummary() + "(" + y.getOriginName() + ") AS DECIMAL(20,2)) AS _" + y.getSummary() + "_" + (StringUtils.equalsIgnoreCase(y.getOriginName(), "*") ? "" : y.getOriginName())).toArray(String[]::new);
String[] group = xAxis.stream().map(ChartViewFieldDTO::getOriginName).toArray(String[]::new);
String[] field = yAxis.stream().map(y -> "CAST(" + y.getSummary() + "(" + y.getDataeaseName() + ") AS " + (y.getDeType() == 2 ? "DECIMAL(20,0)" : "DECIMAL(20,2)") + ") AS _" + y.getSummary() + "_" + (StringUtils.equalsIgnoreCase(y.getDataeaseName(), "*") ? "" : y.getDataeaseName())).toArray(String[]::new);
String[] group = xAxis.stream().map(ChartViewFieldDTO::getDataeaseName).toArray(String[]::new);
String[] xOrder = xAxis.stream().filter(f -> StringUtils.isNotEmpty(f.getSort()) && !StringUtils.equalsIgnoreCase(f.getSort(), "none"))
.map(f -> f.getOriginName() + " " + f.getSort()).toArray(String[]::new);
.map(f -> f.getDataeaseName() + " " + f.getSort()).toArray(String[]::new);
String[] yOrder = yAxis.stream().filter(f -> StringUtils.isNotEmpty(f.getSort()) && !StringUtils.equalsIgnoreCase(f.getSort(), "none"))
.map(f -> "_" + f.getSummary() + "_" + (StringUtils.equalsIgnoreCase(f.getOriginName(), "*") ? "" : f.getOriginName()) + " " + f.getSort()).toArray(String[]::new);
.map(f -> "_" + f.getSummary() + "_" + (StringUtils.equalsIgnoreCase(f.getDataeaseName(), "*") ? "" : f.getDataeaseName()) + " " + f.getSort()).toArray(String[]::new);
String[] order = Arrays.copyOf(xOrder, xOrder.length + yOrder.length);
System.arraycopy(yOrder, 0, order, xOrder.length, yOrder.length);
@ -272,7 +272,7 @@ public class ChartViewService {
.map(x -> {
String[] s = x.getFilter().stream().map(f -> {
StringBuilder filter = new StringBuilder();
filter.append(" AND ").append(x.getOriginName()).append(transMysqlFilterTerm(f.getTerm()));
filter.append(" AND ").append(x.getDataeaseName()).append(transMysqlFilterTerm(f.getTerm()));
if (StringUtils.containsIgnoreCase(f.getTerm(), "null")) {
} else if (StringUtils.containsIgnoreCase(f.getTerm(), "in")) {
filter.append("('").append(StringUtils.join(f.getValue(), "','")).append("')");
@ -301,7 +301,7 @@ public class ChartViewService {
.map(y -> {
String[] s = y.getFilter().stream().map(f -> {
StringBuilder filter = new StringBuilder();
filter.append(" AND _").append(y.getSummary()).append("_").append(StringUtils.equalsIgnoreCase(y.getOriginName(), "*") ? "" : y.getOriginName()).append(transMysqlFilterTerm(f.getTerm()));
filter.append(" AND _").append(y.getSummary()).append("_").append(StringUtils.equalsIgnoreCase(y.getDataeaseName(), "*") ? "" : y.getDataeaseName()).append(transMysqlFilterTerm(f.getTerm()));
if (StringUtils.containsIgnoreCase(f.getTerm(), "null")) {
} else if (StringUtils.containsIgnoreCase(f.getTerm(), "in")) {
filter.append("('").append(StringUtils.join(f.getValue(), "','")).append("')");

View File

@ -74,6 +74,11 @@ public class DataSetTableService {
public DatasetTable save(DatasetTable datasetTable) throws Exception {
checkName(datasetTable);
if (StringUtils.equalsIgnoreCase(datasetTable.getType(), "sql")) {
DataSetTableRequest dataSetTableRequest = new DataSetTableRequest();
BeanUtils.copyBean(dataSetTableRequest, datasetTable);
getSQLPreview(dataSetTableRequest);
}
if (StringUtils.isEmpty(datasetTable.getId())) {
datasetTable.setId(UUID.randomUUID().toString());
datasetTable.setCreateBy(AuthUtils.getUser().getUsername());
@ -163,6 +168,7 @@ public class DataSetTableService {
.tableId(dataSetTableRequest.getId())
.originName("*")
.name("记录数*")
.dataeaseName("*")
.type("INT")
.checked(true)
.columnIndex(999)
@ -221,7 +227,7 @@ public class DataSetTableService {
String table = dataTableInfoDTO.getTable();
datasourceRequest.setQuery(createQuerySQL(ds.getType(), table, fieldArray) + " LIMIT " + (page - 1) * pageSize + "," + realSize);
LogUtil.info(datasourceRequest.getQuery());
try {
data.addAll(datasourceProvider.getData(datasourceRequest));
} catch (Exception e) {
@ -241,7 +247,7 @@ public class DataSetTableService {
String sql = dataTableInfoDTO.getSql();
datasourceRequest.setQuery(createQuerySQL(ds.getType(), " (" + sql + ") AS tmp ", fieldArray) + " LIMIT " + (page - 1) * pageSize + "," + realSize);
LogUtil.info(datasourceRequest.getQuery());
try {
data.addAll(datasourceProvider.getData(datasourceRequest));
} catch (Exception e) {
@ -260,7 +266,7 @@ public class DataSetTableService {
datasourceRequest.setDatasource(ds);
String table = DorisTableUtils.dorisName(dataSetTableRequest.getId());
datasourceRequest.setQuery(createQuerySQL(ds.getType(), table, fieldArray) + " LIMIT " + (page - 1) * pageSize + "," + realSize);
LogUtil.info(datasourceRequest.getQuery());
try {
data.addAll(jdbcProvider.getData(datasourceRequest));
} catch (Exception e) {
@ -280,7 +286,7 @@ public class DataSetTableService {
datasourceRequest.setDatasource(ds);
String table = DorisTableUtils.dorisName(dataSetTableRequest.getId());
datasourceRequest.setQuery(createQuerySQL(ds.getType(), table, fieldArray) + " LIMIT " + (page - 1) * pageSize + "," + realSize);
LogUtil.info(datasourceRequest.getQuery());
try {
data.addAll(jdbcProvider.getData(datasourceRequest));
} catch (Exception e) {
@ -320,7 +326,11 @@ public class DataSetTableService {
DatasourceRequest datasourceRequest = new DatasourceRequest();
datasourceRequest.setDatasource(ds);
String sql = new Gson().fromJson(dataSetTableRequest.getInfo(), DataTableInfoDTO.class).getSql();
datasourceRequest.setQuery("SELECT * FROM (" + sql + ") AS tmp LIMIT 0,1000");
// 使用输入的sql先预执行一次,并拿到所有字段
datasourceRequest.setQuery(sql);
List<TableFiled> previewFields = datasourceProvider.fetchResultField(datasourceRequest);
// 正式执行
datasourceRequest.setQuery("SELECT * FROM (" + sql + ") AS tmp ORDER BY " + previewFields.get(0).getFieldName() + " LIMIT 0,1000");
Map<String, List> result = datasourceProvider.fetchResultAndField(datasourceRequest);
List<String[]> data = result.get("dataList");
List<TableFiled> fields = result.get("fieldList");
@ -353,8 +363,12 @@ public class DataSetTableService {
DataTableInfoDTO dataTableInfoDTO = new Gson().fromJson(dataSetTableRequest.getInfo(), DataTableInfoDTO.class);
List<DataSetTableUnionDTO> list = dataSetTableUnionService.listByTableId(dataTableInfoDTO.getList().get(0).getTableId());
String sql = getCustomSQL(dataTableInfoDTO, list);
// 使用输入的sql先预执行一次,并拿到所有字段
datasourceRequest.setQuery(sql);
List<TableFiled> previewFields = jdbcProvider.fetchResultField(datasourceRequest);
datasourceRequest.setQuery(getCustomSQL(dataTableInfoDTO, list));
datasourceRequest.setQuery("SELECT * FROM (" + sql + ") AS tmp ORDER BY " + previewFields.get(0).getFieldName() + " LIMIT 0,1000");
Map<String, List> result = jdbcProvider.fetchResultAndField(datasourceRequest);
List<String[]> data = result.get("dataList");
List<TableFiled> fields = result.get("fieldList");
@ -543,11 +557,11 @@ public class DataSetTableService {
DatasourceTypes datasourceType = DatasourceTypes.valueOf(type);
switch (datasourceType) {
case mysql:
return MessageFormat.format("SELECT {0} FROM {1}", StringUtils.join(fields, ","), table);
return MessageFormat.format("SELECT {0} FROM {1} ORDER BY " + (fields.length > 0 ? fields[0] : "null"), StringUtils.join(fields, ","), table);
case sqlServer:
return MessageFormat.format("SELECT {0} FROM {1}", StringUtils.join(fields, ","), table);
return MessageFormat.format("SELECT {0} FROM {1} ORDER BY " + (fields.length > 0 ? fields[0] : "null"), StringUtils.join(fields, ","), table);
default:
return MessageFormat.format("SELECT {0} FROM {1}", StringUtils.join(fields, ","), table);
return MessageFormat.format("SELECT {0} FROM {1} ORDER BY " + (fields.length > 0 ? fields[0] : "null"), StringUtils.join(fields, ","), table);
}
}

View File

@ -0,0 +1,134 @@
package io.dataease.service.sys;
import com.google.gson.Gson;
import io.dataease.base.domain.MyPlugin;
import io.dataease.base.mapper.MyPluginMapper;
import io.dataease.base.mapper.ext.ExtSysPluginMapper;
import io.dataease.base.mapper.ext.query.GridExample;
import io.dataease.commons.utils.BeanUtils;
import io.dataease.commons.utils.DeFileUtils;
import io.dataease.commons.utils.ZipUtils;
import io.dataease.controller.sys.base.BaseGridRequest;
import io.dataease.plugins.config.LoadjarUtil;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Map;
@Service
public class PluginService {
@Value("${dataease.plugin.dir:/opt/dataease/plugins/}")
private String pluginDir;
private final static String pluginJsonName = "plugin.json";
@Resource
private ExtSysPluginMapper extSysPluginMapper;
@Resource
private MyPluginMapper myPluginMapper;
@Resource
private LoadjarUtil loadjarUtil;
public List<MyPlugin> query(BaseGridRequest request) {
GridExample gridExample = request.convertExample();
List<MyPlugin> results = extSysPluginMapper.query(gridExample);
return results;
}
/**
* 从本地安装处插件
* @param file
* @return
*/
public Map<String, Object> localInstall(MultipartFile file) {
//1.上传文件到服务器pluginDir目录下
File dest = DeFileUtils.upload(file, pluginDir+"temp/");
//2.解压目标文件dest 得到plugin.json和jar
String folder = pluginDir+"folder/";
try {
ZipUtils.upZipFile(dest, folder);
} catch (IOException e) {
// 需要删除文件
e.printStackTrace();
}
//3.解析plugin.json 失败则 直接返回错误 删除文件
File folderFile = new File(folder);
File[] jsonFiles = folderFile.listFiles(this::isPluginJson);
if (ArrayUtils.isEmpty(jsonFiles)) {
throw new RuntimeException("缺少插件描述文件");
}
MyPlugin myPlugin = formatJsonFile(jsonFiles[0]);
//4.加载jar包 失败则 直接返回错误 删除文件
File[] jarFiles = folderFile.listFiles(this::isPluginJar);
if (ArrayUtils.isEmpty(jarFiles)) {
throw new RuntimeException("缺少插件jar文件");
}
File jarFile = jarFiles[0];
String jarRoot = pluginDir+"jar/";
String jarPath = null;
try {
jarPath = DeFileUtils.copy(jarFile, jarRoot);
} catch (IOException e) {
e.printStackTrace();
}
loadjarUtil.loadJar(jarPath);
//5.写表到my_plugin
myPlugin.setPluginId(0L);
myPluginMapper.insert(myPlugin);
return null;
}
//判断当前文件是否实插件描述文件
//文件名称必须plugin.json
private boolean isPluginJson(File file) {
return StringUtils.equals(file.getName(), pluginJsonName);
}
private boolean isPluginJar(File file) {
String name = file.getName();
return StringUtils.equals(DeFileUtils.getExtensionName(name), "jar");
}
/**
* 从plugin.json文件反序列化为MyPlugin实例对象
* @return
*/
private MyPlugin formatJsonFile(File file) {
String str = DeFileUtils.readJson(file);
Gson gson = new Gson();
Map<String, Object> myPlugin = gson.fromJson(str, Map.class);
myPlugin.put("free", (Double)myPlugin.get("free") > 0.0);
MyPlugin result = new MyPlugin();
try {
org.apache.commons.beanutils.BeanUtils.populate(result, myPlugin);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
//BeanUtils.copyBean(result, myPlugin);
return result;
}
/**
* 从插件商城远程安装插件
* 2.0版本实现
* @param params
* @return
*/
public Map<String, Object> remoteInstall(Map<String, Object> params) {
return null;
}
}

View File

@ -239,3 +239,35 @@ INSERT INTO `sys_users_roles` VALUES (19, 4);
COMMIT;
SET FOREIGN_KEY_CHECKS = 1;
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for my_plugin
-- ----------------------------
DROP TABLE IF EXISTS `my_plugin`;
CREATE TABLE `my_plugin` (
`plugin_id` bigint(20) NOT NULL COMMENT '主键',
`name` varchar(255) DEFAULT NULL COMMENT '插件名称',
`free` tinyint(1) DEFAULT NULL COMMENT '是否免费',
`cost` int(10) DEFAULT NULL COMMENT '费用',
`descript` varchar(255) DEFAULT NULL COMMENT '描述',
`version` varchar(255) DEFAULT NULL COMMENT '版本号',
`install_type` int(4) DEFAULT NULL COMMENT '安装类型',
`creator` varchar(255) DEFAULT NULL COMMENT '开发者',
`release_time` bigint(13) DEFAULT NULL COMMENT '发布时间',
`install_time` bigint(13) DEFAULT NULL COMMENT '安装时间',
`module_name` varchar(255) DEFAULT NULL COMMENT 'jar包名称',
`bean_name` varchar(40) DEFAULT NULL COMMENT 'bean名称',
`icon` varchar(255) DEFAULT NULL COMMENT '图标',
PRIMARY KEY (`plugin_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of my_plugin
-- ----------------------------
BEGIN;
COMMIT;
SET FOREIGN_KEY_CHECKS = 1;

View File

@ -67,7 +67,7 @@
<!-- <table tableName="datasource"/>-->
<!-- <table tableName="sys_dict"/>-->
<!-- <table tableName="sys_dict_item"/>-->
<table tableName="dataset_table_field"/>
<table tableName="my_plugin"/>
<!-- <table tableName="panel_design"/>-->

View File

@ -35,6 +35,7 @@
"umy-ui": "^1.1.6",
"vcolorpicker": "^1.1.0",
"vue": "2.6.10",
"vue-axios": "3.2.4",
"vue-clipboard2": "0.3.1",
"vue-codemirror": "^4.0.6",
"vue-i18n": "7.3.2",

View File

@ -4,7 +4,8 @@
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<!-- <link rel="icon" href="<%= BASE_URL %>favicon.ico">-->
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<title><%= webpackConfig.name %></title>
</head>
<body>

View File

@ -0,0 +1,13 @@
import request from '@/utils/request'
const pathMap = {
queryPath: '/api/plugin/pluginGrid/'
}
export function pluginLists(page, size, data) {
return request({
url: pathMap.queryPath + page + '/' + size,
method: 'post',
data,
loading: true
})
}

View File

@ -0,0 +1,53 @@
<template>
<component
:is="mode"
v-bind="$attrs"
v-on="$listeners"
/>
</template>
<script>
import Axios from 'axios'
export default {
name: 'AsyncComponent',
inheritAttrs: true,
props: {
//
url: {
type: String,
default: ''
}
},
data() {
return {
resData: '',
mode: ''
}
},
watch: {
url: {
immediate: true,
async handler(val, oldVal) {
if (!this.url) return
// Cache url
if (!window.SyncComponentCache) {
window.SyncComponentCache = {}
}
let res
if (!window.SyncComponentCache[this.url]) {
window.SyncComponentCache[this.url] = Axios.get(this.url)
res = await window.SyncComponentCache[this.url]
} else {
res = await window.SyncComponentCache[this.url]
}
const Fn = Function
this.mode = new Fn(`return ${res.data}`)()
}
}
},
methods: {
}
}
</script>

View File

@ -812,9 +812,10 @@ export default {
target_field: '被关联字段',
union_relation: '关联关系',
pls_setting_union_success: '请正确设置关联关系',
invalid_dataset:'Kettle未运行无效数据集',
invalid_dataset: 'Kettle未运行无效数据集',
check_all: '全选',
can_not_union_self: '被关联表不能与关联表相同'
can_not_union_self: '被关联表不能与关联表相同',
float: '小数'
},
datasource: {
datasource: '数据源',
@ -887,5 +888,20 @@ export default {
back: '返回',
view: '视图',
edit: '编辑'
},
plugin: {
local_install: '本地安装',
remote_install: '远程安装',
name: '插件名称',
free: '是否免费',
cost: '费用',
descript: '描述',
version: '版本',
creator: '作者',
install_time: '安装时间',
release_time: '时间',
un_install: '卸载',
uninstall_confirm: '确定卸载该插件',
uninstall_cancel: '取消卸载插件'
}
}

View File

@ -3,7 +3,8 @@ import Cookies from 'js-cookie'
import '@/styles/index.scss' // global css
import ElementUI from 'element-ui'
import Fit2CloudUI from 'fit2cloud-ui'
import axios from 'axios'
import VueAxios from 'vue-axios'
import i18n from './lang' // internationalization
import App from './App'
import store from './store'
@ -59,6 +60,7 @@ Vue.use(ElementUI, {
Vue.use(Fit2CloudUI, {
i18n: (key, value) => i18n.t(key, value)
})
Vue.use(VueAxios, axios)
Vue.use(filter)
Vue.use(directives)
Vue.use(message)

View File

@ -85,7 +85,7 @@ export const constantRoutes = [
{
path: '/',
component: Layout,
redirect: '/panel',
redirect: '/panel/index',
hidden: true
}

View File

@ -28,24 +28,34 @@
<el-col class="panel-height" style="width: 235px;border-top:solid 1px #dcdfe6;padding: 0 15px;overflow-y: auto;">
<dataset-custom-field :table="table" :checked-list="checkedList" @getChecked="getChecked" />
</el-col>
<el-col class="panel-height" style="flex: 1;overflow: scroll;">
<ux-grid
ref="plxTable"
size="mini"
style="width: 100%;"
:height="height"
:checkbox-config="{highlight: true}"
:width-resize="true"
>
<ux-table-column
v-for="field in fields"
:key="field.fieldName"
min-width="200px"
:field="field.fieldName"
:title="field.remarks"
:resizable="true"
/>
</ux-grid>
<el-col class="panel-height" style="flex: 1;overflow: hidden;">
<el-card class="box-card dataPreview" shadow="never">
<div slot="header" class="clearfix">
<span>{{ $t('dataset.data_preview') }}</span>
</div>
<ux-grid
ref="plxTable"
size="mini"
style="width: 100%;"
:height="height"
:checkbox-config="{highlight: true}"
:width-resize="true"
>
<ux-table-column
v-for="field in fields"
:key="field.fieldName"
min-width="200px"
:field="field.fieldName"
:title="field.remarks"
:resizable="true"
/>
</ux-grid>
<span class="table-count">
{{ $t('dataset.preview_show') }}
<span class="span-number">1000</span>
{{ $t('dataset.preview_item') }}
</span>
</el-card>
</el-col>
</el-col>
</el-col>
@ -101,7 +111,7 @@ export default {
const that = this
setTimeout(function() {
const currentHeight = document.documentElement.clientHeight
that.height = currentHeight - 56 - 15 - 26 - 25 - 43 - 15
that.height = currentHeight - 56 - 15 - 26 - 25 - 43 - 16 - 37 - 20 - 10
}, 10)
},
@ -207,6 +217,14 @@ export default {
margin-left: 0;
}
.dataPreview>>>.el-card__header{
padding: 6px 8px;
}
.dataPreview>>>.el-card__body{
padding:10px;
}
span{
font-size: 14px;
}
@ -214,4 +232,11 @@ export default {
.panel-height{
height: calc(100vh - 56px - 15px - 26px - 25px - 43px);
}
.span-number{
color: #f18126;
}
.table-count{
color: #606266;
}
</style>

View File

@ -11,9 +11,9 @@
>
<ux-table-column
v-for="field in fields"
:key="field.originName"
:key="field.dataeaseName"
min-width="200px"
:field="field.originName"
:field="field.dataeaseName"
:title="field.name"
:resizable="true"
/>

View File

@ -12,19 +12,37 @@
</el-row>
<el-divider />
<el-table :data="tableFields" size="mini" :max-height="maxHeight">
<el-table-column property="type" :label="$t('dataset.field_type')" width="100">
<el-table-column property="type" :label="$t('dataset.field_type')" width="180">
<template slot-scope="scope">
<span v-if="scope.row.deType === 0">
<svg-icon v-if="scope.row.deType === 0" icon-class="field_text" class="field-icon-text" />
<span class="field-class">{{ $t('dataset.text') }}</span>
</span>
<span v-if="scope.row.deType === 1">
<svg-icon v-if="scope.row.deType === 1" icon-class="field_time" class="field-icon-time" />
<span class="field-class">{{ $t('dataset.time') }}</span>
</span>
<span v-if="scope.row.deType === 2 || scope.row.deType === 3">
<svg-icon v-if="scope.row.deType === 2 || scope.row.deType === 3" icon-class="field_value" class="field-icon-value" />
<span class="field-class">{{ $t('dataset.value') }}</span>
<el-select v-model="scope.row.deType" size="mini" style="display: inline-block;width: 26px;">
<el-option
v-for="item in fields"
:key="item.value"
:label="item.label"
:value="item.value"
>
<span style="float: left">
<svg-icon v-if="item.value === 0" icon-class="field_text" class="field-icon-text" />
<svg-icon v-if="item.value === 1" icon-class="field_time" class="field-icon-time" />
<svg-icon v-if="item.value === 2 || item.value === 3" icon-class="field_value" class="field-icon-value" />
</span>
<span style="float: left; color: #8492a6; font-size: 12px">{{ item.label }}</span>
</el-option>
</el-select>
<span style="margin-left: 8px;">
<span v-if="scope.row.deType === 0">
<svg-icon v-if="scope.row.deType === 0" icon-class="field_text" class="field-icon-text" />
<span class="field-class">{{ $t('dataset.text') }}</span>
</span>
<span v-if="scope.row.deType === 1">
<svg-icon v-if="scope.row.deType === 1" icon-class="field_time" class="field-icon-time" />
<span class="field-class">{{ $t('dataset.time') }}</span>
</span>
<span v-if="scope.row.deType === 2 || scope.row.deType === 3">
<svg-icon v-if="scope.row.deType === 2 || scope.row.deType === 3" icon-class="field_value" class="field-icon-value" />
<span v-if="scope.row.deType === 2" class="field-class">{{ $t('dataset.value') }}</span>
<span v-if="scope.row.deType === 3" class="field-class">{{ $t('dataset.value') + '(' + $t('dataset.float') + ')' }}</span>
</span>
</span>
</template>
</el-table-column>
@ -58,7 +76,13 @@ export default {
data() {
return {
maxHeight: 'auto',
tableFields: []
tableFields: [],
fields: [
{ label: this.$t('dataset.text'), value: 0 },
{ label: this.$t('dataset.time'), value: 1 },
{ label: this.$t('dataset.value'), value: 2 },
{ label: this.$t('dataset.value') + '(' + this.$t('dataset.float') + ')', value: 3 }
]
}
},
watch: {
@ -108,4 +132,10 @@ export default {
.field-class{
font-size: 12px !important;
}
.el-select>>>input{
padding-right: 10px;
}
.el-select>>>.el-input__suffix{
right: 0;
}
</style>

View File

@ -31,7 +31,7 @@
<el-tab-pane :label="$t('dataset.data_preview')" name="dataPreview">
<tab-data-preview :table="table" :fields="fields" :data="data" :page="page" :form="tableViewRowForm" @reSearch="reSearch" />
</el-tab-pane>
<el-tab-pane v-if="table.type !== 'custom'" :label="$t('dataset.join_view')" name="joinView">
<el-tab-pane v-if="table.type !== 'custom' && table.mode === 1" :label="$t('dataset.join_view')" name="joinView">
<union-view :table="table" />
</el-tab-pane>
<el-tab-pane v-if="table.mode === 1 && (table.type === 'db' || table.type === 'sql')" :label="$t('dataset.update_info')" name="updateInfo">

View File

@ -0,0 +1,162 @@
<template>
<layout-content v-loading="$store.getters.loadingMap[$store.getters.currentPath]">
<complex-table
:data="data"
:columns="columns"
:search-config="searchConfig"
:pagination-config="paginationConfig"
@search="search"
>
<template #toolbar>
<!-- <el-button @click="create">{{ $t('plugin.local_install') }}</el-button> -->
<el-upload
:action="baseUrl+'api/plugin/upload'"
:multiple="false"
:show-file-list="false"
:file-list="fileList"
accept=".zip"
:before-upload="beforeUpload"
:on-success="uploadSuccess"
:on-error="uploadFail"
name="file"
:headers="headers"
>
<el-button size="mini" type="primary" :disabled="uploading">
<span v-if="!uploading" style="font-size: 12px;">{{ $t('plugin.local_install') }}</span>
<span v-if="uploading" style="font-size: 12px;"><i class="el-icon-loading" /> {{ $t('dataset.uploading') }}</span>
</el-button>
</el-upload>
</template>
<el-table-column prop="name" :label="$t('plugin.name')" />
<el-table-column prop="free" :label="$t('plugin.free')" />
<el-table-column prop="cost" :label="$t('plugin.cost')" />
<el-table-column :show-overflow-tooltip="true" prop="descript" :label="$t('plugin.descript')" />
<el-table-column prop="version" :label="$t('plugin.version')" />
<el-table-column prop="creator" :label="$t('plugin.creator')" />
<el-table-column prop="installTime" :label="$t('plugin.install_time')">
<template v-slot:default="scope">
<span>{{ scope.row.installTime | timestampFormatDate }}</span>
</template>
</el-table-column>
<fu-table-operations :buttons="buttons" label="操作" fix />
</complex-table>
</layout-content>
</template>
<script>
import LayoutContent from '@/components/business/LayoutContent'
import ComplexTable from '@/components/business/complex-table'
import { checkPermission } from '@/utils/permission'
import { formatCondition } from '@/utils/index'
import { pluginLists } from '@/api/system/plugin'
import { getToken } from '@/utils/auth'
export default {
components: { ComplexTable, LayoutContent },
data() {
return {
header: '',
columns: [],
buttons: [
{
label: this.$t('commons.delete'), icon: 'el-icon-delete', type: 'danger', click: this.del,
show: checkPermission(['user:del'])
}
],
searchConfig: {
useQuickSearch: false,
quickPlaceholder: '按名称搜索',
components: [
{ field: 'name', label: '名称', component: 'FuComplexInput' }
// {
// field: 'u.enabled',
// label: '',
// component: 'FuComplexSelect',
// options: [
// { label: '', value: '1' },
// { label: '', value: '0' }
// ],
// multiple: false
// }
]
},
paginationConfig: {
currentPage: 1,
pageSize: 10,
total: 0
},
data: [],
uploading: false,
baseUrl: process.env.VUE_APP_BASE_API,
fileList: [],
headers: { Authorization: getToken() }
}
},
mounted() {
this.search()
},
methods: {
search(condition) {
const temp = formatCondition(condition)
const param = temp || {}
const { currentPage, pageSize } = this.paginationConfig
pluginLists(currentPage, pageSize, param).then(response => {
this.data = response.data.listObject
this.paginationConfig.total = response.data.itemCount
})
},
beforeUpload(file) {
this.uploading = true
},
uploadFail(response, file, fileList) {
this.uploading = false
},
uploadSuccess(response, file, fileList) {
console.log(response)
console.log(file)
console.log(fileList)
// this.path = response.data.path
// this.fields = response.data.fields
// this.data = response.data.data
// const datas = this.data
// this.$refs.plxTable.reloadData(datas)
// if (file.name.lastIndexOf('.') > 0) {
// this.name = file.name.substring(0, file.name.lastIndexOf('.'))
// }
// this.fileList = fileList
this.uploading = false
},
del(row) {
this.$confirm(this.$t('user.delete_confirm'), '', {
confirmButtonText: this.$t('commons.confirm'),
cancelButtonText: this.$t('commons.cancel'),
type: 'warning'
}).then(() => {
// delUser(encodeURIComponent(row.userId)).then(res => {
// this.$success(this.$t('commons.delete_success'))
// this.search()
// })
}).catch(() => {
this.$info(this.$t('commons.delete_cancel'))
})
}
}
}
</script>
<style scoped>
</style>

View File

@ -1,34 +1,41 @@
<template>
<el-card>
<el-tabs v-model="activeName" class="system-setting">
<ui-setting v-if="!test" />
<!-- <el-tabs v-model="activeName" class="system-setting">
<el-tab-pane label="显示设置" name="ui">
<ui-setting />
</el-tab-pane>
<el-tab-pane :label="$t('system_parameter_setting.mailbox_service_settings')" name="email">
<email-setting />
</el-tab-pane>
</el-tabs>
</el-tabs> -->
<async-component v-if="test" url="http://localhost:8081/PluginDemo.js" @execute-axios="executeAxios" />
</el-card>
</template>
<script>
import EmailSetting from './EmailSetting'
import UiSetting from './UiSetting'
import AsyncComponent from '@/components/AsyncComponent'
export default {
name: 'SystemParameterSetting',
components: {
UiSetting,
EmailSetting
AsyncComponent
// 'MsDisplay': display.default,
// 'MsAuth': auth.default
},
data() {
return {
activeName: 'ui'
activeName: 'ui',
test: false
}
},
methods: {
// hasLicense
executeAxios(options) {
console.log(options)
}
}
}
</script>