Commit 02897568 authored by liang.tang's avatar liang.tang
Browse files

magic-api

parents
Pipeline #222 failed with stages
in 0 seconds
*.js linguist-language=java
*.css linguist-language=java
*.html linguist-language=java
\ No newline at end of file
### 当前使用版本(必填,否则不予处理)
### 该问题是怎么引起的?
### 重现步骤(如果有就写完整)
### 报错信息
### 该 Pull Request 关联的 Issue
### 描述信息
### 测试用例
### 更新效果的截屏
### 当前使用版本(必填,否则不予处理)
### 该问题是怎么引起的?
### 重现步骤(如果有就写完整)
### 报错信息
### 该 Pull Request 关联的 Issue
### 描述信息
### 测试用例
### 更新效果的截屏
target
*.iml
out/
.idea
.classpath
.project
.settings
bin/
.myeclipse
\ No newline at end of file
MIT License
Copyright (c) 2020 小东
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
\ No newline at end of file
<p align="center">
<img src="https://www.ssssssss.org/images/logo-magic-api.png" width="256">
</p>
<p align="center">
<a target="_blank" href="https://www.oracle.com/technetwork/java/javase/downloads/index.html"><img src="https://img.shields.io/badge/JDK-1.8+-green.svg" /></a>
<a href="https://search.maven.org/search?q=g:org.ssssssss%20AND%20a:magic-api">
<img alt="maven" src="https://img.shields.io/maven-central/v/org.ssssssss/magic-api.svg?style=flat-square">
</a>
<a target="_blank" href="https://www.ssssssss.org"><img src="https://img.shields.io/badge/Docs-latest-blue.svg"/></a>
<a target="_blank" href="https://github.com/ssssssss-team/magic-api/releases"><img src="https://img.shields.io/github/v/release/ssssssss-team/magic-api?logo=github"></a>
<a target="_blank" href="https://gitee.com/ssssssss-team/magic-api"><img src="https://gitee.com/ssssssss-team/magic-api/badge/star.svg?theme=white" /></a>
<a target="_blank" href="https://github.com/ssssssss-team/magic-api"><img src="https://img.shields.io/github/stars/ssssssss-team/magic-api.svg?style=social"/></a>
<a target="_blank" href="LICENSE"><img src="https://img.shields.io/:license-MIT-blue.svg"></a>
</p>
[特性](#特性) | [快速开始](#快速开始) | [文档/演示](#文档演示) | [示例项目](#示例项目) | <a target="_blank" href="http://ssssssss.org/changelog.html">更新日志</a> | [项目截图](#项目截图) | [交流群](#交流群)
# 简介
magic-api 是一个基于Java的接口快速开发框架,编写接口将通过magic-api提供的UI界面完成,自动映射为HTTP接口,无需定义Controller、Service、Dao、Mapper、XML、VO等Java对象即可完成常见的HTTP API接口开发
【已有上千家中小型公司使用,上万名开发者用于接口配置开发。上百名开发者参与提交了功能建议,接近20多名贡献者参与。已被gitee长期推荐。从首个版本开始不断优化升级,目前版本稳定,开发者交流群活跃。参与交流QQ群④700818216】
# 特性
- 支持MySQL、MariaDB、Oracle、DB2、PostgreSQL、SQLServer 等支持jdbc规范的数据库
- 支持非关系型数据库Redis、Mongodb
- 支持集群部署、接口自动同步。
- 支持分页查询以及自定义分页查询
- 支持多数据源配置,支持在线配置数据源
- 支持SQL缓存,以及自定义SQL缓存
- 支持自定义JSON结果、自定义分页结果
- 支持对接口权限配置、拦截器等功能
- 支持运行时动态修改数据源
- 支持Swagger接口文档生成
- 基于[magic-script](https://gitee.com/ssssssss-team/magic-script)脚本引擎,动态编译,无需重启,实时发布
- 支持Linq式查询,关联、转换更简单
- 支持数据库事务、SQL支持拼接,占位符,判断等语法
- 支持文件上传、下载、输出图片
- 支持脚本历史版本对比与恢复
- 支持脚本代码自动提示、参数提示、悬浮提示、错误提示
- 支持导入Spring中的Bean、Java中的类
- 支持在线调试
- 支持自定义工具类、自定义模块包、自定义类型扩展、自定义方言、自定义列名转换等自定义操作
# 快速开始
## maven引入
```xml
<!-- 以spring-boot-starter的方式引用 -->
<dependency>
<groupId>org.ssssssss</groupId>
<artifactId>magic-api-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
```
## 修改application.properties
```properties
server.port=9999
#配置web页面入口
magic-api.web=/magic/web
#配置文件存储位置。当以classpath开头时,为只读模式
magic-api.resource.location=/data/magic-api
```
## 在线编辑
访问`http://localhost:9999/magic/web`进行操作
# 文档/演示
- 文档地址:[https://ssssssss.org](https://ssssssss.org)
- 在线演示:[https://magic-api.ssssssss.org](https://magic-api.ssssssss.org)
# 示例项目
- [magic-api-example](https://gitee.com/ssssssss-team/magic-api-example)
# 项目截图
| ![整体截图](https://images.gitee.com/uploads/images/2021/0711/105714_c1cacf2c_297689.png "整体截图") | ![代码提示](https://images.gitee.com/uploads/images/2021/0711/110448_11b6626b_297689.gif "代码提示") |
|---|---|
| ![DEBUG](https://images.gitee.com/uploads/images/2021/0711/110515_755f178a_297689.gif "DEBUG") | ![参数提示](https://images.gitee.com/uploads/images/2021/0711/110322_9dd6d149_297689.gif "参数提示") |
| ![远程推送](https://images.gitee.com/uploads/images/2021/0711/105803_b53e0d7e_297689.png "远程推送") | ![历史记录](https://images.gitee.com/uploads/images/2021/0711/105910_f2440ea4_297689.png "历史记录") |
| ![数据源](https://images.gitee.com/uploads/images/2021/0711/105846_7ec51a50_297689.png "数据源") | ![全局搜索](https://images.gitee.com/uploads/images/2021/0711/105823_ac18ada7_297689.png "全局搜索") |
# 交流群
| 微信群 | QQ群 |
| ----- | --- |
| <img src="https://www.ssssssss.org/magic-api/images/wxcode.png" alt="作者微信"> | <img src="https://www.ssssssss.org/magic-api/images/qq-group-qrcode.png" alt="QQ群"> |
| 备注:加群,邀您加入群聊| <a href="https://qm.qq.com/cgi-bin/qm/qr?k=38qddUeqrk_x29Xril9a_jxnoCGTmPRF&jump_from=webapi" target="_blank">点击加入QQ群:700818216</a> |
> 0.7.x版本之后仅需要一张表两个字段,建表语句如下:
```sql
CREATE TABLE `magic_api_file_v2` (
`file_path` varchar(512) NOT NULL,
`file_content` mediumtext,
PRIMARY KEY (`file_path`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
```
### 备份表建表语句
```sql
CREATE TABLE `magic_backup_record_v2` (
`id` varchar(32) NOT NULL COMMENT '原对象ID',
`create_date` bigint(13) NOT NULL COMMENT '备份时间',
`tag` varchar(32) DEFAULT NULL COMMENT '标签',
`type` varchar(32) DEFAULT NULL COMMENT '类型',
`name` varchar(64) DEFAULT NULL COMMENT '原名称',
`content` blob COMMENT '备份内容',
`create_by` varchar(64) DEFAULT NULL COMMENT '操作人',
PRIMARY KEY (`id`,`create_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
```
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.ssssssss</groupId>
<artifactId>magic-api-plugins</artifactId>
<version>2.1.1</version>
</parent>
<artifactId>magic-api-plugin-cluster</artifactId>
<packaging>jar</packaging>
<name>magic-api-plugin-cluster</name>
<description>magic-api-plugin-cluster</description>
<dependencies>
<dependency>
<groupId>org.ssssssss</groupId>
<artifactId>magic-api-plugin-redis</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>
package org.ssssssss.magicapi.cluster;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.UUID;
/**
* 集群配置
*
* @author mxd
* @since 1.2.0
*/
@ConfigurationProperties(prefix = "magic-api.cluster")
public class ClusterConfig {
/**
* redis 通道
*/
private String channel = "magic-api:notify:channel";
public String getChannel() {
return channel;
}
public void setChannel(String channel) {
this.channel = channel;
}
}
package org.ssssssss.magicapi.cluster;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.listener.ChannelTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.ssssssss.magicapi.core.config.MagicAPIProperties;
import org.ssssssss.magicapi.core.config.MagicPluginConfiguration;
import org.ssssssss.magicapi.core.model.MagicNotify;
import org.ssssssss.magicapi.core.model.Plugin;
import org.ssssssss.magicapi.core.service.MagicAPIService;
import org.ssssssss.magicapi.core.service.MagicNotifyService;
import org.ssssssss.magicapi.redis.RedisModule;
import org.ssssssss.magicapi.utils.JsonUtils;
import java.util.Arrays;
@EnableConfigurationProperties(ClusterConfig.class)
@Configuration
public class MagicClusterConfiguration implements MagicPluginConfiguration {
private final ClusterConfig config;
private final MagicAPIProperties properties;
private final Logger logger = LoggerFactory.getLogger(MagicClusterConfiguration.class);
public MagicClusterConfiguration(MagicAPIProperties properties, ClusterConfig config) {
this.properties = properties;
this.config = config;
}
@Override
public Plugin plugin() {
return new Plugin("Cluster");
}
/**
* 使用Redis推送通知
*/
@Bean
@ConditionalOnMissingBean
public MagicNotifyService magicNotifyService(RedisModule redisModule) {
return magicNotify -> redisModule.execute("publish", Arrays.asList(config.getChannel(), JsonUtils.toJsonString(magicNotify)));
}
/**
* 消息处理服务
*/
@Bean
@ConditionalOnMissingBean
public MagicSynchronizationService magicSynchronizationService(MagicNotifyService magicNotifyService) {
return new MagicSynchronizationService(magicNotifyService, properties.getInstanceId());
}
/**
* 集群通知监听
*/
@Bean
public RedisMessageListenerContainer magicRedisMessageListenerContainer(RedisConnectionFactory redisConnectionFactory, MagicAPIService magicAPIService) {
logger.info("开启集群通知监听, Redis channel: {}", config.getChannel());
RedisMessageListenerContainer redisMessageListenerContainer = new RedisMessageListenerContainer();
redisMessageListenerContainer.setConnectionFactory(redisConnectionFactory);
redisMessageListenerContainer.addMessageListener((message, pattern) -> magicAPIService.processNotify(JsonUtils.readValue(message.getBody(), MagicNotify.class)), ChannelTopic.of(config.getChannel()));
return redisMessageListenerContainer;
}
}
package org.ssssssss.magicapi.cluster;
import org.springframework.context.event.EventListener;
import org.ssssssss.magicapi.core.event.*;
import org.ssssssss.magicapi.core.config.Constants;
import org.ssssssss.magicapi.core.model.MagicNotify;
import org.ssssssss.magicapi.core.service.MagicNotifyService;
public class MagicSynchronizationService {
private final MagicNotifyService magicNotifyService;
/**
* 当前实例ID
*/
private final String instanceId;
public MagicSynchronizationService(MagicNotifyService magicNotifyService, String instanceId) {
this.magicNotifyService = magicNotifyService;
this.instanceId = instanceId;
}
@EventListener(condition = "#event.source != T(org.ssssssss.magicapi.core.config.Constants).EVENT_SOURCE_NOTIFY")
public void onFolderEvent(GroupEvent event) {
switch (event.getAction()) {
case CREATE:
case SAVE:
case MOVE:
case DELETE:
magicNotifyService.sendNotify(new MagicNotify(instanceId, event.getGroup().getId(), event.getAction(), event.getType()));
break;
}
}
@EventListener(condition = "#event.source != T(org.ssssssss.magicapi.core.config.Constants).EVENT_SOURCE_NOTIFY")
public void onFileEvent(FileEvent event) {
if (Constants.EVENT_SOURCE_NOTIFY.equals(event.getSource())) {
return;
}
switch (event.getAction()) {
case CREATE:
case SAVE:
case MOVE:
case DELETE:
magicNotifyService.sendNotify(new MagicNotify(instanceId, event.getEntity().getId(), event.getAction(), Constants.EVENT_TYPE_FILE));
break;
}
}
@EventListener(condition = "#event.action == T(org.ssssssss.magicapi.core.event.EventAction).CLEAR && #event.source != T(org.ssssssss.magicapi.core.config.Constants).EVENT_SOURCE_NOTIFY")
public void onClearEvent(MagicEvent event){
magicNotifyService.sendNotify(new MagicNotify(instanceId, null, event.getAction(), null));
}
}
org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.ssssssss.magicapi.cluster.MagicClusterConfiguration
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.ssssssss</groupId>
<artifactId>magic-api-plugins</artifactId>
<version>2.1.1</version>
</parent>
<artifactId>magic-api-plugin-component</artifactId>
<packaging>jar</packaging>
<name>magic-api-plugin-component</name>
<description>magic-api-plugin-component</description>
<build>
<plugins>
<!-- npm install && npm run build -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>
<executions>
<execution>
<id>exec-npm-install</id>
<phase>generate-resources</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<executable>npm</executable>
<arguments>
<argument>install</argument>
</arguments>
<workingDirectory>${basedir}/src/console</workingDirectory>
</configuration>
</execution>
<execution>
<id>exec-npm-run-build</id>
<phase>generate-resources</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<executable>npm</executable>
<arguments>
<argument>run</argument>
<argument>build</argument>
</arguments>
<workingDirectory>${basedir}/src/console</workingDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.2.0</version>
<executions>
<execution>
<id>copy-resource</id>
<phase>generate-resources</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${basedir}/target/classes/magic-editor/plugins</outputDirectory>
<resources>
<resource>
<directory>${basedir}/src/console/dist</directory>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
{
"name": "magic-component",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "vite build"
},
"author": "",
"license": "ISC",
"devDependencies": {
"vue": "^3.2.31",
"@vitejs/plugin-vue": "^2.2.4",
"vite-plugin-svg-icons": "^1.1.0",
"vite": "^2.8.6"
}
}
<template>
<div class="magic-component-info">
<form>
<label>{{ $i('component.form.name') }}</label>
<magic-input v-model:value="info.name" :placeholder="$i('component.form.placeholder.name')" width="250px"/>
<label>{{ $i('component.form.path') }}</label>
<magic-input v-model:value="info.path" :placeholder="$i('component.form.placeholder.path')" width="auto" style="flex:1"/>
</form>
<div style="flex:1;padding-top:5px;">
<magic-textarea v-model:value="info.description" :placeholder="$i('component.form.placeholder.description')"/>
</div>
</div>
</template>
<script setup>
import { inject } from 'vue'
const $i = inject('i18n.format')
const info = inject('info')
</script>
<style scoped>
.magic-component-info{
display: flex;
flex-direction: column;
flex: 1;
padding: 5px;
}
.magic-component-info form{
display: flex;
}
.magic-component-info form label{
display: inline-block;
width: 75px;
height: var(--magic-input-height);
line-height: var(--magic-input-height);
font-weight: 400;
text-align: right;
padding: 0 5px;
}
.magic-component-info form :deep(.magic-textarea){
margin: 5px;
}
</style>
export default {
component: {
title: 'Component Info',
name: 'Component',
form: {
name: 'Component Name',
path: 'Component Path',
description: 'Component Description',
placeholder: {
name: 'Please Enter Component Name',
path: 'Please Enter Component Path',
description: 'Please Enter Component Description'
}
}
},
}
export default {
component: {
title: '组件信息',
name: '组件',
form: {
name: '组件名称',
path: '组件路径',
description: '组件描述',
placeholder: {
name: '请输入组件名称',
path: '请输入组件路径',
description: '请输入组件描述'
}
}
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment