924a8158 by lv

框架变更

1 parent 8174f2d0
Showing 1000 changed files with 4538 additions and 0 deletions

Too many changes to show.

To preserve performance only 1000 of 1000+ files are displayed.

# http://editorconfig.org
root = true
# 空格替代Tab缩进在各种编辑工具下效果一致
[*]
indent_style = space
indent_size = 4
charset = utf-8
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true
[*.java]
indent_style = tab
[*.{json,yml}]
indent_size = 2
[*.md]
insert_final_newline = false
trim_trailing_whitespace = false
# maven #
target
logs
# windows #
Thumbs.db
# Mac #
.DS_Store
# eclipse #
.settings
.project
.classpath
.log
*.class
# idea #
.idea
*.iml
# Package Files #
*.jar
*.war
*.ear
/target
#Californium CoAP Properties file
#Tue Oct 08 10:11:32 CST 2019
HTTP_SERVER_SOCKET_BUFFER_SIZE=8192
UDP_CONNECTOR_OUT_CAPACITY=2147483647
CONGESTION_CONTROL_ALGORITHM=Cocoa
USE_CONGESTION_CONTROL=false
ACK_TIMEOUT=2000
MAX_ACTIVE_PEERS=150000
TCP_CONNECT_TIMEOUT=10000
NOTIFICATION_CHECK_INTERVAL_COUNT=100
MAX_MESSAGE_SIZE=1024
MID_TRACKER_GROUPS=16
DEDUPLICATOR=DEDUPLICATOR_MARK_AND_SWEEP
TCP_CONNECTION_IDLE_TIMEOUT=10
COAP_PORT=5683
ACK_TIMEOUT_SCALE=2.0
PREFERRED_BLOCK_SIZE=512
NETWORK_STAGE_RECEIVER_THREAD_COUNT=1
DTLS_AUTO_RESUME_TIMEOUT=30000
DTLS_RESPONSE_MATCHING=STRICT
PROTOCOL_STAGE_THREAD_COUNT=4
MAX_TRANSMIT_WAIT=93000
UDP_CONNECTOR_RECEIVE_BUFFER=0
EXCHANGE_LIFETIME=247000
HTTP_SERVER_SOCKET_TIMEOUT=100000
CROP_ROTATION_PERIOD=2000
UDP_CONNECTOR_DATAGRAM_SIZE=2048
MAX_RETRANSMIT=4
MAX_PEER_INACTIVITY_PERIOD=600
MAX_RESOURCE_BODY_SIZE=8192
NOTIFICATION_CHECK_INTERVAL=86400000
LEISURE=5000
HTTP_CACHE_RESPONSE_MAX_AGE=86400
BLOCKWISE_STATUS_LIFETIME=300000
UDP_CONNECTOR_SEND_BUFFER=0
MID_TACKER=GROUPED
TCP_WORKER_THREADS=1
NETWORK_STAGE_SENDER_THREAD_COUNT=1
NON_LIFETIME=145000
TOKEN_SIZE_LIMIT=8
HTTP_PORT=8080
MARK_AND_SWEEP_INTERVAL=10000
HEALTH_STATUS_INTERVAL=60
ACK_RANDOM_FACTOR=1.5
SECURE_SESSION_TIMEOUT=86400
NSTART=1
USE_RANDOM_MID_START=true
HTTP_CACHE_SIZE=32
PROBING_RATE=1.0
NOTIFICATION_REREGISTRATION_BACKOFF=2000
COAP_SECURE_PORT=5684
FROM anapsix/alpine-java:8_server-jre_unlimited
MAINTAINER smallchill@163.com
RUN mkdir -p /blade/auth
WORKDIR /blade/auth
EXPOSE 8010
ADD ./target/blade-auth.jar ./app.jar
ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "app.jar"]
CMD ["--spring.profiles.active=test"]
## 目前主要支持的oauth协议
一、 授权码模式
授权码模式(authorization_code)主要针对第三方应用,是最为复杂也最为安全的一种模式,操作步骤如下
1. 访问地址:http://localhost:8100/oauth/authorize?client_id=blade&redirect_uri=http://example.com&code=233333&response_type=code
2. 获取跳转后的code值(http://example.com/?code=VhYNLR)之后,调用 http://localhost/blade-auth/oauth/token 传入对应的参数
请求头:
Authorization : Basic YmxhZGU6YmxhZGU= ("YmxhZGU6YmxhZGU="为clientId:clientSecret串转换为的base64编码)
表单:
grant_type:authorization_code
scope:all
code:VhYNLR
redirect_uri: http://example.com
二、 密码模式
密码模式(password)主要针对自家应用,可信度较高,所以可以使用简便安全共存的模式,操作步骤如下
1. 直接调用 http://localhost/blade-auth/oauth/token 传入对应的参数
请求头:
Authorization : Basic YmxhZGU6YmxhZGU= ("YmxhZGU6YmxhZGU="为clientId:clientSecret串转换为的base64编码)
表单:
grant_type:password
scope:all
username:admin
password:123456
## 获取到token后如何获取用户信息
1. 拼接请求头
Authorization :bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0ZXN0IjoidGVzdCIsInVzZXJfbmFtZSI6ImFkbWluIiwic2NvcGUiOlsiYWxsIl0sImV4cCI6MTU1MzE2MTA5NSwiYXV0aG9yaXRpZXMiOlsiUk9MRV9VU0VSIl0sImp0aSI6IjE0YmMyYjAyLTgxY2UtNDFiNC04ZTI3LTA5YWE0ZmU4ZWMwYyIsImNsaWVudF9pZCI6ImJsYWRlIn0.jTmioQDq-fSNNn7YCwl3wP0JE-etSWtzLDe545mDbP4
2. 调用 http://localhost/blade-auth/oauth/user-info 既可获得对应用户信息
\ No newline at end of file
<?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>
<artifactId>BladeX</artifactId>
<groupId>org.springblade</groupId>
<version>2.0.0.RELEASE</version>
</parent>
<artifactId>blade-auth</artifactId>
<name>${project.artifactId}</name>
<version>${bladex.project.version}</version>
<packaging>jar</packaging>
<dependencies>
<!--Blade-->
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-common</artifactId>
<version>${bladex.project.version}</version>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-cloud</artifactId>
<version>${bladex.tool.version}</version>
<exclusions>
<exclusion>
<groupId>org.springblade</groupId>
<artifactId>blade-core-secure</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-user-api</artifactId>
<version>${bladex.project.version}</version>
<exclusions>
<exclusion>
<groupId>org.springblade</groupId>
<artifactId>blade-core-secure</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-system-api</artifactId>
<version>${bladex.project.version}</version>
<exclusions>
<exclusion>
<groupId>org.springblade</groupId>
<artifactId>blade-core-secure</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-starter-swagger</artifactId>
<version>${bladex.tool.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<exclusions>
<exclusion>
<artifactId>tomcat-jdbc</artifactId>
<groupId>org.apache.tomcat</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<!-- MySQL -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.15</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>${docker.plugin.version}</version>
<configuration>
<imageName>${docker.registry.url}/blade/${project.artifactId}:${project.version}</imageName>
<dockerDirectory>${project.basedir}</dockerDirectory>
<dockerHost>${docker.registry.host}</dockerHost>
<resources>
<resource>
<targetPath>/</targetPath>
<directory>${project.build.directory}</directory>
<include>${project.build.finalName}.jar</include>
</resource>
</resources>
<registryUrl>${docker.registry.url}</registryUrl>
<serverId>${docker.registry.url}</serverId>
<pushImage>true</pushImage>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<tasks>
<copy overwrite="true"
tofile="${session.executionRootDirectory}/target/${project.artifactId}.jar"
file="${project.build.directory}/${project.artifactId}.jar" />
</tasks>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.springblade.auth;
import org.springblade.core.cloud.feign.EnableBladeFeign;
import org.springblade.core.launch.BladeApplication;
import org.springblade.core.launch.constant.AppConstant;
import org.springframework.cloud.client.SpringCloudApplication;
/**
* 用户认证服务器
*
* @author Chill
*/
@EnableBladeFeign
@SpringCloudApplication
public class AuthApplication {
public static void main(String[] args) {
BladeApplication.run(AppConstant.APPLICATION_AUTH_NAME, AuthApplication.class, args);
}
}
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.springblade.auth.config;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.springblade.auth.constant.AuthConstant;
import org.springblade.auth.service.BladeClientDetailsServiceImpl;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.List;
/**
* 认证服务器配置
*
* @author Chill
*/
@Order
@Configuration
@AllArgsConstructor
@EnableAuthorizationServer
public class BladeAuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
private final DataSource dataSource;
private AuthenticationManager authenticationManager;
private UserDetailsService userDetailsService;
private TokenStore tokenStore;
private JwtAccessTokenConverter jwtAccessTokenConverter;
private TokenEnhancer jwtTokenEnhancer;
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints.tokenStore(tokenStore)
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService);
//扩展token返回结果
if (jwtAccessTokenConverter != null && jwtTokenEnhancer != null) {
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
List<TokenEnhancer> enhancerList = new ArrayList<>();
enhancerList.add(jwtTokenEnhancer);
enhancerList.add(jwtAccessTokenConverter);
tokenEnhancerChain.setTokenEnhancers(enhancerList);
//jwt
endpoints.tokenEnhancer(tokenEnhancerChain)
.accessTokenConverter(jwtAccessTokenConverter);
}
}
/**
* 配置客户端信息
*/
@Override
@SneakyThrows
public void configure(ClientDetailsServiceConfigurer clients) {
BladeClientDetailsServiceImpl clientDetailsService = new BladeClientDetailsServiceImpl(dataSource);
clientDetailsService.setSelectClientDetailsSql(AuthConstant.DEFAULT_SELECT_STATEMENT);
clientDetailsService.setFindClientDetailsSql(AuthConstant.DEFAULT_FIND_STATEMENT);
clients.withClientDetails(clientDetailsService);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
oauthServer
.allowFormAuthenticationForClients()
.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()");
}
}
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.springblade.auth.config;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
/**
* 自定义登录成功配置
*
* @author Chill
*/
@Configuration
@AllArgsConstructor
@EnableResourceServer
public class BladeResourceServerConfiguration extends ResourceServerConfigurerAdapter {
/**
* 自定义登录成功处理器
*/
private AuthenticationSuccessHandler appLoginInSuccessHandler;
@Override
@SneakyThrows
public void configure(HttpSecurity http) {
http.headers().frameOptions().disable();
http.formLogin()
.successHandler(appLoginInSuccessHandler)
.and()
.authorizeRequests()
.antMatchers("/actuator/**", "/token/**", "/mobile/**", "/v2/api-docs", "/v2/api-docs-ext").permitAll()
.anyRequest().authenticated().and()
.csrf().disable();
}
}
//package org.springblade.auth.config;
//
//import org.springblade.core.tool.api.R;
//import org.springframework.security.core.AuthenticationException;
//import org.springframework.security.web.AuthenticationEntryPoint;
//import org.springframework.stereotype.Component;
//
//import javax.servlet.http.HttpServletRequest;
//import javax.servlet.http.HttpServletResponse;
//import java.io.IOException;
//import java.io.PrintWriter;
//import java.io.Serializable;
//
///**
// * @author: JoeTao
// * createAt: 2018/9/20
// */
//@Component
//public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable {
//
// private static final long serialVersionUID = -8970718410437077606L;
//
// @Override
// public void commence(HttpServletRequest request,
// HttpServletResponse response,
// AuthenticationException authException) throws IOException {
// //验证为未登陆状态会进入此方法,认证错误
// System.out.println("认证失败:" + authException.getMessage());
// response.setStatus(200);
// response.setCharacterEncoding("UTF-8");
// response.setContentType("application/json; charset=utf-8");
// PrintWriter printWriter = response.getWriter();
// String body = R.fail(401, "认证失败").toString();
// printWriter.write(body);
// printWriter.flush();
// }
//}
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.springblade.auth.config;
import org.springblade.auth.support.BladeJwtTokenEnhancer;
import org.springblade.core.launch.constant.TokenConstant;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
/**
* JwtTokenStore
*
* @author Chill
*/
@Configuration
@ConditionalOnProperty(prefix = "blade.security.oauth2", name = "storeType", havingValue = "jwt", matchIfMissing = true)
public class JwtTokenStoreConfiguration {
/**
* 使用jwtTokenStore存储token
*/
@Bean
public TokenStore jwtTokenStore() {
return new JwtTokenStore(jwtAccessTokenConverter());
}
/**
* 用于生成jwt
*/
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
accessTokenConverter.setSigningKey(TokenConstant.SIGN_KEY);
return accessTokenConverter;
}
/**
* 用于扩展jwt
*/
@Bean
@ConditionalOnMissingBean(name = "jwtTokenEnhancer")
public TokenEnhancer jwtTokenEnhancer() {
return new BladeJwtTokenEnhancer();
}
}
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.springblade.auth.config;
import lombok.AllArgsConstructor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
/**
* Redis TokenStore
*
* @author Chill
*/
@Configuration
@AllArgsConstructor
public class RedisTokenStoreConfiguration {
/**
* redis连接工厂
*/
private RedisConnectionFactory redisConnectionFactory;
/**
* 用于存放token
*/
@Bean
@ConditionalOnProperty(prefix = "blade.security.oauth2", name = "storeType", havingValue = "redis")
public TokenStore redisTokenStore() {
return new RedisTokenStore(redisConnectionFactory);
}
}
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.springblade.auth.config;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.springblade.auth.support.BladePasswordEncoderFactories;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* Security配置
*
* @author Chill
*/
@Configuration
@AllArgsConstructor
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Bean
@Override
@SneakyThrows
public AuthenticationManager authenticationManagerBean() {
return super.authenticationManagerBean();
}
@Bean
public PasswordEncoder passwordEncoder() {
return BladePasswordEncoderFactories.createDelegatingPasswordEncoder();
}
@Override
@SneakyThrows
protected void configure(HttpSecurity http) {
http.httpBasic().and().csrf().disable();
}
}
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.springblade.auth.constant;
/**
* 授权校验常量
*
* @author Chill
*/
public interface AuthConstant {
/**
* 密码加密规则
*/
String ENCRYPT = "{blade}";
/**
* blade_client表字段
*/
String CLIENT_FIELDS = "client_id, CONCAT('{noop}',client_secret) as client_secret, resource_ids, scope, authorized_grant_types, " +
"web_server_redirect_uri, authorities, access_token_validity, " +
"refresh_token_validity, additional_information, autoapprove";
/**
* blade_client查询语句
*/
String BASE_STATEMENT = "select " + CLIENT_FIELDS + " from blade_client";
/**
* blade_client查询排序
*/
String DEFAULT_FIND_STATEMENT = BASE_STATEMENT + " order by client_id";
/**
* 查询client_id
*/
String DEFAULT_SELECT_STATEMENT = BASE_STATEMENT + " where client_id = ?";
}
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.springblade.auth.endpoint;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.tool.api.R;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.provider.endpoint.FrameworkEndpoint;
import org.springframework.security.oauth2.provider.token.ConsumerTokenServices;
import org.springframework.web.bind.annotation.*;
/**
* BladeEndPoint
*
* @author Chill
*/
@Slf4j
@RestController
@AllArgsConstructor
@FrameworkEndpoint
public class BladeTokenEndPoint {
@GetMapping("/oauth/user-info")
public R<Authentication> currentUser(Authentication authentication) {
return R.data(authentication);
}
@Autowired
ConsumerTokenServices consumerTokenServices;
@RequestMapping(method = RequestMethod.DELETE, value = "/oauth/token/delete")
@ResponseBody
public String revokeToken(String access_token) {
if (consumerTokenServices.revokeToken(access_token)){
return "注销成功";
}else{
return "注销失败";
}
}
}
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.springblade.auth.handler;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.auth.utils.TokenUtil;
import org.springblade.core.tool.jackson.JsonUtil;
import org.springframework.http.MediaType;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.UnapprovedClientAuthenticationException;
import org.springframework.security.oauth2.provider.*;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
/**
* APP登录成功处理器
*
* @author Chill
*/
@Slf4j
@AllArgsConstructor
@Component("appLoginInSuccessHandler")
public class AppLoginInSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
private PasswordEncoder passwordEncoder;
private ClientDetailsService clientDetailsService;
private AuthorizationServerTokenServices authorizationServerTokenServices;
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException {
log.info("【AppLoginInSuccessHandler】 onAuthenticationSuccess authentication={}", authentication);
String[] tokens = TokenUtil.extractAndDecodeHeader();
assert tokens.length == 2;
String clientId = tokens[0];
String clientSecret = tokens[1];
ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId);
if (clientDetails == null) {
throw new UnapprovedClientAuthenticationException("clientId 对应的配置信息不存在" + clientId);
} else if (!passwordEncoder.matches(clientSecret, clientDetails.getClientSecret())) {
throw new UnapprovedClientAuthenticationException("clientSecret 不匹配" + clientId);
}
TokenRequest tokenRequest = new TokenRequest(new HashMap<>(16), clientId, clientDetails.getScope(), "app");
OAuth2Request oAuth2Request = tokenRequest.createOAuth2Request(clientDetails);
OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(oAuth2Request, authentication);
OAuth2AccessToken token = authorizationServerTokenServices.createAccessToken(oAuth2Authentication);
response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
response.getWriter().write(JsonUtil.toJson(token));
}
}
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.springblade.auth.service;
import lombok.SneakyThrows;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import javax.sql.DataSource;
/**
* 客户端信息
*
* @author Chill
*/
public class BladeClientDetailsServiceImpl extends JdbcClientDetailsService {
public BladeClientDetailsServiceImpl(DataSource dataSource) {
super(dataSource);
}
/**
* 缓存客户端信息
*
* @param clientId 客户端id
*/
@Override
@SneakyThrows
public ClientDetails loadClientByClientId(String clientId) {
return super.loadClientByClientId(clientId);
}
}
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.springblade.auth.service;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.Getter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import java.util.Collection;
/**
* 用户信息拓展
*
* @author Chill
*/
@Getter
public class BladeUserDetails extends User {
/**
* 用户id
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long userId;
/**
* 租户编号
*/
private String tenantCode;
/**
* 昵称
*/
private String name;
/**
* 账号
*/
private String account;
/**
* 角色id
*/
private String roleId;
/**
* 角色名
*/
private String roleName;
/**
* 头像
*/
private String avatar;
private Integer type;
BladeUserDetails(Long userId, String tenantCode, String name, String roleId, String roleName, String avatar, String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities,Integer type) {
super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
this.userId = userId;
this.tenantCode = tenantCode;
this.name = name;
this.account = username;
this.roleId = roleId;
this.roleName = roleName;
this.avatar = avatar;
this.type=type;
}
}
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.springblade.auth.service;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.springblade.auth.constant.AuthConstant;
import org.springblade.auth.utils.TokenUtil;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.WebUtil;
import org.springblade.system.user.entity.User;
import org.springblade.system.user.entity.UserInfo;
import org.springblade.system.user.feign.IUserClient;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
/**
* 用户信息
*
* @author Chill
*/
@Service
@AllArgsConstructor
public class BladeUserDetailsServiceImpl implements UserDetailsService {
private IUserClient userClient;
@Override
@SneakyThrows
public UserDetails loadUserByUsername(String username) {
HttpServletRequest request = WebUtil.getRequest();
String tenantCode = Func.toStr(request.getHeader(TokenUtil.TENANT_HEADER_KEY), TokenUtil.DEFAULT_TENANT_CODE);
R<UserInfo> result = userClient.userInfo(tenantCode, username);
if (result.isSuccess()) {
User user = result.getData().getUser();
if (user == null) {
throw new UsernameNotFoundException(TokenUtil.USER_NOT_FOUND);
}
return new BladeUserDetails(user.getId(),
user.getTenantCode(), user.getName(), user.getRoleId(), Func.join(result.getData().getRoles()), TokenUtil.DEFAULT_AVATAR,
username, AuthConstant.ENCRYPT + user.getPassword(), true, true, true, true,
AuthorityUtils.commaSeparatedStringToAuthorityList(Func.join(result.getData().getRoles())),user.getType());
} else {
throw new UsernameNotFoundException(result.getMsg());
}
}
}
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.springblade.auth.support;
import org.springblade.auth.service.BladeUserDetails;
import org.springblade.auth.utils.TokenUtil;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import java.util.HashMap;
import java.util.Map;
/**
* jwt返回参数增强
*
* @author Chill
*/
public class BladeJwtTokenEnhancer implements TokenEnhancer {
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
BladeUserDetails principal = (BladeUserDetails) authentication.getUserAuthentication().getPrincipal();
Map<String, Object> info = new HashMap<>(16);
info.put(TokenUtil.CLIENT_ID, TokenUtil.getClientIdFromHeader());
info.put(TokenUtil.USER_ID, principal.getUserId());
info.put(TokenUtil.ROLE_ID, principal.getRoleId());
info.put(TokenUtil.TENANT_CODE, principal.getTenantCode());
info.put(TokenUtil.ACCOUNT, principal.getAccount());
info.put(TokenUtil.USER_NAME, principal.getUsername());
info.put(TokenUtil.NICK_NAME, principal.getName());
info.put(TokenUtil.ROLE_NAME, principal.getRoleName());
info.put(TokenUtil.AVATAR, principal.getAvatar());
info.put(TokenUtil.LICENSE, TokenUtil.LICENSE_NAME);
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(info);
return accessToken;
}
}
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.springblade.auth.support;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* 无密码加密
*
* @author Chill
*/
public class BladeNoOpPasswordEncoder implements PasswordEncoder {
@Override
public String encode(CharSequence rawPassword) {
return rawPassword.toString();
}
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return rawPassword.toString().equals(encodedPassword);
}
/**
* Get the singleton {@link BladeNoOpPasswordEncoder}.
*/
public static PasswordEncoder getInstance() {
return INSTANCE;
}
private static final PasswordEncoder INSTANCE = new BladeNoOpPasswordEncoder();
private BladeNoOpPasswordEncoder() {
}
}
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.springblade.auth.support;
import org.springblade.core.tool.utils.DigestUtil;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* 自定义密码加密
*
* @author Chill
*/
public class BladePasswordEncoder implements PasswordEncoder {
@Override
public String encode(CharSequence rawPassword) {
return DigestUtil.encrypt((String) rawPassword);
}
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return encodedPassword.equals(encode(rawPassword));
}
}
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.springblade.auth.support;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.DelegatingPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder;
import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder;
import java.util.HashMap;
import java.util.Map;
/**
* 自定义密码工厂
*
* @author Chill
*/
public class BladePasswordEncoderFactories {
/**
* Creates a {@link DelegatingPasswordEncoder} with default mappings. Additional
* mappings may be added and the encoding will be updated to conform with best
* practices. However, due to the nature of {@link DelegatingPasswordEncoder} the
* updates should not impact users. The mappings current are:
*
* <ul>
* <li>blade - {@link BladePasswordEncoder} (sha1(md5("password")))</li>
* <li>bcrypt - {@link BCryptPasswordEncoder} (Also used for encoding)</li>
* <li>noop - {@link BladeNoOpPasswordEncoder}</li>
* <li>pbkdf2 - {@link Pbkdf2PasswordEncoder}</li>
* <li>scrypt - {@link SCryptPasswordEncoder}</li>
* </ul>
*
* @return the {@link PasswordEncoder} to use
*/
@SuppressWarnings("deprecation")
public static PasswordEncoder createDelegatingPasswordEncoder() {
String encodingId = "blade";
Map<String, PasswordEncoder> encoders = new HashMap<>(16);
encoders.put(encodingId, new BladePasswordEncoder());
encoders.put("bcrypt", new BCryptPasswordEncoder());
encoders.put("noop", BladeNoOpPasswordEncoder.getInstance());
encoders.put("pbkdf2", new Pbkdf2PasswordEncoder());
encoders.put("scrypt", new SCryptPasswordEncoder());
return new DelegatingPasswordEncoder(encodingId, encoders);
}
private BladePasswordEncoderFactories() {
}
}
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.springblade.auth.utils;
import lombok.SneakyThrows;
import org.springblade.core.launch.constant.TokenConstant;
import org.springblade.core.tool.utils.Charsets;
import org.springblade.core.tool.utils.StringPool;
import org.springblade.core.tool.utils.WebUtil;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.oauth2.common.exceptions.UnapprovedClientAuthenticationException;
import java.util.Base64;
import java.util.Calendar;
/**
* 认证工具类
*
* @author Chill
*/
public class TokenUtil {
public final static String AVATAR = TokenConstant.AVATAR;
public final static String ACCOUNT = TokenConstant.ACCOUNT;
public final static String USER_NAME = TokenConstant.USER_NAME;
public final static String NICK_NAME = TokenConstant.NICK_NAME;
public final static String USER_ID = TokenConstant.USER_ID;
public final static String ROLE_ID = TokenConstant.ROLE_ID;
public final static String ROLE_NAME = TokenConstant.ROLE_NAME;
public final static String TENANT_CODE = TokenConstant.TENANT_CODE;
public final static String CLIENT_ID = TokenConstant.CLIENT_ID;
public final static String LICENSE = TokenConstant.LICENSE;
public final static String LICENSE_NAME = TokenConstant.LICENSE_NAME;
public final static String TENANT_HEADER_KEY = "Tenant-Code";
public final static String DEFAULT_TENANT_CODE = "000000";
public final static String USER_NOT_FOUND = "用户名或密码错误";
public final static String HEADER_KEY = "Authorization";
public final static String HEADER_PREFIX = "Basic ";
public final static String DEFAULT_AVATAR = "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png";
/**
* 解码
*/
@SneakyThrows
public static String[] extractAndDecodeHeader() {
String header = WebUtil.getRequest().getHeader(TokenUtil.HEADER_KEY);
if (header == null || !header.startsWith(TokenUtil.HEADER_PREFIX)) {
throw new UnapprovedClientAuthenticationException("请求头中无client信息");
}
byte[] base64Token = header.substring(6).getBytes(Charsets.UTF_8_NAME);
byte[] decoded;
try {
decoded = Base64.getDecoder().decode(base64Token);
} catch (IllegalArgumentException var7) {
throw new BadCredentialsException("Failed to decode basic authentication token");
}
String token = new String(decoded, Charsets.UTF_8_NAME);
int index = token.indexOf(StringPool.COLON);
if (index == -1) {
throw new BadCredentialsException("Invalid basic authentication token");
} else {
return new String[]{token.substring(0, index), token.substring(index + 1)};
}
}
/**
* 获取请求头中的客户端id
*/
public static String getClientIdFromHeader() {
String[] tokens = extractAndDecodeHeader();
assert tokens.length == 2;
return tokens[0];
}
/**
* 获取token过期时间(次日凌晨3点)
*
* @return expire
*/
public static int getTokenValiditySecond() {
Calendar cal = Calendar.getInstance();
cal.add(Calendar.DAY_OF_YEAR, 1);
cal.set(Calendar.HOUR_OF_DAY, 3);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.MILLISECOND, 0);
return (int) (cal.getTimeInMillis() - System.currentTimeMillis()) / 1000;
}
/**
* 获取refreshToken过期时间
*
* @return expire
*/
public static int getRefreshTokenValiditySeconds() {
return 60 * 60 * 24 * 15;
}
}
#服务器端口
server:
port: 8100
#数据源配置
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: ${blade.datasource.dev.url}
username: ${blade.datasource.dev.username}
password: ${blade.datasource.dev.password}
#服务器端口
server:
port: 8100
#数据源配置
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: ${blade.datasource.prod.url}
username: ${blade.datasource.prod.username}
password: ${blade.datasource.prod.password}
#服务器端口
server:
port: 8100
#数据源配置
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: ${blade.datasource.test.url}
username: ${blade.datasource.test.username}
password: ${blade.datasource.test.password}
swagger:
base-packages:
- org.springblade
- org.springframework.security.oauth2.provider.endpoint
<?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">
<parent>
<artifactId>BladeX</artifactId>
<groupId>org.springblade</groupId>
<version>2.0.0.RELEASE</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>blade-common</artifactId>
<name>${project.artifactId}</name>
<version>${bladex.project.version}</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-launch</artifactId>
<version>${bladex.tool.version}</version>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-auto</artifactId>
<version>${bladex.tool.version}</version>
<scope>provided</scope>
</dependency>
<!--<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-tool</artifactId>
<version>${bladex.tool.version}</version>
</dependency>-->
<!--<dependency>-->
<!--<groupId>com.aliyun</groupId>-->
<!--<artifactId>aliyun-java-sdk-core</artifactId>-->
<!--<version>4.1.0</version>-->
<!--</dependency>-->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<skip>true</skip>
<finalName>${project.name}</finalName>
</configuration>
</plugin>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>${docker.plugin.version}</version>
<configuration>
<skipDockerBuild>true</skipDockerBuild>
</configuration>
</plugin>
</plugins>
</build>
</project>
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.springblade.common.cache;
/**
* 缓存名
*
* @author Chill
*/
public interface CacheNames {
String NOTICE_ONE = "notice:one";
}
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.springblade.common.config;
import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Configuration;
/**
* 公共封装包配置类
*
* @author Chill
*/
@Configuration
@AllArgsConstructor
public class BladeCommonConfiguration {
}
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.springblade.common.constant;
import org.springblade.core.launch.constant.AppConstant;
/**
* 通用常量
*
* @author Chill
*/
public interface CommonConstant {
/**118.31.247.145
* nacos dev 地址
*/
String NACOS_DEV_ADDR = "118.31.247.145:8848";
/**
* nacos prod 地址
*/
String NACOS_PROD_ADDR = "118.31.247.145:8848";
/**
* nacos test 地址
*/
String NACOS_TEST_ADDR = "118.31.247.145:8848";
/**
* sentinel dev 地址
*/
String SENTINEL_DEV_ADDR = "118.31.247.145:8858";
/**
* sentinel prod 地址
*/
String SENTINEL_PROD_ADDR = "118.31.247.145:8858";
/**
* sentinel test 地址
*/
String SENTINEL_TEST_ADDR = "118.31.247.145:8858";
/**
* sword 系统名
*/
String SWORD_NAME = "sword";
/**
* saber 系统名
*/
String SABER_NAME = "saber";
/**
* 顶级父节点id
*/
Long TOP_PARENT_ID = 0L;
/**
* 顶级父节点名称
*/
String TOP_PARENT_NAME = "顶级";
/**
* 默认密码
*/
String DEFAULT_PASSWORD = "654321";
/**
* 动态获取nacos地址
*
* @param profile 环境变量
* @return addr
*/
static String nacosAddr(String profile) {
switch (profile) {
case (AppConstant.PROD_CODE):
return NACOS_PROD_ADDR;
case (AppConstant.TEST_CODE):
return NACOS_TEST_ADDR;
default:
return NACOS_DEV_ADDR;
}
}
/**
* 动态获取sentinel地址
*
* @param profile 环境变量
* @return addr
*/
static String sentinelAddr(String profile) {
switch (profile) {
case (AppConstant.PROD_CODE):
return SENTINEL_PROD_ADDR;
case (AppConstant.TEST_CODE):
return SENTINEL_TEST_ADDR;
default:
return SENTINEL_DEV_ADDR;
}
}
}
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.springblade.common.launch;
import org.springblade.common.constant.CommonConstant;
import org.springblade.core.auto.service.AutoService;
import org.springblade.core.launch.service.LauncherService;
import org.springframework.boot.builder.SpringApplicationBuilder;
import java.util.Properties;
/**
* 启动参数拓展
*
* @author smallchil
*/
@AutoService(LauncherService.class)
public class LauncherServiceImpl implements LauncherService {
@Override
public void launcher(SpringApplicationBuilder builder, String appName, String profile, boolean isLocalDev) {
Properties props = System.getProperties();
props.setProperty("spring.cloud.nacos.discovery.server-addr", CommonConstant.nacosAddr(profile));
props.setProperty("spring.cloud.nacos.config.server-addr", CommonConstant.nacosAddr(profile));
props.setProperty("spring.cloud.sentinel.transport.dashboard", CommonConstant.sentinelAddr(profile));
}
}
package org.springblade.common.utils;
public class CommonPageUtil {
/**
* 获取limit start
* @param pageIndex
* @param pageSize
* @return
*/
public static Integer startIndex(Integer pageIndex,Integer pageSize){
if(pageIndex == null || pageIndex < 1){
return 0;
}else if(pageSize == null || pageSize < 1){
pageSize = 15;
}
return (pageIndex -1)*pageSize;
}
/**
* 获取limit size
* @param pageSize
* @return
*/
public static Integer pageSize(Integer pageSize){
if(pageSize == null || pageSize < 1){
return 15;
}
return pageSize;
}
}
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.springblade.common.utils;
/**
* 通用工具类
*
* @author Chill
*/
public class CommonUtil {
}
package org.springblade.common.utils;
import org.springframework.util.ObjectUtils;
import java.sql.Array;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
public class OrderNoUtil {
public static String getOrderNoForRepare(){
Date t = new Date();
SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
String orderNo = df.format(t)+getRandomNo(4);
return orderNo;
}
public static String getRandomNo(int length ){
String result = "";
java.util.Random r=new java.util.Random();
for(int i=0;i<length;i++){
result +=r.nextInt();
}
return result;
}
public static String getSimNO(){
Date t = new Date();
SimpleDateFormat df = new SimpleDateFormat("yyMMddHHmmss");
String orderNo = df.format(t)+getRandomNo(2).replaceAll("-","");
for(int i=0;i<(32-orderNo.length());i++){
orderNo += "0";
}
return orderNo;
}
}
package org.springblade.common.utils.captcha;
import lombok.Data;
import java.awt.image.BufferedImage;
/**
* @author 吕鸿磊
* @date 2019-07-30 18:39
*/
@Data
public class CaptchaBean {
private String result;
private String[] codeArray;
private BufferedImage bufferedImage;
}
package org.springblade.common.utils.captcha;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.util.Random;
/**
* @author 吕鸿磊
* @date 2019-07-30 17:52
*/
public class CaptchaUtil {
private static char[] ch = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".toCharArray();
int codecnt=4;
Random random = new Random();
int width = 90;
int height = 30;
int lineNum = 2;
Color color = new Color(253, 251, 255);
boolean transform=false;
public static void main(String[] args) {
CaptchaBean captchaBean = new CaptchaUtil().generateCaptcha();
System.out.println(captchaBean.toString());
}
public static CaptchaBean getCaptcha(){
return new CaptchaUtil().generateCaptcha();
}
public CaptchaBean generateCaptcha() {
// 1.build image
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics graphics = image.getGraphics();
graphics.setColor(color);
// graphics.setColor(getRandColor(200, 250));
graphics.fillRect(0, 0, width, height);
//2.mixSource
mixSource(image);
//3.drawCode
CaptchaBean result = drawCode(graphics);
result.setBufferedImage(image);
return result;
}
public CaptchaBean generateCode() {
CaptchaBean captchaBean = new CaptchaBean();
String[] codeArray = new String[codecnt];
StringBuffer builder = new StringBuffer();
for (int i = 0; i < codecnt; i++) {
char c = ch[random.nextInt(ch.length)];
String ch = String.valueOf(c);
codeArray[i] = ch;
builder.append(ch);
}
captchaBean.setCodeArray(codeArray);
captchaBean.setResult(builder.toString());
return captchaBean;
}
public void mixSource(BufferedImage image) {
drawPoint(image);
drawLine(image);
}
public void drawLine(BufferedImage image) {
Graphics graphics = image.getGraphics();
for (int i = 0; i < lineNum; i++) {
int xs = random.nextInt(width);
int ys = random.nextInt(height);
int xe = xs + random.nextInt(width);
int ye = ys + random.nextInt(height);
graphics.setColor(getRandColor(1, 255));
graphics.drawLine(xs, ys, xe, ye);
}
}
public void drawPoint(BufferedImage image) {
// 添加噪点
float yawpRate = 0.05f;// 噪声率
int area = (int) (yawpRate * width * height);
for (int i = 0; i < area; i++) {
int x = random.nextInt(width);
int y = random.nextInt(height);
image.setRGB(x, y, random.nextInt(255));
}
}
public CaptchaBean drawCode(Graphics graphics) {
//1.genarate code
CaptchaBean captcha = generateCode();
//2.drapCode
Font font = getFont(16);
graphics.setFont(font);
if(captcha!=null&&captcha.getCodeArray()!=null
&&captcha.getResult()!=null&&captcha.getCodeArray().length>0) {
for(int i=0;i<captcha.getCodeArray().length;i++) {
String code = String.valueOf(captcha.getCodeArray()[i]);
// 文字变形设置
if(transform) {
AffineTransform fontAT = new AffineTransform();
int rotate = random.nextInt(25);
fontAT.rotate(random.nextBoolean() ? Math.toRadians(rotate) : -Math
.toRadians(rotate / 2));
Font fx = new Font(new String[] { "Times New Roman", "Verdana",
"arial" }[random.nextInt(2)], random.nextInt(5),
14 + random.nextInt(8)).deriveFont(fontAT);
graphics.setFont(fx);
}
// 产生随机的颜色分量来构造颜色值,这样输出的每一位数字的颜色值都不同
// int red = random.nextInt(255);
// int green = random.nextInt(255);
// int blue = random.nextInt(255);
// 将随机产生的颜色将验证码绘制到图像中
graphics.setColor(getRandColor(1, 255));
// graphics.drawString(code, (i*20)+5, 10+random.nextInt(10));
graphics.drawString(code, (i*width/5)+5, height/2+ random.nextInt(height/4));
}
}
return captcha;
}
// 得到随机颜色
public Color getRandColor(int fc, int bc) {// 给定范围获得随机颜色
if (fc > 255) {
fc = 255;
}
if (bc > 255) {
bc = 255;
}
int r = fc + random.nextInt(bc - fc);
int g = fc + random.nextInt(bc - fc);
int b = fc + random.nextInt(bc - fc);
return new Color(r, g, b);
}
private Font getFont(int size) {
Random random = new Random();
Font font[] = new Font[5];
font[0] = new Font("Ravie", Font.BOLD, size);
font[1] = new Font("Antique Olive Compact", Font.BOLD, size);
font[2] = new Font("Fixedsys", Font.BOLD, size);
font[3] = new Font("Wide Latin", Font.BOLD, size);
font[4] = new Font("Gill Sans Ultra Bold", Font.BOLD, size);
return font[random.nextInt(5)];
}
}
${AnsiColor.BLUE} ______ _ _ ___ ___
${AnsiColor.BLUE} | ___ \| | | | \ \ / /
${AnsiColor.BLUE} | |_/ /| | __ _ __| | ___ \ V /
${AnsiColor.BLUE} | ___ \| | / _` | / _` | / _ \ > <
${AnsiColor.BLUE} | |_/ /| || (_| || (_| || __/ / . \
${AnsiColor.BLUE} \____/ |_| \__,_| \__,_| \___|/__/ \__\
${AnsiColor.BLUE}:: BladeX :: ${spring.application.name}:${AnsiColor.RED}${blade.env}${AnsiColor.BLUE} :: Running SpringBoot ${spring-boot.version} :: ${AnsiColor.BRIGHT_BLACK}
FROM anapsix/alpine-java:8_server-jre_unlimited
MAINTAINER smallchill@163.com
RUN mkdir -p /blade/gateway
WORKDIR /blade/gateway
EXPOSE 80
ADD ./target/blade-gateway.jar ./app.jar
ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "app.jar"]
CMD ["--spring.profiles.active=test"]
<?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">
<parent>
<artifactId>BladeX</artifactId>
<groupId>org.springblade</groupId>
<version>2.0.0.RELEASE</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>blade-gateway</artifactId>
<name>${project.artifactId}</name>
<version>${bladex.project.version}</version>
<packaging>jar</packaging>
<dependencies>
<!--Blade-->
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-launch</artifactId>
<version>${bladex.tool.version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-common</artifactId>
<version>${bladex.project.version}</version>
<exclusions>
<exclusion>
<groupId>org.springblade</groupId>
<artifactId>blade-core-launch</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--Spring-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
<version>${spring.boot.admin.version}</version>
</dependency>
<!--Hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Nacos -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>${alibaba.cloud.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>${alibaba.cloud.version}</version>
</dependency>
<!--Swagger-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger.version}</version>
<exclusions>
<exclusion>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
<version>${swagger.models.version}</version>
</dependency>
<!--<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger.version}</version>
</dependency>-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>swagger-bootstrap-ui</artifactId>
<version>${swagger.bootstrapui.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>${docker.plugin.version}</version>
<configuration>
<imageName>${docker.registry.url}/blade/${project.artifactId}:${project.version}</imageName>
<dockerDirectory>${project.basedir}</dockerDirectory>
<dockerHost>${docker.registry.host}</dockerHost>
<resources>
<resource>
<targetPath>/</targetPath>
<directory>${project.build.directory}</directory>
<include>${project.build.finalName}.jar</include>
</resource>
</resources>
<registryUrl>${docker.registry.url}</registryUrl>
<serverId>${docker.registry.url}</serverId>
<pushImage>true</pushImage>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<tasks>
<copy overwrite="true"
tofile="${session.executionRootDirectory}/target/${project.artifactId}.jar"
file="${project.build.directory}/${project.artifactId}.jar" />
</tasks>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.springblade.gateway;
import org.springblade.core.launch.constant.AppConstant;
import org.springblade.core.launch.BladeApplication;
import org.springframework.cloud.client.SpringCloudApplication;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.scheduling.annotation.EnableScheduling;
/**
* 项目启动
*
* @author Chill
*/
@EnableHystrix
@EnableScheduling
@SpringCloudApplication
public class GateWayApplication {
public static void main(String[] args) {
BladeApplication.run(AppConstant.APPLICATION_GATEWAY_NAME, GateWayApplication.class, args);
}
}
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.springblade.gateway.config;
import org.springblade.gateway.handler.ErrorExceptionHandler;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.web.ResourceProperties;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.reactive.error.ErrorAttributes;
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.result.view.ViewResolver;
import java.util.Collections;
import java.util.List;
/**
* 异常处理配置类
*
* @author Chill
*/
@Configuration
@AutoConfigureBefore(ErrorWebFluxAutoConfiguration.class)
@EnableConfigurationProperties({ServerProperties.class, ResourceProperties.class})
public class ErrorHandlerConfiguration {
private final ServerProperties serverProperties;
private final ApplicationContext applicationContext;
private final ResourceProperties resourceProperties;
private final List<ViewResolver> viewResolvers;
private final ServerCodecConfigurer serverCodecConfigurer;
public ErrorHandlerConfiguration(ServerProperties serverProperties,
ResourceProperties resourceProperties,
ObjectProvider<List<ViewResolver>> viewResolversProvider,
ServerCodecConfigurer serverCodecConfigurer,
ApplicationContext applicationContext) {
this.serverProperties = serverProperties;
this.applicationContext = applicationContext;
this.resourceProperties = resourceProperties;
this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
this.serverCodecConfigurer = serverCodecConfigurer;
}
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public ErrorWebExceptionHandler errorWebExceptionHandler(ErrorAttributes errorAttributes) {
ErrorExceptionHandler exceptionHandler = new ErrorExceptionHandler(
errorAttributes,
this.resourceProperties,
this.serverProperties.getError(),
this.applicationContext);
exceptionHandler.setViewResolvers(this.viewResolvers);
exceptionHandler.setMessageWriters(this.serverCodecConfigurer.getWriters());
exceptionHandler.setMessageReaders(this.serverCodecConfigurer.getReaders());
return exceptionHandler;
}
}
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.springblade.gateway.config;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.gateway.handler.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.cors.reactive.CorsUtils;
import org.springframework.web.filter.reactive.HiddenHttpMethodFilter;
import org.springframework.web.reactive.function.server.RequestPredicates;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
/**
* 路由配置信息
*
* @author Chill
*/
@Slf4j
@Configuration
@AllArgsConstructor
public class RouterFunctionConfiguration {
/**
* 这里为支持的请求头,如果有自定义的header字段请自己添加
*/
private static final String ALLOWED_HEADERS = "x-requested-with, blade-auth, Content-Type, Authorization, credential, X-XSRF-TOKEN, token, username, client";
private static final String ALLOWED_METHODS = "*";
private static final String ALLOWED_ORIGIN = "*";
private static final String ALLOWED_EXPOSE = "*";
private static final String MAX_AGE = "18000L";
private final HystrixFallbackHandler hystrixFallbackHandler;
private final SwaggerResourceHandler swaggerResourceHandler;
private final SwaggerSecurityHandler swaggerSecurityHandler;
private final SwaggerUiHandler swaggerUiHandler;
@Bean
public WebFilter corsFilter() {
return (ServerWebExchange ctx, WebFilterChain chain) -> {
ServerHttpRequest request = ctx.getRequest();
if (CorsUtils.isCorsRequest(request)) {
ServerHttpResponse response = ctx.getResponse();
HttpHeaders headers = response.getHeaders();
headers.add("Access-Control-Allow-Headers", ALLOWED_HEADERS);
headers.add("Access-Control-Allow-Methods", ALLOWED_METHODS);
headers.add("Access-Control-Allow-Origin", ALLOWED_ORIGIN);
headers.add("Access-Control-Expose-Headers", ALLOWED_EXPOSE);
headers.add("Access-Control-Max-Age", MAX_AGE);
headers.add("Access-Control-Allow-Credentials", "true");
if (request.getMethod() == HttpMethod.OPTIONS) {
response.setStatusCode(HttpStatus.OK);
return Mono.empty();
}
}
return chain.filter(ctx);
};
}
@Bean
public RouterFunction routerFunction() {
return RouterFunctions.route(
RequestPredicates.path("/fallback")
.and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), hystrixFallbackHandler)
.andRoute(RequestPredicates.GET("/swagger-resources")
.and(RequestPredicates.accept(MediaType.ALL)), swaggerResourceHandler)
.andRoute(RequestPredicates.GET("/swagger-resources/configuration/ui")
.and(RequestPredicates.accept(MediaType.ALL)), swaggerUiHandler)
.andRoute(RequestPredicates.GET("/swagger-resources/configuration/security")
.and(RequestPredicates.accept(MediaType.ALL)), swaggerSecurityHandler);
}
/**
* 解决springboot2.0.5版本出现的 Only one connection receive subscriber allowed.
* 参考:https://github.com/spring-cloud/spring-cloud-gateway/issues/541
*/
@Bean
public HiddenHttpMethodFilter hiddenHttpMethodFilter() {
return new HiddenHttpMethodFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
return chain.filter(exchange);
}
};
}
}
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.springblade.gateway.controller;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 服务发现控制器
*
* @author Chill
*/
@Slf4j
@RestController
@AllArgsConstructor
@RequestMapping("/discovery")
public class DiscoveryClientController {
private final DiscoveryClient discoveryClient;
/**
* 获取服务实例
*/
@GetMapping("/instances")
public Map<String, List<ServiceInstance>> instances() {
Map<String, List<ServiceInstance>> instances = new HashMap<>(16);
List<String> services = discoveryClient.getServices();
services.forEach(s -> {
List<ServiceInstance> list = discoveryClient.getInstances(s);
instances.put(s, list);
});
return instances;
}
}
package org.springblade.gateway.filter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.Arrays;
import java.util.stream.Collectors;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.addOriginalRequestUrl;
/**
* <p>
* 全局拦截器,作用所有的微服务
* <p>
* 1. 对请求头中参数进行处理 from 参数进行清洗
* 2. 重写StripPrefix = 1,支持全局
*
* @author lengleng
*/
@Component
public class RequestGlobalFilter implements GlobalFilter, Ordered {
/**
* Process the Web request and (optionally) delegate to the next
* {@code WebFilter} through the given {@link GatewayFilterChain}.
*
* @param exchange the current server exchange
* @param chain provides a way to delegate to the next filter
* @return {@code Mono<Void>} to indicate when request processing is complete
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 1. 清洗请求头中from 参数
ServerHttpRequest request = exchange.getRequest().mutate()
.headers(httpHeaders -> httpHeaders.remove("X"))
.build();
// 2. 重写StripPrefix
addOriginalRequestUrl(exchange, request.getURI());
String rawPath = request.getURI().getRawPath();
String newPath = "/" + Arrays.stream(StringUtils.tokenizeToStringArray(rawPath, "/"))
.skip(1L).collect(Collectors.joining("/"));
ServerHttpRequest newRequest = request.mutate()
.path(newPath)
.build();
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, newRequest.getURI());
return chain.filter(exchange.mutate().request(newRequest.mutate().build()).build());
}
@Override
public int getOrder() {
return -1000;
}
}
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.springblade.gateway.handler;
import org.springframework.boot.autoconfigure.web.ErrorProperties;
import org.springframework.boot.autoconfigure.web.ResourceProperties;
import org.springframework.boot.autoconfigure.web.reactive.error.DefaultErrorWebExceptionHandler;
import org.springframework.boot.web.reactive.error.ErrorAttributes;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpStatus;
import org.springframework.web.reactive.function.server.*;
import org.springframework.web.server.ResponseStatusException;
import java.util.HashMap;
import java.util.Map;
/**
* 异常处理
*
* @author Chill
*/
public class ErrorExceptionHandler extends DefaultErrorWebExceptionHandler {
public ErrorExceptionHandler(ErrorAttributes errorAttributes, ResourceProperties resourceProperties,
ErrorProperties errorProperties, ApplicationContext applicationContext) {
super(errorAttributes, resourceProperties, errorProperties, applicationContext);
}
/**
* 获取异常属性
*/
@Override
protected Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace) {
int code = 500;
Throwable error = super.getError(request);
if (error instanceof NotFoundException) {
code = 404;
}
if (error instanceof ResponseStatusException) {
code = ((ResponseStatusException) error).getStatus().value();
}
return response(code, this.buildMessage(request, error));
}
/**
* 指定响应处理方法为JSON处理的方法
*
* @param errorAttributes
*/
@Override
protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
}
/**
* 根据code获取对应的HttpStatus
*
* @param errorAttributes
*/
@Override
protected HttpStatus getHttpStatus(Map<String, Object> errorAttributes) {
int statusCode = (int) errorAttributes.get("code");
return HttpStatus.valueOf(statusCode);
}
/**
* 构建异常信息
*
* @param request
* @param ex
* @return
*/
private String buildMessage(ServerRequest request, Throwable ex) {
StringBuilder message = new StringBuilder("Failed to handle request [");
message.append(request.methodName());
message.append(" ");
message.append(request.uri());
message.append("]");
if (ex != null) {
message.append(": ");
message.append(ex.getMessage());
}
return message.toString();
}
/**
* 构建返回的JSON数据格式
*
* @param status 状态码
* @param errorMessage 异常信息
* @return
*/
public static Map<String, Object> response(int status, String errorMessage) {
Map<String, Object> map = new HashMap<>(16);
map.put("code", status);
map.put("message", errorMessage);
map.put("data", null);
return map;
}
}
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.springblade.gateway.handler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.HandlerFunction;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
/**
* Hystrix 降级处理
*
* @author lengleng
*/
@Slf4j
@Component
public class HystrixFallbackHandler implements HandlerFunction<ServerResponse> {
@Override
public Mono<ServerResponse> handle(ServerRequest serverRequest) {
log.error("网关执行请求:{}失败,hystrix服务降级处理", serverRequest.uri());
return ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR.value())
.contentType(MediaType.TEXT_PLAIN).body(BodyInserters.fromObject("服务异常"));
}
}
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.springblade.gateway.handler;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.HandlerFunction;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
import springfox.documentation.swagger.web.SwaggerResourcesProvider;
/**
* SwaggerResourceHandler
*
* @author lengleng
*/
@Slf4j
@Component
@AllArgsConstructor
public class SwaggerResourceHandler implements HandlerFunction<ServerResponse> {
private final SwaggerResourcesProvider swaggerResources;
/**
* Handle the given request.
*
* @param request the request to handler
* @return the response
*/
@Override
public Mono<ServerResponse> handle(ServerRequest request) {
return ServerResponse.status(HttpStatus.OK)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.body(BodyInserters.fromObject(swaggerResources.get()));
}
}
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.springblade.gateway.handler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.HandlerFunction;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
import springfox.documentation.swagger.web.SecurityConfiguration;
import springfox.documentation.swagger.web.SecurityConfigurationBuilder;
import java.util.Optional;
/**
* SwaggerSecurityHandler
*
* @author lengleng
*/
@Slf4j
@Component
public class SwaggerSecurityHandler implements HandlerFunction<ServerResponse> {
@Autowired(required = false)
private SecurityConfiguration securityConfiguration;
/**
* Handle the given request.
*
* @param request the request to handler
* @return the response
*/
@Override
public Mono<ServerResponse> handle(ServerRequest request) {
return ServerResponse.status(HttpStatus.OK)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.body(BodyInserters.fromObject(
Optional.ofNullable(securityConfiguration)
.orElse(SecurityConfigurationBuilder.builder().build())));
}
}
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.springblade.gateway.handler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.HandlerFunction;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
import springfox.documentation.swagger.web.UiConfiguration;
import springfox.documentation.swagger.web.UiConfigurationBuilder;
import java.util.Optional;
/**
* SwaggerUiHandler
*
* @author lengleng
*/
@Slf4j
@Component
public class SwaggerUiHandler implements HandlerFunction<ServerResponse> {
@Autowired(required = false)
private UiConfiguration uiConfiguration;
/**
* Handle the given request.
*
* @param request the request to handler
* @return the response
*/
@Override
public Mono<ServerResponse> handle(ServerRequest request) {
return ServerResponse.status(HttpStatus.OK)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.body(BodyInserters.fromObject(
Optional.ofNullable(uiConfiguration)
.orElse(UiConfigurationBuilder.builder().build())));
}
}
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.springblade.gateway.provider;
import lombok.AllArgsConstructor;
import org.springblade.core.launch.constant.AppConstant;
import org.springframework.cloud.gateway.config.GatewayProperties;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.support.NameUtils;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import springfox.documentation.swagger.web.SwaggerResource;
import springfox.documentation.swagger.web.SwaggerResourcesProvider;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 聚合接口文档注册
*
* @author Sywd
*/
@Primary
@Component
@AllArgsConstructor
public class SwaggerProvider implements SwaggerResourcesProvider {
public static final String API_URI = "/v2/api-docs-ext";
private final RouteLocator routeLocator;
private final GatewayProperties gatewayProperties;
private static Map<String, String> routeMap = new HashMap<>();
static {
routeMap.put(AppConstant.APPLICATION_AUTH_NAME, "授权模块");
routeMap.put(AppConstant.APPLICATION_DESK_NAME, "工作台模块");
routeMap.put(AppConstant.APPLICATION_SYSTEM_NAME, "系统模块");
routeMap.put(AppConstant.APPLICATION_USER_NAME, "用户模块");
routeMap.put("blade-apartment", "智能公寓模块");
}
@Override
public List<SwaggerResource> get() {
List<SwaggerResource> resources = new ArrayList<>();
List<String> routes = new ArrayList<>();
routeLocator.getRoutes().subscribe(route -> routes.add(route.getId()));
gatewayProperties.getRoutes().stream().filter(routeDefinition -> routes.contains(routeDefinition.getId()))
.forEach(routeDefinition -> routeDefinition.getPredicates().stream()
.filter(predicateDefinition -> "Path".equalsIgnoreCase(predicateDefinition.getName()))
.forEach(predicateDefinition -> resources.add(swaggerResource(routeDefinition.getId(),
predicateDefinition.getArgs().get(NameUtils.GENERATED_NAME_PREFIX + "0")
.replace("/**", API_URI)))));
return resources;
}
private SwaggerResource swaggerResource(String name, String location) {
SwaggerResource swaggerResource = new SwaggerResource();
swaggerResource.setName((routeMap.get(name) == null ? name : routeMap.get(name)));
swaggerResource.setLocation(location);
swaggerResource.setSwaggerVersion("2.0");
return swaggerResource;
}
}
server:
port: 80
# 需要配置的服务名
blade:
service:
blade-auth: blade-auth
blade-desk: blade-desk
blade-system: blade-system
blade-apartment: blade-apartment
blade-user: blade-user
spring:
cloud:
gateway:
routes:
# 认证中心
- id: ${blade.service.blade-auth}
uri: lb://${blade.service.blade-auth}
predicates:
- Path=/${blade.service.blade-auth}/**
# 首页模块
- id: ${blade.service.blade-desk}
uri: lb://${blade.service.blade-desk}
predicates:
- Path=/${blade.service.blade-desk}/**
# 用户模块
- id: ${blade.service.blade-user}
uri: lb://${blade.service.blade-user}
predicates:
- Path=/${blade.service.blade-user}/**
# 系统模块
- id: ${blade.service.blade-system}
uri: lb://${blade.service.blade-system}
predicates:
- Path=/${blade.service.blade-system}/**
# 智能公寓模块
- id: ${blade.service.blade-apartment}
uri: lb://${blade.service.blade-apartment}
predicates:
- Path=/${blade.service.blade-apartment}/**
discovery:
locator:
enabled: true
loadbalancer:
retry:
enabled: true
<?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">
<parent>
<artifactId>blade-ops-api</artifactId>
<groupId>org.springblade</groupId>
<version>2.0.0.RELEASE</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>blade-flow-api</artifactId>
<name>${project.artifactId}</name>
<version>${bladex.project.version}</version>
<packaging>jar</packaging>
</project>
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.springblade.flow.core.constant;
/**
* 流程常量.
*
* @author Chill
*/
public interface ProcessConstant {
/**
* 请假流程标识
*/
String LEAVE_KEY = "Leave";
/**
* 报销流程标识
*/
String Expense_KEY = "Expense";
/**
* 同意标识
*/
String PASS_KEY = "pass";
/**
* 同意代号
*/
String PASS_ALIAS = "ok";
/**
* 同意默认批复
*/
String PASS_COMMENT = "同意";
/**
* 驳回默认批复
*/
String NOT_PASS_COMMENT = "驳回";
/**
* 创建人变量名
*/
String TASK_VARIABLE_CREATE_USER = "createUser";
}
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.springblade.flow.core.entity;
import lombok.Data;
import org.springblade.flow.core.constant.ProcessConstant;
import java.io.Serializable;
import java.util.Date;
import java.util.Map;
/**
* 工作流通用实体类
*
* @author Chill
*/
@Data
public class BladeFlow implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 任务编号
*/
private String taskId;
/**
* 任务名称
*/
private String taskName;
/**
* 任务定义Key
*/
private String taskDefinitionKey;
/**
* 任务执行人编号
*/
private String assignee;
/**
* 任务执行人名称
*/
private String assigneeName;
/**
* 流程分类
*/
private String category;
/**
* 流程分类名
*/
private String categoryName;
/**
* 创建时间
*/
private Date createTime;
/**
* 结束时间
*/
private Date endTime;
/**
* 签收时间
*/
private Date claimTime;
/**
* 历史任务结束时间
*/
private Date historyTaskEndTime;
/**
* 执行ID
*/
private String executionId;
/**
* 流程实例ID
*/
private String processInstanceId;
/**
* 流程ID
*/
private String processDefinitionId;
/**
* 流程标识
*/
private String processDefinitionKey;
/**
* 流程名
*/
private String processDefinitionName;
/**
* 流程版本
*/
private int processDefinitionVersion;
/**
* 流程说明
*/
private String processDefinitionDesc;
/**
* 流程简图名
*/
private String processDefinitionDiagramResName;
/**
* 流程重命名
*/
private String processDefinitionResName;
/**
* 历史任务流程实例ID 查看流程图会用到
*/
private String historyProcessInstanceId;
/**
* 流程实例是否结束
*/
private String processIsFinished;
/**
* 历史活动流程
*/
private String historyActivityName;
/**
* 历史活动耗时
*/
private String historyActivityDurationTime;
/**
* 业务绑定Table
*/
private String businessTable;
/**
* 业务绑定ID
*/
private String businessId;
/**
* 任务状态
*/
private String status;
/**
* 任务意见
*/
private String comment;
/**
* 是否通过
*/
private boolean isPass;
/**
* 是否通过代号
*/
private String flag;
/**
* 开始查询日期
*/
private Date beginDate;
/**
* 结束查询日期
*/
private Date endDate;
/**
* 流程参数
*/
private Map<String, Object> variables;
/**
* 获取是否通过
*/
public boolean isPass() {
return ProcessConstant.PASS_ALIAS.equals(flag) || ProcessConstant.PASS_COMMENT.equals(comment);
}
}
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.springblade.flow.core.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springblade.core.mp.base.BaseEntity;
/**
* FlowEntity
*
* @author Chill
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class FlowEntity extends BaseEntity {
@TableField(exist = false)
private BladeFlow flow;
public BladeFlow getFlow() {
if (flow == null) {
flow = new BladeFlow();
}
return flow;
}
}
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.springblade.flow.core.feign;
import org.springblade.core.launch.constant.AppConstant;
import org.springblade.core.tool.api.R;
import org.springblade.flow.core.entity.BladeFlow;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.Map;
/**
* 工作流远程调用接口.
*
* @author Chill
*/
@FeignClient(
value = AppConstant.APPLICATION_FLOW_NAME,
fallback = IFlowClientFallback.class
)
public interface IFlowClient {
String API_PREFIX = "/client";
String START_PROCESS_INSTANCE_BY_ID = API_PREFIX + "start-process-instance-by-id";
String START_PROCESS_INSTANCE_BY_KEY = API_PREFIX + "start-process-instance-by-key";
String COMPLETE_TASK = API_PREFIX + "complete-task";
String TASK_VARIABLE = API_PREFIX + "task-variable";
String TASK_VARIABLES = API_PREFIX + "task-variables";
/**
* 开启流程
*
* @param processDefinitionId 流程id
* @param businessKey 业务key
* @param variables 参数
* @return BladeFlow
*/
@PostMapping(START_PROCESS_INSTANCE_BY_ID)
R<BladeFlow> startProcessInstanceById(@RequestParam("processDefinitionId") String processDefinitionId, @RequestParam("businessKey") String businessKey, @RequestBody Map<String, Object> variables);
/**
* 开启流程
*
* @param processDefinitionKey 流程标识
* @param businessKey 业务key
* @param variables 参数
* @return BladeFlow
*/
@PostMapping(START_PROCESS_INSTANCE_BY_KEY)
R<BladeFlow> startProcessInstanceByKey(@RequestParam("processDefinitionKey") String processDefinitionKey, @RequestParam("businessKey") String businessKey, @RequestBody Map<String, Object> variables);
/**
* 完成任务
*
* @param taskId 任务id
* @param processInstanceId 流程实例id
* @param comment 评论
* @param variables 参数
* @return R
*/
@PostMapping(COMPLETE_TASK)
R completeTask(@RequestParam("taskId") String taskId, @RequestParam("processInstanceId") String processInstanceId, @RequestParam("comment") String comment, @RequestBody Map<String, Object> variables);
/**
* 获取流程变量
*
* @param taskId 任务id
* @param variableName 变量名
* @return R
*/
@GetMapping(TASK_VARIABLE)
R<Object> taskVariable(@RequestParam("taskId") String taskId, @RequestParam("variableName") String variableName);
/**
* 获取流程变量集合
*
* @param taskId 任务id
* @return R
*/
@GetMapping(TASK_VARIABLES)
R<Map<String, Object>> taskVariables(@RequestParam("taskId") String taskId);
}
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.springblade.flow.core.feign;
import org.springblade.core.tool.api.R;
import org.springblade.flow.core.entity.BladeFlow;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* 流程远程调用失败处理类
*
* @author Chill
*/
@Component
public class IFlowClientFallback implements IFlowClient {
@Override
public R<BladeFlow> startProcessInstanceById(String processDefinitionId, String businessKey, Map<String, Object> variables) {
return R.fail("远程调用失败");
}
@Override
public R<BladeFlow> startProcessInstanceByKey(String processDefinitionKey, String businessKey, Map<String, Object> variables) {
return R.fail("远程调用失败");
}
@Override
public R completeTask(String taskId, String processInstanceId, String comment, Map<String, Object> variables) {
return R.fail("远程调用失败");
}
@Override
public R<Object> taskVariable(String taskId, String variableName) {
return R.fail("远程调用失败");
}
@Override
public R<Map<String, Object>> taskVariables(String taskId) {
return R.fail("远程调用失败");
}
}
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.springblade.flow.core.utils;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.StringUtil;
import org.springblade.flow.core.constant.ProcessConstant;
import java.util.HashMap;
import java.util.Map;
/**
* 工作流工具类
*
* @author Chill
*/
public class FlowUtil {
/**
* 定义流程key对应的表名
*/
private final static Map<String, String> BUSINESS_TABLE = new HashMap<>();
static {
BUSINESS_TABLE.put(ProcessConstant.LEAVE_KEY, "blade_process_leave");
}
/**
* 通过流程key获取业务表名
*
* @param key 流程key
*/
public static String getBusinessTable(String key) {
String businessTable = BUSINESS_TABLE.get(key);
if (Func.isEmpty(businessTable)) {
throw new RuntimeException("流程启动失败,未找到相关业务表");
}
return businessTable;
}
/**
* 获取业务标识
*
* @param businessTable 业务表
* @param businessId 业务表主键
* @return businessKey
*/
public static String getBusinessKey(String businessTable, String businessId) {
return StringUtil.format("{}:{}", businessTable, businessId);
}
}
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.springblade.flow.core.utils;
import org.springblade.core.secure.utils.SecureUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.StringUtil;
import static org.springblade.core.launch.constant.FlowConstant.TASK_USR_PREFIX;
/**
* 工作流任务工具类
*
* @author Chill
*/
public class TaskUtil {
/**
* 获取任务用户格式
*
* @return taskUser
*/
public static String getTaskUser() {
return StringUtil.format("{}{}", TASK_USR_PREFIX, SecureUtil.getUserId());
}
/**
* 获取任务用户格式
*
* @param userId 用户id
* @return taskUser
*/
public static String getTaskUser(String userId) {
return StringUtil.format("{}{}", TASK_USR_PREFIX, userId);
}
/**
* 获取用户主键
*
* @param taskUser 任务用户
* @return userId
*/
public static Long getUserId(String taskUser) {
return Func.toLong(StringUtil.removePrefix(taskUser, TASK_USR_PREFIX));
}
/**
* 获取用户组格式
*
* @return candidateGroup
*/
public static String getCandidateGroup() {
return SecureUtil.getUserRole();
}
}
<?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">
<parent>
<artifactId>BladeX</artifactId>
<groupId>org.springblade</groupId>
<version>2.0.0.RELEASE</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>blade-ops-api</artifactId>
<name>${project.artifactId}</name>
<version>2.0.0.RELEASE</version>
<packaging>pom</packaging>
<description>BladeX 微服务API集合</description>
<modules>
<module>blade-flow-api</module>
</modules>
<dependencies>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-starter-mybatis</artifactId>
<version>${bladex.tool.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-auto</artifactId>
<version>${bladex.tool.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<skip>true</skip>
<finalName>${project.name}</finalName>
</configuration>
</plugin>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>${docker.plugin.version}</version>
<configuration>
<skipDockerBuild>true</skipDockerBuild>
</configuration>
</plugin>
</plugins>
</build>
</project>
FROM anapsix/alpine-java:8_server-jre_unlimited
MAINTAINER smallchill@163.com
RUN mkdir -p /blade/admin
WORKDIR /blade/admin
EXPOSE 7002
ADD ./target/blade-admin.jar ./app.jar
ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "app.jar"]
CMD ["--spring.profiles.active=test"]
<?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">
<parent>
<artifactId>blade-ops</artifactId>
<groupId>org.springblade</groupId>
<version>2.0.0.RELEASE</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>blade-admin</artifactId>
<name>${project.artifactId}</name>
<version>${bladex.project.version}</version>
<packaging>jar</packaging>
<dependencies>
<!--Blade-->
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-common</artifactId>
<version>${bladex.project.version}</version>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-launch</artifactId>
<version>${bladex.tool.version}</version>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-tool</artifactId>
<version>${bladex.tool.version}</version>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-cloud</artifactId>
<version>${bladex.tool.version}</version>
<exclusions>
<exclusion>
<groupId>org.springblade</groupId>
<artifactId>blade-core-secure</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--Admin-Server-->
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
<version>${spring.boot.admin.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>${docker.plugin.version}</version>
<configuration>
<imageName>${docker.registry.url}/blade/${project.artifactId}:${project.version}</imageName>
<dockerDirectory>${project.basedir}</dockerDirectory>
<dockerHost>${docker.registry.host}</dockerHost>
<resources>
<resource>
<targetPath>/</targetPath>
<directory>${project.build.directory}</directory>
<include>${project.build.finalName}.jar</include>
</resource>
</resources>
<registryUrl>${docker.registry.url}</registryUrl>
<serverId>${docker.registry.url}</serverId>
<pushImage>true</pushImage>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<tasks>
<copy overwrite="true"
tofile="${session.executionRootDirectory}/target/${project.artifactId}.jar"
file="${project.build.directory}/${project.artifactId}.jar" />
</tasks>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.springblade.admin;
import de.codecentric.boot.admin.server.config.EnableAdminServer;
import org.springblade.core.launch.BladeApplication;
import org.springblade.core.launch.constant.AppConstant;
import org.springframework.cloud.client.SpringCloudApplication;
/**
* admin启动器
*
* @author Chill
*/
@EnableAdminServer
@SpringCloudApplication
public class AdminApplication {
public static void main(String[] args) {
BladeApplication.run(AppConstant.APPLICATION_ADMIN_NAME, AdminApplication.class, args);
}
}
server:
port: 7002
undertow:
# 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程
io-threads: 4
# 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载
worker-threads: 20
# 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理
buffer-size: 1024
# 是否分配的直接内存
direct-buffers: true
spring:
boot:
admin:
discovery:
ignored-services: consul
<?xml version="1.0"?>
<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">
<parent>
<groupId>org.springblade</groupId>
<artifactId>blade-ops</artifactId>
<version>2.0.0.RELEASE</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>blade-develop</artifactId>
<name>${project.artifactId}</name>
<version>${bladex.project.version}</version>
<packaging>jar</packaging>
<dependencies>
<!--Blade-->
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-boot</artifactId>
<version>${bladex.tool.version}</version>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-starter-develop</artifactId>
<version>${bladex.tool.version}</version>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-common</artifactId>
<version>${bladex.project.version}</version>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-dict-api</artifactId>
<version>${bladex.project.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>${docker.plugin.version}</version>
<configuration>
<skipDockerBuild>true</skipDockerBuild>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<tasks>
<copy overwrite="true"
tofile="${session.executionRootDirectory}/target/${project.artifactId}.jar"
file="${project.build.directory}/${project.artifactId}.jar" />
</tasks>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.springblade.develop;
import org.springblade.core.cloud.feign.EnableBladeFeign;
import org.springblade.core.launch.BladeApplication;
import org.springblade.core.launch.constant.AppConstant;
import org.springframework.cloud.client.SpringCloudApplication;
/**
* Develop启动器
*
* @author Chill
*/
@EnableBladeFeign
@SpringCloudApplication
public class DevelopApplication {
public static void main(String[] args) {
BladeApplication.run(AppConstant.APPLICATION_DEVELOP_NAME, DevelopApplication.class, args);
}
}
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.springblade.develop.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import io.swagger.annotations.*;
import lombok.AllArgsConstructor;
import org.springblade.core.boot.ctrl.BladeController;
import org.springblade.core.mp.support.Condition;
import org.springblade.core.mp.support.Query;
import org.springblade.core.secure.annotation.PreAuth;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.constant.RoleConstant;
import org.springblade.core.tool.utils.Func;
import org.springblade.develop.entity.Code;
import org.springblade.develop.service.ICodeService;
import org.springblade.develop.support.BladeCodeGenerator;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;
import springfox.documentation.annotations.ApiIgnore;
import javax.validation.Valid;
import java.util.Collection;
import java.util.Map;
/**
* 控制器
*
* @author Chill
*/
@RestController
@AllArgsConstructor
@RequestMapping("/code")
@Api(value = "代码生成", tags = "代码生成")
@PreAuth(RoleConstant.HAS_ROLE_ADMINISTRATOR)
public class CodeController extends BladeController {
private ICodeService codeService;
/**
* 详情
*/
@GetMapping("/detail")
@ApiOperation(value = "详情", notes = "传入code", position = 1)
public R<Code> detail(Code code) {
Code detail = codeService.getOne(Condition.getQueryWrapper(code));
return R.data(detail);
}
/**
* 分页
*/
@GetMapping("/list")
@ApiImplicitParams({
@ApiImplicitParam(name = "codeName", value = "模块名", paramType = "query", dataType = "string"),
@ApiImplicitParam(name = "tableName", value = "表名", paramType = "query", dataType = "string"),
@ApiImplicitParam(name = "modelName", value = "实体名", paramType = "query", dataType = "string")
})
@ApiOperation(value = "分页", notes = "传入code", position = 2)
public R<IPage<Code>> list(@ApiIgnore @RequestParam Map<String, Object> code, Query query) {
IPage<Code> pages = codeService.page(Condition.getPage(query), Condition.getQueryWrapper(code, Code.class));
return R.data(pages);
}
/**
* 新增或修改
*/
@PostMapping("/submit")
@ApiOperation(value = "新增或修改", notes = "传入code", position = 6)
public R submit(@Valid @RequestBody Code code) {
return R.status(codeService.saveOrUpdate(code));
}
/**
* 删除
*/
@PostMapping("/remove")
@ApiOperation(value = "删除", notes = "传入ids", position = 7)
public R remove(@ApiParam(value = "主键集合", required = true) @RequestParam String ids) {
return R.status(codeService.removeByIds(Func.toLongList(ids)));
}
/**
* 代码生成
*/
@PostMapping("/gen-code")
@ApiOperation(value = "代码生成", notes = "传入ids", position = 8)
public R genCode(@ApiParam(value = "主键集合", required = true) @RequestParam String ids, @RequestParam(defaultValue = "sword") String system) {
Collection<Code> codes = codeService.listByIds(Func.toLongList(ids));
codes.forEach(code -> {
BladeCodeGenerator generator = new BladeCodeGenerator();
generator.setSystemName(system);
generator.setServiceName(code.getServiceName());
generator.setCodeName(code.getCodeName());
generator.setPackageName(code.getPackageName());
generator.setPackageDir(code.getApiPath());
generator.setPackageWebDir(code.getWebPath());
generator.setTablePrefix(Func.toStrArray(code.getTablePrefix()));
generator.setIncludeTables(Func.toStrArray(code.getTableName()));
// 设置是否继承基础业务字段
generator.setHasSuperEntity(false);
generator.run();
});
return R.success("代码生成成功");
}
}
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.springblade.develop.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* 实体类
*
* @author Chill
*/
@Data
@TableName("blade_code")
@ApiModel(value = "Code对象", description = "Code对象")
public class Code implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键
*/
@JsonSerialize(using = ToStringSerializer.class)
@ApiModelProperty(value = "主键")
@TableId(value = "id", type = IdType.ID_WORKER)
private Long id;
/**
* 模块名称
*/
@ApiModelProperty(value = "服务名称")
private String serviceName;
/**
* 模块名称
*/
@ApiModelProperty(value = "模块名称")
private String codeName;
/**
* 表名
*/
@ApiModelProperty(value = "表名")
private String tableName;
/**
* 实体名
*/
@ApiModelProperty(value = "表前缀")
private String tablePrefix;
/**
* 主键名
*/
@ApiModelProperty(value = "主键名")
private String pkName;
/**
* 后端包名
*/
@ApiModelProperty(value = "后端包名")
private String packageName;
/**
* 后端路径
*/
@ApiModelProperty(value = "后端路径")
private String apiPath;
/**
* 前端路径
*/
@ApiModelProperty(value = "前端路径")
private String webPath;
/**
* 是否已删除
*/
@TableLogic
@ApiModelProperty(value = "是否已删除")
private Integer isDeleted;
}
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.springblade.develop.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springblade.develop.entity.Code;
/**
* Mapper 接口
*
* @author Chill
*/
public interface CodeMapper extends BaseMapper<Code> {
}
<?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.springblade.develop.mapper.CodeMapper">
<!-- 通用查询映射结果 -->
<resultMap id="codeResultMap" type="org.springblade.develop.entity.Code">
<id column="id" property="id"/>
<result column="service_name" property="serviceName"/>
<result column="code_name" property="codeName"/>
<result column="table_name" property="tableName"/>
<result column="pk_name" property="pkName"/>
<result column="table_prefix" property="tablePrefix"/>
<result column="package_name" property="packageName"/>
<result column="api_path" property="apiPath"/>
<result column="web_path" property="webPath"/>
<result column="is_deleted" property="isDeleted"/>
</resultMap>
<!-- 通用查询结果列 -->
<sql id="baseColumnList">
select
id, service_name, code_name, table_name, pk_name, model_name, package_name, api_path, web_path, is_deleted
</sql>
</mapper>
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.springblade.develop.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.springblade.develop.entity.Code;
/**
* 服务类
*
* @author Chill
*/
public interface ICodeService extends IService<Code> {
}
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.springblade.develop.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springblade.develop.entity.Code;
import org.springblade.develop.mapper.CodeMapper;
import org.springblade.develop.service.ICodeService;
import org.springframework.stereotype.Service;
/**
* 服务实现类
*
* @author Chill
*/
@Service
public class CodeServiceImpl extends ServiceImpl<CodeMapper, Code> implements ICodeService {
}
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (0, 'address', '智能公寓管理', 'menu', '/store/address', NULL, 1, 1, 0, 1, NULL, 0);
set @parentid = (SELECT LAST_INSERT_ID());
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'address_add', '新增', 'add', '/store/address/add', 'plus', 1, 2, 1, 1, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'address_edit', '修改', 'edit', '/store/address/edit', 'form', 2, 2, 2, 2, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'address_delete', '删除', 'delete', '/api/blade-store/address/remove', 'delete', 3, 2, 3, 3, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'address_view', '查看', 'view', '/store/address/view', 'file-text', 4, 2, 2, 2, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (0, 'agentprofile', '智能公寓管理', 'menu', '/user/agentprofile', NULL, 1, 1, 0, 1, NULL, 0);
set @parentid = (SELECT LAST_INSERT_ID());
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'agentprofile_add', '新增', 'add', '/user/agentprofile/add', 'plus', 1, 2, 1, 1, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'agentprofile_edit', '修改', 'edit', '/user/agentprofile/edit', 'form', 2, 2, 2, 2, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'agentprofile_delete', '删除', 'delete', '/api/blade-user/agentprofile/remove', 'delete', 3, 2, 3, 3, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'agentprofile_view', '查看', 'view', '/user/agentprofile/view', 'file-text', 4, 2, 2, 2, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (0, 'apartmentroom', '智能公寓管理', 'menu', '/user/apartmentroom', NULL, 1, 1, 0, 1, NULL, 0);
set @parentid = (SELECT LAST_INSERT_ID());
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'apartmentroom_add', '新增', 'add', '/user/apartmentroom/add', 'plus', 1, 2, 1, 1, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'apartmentroom_edit', '修改', 'edit', '/user/apartmentroom/edit', 'form', 2, 2, 2, 2, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'apartmentroom_delete', '删除', 'delete', '/api/blade-user/apartmentroom/remove', 'delete', 3, 2, 3, 3, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'apartmentroom_view', '查看', 'view', '/user/apartmentroom/view', 'file-text', 4, 2, 2, 2, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (0, 'apartmentuser', '智能公寓管理', 'menu', '/user/apartmentuser', NULL, 1, 1, 0, 1, NULL, 0);
set @parentid = (SELECT LAST_INSERT_ID());
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'apartmentuser_add', '新增', 'add', '/user/apartmentuser/add', 'plus', 1, 2, 1, 1, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'apartmentuser_edit', '修改', 'edit', '/user/apartmentuser/edit', 'form', 2, 2, 2, 2, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'apartmentuser_delete', '删除', 'delete', '/api/blade-user/apartmentuser/remove', 'delete', 3, 2, 3, 3, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'apartmentuser_view', '查看', 'view', '/user/apartmentuser/view', 'file-text', 4, 2, 2, 2, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (0, 'blockauth', '智能公寓管理', 'menu', '/user/blockauth', NULL, 1, 1, 0, 1, NULL, 0);
set @parentid = (SELECT LAST_INSERT_ID());
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'blockauth_add', '新增', 'add', '/user/blockauth/add', 'plus', 1, 2, 1, 1, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'blockauth_edit', '修改', 'edit', '/user/blockauth/edit', 'form', 2, 2, 2, 2, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'blockauth_delete', '删除', 'delete', '/api/blade-user/blockauth/remove', 'delete', 3, 2, 3, 3, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'blockauth_view', '查看', 'view', '/user/blockauth/view', 'file-text', 4, 2, 2, 2, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (0, 'category', '智能公寓管理', 'menu', '/store/category', NULL, 1, 1, 0, 1, NULL, 0);
set @parentid = (SELECT LAST_INSERT_ID());
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'category_add', '新增', 'add', '/store/category/add', 'plus', 1, 2, 1, 1, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'category_edit', '修改', 'edit', '/store/category/edit', 'form', 2, 2, 2, 2, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'category_delete', '删除', 'delete', '/api/blade-store/category/remove', 'delete', 3, 2, 3, 3, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'category_view', '查看', 'view', '/store/category/view', 'file-text', 4, 2, 2, 2, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (0, 'coupon', '智能公寓管理', 'menu', '/store/coupon', NULL, 1, 1, 0, 1, NULL, 0);
set @parentid = (SELECT LAST_INSERT_ID());
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'coupon_add', '新增', 'add', '/store/coupon/add', 'plus', 1, 2, 1, 1, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'coupon_edit', '修改', 'edit', '/store/coupon/edit', 'form', 2, 2, 2, 2, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'coupon_delete', '删除', 'delete', '/api/blade-store/coupon/remove', 'delete', 3, 2, 3, 3, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'coupon_view', '查看', 'view', '/store/coupon/view', 'file-text', 4, 2, 2, 2, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (0, 'customerservice', '智能公寓管理', 'menu', '/user/customerservice', NULL, 1, 1, 0, 1, NULL, 0);
set @parentid = (SELECT LAST_INSERT_ID());
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'customerservice_add', '新增', 'add', '/user/customerservice/add', 'plus', 1, 2, 1, 1, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'customerservice_edit', '修改', 'edit', '/user/customerservice/edit', 'form', 2, 2, 2, 2, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'customerservice_delete', '删除', 'delete', '/api/blade-user/customerservice/remove', 'delete', 3, 2, 3, 3, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'customerservice_view', '查看', 'view', '/user/customerservice/view', 'file-text', 4, 2, 2, 2, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (0, 'customerservicestaff', '智能公寓管理', 'menu', '/user/customerservicestaff', NULL, 1, 1, 0, 1, NULL, 0);
set @parentid = (SELECT LAST_INSERT_ID());
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'customerservicestaff_add', '新增', 'add', '/user/customerservicestaff/add', 'plus', 1, 2, 1, 1, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'customerservicestaff_edit', '修改', 'edit', '/user/customerservicestaff/edit', 'form', 2, 2, 2, 2, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'customerservicestaff_delete', '删除', 'delete', '/api/blade-user/customerservicestaff/remove', 'delete', 3, 2, 3, 3, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'customerservicestaff_view', '查看', 'view', '/user/customerservicestaff/view', 'file-text', 4, 2, 2, 2, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (0, 'equipment', '智能公寓管理', 'menu', '/user/equipment', NULL, 1, 1, 0, 1, NULL, 0);
set @parentid = (SELECT LAST_INSERT_ID());
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'equipment_add', '新增', 'add', '/user/equipment/add', 'plus', 1, 2, 1, 1, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'equipment_edit', '修改', 'edit', '/user/equipment/edit', 'form', 2, 2, 2, 2, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'equipment_delete', '删除', 'delete', '/api/blade-user/equipment/remove', 'delete', 3, 2, 3, 3, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'equipment_view', '查看', 'view', '/user/equipment/view', 'file-text', 4, 2, 2, 2, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (0, 'equipmentalarmlog', '智能公寓管理', 'menu', '/user/equipmentalarmlog', NULL, 1, 1, 0, 1, NULL, 0);
set @parentid = (SELECT LAST_INSERT_ID());
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'equipmentalarmlog_add', '新增', 'add', '/user/equipmentalarmlog/add', 'plus', 1, 2, 1, 1, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'equipmentalarmlog_edit', '修改', 'edit', '/user/equipmentalarmlog/edit', 'form', 2, 2, 2, 2, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'equipmentalarmlog_delete', '删除', 'delete', '/api/blade-user/equipmentalarmlog/remove', 'delete', 3, 2, 3, 3, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'equipmentalarmlog_view', '查看', 'view', '/user/equipmentalarmlog/view', 'file-text', 4, 2, 2, 2, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (0, 'equipmentopenlog', '智能公寓管理', 'menu', '/user/equipmentopenlog', NULL, 1, 1, 0, 1, NULL, 0);
set @parentid = (SELECT LAST_INSERT_ID());
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'equipmentopenlog_add', '新增', 'add', '/user/equipmentopenlog/add', 'plus', 1, 2, 1, 1, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'equipmentopenlog_edit', '修改', 'edit', '/user/equipmentopenlog/edit', 'form', 2, 2, 2, 2, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'equipmentopenlog_delete', '删除', 'delete', '/api/blade-user/equipmentopenlog/remove', 'delete', 3, 2, 3, 3, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'equipmentopenlog_view', '查看', 'view', '/user/equipmentopenlog/view', 'file-text', 4, 2, 2, 2, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (0, 'equipmentpwd', '智能公寓管理', 'menu', '/user/equipmentpwd', NULL, 1, 1, 0, 1, NULL, 0);
set @parentid = (SELECT LAST_INSERT_ID());
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'equipmentpwd_add', '新增', 'add', '/user/equipmentpwd/add', 'plus', 1, 2, 1, 1, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'equipmentpwd_edit', '修改', 'edit', '/user/equipmentpwd/edit', 'form', 2, 2, 2, 2, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'equipmentpwd_delete', '删除', 'delete', '/api/blade-user/equipmentpwd/remove', 'delete', 3, 2, 3, 3, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'equipmentpwd_view', '查看', 'view', '/user/equipmentpwd/view', 'file-text', 4, 2, 2, 2, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (0, 'equipmentrepair', '智能公寓管理', 'menu', '/user/equipmentrepair', NULL, 1, 1, 0, 1, NULL, 0);
set @parentid = (SELECT LAST_INSERT_ID());
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'equipmentrepair_add', '新增', 'add', '/user/equipmentrepair/add', 'plus', 1, 2, 1, 1, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'equipmentrepair_edit', '修改', 'edit', '/user/equipmentrepair/edit', 'form', 2, 2, 2, 2, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'equipmentrepair_delete', '删除', 'delete', '/api/blade-user/equipmentrepair/remove', 'delete', 3, 2, 3, 3, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'equipmentrepair_view', '查看', 'view', '/user/equipmentrepair/view', 'file-text', 4, 2, 2, 2, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (0, 'equipmentrepairresource', '智能公寓管理', 'menu', '/apartment/equipmentrepairresource', NULL, 1, 1, 0, 1, NULL, 0);
set @parentid = (SELECT LAST_INSERT_ID());
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'equipmentrepairresource_add', '新增', 'add', '/apartment/equipmentrepairresource/add', 'plus', 1, 2, 1, 1, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'equipmentrepairresource_edit', '修改', 'edit', '/apartment/equipmentrepairresource/edit', 'form', 2, 2, 2, 2, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'equipmentrepairresource_delete', '删除', 'delete', '/api/blade-apartment/equipmentrepairresource/remove', 'delete', 3, 2, 3, 3, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'equipmentrepairresource_view', '查看', 'view', '/apartment/equipmentrepairresource/view', 'file-text', 4, 2, 2, 2, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (0, 'equipmentreset', '智能公寓管理', 'menu', '/user/equipmentreset', NULL, 1, 1, 0, 1, NULL, 0);
set @parentid = (SELECT LAST_INSERT_ID());
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'equipmentreset_add', '新增', 'add', '/user/equipmentreset/add', 'plus', 1, 2, 1, 1, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'equipmentreset_edit', '修改', 'edit', '/user/equipmentreset/edit', 'form', 2, 2, 2, 2, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'equipmentreset_delete', '删除', 'delete', '/api/blade-user/equipmentreset/remove', 'delete', 3, 2, 3, 3, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'equipmentreset_view', '查看', 'view', '/user/equipmentreset/view', 'file-text', 4, 2, 2, 2, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (0, 'equipmenttype', '智能公寓管理', 'menu', '/user/equipmenttype', NULL, 1, 1, 0, 1, NULL, 0);
set @parentid = (SELECT LAST_INSERT_ID());
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'equipmenttype_add', '新增', 'add', '/user/equipmenttype/add', 'plus', 1, 2, 1, 1, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'equipmenttype_edit', '修改', 'edit', '/user/equipmenttype/edit', 'form', 2, 2, 2, 2, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'equipmenttype_delete', '删除', 'delete', '/api/blade-user/equipmenttype/remove', 'delete', 3, 2, 3, 3, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'equipmenttype_view', '查看', 'view', '/user/equipmenttype/view', 'file-text', 4, 2, 2, 2, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (0, 'iaorder', '智能公寓管理', 'menu', '/apartment/iaorder', NULL, 1, 1, 0, 1, NULL, 0);
set @parentid = (SELECT LAST_INSERT_ID());
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'iaorder_add', '新增', 'add', '/apartment/iaorder/add', 'plus', 1, 2, 1, 1, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'iaorder_edit', '修改', 'edit', '/apartment/iaorder/edit', 'form', 2, 2, 2, 2, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'iaorder_delete', '删除', 'delete', '/api/blade-apartment/iaorder/remove', 'delete', 3, 2, 3, 3, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'iaorder_view', '查看', 'view', '/apartment/iaorder/view', 'file-text', 4, 2, 2, 2, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (0, 'iaorderlog', '智能公寓管理', 'menu', '/apartment/iaorderlog', NULL, 1, 1, 0, 1, NULL, 0);
set @parentid = (SELECT LAST_INSERT_ID());
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'iaorderlog_add', '新增', 'add', '/apartment/iaorderlog/add', 'plus', 1, 2, 1, 1, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'iaorderlog_edit', '修改', 'edit', '/apartment/iaorderlog/edit', 'form', 2, 2, 2, 2, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'iaorderlog_delete', '删除', 'delete', '/api/blade-apartment/iaorderlog/remove', 'delete', 3, 2, 3, 3, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'iaorderlog_view', '查看', 'view', '/apartment/iaorderlog/view', 'file-text', 4, 2, 2, 2, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (0, 'iaordertenant', '智能公寓管理', 'menu', '/apartment/iaordertenant', NULL, 1, 1, 0, 1, NULL, 0);
set @parentid = (SELECT LAST_INSERT_ID());
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'iaordertenant_add', '新增', 'add', '/apartment/iaordertenant/add', 'plus', 1, 2, 1, 1, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'iaordertenant_edit', '修改', 'edit', '/apartment/iaordertenant/edit', 'form', 2, 2, 2, 2, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'iaordertenant_delete', '删除', 'delete', '/api/blade-apartment/iaordertenant/remove', 'delete', 3, 2, 3, 3, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'iaordertenant_view', '查看', 'view', '/apartment/iaordertenant/view', 'file-text', 4, 2, 2, 2, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (0, 'lockauth', '智能公寓管理', 'menu', '/user/lockauth', NULL, 1, 1, 0, 1, NULL, 0);
set @parentid = (SELECT LAST_INSERT_ID());
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'lockauth_add', '新增', 'add', '/user/lockauth/add', 'plus', 1, 2, 1, 1, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'lockauth_edit', '修改', 'edit', '/user/lockauth/edit', 'form', 2, 2, 2, 2, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'lockauth_delete', '删除', 'delete', '/api/blade-user/lockauth/remove', 'delete', 3, 2, 3, 3, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'lockauth_view', '查看', 'view', '/user/lockauth/view', 'file-text', 4, 2, 2, 2, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (0, 'lockmq', '智能公寓管理', 'menu', '/user/lockmq', NULL, 1, 1, 0, 1, NULL, 0);
set @parentid = (SELECT LAST_INSERT_ID());
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'lockmq_add', '新增', 'add', '/user/lockmq/add', 'plus', 1, 2, 1, 1, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'lockmq_edit', '修改', 'edit', '/user/lockmq/edit', 'form', 2, 2, 2, 2, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'lockmq_delete', '删除', 'delete', '/api/blade-user/lockmq/remove', 'delete', 3, 2, 3, 3, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'lockmq_view', '查看', 'view', '/user/lockmq/view', 'file-text', 4, 2, 2, 2, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (0, 'nokeybackmsg', '智能公寓管理', 'menu', '/user/nokeybackmsg', NULL, 1, 1, 0, 1, NULL, 0);
set @parentid = (SELECT LAST_INSERT_ID());
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'nokeybackmsg_add', '新增', 'add', '/user/nokeybackmsg/add', 'plus', 1, 2, 1, 1, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'nokeybackmsg_edit', '修改', 'edit', '/user/nokeybackmsg/edit', 'form', 2, 2, 2, 2, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'nokeybackmsg_delete', '删除', 'delete', '/api/blade-user/nokeybackmsg/remove', 'delete', 3, 2, 3, 3, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'nokeybackmsg_view', '查看', 'view', '/user/nokeybackmsg/view', 'file-text', 4, 2, 2, 2, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (0, 'noticemessage', '智能公寓管理', 'menu', '/user/noticemessage', NULL, 1, 1, 0, 1, NULL, 0);
set @parentid = (SELECT LAST_INSERT_ID());
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'noticemessage_add', '新增', 'add', '/user/noticemessage/add', 'plus', 1, 2, 1, 1, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'noticemessage_edit', '修改', 'edit', '/user/noticemessage/edit', 'form', 2, 2, 2, 2, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'noticemessage_delete', '删除', 'delete', '/api/blade-user/noticemessage/remove', 'delete', 3, 2, 3, 3, NULL, 0);
INSERT INTO `blade_menu`(`parent_id`, `code`, `name`, `alias`, `path`, `source`, `sort`, `category`, `action`, `is_open`, `remark`, `is_deleted`)
VALUES (@parentid, 'noticemessage_view', '查看', 'view', '/user/noticemessage/view', 'file-text', 4, 2, 2, 2, NULL, 0);
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!