diff --git a/.gitignore b/.gitignore index a1c2a23..6b09122 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,314 @@ # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* + +# Created by https://www.toptal.com/developers/gitignore/api/java,maven,vuejs,node,jetbrains +# Edit at https://www.toptal.com/developers/gitignore?templates=java,maven,vuejs,node,jetbrains + +### Java ### +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* +replay_pid* + +### JetBrains ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### JetBrains Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +# https://plugins.jetbrains.com/plugin/7973-sonarlint +.idea/**/sonarlint/ + +# SonarQube Plugin +# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin +.idea/**/sonarIssues.xml + +# Markdown Navigator plugin +# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced +.idea/**/markdown-navigator.xml +.idea/**/markdown-navigator-enh.xml +.idea/**/markdown-navigator/ + +# Cache file creation bug +# See https://youtrack.jetbrains.com/issue/JBR-2257 +.idea/$CACHE_FILE$ + +# CodeStream plugin +# https://plugins.jetbrains.com/plugin/12206-codestream +.idea/codestream.xml + +# Azure Toolkit for IntelliJ plugin +# https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij +.idea/**/azureSettings.xml + +### Maven ### +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties +# https://github.com/takari/maven-wrapper#usage-without-binary-jar +.mvn/wrapper/maven-wrapper.jar + +# Eclipse m2e generated files +# Eclipse Core +.project +# JDT-specific (Eclipse Java Development Tools) +.classpath + +### Node ### +# Logs +logs +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +### Node Patch ### +# Serverless Webpack directories +.webpack/ + +# Optional stylelint cache + +# SvelteKit build / generate output +.svelte-kit + +### Vuejs ### +# Recommended template: Node.gitignore + +dist/ +npm-debug.log +yarn-error.log + +# End of https://www.toptal.com/developers/gitignore/api/java,maven,vuejs,node,jetbrains + +.idea/ \ No newline at end of file diff --git a/joint-cloud-auth/pom.xml b/joint-cloud-auth/pom.xml new file mode 100644 index 0000000..a21b4a0 --- /dev/null +++ b/joint-cloud-auth/pom.xml @@ -0,0 +1,95 @@ + + + + joint-cloud + com.joint.cloud + 1.0.0-SNAPSHOT + + 4.0.0 + + joint-cloud-auth + joint-cloud-auth + jar + + + 8 + 8 + + + + + com.alibaba + druid + + + + org.postgresql + postgresql + runtime + + + org.springframework.boot + spring-boot-starter-jdbc + + + org.springframework.boot + spring-boot-starter-data-redis + + + org.springframework.cloud + spring-cloud-starter-oauth2 + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.cloud + spring-cloud-starter-consul-discovery + + + org.springframework.cloud + spring-cloud-starter-netflix-ribbon + + + org.springframework.cloud + spring-cloud-starter-netflix-zuul + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + com.alibaba + fastjson + + + com.auth0 + java-jwt + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + \ No newline at end of file diff --git a/joint-cloud-auth/src/main/java/com/joint/cloud/auth/AuthServerApplication.java b/joint-cloud-auth/src/main/java/com/joint/cloud/auth/AuthServerApplication.java new file mode 100644 index 0000000..2aee75c --- /dev/null +++ b/joint-cloud-auth/src/main/java/com/joint/cloud/auth/AuthServerApplication.java @@ -0,0 +1,14 @@ +package com.joint.cloud.auth; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; + +@SpringBootApplication +@EnableDiscoveryClient +public class AuthServerApplication { + + public static void main(String[] args) { + SpringApplication.run(AuthServerApplication.class, args); + } +} diff --git a/joint-cloud-auth/src/main/java/com/joint/cloud/auth/config/AccessCheckRedisConfig.java b/joint-cloud-auth/src/main/java/com/joint/cloud/auth/config/AccessCheckRedisConfig.java new file mode 100644 index 0000000..af2a661 --- /dev/null +++ b/joint-cloud-auth/src/main/java/com/joint/cloud/auth/config/AccessCheckRedisConfig.java @@ -0,0 +1,85 @@ +package com.joint.cloud.auth.config; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.parser.Feature; +import com.alibaba.fastjson.serializer.SerializerFeature; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.listener.RedisMessageListenerContainer; +import org.springframework.data.redis.serializer.RedisSerializer; +import org.springframework.data.redis.serializer.SerializationException; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; + +@Configuration +public class AccessCheckRedisConfig { + + @Autowired + RedisConnectionFactory redisConnectionFactory; + + @Bean({"licenseRedisTemplate"}) + public RedisTemplate redisTemplate() { + //配置项目应该与license信息保持一致 + final RedisTemplate redisTemplate = new RedisTemplate(); + redisTemplate.setConnectionFactory(redisConnectionFactory); + + //key采用String序列化方式 + StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); + redisTemplate.setKeySerializer(stringRedisSerializer); + redisTemplate.setHashKeySerializer(stringRedisSerializer); + + //value采用fast-json序列化方式。 + RedisSerializer fastJson2JsonRedisSerializer = fastJson2JsonRedisSerializer(); + redisTemplate.setValueSerializer(fastJson2JsonRedisSerializer); + redisTemplate.setHashValueSerializer(fastJson2JsonRedisSerializer); + + redisTemplate.afterPropertiesSet(); + return redisTemplate; + } + + @Bean + public RedisMessageListenerContainer redisMessageListenerContainer() { + RedisMessageListenerContainer container = new RedisMessageListenerContainer(); + container.setConnectionFactory(redisConnectionFactory); + return container; + } + + private RedisSerializer fastJson2JsonRedisSerializer() { + return new FastJson2JsonRedisSerializer<>(Object.class); + } + + static class FastJson2JsonRedisSerializer implements RedisSerializer { + + public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8; + + private Class clazz; + + public FastJson2JsonRedisSerializer(Class clazz) { + super(); + this.clazz = clazz; + } + + @Override + public byte[] serialize(T t) throws SerializationException { + if (t == null) { + return new byte[0]; + } + return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET); + } + + @Override + public T deserialize(byte[] bytes) throws SerializationException { + if (bytes == null || bytes.length <= 0) { + return null; + } + String str = new String(bytes, DEFAULT_CHARSET); + return JSON.parseObject(str, clazz, Feature.SupportAutoType); + } + + } +} diff --git a/joint-cloud-auth/src/main/java/com/joint/cloud/auth/config/AuthenticationManagerConfig.java b/joint-cloud-auth/src/main/java/com/joint/cloud/auth/config/AuthenticationManagerConfig.java new file mode 100644 index 0000000..f919327 --- /dev/null +++ b/joint-cloud-auth/src/main/java/com/joint/cloud/auth/config/AuthenticationManagerConfig.java @@ -0,0 +1,24 @@ +package com.joint.cloud.auth.config; + +import com.joint.cloud.auth.config.custom.CustomAuthenticationProvider; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.authentication.configuration.GlobalAuthenticationConfigurerAdapter; + +/** + * @author keets + * @date 2017/9/25 + */ +@Configuration +public class AuthenticationManagerConfig extends GlobalAuthenticationConfigurerAdapter { + + @Autowired + CustomAuthenticationProvider customAuthenticationProvider; + + @Override + public void configure(AuthenticationManagerBuilder auth) throws Exception { + auth.authenticationProvider(customAuthenticationProvider); + } + +} \ No newline at end of file diff --git a/joint-cloud-auth/src/main/java/com/joint/cloud/auth/config/AuthorizationServerConfiguration.java b/joint-cloud-auth/src/main/java/com/joint/cloud/auth/config/AuthorizationServerConfiguration.java new file mode 100644 index 0000000..0b5a240 --- /dev/null +++ b/joint-cloud-auth/src/main/java/com/joint/cloud/auth/config/AuthorizationServerConfiguration.java @@ -0,0 +1,100 @@ +package com.joint.cloud.auth.config; + +import com.joint.cloud.auth.config.custom.CustomJwtAccessTokenConverter; +import com.joint.cloud.auth.config.custom.CustomRedisTokenStore; +import com.joint.cloud.auth.config.impl.ClientDetailsServiceImpl; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.security.authentication.AuthenticationManager; +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.ClientDetailsService; +import org.springframework.security.oauth2.provider.token.TokenStore; +import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter; + + +/** + * @description: + * @author: super.wu + * @date: Created in 2018/5/4 0004 + * @modified By: + * @version: 1.0 + **/ +@Configuration +@EnableAuthorizationServer +public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter { + + /** + * 认证管理器,当你选择了资源所有者密码(password)授权类型的时候, + * 请设置这个属性注入一个 AuthenticationManager 对象 + */ + @Autowired + @Qualifier("authenticationManagerBean") + private AuthenticationManager authenticationManager; + + + @Autowired + RedisConnectionFactory redisConnectionFactory; + + /** + * 配置令牌端点(Token Endpoint)的安全约束. + * @param security + * @throws Exception + */ + @Override + public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { + security.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()"); + } + + /** + * 配置客户端详情服务(ClientDetailsService),客户端详情信息在这里进行初始化,你能够把客户端详情信息写死在这里或者是通过数据库来存储调取详情信息 + * @param clients + * @throws Exception + */ + @Override + public void configure(ClientDetailsServiceConfigurer clients) throws Exception { + clients.withClientDetails(getClientDetailsService()); + } + + /** + * 配置授权(authorization)以及令牌(token)的访问端点和令牌服务(token services) + * @param endpoints + * @throws Exception + */ + @Override + public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { + endpoints.authenticationManager(authenticationManager) + .tokenStore(tokenStore()) + .accessTokenConverter(accessTokenConverter()); + } + + + /** + * 使用Jwt的方式生成token + * @return + */ + @Bean + public JwtAccessTokenConverter accessTokenConverter() { + CustomJwtAccessTokenConverter converter = new CustomJwtAccessTokenConverter(); + converter.setSigningKey("secret"); + return converter; + } + + + @Bean + public TokenStore tokenStore() { + return new CustomRedisTokenStore(redisConnectionFactory); + } + + @Bean + public ClientDetailsService getClientDetailsService() { + return new ClientDetailsServiceImpl(); + } + +} \ No newline at end of file diff --git a/joint-cloud-auth/src/main/java/com/joint/cloud/auth/config/ResourceServerConfig.java b/joint-cloud-auth/src/main/java/com/joint/cloud/auth/config/ResourceServerConfig.java new file mode 100644 index 0000000..1307028 --- /dev/null +++ b/joint-cloud-auth/src/main/java/com/joint/cloud/auth/config/ResourceServerConfig.java @@ -0,0 +1,37 @@ +package com.joint.cloud.auth.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; +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 javax.servlet.http.HttpServletResponse; + +/** + * @description: + * @author: super.wu + * @date: Created in 2018/5/25 0025 + * @modified By: + * @version: 1.0 + **/ +@Configuration +@EnableResourceServer +public class ResourceServerConfig extends ResourceServerConfigurerAdapter { + + @Override + public void configure(HttpSecurity http) throws Exception { + http + .csrf().disable() + .exceptionHandling() + .authenticationEntryPoint((request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED)) + .and() + .authorizeRequests() + .antMatchers(HttpMethod.GET,"/health/**").permitAll() + .and() + .authorizeRequests() + .anyRequest().authenticated() + .and() + .httpBasic(); + } +} diff --git a/joint-cloud-auth/src/main/java/com/joint/cloud/auth/config/WebLogAspect.java b/joint-cloud-auth/src/main/java/com/joint/cloud/auth/config/WebLogAspect.java new file mode 100644 index 0000000..0867cc0 --- /dev/null +++ b/joint-cloud-auth/src/main/java/com/joint/cloud/auth/config/WebLogAspect.java @@ -0,0 +1,53 @@ +package com.joint.cloud.auth.config; + +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.annotation.Pointcut; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; + +@Aspect +@Component +public class WebLogAspect { + + private final Logger logger = LoggerFactory.getLogger("apiLogger"); + + @Pointcut("execution(public * com.joint.cloud.auth.controller..*.*(..))")//切入点描述 这个是controller包的切入点 + public void controllerLog(){}//签名,可以理解成这个切入点的一个名称 + +// @Pointcut("execution(public * com.stuPayment.uiController..*.*(..))")//切入点描述,这个是uiController包的切入点 +// public void uiControllerLog(){} + + @Before("controllerLog()") //在切入点的方法run之前要干的 + public void logBeforeController(JoinPoint joinPoint) { + + RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();//这个RequestContextHolder是Springmvc提供来获得请求的东西 + HttpServletRequest request = ((ServletRequestAttributes)requestAttributes).getRequest(); + + // 过滤 health check + if ("com.joint.cloud.auth.controller.HealthController".equals(joinPoint.getSignature().getDeclaringTypeName())) { + return; + } + + // 记录下请求内容 + logger.info("### URL : " + request.getRequestURL().toString()); + logger.info("### HTTP_METHOD : " + request.getMethod()); + logger.info("### IP : " + request.getRemoteAddr()); +// logger.info("################THE ARGS OF THE CONTROLLER : " + Arrays.toString(joinPoint.getArgs())); + + //下面这个getSignature().getDeclaringTypeName()是获取包+类名的 然后后面的joinPoint.getSignature.getName()获取了方法名 + logger.info("### CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName()); + //logger.info("################TARGET: " + joinPoint.getTarget());//返回的是需要加强的目标类的对象 + //logger.info("################THIS: " + joinPoint.getThis());//返回的是经过加强后的代理类的对象 + + } + + +} \ No newline at end of file diff --git a/joint-cloud-auth/src/main/java/com/joint/cloud/auth/config/WebSecurityConfiguration.java b/joint-cloud-auth/src/main/java/com/joint/cloud/auth/config/WebSecurityConfiguration.java new file mode 100644 index 0000000..508eeaf --- /dev/null +++ b/joint-cloud-auth/src/main/java/com/joint/cloud/auth/config/WebSecurityConfiguration.java @@ -0,0 +1,47 @@ +package com.joint.cloud.auth.config; + +import com.joint.cloud.auth.config.encoder.MyPasswordEncoder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.crypto.password.PasswordEncoder; + + +/** + * @description: + * @author: super.wu + * @date: Created in 2018/5/4 0004 + * @modified By: + * @version: 1.0 + **/ +@Configuration +@EnableWebSecurity +public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { + + /** + * 设置获取token的url + * @param http + * @throws Exception + */ + @Override + protected void configure(HttpSecurity http) throws Exception { + http.authorizeRequests().antMatchers(HttpMethod.OPTIONS).permitAll().anyRequest().authenticated().and() + .httpBasic().and().csrf().disable(); + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new MyPasswordEncoder(); + } + + @Override + @Bean + public AuthenticationManager authenticationManagerBean() throws Exception { + return super.authenticationManagerBean(); + } + +} \ No newline at end of file diff --git a/joint-cloud-auth/src/main/java/com/joint/cloud/auth/config/custom/CustomAuthenticationKeyGenerator.java b/joint-cloud-auth/src/main/java/com/joint/cloud/auth/config/custom/CustomAuthenticationKeyGenerator.java new file mode 100644 index 0000000..ae51807 --- /dev/null +++ b/joint-cloud-auth/src/main/java/com/joint/cloud/auth/config/custom/CustomAuthenticationKeyGenerator.java @@ -0,0 +1,65 @@ +package com.joint.cloud.auth.config.custom; + +import org.springframework.security.oauth2.common.util.OAuth2Utils; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.security.oauth2.provider.OAuth2Request; +import org.springframework.security.oauth2.provider.token.AuthenticationKeyGenerator; + +import java.io.UnsupportedEncodingException; +import java.math.BigInteger; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.TreeSet; + +/** + * @author lilengyi + * @date 2020/8/11 16:17 + */ +public class CustomAuthenticationKeyGenerator implements AuthenticationKeyGenerator { + private static final String CLIENT_ID = "client_id"; + private static final String SCOPE = "scope"; + private static final String USERNAME = "username"; + + public CustomAuthenticationKeyGenerator() { + } + + @Override + public String extractKey(OAuth2Authentication authentication) { + Map values = new LinkedHashMap(); + OAuth2Request authorizationRequest = authentication.getOAuth2Request(); + if (!authentication.isClientOnly()) { + values.put("username", authentication.getName()); + } + + values.put("client_id", authorizationRequest.getClientId()); + if (authorizationRequest.getScope() != null) { + values.put("scope", OAuth2Utils.formatParameterList(new TreeSet(authorizationRequest.getScope()))); + } + + CustomUserDetails userDetails = (CustomUserDetails) authentication.getUserAuthentication().getPrincipal(); + if (null != userDetails.getUserId()) { + values.put("userId", userDetails.getUserId()); + } + + Map parameters = authorizationRequest.getRequestParameters(); + if (null != parameters && parameters.containsKey("client")) { + values.put("client", parameters.get("client")); + } + + return this.generateKey(values); + } + + protected String generateKey(Map values) { + try { + MessageDigest digest = MessageDigest.getInstance("MD5"); + byte[] bytes = digest.digest(values.toString().getBytes("UTF-8")); + return String.format("%032x", new BigInteger(1, bytes)); + } catch (NoSuchAlgorithmException var4) { + throw new IllegalStateException("MD5 algorithm not available. Fatal (should be in the JDK).", var4); + } catch (UnsupportedEncodingException var5) { + throw new IllegalStateException("UTF-8 encoding not available. Fatal (should be in the JDK).", var5); + } + } +} diff --git a/joint-cloud-auth/src/main/java/com/joint/cloud/auth/config/custom/CustomAuthenticationProvider.java b/joint-cloud-auth/src/main/java/com/joint/cloud/auth/config/custom/CustomAuthenticationProvider.java new file mode 100644 index 0000000..7e7f4e0 --- /dev/null +++ b/joint-cloud-auth/src/main/java/com/joint/cloud/auth/config/custom/CustomAuthenticationProvider.java @@ -0,0 +1,102 @@ +package com.joint.cloud.auth.config.custom; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.HashOperations; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.oauth2.provider.NoSuchClientException; +import org.springframework.stereotype.Component; +import org.springframework.util.Assert; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; +import java.util.Map; + +/** + * 认证管理 + * + * @author keets + * @date 2017/8/5 + */ +@Component +public class CustomAuthenticationProvider implements AuthenticationProvider { + + @Autowired + private UserDetailsService userDetailsService; + + @Resource(name = "licenseRedisTemplate") + private RedisTemplate redisTemplate; + + private HashOperations hashOperations; + + @PostConstruct + private void init() { + hashOperations = redisTemplate.opsForHash(); + } + + private static final String CMP_LICENSE_CHECK = "CMP_ACCESS_CHECK"; + private final static String LICENSE_STATUS = "LICENSE_STATUS"; + private final static String LICENSE_INVALID = "LICENSE_INVALID"; + + @Override + public Authentication authenticate(Authentication authentication) throws AuthenticationException { + String username = authentication.getName(); + + Map data; + if (authentication.getDetails() instanceof Map) { + data = (Map) authentication.getDetails(); + } else { + return null; + } + String clientId = (String) data.get("client"); + Assert.hasText(clientId, "clientId must have value"); + + String password = (String) authentication.getCredentials(); + try { + userDetailsService.loadUserByUsername(username); + } catch (NoSuchClientException | UsernameNotFoundException e) { + throw new BadCredentialsException("用户名错误"); + } + + CustomUserDetails customUserDetails = checkUsernameAndPassword(username, password); + if (customUserDetails == null) { + throw new BadCredentialsException("用户名或密码错误"); + } + + /** + * 如果是非超级管理员 && 证书过期,不允许登录 + * 如果是超级管理员 && 证书过期,允许登录,只访问许可证书菜单 + */ + String licenseStatus = this.getCheckStatus(LICENSE_STATUS); +// if(!StringUtils.hasLength(licenseStatus)){ +// throw new LicenseStatusException("后台服务未启动,请稍后重试"); +// } else if (LICENSE_INVALID.equals(licenseStatus) && !customUserDetails.getRoles().contains("SUPER_ADMIN_ROLE")) { +// throw new LicenseStatusException("许可证书已过期"); +// } + + customUserDetails.setClientId(clientId); + return new CustomAuthenticationToken(customUserDetails); + } + + private CustomUserDetails checkUsernameAndPassword(String username, String password) { + CustomUserDetails userDetails = (CustomUserDetails) userDetailsService.loadUserByUsername(username); + if (userDetails == null || !userDetails.getPassword().equals(password)) { + return null; + } + return userDetails; + } + + @Override + public boolean supports(Class aClass) { + return true; + } + + private String getCheckStatus(String id) { + return (String) hashOperations.get(CMP_LICENSE_CHECK, id); + } +} \ No newline at end of file diff --git a/joint-cloud-auth/src/main/java/com/joint/cloud/auth/config/custom/CustomAuthenticationToken.java b/joint-cloud-auth/src/main/java/com/joint/cloud/auth/config/custom/CustomAuthenticationToken.java new file mode 100644 index 0000000..2461a55 --- /dev/null +++ b/joint-cloud-auth/src/main/java/com/joint/cloud/auth/config/custom/CustomAuthenticationToken.java @@ -0,0 +1,28 @@ +package com.joint.cloud.auth.config.custom; + +import org.springframework.security.authentication.AbstractAuthenticationToken; + +/** + * Created by keets on 2017/8/5. + */ +public class CustomAuthenticationToken extends AbstractAuthenticationToken { + + private CustomUserDetails userDetails; + + public CustomAuthenticationToken(CustomUserDetails userDetails) { + super(null); + this.userDetails = userDetails; + super.setAuthenticated(true); + } + + @Override + public Object getPrincipal() { + return this.userDetails; + } + + @Override + public Object getCredentials() { + return this.userDetails.getPassword(); + } + +} \ No newline at end of file diff --git a/joint-cloud-auth/src/main/java/com/joint/cloud/auth/config/custom/CustomJwtAccessTokenConverter.java b/joint-cloud-auth/src/main/java/com/joint/cloud/auth/config/custom/CustomJwtAccessTokenConverter.java new file mode 100644 index 0000000..0ecfbf8 --- /dev/null +++ b/joint-cloud-auth/src/main/java/com/joint/cloud/auth/config/custom/CustomJwtAccessTokenConverter.java @@ -0,0 +1,44 @@ +package com.joint.cloud.auth.config.custom; + +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.store.JwtAccessTokenConverter; + +import java.util.HashMap; +import java.util.Map; + +/** + * @description: 自定义token生成 + * @author: super.wu + * @date: Created in 2018/5/7 0007 + * @modified By: + * @version: 1.0 + **/ +public class CustomJwtAccessTokenConverter extends JwtAccessTokenConverter { + + private static final int authenticateCodeExpiresTime = 10 * 60; + + private static final String TOKEN_SEG_USER_ID = "X-AOHO-UserId"; + private static final String TOKEN_SEG_CLIENT = "X-AOHO-ClientId"; + + @Override + public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) { + CustomUserDetails userDetails = (CustomUserDetails) authentication.getUserAuthentication().getPrincipal(); + + authentication.getUserAuthentication().getPrincipal(); + Map info = new HashMap(2) {{ + put(TOKEN_SEG_USER_ID, userDetails.getUserId()); + put("roles", String.join(",", userDetails.getRoles())); + put("name",userDetails.getName()); + }}; + + DefaultOAuth2AccessToken customAccessToken = new DefaultOAuth2AccessToken(accessToken); + customAccessToken.setAdditionalInformation(info); + + OAuth2AccessToken enhancedToken = super.enhance(customAccessToken, authentication); + enhancedToken.getAdditionalInformation().put(TOKEN_SEG_CLIENT, userDetails.getClientId()); + return enhancedToken; + } + +} \ No newline at end of file diff --git a/joint-cloud-auth/src/main/java/com/joint/cloud/auth/config/custom/CustomRedisTokenStore.java b/joint-cloud-auth/src/main/java/com/joint/cloud/auth/config/custom/CustomRedisTokenStore.java new file mode 100644 index 0000000..7e97990 --- /dev/null +++ b/joint-cloud-auth/src/main/java/com/joint/cloud/auth/config/custom/CustomRedisTokenStore.java @@ -0,0 +1,396 @@ +package com.joint.cloud.auth.config.custom; + +import org.springframework.data.redis.connection.RedisConnection; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.security.oauth2.common.ExpiringOAuth2RefreshToken; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.security.oauth2.common.OAuth2RefreshToken; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.security.oauth2.provider.token.AuthenticationKeyGenerator; +import org.springframework.security.oauth2.provider.token.TokenStore; +import org.springframework.security.oauth2.provider.token.store.redis.JdkSerializationStrategy; +import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStoreSerializationStrategy; +import org.springframework.stereotype.Component; + +import java.util.*; + +/** + * 重写tokenStore .因为最新版中RedisTokenStore的set已经被弃用了, + * 所以我就只能自定义一个,代码和RedisTokenStore一样, + * 只是把所有conn.set(…)都换成conn..stringCommands().set(…), + *
+ * + * @author fb
+ * @version 1.0
+ * @taskId
+ * @CreateDate Create in 10:59 2018/2/13 + * @since V1.0
+ */ +@Component +public class CustomRedisTokenStore implements TokenStore { + private static final String ACCESS = "access:"; + private static final String AUTH_TO_ACCESS = "auth_to_access:"; + private static final String AUTH = "auth:"; + private static final String REFRESH_AUTH = "refresh_auth:"; + private static final String ACCESS_TO_REFRESH = "access_to_refresh:"; + private static final String REFRESH = "refresh:"; + private static final String REFRESH_TO_ACCESS = "refresh_to_access:"; + private static final String CLIENT_ID_TO_ACCESS = "client_id_to_access:"; + private static final String UNAME_TO_ACCESS = "uname_to_access:"; + private final RedisConnectionFactory connectionFactory; + private AuthenticationKeyGenerator authenticationKeyGenerator = new CustomAuthenticationKeyGenerator(); + private RedisTokenStoreSerializationStrategy serializationStrategy = new JdkSerializationStrategy(); + private String prefix = ""; + + public CustomRedisTokenStore(RedisConnectionFactory connectionFactory) { + this.connectionFactory = connectionFactory; + } + + public void setAuthenticationKeyGenerator(AuthenticationKeyGenerator authenticationKeyGenerator) { + this.authenticationKeyGenerator = authenticationKeyGenerator; + } + + public void setSerializationStrategy(RedisTokenStoreSerializationStrategy serializationStrategy) { + this.serializationStrategy = serializationStrategy; + } + + public void setPrefix(String prefix) { + this.prefix = prefix; + } + + private RedisConnection getConnection() { + return this.connectionFactory.getConnection(); + } + + private byte[] serialize(Object object) { + return this.serializationStrategy.serialize(object); + } + + private byte[] serializeKey(String object) { + return this.serialize(this.prefix + object); + } + + private OAuth2AccessToken deserializeAccessToken(byte[] bytes) { + return (OAuth2AccessToken)this.serializationStrategy.deserialize(bytes, OAuth2AccessToken.class); + } + + private OAuth2Authentication deserializeAuthentication(byte[] bytes) { + return (OAuth2Authentication)this.serializationStrategy.deserialize(bytes, OAuth2Authentication.class); + } + + private OAuth2RefreshToken deserializeRefreshToken(byte[] bytes) { + return (OAuth2RefreshToken)this.serializationStrategy.deserialize(bytes, OAuth2RefreshToken.class); + } + + private byte[] serialize(String string) { + return this.serializationStrategy.serialize(string); + } + + private String deserializeString(byte[] bytes) { + return this.serializationStrategy.deserializeString(bytes); + } + + @Override + public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) { + String key = this.authenticationKeyGenerator.extractKey(authentication); + byte[] serializedKey = this.serializeKey(AUTH_TO_ACCESS + key); + byte[] bytes = null; + RedisConnection conn = this.getConnection(); + try { + bytes = conn.get(serializedKey); + } finally { + conn.close(); + } + OAuth2AccessToken accessToken = this.deserializeAccessToken(bytes); + if (accessToken != null) { + OAuth2Authentication storedAuthentication = this.readAuthentication(accessToken.getValue()); + if (storedAuthentication == null || !key.equals(this.authenticationKeyGenerator.extractKey(storedAuthentication))) { + this.storeAccessToken(accessToken, authentication); + } + } + return accessToken; + } + + @Override + public OAuth2Authentication readAuthentication(OAuth2AccessToken token) { + return this.readAuthentication(token.getValue()); + } + + @Override + public OAuth2Authentication readAuthentication(String token) { + byte[] bytes = null; + RedisConnection conn = this.getConnection(); + try { + bytes = conn.get(this.serializeKey("auth:" + token)); + } finally { + conn.close(); + } + OAuth2Authentication auth = this.deserializeAuthentication(bytes); + return auth; + } + + @Override + public OAuth2Authentication readAuthenticationForRefreshToken(OAuth2RefreshToken token) { + return this.readAuthenticationForRefreshToken(token.getValue()); + } + + public OAuth2Authentication readAuthenticationForRefreshToken(String token) { + RedisConnection conn = getConnection(); + try { + byte[] bytes = conn.get(serializeKey(REFRESH_AUTH + token)); + OAuth2Authentication auth = deserializeAuthentication(bytes); + return auth; + } finally { + conn.close(); + } + } + + @Override + public void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) { + byte[] serializedAccessToken = serialize(token); + byte[] serializedAuth = serialize(authentication); + byte[] accessKey = serializeKey(ACCESS + token.getValue()); + byte[] authKey = serializeKey(AUTH + token.getValue()); + byte[] authToAccessKey = serializeKey(AUTH_TO_ACCESS + authenticationKeyGenerator.extractKey(authentication)); + byte[] approvalKey = serializeKey(UNAME_TO_ACCESS + getApprovalKey(authentication)); + byte[] clientId = serializeKey(CLIENT_ID_TO_ACCESS + authentication.getOAuth2Request().getClientId()); + + RedisConnection conn = getConnection(); + try { + conn.openPipeline(); + conn.stringCommands().set(accessKey, serializedAccessToken); + conn.stringCommands().set(authKey, serializedAuth); + conn.stringCommands().set(authToAccessKey, serializedAccessToken); + if (!authentication.isClientOnly()) { + conn.rPush(approvalKey, serializedAccessToken); + } + conn.rPush(clientId, serializedAccessToken); + if (token.getExpiration() != null) { + int seconds = token.getExpiresIn(); + conn.expire(accessKey, seconds); + conn.expire(authKey, seconds); + conn.expire(authToAccessKey, seconds); + conn.expire(clientId, seconds); + conn.expire(approvalKey, seconds); + } + OAuth2RefreshToken refreshToken = token.getRefreshToken(); + if (refreshToken != null && refreshToken.getValue() != null) { + byte[] refresh = serialize(token.getRefreshToken().getValue()); + byte[] auth = serialize(token.getValue()); + byte[] refreshToAccessKey = serializeKey(REFRESH_TO_ACCESS + token.getRefreshToken().getValue()); + conn.stringCommands().set(refreshToAccessKey, auth); + byte[] accessToRefreshKey = serializeKey(ACCESS_TO_REFRESH + token.getValue()); + conn.stringCommands().set(accessToRefreshKey, refresh); + if (refreshToken instanceof ExpiringOAuth2RefreshToken) { + ExpiringOAuth2RefreshToken expiringRefreshToken = (ExpiringOAuth2RefreshToken) refreshToken; + Date expiration = expiringRefreshToken.getExpiration(); + if (expiration != null) { + int seconds = Long.valueOf((expiration.getTime() - System.currentTimeMillis()) / 1000L) + .intValue(); + conn.expire(refreshToAccessKey, seconds); + conn.expire(accessToRefreshKey, seconds); + } + } + } + conn.closePipeline(); + } finally { + conn.close(); + } + } + + private static String getApprovalKey(OAuth2Authentication authentication) { + String userName = authentication.getUserAuthentication() == null ? "": authentication.getUserAuthentication().getName(); + return getApprovalKey(authentication.getOAuth2Request().getClientId(), userName); + } + + private static String getApprovalKey(String clientId, String userName) { + return clientId + (userName == null ? "" : ":" + userName); + } + + @Override + public void removeAccessToken(OAuth2AccessToken accessToken) { + this.removeAccessToken(accessToken.getValue()); + } + + @Override + public OAuth2AccessToken readAccessToken(String tokenValue) { + byte[] key = serializeKey(ACCESS + tokenValue); + byte[] bytes = null; + RedisConnection conn = getConnection(); + try { + bytes = conn.get(key); + } finally { + conn.close(); + } + OAuth2AccessToken accessToken = deserializeAccessToken(bytes); + return accessToken; + } + + public void removeAccessToken(String tokenValue) { + byte[] accessKey = serializeKey(ACCESS + tokenValue); + byte[] authKey = serializeKey(AUTH + tokenValue); + byte[] accessToRefreshKey = serializeKey(ACCESS_TO_REFRESH + tokenValue); + RedisConnection conn = getConnection(); + try { + conn.openPipeline(); + conn.get(accessKey); + conn.get(authKey); + conn.del(accessKey); + conn.del(accessToRefreshKey); + // Don't remove the refresh token - it's up to the caller to do that + conn.del(authKey); + List results = conn.closePipeline(); + byte[] access = (byte[]) results.get(0); + byte[] auth = (byte[]) results.get(1); + + OAuth2Authentication authentication = deserializeAuthentication(auth); + if (authentication != null) { + String key = authenticationKeyGenerator.extractKey(authentication); + byte[] authToAccessKey = serializeKey(AUTH_TO_ACCESS + key); + byte[] unameKey = serializeKey(UNAME_TO_ACCESS + getApprovalKey(authentication)); + byte[] clientId = serializeKey(CLIENT_ID_TO_ACCESS + authentication.getOAuth2Request().getClientId()); + conn.openPipeline(); + conn.del(authToAccessKey); + conn.lRem(unameKey, 1, access); + conn.lRem(clientId, 1, access); + conn.del(serialize(ACCESS + key)); + conn.closePipeline(); + } + } finally { + conn.close(); + } + } + + @Override + public void storeRefreshToken(OAuth2RefreshToken refreshToken, OAuth2Authentication authentication) { + byte[] refreshKey = serializeKey(REFRESH + refreshToken.getValue()); + byte[] refreshAuthKey = serializeKey(REFRESH_AUTH + refreshToken.getValue()); + byte[] serializedRefreshToken = serialize(refreshToken); + RedisConnection conn = getConnection(); + try { + conn.openPipeline(); + conn.stringCommands().set(refreshKey, serializedRefreshToken); + conn.stringCommands().set(refreshAuthKey, serialize(authentication)); + if (refreshToken instanceof ExpiringOAuth2RefreshToken) { + ExpiringOAuth2RefreshToken expiringRefreshToken = (ExpiringOAuth2RefreshToken) refreshToken; + Date expiration = expiringRefreshToken.getExpiration(); + if (expiration != null) { + int seconds = Long.valueOf((expiration.getTime() - System.currentTimeMillis()) / 1000L) + .intValue(); + conn.expire(refreshKey, seconds); + conn.expire(refreshAuthKey, seconds); + } + } + conn.closePipeline(); + } finally { + conn.close(); + } + } + + @Override + public OAuth2RefreshToken readRefreshToken(String tokenValue) { + byte[] key = serializeKey(REFRESH + tokenValue); + byte[] bytes = null; + RedisConnection conn = getConnection(); + try { + bytes = conn.get(key); + } finally { + conn.close(); + } + OAuth2RefreshToken refreshToken = deserializeRefreshToken(bytes); + return refreshToken; + } + + @Override + public void removeRefreshToken(OAuth2RefreshToken refreshToken) { + this.removeRefreshToken(refreshToken.getValue()); + } + + public void removeRefreshToken(String tokenValue) { + byte[] refreshKey = serializeKey(REFRESH + tokenValue); + byte[] refreshAuthKey = serializeKey(REFRESH_AUTH + tokenValue); + byte[] refresh2AccessKey = serializeKey(REFRESH_TO_ACCESS + tokenValue); + byte[] access2RefreshKey = serializeKey(ACCESS_TO_REFRESH + tokenValue); + RedisConnection conn = getConnection(); + try { + conn.openPipeline(); + conn.del(refreshKey); + conn.del(refreshAuthKey); + conn.del(refresh2AccessKey); + conn.del(access2RefreshKey); + conn.closePipeline(); + } finally { + conn.close(); + } + } + + @Override + public void removeAccessTokenUsingRefreshToken(OAuth2RefreshToken refreshToken) { + this.removeAccessTokenUsingRefreshToken(refreshToken.getValue()); + } + + private void removeAccessTokenUsingRefreshToken(String refreshToken) { + byte[] key = serializeKey(REFRESH_TO_ACCESS + refreshToken); + List results = null; + RedisConnection conn = getConnection(); + try { + conn.openPipeline(); + conn.get(key); + conn.del(key); + results = conn.closePipeline(); + } finally { + conn.close(); + } + if (results == null) { + return; + } + byte[] bytes = (byte[]) results.get(0); + String accessToken = deserializeString(bytes); + if (accessToken != null) { + removeAccessToken(accessToken); + } + } + + @Override + public Collection findTokensByClientIdAndUserName(String clientId, String userName) { + byte[] approvalKey = serializeKey(UNAME_TO_ACCESS + getApprovalKey(clientId, userName)); + List byteList = null; + RedisConnection conn = getConnection(); + try { + byteList = conn.lRange(approvalKey, 0, -1); + } finally { + conn.close(); + } + if (byteList == null || byteList.size() == 0) { + return Collections. emptySet(); + } + List accessTokens = new ArrayList(byteList.size()); + for (byte[] bytes : byteList) { + OAuth2AccessToken accessToken = deserializeAccessToken(bytes); + accessTokens.add(accessToken); + } + return Collections. unmodifiableCollection(accessTokens); + } + + @Override + public Collection findTokensByClientId(String clientId) { + byte[] key = serializeKey(CLIENT_ID_TO_ACCESS + clientId); + List byteList = null; + RedisConnection conn = getConnection(); + try { + byteList = conn.lRange(key, 0, -1); + } finally { + conn.close(); + } + if (byteList == null || byteList.size() == 0) { + return Collections. emptySet(); + } + List accessTokens = new ArrayList(byteList.size()); + for (byte[] bytes : byteList) { + OAuth2AccessToken accessToken = deserializeAccessToken(bytes); + accessTokens.add(accessToken); + } + return Collections. unmodifiableCollection(accessTokens); + } + +} \ No newline at end of file diff --git a/joint-cloud-auth/src/main/java/com/joint/cloud/auth/config/custom/CustomUserDetails.java b/joint-cloud-auth/src/main/java/com/joint/cloud/auth/config/custom/CustomUserDetails.java new file mode 100644 index 0000000..950a384 --- /dev/null +++ b/joint-cloud-auth/src/main/java/com/joint/cloud/auth/config/custom/CustomUserDetails.java @@ -0,0 +1,152 @@ +package com.joint.cloud.auth.config.custom; + +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import java.util.Collection; +import java.util.List; + +public class CustomUserDetails implements UserDetails { + + static final long serialVersionUID = -7588980448693010399L; + + private String userId; + + private String username; + + private String name; + + private String password; + + private boolean enabled = true; + + private String clientId; + + private List roles; + + private Collection authorities; + + public String getClientId() { + return clientId; + } + + public void setClientId(String clientId) { + this.clientId = clientId; + } + + public String getUserId() { + return userId; + } + + public void setUsername(String username) { + this.username = username; + } + + public void setPassword(String password) { + this.password = password; + } + + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public void setAuthorities(Collection authorities) { + this.authorities = authorities; + } + + public List getRoles() { + return roles; + } + + public void setRoles(List roles) { + this.roles = roles; + } + + @Override + public Collection getAuthorities() { + return authorities; + } + + @Override + public String getPassword() { + return this.password; + } + + @Override + public String getUsername() { + return this.username; + } + + @Override + public boolean isAccountNonExpired() { + return true; + } + + @Override + public boolean isAccountNonLocked() { + return true; + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + public boolean isEnabled() { + return enabled; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public static class CustomUserDetailsBuilder { + private CustomUserDetails userDetails = new CustomUserDetails(); + + public CustomUserDetailsBuilder withUsername(String username) { + userDetails.setUsername(username); + userDetails.setAuthorities(null); + return this; + } + + public CustomUserDetailsBuilder withName(String name) { + userDetails.setName(name); + return this; + } + + public CustomUserDetailsBuilder withPassword(String password) { + userDetails.setPassword(password); + return this; + } + + public CustomUserDetailsBuilder withClientId(String clientId) { + userDetails.setClientId(clientId); + return this; + } + + public CustomUserDetailsBuilder withUserId(String userId) { + userDetails.setUserId(userId); + return this; + } + + public CustomUserDetailsBuilder withRoles(List roles) { + userDetails.setRoles(roles); + return this; + } + + public CustomUserDetails build() { + return userDetails; + } + } + +} \ No newline at end of file diff --git a/joint-cloud-auth/src/main/java/com/joint/cloud/auth/config/encoder/MyPasswordEncoder.java b/joint-cloud-auth/src/main/java/com/joint/cloud/auth/config/encoder/MyPasswordEncoder.java new file mode 100644 index 0000000..59986ca --- /dev/null +++ b/joint-cloud-auth/src/main/java/com/joint/cloud/auth/config/encoder/MyPasswordEncoder.java @@ -0,0 +1,24 @@ +package com.joint.cloud.auth.config.encoder; + +import org.springframework.security.crypto.password.PasswordEncoder; + +/** + * @description: Client Password Encoder + * @author: super.wu + * @date: Created in 2018/5/25 0025 + * @modified By: + * @version: 1.0 + * 此处可重新定义密码的加密算法,例如和可以采用BASE64或者MD5加salt的方式在数据库中进行加密,匹配密码的做同样的处理 + **/ +public class MyPasswordEncoder implements PasswordEncoder { + + @Override + public String encode(CharSequence arg0) { + return arg0.toString(); + } + + @Override + public boolean matches(CharSequence arg0, String arg1) { + return arg1.equals(arg0.toString()); + } +} diff --git a/joint-cloud-auth/src/main/java/com/joint/cloud/auth/config/impl/ClientDetailsServiceImpl.java b/joint-cloud-auth/src/main/java/com/joint/cloud/auth/config/impl/ClientDetailsServiceImpl.java new file mode 100644 index 0000000..579c8a0 --- /dev/null +++ b/joint-cloud-auth/src/main/java/com/joint/cloud/auth/config/impl/ClientDetailsServiceImpl.java @@ -0,0 +1,69 @@ +package com.joint.cloud.auth.config.impl; + +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.security.oauth2.provider.ClientDetails; +import org.springframework.security.oauth2.provider.ClientDetailsService; +import org.springframework.security.oauth2.provider.ClientRegistrationException; +import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import javax.sql.DataSource; +import java.util.HashMap; +import java.util.Map; + +/** + * @description: 自定义,添加了缓存配置 + * @author: super.wu + * @date: Created in 2018/5/8 0008 + * @modified By: + * @version: 1.0 + **/ +@Service +public class ClientDetailsServiceImpl implements ClientDetailsService { + + @Resource + DataSource dataSource; + + @Resource + RedisTemplate redisTemplate; + + private final String CACHE_KEY = "CACHE_CLIENT_DETAILS"; + + @Override + public ClientDetails loadClientByClientId(String clientId) throws ClientRegistrationException{ + return getClientDetails(clientId); + } + + private ClientDetails getClientDetails(String clientId) { + if (redisTemplate.hasKey(CACHE_KEY)) { + Map map = (Map) redisTemplate.opsForValue().get(CACHE_KEY); + if (map.containsKey(clientId)) { + return map.get(clientId); + } + } + return loadClientDetails(clientId); + } + + private ClientDetails loadClientDetails(String clientId) { + JdbcClientDetailsService jdbcClientDetailsService = new JdbcClientDetailsService(dataSource); + ClientDetails clientDetails = jdbcClientDetailsService.loadClientByClientId(clientId); + if(clientDetails == null) { + throw new ClientRegistrationException("应用" + clientId + "不存在!"); + } + cacheClientDetails(clientDetails); + return clientDetails; + } + + private void cacheClientDetails(ClientDetails clientDetails) { + Map map; + if (redisTemplate.hasKey(CACHE_KEY)) { + map = (Map) redisTemplate.opsForValue().get(CACHE_KEY); + } else { + map = new HashMap<>(1); + } + map.put(clientDetails.getClientId(), clientDetails); + redisTemplate.opsForValue().set(CACHE_KEY, map); + } + +} diff --git a/joint-cloud-auth/src/main/java/com/joint/cloud/auth/config/impl/UserDetailsServiceImpl.java b/joint-cloud-auth/src/main/java/com/joint/cloud/auth/config/impl/UserDetailsServiceImpl.java new file mode 100644 index 0000000..58be90b --- /dev/null +++ b/joint-cloud-auth/src/main/java/com/joint/cloud/auth/config/impl/UserDetailsServiceImpl.java @@ -0,0 +1,41 @@ +package com.joint.cloud.auth.config.impl; + +import com.joint.cloud.auth.config.custom.CustomUserDetails; +import com.joint.cloud.auth.config.service.JdbcUserDetailsService; +import org.springframework.beans.factory.annotation.Autowired; +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; + + +/** + * @description: + * @author: super.wu + * @date: Created in 2018/5/25 0025 + * @modified By: + * @version: 1.0 + **/ +@Service +public class UserDetailsServiceImpl implements UserDetailsService { + + + @Autowired + JdbcUserDetailsService jdbcUserDetailsService; + + /** + * 根据用户名获取登录用户信息 + * @param username + * @return + * @throws UsernameNotFoundException + */ + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + CustomUserDetails customUserDetails = jdbcUserDetailsService.loadUserDetailsByUserName(username); + if(customUserDetails == null){ + throw new UsernameNotFoundException("用户名:"+ username + "不存在!"); + } + customUserDetails.setRoles(jdbcUserDetailsService.loadUserRolesByUsername(customUserDetails.getName())); + return customUserDetails; + } +} diff --git a/joint-cloud-auth/src/main/java/com/joint/cloud/auth/config/service/JdbcUserDetailsService.java b/joint-cloud-auth/src/main/java/com/joint/cloud/auth/config/service/JdbcUserDetailsService.java new file mode 100644 index 0000000..b8dfb65 --- /dev/null +++ b/joint-cloud-auth/src/main/java/com/joint/cloud/auth/config/service/JdbcUserDetailsService.java @@ -0,0 +1,82 @@ +package com.joint.cloud.auth.config.service; + +import com.joint.cloud.auth.config.custom.CustomUserDetails; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.security.oauth2.common.exceptions.InvalidClientException; +import org.springframework.security.oauth2.provider.NoSuchClientException; +import org.springframework.stereotype.Repository; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashSet; +import java.util.List; + + +/** + * @description: + * @author: super.wu + * @date: Created in 2018/5/25 0025 + * @modified By: + * @version: 1.0 + **/ +@Repository +public class JdbcUserDetailsService { + + private String selectUserDetailsSql; + + private String selectUserDetailsRoleSql; + + @Autowired + private JdbcTemplate jdbcTemplate; + + public JdbcUserDetailsService() { + this.selectUserDetailsSql = "select u.user_id as userId, u.login_name as username, u.show_name as name,u.password,u.user_type,u.head_image_url,u.dept_id ,u.user_status from zz_sys_user AS u where login_name = ? and deleted_flag = 1"; + this.selectUserDetailsRoleSql = "select r.role_id FROM zz_sys_user u,zz_sys_user_role ur, zz_sys_role r WHERE u.user_id=ur.user_id AND r.role_id=ur.role_id AND u.login_name = ?"; + } + + public CustomUserDetails loadUserDetailsByUserName(String username) throws InvalidClientException { + try { + CustomUserDetails details = this.jdbcTemplate.queryForObject(this.selectUserDetailsSql, new UserDetailsRowMapper(), new Object[]{username}); + return details; + } catch (EmptyResultDataAccessException var4) { + throw new NoSuchClientException("No client with requested username: " + username); + } + } + + public List loadUserRolesByUserId(Long userId) { + List roles = this.jdbcTemplate.query(this.selectUserDetailsRoleSql, new RoleRowMapper(), new Object[]{userId, userId}); + return roles; + } + + public List loadUserRolesByUsername(String username) { + List roles = this.jdbcTemplate.query(this.selectUserDetailsRoleSql, new RoleRowMapper(), new Object[]{username}); + return roles; + } + + private static class UserDetailsRowMapper implements RowMapper { + + @Override + public CustomUserDetails mapRow(ResultSet resultSet, int i) throws SQLException { + CustomUserDetails customUserDetails = new CustomUserDetails(); + customUserDetails.setUserId(resultSet.getString(1)); + customUserDetails.setUsername(resultSet.getString(2)); + customUserDetails.setPassword(resultSet.getString(4)); + customUserDetails.setEnabled(true); + customUserDetails.setAuthorities(new HashSet<>()); + customUserDetails.setName(resultSet.getString(3)); + return customUserDetails; + } + + } + + private static class RoleRowMapper implements RowMapper { + @Override + public String mapRow(ResultSet resultSet, int i) throws SQLException { + return resultSet.getString(1); + } + } + +} diff --git a/joint-cloud-auth/src/main/java/com/joint/cloud/auth/controller/HealthController.java b/joint-cloud-auth/src/main/java/com/joint/cloud/auth/controller/HealthController.java new file mode 100644 index 0000000..2d2537a --- /dev/null +++ b/joint-cloud-auth/src/main/java/com/joint/cloud/auth/controller/HealthController.java @@ -0,0 +1,16 @@ +package com.joint.cloud.auth.controller; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Date; + + +@RestController +public class HealthController { + + @GetMapping("/health") + public String health() { + return "I'm health now " + (new Date()).toString(); + } +} diff --git a/joint-cloud-auth/src/main/java/com/joint/cloud/auth/controller/UserController.java b/joint-cloud-auth/src/main/java/com/joint/cloud/auth/controller/UserController.java new file mode 100644 index 0000000..eca6e1f --- /dev/null +++ b/joint-cloud-auth/src/main/java/com/joint/cloud/auth/controller/UserController.java @@ -0,0 +1,19 @@ +package com.joint.cloud.auth.controller; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.security.Principal; + +/** + * Created by wangyunfei on 2017/6/12. + */ +@RestController +public class UserController { + + @GetMapping("/user") + public Principal user(Principal user){ + return user; + } + +} diff --git a/joint-cloud-auth/src/main/java/com/joint/cloud/auth/exception/LicenseStatusException.java b/joint-cloud-auth/src/main/java/com/joint/cloud/auth/exception/LicenseStatusException.java new file mode 100644 index 0000000..9e679d0 --- /dev/null +++ b/joint-cloud-auth/src/main/java/com/joint/cloud/auth/exception/LicenseStatusException.java @@ -0,0 +1,10 @@ +package com.joint.cloud.auth.exception; + +import org.springframework.security.authentication.AccountStatusException; + +public class LicenseStatusException extends AccountStatusException { + + public LicenseStatusException(String msg) { + super(msg); + } +} diff --git a/joint-cloud-auth/src/main/java/com/joint/cloud/auth/exception/ParamException.java b/joint-cloud-auth/src/main/java/com/joint/cloud/auth/exception/ParamException.java new file mode 100644 index 0000000..d1a6536 --- /dev/null +++ b/joint-cloud-auth/src/main/java/com/joint/cloud/auth/exception/ParamException.java @@ -0,0 +1,14 @@ +package com.joint.cloud.auth.exception; + +/** + * @description: 参数异常 + * @author: super.wu + * @date: Created in 2018/4/28 0028 + * @modified By: + * @version: 1.0 + **/ +public class ParamException extends Exception{ + + private static final long serialVersionUID = -4970436179123713406L; + +} diff --git a/joint-cloud-auth/src/main/resources/application-dev.yml b/joint-cloud-auth/src/main/resources/application-dev.yml new file mode 100644 index 0000000..e2932a1 --- /dev/null +++ b/joint-cloud-auth/src/main/resources/application-dev.yml @@ -0,0 +1,8 @@ +spring: + cloud: + consul: + host: ${CONSUL_ADDR:10.4.79.107} + datasource: + url: jdbc:postgresql://${DB_ADDR:10.4.79.103}:5432/jc_admin?useUnicode=true&characterEncoding=utf-8 + redis: + host: ${REDIS_ADDR:10.4.79.103} diff --git a/joint-cloud-auth/src/main/resources/application.yml b/joint-cloud-auth/src/main/resources/application.yml new file mode 100644 index 0000000..7211460 --- /dev/null +++ b/joint-cloud-auth/src/main/resources/application.yml @@ -0,0 +1,40 @@ +server: + port: 8095 +spring: + application: + name: joint-cloud-auth + profiles: + active: dev + cloud: + consul: + port: 8500 + discovery: + enabled: true + register: true + health-check-path: /health + health-check-interval: 60s + health-check-critical-timeout: 30s + instance-id: ${spring.application.name}:${spring.cloud.consul.discovery.ip-address}:${server.port} + ip-address: ${REGISTRY_ADDR:${spring.cloud.client.ip-address}} + prefer-ip-address: true + datasource: + driverClassName: org.postgresql.Driver + type: com.alibaba.druid.pool.DruidDataSource + username: postgres + password: aaa123+-*/ + redis: + database: 2 + password: Aaa123+-*/ + timeout: 3000 + jedis: + pool: + max-active: 5 + min-idle: 0 + max-wait: 1 + max-idle: 8 + port: 6379 +logging: + config: classpath:logback.xml +ribbon: + ConnectTimeout: 5000 + ReadTimeout: 10000 \ No newline at end of file diff --git a/joint-cloud-auth/src/main/resources/logback.xml b/joint-cloud-auth/src/main/resources/logback.xml new file mode 100644 index 0000000..83056cb --- /dev/null +++ b/joint-cloud-auth/src/main/resources/logback.xml @@ -0,0 +1,83 @@ + + + + + + + %d %p (%file:%line\)- %m%n + + UTF-8 + + + + + + + logs/aclome-cmp-auth-server.log + + + + + + logs/aclome-cmp-auth-server-%d.%i.log + + 30 + + + 10MB + + + + + + %d %p (%file:%line\)- %m%n + + + UTF-8 + + + + + logs/aclome-cmp-auth-api.log + + + + + + logs/aclome-cmp-auth-api-%d.%i.log + + 30 + + + 10MB + + + + + + %d %p (%file:%line\)- %m%n + + + UTF-8 + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/joint-cloud-common/pom.xml b/joint-cloud-common/pom.xml new file mode 100644 index 0000000..8fe8d82 --- /dev/null +++ b/joint-cloud-common/pom.xml @@ -0,0 +1,36 @@ + + + + joint-cloud + com.joint.cloud + 1.0.0-SNAPSHOT + + 4.0.0 + + joint-cloud-common + joint-cloud-common + jar + + + 8 + 8 + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.apache.commons + commons-lang3 + + + + \ No newline at end of file diff --git a/joint-cloud-common/src/main/java/com/joint/cloud/common/constants/AIOpsObjectType.java b/joint-cloud-common/src/main/java/com/joint/cloud/common/constants/AIOpsObjectType.java new file mode 100644 index 0000000..7b4e1d6 --- /dev/null +++ b/joint-cloud-common/src/main/java/com/joint/cloud/common/constants/AIOpsObjectType.java @@ -0,0 +1,13 @@ +package com.joint.cloud.common.constants; + +/** + * @author lilengyi + * @date 2021/11/18 15:42 + * 系统对象类型枚举类 + */ +public enum AIOpsObjectType { + USER, ROLE, + + // 告警相关 + ALERT_RULE +} diff --git a/joint-cloud-common/src/main/java/com/joint/cloud/common/constants/NotificationType.java b/joint-cloud-common/src/main/java/com/joint/cloud/common/constants/NotificationType.java new file mode 100644 index 0000000..e5f4580 --- /dev/null +++ b/joint-cloud-common/src/main/java/com/joint/cloud/common/constants/NotificationType.java @@ -0,0 +1,35 @@ +package com.joint.cloud.common.constants; + +public enum NotificationType { + /** + * 事件 + */ + EVENT, + /** + * 问题 + */ + PROBLEM, + /** + * 变更 + */ + CHANGE, + /** + * 请求 + */ + REQUEST, + /** + * 被动运维 + */ + PASV, + /** + * 主动运维 + */ + ACTIVE, + QUESTION, + /** + * 中山档案馆-被动 + */ + ZSPASSTIVE, + ZSACTIVE + +} \ No newline at end of file diff --git a/joint-cloud-common/src/main/java/com/joint/cloud/common/constants/ResponseCode.java b/joint-cloud-common/src/main/java/com/joint/cloud/common/constants/ResponseCode.java new file mode 100644 index 0000000..ffcaf01 --- /dev/null +++ b/joint-cloud-common/src/main/java/com/joint/cloud/common/constants/ResponseCode.java @@ -0,0 +1,45 @@ +package com.joint.cloud.common.constants; + +public enum ResponseCode { + /** + * 成功返回的状态码 + */ + SUCCESS(200, "SUCCESS"), + + /** + * 非法参数 + */ + BAD_REQUEST(400, "非法参数"), + + /** + * 资源不存在的状态码 + */ + RESOURCES_NOT_EXIST(404, "资源不存在"), + + /** + * 所有无法识别的异常默认的返回状态码 + */ + SERVICE_ERROR(500, "服务器异常"); + + /** + * 状态码 + */ + private int code; + /** + * 返回信息 + */ + private String msg; + + ResponseCode(int code, String msg) { + this.code = code; + this.msg = msg; + } + + public int getCode() { + return code; + } + + public String getMsg() { + return msg; + } +} diff --git a/joint-cloud-common/src/main/java/com/joint/cloud/common/constants/ResponseResult.java b/joint-cloud-common/src/main/java/com/joint/cloud/common/constants/ResponseResult.java new file mode 100644 index 0000000..1bf8668 --- /dev/null +++ b/joint-cloud-common/src/main/java/com/joint/cloud/common/constants/ResponseResult.java @@ -0,0 +1,65 @@ +package com.joint.cloud.common.constants; + +import java.io.Serializable; + +public class ResponseResult implements Serializable { + + /** + * 返回状态码 + */ + private Integer code; + + /** + * 返回信息 + */ + private String msg; + + /** + * 数据 + */ + private T data; + + /** + * 发生异常时回显 + */ + private String stack; + + public ResponseResult(Integer code, String msg, T data, String stack) { + this.code = code; + this.msg = msg; + this.data = data; + this.stack = stack; + } + + public Integer getCode() { + return code; + } + + public void setCode(Integer code) { + this.code = code; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } + + public T getData() { + return data; + } + + public void setData(T data) { + this.data = data; + } + + public String getStack() { + return stack; + } + + public void setStack(String stack) { + this.stack = stack; + } +} diff --git a/joint-cloud-common/src/main/java/com/joint/cloud/common/constants/SysConstant.java b/joint-cloud-common/src/main/java/com/joint/cloud/common/constants/SysConstant.java new file mode 100644 index 0000000..42b5fc9 --- /dev/null +++ b/joint-cloud-common/src/main/java/com/joint/cloud/common/constants/SysConstant.java @@ -0,0 +1,19 @@ +package com.joint.cloud.common.constants; + +/** + * @description: + * @author: super.wu + * @date: Created in 2018/6/13 0013 + * @modified By: + * @version: 1.0 + **/ +public class SysConstant { + + /** 超级管理员角色编码 **/ + public static final String SUPER_ADMIN_ROLE_CODE = "SUPER_ADMIN_ROLE"; + + /** api前缀, 此前缀用做拦截器判断不需要做权限过滤 **/ + public static final String API_NO_PERMISSION = "/noPermission/"; + + +} diff --git a/joint-cloud-common/src/main/java/com/joint/cloud/common/exception/AIOpsException.java b/joint-cloud-common/src/main/java/com/joint/cloud/common/exception/AIOpsException.java new file mode 100644 index 0000000..06d7a2d --- /dev/null +++ b/joint-cloud-common/src/main/java/com/joint/cloud/common/exception/AIOpsException.java @@ -0,0 +1,68 @@ +package com.joint.cloud.common.exception; + +import com.joint.cloud.common.constants.ResponseCode; +import com.joint.cloud.common.exception.msg.BaseAIOpsExceptionErrorMsg; +import org.springframework.util.StringUtils; + +public class AIOpsException extends RuntimeException { + + private final ResponseCode code; + + private final BaseAIOpsExceptionErrorMsg errorMsg; + + public AIOpsException(ResponseCode code) { + super(code.getMsg()); + this.code = code; + this.errorMsg = null; + } + + public AIOpsException(ResponseCode code, BaseAIOpsExceptionErrorMsg errorMsg) { + super(errorMsg.getErrorMsgLocal()); + this.code = code; + this.errorMsg = errorMsg; + } + + public AIOpsException(BaseAIOpsExceptionErrorMsg errorMsg) { + super(errorMsg.getErrorMsgLocal()); + this.code = ResponseCode.SERVICE_ERROR; + this.errorMsg = errorMsg; + } + + public static AIOpsException newInstance(Class errorMsgClass) { + BaseAIOpsExceptionErrorMsg baseAIOpsExceptionErrorMsg = null; + try { + baseAIOpsExceptionErrorMsg = errorMsgClass.newInstance(); + } catch (InstantiationException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + return new AIOpsException(ResponseCode.SERVICE_ERROR, baseAIOpsExceptionErrorMsg); + } + + public ResponseCode getCode() { + return code; + } + + public String getErrorMsg() { + if (errorMsg != null) { + if (StringUtils.hasLength(errorMsg.getErrorMsg())) { + return errorMsg.getErrorMsg(); + } else if (StringUtils.hasLength(errorMsg.getErrorMsgLocal())) { + return errorMsg.getErrorMsgLocal(); + } + } + return null; + } + + public String getErrorMsgLocal() { + if (errorMsg != null) { + if (StringUtils.hasLength(errorMsg.getErrorMsgLocal())) { + return errorMsg.getErrorMsgLocal(); + } else if (StringUtils.hasLength(errorMsg.getErrorMsg())) { + return errorMsg.getErrorMsg(); + } + } + return null; + } +} diff --git a/joint-cloud-common/src/main/java/com/joint/cloud/common/exception/BusinessException.java b/joint-cloud-common/src/main/java/com/joint/cloud/common/exception/BusinessException.java new file mode 100644 index 0000000..1b84418 --- /dev/null +++ b/joint-cloud-common/src/main/java/com/joint/cloud/common/exception/BusinessException.java @@ -0,0 +1,30 @@ +package com.joint.cloud.common.exception; + +import com.joint.cloud.common.constants.ResponseCode; + +public class BusinessException extends RuntimeException { + + private final ResponseCode code; + + private final String msg; + + public BusinessException(ResponseCode code) { + super(code.getMsg()); + this.code = code; + this.msg = null; + } + + public BusinessException(ResponseCode code, String msg) { + super(msg); + this.code = code; + this.msg = msg; + } + + public ResponseCode getCode() { + return code; + } + + public String getMsg() { + return msg; + } +} diff --git a/joint-cloud-common/src/main/java/com/joint/cloud/common/exception/ExceptionHandlerAdvice.java b/joint-cloud-common/src/main/java/com/joint/cloud/common/exception/ExceptionHandlerAdvice.java new file mode 100644 index 0000000..742eeab --- /dev/null +++ b/joint-cloud-common/src/main/java/com/joint/cloud/common/exception/ExceptionHandlerAdvice.java @@ -0,0 +1,104 @@ +package com.joint.cloud.common.exception; + +import com.joint.cloud.common.constants.ResponseCode; +import com.joint.cloud.common.constants.ResponseResult; +import com.joint.cloud.common.response.BaseResponse; +import com.joint.cloud.common.utils.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.validation.BindingResult; +import org.springframework.validation.FieldError; +import org.springframework.validation.ObjectError; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseBody; + +import java.util.List; + +@ControllerAdvice(annotations = BaseResponse.class) +@ResponseBody +public class ExceptionHandlerAdvice { + + private static final Logger log = LoggerFactory.getLogger(ExceptionHandlerAdvice.class); + /** + * 处理未捕获的Exception + * + * @param e 异常 + * @return 统一响应体 + */ + @ExceptionHandler(Exception.class) + public ResponseResult handleException(Exception e) { + log.error(e.getMessage(), e); + return new ResponseResult(ResponseCode.SERVICE_ERROR.getCode(), ResponseCode.SERVICE_ERROR.getMsg(), null, StringUtils.getStackMessage(e)); + } + + /** + * 处理未捕获的RuntimeException + * + * @param e 运行时异常 + * @return 统一响应体 + */ + @ExceptionHandler(RuntimeException.class) + public ResponseResult handleRuntimeException(RuntimeException e) { + log.error(e.getMessage(), e); + return new ResponseResult(ResponseCode.SERVICE_ERROR.getCode(), ResponseCode.SERVICE_ERROR.getMsg(), null, StringUtils.getStackMessage(e)); + } + + /** + * 处理业务异常BaseException + * + * @param e 业务异常 + * @return 统一响应体 + */ + @ExceptionHandler(BusinessException.class) + public ResponseResult handleBaseException(BusinessException e) { + ResponseCode code = e.getCode(); + if (e.getMsg() != null) { + return new ResponseResult(code.getCode(), e.getMsg(), null, null); + } + return new ResponseResult(code.getCode(), code.getMsg(), null, null); + } + + /** + * 处理业务异常BaseException + * + * @param e 业务异常 + * @return 统一响应体 + */ + @ExceptionHandler(AIOpsException.class) + public ResponseResult handleBaseException(AIOpsException e) { + log.error(e.getMessage(), e); + ResponseCode code = e.getCode(); + if (e.getErrorMsgLocal() != null) { + return new ResponseResult(code.getCode(), e.getErrorMsgLocal(), null, null); + } + return new ResponseResult(code.getCode(), code.getMsg(), null, null); + } + + /** + * 参数校验异常MethodArgumentNotValidException + * + * @param e 业务异常 + * @return 统一响应体 + */ + @ExceptionHandler(MethodArgumentNotValidException.class) + public ResponseResult handleBindException(MethodArgumentNotValidException e) { + BindingResult result = e.getBindingResult(); + String message = ""; + if (result.hasErrors()) { + List errors = result.getAllErrors(); + errors.forEach(p -> { + FieldError fieldError = (FieldError) p; + log.error("Data check failure : object{" + fieldError.getObjectName() + "},field{" + fieldError.getField() + + "},errorMessage{" + fieldError.getDefaultMessage() + "}"); + + }); + if (errors.size() > 0) { + FieldError fieldError = (FieldError) errors.get(0); + message = fieldError.getDefaultMessage(); + } + } + return new ResponseResult(ResponseCode.BAD_REQUEST.getCode(), message, null, null); + } +} diff --git a/joint-cloud-common/src/main/java/com/joint/cloud/common/exception/msg/BaseAIOpsExceptionErrorMsg.java b/joint-cloud-common/src/main/java/com/joint/cloud/common/exception/msg/BaseAIOpsExceptionErrorMsg.java new file mode 100644 index 0000000..5a32d27 --- /dev/null +++ b/joint-cloud-common/src/main/java/com/joint/cloud/common/exception/msg/BaseAIOpsExceptionErrorMsg.java @@ -0,0 +1,58 @@ +package com.joint.cloud.common.exception.msg; + +import org.springframework.util.StringUtils; + +/** + * @author lilengyi + * @date 2021/11/12 10:48 + * + */ +public abstract class BaseAIOpsExceptionErrorMsg { + + private String errorCode; + + private String errorMsg; + + private String errorMsgLocal; + + public void setErrorCode(String errorCode) { + this.errorCode = errorCode; + } + + public void setErrorMsg(String errorMsg) { + this.errorMsg = errorMsg; + } + + public void setErrorMsgLocal(String errorMsgLocal) { + this.errorMsgLocal = errorMsgLocal; + } + + public String getErrorCode() { + return errorCode; + } + + public String getErrorMsg() { + if (!StringUtils.hasLength(this.errorMsg)) { + this.errorMsg = generateErrorMsg(); + } + if (!StringUtils.hasLength(this.errorMsg)) { + this.errorMsg = generateErrorMsgLocal(); + } + return errorMsg; + } + + public String getErrorMsgLocal() { + if (!StringUtils.hasLength(this.errorMsgLocal)) { + this.errorMsgLocal = generateErrorMsgLocal(); + } + if (!StringUtils.hasLength(this.errorMsgLocal)) { + this.errorMsgLocal = generateErrorMsg(); + } + return errorMsgLocal; + } + + public abstract String generateErrorMsg(); + + public abstract String generateErrorMsgLocal(); + +} diff --git a/joint-cloud-common/src/main/java/com/joint/cloud/common/exception/msg/InvalidParameterValueExceptionErrorMsg.java b/joint-cloud-common/src/main/java/com/joint/cloud/common/exception/msg/InvalidParameterValueExceptionErrorMsg.java new file mode 100644 index 0000000..cc0858b --- /dev/null +++ b/joint-cloud-common/src/main/java/com/joint/cloud/common/exception/msg/InvalidParameterValueExceptionErrorMsg.java @@ -0,0 +1,20 @@ +package com.joint.cloud.common.exception.msg; + +public class InvalidParameterValueExceptionErrorMsg extends BaseAIOpsExceptionErrorMsg { + + private String invalidParamKey; + + public InvalidParameterValueExceptionErrorMsg(String invalidParamKey) { + this.invalidParamKey = invalidParamKey; + } + + @Override + public String generateErrorMsg() { + return "Invalid Parameter: "+invalidParamKey; + } + + @Override + public String generateErrorMsgLocal() { + return "参数非法: "+invalidParamKey; + } +} diff --git a/joint-cloud-common/src/main/java/com/joint/cloud/common/exception/msg/NullParameterExceptionErrorMsg.java b/joint-cloud-common/src/main/java/com/joint/cloud/common/exception/msg/NullParameterExceptionErrorMsg.java new file mode 100644 index 0000000..e339a4b --- /dev/null +++ b/joint-cloud-common/src/main/java/com/joint/cloud/common/exception/msg/NullParameterExceptionErrorMsg.java @@ -0,0 +1,20 @@ +package com.joint.cloud.common.exception.msg; + +public class NullParameterExceptionErrorMsg extends BaseAIOpsExceptionErrorMsg { + + private String invalidParamKey; + + public NullParameterExceptionErrorMsg(String invalidParamKey) { + this.invalidParamKey = invalidParamKey; + } + + @Override + public String generateErrorMsg() { + return "Parameter '" + invalidParamKey + "' can not be null or empty."; + } + + @Override + public String generateErrorMsgLocal() { + return "参数'" + invalidParamKey + "'不能为空"; + } +} diff --git a/joint-cloud-common/src/main/java/com/joint/cloud/common/exception/msg/ResourceAlreadyExistExceptionErrorMsg.java b/joint-cloud-common/src/main/java/com/joint/cloud/common/exception/msg/ResourceAlreadyExistExceptionErrorMsg.java new file mode 100644 index 0000000..7930844 --- /dev/null +++ b/joint-cloud-common/src/main/java/com/joint/cloud/common/exception/msg/ResourceAlreadyExistExceptionErrorMsg.java @@ -0,0 +1,23 @@ +package com.joint.cloud.common.exception.msg; + +public class ResourceAlreadyExistExceptionErrorMsg extends BaseAIOpsExceptionErrorMsg { + + private String resourceType; + + private Object resourceName; + + public ResourceAlreadyExistExceptionErrorMsg(String resourceType, Object resourceName) { + this.resourceType = resourceType; + this.resourceName = resourceName; + } + + @Override + public String generateErrorMsg() { + return "Resource '" + resourceType + "' ( with Name " + resourceName.toString() + " ) Already Exists!"; + } + + @Override + public String generateErrorMsgLocal() { + return "资源'" + resourceType + "'(名称为" + resourceName.toString() + ")已存在!"; + } +} diff --git a/joint-cloud-common/src/main/java/com/joint/cloud/common/exception/msg/ResourceNotExistExceptionErrorMsg.java b/joint-cloud-common/src/main/java/com/joint/cloud/common/exception/msg/ResourceNotExistExceptionErrorMsg.java new file mode 100644 index 0000000..8ca1813 --- /dev/null +++ b/joint-cloud-common/src/main/java/com/joint/cloud/common/exception/msg/ResourceNotExistExceptionErrorMsg.java @@ -0,0 +1,23 @@ +package com.joint.cloud.common.exception.msg; + +public class ResourceNotExistExceptionErrorMsg extends BaseAIOpsExceptionErrorMsg { + + private String resourceType; + + private Object resourceId; + + public ResourceNotExistExceptionErrorMsg(String resourceType, Object resourceId) { + this.resourceType = resourceType; + this.resourceId = resourceId; + } + + @Override + public String generateErrorMsg() { + return "Resource '" + resourceType + "' ( with ID " + resourceId.toString() + " ) Not Exists!"; + } + + @Override + public String generateErrorMsgLocal() { + return "资源'" + resourceType + "'(ID为" + resourceId.toString() + ")不存在!"; + } +} diff --git a/joint-cloud-common/src/main/java/com/joint/cloud/common/exception/msg/UnknownExceptionErrorMsg.java b/joint-cloud-common/src/main/java/com/joint/cloud/common/exception/msg/UnknownExceptionErrorMsg.java new file mode 100644 index 0000000..d191e2e --- /dev/null +++ b/joint-cloud-common/src/main/java/com/joint/cloud/common/exception/msg/UnknownExceptionErrorMsg.java @@ -0,0 +1,15 @@ +package com.joint.cloud.common.exception.msg; + +public class UnknownExceptionErrorMsg extends BaseAIOpsExceptionErrorMsg { + + + @Override + public String generateErrorMsg() { + return "Unknown system exception. Contact your administrator"; + } + + @Override + public String generateErrorMsgLocal() { + return "未知系统异常,请联系管理员"; + } +} diff --git a/joint-cloud-common/src/main/java/com/joint/cloud/common/model/dto/RequestDto.java b/joint-cloud-common/src/main/java/com/joint/cloud/common/model/dto/RequestDto.java new file mode 100644 index 0000000..6d1e50f --- /dev/null +++ b/joint-cloud-common/src/main/java/com/joint/cloud/common/model/dto/RequestDto.java @@ -0,0 +1,30 @@ +package com.joint.cloud.common.model.dto; + + +/** + * @description: + * @date: Created in 2018/5/9 0009 + * @modified By: + * @version: 1.0 + **/ +public class RequestDto { + + private int size = Integer.MAX_VALUE; + private int page = 1; + public int getSize() { + return size; + } + public void setSize(int size) { + this.size = size; + } + public int getPage() { + return page; + } + public void setPage(int page) { + this.page = page; + } + + public RequestDto(){ + + } +} diff --git a/joint-cloud-common/src/main/java/com/joint/cloud/common/model/wrapper/BaseDTOEntityWrapper.java b/joint-cloud-common/src/main/java/com/joint/cloud/common/model/wrapper/BaseDTOEntityWrapper.java new file mode 100644 index 0000000..b1e1836 --- /dev/null +++ b/joint-cloud-common/src/main/java/com/joint/cloud/common/model/wrapper/BaseDTOEntityWrapper.java @@ -0,0 +1,22 @@ +package com.joint.cloud.common.model.wrapper; + +import java.util.List; + +/** + * @author lilengyi + * @date 2020/1/20 15:04 + * + * Data Transfer Object数据传输对象与Entity实体类相互转换 + * @param + * @param + */ +public interface BaseDTOEntityWrapper { + + DTO toDTOFromEntity(Entity entity, DTO dto); + + Entity toEntityFromDTO(DTO dto, Entity entity); + + List toDTOListFromEntity(List entities); + + List toEntityListFromDTO(List dtos); +} diff --git a/joint-cloud-common/src/main/java/com/joint/cloud/common/model/wrapper/BasePOEntityWrapper.java b/joint-cloud-common/src/main/java/com/joint/cloud/common/model/wrapper/BasePOEntityWrapper.java new file mode 100644 index 0000000..a436528 --- /dev/null +++ b/joint-cloud-common/src/main/java/com/joint/cloud/common/model/wrapper/BasePOEntityWrapper.java @@ -0,0 +1,22 @@ +package com.joint.cloud.common.model.wrapper; + +import java.util.List; + +/** + * @author lilengyi + * @date 2020/1/20 15:04 + * + * Persistent Object持久化对象与Entity实体类转换 + * @param + * @param + */ +public interface BasePOEntityWrapper { + + PO toPOFromEntity(Entity entity, PO po); + + Entity toEntityFromPO(PO po, Entity entity); + + List toPOListFromEntity(List entities); + + List toEntityListFromPO(List pos); +} diff --git a/joint-cloud-common/src/main/java/com/joint/cloud/common/model/wrapper/BaseVOEntityWrapper.java b/joint-cloud-common/src/main/java/com/joint/cloud/common/model/wrapper/BaseVOEntityWrapper.java new file mode 100644 index 0000000..b673848 --- /dev/null +++ b/joint-cloud-common/src/main/java/com/joint/cloud/common/model/wrapper/BaseVOEntityWrapper.java @@ -0,0 +1,19 @@ +package com.joint.cloud.common.model.wrapper; + +import java.util.List; + +/** + * @author lilengyi + * @date 2020/1/20 15:04 + * + * View Object视图类与Entity实体类转换 + * @param + * @param + */ +public interface BaseVOEntityWrapper { + + VO toVOFromEntity(Entity entity, VO vo); + + List toVOListFromEntity(List entities); + +} diff --git a/joint-cloud-common/src/main/java/com/joint/cloud/common/response/BaseResponse.java b/joint-cloud-common/src/main/java/com/joint/cloud/common/response/BaseResponse.java new file mode 100644 index 0000000..3cd3b97 --- /dev/null +++ b/joint-cloud-common/src/main/java/com/joint/cloud/common/response/BaseResponse.java @@ -0,0 +1,11 @@ +package com.joint.cloud.common.response; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.TYPE}) +public @interface BaseResponse { +} diff --git a/joint-cloud-common/src/main/java/com/joint/cloud/common/response/ResponseResultHandlerAdvice.java b/joint-cloud-common/src/main/java/com/joint/cloud/common/response/ResponseResultHandlerAdvice.java new file mode 100644 index 0000000..c6c570b --- /dev/null +++ b/joint-cloud-common/src/main/java/com/joint/cloud/common/response/ResponseResultHandlerAdvice.java @@ -0,0 +1,36 @@ +package com.joint.cloud.common.response; + +import com.joint.cloud.common.constants.ResponseCode; +import com.joint.cloud.common.constants.ResponseResult; +import com.joint.cloud.common.utils.JsonUtil; +import org.springframework.core.MethodParameter; +import org.springframework.http.MediaType; +import org.springframework.http.server.ServerHttpRequest; +import org.springframework.http.server.ServerHttpResponse; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; + +@ControllerAdvice(annotations = BaseResponse.class) +public class ResponseResultHandlerAdvice implements ResponseBodyAdvice { + + @Override + public boolean supports(MethodParameter returnType, Class converterType) { + return true; + } + + @Override + public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, + ServerHttpRequest request, ServerHttpResponse response) { + if (body instanceof String) { + return JsonUtil.serialize(new ResponseResult(ResponseCode.SUCCESS.getCode(), ResponseCode.SUCCESS.getMsg(), body, null)); + } else if (body instanceof ResponseResult) { + return body; + } + + if (MediaType.APPLICATION_JSON.equals(selectedContentType)) { + return new ResponseResult(ResponseCode.SUCCESS.getCode(), ResponseCode.SUCCESS.getMsg(), body, null); + } + return body; + } + +} diff --git a/joint-cloud-common/src/main/java/com/joint/cloud/common/utils/EntityUtils.java b/joint-cloud-common/src/main/java/com/joint/cloud/common/utils/EntityUtils.java new file mode 100644 index 0000000..870e012 --- /dev/null +++ b/joint-cloud-common/src/main/java/com/joint/cloud/common/utils/EntityUtils.java @@ -0,0 +1,131 @@ +package com.joint.cloud.common.utils; + +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; +import java.lang.reflect.Field; +import java.net.URLDecoder; +import java.util.Date; + +/** + * @description: + * @author: 王浩彬 + * @date: Created in 2018/5/10 0010 + * @modified By: + * @version: 1.0 + **/ +public class EntityUtils { + + /** + * 快速将bean的crtUser、crtHost、crtTime、updUser、updHost、updTime附上相关值 + * + * @param entity 实体bean + * @author 王浩彬 + */ + public static void setCreateAndUpdateInfo(T entity) { + try { + setCreateInfo(entity); + setUpdatedInfo(entity); + } catch (Exception e) { + + } + } + + /** + * 快速将bean的crtUser、crtHost、crtTime附上相关值 + * + * @param entity 实体bean + * @author 王浩彬 + */ + public static void setCreateInfo(T entity) { + HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); + String hostIp = ""; + String name = ""; + String id = ""; + if(request!=null) { + hostIp = request.getHeader("userHost"); + name = request.getHeader("userName"); + try { + name = URLDecoder.decode(name, "utf-8"); + } catch (Exception e) { + + } + id = String.valueOf(request.getHeader("userId")); + } + // 默认属性 + String[] fields = {"crtName","crtUser","crtHost","crtTime"}; + Field field = ReflectionUtils.getAccessibleField(entity, "crtTime"); + // 默认值 + Object [] value = null; + if(field!=null&&field.getType().equals(Date.class)){ + value = new Object []{name,id,hostIp,new Date()}; + } + // 填充默认属性值 + setDefaultValues(entity, fields, value); + } + + /** + * 快速将bean的updUser、updHost、updTime附上相关值 + * + * @param entity 实体bean + * @author 王浩彬 + */ + public static void setUpdatedInfo(T entity) { + HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest(); + String hostIp = ""; + String name = ""; + String id = ""; + if(request!=null) { + hostIp = request.getHeader("userHost"); + name = request.getHeader("userName"); + try { + name = URLDecoder.decode(name, "utf-8"); + } catch (Exception e) { + + } + id = String.valueOf(request.getHeader("userId")); + } + // 默认属性 + String[] fields = {"updName","updUser","updHost","updTime"}; + Field field = ReflectionUtils.getAccessibleField(entity, "updTime"); + Object [] value = null; + if(field!=null&&field.getType().equals(Date.class)){ + value = new Object []{name,id,hostIp,new Date()}; + } + // 填充默认属性值 + setDefaultValues(entity, fields, value); + } + /** + * 依据对象的属性数组和值数组对对象的属性进行赋值 + * + * @param entity 对象 + * @param fields 属性数组 + * @param value 值数组 + * @author 王浩彬 + */ + private static void setDefaultValues(T entity, String[] fields, Object[] value) { + for(int i=0;i boolean isPKNotNull(T entity,String field){ + if(!ReflectionUtils.hasField(entity, field)) { + return false; + } + Object value = ReflectionUtils.getFieldValue(entity, field); + return value!=null&&!"".equals(value); + } +} diff --git a/joint-cloud-common/src/main/java/com/joint/cloud/common/utils/FieldUtils.java b/joint-cloud-common/src/main/java/com/joint/cloud/common/utils/FieldUtils.java new file mode 100644 index 0000000..836a95d --- /dev/null +++ b/joint-cloud-common/src/main/java/com/joint/cloud/common/utils/FieldUtils.java @@ -0,0 +1,45 @@ +package com.joint.cloud.common.utils; + +import org.springframework.cglib.beans.BeanCopier; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author: lilengyi + * @date: 2020/1/15 14:32 + * 模型同类型同名参数转换工具 + */ +public class FieldUtils { + + private static final Map BEAN_COPIER_CACHE = new ConcurrentHashMap<>(); + + public static Object convertModel(Object source, Object target) { + BeanCopier copier = getBeanCopier(source.getClass(), target.getClass()); + copier.copy(source, target, null); + return target; + } + + private static BeanCopier getBeanCopier(Class sourceClass, Class targetClass) { + String beanKey = generateKey(sourceClass, targetClass); + BeanCopier copier = null; + if (!BEAN_COPIER_CACHE.containsKey(beanKey)) { + copier = BeanCopier.create(sourceClass, targetClass, false); + BEAN_COPIER_CACHE.put(beanKey, copier); + } else { + copier = BEAN_COPIER_CACHE.get(beanKey); + } + return copier; + } + + /** + * 两个类的全限定名拼接起来构成Key + * + * @param sourceClass + * @param targetClass + * @return + */ + private static String generateKey(Class sourceClass, Class targetClass) { + return sourceClass.getName() + targetClass.getName(); + } +} diff --git a/joint-cloud-common/src/main/java/com/joint/cloud/common/utils/IDUtil.java b/joint-cloud-common/src/main/java/com/joint/cloud/common/utils/IDUtil.java new file mode 100644 index 0000000..eb489e0 --- /dev/null +++ b/joint-cloud-common/src/main/java/com/joint/cloud/common/utils/IDUtil.java @@ -0,0 +1,86 @@ +package com.joint.cloud.common.utils; + +import org.springframework.util.StringUtils; + +import java.util.Random; +import java.util.UUID; + +/** + * @author lilengyi + * @date 2019/4/26 + */ +public class IDUtil { + + private static final String[] chars = new String[] { "a", "b", "c", "d", + "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", + "r", "s", "t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", + "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", + "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", + "U", "V", "W", "X", "Y", "Z" }; + + public static String UUID() { + return UUID.randomUUID().toString().replace("-", ""); + } + + public static String Short_UUID() { + StringBuffer shortBuffer = new StringBuffer(); + String uuid = UUID.randomUUID().toString().replace("-", ""); + for (int i = 0; i < 8; i++) { + String str = uuid.substring(i * 4, i * 4 + 4); + int x = Integer.parseInt(str, 16); + shortBuffer.append(chars[x % 0x3E]); + } + return shortBuffer.toString(); + + } + + /** + * 名称后添加n位随机串 + * @param name + * @param maxlength + * @return + */ + public static String generateName(String name, int maxlength) { + if (!StringUtils.hasLength(name)) { + throw new RuntimeException("generate name failed, name is null"); + } + return name.concat("_").concat(getRandomString(maxlength)); + } + + /** + * 构成规律:生成随机串 数字字母组合 + * + * * + */ + private static String getRandomString(int length){ + //产生随机数 + Random random=new Random(); + StringBuffer sb=new StringBuffer(); + //循环length次 + for(int i=0; i T deserialize(final String jsonStr, final Class cls) { + return deserialize(jsonStr, MAPPER.getTypeFactory().constructType(cls)); + } + + public static T deserialize(final String jsonStr, final JavaType type) { + if (jsonStr == null) { + return null; + } + + try { + return MAPPER.readValue(jsonStr, type); + } catch (IOException ex) { + log.error("failed deserialize string: {} to Class {}'s instance!", jsonStr, type); + + throw new RuntimeException(ex); + } + } + + /** + * 获取泛型的Collection Type + * + * @param collectionClass 泛型的Collection + * @param elementClasses 元素类 + * @return JavaType Java类型 + * @since 1.0 + */ + public static JavaType getCollectionType(Class collectionClass, Class... elementClasses) { + return MAPPER.getTypeFactory().constructParametricType(collectionClass, elementClasses); + } + + /** + * 转换Json到List + * + * @param cls:映射的类 json字符串转换成list. + */ + public static List getList(String serialize, Class cls) throws IOException { + JavaType javaType = MAPPER.getTypeFactory().constructParametricType(ArrayList.class, cls); + return MAPPER.readValue(serialize, javaType); + } + + public static JsonNode jsonNode(final String jsonStr) { + + try { + return MAPPER.readTree(jsonStr); + } catch (IOException ex) { + log.error("failed deserialize string: {} to JsonNode", jsonStr); + } + return null; + } + + public static T convertValue(Object fromValue, Class toValueType) { + return MAPPER.convertValue(fromValue, toValueType); + } +} diff --git a/joint-cloud-common/src/main/java/com/joint/cloud/common/utils/ListUtil.java b/joint-cloud-common/src/main/java/com/joint/cloud/common/utils/ListUtil.java new file mode 100644 index 0000000..3557fb1 --- /dev/null +++ b/joint-cloud-common/src/main/java/com/joint/cloud/common/utils/ListUtil.java @@ -0,0 +1,67 @@ +package com.joint.cloud.common.utils; + +import java.util.*; + +/** + * @author: zhanglc + * @date: 2020/4/17 11:35 + */ +public class ListUtil { + + public static boolean isListEqual(List list1, List list2) { + // 两个list引用相同(包括两者都为空指针的情况) + if (list1 == list2) { + return true; + } + if((list1 == null && list2!=null && list2.size()!=0) + || (list2 == null && list1 != null && list1.size() != 0)){ + return false; + } + // 两个list都为空(包括空指针、元素个数为0) + if ((list1 == null && list2 != null && list2.size() == 0) + || (list2 == null && list1 != null && list1.size() == 0)) { + return true; + } + + // 两个list元素个数不相同 + if (list1.size() != list2.size()) { + return false; + } + + // 两个list元素个数已经相同,再比较两者内容 + // 采用这种可以忽略list中的元素的顺序 + // 涉及到对象的比较是否相同时,确保实现了equals()方法 + if (!list1.containsAll(list2) || !list2.containsAll(list1)) { + return false; + } + + return true; + } + + public static boolean isRepeatInCollection(Collection datas) { + if (datas == null) {// 为空则认为不含重复元素 + return false; + } + if (datas instanceof Set) {//如果是set则不含有重复元素 + return false; + } + Set noRepeatSet = new HashSet<>(datas); + return !(datas.size() == noRepeatSet.size()); + } + + public static List findRepeat(Collection datas) { + if (datas instanceof Set) { + return new ArrayList<>(); + } + HashSet set = new HashSet(); + List repeatEles = new ArrayList(); + for (T t : datas) { + if (set.contains(t)) { + repeatEles.add(t); + } else { + set.add(t); + } + } + return repeatEles; + } +} diff --git a/joint-cloud-common/src/main/java/com/joint/cloud/common/utils/MD5Util.java b/joint-cloud-common/src/main/java/com/joint/cloud/common/utils/MD5Util.java new file mode 100644 index 0000000..bdc2600 --- /dev/null +++ b/joint-cloud-common/src/main/java/com/joint/cloud/common/utils/MD5Util.java @@ -0,0 +1,97 @@ +/** + * + */ +package com.joint.cloud.common.utils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.List; + + +public class MD5Util { + + private static final Logger s_logger = LoggerFactory.getLogger(MD5Util.class); + + private static final String MD5_ALGORITHM = "MD5"; + + private static final int HEX_NUM = 0xff; + + /** + * md5加密 + * + * @param str + * @return + * @author wangyzh + * @date 2012-12-7 下午12:54:02 + */ + public static String md5Encode(String str) { + if (str == null) { + s_logger.warn("String is null for md5 encoder."); + return null; + } + try { + MessageDigest mdEncoder = MessageDigest.getInstance(MD5_ALGORITHM); + mdEncoder.update(str.getBytes()); + byte[] bytes = mdEncoder.digest(); + return convertToHEX(bytes); + } catch (NoSuchAlgorithmException e) { + s_logger.warn("Failed to encode string . There is no such algorithm.", e); + return null; + } + } + + /** + * 二进制转换成十六进制 + * + * @param bytes + * @return + * @author wangyzh + * @date 2012-12-7 下午12:58:26 + */ + private static String convertToHEX(byte[] bytes) { + if (bytes == null) { + return null; + } + StringBuffer stringBuilder = new StringBuffer(); + for (byte b : bytes) { + int value = b & HEX_NUM; + if (value < 16) { + stringBuilder.append(0); + } + stringBuilder.append(Integer.toHexString(value)); + } + return stringBuilder.toString(); + } + + public static String genMD5(List stringList){ + try { + StringBuffer sb = new StringBuffer(); + for (String s : stringList) { + sb.append(s); + } + MessageDigest md = MessageDigest.getInstance("MD5"); + md.update(sb.toString().getBytes()); + byte b[] = md.digest(); + int i; + StringBuffer buf = new StringBuffer(); + for (int offset = 0; offset < b.length; offset++) { + i = b[offset]; + if (i < 0) { + i += 256; + } + if (i < 16) { + buf.append("0"); + } + buf.append(Integer.toHexString(i)); + } + String result = buf.toString(); + return result; + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + return null; + } +} diff --git a/joint-cloud-common/src/main/java/com/joint/cloud/common/utils/ReflectionUtils.java b/joint-cloud-common/src/main/java/com/joint/cloud/common/utils/ReflectionUtils.java new file mode 100644 index 0000000..5f64ce6 --- /dev/null +++ b/joint-cloud-common/src/main/java/com/joint/cloud/common/utils/ReflectionUtils.java @@ -0,0 +1,340 @@ +package com.joint.cloud.common.utils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.Assert; + +import java.lang.reflect.*; + + +/** + * @description: + * @author: Mr.Ag + * @date: Created in 2018/5/10 0010 + * @modified By: + * @version: 1.0 + **/ +public class ReflectionUtils { + + private static final String SETTER_PREFIX = "set"; + + private static final String GETTER_PREFIX = "get"; + + private static final String CGLIB_CLASS_SEPARATOR = "$$"; + + private static Logger logger = LoggerFactory.getLogger(ReflectionUtils.class); + + /** + * 调用Getter方法. + * 支持多级,如:对象名.对象名.方法 + */ + public static Object invokeGetter(Object obj, String propertyName) { + Object object = obj; + for (String name : StringUtils.split(propertyName, ".")){ + String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name); + object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {}); + } + return object; + } + + /** + * 调用Setter方法, 仅匹配方法名。 + * 支持多级,如:对象名.对象名.方法 + */ + public static void invokeSetter(Object obj, String propertyName, Object value) { + Object object = obj; + String[] names = StringUtils.split(propertyName, "."); + for (int i=0; i type) { + if (object instanceof Number) { + Number number = (Number) object; + if (type.equals(byte.class) || type.equals(Byte.class)) { + return number.byteValue(); + } + if (type.equals(short.class) || type.equals(Short.class)) { + return number.shortValue(); + } + if (type.equals(int.class) || type.equals(Integer.class)) { + return number.intValue(); + } + if (type.equals(long.class) || type.equals(Long.class)) { + return number.longValue(); + } + if (type.equals(float.class) || type.equals(Float.class)) { + return number.floatValue(); + } + if (type.equals(double.class) || type.equals(Double.class)) { + return number.doubleValue(); + } + } + if(type.equals(String.class)){ + return object==null?"":object.toString(); + } + return object; + } + + /** + * 直接调用对象方法, 无视private/protected修饰符. + * 用于一次性调用的情况,否则应使用getAccessibleMethod()函数获得Method后反复调用. + * 同时匹配方法名+参数类型, + */ + public static Object invokeMethod(final Object obj, final String methodName, final Class[] parameterTypes, + final Object[] args) { + Method method = getAccessibleMethod(obj, methodName, parameterTypes); + if (method == null) { + throw new IllegalArgumentException("Could not find method [" + methodName + "] on target [" + obj + "]"); + } + + try { + return method.invoke(obj, args); + } catch (Exception e) { + throw convertReflectionExceptionToUnchecked(e); + } + } + + /** + * 直接调用对象方法, 无视private/protected修饰符, + * 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用. + * 只匹配函数名,如果有多个同名函数调用第一个。 + */ + public static Object invokeMethodByName(final Object obj, final String methodName, final Object[] args) { + Method method = getAccessibleMethodByName(obj, methodName); + if (method == null) { + throw new IllegalArgumentException("Could not find method [" + methodName + "] on target [" + obj + "]"); + } + + try { + return method.invoke(obj, args); + } catch (Exception e) { + throw convertReflectionExceptionToUnchecked(e); + } + } + + /** + * 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问. + * + * 如向上转型到Object仍无法找到, 返回null. + */ + public static Field getAccessibleField(final Object obj, final String fieldName) { +// Validate.notNull(obj, "object can't be null"); +// Validate.notEmpty(fieldName, "fieldName can't be blank"); + for (Class superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) { + try { + Field field = superClass.getDeclaredField(fieldName); + makeAccessible(field); + return field; + } catch (NoSuchFieldException e) {//NOSONAR + // Field不在当前类定义,继续向上转型 + continue;// new add + } + } + return null; + } + + /** + * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. + * 如向上转型到Object仍无法找到, 返回null. + * 匹配函数名+参数类型。 + * + * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args) + */ + public static Method getAccessibleMethod(final Object obj, final String methodName, + final Class... parameterTypes) { +// Validate.notNull(obj, "object can't be null"); +// Validate.notEmpty(methodName, "methodName can't be blank"); + + for (Class searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) { + try { + Method method = searchType.getDeclaredMethod(methodName, parameterTypes); + makeAccessible(method); + return method; + } catch (NoSuchMethodException e) { + // Method不在当前类定义,继续向上转型 + continue;// new add + } + } + return null; + } + + /** + * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. + * 如向上转型到Object仍无法找到, 返回null. + * 只匹配函数名。 + * + * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args) + */ + public static Method getAccessibleMethodByName(final Object obj, final String methodName) { +// Validate.notNull(obj, "object can't be null"); +// Validate.notEmpty(methodName, "methodName can't be blank"); + + for (Class searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) { + Method[] methods = searchType.getDeclaredMethods(); + for (Method method : methods) { + if (method.getName().equals(methodName)) { + makeAccessible(method); + return method; + } + } + } + return null; + } + + /** + * 改变private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。 + */ + public static void makeAccessible(Method method) { + if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) + && !method.isAccessible()) { + method.setAccessible(true); + } + } + + /** + * 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。 + */ + public static void makeAccessible(Field field) { + if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) || Modifier + .isFinal(field.getModifiers())) && !field.isAccessible()) { + field.setAccessible(true); + } + } + + /** + * 通过反射, 获得Class定义中声明的泛型参数的类型, 注意泛型必须定义在父类处 + * 如无法找到, 返回Object.class. + * eg. + * public UserDao extends HibernateDao + * + * @param clazz The class to introspect + * @return the first generic declaration, or Object.class if cannot be determined + */ + @SuppressWarnings("unchecked") + public static Class getClassGenricType(final Class clazz) { + return getClassGenricType(clazz, 0); + } + + /** + * 通过反射, 获得Class定义中声明的父类的泛型参数的类型. + * 如无法找到, 返回Object.class. + * + * 如public UserDao extends HibernateDao + * + * @param clazz clazz The class to introspect + * @param index the Index of the generic ddeclaration,start from 0. + * @return the index generic declaration, or Object.class if cannot be determined + */ + public static Class getClassGenricType(final Class clazz, final int index) { + + Type genType = clazz.getGenericSuperclass(); + + if (!(genType instanceof ParameterizedType)) { + logger.warn(clazz.getSimpleName() + "'s superclass not ParameterizedType"); + return Object.class; + } + + Type[] params = ((ParameterizedType) genType).getActualTypeArguments(); + + if (index >= params.length || index < 0) { + logger.warn("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: " + + params.length); + return Object.class; + } + if (!(params[index] instanceof Class)) { + logger.warn(clazz.getSimpleName() + " not set the actual class on superclass generic parameter"); + return Object.class; + } + + return (Class) params[index]; + } + + public static Class getUserClass(Object instance) { + Assert.notNull(instance, "Instance must not be null"); + Class clazz = instance.getClass(); + if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) { + Class superClass = clazz.getSuperclass(); + if (superClass != null && !Object.class.equals(superClass)) { + return superClass; + } + } + return clazz; + + } + + /** + * 将反射时的checked exception转换为unchecked exception. + */ + public static RuntimeException convertReflectionExceptionToUnchecked(Exception e) { + if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException + || e instanceof NoSuchMethodException) { + return new IllegalArgumentException(e); + } else if (e instanceof InvocationTargetException) { + return new RuntimeException(((InvocationTargetException) e).getTargetException()); + } else if (e instanceof RuntimeException) { + return (RuntimeException) e; + } + return new RuntimeException("Unexpected Checked Exception.", e); + } + /** + * 判断某个对象是否拥有某个属性 + * + * @param obj 对象 + * @param fieldName 属性名 + * @return 有属性返回true + * 无属性返回false + */ + public static boolean hasField(final Object obj, final String fieldName){ + Field field = getAccessibleField(obj, fieldName); + if (field == null) { + return false; + } + return true; + + } +} diff --git a/joint-cloud-common/src/main/java/com/joint/cloud/common/utils/SpringUtil.java b/joint-cloud-common/src/main/java/com/joint/cloud/common/utils/SpringUtil.java new file mode 100644 index 0000000..c90205f --- /dev/null +++ b/joint-cloud-common/src/main/java/com/joint/cloud/common/utils/SpringUtil.java @@ -0,0 +1,63 @@ +package com.joint.cloud.common.utils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.ApplicationEvent; +import org.springframework.stereotype.Component; + +import java.beans.Introspector; + +@Component +public class SpringUtil implements ApplicationContextAware { + + private static Logger logger = LoggerFactory.getLogger(SpringUtil.class); + + private static ApplicationContext applicationContext; + + @Override + public void setApplicationContext(ApplicationContext applicationContextParam) throws BeansException { + applicationContext = applicationContextParam; + } + + public static Object getObject(String id) { + Object object = null; + object = applicationContext.getBean(id); + return object; + } + + public static T getObject(Class tClass) { + return applicationContext.getBean(tClass); + } + + public static Object getBean(String tClass) { + return applicationContext.getBean(tClass); + } + + public static T getBean(Class tClass) { + return applicationContext.getBean(tClass); + } + + public static T getBean(String beanId, Class requiredType) { + if (applicationContext == null) { + logger.warn("Spring context has not bean initilaized!"); + return null; + } + return applicationContext.getBean(beanId, requiredType); + } + + public static String toBeanName(String simpleClassName) { + + //参照Spring Context中AnnotationBeanNameGenerator的方法 + return Introspector.decapitalize(simpleClassName); +// return simpleClassName.substring(0, 1).toLowerCase() +// + simpleClassName.substring(1); + + + } + public static void publishEvent(ApplicationEvent event){ + applicationContext.publishEvent(event); + } +} \ No newline at end of file diff --git a/joint-cloud-common/src/main/java/com/joint/cloud/common/utils/StringUtils.java b/joint-cloud-common/src/main/java/com/joint/cloud/common/utils/StringUtils.java new file mode 100644 index 0000000..00e6458 --- /dev/null +++ b/joint-cloud-common/src/main/java/com/joint/cloud/common/utils/StringUtils.java @@ -0,0 +1,71 @@ +package com.joint.cloud.common.utils; + +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.springframework.lang.Nullable; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * @author lilengyi + * @date 2020/3/30 18:33 + */ +public class StringUtils extends org.springframework.util.StringUtils { + + public static String getStackMessage(Throwable throwable) { + String fullStackTrace = ExceptionUtils.getStackTrace(throwable); + if (fullStackTrace.length() > 500) { + fullStackTrace = fullStackTrace.substring(0, 500); + } + return fullStackTrace; + } + + public static String[] split(@Nullable String toSplit, @Nullable String delimiter) { + if (!hasLength(toSplit) || !hasLength(delimiter)) { + return null; + } + int offset = toSplit.indexOf(delimiter); + if (offset < 0) { + return new String[]{toSplit}; + } + String[] values = toSplit.split(delimiter); + return values; + } + + public static String removeFromString(String values, String value) { + // 返回结果 + String result = ""; + // 判断是否存在。如果存在,移除指定用户 ID;如果不存在,则直接返回空 + if(values.indexOf(",") != -1) { + // 拆分成数组 + String[] array = values.split(","); + // 数组转集合 + List list = new ArrayList(Arrays.asList(array)); + // 移除指定值 + list.remove(value); + // 把剩下的值再拼接起来 + result = StringUtils.collectionToDelimitedString(list, ","); + } + // 返回 + return result; + } + public static String addToString(String values, String value) { + // 返回结果 + String result = ""; + // 拆分成数组 + if(hasLength(values)) { + String[] array = values.split(","); + // 数组转集合 + List list = new ArrayList(Arrays.asList(array)); + // 移除指定值 + list.add(value); + // 把剩下的值再拼接起来 + result = StringUtils.collectionToDelimitedString(list, ","); + }else{ + result = value; + } + // 返回 + return result; + } +} diff --git a/joint-cloud-common/src/main/java/com/joint/cloud/common/utils/TimeUtil.java b/joint-cloud-common/src/main/java/com/joint/cloud/common/utils/TimeUtil.java new file mode 100644 index 0000000..7c78abd --- /dev/null +++ b/joint-cloud-common/src/main/java/com/joint/cloud/common/utils/TimeUtil.java @@ -0,0 +1,249 @@ +package com.joint.cloud.common.utils; + +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; + +/** + * @author lilengyi + * @date 2020/1/20 15:04 + */ +public class TimeUtil { + public static final String dateFormat = "yyyyMMddHHmmss"; + + public static String toTimeString(Date time) { + DateFormat df = new SimpleDateFormat(dateFormat); + return df.format(time); + } + + public static Date fromTimeString(String timeStr) { + DateFormat df = new SimpleDateFormat(dateFormat); + try { + return df.parse(timeStr); + } catch (ParseException e) { + e.printStackTrace(); + } + return null; + } + + /** + * 向下取整小时 + * @param time + * @return + */ + public static Date toHourRoundDown(Date time){ + DateFormat df = new SimpleDateFormat(dateFormat); + String hour = df.format(time); + hour = hour.substring(0, 10).concat("0000"); + try { + return df.parse(hour); + } catch (ParseException e) { + e.printStackTrace(); + } + return null; + } + + /** + * 向上取整小时 + * @param time + * @return + */ + public static Date toHourRoundUp(Date time){ + DateFormat df = new SimpleDateFormat(dateFormat); + String timeStr = df.format(time); + if (!"0000".equals(timeStr.substring(10, 14))){ + Calendar c = Calendar.getInstance(); + c.setTime(toHourRoundDown(time)); + c.add(Calendar.HOUR_OF_DAY, 1);// +1天 + return c.getTime(); + } else { + return time; + } + } + + /** + * 取下一小时 + * @param time + * @return + */ + public static Date toNextHour(Date time){ + Calendar c = Calendar.getInstance(); + c.setTime(time); + c.add(Calendar.HOUR_OF_DAY, 1);// +1天 + return c.getTime(); + } + + /** + * 取前一小时 + * @param time + * @return + */ + public static Date toPreHour(Date time){ + Calendar c = Calendar.getInstance(); + c.setTime(time); + c.add(Calendar.HOUR_OF_DAY, -1);// +1天 + return c.getTime(); + } + + /** + * 向下取整天数,设为当天0点 + * @param time + * @return + */ + public static Date toDayRoundDown(Date time){ + DateFormat df = new SimpleDateFormat(dateFormat); + String dayTime = df.format(time); + dayTime = dayTime.substring(0, 8).concat("000000"); + try { + return df.parse(dayTime); + } catch (ParseException e) { + e.printStackTrace(); + } + return time; + } + + /** + * 向上取整天数,如果时间不为整天设为第二天0点 + * @param time + * @return + */ + public static Date toDayRoundUp(Date time){ + DateFormat df = new SimpleDateFormat(dateFormat); + String timeStr = df.format(time); + if (!"000000".equals(timeStr.substring(8, 14))){ + Calendar c = Calendar.getInstance(); + c.setTime(toDayRoundDown(time)); + c.add(Calendar.DAY_OF_MONTH, 1);// +1天 + return c.getTime(); + } else { + return time; + } + } + + /** + * 取下一天日期 + * @param time + * @return + */ + public static Date toNextDay(Date time){ + Calendar c = Calendar.getInstance(); + c.setTime(time); + c.add(Calendar.DAY_OF_MONTH, 1);// +1天 + return c.getTime(); + } + + /** + * 取前一天日期 + * @param time + * @return + */ + public static Date toPreDay(Date time){ + Calendar c = Calendar.getInstance(); + c.setTime(time); + c.add(Calendar.DAY_OF_MONTH, -1);// +1天 + return c.getTime(); + } + + /** + * 向下取整月数,设为当月1日0点 + * @param time + * @return + */ + public static Date toMonthRoundDown(Date time){ + DateFormat df = new SimpleDateFormat(dateFormat); + String monthTime = df.format(time); + monthTime = monthTime.substring(0, 6).concat("01000000"); + try { + return df.parse(monthTime); + } catch (ParseException e) { + e.printStackTrace(); + } + return null; + } + + /** + * 向上取整天数,如果时间不为整月设为下个月1号0点 + * @param time + * @return + */ + public static Date toMonthRoundUp(Date time){ + DateFormat df = new SimpleDateFormat(dateFormat); + String timeStr = df.format(time); + if (!"01000000".equals(timeStr.substring(6, 14))){ + return toNextMonth(toMonthRoundDown(time)); + } else { + return time; + } + + } + + /** + * 取下一月日期 + * @param time + * @return + */ + public static Date toNextMonth(Date time){ + Calendar c = Calendar.getInstance(); + c.setTime(time); + c.add(Calendar.MONTH, 1);// +1月 + return c.getTime(); + } + + /** + * 取上一月日期 + * @param time + * @return + */ + public static Date toPreMonth(Date time){ + Calendar c = Calendar.getInstance(); + c.setTime(time); + c.add(Calendar.MONTH, -1);// -1月 + return c.getTime(); + } + + /** + * 向下取整年,设为当年1日0点 + * @param time + * @return + */ + public static Date toYearRoundDown(Date time){ + DateFormat df = new SimpleDateFormat(dateFormat); + String monthTime = df.format(time); + monthTime = monthTime.substring(0, 4).concat("0101000000"); + try { + return df.parse(monthTime); + } catch (ParseException e) { + e.printStackTrace(); + } + return null; + } + + /** + * 向下取整年,设为当年1日0点 + * @param time + * @return + */ + public static Date toYearRoundUp(Date time){ + DateFormat df = new SimpleDateFormat(dateFormat); + String timeStr = df.format(time); + if (!"0101000000".equals(timeStr.substring(4, 14))){ + return toNextYear(toYearRoundDown(time)); + } else { + return time; + } + } + + /** + * 取下一年日期 + * @param time + * @return + */ + public static Date toNextYear(Date time){ + Calendar c = Calendar.getInstance(); + c.setTime(time); + c.add(Calendar.YEAR, 1);// +1月 + return c.getTime(); + } +} diff --git a/joint-cloud-common/src/main/java/com/joint/cloud/common/utils/UnitUtil.java b/joint-cloud-common/src/main/java/com/joint/cloud/common/utils/UnitUtil.java new file mode 100644 index 0000000..ee37ada --- /dev/null +++ b/joint-cloud-common/src/main/java/com/joint/cloud/common/utils/UnitUtil.java @@ -0,0 +1,39 @@ +package com.joint.cloud.common.utils; + +/** + * @author: zhanglc + * @date: 2020/1/3 14:49 + */ +public class UnitUtil { + + public static final long B = 1; + + public static final long KB = B * 1024; + + public static final long MB = KB * 1024; + + public static final long GB = MB * 1024; + + public static final long TB = GB * 1024; + + public static final String B_STRING = "B"; + + public static final String KB_STRING = "KB"; + + public static final String MB_STRING = "MB"; + + public static final String GB_STRING = "GB"; + + public static final String CORE_STRING = "Core"; + + public static final double ONE = 1.0d; + + public static final long SECOND = 1000; + + public static final long MINUTE = 60 * SECOND; + + public static final long HOUR = 60 * MINUTE; + + public static final long DAY = 24 * HOUR; + +} diff --git a/joint-cloud-common/src/main/java/com/joint/cloud/common/utils/UserUtil.java b/joint-cloud-common/src/main/java/com/joint/cloud/common/utils/UserUtil.java new file mode 100644 index 0000000..d59e4df --- /dev/null +++ b/joint-cloud-common/src/main/java/com/joint/cloud/common/utils/UserUtil.java @@ -0,0 +1,38 @@ +package com.joint.cloud.common.utils; + +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; + +/** + * @author lilengyi + * @date 2020/4/10 15:50 + */ +public class UserUtil { + public static String getCurrentUser(){ + if (null != RequestContextHolder.getRequestAttributes()) { + HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); + if (null != request.getHeader("username")) { + // 也可获取到roles userId +// request.getHeader("roles"); +// request.getHeader("userId"); + return request.getHeader("username"); + } + } + return "system"; + } + + + public static String getCurrentIp(){ + if(null != RequestContextHolder.getRequestAttributes()) { + HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); + String ip =request.getHeader("X-Real-IP"); + if(!StringUtils.hasLength(ip)) { + return null; + } + return ip; + } + return "system"; + } +} diff --git a/joint-cloud-common/src/main/java/com/joint/cloud/common/utils/ValidatorUtil.java b/joint-cloud-common/src/main/java/com/joint/cloud/common/utils/ValidatorUtil.java new file mode 100644 index 0000000..1327807 --- /dev/null +++ b/joint-cloud-common/src/main/java/com/joint/cloud/common/utils/ValidatorUtil.java @@ -0,0 +1,24 @@ +package com.joint.cloud.common.utils; + +import com.joint.cloud.common.exception.AIOpsException; +import com.joint.cloud.common.exception.msg.NullParameterExceptionErrorMsg; + +/** + * @author lilengyi + * @date 2020/1/3 14:58 + */ +public class ValidatorUtil { + + /** + * 参数校验,判断是否为空 + * + * @param param + * @param paramName + */ + public static void paramNotNull(Object param, String paramName) { + if (param == null) { + throw new AIOpsException( + new NullParameterExceptionErrorMsg(paramName)); + } + } +} diff --git a/joint-cloud-gateway/pom.xml b/joint-cloud-gateway/pom.xml new file mode 100644 index 0000000..fed21bb --- /dev/null +++ b/joint-cloud-gateway/pom.xml @@ -0,0 +1,76 @@ + + + + joint-cloud + com.joint.cloud + 1.0.0-SNAPSHOT + + 4.0.0 + + joint-cloud-gateway + joint-cloud-gateway + ${joint.cloud.version} + jar + + + 8 + 8 + + + + + org.springframework.cloud + spring-cloud-starter-oauth2 + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.cloud + spring-cloud-starter-netflix-zuul + + + org.springframework.cloud + spring-cloud-starter-consul-discovery + + + org.springframework.cloud + spring-cloud-starter-netflix-ribbon + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + \ No newline at end of file diff --git a/joint-cloud-gateway/src/main/java/com/joint/cloud/gateway/GatewayApplication.java b/joint-cloud-gateway/src/main/java/com/joint/cloud/gateway/GatewayApplication.java new file mode 100644 index 0000000..bb8f0e1 --- /dev/null +++ b/joint-cloud-gateway/src/main/java/com/joint/cloud/gateway/GatewayApplication.java @@ -0,0 +1,41 @@ +package com.joint.cloud.gateway; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.cloud.netflix.zuul.EnableZuulProxy; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.context.annotation.Bean; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; + +@SpringBootApplication +@EnableDiscoveryClient +@EnableZuulProxy +@EnableFeignClients +public class GatewayApplication { + + public static void main(String[] args) { + SpringApplication.run(GatewayApplication.class, args); + } + + @Bean + public CorsFilter corsFilter() { + final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + final CorsConfiguration config = new CorsConfiguration(); + config.setAllowCredentials(true); + config.addAllowedOrigin("*"); + config.addAllowedHeader("*"); + config.addAllowedMethod("OPTIONS"); + config.addAllowedMethod("HEAD"); + config.addAllowedMethod("GET"); + config.addAllowedMethod("PUT"); + config.addAllowedMethod("POST"); + config.addAllowedMethod("DELETE"); + config.addAllowedMethod("PATCH"); + source.registerCorsConfiguration("/**", config); + return new CorsFilter(source); + } + +} diff --git a/joint-cloud-gateway/src/main/java/com/joint/cloud/gateway/config/FilterConfig.java b/joint-cloud-gateway/src/main/java/com/joint/cloud/gateway/config/FilterConfig.java new file mode 100644 index 0000000..f9261c6 --- /dev/null +++ b/joint-cloud-gateway/src/main/java/com/joint/cloud/gateway/config/FilterConfig.java @@ -0,0 +1,31 @@ +package com.joint.cloud.gateway.config; + +import com.joint.cloud.gateway.security.HeaderEnhanceFilter; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.Ordered; + +/** + * @author super.wu + */ +@Configuration +@EnableAutoConfiguration +public class FilterConfig { + // + + @Bean + public FilterRegistrationBean filterRegistrationBean() { + FilterRegistrationBean registrationBean = new FilterRegistrationBean(); + registrationBean.setFilter(headerEnhanceFilter()); + registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE); + return registrationBean; + } + + @Bean + public HeaderEnhanceFilter headerEnhanceFilter() { + return new HeaderEnhanceFilter(); + } + +} diff --git a/joint-cloud-gateway/src/main/java/com/joint/cloud/gateway/config/ResourceServerConfig.java b/joint-cloud-gateway/src/main/java/com/joint/cloud/gateway/config/ResourceServerConfig.java new file mode 100644 index 0000000..e9c9adb --- /dev/null +++ b/joint-cloud-gateway/src/main/java/com/joint/cloud/gateway/config/ResourceServerConfig.java @@ -0,0 +1,51 @@ +package com.joint.cloud.gateway.config; + +import com.joint.cloud.gateway.properties.PermitAllUrlProperties; +import com.joint.cloud.gateway.security.CustomRemoteTokenServices; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties; +import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; +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.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer; + +/** + * @author super.wu + */ +@Configuration +@EnableResourceServer +public class ResourceServerConfig extends ResourceServerConfigurerAdapter { + + @Autowired + private LoadBalancerClient loadBalancerClient; + + @Autowired + private ResourceServerProperties resource; + + @Autowired + private PermitAllUrlProperties permitAllUrlProperties; + + + @Override + public void configure(HttpSecurity http) throws Exception { + http.csrf().disable() + .requestMatchers().antMatchers("/**") + .and() + .authorizeRequests() + .antMatchers(permitAllUrlProperties.getPermitallPatterns()).permitAll() + .anyRequest().authenticated(); + } + + @Override + public void configure(ResourceServerSecurityConfigurer resources) { + CustomRemoteTokenServices resourceServerTokenServices = new CustomRemoteTokenServices(); + resourceServerTokenServices.setCheckTokenEndpointUrl(resource.getTokenInfoUri()); + resourceServerTokenServices.setClientId(resource.getClientId()); + resourceServerTokenServices.setClientSecret(resource.getClientSecret()); + resourceServerTokenServices.setLoadBalancerClient(loadBalancerClient); + resources.tokenServices(resourceServerTokenServices); + } + +} diff --git a/joint-cloud-gateway/src/main/java/com/joint/cloud/gateway/config/ServiceConfig.java b/joint-cloud-gateway/src/main/java/com/joint/cloud/gateway/config/ServiceConfig.java new file mode 100644 index 0000000..be63ac8 --- /dev/null +++ b/joint-cloud-gateway/src/main/java/com/joint/cloud/gateway/config/ServiceConfig.java @@ -0,0 +1,27 @@ +package com.joint.cloud.gateway.config; + +import com.joint.cloud.gateway.properties.PermitAllUrlProperties; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author keets + * @date 2017/9/29 + */ +@Configuration +public class ServiceConfig { + + + @Value("${server.port}") + private int securePort; + + + @Bean + @ConfigurationProperties(prefix = "auth") + public PermitAllUrlProperties getPermitAllUrlProperties() { + return new PermitAllUrlProperties(); + } + +} diff --git a/joint-cloud-gateway/src/main/java/com/joint/cloud/gateway/config/WebLogAspect.java b/joint-cloud-gateway/src/main/java/com/joint/cloud/gateway/config/WebLogAspect.java new file mode 100644 index 0000000..bc2ebcb --- /dev/null +++ b/joint-cloud-gateway/src/main/java/com/joint/cloud/gateway/config/WebLogAspect.java @@ -0,0 +1,53 @@ +package com.joint.cloud.gateway.config; + +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.annotation.Pointcut; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; + +@Aspect +@Component +public class WebLogAspect { + + private final Logger logger = LoggerFactory.getLogger("apiLogger"); + + @Pointcut("execution(public * com.joint.cloud.gateway.controller..*.*(..))")//切入点描述 这个是controller包的切入点 + public void controllerLog(){}//签名,可以理解成这个切入点的一个名称 + +// @Pointcut("execution(public * com.stuPayment.uiController..*.*(..))")//切入点描述,这个是uiController包的切入点 +// public void uiControllerLog(){} + + @Before("controllerLog()") //在切入点的方法run之前要干的 + public void logBeforeController(JoinPoint joinPoint) { + + RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();//这个RequestContextHolder是Springmvc提供来获得请求的东西 + HttpServletRequest request = ((ServletRequestAttributes)requestAttributes).getRequest(); + + // 过滤 health check + if ("com.joint.cloud.gateway.controller.HealthController".equals(joinPoint.getSignature().getDeclaringTypeName())) { + return; + } + + // 记录下请求内容 + logger.info("### URL : " + request.getRequestURL().toString()); + logger.info("### HTTP_METHOD : " + request.getMethod()); + logger.info("### IP : " + request.getRemoteAddr()); +// logger.info("################THE ARGS OF THE CONTROLLER : " + Arrays.toString(joinPoint.getArgs())); + + //下面这个getSignature().getDeclaringTypeName()是获取包+类名的 然后后面的joinPoint.getSignature.getName()获取了方法名 + logger.info("### CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName()); + //logger.info("################TARGET: " + joinPoint.getTarget());//返回的是需要加强的目标类的对象 + //logger.info("################THIS: " + joinPoint.getThis());//返回的是经过加强后的代理类的对象 + + } + + +} \ No newline at end of file diff --git a/joint-cloud-gateway/src/main/java/com/joint/cloud/gateway/config/WebSocketFilter.java b/joint-cloud-gateway/src/main/java/com/joint/cloud/gateway/config/WebSocketFilter.java new file mode 100644 index 0000000..10638dc --- /dev/null +++ b/joint-cloud-gateway/src/main/java/com/joint/cloud/gateway/config/WebSocketFilter.java @@ -0,0 +1,42 @@ +package com.joint.cloud.gateway.config; + +import com.netflix.zuul.ZuulFilter; +import com.netflix.zuul.context.RequestContext; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; + +@Component +public class WebSocketFilter extends ZuulFilter { + @Override + public String filterType() { + return "pre"; + + } + + @Override + public int filterOrder() { + return 0; + + } + + @Override + public boolean shouldFilter() { + return true; + + } + + @Override + public Object run() { + RequestContext context = RequestContext.getCurrentContext(); + HttpServletRequest request = context.getRequest(); + String upgradeHeader = request.getHeader("Upgrade"); + if (null == upgradeHeader) { + upgradeHeader = request.getHeader("upgrade"); + } + if (null != upgradeHeader && "websocket".equalsIgnoreCase(upgradeHeader)) { + context.addZuulRequestHeader("connection", "Upgrade"); + } + return null; + } +} \ No newline at end of file diff --git a/joint-cloud-gateway/src/main/java/com/joint/cloud/gateway/constants/SecurityConstants.java b/joint-cloud-gateway/src/main/java/com/joint/cloud/gateway/constants/SecurityConstants.java new file mode 100644 index 0000000..3a01937 --- /dev/null +++ b/joint-cloud-gateway/src/main/java/com/joint/cloud/gateway/constants/SecurityConstants.java @@ -0,0 +1,45 @@ +package com.joint.cloud.gateway.constants; + +/** + * Created by keets on 2017/12/9. + */ +//@Component +//@Configuration +public class SecurityConstants { + +// public static final String USER_ID_IN_HEADER = "X-AOHO-UserId"; + +// public static final String AUTH_SERVICE = "nycloud-auth-server"; + +// @Value("${authserver.user-id-in-header}") + private String userIdInHeader = "X-AOHO-UserId"; + + private String realIPInHeader = "X-Real-IP"; + +// @Value("${authserver.service-id}") + private String authServerServiceId = "joint-cloud-auth"; + + public String getUserIdInHeader() { + return userIdInHeader; + } + + public void setUserIdInHeader(String userIdInHeader) { + this.userIdInHeader = userIdInHeader; + } + + public String getAuthServerServiceId() { + return authServerServiceId; + } + + public void setAuthServerServiceId(String authServerServiceId) { + this.authServerServiceId = authServerServiceId; + } + + public String getRealIPInHeader() { + return realIPInHeader; + } + + public void setRealIPInHeader(String realIPInHeader) { + this.realIPInHeader = realIPInHeader; + } +} diff --git a/joint-cloud-gateway/src/main/java/com/joint/cloud/gateway/controller/HealthController.java b/joint-cloud-gateway/src/main/java/com/joint/cloud/gateway/controller/HealthController.java new file mode 100644 index 0000000..6d91a20 --- /dev/null +++ b/joint-cloud-gateway/src/main/java/com/joint/cloud/gateway/controller/HealthController.java @@ -0,0 +1,16 @@ +package com.joint.cloud.gateway.controller; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Date; + + +@RestController +public class HealthController { + + @GetMapping("/health") + public String health() { + return "I'm health now " + (new Date()).toString(); + } +} diff --git a/joint-cloud-gateway/src/main/java/com/joint/cloud/gateway/properties/PermitAllUrlProperties.java b/joint-cloud-gateway/src/main/java/com/joint/cloud/gateway/properties/PermitAllUrlProperties.java new file mode 100644 index 0000000..1ba1d89 --- /dev/null +++ b/joint-cloud-gateway/src/main/java/com/joint/cloud/gateway/properties/PermitAllUrlProperties.java @@ -0,0 +1,75 @@ +package com.joint.cloud.gateway.properties; + +import javax.annotation.PostConstruct; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.regex.Pattern; + +/** + * @author keets + * @date 2017/9/25 + */ +public class PermitAllUrlProperties { + + private static List permitAllUrlPattern; + + private List permitAll; + + public String[] getPermitallPatterns() { + List urls = new ArrayList(); + Iterator iterator = permitAll.iterator(); + while (iterator.hasNext()) { + urls.add(iterator.next().getPattern()); + } + return urls.toArray(new String[0]); + } + + public static List getPermitallUrlPattern() { + return permitAllUrlPattern; + } + + public static class url { + private String pattern; + + public String getPattern() { + return pattern; + } + + public void setPattern(String pattern) { + this.pattern = pattern; + } + } + + @PostConstruct + public void init() { + if (permitAll != null && permitAll.size() > 0) { + permitAllUrlPattern = new ArrayList(); + Iterator iterator = permitAll.iterator(); + while (iterator.hasNext()) { + String currentUrl = iterator.next().getPattern().replaceAll("\\*\\*", "(.*?)"); + Pattern currentPattern = Pattern.compile(currentUrl, Pattern.CASE_INSENSITIVE); + permitAllUrlPattern.add(currentPattern); + } + + } + } + + public boolean isPermitAllUrl(String url) { + for (Pattern pattern : permitAllUrlPattern) { + if (pattern.matcher(url).find()) { + return true; + } + } + return false; + } + + public List getPermitAll() { + return permitAll; + } + + public void setPermitAll(List permitAll) { + this.permitAll = permitAll; + } +} + diff --git a/joint-cloud-gateway/src/main/java/com/joint/cloud/gateway/security/CustomRemoteTokenServices.java b/joint-cloud-gateway/src/main/java/com/joint/cloud/gateway/security/CustomRemoteTokenServices.java new file mode 100644 index 0000000..19d4a83 --- /dev/null +++ b/joint-cloud-gateway/src/main/java/com/joint/cloud/gateway/security/CustomRemoteTokenServices.java @@ -0,0 +1,146 @@ +package com.joint.cloud.gateway.security; + +/** + * Created by keets on 2017/12/9. + */ + +import com.joint.cloud.gateway.constants.SecurityConstants; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.client.ClientHttpResponse; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.security.oauth2.common.exceptions.InvalidTokenException; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.security.oauth2.provider.token.AccessTokenConverter; +import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter; +import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices; +import org.springframework.util.Assert; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.DefaultResponseErrorHandler; +import org.springframework.web.client.RestOperations; +import org.springframework.web.client.RestTemplate; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.util.Base64; +import java.util.Map; + +public class CustomRemoteTokenServices implements ResourceServerTokenServices { + + private SecurityConstants securityConstants=new SecurityConstants(); + + private LoadBalancerClient loadBalancerClient; + + protected final Log logger = LogFactory.getLog(getClass()); + + private RestOperations restTemplate; + + private String checkTokenEndpointUrl; + + private String clientId; + + private String clientSecret; + + private String tokenName = "token"; + + private AccessTokenConverter tokenConverter = new DefaultAccessTokenConverter(); + + public CustomRemoteTokenServices() { + restTemplate = new RestTemplate(); + ((RestTemplate) restTemplate).setErrorHandler(new DefaultResponseErrorHandler() { + @Override + // Ignore 400 + public void handleError(ClientHttpResponse response) throws IOException { + if (response.getRawStatusCode() != 400) { + super.handleError(response); + } + } + }); + } + + public void setRestTemplate(RestOperations restTemplate) { + this.restTemplate = restTemplate; + } + + public void setCheckTokenEndpointUrl(String checkTokenEndpointUrl) { + this.checkTokenEndpointUrl = checkTokenEndpointUrl; + } + + public void setClientId(String clientId) { + this.clientId = clientId; + } + + public void setClientSecret(String clientSecret) { + this.clientSecret = clientSecret; + } + + public void setAccessTokenConverter(AccessTokenConverter accessTokenConverter) { + this.tokenConverter = accessTokenConverter; + } + + public void setTokenName(String tokenName) { + this.tokenName = tokenName; + } + + @Override + public OAuth2Authentication loadAuthentication(String accessToken) throws AuthenticationException, InvalidTokenException { + + MultiValueMap formData = new LinkedMultiValueMap<>(1); + formData.add(tokenName, accessToken); + HttpHeaders headers = new HttpHeaders(); + headers.set("Authorization", getAuthorizationHeader(clientId, clientSecret)); + String authServerServiceId=securityConstants.getAuthServerServiceId(); + ServiceInstance serviceInstance = loadBalancerClient.choose(authServerServiceId); + if (serviceInstance == null) { + throw new RuntimeException("Failed to choose an auth instance."); + } + + Map map = postForMap(serviceInstance.getUri().toString() + checkTokenEndpointUrl, formData, headers); + + if (map.containsKey("error")) { + logger.debug("check_token returned error: " + map.get("error")); + throw new InvalidTokenException(accessToken); + } + + Assert.state(map.containsKey("client_id"), "Client id must be present in response from auth server"); + return tokenConverter.extractAuthentication(map); + } + + public void setLoadBalancerClient(LoadBalancerClient loadBalancerClient) { + this.loadBalancerClient = loadBalancerClient; + } + + @Override + public OAuth2AccessToken readAccessToken(String accessToken) { + throw new UnsupportedOperationException("Not supported: read access token"); + } + + private String getAuthorizationHeader(String clientId, String clientSecret) { + String creds = String.format("%s:%s", clientId, clientSecret); + try { + return new StringBuilder("Basic ").append(Base64.getEncoder().encodeToString(creds.getBytes("utf-8"))).toString(); + } catch (UnsupportedEncodingException e) { + throw new IllegalStateException("Could not convert String"); + } + } + + private Map postForMap(String path, MultiValueMap formData, HttpHeaders headers) { + if (headers.getContentType() == null) { + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + } + @SuppressWarnings("rawtypes") + Map map = restTemplate.exchange(path, HttpMethod.POST, new HttpEntity>(formData, headers), Map.class).getBody(); + @SuppressWarnings("unchecked") + Map result = map; + return result; + } + +} diff --git a/joint-cloud-gateway/src/main/java/com/joint/cloud/gateway/security/HeaderEnhanceFilter.java b/joint-cloud-gateway/src/main/java/com/joint/cloud/gateway/security/HeaderEnhanceFilter.java new file mode 100644 index 0000000..a0e3552 --- /dev/null +++ b/joint-cloud-gateway/src/main/java/com/joint/cloud/gateway/security/HeaderEnhanceFilter.java @@ -0,0 +1,151 @@ +package com.joint.cloud.gateway.security; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.netflix.zuul.context.RequestContext; +import com.joint.cloud.gateway.constants.SecurityConstants; +import com.joint.cloud.gateway.properties.PermitAllUrlProperties; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import java.io.IOException; +import java.net.URLEncoder; +import java.util.*; + +/** + * @author super.wu + */ +public class HeaderEnhanceFilter implements Filter { + + private static final Logger LOGGER = LoggerFactory.getLogger(HeaderEnhanceFilter.class); + + private static final String ANONYMOUS_USER_ID = "d4a65d04-a5a3-465c-8408-405971ac3346"; + private static final String AuthorizationKey = "Authorization"; + + private SecurityConstants securityConstants=new SecurityConstants(); + + @Autowired + private PermitAllUrlProperties permitAllUrlProperties; + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + } + + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { + String authorization = ((HttpServletRequest) servletRequest).getHeader(AuthorizationKey); + String requestURI = ((HttpServletRequest) servletRequest).getRequestURI(); + // test if request url is permit all , then remove authorization from header + LOGGER.info(String.format("Enhance request URI : %s.", requestURI)); + if(isPermitAllUrl(requestURI) && isNotOAuthEndpoint(requestURI)) { + HttpServletRequest resetRequest = removeValueFromRequestHeader((HttpServletRequest) servletRequest); + filterChain.doFilter(resetRequest, servletResponse); + return; + } else { + if (StringUtils.isNotEmpty(authorization)) { + // 判断是否是jwt token + if (isJwtBearerToken(authorization)) { + try { + authorization = StringUtils.substringBetween(authorization, "."); + String decoded = new String(Base64.decodeBase64(authorization)); + + Map properties = new ObjectMapper().readValue(decoded, Map.class); + + String userId = (String) properties.get(securityConstants.getUserIdInHeader()); + + RequestContext.getCurrentContext().addZuulRequestHeader("userId", userId); + RequestContext.getCurrentContext().addZuulRequestHeader("username", (String)properties.get("user_name")); + RequestContext.getCurrentContext().addZuulRequestHeader("name", URLEncoder.encode((String)properties.get("name"),"UTF-8")); + RequestContext.getCurrentContext().addZuulRequestHeader("roles", (String)properties.get("roles")); + String realIp =((HttpServletRequest) servletRequest).getHeader("X-Real-IP"); + if(realIp != null) { + RequestContext.getCurrentContext().addZuulRequestHeader(securityConstants.getRealIPInHeader(), realIp); + } + } catch (Exception e) { + e.printStackTrace(); + LOGGER.error("Failed to customize header for the request, but still release it as the it would be regarded without any user details.", e); + } + } + } else { + LOGGER.info("Regard this request as anonymous request, so set anonymous user_id in the header."); + RequestContext.getCurrentContext().addZuulRequestHeader(securityConstants.getUserIdInHeader(), ANONYMOUS_USER_ID); + } + } + filterChain.doFilter(servletRequest, servletResponse); + } + + @Override + public void destroy() { + + } + + private boolean isJwtBearerToken(String token) { + return StringUtils.countMatches(token, ".") == 2 && (token.startsWith("Bearer") || token.startsWith("bearer")); + } + + private boolean isNotOAuthEndpoint(String requestURI) { + return !requestURI.contains("/login"); + } + + private HttpServletRequestWrapper removeValueFromRequestHeader(HttpServletRequest request) { + HttpServletRequestWrapper httpServletRequestWrapper = new HttpServletRequestWrapper(request) { + private Set headerNameSet; + private Set headerSet; + + @Override + public Enumeration getHeaderNames() { + if (headerNameSet == null) { + // first time this method is called, cache the wrapped request's header names: + headerNameSet = new HashSet(); + Enumeration wrappedHeaderNames = super.getHeaderNames(); + while (wrappedHeaderNames.hasMoreElements()) { + String headerName = wrappedHeaderNames.nextElement(); + if (!AuthorizationKey.equalsIgnoreCase(headerName)) { + headerNameSet.add(headerName); + } + } + //set default header name value of tenant id and user id + headerNameSet.add(securityConstants.getUserIdInHeader()); + } + + return Collections.enumeration(headerNameSet); + } + + @Override + public Enumeration getHeaders(String name) { + + if (AuthorizationKey.equalsIgnoreCase(name)) { + return Collections.emptyEnumeration(); + } + if (securityConstants.getUserIdInHeader().equalsIgnoreCase(name)) { + headerSet = new HashSet(); + headerSet.add(ANONYMOUS_USER_ID); + return Collections.enumeration(headerSet); + } + return super.getHeaders(name); + } + + @Override + public String getHeader(String name) { + if (AuthorizationKey.equalsIgnoreCase(name)) { + return null; + } + if (securityConstants.getUserIdInHeader().equalsIgnoreCase(name)) { + return ANONYMOUS_USER_ID; + } + return super.getHeader(name); + } + }; + return httpServletRequestWrapper; + } + + private boolean isPermitAllUrl(String url) { + return permitAllUrlProperties.isPermitAllUrl(url); + } + +} diff --git a/joint-cloud-gateway/src/main/resources/application-dev.yml b/joint-cloud-gateway/src/main/resources/application-dev.yml new file mode 100644 index 0000000..eb51566 --- /dev/null +++ b/joint-cloud-gateway/src/main/resources/application-dev.yml @@ -0,0 +1,4 @@ +spring: + cloud: + consul: + host: ${CONSUL_ADDR:10.4.79.107} diff --git a/joint-cloud-gateway/src/main/resources/application.yml b/joint-cloud-gateway/src/main/resources/application.yml new file mode 100644 index 0000000..5a98092 --- /dev/null +++ b/joint-cloud-gateway/src/main/resources/application.yml @@ -0,0 +1,85 @@ +server: + port: 8090 +spring: + application: + name: joint-cloud-gateway + profiles: + active: dev + cloud: + consul: + port: 8500 + discovery: + instance-id: ${spring.application.name}:${spring.cloud.consul.discovery.ip-address}:${server.port} + prefer-ip-address: true + health-check-path: /health + health-check-interval: 60s + health-check-critical-timeout: 30s + ip-address: ${REGISTRY_ADDR:${spring.cloud.client.ip-address}} + +authserver: + service-id: joint-cloud-auth + user-id-in-header: X-AOHO-UserId + +zuul: + ribbon: + eager-load: + enabled: true + #不加此参数第一次调用很有可能超时 + ignored-headers: Access-Control-Allow-Credentials, Access-Control-Allow-Origin + set-content-length: true + prefix: / + routes: + auth: + path: /user/** + serviceId: ${authserver.service-id} + sensitiveHeaders: Cookie,Set-Cookie + login: + path: /login/** + serviceId: ${authserver.service-id} + sensitiveHeaders: Cookie,Set-Cookie + admin: + path: /joint/admin/** + serviceId: joint-cloud-admin + # url: http://10.4.79.107:8096 + sensitiveHeaders: Cookie,Set-Cookie + subject1: + path: /subject1/** + serviceId: joint-cloud-subject1-gateway + sensitiveHeaders: Cookie,Set-Cookie + subject2: + path: /subject2/** + serviceId: joint-cloud-subject2-gateway + sensitiveHeaders: Cookie,Set-Cookie + subject3: + path: /subject3/** + serviceId: joint-cloud-subject3-gateway + sensitiveHeaders: Cookie,Set-Cookie + subject4: + path: /subject4/** + serviceId: joint-cloud-subject4-gateway + sensitiveHeaders: Cookie,Set-Cookie + +ribbon: + ConnectTimeout: 3000 + ReadTimeout: 300000 +auth: + permitall: + - pattern: /health/** + - pattern: /login/** + - pattern: /**/public/** + - pattern: /**/download/** + - pattern: /message/** +security: + oauth2: + client: + clientId: frontend + clientSecret: frontend + accessTokenUri: /oauth/token + userAuthorizationUri: /oauth/authorize + #clientAuthenticationScheme: form + resource: + userInfoUri: /userinfo + tokenInfoUri: /oauth/check_token + #preferTokenInfo: false +logging: + config: classpath:logback.xml \ No newline at end of file diff --git a/joint-cloud-gateway/src/main/resources/logback.xml b/joint-cloud-gateway/src/main/resources/logback.xml new file mode 100644 index 0000000..39d82d4 --- /dev/null +++ b/joint-cloud-gateway/src/main/resources/logback.xml @@ -0,0 +1,84 @@ + + + + + + + %d %p (%file:%line\)- %m%n + + UTF-8 + + + + + + + logs/joint-cloud-gateway.log + + + + + + logs/joint-cloud-gateway-%d.%i.log + + 30 + + + 10MB + + + + + + %d %p (%file:%line\)- %m%n + + + UTF-8 + + + + + logs/joint-cloud-gateway-api.log + + + + + + logs/joint-cloud-gateway-api-%d.%i.log + + 30 + + + 10MB + + + + + + %d %p (%file:%line\)- %m%n + + + UTF-8 + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/joint-cloud-subject-gateway/pom.xml b/joint-cloud-subject-gateway/pom.xml new file mode 100644 index 0000000..6a87ee9 --- /dev/null +++ b/joint-cloud-subject-gateway/pom.xml @@ -0,0 +1,53 @@ + + + + joint-cloud + com.joint.cloud + 1.0.0-SNAPSHOT + + 4.0.0 + + joint-cloud-subject-gateway + joint-cloud-subject-gateway + ${joint.cloud.version} + jar + + + 8 + 8 + + + + + org.springframework.cloud + spring-cloud-starter-gateway + + + org.springframework.cloud + spring-cloud-starter-consul-discovery + + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + \ No newline at end of file diff --git a/joint-cloud-subject-gateway/src/main/java/com/joint/cloud/subject/gateway/GatewaySubjectApplication.java b/joint-cloud-subject-gateway/src/main/java/com/joint/cloud/subject/gateway/GatewaySubjectApplication.java new file mode 100644 index 0000000..c5282c1 --- /dev/null +++ b/joint-cloud-subject-gateway/src/main/java/com/joint/cloud/subject/gateway/GatewaySubjectApplication.java @@ -0,0 +1,13 @@ +package com.joint.cloud.subject.gateway; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; + +@SpringBootApplication +@EnableDiscoveryClient +public class GatewaySubjectApplication { + public static void main(String[] args) { + SpringApplication.run(GatewaySubjectApplication.class, args); + } +} diff --git a/joint-cloud-subject-gateway/src/main/java/com/joint/cloud/subject/gateway/contorller/HealthController.java b/joint-cloud-subject-gateway/src/main/java/com/joint/cloud/subject/gateway/contorller/HealthController.java new file mode 100644 index 0000000..e26c29f --- /dev/null +++ b/joint-cloud-subject-gateway/src/main/java/com/joint/cloud/subject/gateway/contorller/HealthController.java @@ -0,0 +1,16 @@ +package com.joint.cloud.subject.gateway.contorller; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Date; + + +@RestController +public class HealthController { + + @GetMapping("/health") + public String health() { + return "I'm health now " + (new Date()).toString(); + } +} diff --git a/joint-cloud-subject-gateway/src/main/resources/application-subject1.yml b/joint-cloud-subject-gateway/src/main/resources/application-subject1.yml new file mode 100644 index 0000000..7b3674c --- /dev/null +++ b/joint-cloud-subject-gateway/src/main/resources/application-subject1.yml @@ -0,0 +1,15 @@ +server: + port: 8091 +spring: + application: + name: joint-cloud-subject1-gateway + cloud: + gateway: + discovery: + locator: + enabled: true + routes: + - id: 1-1-1 + uri: lb://1-1-1-helloworld + predicates: + - Path=/1-1-1/** \ No newline at end of file diff --git a/joint-cloud-subject-gateway/src/main/resources/application-subject2.yml b/joint-cloud-subject-gateway/src/main/resources/application-subject2.yml new file mode 100644 index 0000000..90170a5 --- /dev/null +++ b/joint-cloud-subject-gateway/src/main/resources/application-subject2.yml @@ -0,0 +1,15 @@ +server: + port: 8092 +spring: + application: + name: joint-cloud-subject2-gateway + cloud: + gateway: + discovery: + locator: + enabled: true + routes: + - id: 2-1-1 + uri: lb://2-1-1-helloworld + predicates: + - Path=/2-1-1/** \ No newline at end of file diff --git a/joint-cloud-subject-gateway/src/main/resources/application-subject3.yml b/joint-cloud-subject-gateway/src/main/resources/application-subject3.yml new file mode 100644 index 0000000..e3d3b5a --- /dev/null +++ b/joint-cloud-subject-gateway/src/main/resources/application-subject3.yml @@ -0,0 +1,15 @@ +server: + port: 8093 +spring: + application: + name: joint-cloud-subject3-gateway + cloud: + gateway: + discovery: + locator: + enabled: true + routes: + - id: 3-1-1 + uri: lb://3-1-1-helloworld + predicates: + - Path=/3-1-1/** \ No newline at end of file diff --git a/joint-cloud-subject-gateway/src/main/resources/application-subject4.yml b/joint-cloud-subject-gateway/src/main/resources/application-subject4.yml new file mode 100644 index 0000000..acf19e7 --- /dev/null +++ b/joint-cloud-subject-gateway/src/main/resources/application-subject4.yml @@ -0,0 +1,23 @@ +server: + port: 8094 +spring: + application: + name: joint-cloud-subject4-gateway + cloud: + gateway: + discovery: + locator: + enabled: true + routes: + - id: 4-1-1 + uri: lb://subject4-1-1-helloworld + predicates: + - Path=/4-1-1/** + filters: + - StripPrefix=1 + - id: 4-1-2 + uri: lb://subject4-1-1-abc + predicates: + - Path=/4-1-2/** + filters: + - StripPrefix=1 \ No newline at end of file diff --git a/joint-cloud-subject-gateway/src/main/resources/application.yml b/joint-cloud-subject-gateway/src/main/resources/application.yml new file mode 100644 index 0000000..1ffe749 --- /dev/null +++ b/joint-cloud-subject-gateway/src/main/resources/application.yml @@ -0,0 +1,18 @@ +server: + port: 9001 +spring: + application: + name: joint-cloud-top-gateway + profiles: + active: subject4 + cloud: + consul: + host: 10.4.79.107 + port: 8500 + discovery: + instance-id: ${spring.application.name}:${spring.cloud.consul.discovery.ip-address}:${server.port} + prefer-ip-address: true + health-check-path: /health + health-check-interval: 60s + health-check-critical-timeout: 30s + ip-address: ${REGISTRY_ADDR:${spring.cloud.client.ip-address}} diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..0d0aa51 --- /dev/null +++ b/pom.xml @@ -0,0 +1,292 @@ + + + 4.0.0 + + joint-cloud-gateway + joint-cloud-subject-gateway + joint-cloud-common + joint-cloud-auth + subject4-1-1-helloworld + subject4-1-1-bc + + + org.springframework.boot + spring-boot-starter-parent + 2.1.4.RELEASE + + + + com.joint.cloud + joint-cloud + 1.0.0-SNAPSHOT + pom + + + + 1.8 + + 8 + 8 + + 2.1.4 + Greenwich.SR2 + + 1.0.0-SNAPSHOT + + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + de.codecentric + spring-boot-admin-dependencies + ${spring-boot-admin.version} + pom + import + + + com.github.ulisesbocchio + jasypt-spring-boot-starter + 3.0.0 + + + com.alibaba + druid + 1.1.16 + + + + org.activiti + activiti-spring-boot-starter-jpa + 6.0.0 + + + org.activiti + activiti-spring-boot-starter-basic + 6.0.0 + + + org.mybatis + mybatis + + + + + + + cn.afterturn + easypoi-spring-boot-starter + 4.3.0 + + + cn.afterturn + easypoi-base + 4.3.0 + + + cn.afterturn + easypoi-web + 4.3.0 + + + cn.afterturn + easypoi-annotation + 4.3.0 + + + + org.apache.poi + poi + 3.17 + + + + org.apache.poi + poi-ooxml + 3.17 + + + + com.alibaba + druid-spring-boot-starter + 1.1.14 + + + + commons-io + commons-io + 2.5 + + + + + commons-fileupload + commons-fileupload + 1.3.3 + + + + org.apache.velocity + velocity + 1.7 + + + + commons-logging + commons-logging + 1.2 + + + log4j + log4j + 1.2.17 + + + + + com.github.oshi + oshi-core + 3.9.1 + + + + + io.jsonwebtoken + jjwt + 0.9.0 + + + + + eu.bitwalker + UserAgentUtils + 1.19 + + + + + mysql + mysql-connector-java + 8.0.15 + + + com.github.pagehelper + pagehelper-spring-boot-starter + 1.2.13 + + + tk.mybatis + mapper-spring-boot-starter + 2.1.5 + + + tk.mybatis + mapper + 3.5.3 + + + javax.persistence + persistence-api + 1.0 + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + 2.1.1 + + + org.mybatis + mybatis-spring + 2.0.3 + + + io.springfox + springfox-swagger2 + 2.7.0 + + + io.springfox + springfox-swagger-ui + 2.7.0 + + + commons-collections + commons-collections + 3.2.2 + + + net.sf.json-lib + json-lib + 2.4 + jdk15 + + + com.alibaba + fastjson + 1.2.70 + + + org.projectlombok + lombok + 1.18.4 + provided + + + com.fasterxml.jackson.core + jackson-databind + 2.10.1 + + + com.fasterxml.jackson.core + jackson-core + 2.10.1 + + + com.fasterxml.jackson.core + jackson-annotations + 2.10.1 + + + + com.squareup.okhttp3 + okhttp + 4.9.0 + + + + org.jetbrains.kotlin + kotlin-stdlib + 1.3.70 + + + + com.auth0 + java-jwt + 3.1.0 + + + + com.github.housepower + clickhouse-native-jdbc-shaded + 2.5.6 + + + + com.github.jsonzou + jmockdata + 4.2.0 + + + + + + \ No newline at end of file diff --git a/regulatory-governance-system/.keep b/regulatory-governance-system/.keep new file mode 100644 index 0000000..e69de29 diff --git a/regulatory-governance-system/api/testApi.js b/regulatory-governance-system/api/testApi.js new file mode 100644 index 0000000..d053d18 --- /dev/null +++ b/regulatory-governance-system/api/testApi.js @@ -0,0 +1,202 @@ +import request from '@/utils/request' + +const testApi = { + getUsageHostInfo: getUsageHostInfo, + getTaskAllocationChartData: getTaskAllocationChartData, + getExpenseLists: getExpenseLists, + chooseSet: chooseSet, + + getHostNums: getHostNums, + getGailanInfo: getGailanInfo, + getAbnormalDataList: getAbnormalDataList, + getManufacturerChartData: getManufacturerChartData, + getCpuChartData: getCpuChartData, + getDiskChartData: getDiskChartData, + getMemoryChartData: getMemoryChartData, + getPerformanceChartData: getPerformanceChartData, + getLineChartByOrgName: getLineChartByOrgName, + getAlarmTableData: getAlarmTableData, + + getAbnormalInfoList: getAbnormalInfoList, + getEventResponseChartData: getEventResponseChartData, + getIllegalInfoList: getIllegalInfoList, + getIllegalPublicityChartData: getIllegalPublicityChartData, + getTransactionInfoList: getTransactionInfoList, + getSupplierList: getSupplierList, + getSupplierInfoById: getSupplierInfoById, + searchHostInfoById: searchHostInfoById, + getHostChartsById: getHostChartsById + +} +// kt2/taskAllocation +function getUsageHostInfo() { + return request({ + url: `/cloud/v1.0/kt2/taskAllocation/getUsageHostInfo`, + method: 'get' + }) +} +function getTaskAllocationChartData() { + return request({ + url: `/cloud/v1.0/kt2/taskAllocation/getTaskAllocationChartData`, + method: 'get' + }) +} + +function getExpenseLists() { + return request({ + url: `/cloud/v1.0/kt2/taskAllocation/getExpenseLists`, + method: 'get' + }) +} + +function chooseSet(data) { + return request({ + url: `/cloud/v1.0/kt2/taskAllocation/chooseSet`, + data: data, + method: 'post' + }) +} + +// kt3/screen +function getHostNums() { + return request({ + url: `/cloud/v1.0/kt3/screen/getHostNums`, + method: 'get' + }) +} + +function getGailanInfo() { + return request({ + url: `/cloud/v1.0/kt3/screen/getGailanInfo`, + method: 'get' + }) +} +function getManufacturerChartData() { + return request({ + url: `/cloud/v1.0/kt3/screen/getManufacturerChartData`, + method: 'get' + }) +} + +function getCpuChartData(params) { + return request({ + url: `/cloud/v1.0/kt3/screen/getCpuChartData`, + method: 'get', + params: params + }) +} + +function getDiskChartData(params) { + return request({ + url: `/cloud/v1.0/kt3/screen/getDiskChartData`, + method: 'get', + params: params + }) +} + +function getMemoryChartData(params) { + return request({ + url: `/cloud/v1.0/kt3/screen/getMemoryChartData`, + method: 'get', + params: params + }) +} + +function getPerformanceChartData(params) { + return request({ + url: `/cloud/v1.0/kt3/screen/getPerformanceChartData`, + method: 'get', + params: params + }) +} + +function getLineChartByOrgName(params) { + return request({ + url: `/cloud/v1.0/kt3/screen/getLineChartByOrgName`, + method: 'get', + params: params + }) +} + +function getAlarmTableData(params) { + return request({ + url: `/cloud/v1.0/kt3/screen/getAlarmTableData`, + method: 'get', + params: params + }) +} + +function getAbnormalDataList() { + return request({ + url: `/cloud/v1.0/kt3/screen/getAbnormalDataList`, + method: 'get' + }) +} + +// kt3/eventResponse +function getAbnormalInfoList() { + return request({ + url: `/cloud/v1.0/kt3/eventResponse/getAbnormalInfoList`, + method: 'get' + }) +} + +function getEventResponseChartData() { + return request({ + url: `/cloud/v1.0/kt3/eventResponse/getEventResponseChartData`, + method: 'get' + }) +} + +// kt3/illegalPublicity +function getIllegalInfoList() { + return request({ + url: `/cloud/v1.0/kt3/illegalPublicity/getIllegalInfoList`, + method: 'get' + }) +} + +function getIllegalPublicityChartData() { + return request({ + url: `/cloud/v1.0/kt3/illegalPublicity/getIllegalPublicityChartData`, + method: 'get' + }) +} + +// kt3/supplierManagement +function getTransactionInfoList() { + return request({ + url: `/cloud/v1.0/kt3/supplierManagement/getTransactionInfoList`, + method: 'get' + }) +} +function getSupplierList() { + return request({ + url: `/cloud/v1.0/kt3/supplierManagement/getSupplierList`, + method: 'get' + }) +} + +function getSupplierInfoById(params) { + return request({ + url: `/cloud/v1.0/kt3/supplierManagement/getSupplierInfoById`, + method: 'get', + params: params + }) +} +// kt3/hostPerformance +function searchHostInfoById(params) { + return request({ + url: `/cloud/v1.0/kt3/hostPerformance/searchHostInfoById`, + method: 'get', + params: params + }) +} +function getHostChartsById(params) { + return request({ + url: `/cloud/v1.0/kt3/hostPerformance/searchHostInfoById`, + method: 'get', + params: params + }) +} +export default testApi diff --git a/regulatory-governance-system/views/taskAllocation/css/taskAllocation.scss b/regulatory-governance-system/views/taskAllocation/css/taskAllocation.scss new file mode 100644 index 0000000..7836d2f --- /dev/null +++ b/regulatory-governance-system/views/taskAllocation/css/taskAllocation.scss @@ -0,0 +1,235 @@ +#taskAllocation { + .top_row_button{ + margin-top:1.8vh; + } + #top-row-button-box { + .el-button--primary { + background-color: #1890ff1f + } + } + + .top_row_text{ + color: #FFFFFF; + margin-top:1.8vh; + } + + .title_row { + display: flex; + display: -webkit-flex; + justify-content: center; + } + + .title_box { + display: flex; + flex-direction: column; + justify-content: flex-end; + } + + .title_box_text { + font-family: 'Alternate'; + font-size: 30px; + color: white; + } + + .title_box_hr { + border-bottom: 2px solid; + opacity: 0.2; + color: white; + width: 120%; + margin-top: 2%; + margin-left: -10%; + } + + .el-card { + border-radius: 4px; + border: 1px solid #ffffff; + background-color: rgba(255, 255, 255, 0); + } + .el-card__header { + padding: 3px 10px; + border-bottom: 1px solid #EBEEF5; + -webkit-box-sizing: border-box; + box-sizing: border-box; + height: 4vh; + } + .el-card__body, .el-main { + padding: 5px; + } + .el-card__body{ + padding: 5px; + height: 20vh; + } + + .card1{ + height:30vh; + margin: 20px 20px 20px 20px; + padding: 10px 20px 0 20px; + } + .card2{ + height:30vh; + margin: 20px 20px 20px 20px; + padding: 10px 20px 0 20px; + } + .card3{ + height:26vh; + margin: 20px 20px 20px 20px; + padding: 10px 20px 0 20px; + } + .card3-box{ + display: flex; + display: -webkit-flex; + height:25vh; + } + .chart_box { + width: 90%; + margin-left: 10%; + } + .tableBox { + width: 100%; + overflow: hidden + } + .tableInner { + height: auto; + } + + .card1-inner{ + height:24vh; + padding-bottom:80px; + color: #FFFFFF; + } + .card1_inner_row{ + height:5vh; + display: flex; + display: -webkit-flex; + align-items: center; + justify-content: center; + } + .card1_inner_num { + font-family: 'Alternate'; + color: #FFFFFF; + letter-spacing: 0; + text-shadow: 0 0 6px #26A2FF; + font-size: 11pt; + } + + .card1_inner_img { + margin-top:-2px; + height: 25px; + width: 25px; + } + .card2-inner{ + height:25vh; + padding-bottom:80px; + color: #FFFFFF; + flex-wrap: wrap; + margin-top: -1vh; + } + .card-chart-box{ + width: 103%; + margin-top: -5vh; + margin-left: 2%; + } + .card3-inner-box{ + height:100%; + padding-bottom:80px; + color: #FFFFFF; + width: 23.8%; + flex-shrink: 0; + margin-right: 1.8%; + } + .card3-inner{ + height:19vh; + color: #FFFFFF; + width: 100%; + } + .card3_inner_img { + margin-top:-2px; + height: 20px; + width: 20px; + } + .card3_detail_box{ + margin-left: 20px; + overflow: auto; + height:13vh + } + + + .el-dialog { + background: rgba(0, 0, 0, 0.3); + border-radius: 10px; + } + + .el-dialog > .el-dialog__body { + padding: 0px; + border-left: 2px solid #FFFFFF; + border-right: 2px solid #FFFFFF; + background-color: rgba(0, 0, 0, 0.3); + color: white; + margin: 0 0 0 0; + } + + .el-dialog__title { + color: #FFFFFF; + } + + .el-dialog__header { + border-top: 2px solid #FFFFFF; + border-left: 2px solid #FFFFFF; + border-right: 2px solid #FFFFFF; + border-top-left-radius: 10px; + border-top-right-radius: 10px; + background-color: rgba(0, 0, 0, 0.3); + color: white; + } + + .el-dialog__footer { + border-bottom: 2px solid #FFFFFF; + border-left: 2px solid #FFFFFF; + border-right: 2px solid #FFFFFF; + border-bottom-left-radius: 10px; + border-bottom-right-radius: 10px; + background-color: rgba(0, 0, 0, 0.3); + color: white; + } + + .top_button_box { + margin-top: 1%; + margin-left: auto; + display: flex; + display: -webkit-flex; + align-items: center; + } + + + .title_class { + font-family: 'Alternate'; + font-weight: bold; + font-size: 11pt; + color: white; + } + + .el-descriptions { + background: rgba(255, 255, 255, 0); + margin-left: 3%; + } + + .el-descriptions__body { + color: #ffffff; + background-color: rgba(255, 255, 255, 0); + } + + .el-descriptions__title { + color: white; + font-size: 11pt; + } + + el-descriptions-item el-descriptions-item__cell { + height: 15px; + } + + +} + + + + diff --git a/regulatory-governance-system/views/taskAllocation/index.vue b/regulatory-governance-system/views/taskAllocation/index.vue new file mode 100644 index 0000000..18541fc --- /dev/null +++ b/regulatory-governance-system/views/taskAllocation/index.vue @@ -0,0 +1,383 @@ + + + + diff --git a/regulatory-governance-system/views/taskAllocation/js/index.js b/regulatory-governance-system/views/taskAllocation/js/index.js new file mode 100644 index 0000000..aad4eea --- /dev/null +++ b/regulatory-governance-system/views/taskAllocation/js/index.js @@ -0,0 +1,702 @@ +import echarts from 'echarts' +import $ from 'jQuery' +import testApi from '@/api/testApi' + +export default { + name: 'businessScreen', + data() { + return { + scrollValue: '开', + intervalCpu: '', + intervalMemory: '', + intervalDisk: '', + intervalBandWidth: '', + intervalDispatch: '', + // 第一个card + usageDataList: [], + usageDataList1: [], // 没用 + // 第二个card + chartDialogVisible: false, + chooseBtnFlag: false, + recentExpense: [], + shortTermExpense: [], + longTermExpense: [], + mediumTermExpense: [], + chartShortOption: '', + chartMediumOption: '', + chartLongOption: '', + expenseLists: {}, + expenseInDialog: {} + + } + }, + created() { + }, + beforeDestroy() { + if (this.intervalCpu !== '') { + clearInterval(this.intervalCpu) + } + if (this.intervalMemory !== '') { + clearInterval(this.intervalMemory) + } + if (this.intervalBandWidth !== '') { + clearInterval(this.intervalBandWidth) + } + if (this.intervalDisk !== '') { + clearInterval(this.intervalDisk) + } + if (this.intervalDispatch !== '') { + clearInterval(this.intervalDispatch) + } + }, + async mounted() { + await this.getUsageHostInfo() + await this.upDateResults() + this.scrollChange() + }, + methods: { + async upDateResults() { + const self = this + this.getCharts() + await this.getExpenseLists() + this.chooseBtnFlag = false + setTimeout(function() { + self.chooseBtnFlag = true + }, 10000) + }, + async getUsageHostInfo() { + await testApi.getUsageHostInfo().then((res) => { + this.usageDataList = res.data + }).catch(() => { + this.usageDataList = [ + { id: 0, label: '阿里云', icon: '/static/hostPerformance/阿里云.png', cpuUsage: '4', cpuTotal: '10', memoryUsage: '4GB', memoryTotal: '116GB', diskUsage: '1TB', diskTotal: '10TB', bandWidthUsage: '2Mbps', bandWidthTotal: '10Mbps', + showAll: true, showCpuDetail: false, showMemoryDetail: false, showBandWidthDetail: false, showDiskDetail: false, + cpuList: [{ num: 2, detail: '2.5GHz' }, { num: 4, detail: '3GHz' }, { num: 4, detail: '3.6GHz' }], + memoryList: [{ num: 1, detail: '64GB RAM' }, { num: 10, detail: '2GB RAM' }, { num: 4, detail: '8GB RAM' }], + diskList: [{ num: '8TB', detail: 'SSD' }, { num: '2TB', detail: 'HDD' }] }, + { id: 1, label: '华为云', icon: '/static/hostPerformance/华为云.png', cpuUsage: '2', cpuTotal: '10', memoryUsage: '4GB', memoryTotal: '48GB', diskUsage: '8TB', diskTotal: '15TB', bandWidthUsage: '20Mbps', bandWidthTotal: '50Mbps', + showAll: true, showCpuDetail: false, showMemoryDetail: false, showBandWidthDetail: false, showDiskDetail: false, + cpuList: [{ num: 4, detail: '2.5GHz' }, { num: 2, detail: '3GHz' }, { num: 4, detail: '3.6GHz' }], + memoryList: [{ num: 4, detail: '8GB RAM' }, { num: 2, detail: '4GB RAM' }, { num: 4, detail: '2GB RAM' }], + diskList: [{ num: '10TB', detail: 'SSD' }, { num: '5TB', detail: 'HDD' }] }, + { id: 2, label: '联通云', icon: '/static/hostPerformance/联通云.png', cpuUsage: '3', cpuTotal: '10', memoryUsage: '8GB', memoryTotal: '48GB', diskUsage: '5TB', diskTotal: '8TB', bandWidthUsage: '60Mbps', bandWidthTotal: '100Mbps', + showAll: true, showCpuDetail: false, showMemoryDetail: false, showBandWidthDetail: false, showDiskDetail: false, + cpuList: [{ num: 6, detail: '2.5GHz' }, { num: 4, detail: '3GHz' }], + memoryList: [{ num: 1, detail: '64GB RAM' }, { num: 10, detail: '2GB RAM' }, { num: 4, detail: '8GB RAM' }], + diskList: [{ num: '6TB', detail: 'SSD' }, { num: '2TB', detail: 'HDD' }] }, + { id: 3, label: '火山云', icon: '/static/hostPerformance/火山云.png', cpuUsage: '5', cpuTotal: '10', memoryUsage: '16GB', memoryTotal: '116GB', diskUsage: '4TB', diskTotal: '20TB', bandWidthUsage: '10Mbps', bandWidthTotal: '100Mbps', + showAll: true, showCpuDetail: false, showMemoryDetail: false, showBandWidthDetail: false, showDiskDetail: false, + cpuList: [{ num: 2, detail: '2.5GHz' }, { num: 2, detail: '3GHz' }, { num: 6, detail: '3.7GHz' }], + memoryList: [{ num: 1, detail: '64GB RAM' }, { num: 10, detail: '2GB RAM' }, { num: 4, detail: '8GB RAM' }], + diskList: [{ num: '16TB', detail: 'SSD' }, { num: '4TB', detail: 'HDD' }] }, + { id: 4, label: 'test1', icon: '/static/hostPerformance/火山云.png', cpuUsage: '4', cpuTotal: '10', memoryUsage: '16GB', memoryTotal: '128GB', diskUsage: '3TB', diskTotal: '10TB', bandWidthUsage: '40Mbps', bandWidthTotal: '200Mbps', + showAll: true, showCpuDetail: false, showMemoryDetail: false, showBandWidthDetail: false, showDiskDetail: false, + cpuList: [{ num: 4, detail: '2.4GHz' }, { num: 4, detail: '3GHz' }, { num: 2, detail: '3.7GHz' }], + memoryList: [{ num: 1, detail: '64GB RAM' }, { num: 10, detail: '2GB RAM' }, { num: 4, detail: '8GB RAM' }], + diskList: [{ num: '8TB', detail: 'SSD' }, { num: '2TB', detail: 'HDD' }] }, + { id: 5, label: 'test2', icon: '/static/hostPerformance/火山云.png', cpuUsage: '2', cpuTotal: '10', memoryUsage: '16GB', memoryTotal: '128GB', diskUsage: '4.5TB', diskTotal: '10TB', bandWidthUsage: '20Mbps', bandWidthTotal: '100Mbps', + showAll: true, showCpuDetail: false, showMemoryDetail: false, showBandWidthDetail: false, showDiskDetail: false, + cpuList: [{ num: 2, detail: '2.5GHz' }, { num: 4, detail: '3GHz' }, { num: 4, detail: '3.6GHz' }], + memoryList: [{ num: 1, detail: '64GB RAM' }, { num: 10, detail: '2GB RAM' }, { num: 4, detail: '8GB RAM' }], + diskList: [{ num: '8TB', detail: 'SSD' }, { num: '2TB', detail: 'HDD' }] }, + { id: 6, label: 'test3', icon: '/static/hostPerformance/火山云.png', cpuUsage: '5', cpuTotal: '10', memoryUsage: '12GB', memoryTotal: '128GB', diskUsage: '2.5TB', diskTotal: '10TB', bandWidthUsage: '20Mbps', bandWidthTotal: '100Mbps', + showAll: true, showCpuDetail: false, showMemoryDetail: false, showBandWidthDetail: false, showDiskDetail: false, + cpuList: [{ num: 2, detail: '2.5GHz' }, { num: 4, detail: '3GHz' }, { num: 4, detail: '3.6GHz' }], + memoryList: [{ num: 1, detail: '64GB RAM' }, { num: 10, detail: '2GB RAM' }, { num: 4, detail: '8GB RAM' }], + diskList: [{ num: '8TB', detail: 'SSD' }, { num: '2TB', detail: 'HDD' }] }, + { id: 7, label: 'test4', icon: '/static/hostPerformance/火山云.png', cpuUsage: '4', cpuTotal: '10', memoryUsage: '16GB', memoryTotal: '128GB', diskUsage: '1TB', diskTotal: '10TB', bandWidthUsage: '20Mbps', bandWidthTotal: '100Mbps', + showAll: true, showCpuDetail: false, showMemoryDetail: false, showBandWidthDetail: false, showDiskDetail: false, + cpuList: [{ num: 2, detail: '2.5GHz' }, { num: 4, detail: '3GHz' }, { num: 4, detail: '3.6GHz' }], + memoryList: [{ num: 1, detail: '64GB RAM' }, { num: 10, detail: '2GB RAM' }, { num: 4, detail: '8GB RAM' }], + diskList: [{ num: '8TB', detail: 'SSD' }, { num: '2TB', detail: 'HDD' }] } + ] + this.usageDataList1 = [{ id: 0, label: '阿里云', icon: '/static/hostPerformance/阿里云.png', cpuUsage: '4', cpuTotal: '15', memoryUsage: '4GB', memoryTotal: '48GB', diskUsage: '1TB', diskTotal: '15TB', bandWidthUsage: '2Mbps', bandWidthTotal: '20Mbps', + showAll: true, showCpuDetail: false, showMemoryDetail: false, showBandWidthDetail: false, showDiskDetail: false, + cpuList: [{ num: 2, detail: '2.5GHz' }, { num: 4, detail: '3GHz' }, { num: 4, detail: '3.6GHz' }], + memoryList: [{ num: 1, detail: '64GB RAM' }, { num: 10, detail: '2GB RAM' }, { num: 4, detail: '8GB RAM' }], + diskList: [{ num: '8TB', detail: 'SSD' }, { num: '2TB', detail: 'HDD' }] }, + { id: 1, label: '华为云', icon: '/static/hostPerformance/华为云.png', cpuUsage: '2', cpuTotal: '8', memoryUsage: '4GB', memoryTotal: '112GB', diskUsage: '8TB', diskTotal: '10TB', bandWidthUsage: '20Mbps', bandWidthTotal: '20Mbps', + showAll: true, showCpuDetail: false, showMemoryDetail: false, showBandWidthDetail: false, showDiskDetail: false, + cpuList: [{ num: 4, detail: '2.5GHz' }, { num: 2, detail: '3GHz' }, { num: 4, detail: '3.6GHz' }], + memoryList: [{ num: 4, detail: '8GB RAM' }, { num: 2, detail: '4GB RAM' }, { num: 4, detail: '2GB RAM' }], + diskList: [{ num: '10TB', detail: 'SSD' }, { num: '5TB', detail: 'HDD' }] }, + { id: 2, label: '联通云', icon: '/static/hostPerformance/联通云.png', cpuUsage: '3', cpuTotal: '15', memoryUsage: '8GB', memoryTotal: '128GB', diskUsage: '5TB', diskTotal: '10TB', bandWidthUsage: '60Mbps', bandWidthTotal: '200Mbps', + showAll: true, showCpuDetail: false, showMemoryDetail: false, showBandWidthDetail: false, showDiskDetail: false, + cpuList: [{ num: 6, detail: '2.5GHz' }, { num: 4, detail: '3GHz' }], + memoryList: [{ num: 1, detail: '64GB RAM' }, { num: 10, detail: '2GB RAM' }, { num: 4, detail: '8GB RAM' }], + diskList: [{ num: '6TB', detail: 'SSD' }, { num: '2TB', detail: 'HDD' }] }, + { id: 3, label: '火山云', icon: '/static/hostPerformance/火山云.png', cpuUsage: '5', cpuTotal: '8', memoryUsage: '16GB', memoryTotal: '116GB', diskUsage: '4TB', diskTotal: '8TB', bandWidthUsage: '10Mbps', bandWidthTotal: '50Mbps', + showAll: true, showCpuDetail: false, showMemoryDetail: false, showBandWidthDetail: false, showDiskDetail: false, + cpuList: [{ num: 2, detail: '2.5GHz' }, { num: 2, detail: '3GHz' }, { num: 6, detail: '3.7GHz' }], + memoryList: [{ num: 1, detail: '64GB RAM' }, { num: 10, detail: '2GB RAM' }, { num: 4, detail: '8GB RAM' }], + diskList: [{ num: '16TB', detail: 'SSD' }, { num: '4TB', detail: 'HDD' }] }, + { id: 4, label: 'test1', icon: '/static/hostPerformance/火山云.png', cpuUsage: '4', cpuTotal: '10', memoryUsage: '16GB', memoryTotal: '128GB', diskUsage: '3TB', diskTotal: '15TB', bandWidthUsage: '40Mbps', bandWidthTotal: '200Mbps', + showAll: true, showCpuDetail: false, showMemoryDetail: false, showBandWidthDetail: false, showDiskDetail: false, + cpuList: [{ num: 4, detail: '2.4GHz' }, { num: 4, detail: '3GHz' }, { num: 2, detail: '3.7GHz' }], + memoryList: [{ num: 1, detail: '64GB RAM' }, { num: 10, detail: '2GB RAM' }, { num: 4, detail: '8GB RAM' }], + diskList: [{ num: '8TB', detail: 'SSD' }, { num: '2TB', detail: 'HDD' }] }, + { id: 5, label: 'test2', icon: '/static/hostPerformance/火山云.png', cpuUsage: '2', cpuTotal: '10', memoryUsage: '16GB', memoryTotal: '128GB', diskUsage: '4.5TB', diskTotal: '10TB', bandWidthUsage: '20Mbps', bandWidthTotal: '100Mbps', + showAll: true, showCpuDetail: false, showMemoryDetail: false, showBandWidthDetail: false, showDiskDetail: false, + cpuList: [{ num: 2, detail: '2.5GHz' }, { num: 4, detail: '3GHz' }, { num: 4, detail: '3.6GHz' }], + memoryList: [{ num: 1, detail: '64GB RAM' }, { num: 10, detail: '2GB RAM' }, { num: 4, detail: '8GB RAM' }], + diskList: [{ num: '8TB', detail: 'SSD' }, { num: '2TB', detail: 'HDD' }] }, + { id: 6, label: 'test3', icon: '/static/hostPerformance/火山云.png', cpuUsage: '5', cpuTotal: '10', memoryUsage: '12GB', memoryTotal: '128GB', diskUsage: '2.5TB', diskTotal: '10TB', bandWidthUsage: '20Mbps', bandWidthTotal: '100Mbps', + showAll: true, showCpuDetail: false, showMemoryDetail: false, showBandWidthDetail: false, showDiskDetail: false, + cpuList: [{ num: 2, detail: '2.5GHz' }, { num: 4, detail: '3GHz' }, { num: 4, detail: '3.6GHz' }], + memoryList: [{ num: 1, detail: '64GB RAM' }, { num: 10, detail: '2GB RAM' }, { num: 4, detail: '8GB RAM' }], + diskList: [{ num: '8TB', detail: 'SSD' }, { num: '2TB', detail: 'HDD' }] }, + { id: 7, label: 'test4', icon: '/static/hostPerformance/火山云.png', cpuUsage: '4', cpuTotal: '10', memoryUsage: '16GB', memoryTotal: '128GB', diskUsage: '1TB', diskTotal: '10TB', bandWidthUsage: '20Mbps', bandWidthTotal: '100Mbps', + showAll: true, showCpuDetail: false, showMemoryDetail: false, showBandWidthDetail: false, showDiskDetail: false, + cpuList: [{ num: 2, detail: '2.5GHz' }, { num: 4, detail: '3GHz' }, { num: 4, detail: '3.6GHz' }], + memoryList: [{ num: 1, detail: '64GB RAM' }, { num: 10, detail: '2GB RAM' }, { num: 4, detail: '8GB RAM' }], + diskList: [{ num: '8TB', detail: 'SSD' }, { num: '2TB', detail: 'HDD' }] }] + }) + }, + async getExpenseLists() { + await testApi.getExpenseLists().then((res) => { + this.expenseLists = res.data + }).catch(() => { + this.expenseLists = { + recentExpenseDetail: { + totalExpense: '200', + objectStorage: { + storageCapacity: '100GB', + storageDuration: 720, + unitPrice: '$0.02/GB/h', + cost: '$14.40' + }, + dataTransfer: { + transferCapacity: '500GB', + unitPrice: '$0.10/GB', + cost: '$50.00' + }, + databaseService: { + databaseType: 'MySQL', + storageCapacity: '50GB', + duration: 720, + unitPrice: '$0.02/GB/h', + cost: '$28.80' + }, + loadBalancer: { + instancesNum: 2, + duration: 720, + unitPrice: '$0.02', + cost: '$28.80' + } + }, + shortTermExpenseDetail: { + totalExpense: '122', + objectStorage: { + storageCapacity: '100GB', + storageDuration: 720, + unitPrice: '$0.02/GB/h', + cost: '$14.40' + }, + dataTransfer: { + transferCapacity: '500GB', + unitPrice: '$0.10/GB', + cost: '$50.00' + }, + databaseService: { + databaseType: 'MySQL', + storageCapacity: '50GB', + duration: 720, + unitPrice: '$0.02/GB/h', + cost: '$28.80' + }, + loadBalancer: { + instancesNum: 2, + duration: 720, + unitPrice: '$0.02', + cost: '$28.80' + } + }, + mediumTermExpenseDetail: { + totalExpense: '158', + objectStorage: { + storageCapacity: '100GB', + storageDuration: 720, + unitPrice: '$0.02/GB/h', + cost: '$14.40' + }, + dataTransfer: { + transferCapacity: '500GB', + unitPrice: '$0.10/GB', + cost: '$50.00' + }, + databaseService: { + databaseType: 'MySQL', + storageCapacity: '50GB', + duration: 720, + unitPrice: '$0.02/GB/h', + cost: '$28.80' + }, + loadBalancer: { + instancesNum: 2, + duration: 720, + unitPrice: '$0.02', + cost: '$28.80' + } + }, + longTermExpenseDetail: { + totalExpense: '158', + objectStorage: { + storageCapacity: '100GB', + storageDuration: 720, + unitPrice: '$0.02/GB/h', + cost: '$14.40' + }, + dataTransfer: { + transferCapacity: '500GB', + unitPrice: '$0.10/GB', + cost: '$50.00' + }, + databaseService: { + databaseType: 'MySQL', + storageCapacity: '50GB', + duration: 720, + unitPrice: '$0.02/GB/h', + cost: '$28.80' + }, + loadBalancer: { + instancesNum: 2, + duration: 720, + unitPrice: '$0.02', + cost: '$28.80' + } + } + } + this.expenseLists.shortTermExpenseDetail.totalExpense = (Math.abs(Math.random() * 100) + 100).toFixed(2) + this.expenseLists.longTermExpenseDetail.totalExpense = (Math.abs(Math.random() * 100) + 100).toFixed(2) + this.expenseLists.mediumTermExpenseDetail.totalExpense = (Math.abs(Math.random() * 100) + 100).toFixed(2) + }) + }, + scrollChange() { + const self = this + if (self.scrollValue === '开') { + const doscrollDispatch = function() { + const $parent = $('.card3-box') + const $first = $parent.find('div:first') + console.log($first) + const width = $first.width() + $first.animate({ + width: 0 + }, 500, function() { + $first.css('width', width).appendTo($parent) + }) + } + this.intervalDispatch = setInterval(function() { doscrollDispatch() }, 5000) + const doscrollCpu = function() { + const $parent = $('.tableInnerCpu') + const $first = $parent.find('div:first') + console.log($first) + const height = $first.height() + $first.animate({ + height: 0 + }, 500, function() { + $first.css('height', height).appendTo($parent) + }) + } + + const doscrollMemory = function() { + const $parent = $('.tableInnerMemory') + const $first = $parent.find('div:first') + const height = $first.height() + $first.animate({ + height: 0 + }, 500, function() { + $first.css('height', height).appendTo($parent) + }) + } + const doscrollBandWidth = function() { + const $parent = $('.tableInnerBandWidth') + const $first = $parent.find('div:first') + const height = $first.height() + $first.animate({ + height: 0 + }, 500, function() { + $first.css('height', height).appendTo($parent) + }) + } + const doscrollDisk = function() { + const $parent = $('.tableInnerDisk') + const $first = $parent.find('div:first') + const height = $first.height() + $first.animate({ + height: 0 + }, 500, function() { + $first.css('height', height).appendTo($parent) + }) + } + console.log(this.intervalCpu) + console.log(this.intervalMemory) + + self.intervalCpu = setInterval(function() { doscrollCpu() }, 5000) + self.intervalMemory = setInterval(function() { doscrollMemory() }, 5000) + self.intervalBandWidth = setInterval(function() { doscrollBandWidth() }, 5000) + self.intervalDisk = setInterval(function() { doscrollDisk() }, 5000) + } else if (this.scrollValue === '关') { + if (this.intervalCpu !== '') { + clearInterval(self.intervalCpu) + } + if (this.intervalMemory !== '') { + clearInterval(self.intervalMemory) + } + if (this.intervalBandWidth !== '') { + clearInterval(self.intervalBandWidth) + } + if (this.intervalDisk !== '') { + clearInterval(self.intervalDisk) + } + if (this.intervalDispatch !== '') { + clearInterval(self.intervalDispatch) + } + } + }, + + makeData() { + const base = +new Date() + const oneDay = 24 * 3600 * 1000 + // const data = [[base, Math.random() * 300]] + const data = [] + for (let i = -49; i < 50; i++) { + const now = new Date((base + i * oneDay)) + data.push([+now, Math.abs(Math.random() * 300).toFixed(2)]) + } + return data + }, + async getChartsData() { + await testApi.getTaskAllocationChartData().then((res) => { + this.recentExpense = res.data.recentExpense + this.shortTermExpense = res.data.shortTermExpense + this.mediumTermExpense = res.data.mediumTermExpense + this.longTermExpense = res.data.longTermExpense + }).catch(() => { + this.recentExpense = this.makeData() + this.shortTermExpense = this.makeData() + this.mediumTermExpense = this.makeData() + this.longTermExpense = this.makeData() + }) + }, + getCharts() { + this.getChartsData().then(r => { + const chart_real = echarts.init(document.getElementById('chart_real'), 'dark') + const chartRealOption = { + tooltip: { + trigger: 'axis' + }, + title: { + left: 'center', + text: '' + }, + backgroundColor: 'transparent', + toolbox: { + feature: { + dataZoom: { + yAxisIndex: 'none' + }, + restore: {}, + saveAsImage: {} + } + }, + xAxis: { + type: 'time', + boundaryGap: false + }, + yAxis: { + type: 'value', + boundaryGap: [0, '100%'] + }, + dataZoom: [ + { + type: 'inside', + start: 0, + end: 100 + }, + { + type: 'inside', + start: 0, + end: 100 + } + ], + series: [ + { + name: '费用(元)', + type: 'line', + smooth: true, + symbol: 'none', + areaStyle: {}, + data: this.recentExpense, + itemStyle: { + color: '#6495ED' + } + } + ] + } + chart_real.setOption(chartRealOption) + + const short_term_chart = echarts.init(document.getElementById('short_term_chart'), 'dark') + this.chartShortOption = { + tooltip: { + trigger: 'axis' + }, + title: { + left: 'center', + text: '' + }, + backgroundColor: 'transparent', + xAxis: { + type: 'time', + boundaryGap: false + }, + yAxis: { + type: 'value', + boundaryGap: [0, '100%'] + }, + dataZoom: [ + { + type: 'inside', + start: 46, + end: 54 + }, + { + type: 'inside', + start: 46, + end: 54 + } + ], + series: [ + { + name: '当前费用(元)', + type: 'line', + smooth: true, + symbol: 'none', + data: this.recentExpense, + itemStyle: { + color: '#6495ED' + } + }, { + name: '短期套餐费用(元)', + type: 'line', + smooth: true, + symbol: 'none', + data: this.shortTermExpense, + itemStyle: { + color: '#f5b000' + } + } + ] + } + short_term_chart.setOption(this.chartShortOption) + + const medium_term_chart = echarts.init(document.getElementById('medium_term_chart'), 'dark') + this.chartMediumOption = { + tooltip: { + trigger: 'axis' + }, + title: { + left: 'center', + text: '' + }, + backgroundColor: 'transparent', + xAxis: { + type: 'time', + boundaryGap: false + }, + yAxis: { + type: 'value', + boundaryGap: [0, '100%'] + }, + dataZoom: [ + { + type: 'inside', + start: 46, + end: 54 + }, + { + type: 'inside', + start: 46, + end: 54 + } + ], + series: [ + { + name: '当前费用(元)', + type: 'line', + smooth: true, + symbol: 'none', + data: this.recentExpense, + itemStyle: { + color: '#6495ED' + } + }, { + name: '中期套餐费用(元)', + type: 'line', + smooth: true, + symbol: 'none', + data: this.mediumTermExpense, + itemStyle: { + color: '#f5b000' + } + } + ] + } + medium_term_chart.setOption(this.chartMediumOption) + + const long_term_chart = echarts.init(document.getElementById('long_term_chart'), 'dark') + this.chartLongOption = { + tooltip: { + trigger: 'axis' + }, + title: { + left: 'center', + text: '' + }, + backgroundColor: 'transparent', + xAxis: { + type: 'time', + boundaryGap: false + }, + yAxis: { + type: 'value', + boundaryGap: [0, '100%'] + }, + dataZoom: [ + { + type: 'inside', + start: 46, + end: 54 + }, + { + type: 'inside', + start: 46, + end: 54 + } + ], + series: [ + { + name: '当前费用(元)', + type: 'line', + smooth: true, + symbol: 'none', + data: this.recentExpense, + itemStyle: { + color: '#6495ED' + } + }, { + name: '长期套餐费用(元)', + type: 'line', + smooth: true, + symbol: 'none', + data: this.longTermExpense, + itemStyle: { + color: '#f5b000' + } + } + ] + } + long_term_chart.setOption(this.chartLongOption) + }) + }, + openDetailDialog(termType) { + this.chartDialogVisible = true + + this.$nextTick(() => { + const dialogChart = echarts.init(document.getElementById('chartInDialog'), 'dark') + let chartOption + if (termType === `shortTerm`) { + chartOption = this.chartShortOption + this.expenseInDialog = this.expenseLists.shortTermExpenseDetail + } else if (termType === `mediumTerm`) { + chartOption = this.chartMediumOption + this.expenseInDialog = this.expenseLists.mediumTermExpenseDetail + } else if (termType === `longTerm`) { + chartOption = this.chartLongOption + this.expenseInDialog = this.expenseLists.longTermExpenseDetail + } + dialogChart.setOption(chartOption) + }) + }, + async chooseSet(termType) { + await testApi.chooseSet({ termType: termType }).then((res) => { + this.getUsageHostInfo() + this.upDateResults() + }).catch(() => { + this.getUsageHostInfo() + this.upDateResults() + const tempData = this.usageDataList + this.usageDataList = this.usageDataList1 + this.usageDataList1 = tempData + }) + }, + + showCpuDetail(item) { + for (let i = 0; i < this.usageDataList.length; i++) { + if (this.usageDataList[i].id === item.id) { + this.usageDataList[i].showAll = false + this.usageDataList[i].showCpuDetail = true + this.usageDataList[i].showBandWidthDetail = false + this.usageDataList[i].showDiskDetail = false + this.usageDataList[i].showMemoryDetail = false + } + } + }, + showBandWidthDetail(item) { + for (let i = 0; i < this.usageDataList.length; i++) { + if (this.usageDataList[i].id === item.id) { + this.usageDataList[i].showAll = false + this.usageDataList[i].showCpuDetail = false + this.usageDataList[i].showBandWidthDetail = true + this.usageDataList[i].showDiskDetail = false + this.usageDataList[i].showMemoryDetail = false + } + } + }, + showDiskDetail(item) { + for (let i = 0; i < this.usageDataList.length; i++) { + if (this.usageDataList[i].id === item.id) { + this.usageDataList[i].showAll = false + this.usageDataList[i].showCpuDetail = false + this.usageDataList[i].showBandWidthDetail = false + this.usageDataList[i].showDiskDetail = true + this.usageDataList[i].showMemoryDetail = false + } + } + }, + showMemoryDetail(item) { + for (let i = 0; i < this.usageDataList.length; i++) { + if (this.usageDataList[i].id === item.id) { + this.usageDataList[i].showAll = false + this.usageDataList[i].showCpuDetail = false + this.usageDataList[i].showBandWidthDetail = false + this.usageDataList[i].showDiskDetail = false + this.usageDataList[i].showMemoryDetail = true + } + } + }, + quitDetail(item) { + for (let i = 0; i < this.usageDataList.length; i++) { + if (this.usageDataList[i].id === item.id) { + this.usageDataList[i].showAll = true + this.usageDataList[i].showCpuDetail = false + this.usageDataList[i].showBandWidthDetail = false + this.usageDataList[i].showDiskDetail = false + this.usageDataList[i].showMemoryDetail = false + } + } + }, + fullScreen() { + const element = document.getElementById('entirety') + if (element.requestFullscreen) { + element.requestFullscreen() + } else if (element.mozRequestFullScreen) { + element.mozRequestFullScreen() + } else if (element.webkitRequestFullscreen) { + element.webkitRequestFullscreen() + } else if (element.msRequestFullscreen) { + element.msRequestFullscreen() + } + } + } + +} diff --git a/subject4-1-1-bc/pom.xml b/subject4-1-1-bc/pom.xml new file mode 100644 index 0000000..754ed3a --- /dev/null +++ b/subject4-1-1-bc/pom.xml @@ -0,0 +1,19 @@ + + + + joint-cloud + com.joint.cloud + 1.0.0-SNAPSHOT + + 4.0.0 + + subject4-1-1-bc + + + 8 + 8 + + + \ No newline at end of file diff --git a/subject4-1-1-helloworld/pom.xml b/subject4-1-1-helloworld/pom.xml new file mode 100644 index 0000000..5aa50e3 --- /dev/null +++ b/subject4-1-1-helloworld/pom.xml @@ -0,0 +1,67 @@ + + + + joint-cloud + com.joint.cloud + 1.0.0-SNAPSHOT + + 4.0.0 + + subject4-1-1-helloworld + subject4-1-1-helloworld + 1.0.1 + jar + + + 8 + 8 + + + + + com.joint.cloud + joint-cloud-common + ${joint.cloud.version} + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-web + + + io.springfox + springfox-swagger2 + + + io.springfox + springfox-swagger-ui + + + org.projectlombok + lombok + compile + + + org.springframework.cloud + spring-cloud-starter-consul-discovery + + + org.springframework.boot + spring-boot-starter-actuator + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + \ No newline at end of file diff --git a/subject4-1-1-helloworld/src/main/java/com/joint/cloud/subject4_1_1/helloworld/HelloworldApplication.java b/subject4-1-1-helloworld/src/main/java/com/joint/cloud/subject4_1_1/helloworld/HelloworldApplication.java new file mode 100644 index 0000000..78e69da --- /dev/null +++ b/subject4-1-1-helloworld/src/main/java/com/joint/cloud/subject4_1_1/helloworld/HelloworldApplication.java @@ -0,0 +1,17 @@ +package com.joint.cloud.subject4_1_1.helloworld; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.context.annotation.ComponentScan; + +@ComponentScan(basePackages = {"com.joint.cloud.*"}) +@SpringBootApplication +@EnableDiscoveryClient +public class HelloworldApplication { + + public static void main(String[] args) { + SpringApplication.run(HelloworldApplication.class, args); + } + +} diff --git a/subject4-1-1-helloworld/src/main/java/com/joint/cloud/subject4_1_1/helloworld/config/SwaggerConfig.java b/subject4-1-1-helloworld/src/main/java/com/joint/cloud/subject4_1_1/helloworld/config/SwaggerConfig.java new file mode 100644 index 0000000..3ffa046 --- /dev/null +++ b/subject4-1-1-helloworld/src/main/java/com/joint/cloud/subject4_1_1/helloworld/config/SwaggerConfig.java @@ -0,0 +1,34 @@ +package com.joint.cloud.subject4_1_1.helloworld.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +@Configuration +@EnableSwagger2 +public class SwaggerConfig { + + public static final String SWAGGER_TITLE = "云际项目API"; + public static final String SWAGGER_DESC = "云际项目"; + + @Bean + public Docket documentation() { + return new Docket(DocumentationType.SWAGGER_2) + .apiInfo(metadata()) + .select() + .apis(RequestHandlerSelectors.basePackage("com.joint.cloud.subject4_1_1.helloworld.controller")) + .build(); + } + + private ApiInfo metadata() { + return new ApiInfoBuilder().title(SWAGGER_TITLE) + .description(SWAGGER_DESC) + .version("1.0").build(); + } + +} diff --git a/subject4-1-1-helloworld/src/main/java/com/joint/cloud/subject4_1_1/helloworld/constants/ApiConstant.java b/subject4-1-1-helloworld/src/main/java/com/joint/cloud/subject4_1_1/helloworld/constants/ApiConstant.java new file mode 100644 index 0000000..e649e42 --- /dev/null +++ b/subject4-1-1-helloworld/src/main/java/com/joint/cloud/subject4_1_1/helloworld/constants/ApiConstant.java @@ -0,0 +1,6 @@ +package com.joint.cloud.subject4_1_1.helloworld.constants; + +public class ApiConstant { + + public static final String VERSION = "/v1"; +} diff --git a/subject4-1-1-helloworld/src/main/java/com/joint/cloud/subject4_1_1/helloworld/controller/DemoController.java b/subject4-1-1-helloworld/src/main/java/com/joint/cloud/subject4_1_1/helloworld/controller/DemoController.java new file mode 100644 index 0000000..9c40e23 --- /dev/null +++ b/subject4-1-1-helloworld/src/main/java/com/joint/cloud/subject4_1_1/helloworld/controller/DemoController.java @@ -0,0 +1,56 @@ +package com.joint.cloud.subject4_1_1.helloworld.controller; + +import com.joint.cloud.common.constants.ResponseCode; +import com.joint.cloud.common.exception.BusinessException; +import com.joint.cloud.common.response.BaseResponse; +import com.joint.cloud.subject4_1_1.helloworld.constants.ApiConstant; +import com.joint.cloud.subject4_1_1.helloworld.controller.request.DemoParam; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping(ApiConstant.VERSION + "/demo") +@Slf4j +@Api(tags = {"示例"}) +@BaseResponse +public class DemoController { + + @ApiOperation("URL入参示例") + @GetMapping("/param/1/{id}") + @ResponseBody + public Long paramTest1(@ApiParam("测试入参id") @PathVariable("id") Long id) { + log.info("接到入参: {}", id); + log.warn("接到入参: {}", id); + log.debug("接到入参: {}", id); + log.error("接到入参: {}", id); + return id; + } + + @ApiOperation("param入参示例") + @GetMapping("/param/2") + public Object paramTest2(@ApiParam("测试入参key") @RequestParam("key") Long key) { + return key; + } + + @ApiOperation("body入参示例") + @PostMapping("/param/3") + public DemoParam paramTest3(@Validated @ApiParam("测试入参demoParam") @RequestBody DemoParam demoParam) { + return demoParam; + } + + @ApiOperation("统一返回、统一异常示例") + @GetMapping("/{userId}") + public Object getUserById(@PathVariable Long userId) { + if (userId.equals(0L)) { + throw new BusinessException(ResponseCode.BAD_REQUEST, "提示信息"); + } else if (userId.equals(1L)) { + int i = 1 / 0; + } + return userId; + } +} + diff --git a/subject4-1-1-helloworld/src/main/java/com/joint/cloud/subject4_1_1/helloworld/controller/HealthController.java b/subject4-1-1-helloworld/src/main/java/com/joint/cloud/subject4_1_1/helloworld/controller/HealthController.java new file mode 100644 index 0000000..3584487 --- /dev/null +++ b/subject4-1-1-helloworld/src/main/java/com/joint/cloud/subject4_1_1/helloworld/controller/HealthController.java @@ -0,0 +1,16 @@ +package com.joint.cloud.subject4_1_1.helloworld.controller; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Date; + + +@RestController +public class HealthController { + + @GetMapping("/health") + public String health() { + return "I'm health now " + (new Date()).toString(); + } +} diff --git a/subject4-1-1-helloworld/src/main/java/com/joint/cloud/subject4_1_1/helloworld/controller/request/DemoParam.java b/subject4-1-1-helloworld/src/main/java/com/joint/cloud/subject4_1_1/helloworld/controller/request/DemoParam.java new file mode 100644 index 0000000..18aed0e --- /dev/null +++ b/subject4-1-1-helloworld/src/main/java/com/joint/cloud/subject4_1_1/helloworld/controller/request/DemoParam.java @@ -0,0 +1,20 @@ +package com.joint.cloud.subject4_1_1.helloworld.controller.request; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotEmpty; + +@Data +@ApiModel("测试DEMO入参") +public class DemoParam { + + @ApiModelProperty(value = "主键", required = true) + private Long id; + + @ApiModelProperty("名称") + @NotEmpty(message = "名称不能为空") + private String name; + +} diff --git a/subject4-1-1-helloworld/src/main/resources/application-dev.yml b/subject4-1-1-helloworld/src/main/resources/application-dev.yml new file mode 100644 index 0000000..eb51566 --- /dev/null +++ b/subject4-1-1-helloworld/src/main/resources/application-dev.yml @@ -0,0 +1,4 @@ +spring: + cloud: + consul: + host: ${CONSUL_ADDR:10.4.79.107} diff --git a/subject4-1-1-helloworld/src/main/resources/application.yml b/subject4-1-1-helloworld/src/main/resources/application.yml new file mode 100644 index 0000000..655db8d --- /dev/null +++ b/subject4-1-1-helloworld/src/main/resources/application.yml @@ -0,0 +1,19 @@ +server: + port: 40101 +spring: + application: + name: subject4-1-1-helloworld + profiles: + active: dev + cloud: + consul: + port: 8500 + discovery: + service-name: ${spring.application.name} + instance-id: ${spring.application.name}:${spring.cloud.consul.discovery.ip-address}:${server.port} + ip-address: ${REGISTRY_ADDR:${spring.cloud.client.ip-address}} + prefer-ip-address: true + health-check-path: /health + health-check-interval: 10s + health-check-critical-timeout: 30s + host: 10.4.79.107 diff --git a/task-resource-system/.keep b/task-resource-system/.keep new file mode 100644 index 0000000..e69de29 diff --git a/task-resource-system/api/testApi.js b/task-resource-system/api/testApi.js new file mode 100644 index 0000000..d053d18 --- /dev/null +++ b/task-resource-system/api/testApi.js @@ -0,0 +1,202 @@ +import request from '@/utils/request' + +const testApi = { + getUsageHostInfo: getUsageHostInfo, + getTaskAllocationChartData: getTaskAllocationChartData, + getExpenseLists: getExpenseLists, + chooseSet: chooseSet, + + getHostNums: getHostNums, + getGailanInfo: getGailanInfo, + getAbnormalDataList: getAbnormalDataList, + getManufacturerChartData: getManufacturerChartData, + getCpuChartData: getCpuChartData, + getDiskChartData: getDiskChartData, + getMemoryChartData: getMemoryChartData, + getPerformanceChartData: getPerformanceChartData, + getLineChartByOrgName: getLineChartByOrgName, + getAlarmTableData: getAlarmTableData, + + getAbnormalInfoList: getAbnormalInfoList, + getEventResponseChartData: getEventResponseChartData, + getIllegalInfoList: getIllegalInfoList, + getIllegalPublicityChartData: getIllegalPublicityChartData, + getTransactionInfoList: getTransactionInfoList, + getSupplierList: getSupplierList, + getSupplierInfoById: getSupplierInfoById, + searchHostInfoById: searchHostInfoById, + getHostChartsById: getHostChartsById + +} +// kt2/taskAllocation +function getUsageHostInfo() { + return request({ + url: `/cloud/v1.0/kt2/taskAllocation/getUsageHostInfo`, + method: 'get' + }) +} +function getTaskAllocationChartData() { + return request({ + url: `/cloud/v1.0/kt2/taskAllocation/getTaskAllocationChartData`, + method: 'get' + }) +} + +function getExpenseLists() { + return request({ + url: `/cloud/v1.0/kt2/taskAllocation/getExpenseLists`, + method: 'get' + }) +} + +function chooseSet(data) { + return request({ + url: `/cloud/v1.0/kt2/taskAllocation/chooseSet`, + data: data, + method: 'post' + }) +} + +// kt3/screen +function getHostNums() { + return request({ + url: `/cloud/v1.0/kt3/screen/getHostNums`, + method: 'get' + }) +} + +function getGailanInfo() { + return request({ + url: `/cloud/v1.0/kt3/screen/getGailanInfo`, + method: 'get' + }) +} +function getManufacturerChartData() { + return request({ + url: `/cloud/v1.0/kt3/screen/getManufacturerChartData`, + method: 'get' + }) +} + +function getCpuChartData(params) { + return request({ + url: `/cloud/v1.0/kt3/screen/getCpuChartData`, + method: 'get', + params: params + }) +} + +function getDiskChartData(params) { + return request({ + url: `/cloud/v1.0/kt3/screen/getDiskChartData`, + method: 'get', + params: params + }) +} + +function getMemoryChartData(params) { + return request({ + url: `/cloud/v1.0/kt3/screen/getMemoryChartData`, + method: 'get', + params: params + }) +} + +function getPerformanceChartData(params) { + return request({ + url: `/cloud/v1.0/kt3/screen/getPerformanceChartData`, + method: 'get', + params: params + }) +} + +function getLineChartByOrgName(params) { + return request({ + url: `/cloud/v1.0/kt3/screen/getLineChartByOrgName`, + method: 'get', + params: params + }) +} + +function getAlarmTableData(params) { + return request({ + url: `/cloud/v1.0/kt3/screen/getAlarmTableData`, + method: 'get', + params: params + }) +} + +function getAbnormalDataList() { + return request({ + url: `/cloud/v1.0/kt3/screen/getAbnormalDataList`, + method: 'get' + }) +} + +// kt3/eventResponse +function getAbnormalInfoList() { + return request({ + url: `/cloud/v1.0/kt3/eventResponse/getAbnormalInfoList`, + method: 'get' + }) +} + +function getEventResponseChartData() { + return request({ + url: `/cloud/v1.0/kt3/eventResponse/getEventResponseChartData`, + method: 'get' + }) +} + +// kt3/illegalPublicity +function getIllegalInfoList() { + return request({ + url: `/cloud/v1.0/kt3/illegalPublicity/getIllegalInfoList`, + method: 'get' + }) +} + +function getIllegalPublicityChartData() { + return request({ + url: `/cloud/v1.0/kt3/illegalPublicity/getIllegalPublicityChartData`, + method: 'get' + }) +} + +// kt3/supplierManagement +function getTransactionInfoList() { + return request({ + url: `/cloud/v1.0/kt3/supplierManagement/getTransactionInfoList`, + method: 'get' + }) +} +function getSupplierList() { + return request({ + url: `/cloud/v1.0/kt3/supplierManagement/getSupplierList`, + method: 'get' + }) +} + +function getSupplierInfoById(params) { + return request({ + url: `/cloud/v1.0/kt3/supplierManagement/getSupplierInfoById`, + method: 'get', + params: params + }) +} +// kt3/hostPerformance +function searchHostInfoById(params) { + return request({ + url: `/cloud/v1.0/kt3/hostPerformance/searchHostInfoById`, + method: 'get', + params: params + }) +} +function getHostChartsById(params) { + return request({ + url: `/cloud/v1.0/kt3/hostPerformance/searchHostInfoById`, + method: 'get', + params: params + }) +} +export default testApi diff --git a/task-resource-system/views/eventResponse/css/eventResponse.scss b/task-resource-system/views/eventResponse/css/eventResponse.scss new file mode 100644 index 0000000..092d888 --- /dev/null +++ b/task-resource-system/views/eventResponse/css/eventResponse.scss @@ -0,0 +1,113 @@ +#eventResponse{ + + .title_class { + font-family: 'Alternate'; + color: #FFFFFF; + letter-spacing: 0; + text-shadow: 0 0 6px #26A2FF; + font-size: 12pt; + } + + .hr { + border-bottom: 1px solid; + opacity: 0.2; + color: white; + } + + .hr_tag { + margin-top: 17px; + border-bottom: 2px solid; + color: #FFFFFF; + width: 14px; + } + + .hr_left { + margin-left: 50px; + margin-right: 50px; + } + + .el-card { + border-radius: 4px; + border: 1px solid #ffffff; + background-color: rgba(255, 255, 255, 0); + } + .el-card__header { + padding: 3px 10px; + border-bottom: 1px solid #EBEEF5; + -webkit-box-sizing: border-box; + box-sizing: border-box; + height: 4vh; + } + .el-card__body, .el-main { + padding: 5px; + } + .el-card__body{ + padding: 5px; + height: 20vh; + } + .chart_card{ + height:36vh; + padding-bottom:80px; + color: #FFFFFF; + flex-wrap: wrap; + margin-top: -1vh; + } + + .cardDiv{ + margin: 10px 50px 20px 50px; + } + + .table_box { + //表格间距 + margin: 20px 50px 20px 50px; + .el-table--mini .el-table__cell { + } + + //整个表格背景颜色及边框弧度 + .el-table { + background-color: rgba(38, 38, 38, 0); + border-radius: 3px; + border-color:white ; + } + + //字体颜色 + .el-table tr { + background-color: transparent; + color: white; + } + + .el-table th.el-table__cell.is-leaf { + border-bottom: 1px solid rgba(255, 255, 255, 0.8); + } + + + //标题背景颜色 + .el-table th { + background-color: transparent !important; + font-size: 11pt; + color: #a8a7a7; + } + + //标题字体颜色 + .el-table th .cell { + color: #a8a7a7; + font-size: 11pt; + height:19px + } + //去除行下划线 + .el-table td { + border-color: rgba(255, 255, 255, 0.2); + font-size: 11pt; + } + + //修改鼠标滑过背景颜色 + .el-table--enable-row-hover .el-table__body tr:hover td { + background-color: rgba(38, 38, 38, 0.5) !important; + color: white !important; + } + } +} + + + + diff --git a/task-resource-system/views/eventResponse/index.vue b/task-resource-system/views/eventResponse/index.vue new file mode 100644 index 0000000..718e187 --- /dev/null +++ b/task-resource-system/views/eventResponse/index.vue @@ -0,0 +1,85 @@ + + + + diff --git a/task-resource-system/views/eventResponse/js/index.js b/task-resource-system/views/eventResponse/js/index.js new file mode 100644 index 0000000..3a44389 --- /dev/null +++ b/task-resource-system/views/eventResponse/js/index.js @@ -0,0 +1,294 @@ +import echarts from 'echarts' +import testApi from '@/api/testApi' + +export default { + name: 'eventResponse', + data() { + return { + abnormalInfoList: [], + chartsDatas: [] + } + }, + created() { + }, + + async mounted() { + await this.getList() + this.makeCharts() + }, + methods: { + async getList() { + await testApi.getAbnormalInfoList().then((res) => { + this.abnormalInfoList = res.data + }).catch((error) => { + this.abnormalInfoList = [ + { + id: '0', + hostId: '12', + supplierName: '阿里云', + abnormalType: '0', + abnormalTime: '2023-07-02 15:42:53', + responseTime: '2023-07-02 15:43:12', + recoverTime: '2023-07-02 15:45:33' + }, + { + id: '1', + hostId: '22', + supplierName: '阿里云', + abnormalType: '1', + abnormalTime: '2023-07-02 15:42:23', + responseTime: '2023-07-02 15:44:11', + recoverTime: '2023-07-02 15:45:12' + }, + { + id: '2', + hostId: '17', + supplierName: '联通云', + abnormalType: '0', + abnormalTime: '2023-07-02 15:12:12', + responseTime: '2023-07-02 15:12:31', + recoverTime: '2023-07-02 15:16:08' + }, + { + id: '3', + hostId: '14', + supplierName: '华为云', + abnormalType: '1', + abnormalTime: '2023-07-02 14:29:14', + responseTime: '2023-07-02 14:29:57', + recoverTime: '2023-07-02 14:33:43' + }, + { + id: '4', + hostId: '19', + supplierName: '华为云', + abnormalType: '2', + abnormalTime: '2023-07-02 14:22:33', + responseTime: '2023-07-02 14:22:42', + recoverTime: '2023-07-02 14:25:55' + }, + { + id: '5', + hostId: '33', + supplierName: '火山云', + abnormalType: '3', + abnormalTime: '2023-07-02 13:42:33', + responseTime: '2023-07-02 13:43:01', + recoverTime: '2023-07-02 13:44:17' + }, + { + id: '6', + hostId: '11', + supplierName: '火山云', + abnormalType: '2', + abnormalTime: '2023-07-02 13:22:33', + responseTime: '2023-07-02 13:22:13', + recoverTime: '2023-07-02 13:23:33' + } + ] + }) + + await testApi.getEventResponseChartData().then((res) => { + this.chartsDatas = res.data + }).catch((error) => { + this.chartsDatas = [ + { id: 0, supplierName: '阿里云', abnormalNum: 12, argResponseTime: 12, argRecoverTime: 49 }, + { id: 1, supplierName: '联通云', abnormalNum: 15, argResponseTime: 9, argRecoverTime: 35 }, + { id: 2, supplierName: '华为云', abnormalNum: 8, argResponseTime: 15, argRecoverTime: 79 }, + { id: 3, supplierName: '火山云', abnormalNum: 11, argResponseTime: 21, argRecoverTime: 125 } + ] + }) + }, + makeCharts() { + this.makePieChart() + this.makeBarCharts() + }, + fullScreen() { + const element = document.getElementById('entirety') + if (element.requestFullscreen) { + element.requestFullscreen() + } else if (element.mozRequestFullScreen) { + element.mozRequestFullScreen() + } else if (element.webkitRequestFullscreen) { + element.webkitRequestFullscreen() + } else if (element.msRequestFullscreen) { + element.msRequestFullscreen() + } + }, + getTypeText(type) { + switch (type) { + case '0': + return { + label: '网络异常' + } + case '1': + return { + label: '传输异常' + } + case '2': + return { + label: '服务失败' + } + case '3': + return { + label: '计算错误' + } + } + }, + makePieChart() { + const pieChart = echarts.init(document.getElementById('pieChart'), 'light') + const data = [] + for (let i = 0; i < this.chartsDatas.length; i++) { + data.push({ + value: this.chartsDatas[i].abnormalNum, + name: this.chartsDatas[i].supplierName + }) + } + const option = { + tooltip: { + trigger: 'item', + formatter: '{b} : {c} ({d}%)' + }, + legend: { + orient: 'horizontal', + left: 'left', + textStyle: { + color: '#FFFFFF' + } + }, + series: [ + { + name: '', + type: 'pie', + radius: '50%', + data: data, + emphasis: { + itemStyle: { + shadowBlur: 10, + shadowOffsetX: 0, + shadowColor: 'rgba(0, 0, 0, 0.5)' + } + }, + label: { + formatter: '{b}: {c} ({d}%)' + } + } + ] + } + pieChart.setOption(option) + }, + makeBarCharts() { + const responseTimeChart = echarts.init(document.getElementById('responseTimeChart'), 'light') + const recoverTimeChart = echarts.init(document.getElementById('recoverTimeChart'), 'light') + + const data = this.chartsDatas + const responseTimeOption = { + tooltip: { + trigger: 'axis', + formatter(params) { + return params[0].name + }, + axisPointer: { + type: 'shadow' + } + }, + yAxis: { + type: 'value', + show: true, + axisLabel: { + color: 'rgb(255, 255, 255, 0.5)', + show: true + } + }, + xAxis: { + type: 'category', + data: data.map(s => s.supplierName), + axisLabel: { + color: 'rgb(255, 255, 255, 0.5)', + formatter(val) { + if (val.length < 5) { + return val + } else { + return val.substring(0, 4) + '..' + } + }, + show: true + } + }, + series: [{ + data: data.map(s => s.argResponseTime), + type: 'bar', + barWidth: 25, + itemStyle: { + normal: { + label: { + show: true, // 开启显示 + position: 'top', + textStyle: { // 数值样式 + color: '#5FA4E9', + fontSize: 12 + } + }, + color: '#307CE0' + } + } + }] + } + const recoverTimeOption = { + tooltip: { + trigger: 'axis', + formatter(params) { + return params[0].name + }, + axisPointer: { + type: 'shadow' + } + }, + yAxis: { + type: 'value', + show: true, + axisLabel: { + color: 'rgb(255, 255, 255, 0.5)', + show: true + } + }, + xAxis: { + type: 'category', + data: data.map(s => s.supplierName), + axisLabel: { + color: 'rgb(255, 255, 255, 0.5)', + formatter(val) { + if (val.length < 5) { + return val + } else { + return val.substring(0, 4) + '..' + } + }, + show: true + } + }, + series: [{ + data: data.map(s => s.argRecoverTime), + type: 'bar', + barWidth: 25, + itemStyle: { + normal: { + label: { + show: true, // 开启显示 + position: 'top', + textStyle: { // 数值样式 + color: '#5FA4E9', + fontSize: 12 + } + }, + color: '#307CE0' + } + } + }] + } + responseTimeChart.setOption(responseTimeOption) + recoverTimeChart.setOption(recoverTimeOption) + } + + } +} diff --git a/task-resource-system/views/hostPerformance/css/hostPerformance.scss b/task-resource-system/views/hostPerformance/css/hostPerformance.scss new file mode 100644 index 0000000..3b5942a --- /dev/null +++ b/task-resource-system/views/hostPerformance/css/hostPerformance.scss @@ -0,0 +1,333 @@ +#hostPerformance { + .el-radio-button__inner { + color: white; + } + + .el-radio-button--mini .el-radio-button__inner { + padding: 7px 7px; + background: rgba(255, 255, 255, 0); + } + + .el-popover { + background: rgba(0, 0, 0, 0.7); + } + + .el-popover .my-el-popover { + + } + + .el-descriptions { + background: rgba(255, 255, 255, 0); + } + + .el-descriptions__body { + color: #ffffff; + background-color: rgba(255, 255, 255, 0); + } + + .el-descriptions__title { + color: white; + } + + el-descriptions-item el-descriptions-item__cell { + height: 20px; + } + + .el-notification.right { + background: rgba(0, 0, 0, 0.7); + color: white; + right: 16px; + overflow: visible; + } + + .el-notification__title { + color: white + } + + .el-notification__content { + color: white; + } + + .el-notification__closeBtn { + color: #ffffff; + + } + + .el-dialog { + background: rgba(0, 0, 0, 0.3); + border-radius: 10px; + } + + .el-dialog > .el-dialog__body { + padding: 0px; + border-left: 2px solid #FFFFFF; + border-right: 2px solid #FFFFFF; + background-color: rgba(0, 0, 0, 0.3); + color: white; + margin: 0 0 0 0; + } + + .el-dialog__title { + color: #FFFFFF; + } + + .el-dialog__header { + border-top: 2px solid #FFFFFF; + border-left: 2px solid #FFFFFF; + border-right: 2px solid #FFFFFF; + border-top-left-radius: 10px; + border-top-right-radius: 10px; + background-color: rgba(0, 0, 0, 0.3); + color: white; + } + + .el-dialog__footer { + border-bottom: 2px solid #FFFFFF; + border-left: 2px solid #FFFFFF; + border-right: 2px solid #FFFFFF; + border-bottom-left-radius: 10px; + border-bottom-right-radius: 10px; + background-color: rgba(0, 0, 0, 0.3); + color: white; + } + + .title_row { + margin-top: 1%; + display: flex; + display: -webkit-flex; + justify-content: center; + } + + .title_box { + display: flex; + flex-direction: column; + justify-content: flex-end; + } + + .title_box_text { + font-family: 'Alternate'; + font-size: 32px; + color: white; + } + + .title_box_hr { + border-bottom: 2px solid; + opacity: 0.2; + color: white; + width: 120%; + margin-top: 2%; + margin-left: -10%; + } + + .top_button_box { + margin-top: 1%; + margin-left: auto; + display: flex; + display: -webkit-flex; + align-items: center; + } + + + .gailan_label { + font-family: 'Alternate'; + opacity: 0.7; + font-size: 12px; + color: #FFFFFF; + letter-spacing: 0; + line-height: 12px; + } + + .title_class { + font-family: 'Alternate'; + font-size: 15px; + color: white; + } + + .gailan_box { + background-image: url("/static/hostPerformance/box.png"); + background-size: 100% 100%; + border-color: white; + margin-left: 10px; + margin-top: 10px; + box-shadow: 5px 5px 20px 0 rgba(19, 29, 39, 0.50); + } + + .gailan_box hr { + background: #FFFFFF; + width: 14px; + /*height: 2px;*/ + } + + .gailan_box img { + /*box-shadow: 0 0 6px 1px #26A2FF;*/ + text-align: center; + } + + .gailan_box_total { + font-family: 'Alternate'; + /*font-size: 42px;*/ + font-size: 36px; + color: #FFFFFF; + letter-spacing: 0; + line-height: 59px; + text-shadow: 0 0 6px #26A2FF; + } + + .gailan_box_center { + text-align: center; + } + + .gailan_box_img { + margin-top: 28px; + height: 44px; + width: 44px; + } + + .gailan_box_lable { + font-family: 'Alternate'; + font-size: 14px; + color: #FFFFFF; + letter-spacing: 0; + text-align: center; + } + + .host_num_box { + margin-top: 1.5vh; + display: flex; + display: -webkit-flex; + flex-direction: column; + justify-content: center; + text-align: center; + } + + .host_num_text { + font-family: 'Alternate'; + font-size: 16px; + font-weight: bold; + } + + .card_title_row { + display: flex; + display: -webkit-flex; + justify-content: space-between; + } + + .chart_box { + width: 100%; + } + + .hr { + border-bottom: 1px solid; + opacity: 0.2; + color: white; + } + + .hr_tag { + margin-top: 17px; + border-bottom: 2px solid; + color: #FFFFFF; + width: 14px; + } + + .hr_left { + margin-left: 50px; + margin-right: 50px; + } + + .title_class { + font-family: 'Alternate'; + font-weight: bold; + font-size: 11pt; + color: white; + } + + .title_left_class { + margin-left: 50px; + } + + .hr_mid { + /*margin-top: 17px;*/ + /*width: 630px*/ + } + + .hr_right { + /*width: 590px*/ + } + + .table_box { + //表格间距 + .el-table--mini .el-table__cell { + padding: 2px 0 + } + + //整个表格背景颜色及边框弧度 + .el-table { + background-color: rgba(38, 38, 38, 0); + border-radius: 9px; + } + + //字体颜色 + .el-table tr { + background-color: transparent; + color: white; + height: 8vh + } + + .el-table th.el-table__cell.is-leaf { + border-bottom: 1px solid rgba(255, 255, 255, 0.3); + } + + //标题背景颜色 + .el-table th { + background-color: transparent !important; + } + + //标题字体颜色 + .el-table th .cell { + color: #a8a7a7 + } + + //去除边框 + .el-table--border { + border: none; + } + + //去除行下划线 + .el-table td { + border-color: rgba(255, 255, 255, 0.2); + } + + //修改鼠标滑过背景颜色 + .el-table--enable-row-hover .el-table__body tr:hover td { + background-color: rgba(38, 38, 38, 0.5) !important; + color: white !important; + } + } + + .table_title span { + color: white; + opacity: 0.5; + font-size: 12px; + } + + .table_value { + padding-left: 20px; + margin-left: 50px; + height: 30px; + padding-top: 4px; + } + + .table_value span { + color: white; + + } + + /* 偶数行的样式 */ + .table_value:nth-child(even) { + background: rgba(255, 255, 255, 0.06); + } +} + + + + diff --git a/task-resource-system/views/hostPerformance/index.vue b/task-resource-system/views/hostPerformance/index.vue new file mode 100644 index 0000000..0dd4581 --- /dev/null +++ b/task-resource-system/views/hostPerformance/index.vue @@ -0,0 +1,302 @@ + + + + diff --git a/task-resource-system/views/hostPerformance/js/index.js b/task-resource-system/views/hostPerformance/js/index.js new file mode 100644 index 0000000..185dbc8 --- /dev/null +++ b/task-resource-system/views/hostPerformance/js/index.js @@ -0,0 +1,1219 @@ +import echarts from 'echarts' +import testApi from '@/api/testApi' + +export default { + name: 'businessScreen', + data() { + return { + switchValue: true, + chartDialogVisible: false, + tempData: '', + dateRange: '', + hostNum: '', + normalHostNum: '', + abnormalHostNum: '', + cpuRadio: '云主机', + memoryRadio: '云主机', + diskRadio: '云主机', + abnormalRadio: 'CPU', + alarmRadio: '全部', + serviceRadio: 'aaaa', + pieChartFlag: '', + pieChartDialogVisible: false, + pieChartDialogTitle: '', + pieChartAbnormalList: [], + tableDetailDialogVisible: false, + tableDetailMessage: '', + abnormalData: [], + gailan_box: [], + // chartsDatas: [], + manufacturerChartData: [], + alarmTableData: [], + // dialogChartData: [], + alarmTableDataShow: [] + } + }, + created() { + }, + + async mounted() { + await this.getBaseDatas() + await this.makeUsageCharts() + await this.getAlarmTableData() + // this.getDialogChartDatas() + }, + methods: { + makeData() { + let base = +new Date(2023, 5, 10) + const oneDay = 24 * 3600 * 1000 + const data = [[base, Math.random() * 300]] + for (let i = 1; i < 2000; i++) { + const now = new Date((base += oneDay)) + data.push([+now, Math.abs(Math.round((Math.random() - 0.5) * 20 + data[i - 1][1]))]) + } + return data + }, + /* getDialogChartDatas() { + this.chartsDatas = [ + { orgId: 0, orgName: 'ali-region22-1', cpu: 6, memory: 12, disk: 130 }, + { orgId: 11, orgName: 'ali-region22-2', cpu: 4, memory: 16, disk: 319 }, + { orgId: 106, orgName: 'huawei-region37-1', cpu: 12, memory: 1, disk: 325 }, + { orgId: 107, orgName: 'unicom-region24-1', cpu: 0, memory: 9, disk: 288 }, + { orgId: 108, orgName: 'volcano-region33-1', cpu: 45, memory: 2, disk: 255 }, + { orgId: 109, orgName: 'huawei-region37-2', cpu: 0, memory: 8, disk: 198 } + ] + for (let i = 0; i < this.chartsDatas.length; i++) { + const valueCpu = this.makeData() + const valueMemory = this.makeData() + const valueDisk = this.makeData() + const valuePerformance = this.makeData() + + this.dialogChartData.push({ orgName: this.chartsDatas[i].orgName, + dataListCpu: valueCpu, + dataListMemory: valueMemory, + dataListDisk: valueDisk, + dataListPerformance: valuePerformance + }) + } + },*/ + fullScreen() { + const element = document.getElementById('entirety') + if (element.requestFullscreen) { + element.requestFullscreen() + } else if (element.mozRequestFullScreen) { + element.mozRequestFullScreen() + } else if (element.webkitRequestFullscreen) { + element.webkitRequestFullscreen() + } else if (element.msRequestFullscreen) { + element.msRequestFullscreen() + } + }, + async makeCpuChart() { + const statistics_cpu_chart = echarts.init(document.getElementById('statistics_cpu'), 'light') + let data + await testApi.getCpuChartData({ cpuType: this.cpuRadio }).then((res) => { + data = res.data + }).catch(() => { + data = [ + { orgId: 0, orgName: 'ali-region22-1', cpu: 6 }, + { orgId: 11, orgName: 'ali-region22-2', cpu: 4 }, + { orgId: 106, orgName: 'huawei-region37-1', cpu: 12 }, + { orgId: 107, orgName: 'unicom-region24-1', cpu: 0 }, + { orgId: 108, orgName: 'volcano-region33-1', cpu: 45 }, + { orgId: 109, orgName: 'huawei-region37-2', cpu: 0 } + ] + }) + const maxCpu = Math.max.apply(Math, data.map(item => item.cpu)) + data.sort((a, b) => b.cpu - a.cpu) + const statistics_cpu_option = { + tooltip: { + trigger: 'axis', + formatter(params) { + return params[0].name + }, + axisPointer: { + type: 'shadow' + } + }, + xAxis: { + type: 'value', + show: false, + max: 'dataMax' + }, + yAxis: { + type: 'category', + data: data.map(s => s.orgName).slice(0, 5).reverse(), + axisLabel: { + color: 'rgb(255, 255, 255, 0.5)', + formatter(val) { + if (val.length < 5) { + return val + } else { + return val.substring(0, 4) + '..' + } + }, + show: true + }, + // 去掉刻度线 + axisLine: { 'show': false }, + axisTick: { 'show': false } + }, + series: [{ + // shadow + type: 'bar', + itemStyle: { + normal: { color: 'rgb(255, 255, 255, 0.1)' } + }, + data: [maxCpu, maxCpu, maxCpu, maxCpu, maxCpu], + barGap: '-100%', + barWidth: 10 + }, { + data: data.map(s => s.cpu).slice(0, 5).reverse(), + type: 'bar', + barWidth: 10, + itemStyle: { + normal: { + label: { + show: true, // 开启显示 + position: 'right', + textStyle: { // 数值样式 + color: '#5FA4E9', + fontSize: 12 + } + }, + color: '#307CE0' + } + } + }], + grid: { + containLabel: true, + top: 10, + left: 10, + bottom: 10 + } + } + + statistics_cpu_chart.setOption(statistics_cpu_option) + statistics_cpu_chart.getZr().on('click', async params => { + const pointInPixel = [params.offsetX, params.offsetY] + if (statistics_cpu_chart.containPixel('grid', pointInPixel)) { + // 点击第几个柱子 + const pointInGrid = statistics_cpu_chart.convertFromPixel({ seriesIndex: 0 }, pointInPixel) + const yIndex = pointInGrid[1] + data.sort((a, b) => b.cpu - a.cpu) + const orgName = data.map(s => s.orgName).slice(0, 5).reverse()[yIndex] + + let dialogChartDataUsage = [] + /* for (let i = 0; i < this.dialogChartData.length; i++) { + if (this.dialogChartData[i].orgName === orgName) { + dialogChartDataUsage = this.dialogChartData[i].dataListCpu + } + }*/ + + await testApi.getLineChartByOrgName({ orgName: orgName, type: 'cpu' }).then((res) => { + dialogChartDataUsage = res.data + }).catch(() => { + dialogChartDataUsage = this.makeData() + }) + + this.chartDialogVisible = true + this.$nextTick(() => { + const dialogChart = echarts.init(this.$refs.chartInDialog, 'dark') + const dialogChartOption = { + tooltip: { + trigger: 'axis', + position: function(pt) { + return [pt[0], '10%'] + } + }, + title: { + left: 'center', + text: '' + }, + backgroundColor: 'transparent', + toolbox: { + feature: { + dataZoom: { + yAxisIndex: 'none' + }, + restore: {}, + saveAsImage: {} + } + }, + xAxis: { + type: 'time', + boundaryGap: false + }, + yAxis: { + type: 'value', + boundaryGap: [0, '100%'] + }, + dataZoom: [ + { + type: 'inside', + start: 0, + end: 20 + }, + { + type: 'inside', + start: 0, + end: 20 + } + ], + series: [ + { + name: 'test data', + type: 'line', + smooth: true, + symbol: 'none', + areaStyle: {}, + data: dialogChartDataUsage + } + ] + } + dialogChart.setOption(dialogChartOption) + }) + } + }) + }, + async makeMemoryChart() { + const statistics_memory_chart = echarts.init(document.getElementById('statistics_memory'), 'light') + let data + await testApi.getMemoryChartData({ memoryType: this.memoryRadio }).then((res) => { + data = res.data + }).catch(() => { + data = [ + { orgId: 0, orgName: 'ali-region22-1', memory: 12 }, + { orgId: 11, orgName: 'ali-region22-2', memory: 16 }, + { orgId: 106, orgName: 'huawei-region37-1', memory: 1 }, + { orgId: 107, orgName: 'unicom-region24-1', memory: 9 }, + { orgId: 108, orgName: 'volcano-region33-1', memory: 2 }, + { orgId: 109, orgName: 'huawei-region37-2', memory: 8 } + ] + }) + const maxMemory = Math.max.apply(Math, data.map(item => item.memory)) + data.sort((a, b) => b.memory - a.memory) + const statistics_memory_option = { + tooltip: { + trigger: 'axis', + formatter(params) { + return params[0].name + }, + axisPointer: { + type: 'shadow' + } + }, + xAxis: { + type: 'value', + show: false, + max: 'dataMax' + }, + yAxis: { + type: 'category', + data: data.map(s => s.orgName).slice(0, 5).reverse(), + axisLabel: { + color: 'rgb(255, 255, 255, 0.5)', + formatter(val) { + if (val.length < 5) { + return val + } else { + return val.substring(0, 4) + '..' + } + }, + show: true + }, + // 去掉刻度线 + axisLine: { 'show': false }, + axisTick: { 'show': false } + }, + series: [{ + // shadow + type: 'bar', + itemStyle: { + normal: { color: 'rgb(255, 255, 255, 0.1)' } + }, + data: [maxMemory, maxMemory, maxMemory, maxMemory, maxMemory], + barGap: '-100%', + barWidth: 10 + }, { + data: data.map(s => s.memory).slice(0, 5).reverse(), + type: 'bar', + barWidth: 10, + itemStyle: { + normal: { + label: { + show: true, // 开启显示 + position: 'right', + textStyle: { // 数值样式 + color: '#5FA4E9', + fontSize: 12 + } + }, + color: '#307CE0' + } + } + }], + grid: { + containLabel: true, + top: 10, + left: 10, + bottom: 10 + } + } + statistics_memory_chart.setOption(statistics_memory_option) + statistics_memory_chart.getZr().on('click', async params => { + const pointInPixel = [params.offsetX, params.offsetY] + if (statistics_memory_chart.containPixel('grid', pointInPixel)) { + // 点击第几个柱子 + const pointInGrid = statistics_memory_chart.convertFromPixel({ seriesIndex: 0 }, pointInPixel) + const yIndex = pointInGrid[1] + data.sort((a, b) => b.memory - a.memory) + const orgName = data.map(s => s.orgName).slice(0, 5).reverse()[yIndex] + + let dialogChartDataUsage = [] + /* for (let i = 0; i < this.dialogChartData.length; i++) { + if (this.dialogChartData[i].orgName === orgName) { + dialogChartDataUsage = this.dialogChartData[i].dataListMemory + } + }*/ + await testApi.getLineChartByOrgName({ orgName: orgName, type: 'memory' }).then((res) => { + dialogChartDataUsage = res.data + }).catch(() => { + dialogChartDataUsage = this.makeData() + }) + this.chartDialogVisible = true + this.$nextTick(() => { + const dialogChart = echarts.init(this.$refs.chartInDialog, 'dark') + const dialogChartOption = { + tooltip: { + trigger: 'axis', + position: function(pt) { + return [pt[0], '10%'] + } + }, + title: { + left: 'center', + text: '' + }, + backgroundColor: 'transparent', + toolbox: { + feature: { + dataZoom: { + yAxisIndex: 'none' + }, + restore: {}, + saveAsImage: {} + } + }, + xAxis: { + type: 'time', + boundaryGap: false + }, + yAxis: { + type: 'value', + boundaryGap: [0, '100%'] + }, + dataZoom: [ + { + type: 'inside', + start: 0, + end: 20 + }, + { + type: 'inside', + start: 0, + end: 20 + } + ], + series: [ + { + name: 'test data', + type: 'line', + smooth: true, + symbol: 'none', + areaStyle: {}, + data: dialogChartDataUsage + } + ] + } + dialogChart.setOption(dialogChartOption) + }) + } + }) + }, + async makeDiskChart() { + const statistics_disk_chart = echarts.init(document.getElementById('statistics_disk'), 'light') + let data + await testApi.getDiskChartData({ diskType: this.diskRadio }).then((res) => { + data = res.data + }).catch(() => { + data = [ + { orgId: 0, orgName: 'ali-region22-1', disk: 130 }, + { orgId: 11, orgName: 'ali-region22-2', disk: 319 }, + { orgId: 106, orgName: 'huawei-region37-1', disk: 325 }, + { orgId: 107, orgName: 'unicom-region24-1', disk: 288 }, + { orgId: 108, orgName: 'volcano-region33-1', disk: 255 }, + { orgId: 109, orgName: 'huawei-region37-2', disk: 198 } + ] + }) + const maxDisk = Math.max.apply(Math, data.map(item => item.disk)) + data.sort((a, b) => b.disk - a.disk) + const statistics_disk_option = { + tooltip: { + trigger: 'axis', + formatter(params) { + return params[0].name + }, + axisPointer: { + type: 'shadow' + } + }, + xAxis: { + type: 'value', + show: false, + max: 'dataMax' + }, + yAxis: { + type: 'category', + data: data.map(s => s.orgName).slice(0, 5).reverse(), + axisLabel: { + color: 'rgb(255, 255, 255, 0.5)', + formatter(val) { + if (val.length < 5) { + return val + } else { + return val.substring(0, 4) + '..' + } + }, + show: true + }, + // 去掉刻度线 + axisLine: { 'show': false }, + axisTick: { 'show': false } + }, + series: [{ + // shadow + type: 'bar', + itemStyle: { + normal: { color: 'rgb(255, 255, 255, 0.1)' } + }, + data: [maxDisk, maxDisk, maxDisk, maxDisk, maxDisk], + barGap: '-100%', + barWidth: 10 + }, { + data: data.map(s => s.disk).slice(0, 5).reverse(), + type: 'bar', + barWidth: 10, + itemStyle: { + normal: { + label: { + show: true, // 开启显示 + position: 'right', + textStyle: { // 数值样式 + color: '#5FA4E9', + fontSize: 12 + } + }, + color: '#307CE0' + } + } + }], + grid: { + containLabel: true, + top: 10, + left: 10, + bottom: 10 + } + } + + statistics_disk_chart.setOption(statistics_disk_option) + statistics_disk_chart.getZr().on('click', async params => { + const pointInPixel = [params.offsetX, params.offsetY] + if (statistics_disk_chart.containPixel('grid', pointInPixel)) { + // 点击第几个柱子 + const pointInGrid = statistics_disk_chart.convertFromPixel({ seriesIndex: 0 }, pointInPixel) + const yIndex = pointInGrid[1] + data.sort((a, b) => b.disk - a.disk) + const orgName = data.map(s => s.orgName).slice(0, 5).reverse()[yIndex] + let dialogChartDataUsage = [] + /* for (let i = 0; i < this.dialogChartData.length; i++) { + if (this.dialogChartData[i].orgName === orgName) { + dialogChartDataUsage = this.dialogChartData[i].dataListDisk + } + }*/ + await testApi.getLineChartByOrgName({ orgName: orgName, type: 'disk' }).then((res) => { + dialogChartDataUsage = res.data + }).catch(() => { + dialogChartDataUsage = this.makeData() + }) + this.chartDialogVisible = true + this.$nextTick(() => { + const dialogChart = echarts.init(this.$refs.chartInDialog, 'dark') + const dialogChartOption = { + tooltip: { + trigger: 'axis', + position: function(pt) { + return [pt[0], '10%'] + } + }, + title: { + left: 'center', + text: '' + }, + backgroundColor: 'transparent', + toolbox: { + feature: { + dataZoom: { + yAxisIndex: 'none' + }, + restore: {}, + saveAsImage: {} + } + }, + xAxis: { + type: 'time', + boundaryGap: false + }, + yAxis: { + type: 'value', + boundaryGap: [0, '100%'] + }, + dataZoom: [ + { + type: 'inside', + start: 0, + end: 20 + }, + { + type: 'inside', + start: 0, + end: 20 + } + ], + series: [ + { + name: 'test data', + type: 'line', + smooth: true, + symbol: 'none', + areaStyle: {}, + data: dialogChartDataUsage + } + ] + } + dialogChart.setOption(dialogChartOption) + }) + } + }) + }, + async makeManufacturerChart() { + const manufacturer_chart = echarts.init(document.getElementById('manufacturer_chart'), 'light') + // 上云部门业务 + let data_manufacturer + await testApi.getManufacturerChartData().then((res) => { + data_manufacturer = res.data + }).catch(() => { + data_manufacturer = [ + { orgId: 0, orgName: 'aaa', num: 1033 }, + { orgId: 11, orgName: 'bbb', num: 2890 }, + { orgId: 106, orgName: 'ccc', num: 388 }, + { orgId: 107, orgName: 'ddd', num: 12 }, + { orgId: 108, orgName: 'eee', num: 38 }, + { orgId: 109, orgName: 'fff', num: 0 } + ] + }) + const maxNum = Math.max.apply(Math, data_manufacturer.map(item => item.num)) + data_manufacturer.sort((a, b) => b.num - a.num) + const manufacturer_option = { + tooltip: { + trigger: 'axis', + formatter(params) { + return params[0].name + }, + axisPointer: { + type: 'shadow' + } + }, + yAxis: { + type: 'value', + show: false, + max: 'dataMax' + }, + xAxis: { + type: 'category', + data: data_manufacturer.map(s => s.orgName).slice(0, 5), + axisLabel: { + color: 'rgb(255,255,255)', + formatter(val) { + if (val.length < 5) { + return val + } else { + return val.substring(0, 4) + '..' + } + } + }, + // 去掉刻度线 + axisLine: { 'show': false }, + axisTick: { 'show': false } + }, + series: [ + { + // shadow + type: 'bar', + itemStyle: { + normal: { color: 'rgb(255, 255, 255, 0.2)' } + }, + data: [maxNum, maxNum, maxNum, maxNum, maxNum], + barGap: '-100%', + barWidth: 12 + }, + { + data: data_manufacturer.map(s => s.num).slice(0, 5), + type: 'bar', + barWidth: 12, + itemStyle: { + normal: { + label: { + show: true, // 开启显示 + position: 'bottom', + offset: [0, -160], + textStyle: { // 数值样式 + color: '#ffffff', + fontSize: 12 + } + }, + color: '#042252' + } + } + }], + grid: { + containLabel: true, + left: -10, + right: 10, + bottom: 10 + } + } + manufacturer_chart.setOption(manufacturer_option) + }, + async makePieChart() { + // 主机数 + await testApi.getAbnormalDataList().then((res) => { + this.abnormalData = res.data + }).catch(() => { + this.abnormalData = [ + { name: '阿里云', + abnormalCpuNum: 2, + abnormalMemoryNum: 5, + abnormalDiskNum: 3, + abnormalCpuList: [ + { id: 0, message: '满足告警触发公式,当前采集值 15.52 %' }, + { id: 1, message: '满足告警触发公式,当前采集值 15.52 %' } + ], + abnormalMemoryList: [ + { id: 0, message: '满足告警触发公式,当前采集值 15.52 %' }, + { id: 1, message: '满足告警触发公式,当前采集值 15.52 %' }, + { id: 2, message: '满足告警触发公式,当前采集值 15.52 %' }, + { id: 3, message: '满足告警触发公式,当前采集值 15.52 %' }, + { id: 4, message: '满足告警触发公式,当前采集值 15.52 %' } + ], + abnormalDiskList: [ + { id: 0, message: '满足告警触发公式,当前采集值 15.52 %' }, + { id: 1, message: '满足告警触发公式,当前采集值 33.87 %' }, + { id: 2, message: '满足告警触发公式,当前采集值 10.45 %' } + ] + }, + { name: '华为云', + abnormalCpuNum: 3, + abnormalMemoryNum: 2, + abnormalDiskNum: 8, + abnormalCpuList: [ + { id: 0, message: '满足告警触发公式,当前采集值 15.52 %' }, + { id: 1, message: '满足告警触发公式,当前采集值 15.52 %' }, + { id: 2, message: '满足告警触发公式,当前采集值 15.52 %' } + ], + abnormalMemoryList: [ + { id: 0, message: '满足告警触发公式,当前采集值 15.52 %' }, + { id: 1, message: '满足告警触发公式,当前采集值 15.52 %' } + ], + abnormalDiskList: [ + { id: 0, message: '满足告警触发公式,当前采集值 15.52 %' }, + { id: 1, message: '满足告警触发公式,当前采集值 15.52 %' }, + { id: 2, message: '满足告警触发公式,当前采集值 15.52 %' }, + { id: 3, message: '满足告警触发公式,当前采集值 15.52 %' }, + { id: 4, message: '满足告警触发公式,当前采集值 15.52 %' }, + { id: 5, message: '满足告警触发公式,当前采集值 15.52 %' }, + { id: 6, message: '满足告警触发公式,当前采集值 15.52 %' }, + { id: 7, message: '满足告警触发公式,当前采集值 15.52 %' } + ] + }, + { name: '联通云', + abnormalCpuNum: 8, + abnormalMemoryNum: 1, + abnormalDiskNum: 4, + abnormalCpuList: [ + { id: 0, message: '满足告警触发公式,当前采集值 15.52 %' }, + { id: 1, message: '满足告警触发公式,当前采集值 15.52 %' }, + { id: 2, message: '满足告警触发公式,当前采集值 15.52 %' }, + { id: 3, message: '满足告警触发公式,当前采集值 15.52 %' }, + { id: 4, message: '满足告警触发公式,当前采集值 15.52 %' }, + { id: 5, message: '满足告警触发公式,当前采集值 15.52 %' }, + { id: 6, message: '满足告警触发公式,当前采集值 15.52 %' }, + { id: 7, message: '满足告警触发公式,当前采集值 15.52 %' } + ], + abnormalMemoryList: [ + { id: 0, message: '满足告警触发公式,当前采集值 15.52 %' } + ], + abnormalDiskList: [ + { id: 0, message: '满足告警触发公式,当前采集值 15.52 %' }, + { id: 1, message: '满足告警触发公式,当前采集值 15.52 %' }, + { id: 2, message: '满足告警触发公式,当前采集值 15.52 %' }, + { id: 3, message: '满足告警触发公式,当前采集值 15.52 %' } + ] + }, + { + name: '火山云', + abnormalCpuNum: 3, + abnormalMemoryNum: 1, + abnormalDiskNum: 3, + abnormalCpuList: [ + { id: 0, message: '满足告警触发公式,当前采集值 15.52 %' }, + { id: 1, message: '满足告警触发公式,当前采集值 15.52 %' }, + { id: 2, message: '满足告警触发公式,当前采集值 15.52 %' } + ], + abnormalMemoryList: [ + { id: 0, message: '满足告警触发公式,当前采集值 15.52 %' } + ], + abnormalDiskList: [ + { id: 0, message: '满足告警触发公式,当前采集值 15.52 %' }, + { id: 1, message: '满足告警触发公式,当前采集值 15.52 %' }, + { id: 2, message: '满足告警触发公式,当前采集值 15.52 %' } + ] + } + ] + }) + const pieChart = echarts.init(document.getElementById('abnormal_chart'), 'light') + const data = [] + switch (this.abnormalRadio) { + case 'CPU': + for (let i = 0; i < this.abnormalData.length; i++) { + data.push({ value: this.abnormalData[i].abnormalCpuNum, + name: this.abnormalData[i].name, + abnormalList: this.abnormalData[i].abnormalCpuList, + type: 'CPU' + }, + ) + } + break + case '内存': + for (let i = 0; i < this.abnormalData.length; i++) { + data.push({ value: this.abnormalData[i].abnormalMemoryNum, + name: this.abnormalData[i].name, + abnormalList: this.abnormalData[i].abnormalMemoryList, + type: '内存' + }) + } + break + case '磁盘': + for (let i = 0; i < this.abnormalData.length; i++) { + data.push({ value: this.abnormalData[i].abnormalDiskNum, + name: this.abnormalData[i].name, + abnormalList: this.abnormalData[i].abnormalDiskList, + type: '磁盘' }) + } + break + } + + // 3.配置 + const option = { + series: [ + { + type: 'pie', + data: data, + roseType: 'radius', // 指定为南格尔玫瑰图 + itemStyle: { + normal: { + labelLine: { + show: true, + length: 8 + } + } + }, + label: { + show: true, + formatter: function(arg) { + return arg.data.name + '\n' + '异常数:' + arg.data.value + '\n' + arg.percent + '%' + // return arg.data.name + } + } + } + ] + } + pieChart.setOption(option) + const self = this + + pieChart.on('click', function(param) { + // 关掉饼图上次点击的提示框 + // 添加点击事件 + console.log(param.data) + let tempData = '' + for (let i = 0; i < param.data.abnormalList.length; i++) { + tempData = tempData + 'id: ' + param.data.abnormalList[i].id + ',' + param.data.abnormalList[i].message + '\n' + } + self.pieChartAbnormalList = [] + self.pieChartAbnormalList = param.data.abnormalList + self.pieChartDialogTitle = '异常数据' + '-' + param.data.name + '-' + param.data.type + self.pieChartDialogVisible = true + + console.log(self.pieChartFlag) + }) + }, + + async makePerformenceChart() { + const test_chart = echarts.init(document.getElementById('test'), 'light') + let data + await testApi.getPerformanceChartData({ type: this.serviceRadio }).then((res) => { + data = res.data + }).catch(() => { + data = [ + { orgId: 0, orgName: 'ali-region22-1', num: 6 }, + { orgId: 11, orgName: 'ali-region22-2', num: 4 }, + { orgId: 106, orgName: 'huawei-region37-1', num: 12 }, + { orgId: 107, orgName: 'unicom-region24-1', num: 0 }, + { orgId: 108, orgName: 'volcano-region33-1', num: 45 }, + { orgId: 109, orgName: 'huawei-region37-2', num: 0 } + ] + }) + const maxNum = Math.max.apply(Math, data.map(item => item.num)) + data.sort((a, b) => b.num - a.num) + const test_option = { + tooltip: { + trigger: 'axis', + formatter(params) { + return params[0].name + }, + axisPointer: { + type: 'shadow' + } + }, + xAxis: { + type: 'value', + show: false, + max: 'dataMax' + }, + yAxis: { + type: 'category', + data: data.map(s => s.orgName).slice(0, 5).reverse(), + axisLabel: { + color: 'rgb(255, 255, 255, 0.5)', + formatter(val) { + if (val.length < 5) { + return val + } else { + return val.substring(0, 4) + '..' + } + }, + show: true + }, + // 去掉刻度线 + axisLine: { 'show': false }, + axisTick: { 'show': false } + }, + series: [{ + // shadow + type: 'bar', + itemStyle: { + normal: { color: 'rgb(255, 255, 255, 0.1)' } + }, + data: [maxNum, maxNum, maxNum, maxNum, maxNum], + barGap: '-100%', + barWidth: 10 + }, { + data: data.map(s => s.disk).slice(0, 5).reverse(), + type: 'bar', + barWidth: 10, + itemStyle: { + normal: { + label: { + show: true, // 开启显示 + position: 'right', + textStyle: { // 数值样式 + color: '#5FA4E9', + fontSize: 12 + } + }, + color: '#042252' + } + } + }], + grid: { + containLabel: true, + top: 10, + left: 10, + bottom: 10 + } + } + + test_chart.setOption(test_option) + test_chart.getZr().on('click', async params => { + const pointInPixel = [params.offsetX, params.offsetY] + if (test_chart.containPixel('grid', pointInPixel)) { + // 点击第几个柱子 + const pointInGrid = test_chart.convertFromPixel({ seriesIndex: 0 }, pointInPixel) + const yIndex = pointInGrid[1] + data.sort((a, b) => b.num - a.num) + const orgName = data.map(s => s.orgName).slice(0, 5).reverse()[yIndex] + let dialogChartDataUsage = [] + /* for (let i = 0; i < this.dialogChartData.length; i++) { + if (this.dialogChartData[i].orgName === orgName) { + dialogChartDataUsage = this.dialogChartData[i].dataListPerformance + } + }*/ + + await testApi.getLineChartByOrgName({ orgName: orgName, type: 'service' }).then((res) => { + dialogChartDataUsage = res.data + }).catch(() => { + dialogChartDataUsage = this.makeData() + }) + this.chartDialogVisible = true + this.$nextTick(() => { + const dialogChart = echarts.init(this.$refs.chartInDialog, 'dark') + const dialogChartOption = { + tooltip: { + trigger: 'axis', + position: function(pt) { + return [pt[0], '10%'] + } + }, + title: { + left: 'center', + text: '' + }, + backgroundColor: 'transparent', + toolbox: { + feature: { + dataZoom: { + yAxisIndex: 'none' + }, + restore: {}, + saveAsImage: {} + } + }, + xAxis: { + type: 'time', + boundaryGap: false + }, + yAxis: { + type: 'value', + boundaryGap: [0, '100%'] + }, + dataZoom: [ + { + type: 'inside', + start: 0, + end: 20 + }, + { + type: 'inside', + start: 0, + end: 20 + } + ], + series: [ + { + name: 'test data', + type: 'line', + smooth: true, + symbol: 'none', + areaStyle: {}, + data: dialogChartDataUsage + } + ] + } + dialogChart.setOption(dialogChartOption) + }) + } + }) + }, + + openTableDetailDialog(row) { + this.tableDetailMessage = row + this.tableDetailMessage.messageName = row.message.slice(10, 21) + this.tableDetailDialogVisible = true + }, + async getAlarmTableData() { + await testApi.getAlarmTableData({ type: this.alarmRadio }).then((res) => { + this.alarmTableDataShow = res.data + }).catch(() => { + this.alarmTableDataShow = [] + this.alarmTableData = [ + { + 'id': '5f3cc9c46a514a9a8ffbd8e7ac9515b0', + 'severity': 'Major', + 'firstTime': '2023-06-13 15:06:35', + 'lastTime': null, + 'message': '满足告警触发公式:{10.4.78.100:zabbix[process,discoverer,avg,busy].avg(10m)}>75,当前采集值 100 %.', + 'source': null, + 'host': '127.0.0.1', + 'type': null, + 'tags': {}, + 'rootCause': null, + 'mergedBy': null, + 'count': 2, + 'manager': null, + 'alertId': 'bb35ab64fa01334a06a13135b157f05b', + 'systemId': 3, + 'sourceName': null, + 'month': 6, + 'affirmedTime': '2023-06-13 15:50:35', + 'affirmedContent': '不满足告警触发公式:{10.4.78.100:zabbix[process,discoverer,avg,busy].avg(10m)}>75,当前采集值 0 %.', + 'affirmedUser': 'system', + 'ip': null + }, + { + 'id': '73717899659d43378714dfd3145da982', + 'severity': 'Major', + 'firstTime': '2023-06-13 13:33:35', + 'lastTime': null, + 'message': '满足告警触发公式:{10.4.78.100:zabbix[process,discoverer,avg,busy].avg(10m)}>75,当前采集值 100 %.', + 'source': null, + 'host': '127.0.0.1', + 'type': null, + 'tags': {}, + 'rootCause': null, + 'mergedBy': null, + 'count': 2, + 'manager': null, + 'alertId': '4851c52235c508370e27e1e955c0e6e7', + 'systemId': 3, + 'sourceName': null, + 'month': 6, + 'affirmedTime': '2023-06-13 14:16:35', + 'affirmedContent': '不满足告警触发公式:{10.4.78.100:zabbix[process,discoverer,avg,busy].avg(10m)}>75,当前采集值 0 %.', + 'affirmedUser': 'system', + 'ip': null + }, + { + 'id': '355baba65147431fa4f3b10a9b446c03', + 'severity': 'Minor', + 'firstTime': '2023-06-13 12:40:35', + 'lastTime': null, + 'message': '满足告警触发公式:{10.4.78.100:zabbix[process,discoverer,avg,busy].avg(10m)}>75,当前采集值 100 %.', + 'source': null, + 'host': '127.0.0.1', + 'type': null, + 'tags': {}, + 'rootCause': null, + 'mergedBy': null, + 'count': 2, + 'manager': null, + 'alertId': '338e6d9793fab129f509354515f9e425', + 'systemId': 3, + 'sourceName': null, + 'month': 6, + 'affirmedTime': '2023-06-13 12:49:35', + 'affirmedContent': '不满足告警触发公式:{10.4.78.100:zabbix[process,discoverer,avg,busy].avg(10m)}>75,当前采集值 0 %.', + 'affirmedUser': 'system', + 'ip': null + }, + { + 'id': '209597f2c93a4f27858ad2db4dcd284a', + 'severity': 'Minor', + 'firstTime': '2023-06-13 12:00:35', + 'lastTime': null, + 'message': '满足告警触发公式:{10.4.78.100:zabbix[process,discoverer,avg,busy].avg(10m)}>75,当前采集值 100 %.', + 'source': null, + 'host': '127.0.0.1', + 'type': null, + 'tags': {}, + 'rootCause': null, + 'mergedBy': null, + 'count': 2, + 'manager': null, + 'alertId': 'd82cbb6b4b0a38df769f7b752f831dbf', + 'systemId': 3, + 'sourceName': null, + 'month': 6, + 'affirmedTime': '2023-06-13 12:29:35', + 'affirmedContent': '不满足告警触发公式:{10.4.78.100:zabbix[process,discoverer,avg,busy].avg(10m)}>75,当前采集值 0.02 %.', + 'affirmedUser': 'system', + 'ip': null + }, + { + 'id': '5be8447db2c246c3b6b4230d2a5a3da6', + 'severity': 'Warning', + 'firstTime': '2023-06-13 11:26:35', + 'lastTime': null, + 'message': '满足告警触发公式:{10.4.78.100:zabbix[process,discoverer,avg,busy].avg(10m)}>75,当前采集值 100 %.', + 'source': null, + 'host': '127.0.0.1', + 'type': null, + 'tags': {}, + 'rootCause': null, + 'mergedBy': null, + 'count': 2, + 'manager': null, + 'alertId': '8e26bf7ddf68639778f64972c8ba03e9', + 'systemId': 3, + 'sourceName': null, + 'month': 6, + 'affirmedTime': '2023-06-13 11:36:35', + 'affirmedContent': '不满足告警触发公式:{10.4.78.100:zabbix[process,discoverer,avg,busy].avg(10m)}>75,当前采集值 0 %.', + 'affirmedUser': 'system', + 'ip': null + } + ] + switch (this.alarmRadio) { + case '全部': + this.alarmTableDataShow = this.alarmTableData + break + case '严重': + for (let i = 0; i < this.alarmTableData.length; i++) { + if (this.alarmTableData[i].severity === 'Major') { + this.alarmTableDataShow.push(this.alarmTableData[i]) + } + } + break + case '中等': + for (let i = 0; i < this.alarmTableData.length; i++) { + if (this.alarmTableData[i].severity === 'Minor') { + this.alarmTableDataShow.push(this.alarmTableData[i]) + } + } + break + case '一般': + for (let i = 0; i < this.alarmTableData.length; i++) { + if (this.alarmTableData[i].severity === 'Warning') { + this.alarmTableDataShow.push(this.alarmTableData[i]) + } + } + break + } + }) + }, + getSeverityText(severity) { + switch (severity) { + case 'Major': + return '严重' + case 'Minor': + return '中等' + case 'Warning': + return '一般' + } + }, + async getBaseDatas() { + // 主机数 + await testApi.getHostNums().then((res) => { + this.hostNum = res.data.hostNum + this.normalHostNum = res.data.normalHostNum + this.abnormalHostNum = res.data.abnormalHostNum + }).catch(() => { + this.hostNum = 30 + this.normalHostNum = 25 + this.abnormalHostNum = 5 + }) + // 大屏资源概览8项 + await testApi.getGailanInfo().then((res) => { + this.gailan_box = res.data + }).catch(() => { + this.gailan_box = [ + { label: ['云主机概览'], value: '26', total: '30', icon: '/static/hostPerformance/云平台.png' }, + { label: ['CPU概览'], value: '23', total: '30', icon: '/static/hostPerformance/VCPU.png' }, + { label: ['内存概览'], value: '28', total: '30', icon: '/static/hostPerformance/内存.png' }, + { label: ['磁盘概览'], value: '133', total: '150', icon: '/static/hostPerformance/存储.png' }, + { label: ['阿里云'], value: '7', total: '30', icon: '/static/hostPerformance/阿里云.png' }, + { label: ['华为云'], value: '8', total: '30', icon: '/static/hostPerformance/华为云.png' }, + { label: ['联通云'], value: '9', total: '30', icon: '/static/hostPerformance/联通云.png' }, + { label: ['火山云'], value: '6', total: '30', icon: '/static/hostPerformance/火山云.png' } + ] + }) + }, + async makeUsageCharts() { + await this.makeManufacturerChart() + await this.makePieChart() + await this.makeCpuChart() + await this.makeDiskChart() + await this.makeMemoryChart() + await this.makePerformenceChart() + } + } +} diff --git a/task-resource-system/views/illegalPublicity/css/illegalPublicity.scss b/task-resource-system/views/illegalPublicity/css/illegalPublicity.scss new file mode 100644 index 0000000..a2b486b --- /dev/null +++ b/task-resource-system/views/illegalPublicity/css/illegalPublicity.scss @@ -0,0 +1,118 @@ +#illegalPublicity{ + .title_class { + font-family: 'Alternate'; + color: #FFFFFF; + letter-spacing: 0; + text-shadow: 0 0 6px #26A2FF; + font-size: 12pt; + } + + .hr { + border-bottom: 1px solid; + opacity: 0.2; + color: white; + } + + .hr_tag { + margin-top: 17px; + border-bottom: 2px solid; + color: #FFFFFF; + width: 14px; + } + + .hr_left { + margin-left: 50px; + margin-right: 50px; + } + + .el-card { + border-radius: 4px; + border: 1px solid #ffffff; + background-color: rgba(255, 255, 255, 0); + } + .el-card__header { + padding: 3px 10px; + border-bottom: 1px solid #EBEEF5; + -webkit-box-sizing: border-box; + box-sizing: border-box; + height: 4vh; + } + .el-card__body, .el-main { + padding: 5px; + } + .el-card__body{ + padding: 5px; + height: 20vh; + } + .chart_card1{ + height:51vh; + padding-bottom:80px; + color: #FFFFFF; + flex-wrap: wrap; + margin-top: 1vh; + } + .chart_card2{ + height:25vh; + padding-bottom:80px; + color: #FFFFFF; + flex-wrap: wrap; + margin-top: 1vh; + } + + .cardDiv{ + margin: 10px 50px 20px 50px; + } + + .table_box { + //表格间距 + margin: 20px 50px 20px 50px; + .el-table--mini .el-table__cell { + } + + //整个表格背景颜色及边框弧度 + .el-table { + background-color: rgba(38, 38, 38, 0); + border-radius: 3px; + border-color:white ; + } + + //字体颜色 + .el-table tr { + background-color: transparent; + color: white; + height:3vh + } + + .el-table th.el-table__cell.is-leaf { + border-bottom: 1px solid rgba(255, 255, 255, 0.8); + } + + + //标题背景颜色 + .el-table th { + background-color: transparent !important; + } + + //标题字体颜色 + .el-table th .cell { + color: #a8a7a7; + font-size: 11pt; + height:19px + } + //去除行下划线 + .el-table td { + border-color: rgba(255, 255, 255, 0.2); + font-size: 11pt; + } + + //修改鼠标滑过背景颜色 + .el-table--enable-row-hover .el-table__body tr:hover td { + background-color: rgba(38, 38, 38, 0.5) !important; + color: white !important; + } + } +} + + + + diff --git a/task-resource-system/views/illegalPublicity/index.vue b/task-resource-system/views/illegalPublicity/index.vue new file mode 100644 index 0000000..1f22419 --- /dev/null +++ b/task-resource-system/views/illegalPublicity/index.vue @@ -0,0 +1,107 @@ + + + + diff --git a/task-resource-system/views/illegalPublicity/js/index.js b/task-resource-system/views/illegalPublicity/js/index.js new file mode 100644 index 0000000..0d84a3a --- /dev/null +++ b/task-resource-system/views/illegalPublicity/js/index.js @@ -0,0 +1,440 @@ +import echarts from 'echarts' +import testApi from '@/api/testApi' + +export default { + name: 'illegalPublicity', + data() { + return { + illegalInfoList: [], + chartsDatas: [] + } + }, + created() { + }, + + async mounted() { + await this.getList() + this.makeCharts() + }, + methods: { + async getList() { + await testApi.getIllegalInfoList().then((res) => { + this.illegalInfoList = res.data + }).catch((error) => { + this.illegalInfoList = [ + { + id: '0', + supplierName: '阿里云', + credenceValue: '90', + punishmentLevel: '1', + time: '2023-07-02', + remark: '' + }, + { + id: '1', + supplierName: '联通云', + credenceValue: '92', + punishmentLevel: '2', + time: '2023-07-02', + remark: '' + }, + { + id: '2', + supplierName: '华为云', + credenceValue: '95', + punishmentLevel: '1', + time: '2023-07-02', + remark: '' + }, + { + id: '3', + supplierName: '火山云', + credenceValue: '90', + punishmentLevel: '3', + time: '2023-07-02', + remark: '' + }, + { + id: '4', + supplierName: '阿里云', + credenceValue: '91', + punishmentLevel: '4', + time: '2023-07-02', + remark: '' + }, + { + id: '5', + supplierName: '华为云', + credenceValue: '99', + punishmentLevel: '2', + time: '2023-07-02', + remark: '' + }, + { + id: '6', + supplierName: '火山云', + credenceValue: '90', + punishmentLevel: '3', + time: '2023-07-02', + remark: '' + } + ] + }) + + await testApi.getIllegalPublicityChartData().then((res ) => { + this.chartsDatas = res.data + }).catch((error) => { + this.chartsDatas = [ + { id: 0, supplierName: '阿里云', punishmentNum: 55, level1Num: 22, level2Num: 13, level3Num: 18, level4Num: 2 }, + { id: 1, supplierName: '联通云', punishmentNum: 61, level1Num: 12, level2Num: 19, level3Num: 22, level4Num: 8 }, + { id: 2, supplierName: '华为云', punishmentNum: 71, level1Num: 13, level2Num: 17, level3Num: 33, level4Num: 5 }, + { id: 3, supplierName: '火山云', punishmentNum: 68, level1Num: 28, level2Num: 13, level3Num: 18, level4Num: 9 } + ] + }) + }, + makeCharts() { + this.makePieChart() + this.makeBarCharts() + }, + fullScreen() { + const element = document.getElementById('entirety') + if (element.requestFullscreen) { + element.requestFullscreen() + } else if (element.mozRequestFullScreen) { + element.mozRequestFullScreen() + } else if (element.webkitRequestFullscreen) { + element.webkitRequestFullscreen() + } else if (element.msRequestFullscreen) { + element.msRequestFullscreen() + } + }, + getLevelInfo(level) { + switch (level) { + case '1': + return { + levelLabel: '一级', + measureLabel: '警告', + type: '' + } + case '2': + return { + levelLabel: '二级', + measureLabel: '罚款', + type: 'success' + } + case '3': + return { + levelLabel: '三级', + measureLabel: '暂停服务', + type: 'warning' + } + case '4': + return { + levelLabel: '四级', + measureLabel: '重新审查', + type: 'danger' + } + } + }, + makePieChart() { + const pieChart = echarts.init(document.getElementById('pieChart'), 'light') + const data = [] + for (let i = 0; i < this.chartsDatas.length; i++) { + data.push({ + value: this.chartsDatas[i].punishmentNum, + name: this.chartsDatas[i].supplierName + }) + } + const option = { + tooltip: { + trigger: 'item', + formatter: '{b} : {c} ({d}%)' + }, + legend: { + orient: 'horizontal', + left: 'left', + textStyle: { + color: '#FFFFFF' + } + }, + series: [ + { + name: '', + type: 'pie', + radius: '50%', + data: data, + emphasis: { + itemStyle: { + shadowBlur: 10, + shadowOffsetX: 0, + shadowColor: 'rgba(0, 0, 0, 0.5)' + } + }, + label: { + formatter: '{b|{b}:}\n{c} {per|{d}%} ', + rich: { + b: { + fontSize: 14, + fontWeight: 'bold', + lineHeight: 33 + }, + per: { + color: '#fff', + backgroundColor: '#4C5058', + padding: [3, 4], + borderRadius: 4 + } + } + } + } + ] + } + pieChart.setOption(option) + }, + makeBarCharts() { + const level1Chart = echarts.init(document.getElementById('level1Chart'), 'light') + const level2Chart = echarts.init(document.getElementById('level2Chart'), 'light') + const level3Chart = echarts.init(document.getElementById('level3Chart'), 'light') + const level4Chart = echarts.init(document.getElementById('level4Chart'), 'light') + + const data = this.chartsDatas + const level1Option = { + tooltip: { + trigger: 'axis', + formatter(params) { + return params[0].name + }, + axisPointer: { + type: 'shadow' + } + }, + xAxis: { + type: 'value', + show: false, + max: 'dataMax' + }, + yAxis: { + type: 'category', + data: data.map(s => s.supplierName).reverse(), + axisLabel: { + color: 'rgb(255, 255, 255, 0.5)', + formatter(val) { + if (val.length < 5) { + return val + } else { + return val.substring(0, 4) + '..' + } + }, + show: true + }, + // 去掉刻度线 + axisLine: { 'show': false }, + axisTick: { 'show': false } + }, + series: [{ + data: data.map(s => s.level1Num).reverse(), + type: 'bar', + barWidth: 10, + itemStyle: { + normal: { + label: { + show: true, // 开启显示 + position: 'right', + textStyle: { // 数值样式 + color: '#5FA4E9', + fontSize: 12 + } + }, + color: '#307CE0' + } + } + }], + grid: { + containLabel: true, + top: 5, + left: 10, + bottom: 10 + } + } + const level2Option = { + tooltip: { + trigger: 'axis', + formatter(params) { + return params[0].name + }, + axisPointer: { + type: 'shadow' + } + }, + xAxis: { + type: 'value', + show: false, + max: 'dataMax' + }, + yAxis: { + type: 'category', + data: data.map(s => s.supplierName).reverse(), + axisLabel: { + color: 'rgb(255, 255, 255, 0.5)', + formatter(val) { + if (val.length < 5) { + return val + } else { + return val.substring(0, 4) + '..' + } + }, + show: true + }, + // 去掉刻度线 + axisLine: { 'show': false }, + axisTick: { 'show': false } + }, + series: [{ + data: data.map(s => s.level2Num).reverse(), + type: 'bar', + barWidth: 10, + itemStyle: { + normal: { + label: { + show: true, // 开启显示 + position: 'right', + textStyle: { // 数值样式 + color: '#5FA4E9', + fontSize: 12 + } + }, + color: '#307CE0' + } + } + }], + grid: { + containLabel: true, + top: 5, + left: 10, + bottom: 10 + } + } + const level3Option = { + tooltip: { + trigger: 'axis', + formatter(params) { + return params[0].name + }, + axisPointer: { + type: 'shadow' + } + }, + xAxis: { + type: 'value', + show: false, + max: 'dataMax' + }, + yAxis: { + type: 'category', + data: data.map(s => s.supplierName).reverse(), + axisLabel: { + color: 'rgb(255, 255, 255, 0.5)', + formatter(val) { + if (val.length < 5) { + return val + } else { + return val.substring(0, 4) + '..' + } + }, + show: true + }, + // 去掉刻度线 + axisLine: { 'show': false }, + axisTick: { 'show': false } + }, + series: [{ + data: data.map(s => s.level3Num).reverse(), + type: 'bar', + barWidth: 10, + itemStyle: { + normal: { + label: { + show: true, // 开启显示 + position: 'right', + textStyle: { // 数值样式 + color: '#5FA4E9', + fontSize: 12 + } + }, + color: '#307CE0' + } + } + }], + grid: { + containLabel: true, + top: 5, + left: 10, + bottom: 10 + } + } + const level4Option = { + tooltip: { + trigger: 'axis', + formatter(params) { + return params[0].name + }, + axisPointer: { + type: 'shadow' + } + }, + xAxis: { + type: 'value', + show: false, + max: 'dataMax' + }, + yAxis: { + type: 'category', + data: data.map(s => s.supplierName).reverse(), + axisLabel: { + color: 'rgb(255, 255, 255, 0.5)', + formatter(val) { + if (val.length < 5) { + return val + } else { + return val.substring(0, 4) + '..' + } + }, + show: true + }, + // 去掉刻度线 + axisLine: { 'show': false }, + axisTick: { 'show': false } + }, + series: [{ + data: data.map(s => s.level4Num).reverse(), + type: 'bar', + barWidth: 10, + itemStyle: { + normal: { + label: { + show: true, // 开启显示 + position: 'right', + textStyle: { // 数值样式 + color: '#5FA4E9', + fontSize: 12 + } + }, + color: '#307CE0' + } + } + }], + grid: { + containLabel: true, + top: 5, + left: 10, + bottom: 10 + } + } + level1Chart.setOption(level1Option) + level2Chart.setOption(level2Option) + level3Chart.setOption(level3Option) + level4Chart.setOption(level4Option) + } + + } +} diff --git a/task-resource-system/views/storageTransfer/css/storageTransfer.scss b/task-resource-system/views/storageTransfer/css/storageTransfer.scss new file mode 100644 index 0000000..2832af9 --- /dev/null +++ b/task-resource-system/views/storageTransfer/css/storageTransfer.scss @@ -0,0 +1,108 @@ +#storageTransfer{ + .title_class { + font-family: 'Alternate'; + color: #FFFFFF; + letter-spacing: 0; + text-shadow: 0 0 6px #26A2FF; + font-size: 12pt; + } + + + .el-card { + border-radius: 4px; + border: 1px solid #ffffff; + background-color: rgba(255, 255, 255, 0); + } + .el-card__header { + padding: 3px 10px; + border-bottom: 1px solid #EBEEF5; + -webkit-box-sizing: border-box; + box-sizing: border-box; + height: 4vh; + } + .el-card__body, .el-main { + padding: 5px; + } + .el-card__body{ + padding: 5px; + height: 20vh; + } + .chart_card{ + height:43vh; + padding-bottom:80px; + color: #FFFFFF; + flex-wrap: wrap; + margin-top: 1vh; + } + + .cardDiv{ + margin: 10px 50px 20px 50px; + } + .el-input__inner { + background-color: transparent !important; + color:#FFFFFF; + } + .description1 { + .el-descriptions { + background: rgba(255, 255, 255, 0); + } + + .el-descriptions__body { + color: #ffffff; + background-color: rgba(255, 255, 255, 0); + } + + .el-descriptions__title { + color: white; + } + + .el-descriptions-item el-descriptions-item__cell { + height: 20px; + } + + .el-descriptions-item__content { + word-break: break-word; + overflow-wrap: break-word; + font-size: 13pt; + color: #FFFFFF; + letter-spacing: 0; + text-shadow: 0 0 6px #26A2FF; + } + } +.description2 { + padding-left:20px; + .el-descriptions { + background: rgba(255, 255, 255, 0); + } + + .el-descriptions__body { + color: #ffffff; + background-color: rgba(255, 255, 255, 0); + } + + .el-descriptions__title { + color: white; + } + + .el-descriptions-item el-descriptions-item__cell { + height: 10px; + } + + .el-descriptions-item__content { + word-break: break-word; + overflow-wrap: break-word; + font-size: 10pt; + color: rgba(243, 232, 232, 0.6); + } + .el-descriptions-item__label { + word-break: break-word; + overflow-wrap: break-word; + font-size: 10pt; + color: rgba(243, 232, 232, 0.6); + } +} +} + + + + diff --git a/task-resource-system/views/storageTransfer/index.vue b/task-resource-system/views/storageTransfer/index.vue new file mode 100644 index 0000000..c6df381 --- /dev/null +++ b/task-resource-system/views/storageTransfer/index.vue @@ -0,0 +1,170 @@ + + + + diff --git a/task-resource-system/views/storageTransfer/js/index.js b/task-resource-system/views/storageTransfer/js/index.js new file mode 100644 index 0000000..c7c0db2 --- /dev/null +++ b/task-resource-system/views/storageTransfer/js/index.js @@ -0,0 +1,314 @@ +import echarts from 'echarts' +import testApi from '@/api/testApi' + +export default { + name: 'storageTransfer', + data() { + return { + searchId: '0', + cpuChartData: [], + memoryChartData: [], + diskChartData: [], + networkChartData: [], + hostInfoList: [], + hostInfoShow: {} + } + }, + created() { + }, + + async mounted() { + await this.searchHost(this.searchId) + }, + methods: { + async searchHost() { + await testApi.searchHostInfoById({ id: this.searchId }).then((res) => { + if (res.data !== null) { + this.hostInfoShow = res.data + this.makeCharts() + } else { + this.$message({ + message: '请输入正确的主机ID', + type: 'warning' + }) + } + }).catch((error) => { + this.hostInfoList = [{ id: '0', cpu: { useRatio: '80%', speed: '3.89GHZ', process: '210', thread: '3008', handle: '115863', operationTime: '23:00:56:37', + referenceSpeed: '3.00 GHz', slot: 1, kernel: 6, logicProcessor: 6, virtualization: '已启用', l1: '384KB', l2: '1.5MB', l3: '9.0MB' }, + memory: { using: '10.2GB', usable: '5.2GB', submitted: '20.4/30.7GB', cache: '4.1GB', pagingBuffer: '1.0GB', nonPagingBuffer: '977MB', + speed: '2666 MHz', usedSlot: '2/4', shape: 'DIMM', reservedMemory: '165MB' }, + disk: { activeTime: '13%', argResponseTime: '1.4ms', readSpeed: '0KB/s', writeSpeed: '2.8MB/s', capacity: '932GB', formatted: '931GB', systemDisk: '是', pageFile: '是', type: 'HDD' }, + network: { send: '16Kbps', receive: '24Kbps', adapter: '以太网', connectType: '以太网', IPv4: '11.4.11.11', IPv6: 'fe80::f9b6:a4b4:3116:592b%8' }}, + { id: '1', cpu: { useRatio: '84%', speed: '3.89GHZ', process: '210', thread: '3008', handle: '115863', operationTime: '23:00:56:37', + referenceSpeed: '3.00 GHz', slot: 1, kernel: 6, logicProcessor: 6, virtualization: '已启用', l1: '384KB', l2: '1.5MB', l3: '9.0MB' }, + memory: { using: '10.2GB', usable: '5.2GB', submitted: '20.4/30.7GB', cache: '4.1GB', pagingBuffer: '1.0GB', nonPagingBuffer: '977MB', + speed: '2666 MHz', usedSlot: '2/4', shape: 'DIMM', reservedMemory: '165MB' }, + disk: { activeTime: '13%', argResponseTime: '1.4ms', readSpeed: '0KB/s', writeSpeed: '2.8MB/s', capacity: '932GB', formatted: '931GB', systemDisk: '是', pageFile: '是', type: 'HDD' }, + network: { send: '16Kbps', receive: '24Kbps', adapter: '以太网', connectType: '以太网', IPv4: '11.4.11.11', IPv6: 'fe80::f9b6:a4b4:3116:592b%8' }}, + { id: '2', cpu: { useRatio: '70%', speed: '3.89GHZ', process: '210', thread: '3008', handle: '115863', operationTime: '23:00:56:37', + referenceSpeed: '3.00 GHz', slot: 1, kernel: 6, logicProcessor: 6, virtualization: '已启用', l1: '384KB', l2: '1.5MB', l3: '9.0MB' }, + memory: { using: '10.2GB', usable: '5.2GB', submitted: '20.4/30.7GB', cache: '4.1GB', pagingBuffer: '1.0GB', nonPagingBuffer: '977MB', + speed: '2666 MHz', usedSlot: '2/4', shape: 'DIMM', reservedMemory: '165MB' }, + disk: { activeTime: '13%', argResponseTime: '1.4ms', readSpeed: '0KB/s', writeSpeed: '2.8MB/s', capacity: '932GB', formatted: '931GB', systemDisk: '是', pageFile: '是', type: 'HDD' }, + network: { send: '16Kbps', receive: '24Kbps', adapter: '以太网', connectType: '以太网', IPv4: '11.4.11.11', IPv6: 'fe80::f9b6:a4b4:3116:592b%8' }}, + { id: '3', cpu: { useRatio: '90%', speed: '3.89GHZ', process: '210', thread: '3008', handle: '115863', operationTime: '23:00:56:37', + referenceSpeed: '3.00 GHz', slot: 1, kernel: 6, logicProcessor: 6, virtualization: '已启用', l1: '384KB', l2: '1.5MB', l3: '9.0MB' }, + memory: { using: '10.2GB', usable: '5.2GB', submitted: '20.4/30.7GB', cache: '4.1GB', pagingBuffer: '1.0GB', nonPagingBuffer: '977MB', + speed: '2666 MHz', usedSlot: '2/4', shape: 'DIMM', reservedMemory: '165MB' }, + disk: { activeTime: '13%', argResponseTime: '1.4ms', readSpeed: '0KB/s', writeSpeed: '2.8MB/s', capacity: '932GB', formatted: '931GB', systemDisk: '是', pageFile: '是', type: 'HDD' }, + network: { send: '16Kbps', receive: '24Kbps', adapter: '以太网', connectType: '以太网', IPv4: '11.4.11.11', IPv6: 'fe80::f9b6:a4b4:3116:592b%8' }}] + + this.hostInfoShow = null + for (let i = 0; i < this.hostInfoList.length; i++) { + if (this.hostInfoList[i].id === this.searchId) { + this.hostInfoShow = this.hostInfoList[i] + } + } + if (!this.hostInfoShow) { + this.$message({ + message: '请输入正确的主机ID', + type: 'warning' + }) + } else { + this.makeCharts() + } + }) + }, + makeData() { + const base = +new Date() + const data = [] + for (let i = -60; i < 0; i++) { + const now = new Date((base + i * 1000)) + data.push([+now, Math.abs(Math.random()).toFixed(2)]) + } + return data + }, + async getChartsData() { + await testApi.getHostChartsById({ id: this.searchId }).then((res) => { + this.cpuChartData = res.data.cpuChartData + this.memoryChartData = res.data.memoryChartData + this.diskChartData = res.data.diskChartData + this.networkChartData = res.data.networkChartData + }).catch((error) => { + this.cpuChartData = this.makeData() + this.memoryChartData = this.makeData() + this.diskChartData = this.makeData() + this.networkChartData = this.makeData() + }) + }, + makeCharts() { + this.getChartsData().then(r => { + const cpuChart = echarts.init(document.getElementById('CPUChart'), 'dark') + const memoryChart = echarts.init(document.getElementById('memoryChart'), 'dark') + const diskChart = echarts.init(document.getElementById('diskChart'), 'dark') + const networkChart = echarts.init(document.getElementById('networkChart'), 'dark') + + const cpuChartOption = { + tooltip: { + trigger: 'axis' + }, + title: { + left: 'center', + text: '' + }, + backgroundColor: 'transparent', + toolbox: { + }, + xAxis: { + type: 'time', + boundaryGap: false + }, + yAxis: { + max: 1, + type: 'value', + boundaryGap: [0, '100%'] + }, + dataZoom: [ + { + type: 'inside', + start: 0, + end: 100 + }, + { + type: 'inside', + start: 0, + end: 100 + } + ], + series: [ + { + name: '', + type: 'line', + smooth: true, + symbol: 'none', + areaStyle: {}, + data: this.cpuChartData, + itemStyle: { + color: '#6495ED' + } + } + ] + } + + const memoryChartOption = { + tooltip: { + trigger: 'axis' + }, + title: { + left: 'center', + text: '' + }, + backgroundColor: 'transparent', + toolbox: { + }, + xAxis: { + type: 'time', + boundaryGap: false + }, + yAxis: { + max: 1, + type: 'value', + boundaryGap: [0, '100%'] + }, + dataZoom: [ + { + type: 'inside', + start: 0, + end: 100 + }, + { + type: 'inside', + start: 0, + end: 100 + } + ], + series: [ + { + name: '', + type: 'line', + smooth: true, + symbol: 'none', + areaStyle: {}, + data: this.memoryChartData, + itemStyle: { + color: '#6495ED' + } + } + ] + } + + const diskChartOption = { + tooltip: { + trigger: 'axis' + }, + title: { + left: 'center', + text: '' + }, + backgroundColor: 'transparent', + toolbox: { + }, + xAxis: { + type: 'time', + boundaryGap: false + }, + yAxis: { + max: 1, + type: 'value', + boundaryGap: [0, '100%'] + }, + dataZoom: [ + { + type: 'inside', + start: 0, + end: 100 + }, + { + type: 'inside', + start: 0, + end: 100 + } + ], + series: [ + { + name: '', + type: 'line', + smooth: true, + symbol: 'none', + areaStyle: {}, + data: this.diskChartData, + itemStyle: { + color: '#6495ED' + } + } + ] + } + + const networkChartOption = { + tooltip: { + trigger: 'axis' + }, + title: { + left: 'center', + text: '' + }, + backgroundColor: 'transparent', + toolbox: { + }, + xAxis: { + type: 'time', + boundaryGap: false + }, + yAxis: { + max: 1, + type: 'value', + boundaryGap: [0, '100%'] + }, + dataZoom: [ + { + type: 'inside', + start: 0, + end: 100 + }, + { + type: 'inside', + start: 0, + end: 100 + } + ], + series: [ + { + name: '', + type: 'line', + smooth: true, + symbol: 'none', + areaStyle: {}, + data: this.networkChartData, + itemStyle: { + color: '#6495ED' + } + } + ] + } + + cpuChart.setOption(cpuChartOption) + memoryChart.setOption(memoryChartOption) + diskChart.setOption(diskChartOption) + networkChart.setOption(networkChartOption) + }) + }, + fullScreen() { + const element = document.getElementById('entirety') + if (element.requestFullscreen) { + element.requestFullscreen() + } else if (element.mozRequestFullScreen) { + element.mozRequestFullScreen() + } else if (element.webkitRequestFullscreen) { + element.webkitRequestFullscreen() + } else if (element.msRequestFullscreen) { + element.msRequestFullscreen() + } + } + + } +} diff --git a/task-resource-system/views/storageTransfer/js/indexold.js b/task-resource-system/views/storageTransfer/js/indexold.js new file mode 100644 index 0000000..37d8302 --- /dev/null +++ b/task-resource-system/views/storageTransfer/js/indexold.js @@ -0,0 +1,308 @@ +import echarts from 'echarts' + +export default { + name: 'storageTransfer', + data() { + return { + searchId: '', + cpuChartData: [], + memoryChartData: [], + diskChartData: [], + networkChartData: [], + hostInfoList: [], + hostInfoShow: { id: 0, cpu: { useRatio: '80%', speed: '3.89GHZ', process: '210', thread: '3008', handle: '115863', operationTime: '23:00:56:37', + referenceSpeed: '3.00 GHz', slot: 1, kernel: 6, logicProcessor: 6, virtualization: '已启用', l1: '384KB', l2: '1.5MB', l3: '9.0MB' }, + memory: { using: '10.2GB', usable: '5.2GB', submitted: '20.4/30.7GB', cache: '4.1GB', pagingBuffer: '1.0GB', nonPagingBuffer: '977MB', + speed: '2666 MHz', usedSlot: '2/4', shape: 'DIMM', reservedMemory: '165MB' }, + disk: { activeTime: '13%', argResponseTime: '1.4ms', readSpeed: '0KB/s', writeSpeed: '2.8MB/s', capacity: '932GB', formatted: '931GB', systemDisk: '是', pageFile: '是', type: 'HDD' }, + network: { send: '16Kbps', receive: '24Kbps', adapter: '以太网', connectType: '以太网', IPv4: '11.4.11.11', IPv6: 'fe80::f9b6:a4b4:3116:592b%8' }} + } + }, + created() { + }, + + async mounted() { + this.makeCharts() + this.getList() + }, + methods: { + getList() { + this.hostInfoList = [{ id: '0', cpu: { useRatio: '80%', speed: '3.89GHZ', process: '210', thread: '3008', handle: '115863', operationTime: '23:00:56:37', + referenceSpeed: '3.00 GHz', slot: 1, kernel: 6, logicProcessor: 6, virtualization: '已启用', l1: '384KB', l2: '1.5MB', l3: '9.0MB' }, + memory: { using: '10.2GB', usable: '5.2GB', submitted: '20.4/30.7GB', cache: '4.1GB', pagingBuffer: '1.0GB', nonPagingBuffer: '977MB', + speed: '2666 MHz', usedSlot: '2/4', shape: 'DIMM', reservedMemory: '165MB' }, + disk: { activeTime: '13%', argResponseTime: '1.4ms', readSpeed: '0KB/s', writeSpeed: '2.8MB/s', capacity: '932GB', formatted: '931GB', systemDisk: '是', pageFile: '是', type: 'HDD' }, + network: { send: '16Kbps', receive: '24Kbps', adapter: '以太网', connectType: '以太网', IPv4: '11.4.11.11', IPv6: 'fe80::f9b6:a4b4:3116:592b%8' }}, + { id: '1', cpu: { useRatio: '84%', speed: '3.89GHZ', process: '210', thread: '3008', handle: '115863', operationTime: '23:00:56:37', + referenceSpeed: '3.00 GHz', slot: 1, kernel: 6, logicProcessor: 6, virtualization: '已启用', l1: '384KB', l2: '1.5MB', l3: '9.0MB' }, + memory: { using: '10.2GB', usable: '5.2GB', submitted: '20.4/30.7GB', cache: '4.1GB', pagingBuffer: '1.0GB', nonPagingBuffer: '977MB', + speed: '2666 MHz', usedSlot: '2/4', shape: 'DIMM', reservedMemory: '165MB' }, + disk: { activeTime: '13%', argResponseTime: '1.4ms', readSpeed: '0KB/s', writeSpeed: '2.8MB/s', capacity: '932GB', formatted: '931GB', systemDisk: '是', pageFile: '是', type: 'HDD' }, + network: { send: '16Kbps', receive: '24Kbps', adapter: '以太网', connectType: '以太网', IPv4: '11.4.11.11', IPv6: 'fe80::f9b6:a4b4:3116:592b%8' }}, + { id: '2', cpu: { useRatio: '70%', speed: '3.89GHZ', process: '210', thread: '3008', handle: '115863', operationTime: '23:00:56:37', + referenceSpeed: '3.00 GHz', slot: 1, kernel: 6, logicProcessor: 6, virtualization: '已启用', l1: '384KB', l2: '1.5MB', l3: '9.0MB' }, + memory: { using: '10.2GB', usable: '5.2GB', submitted: '20.4/30.7GB', cache: '4.1GB', pagingBuffer: '1.0GB', nonPagingBuffer: '977MB', + speed: '2666 MHz', usedSlot: '2/4', shape: 'DIMM', reservedMemory: '165MB' }, + disk: { activeTime: '13%', argResponseTime: '1.4ms', readSpeed: '0KB/s', writeSpeed: '2.8MB/s', capacity: '932GB', formatted: '931GB', systemDisk: '是', pageFile: '是', type: 'HDD' }, + network: { send: '16Kbps', receive: '24Kbps', adapter: '以太网', connectType: '以太网', IPv4: '11.4.11.11', IPv6: 'fe80::f9b6:a4b4:3116:592b%8' }}, + { id: '3', cpu: { useRatio: '90%', speed: '3.89GHZ', process: '210', thread: '3008', handle: '115863', operationTime: '23:00:56:37', + referenceSpeed: '3.00 GHz', slot: 1, kernel: 6, logicProcessor: 6, virtualization: '已启用', l1: '384KB', l2: '1.5MB', l3: '9.0MB' }, + memory: { using: '10.2GB', usable: '5.2GB', submitted: '20.4/30.7GB', cache: '4.1GB', pagingBuffer: '1.0GB', nonPagingBuffer: '977MB', + speed: '2666 MHz', usedSlot: '2/4', shape: 'DIMM', reservedMemory: '165MB' }, + disk: { activeTime: '13%', argResponseTime: '1.4ms', readSpeed: '0KB/s', writeSpeed: '2.8MB/s', capacity: '932GB', formatted: '931GB', systemDisk: '是', pageFile: '是', type: 'HDD' }, + network: { send: '16Kbps', receive: '24Kbps', adapter: '以太网', connectType: '以太网', IPv4: '11.4.11.11', IPv6: 'fe80::f9b6:a4b4:3116:592b%8' }}] + }, + searchHost() { + const flag = this.getInfoShow(this.searchId) + if (flag) { + this.makeCharts() + } + }, + makeData() { + const base = +new Date() + const data = [] + for (let i = -60; i < 0; i++) { + const now = new Date((base + i * 1000)) + data.push([+now, Math.abs(Math.random()).toFixed(2)]) + } + return data + }, + getChartsData() { + this.cpuChartData = this.makeData() + this.memoryChartData = this.makeData() + this.diskChartData = this.makeData() + this.networkChartData = this.makeData() + }, + makeCharts() { + this.getChartsData() + const cpuChart = echarts.init(document.getElementById('CPUChart'), 'dark') + const memoryChart = echarts.init(document.getElementById('memoryChart'), 'dark') + const diskChart = echarts.init(document.getElementById('diskChart'), 'dark') + const networkChart = echarts.init(document.getElementById('networkChart'), 'dark') + + const cpuChartOption = { + tooltip: { + trigger: 'axis' + }, + title: { + left: 'center', + text: '' + }, + backgroundColor: 'transparent', + toolbox: { + }, + xAxis: { + type: 'time', + boundaryGap: false + }, + yAxis: { + max: 1, + type: 'value', + boundaryGap: [0, '100%'] + }, + dataZoom: [ + { + type: 'inside', + start: 0, + end: 100 + }, + { + type: 'inside', + start: 0, + end: 100 + } + ], + series: [ + { + name: '', + type: 'line', + smooth: true, + symbol: 'none', + areaStyle: {}, + data: this.cpuChartData, + itemStyle: { + color: '#6495ED' + } + } + ] + } + + const memoryChartOption = { + tooltip: { + trigger: 'axis' + }, + title: { + left: 'center', + text: '' + }, + backgroundColor: 'transparent', + toolbox: { + }, + xAxis: { + type: 'time', + boundaryGap: false + }, + yAxis: { + max: 1, + type: 'value', + boundaryGap: [0, '100%'] + }, + dataZoom: [ + { + type: 'inside', + start: 0, + end: 100 + }, + { + type: 'inside', + start: 0, + end: 100 + } + ], + series: [ + { + name: '', + type: 'line', + smooth: true, + symbol: 'none', + areaStyle: {}, + data: this.memoryChartData, + itemStyle: { + color: '#6495ED' + } + } + ] + } + + const diskChartOption = { + tooltip: { + trigger: 'axis' + }, + title: { + left: 'center', + text: '' + }, + backgroundColor: 'transparent', + toolbox: { + }, + xAxis: { + type: 'time', + boundaryGap: false + }, + yAxis: { + max: 1, + type: 'value', + boundaryGap: [0, '100%'] + }, + dataZoom: [ + { + type: 'inside', + start: 0, + end: 100 + }, + { + type: 'inside', + start: 0, + end: 100 + } + ], + series: [ + { + name: '', + type: 'line', + smooth: true, + symbol: 'none', + areaStyle: {}, + data: this.diskChartData, + itemStyle: { + color: '#6495ED' + } + } + ] + } + + const networkChartOption = { + tooltip: { + trigger: 'axis' + }, + title: { + left: 'center', + text: '' + }, + backgroundColor: 'transparent', + toolbox: { + }, + xAxis: { + type: 'time', + boundaryGap: false + }, + yAxis: { + max: 1, + type: 'value', + boundaryGap: [0, '100%'] + }, + dataZoom: [ + { + type: 'inside', + start: 0, + end: 100 + }, + { + type: 'inside', + start: 0, + end: 100 + } + ], + series: [ + { + name: '', + type: 'line', + smooth: true, + symbol: 'none', + areaStyle: {}, + data: this.networkChartData, + itemStyle: { + color: '#6495ED' + } + } + ] + } + + cpuChart.setOption(cpuChartOption) + memoryChart.setOption(memoryChartOption) + diskChart.setOption(diskChartOption) + networkChart.setOption(networkChartOption) + }, + getInfoShow(hostId) { + let idFlag = 0 + for (let i = 0; i < this.hostInfoList.length; i++) { + if (this.hostInfoList[i].id === hostId) { + this.hostInfoShow = this.hostInfoList[i] + idFlag = 1 + } + } + if (idFlag === 0) { + this.$message({ + message: '请输入正确的主机ID', + type: 'warning' + }) + return false + } else { + return true + } + }, + fullScreen() { + const element = document.getElementById('entirety') + if (element.requestFullscreen) { + element.requestFullscreen() + } else if (element.mozRequestFullScreen) { + element.mozRequestFullScreen() + } else if (element.webkitRequestFullscreen) { + element.webkitRequestFullscreen() + } else if (element.msRequestFullscreen) { + element.msRequestFullscreen() + } + } + + } +} diff --git a/task-resource-system/views/supplierManagement/css/supplierManagement.scss b/task-resource-system/views/supplierManagement/css/supplierManagement.scss new file mode 100644 index 0000000..8991d19 --- /dev/null +++ b/task-resource-system/views/supplierManagement/css/supplierManagement.scss @@ -0,0 +1,151 @@ +#supplierManagement { + .el-radio-group { + font-size: 0; + width: 25%; + } + .el-radio-button__inner { + color: white; + } + + .el-radio-button--mini .el-radio-button__inner { + padding: 7px 7px; + background: rgba(255, 255, 255, 0); + } + + .title_class { + font-family: 'Alternate'; + color: #FFFFFF; + letter-spacing: 0; + text-shadow: 0 0 6px #26A2FF; + font-size: 13pt; + } + + .tab_row { + display: flex; + display: -webkit-flex; + justify-content: space-between; + } + + .hr { + border-bottom: 1px solid; + opacity: 0.2; + color: white; + } + + .hr_tag { + margin-top: 17px; + border-bottom: 2px solid; + color: #FFFFFF; + width: 14px; + } + + .hr_left { + margin-left: 50px; + margin-right: 50px; + } + + + .el-card { + border-radius: 4px; + border: 1px solid #ffffff; + background-color: rgba(255, 255, 255, 0); + } + .el-card__header { + padding: 3px 10px; + border-bottom: 1px solid #EBEEF5; + -webkit-box-sizing: border-box; + box-sizing: border-box; + height: 4vh; + } + .el-card__body, .el-main { + padding: 5px; + } + .el-card__body{ + padding: 5px; + height: 20vh; + } + + .cardTop{ + height:30vh; + margin: 20px 50px 20px 50px; + padding: 10px 20px 0 20px; + } + .card1{ + display: flex; + display: -webkit-flex; + justify-content: center; + flex-direction: column; + height:20vh; + margin-left: 25%; + } + .card1_inner_row{ + margin-bottom: 1vh; + } + .card1_inner_num { + font-family: 'Alternate'; + color: #FFFFFF; + letter-spacing: 0; + text-shadow: 0 0 6px #26A2FF; + font-size: 13pt; + } + .card2_inner_num { + font-family: 'Alternate'; + color: #FFFFFF; + letter-spacing: 0; + text-shadow: 0 0 6px #26A2FF; + font-size: 12pt; + } + + .table_box { + //表格间距 + margin: 20px 50px 20px 50px; + .el-table--mini .el-table__cell { + padding: 2px 0 + } + + //整个表格背景颜色及边框弧度 + .el-table { + background-color: rgba(38, 38, 38, 0); + border-radius: 9px; + border-color:white ; + } + + //字体颜色 + .el-table tr { + background-color: transparent; + color: white; + height: 8vh + } + + .el-table th.el-table__cell.is-leaf { + border-bottom: 1px solid rgba(255, 255, 255, 0.3); + } + + //标题背景颜色 + .el-table th { + background-color: transparent !important; + } + + //标题字体颜色 + .el-table th .cell { + color: #a8a7a7; + font-size: 11pt; + height:25px; + } + //去除行下划线 + .el-table td { + border-color: rgba(255, 255, 255, 0.2); + font-size: 11pt; + } + + //修改鼠标滑过背景颜色 + .el-table--enable-row-hover .el-table__body tr:hover td { + background-color: rgba(38, 38, 38, 0.5) !important; + color: white !important; + } + } +} + + + + diff --git a/task-resource-system/views/supplierManagement/index.vue b/task-resource-system/views/supplierManagement/index.vue new file mode 100644 index 0000000..d479d7d --- /dev/null +++ b/task-resource-system/views/supplierManagement/index.vue @@ -0,0 +1,104 @@ + + + + diff --git a/task-resource-system/views/supplierManagement/js/index.js b/task-resource-system/views/supplierManagement/js/index.js new file mode 100644 index 0000000..acab399 --- /dev/null +++ b/task-resource-system/views/supplierManagement/js/index.js @@ -0,0 +1,101 @@ +import testApi from '@/api/testApi' + +export default { + name: 'supplierManagement', + data() { + return { + supplierList: [], + supplierSelected: '', + supplierInfoShow: { }, + transactionInfoList: [] + } + }, + created() { + }, + + async mounted() { + await this.getTransactionInfoList() + await this.getSupplierList() + }, + methods: { + async getTransactionInfoList() { + await testApi.getTransactionInfoList().then((res) => { + this.transactionInfoList = res.data + }).catch((error) => { + this.transactionInfoList = [ + { id: '0', userId: '10000', supplierId: '0', transInfo: '计算服务', transDate: '2023-07-02', state: '0' }, + { id: '1', userId: '20000', supplierId: '1', transInfo: '存储服务', transDate: '2023-07-02', state: '1' }, + { id: '2', userId: '30000', supplierId: '2', transInfo: '计算服务', transDate: '2023-07-02', state: '2' }, + { id: '3', userId: '40000', supplierId: '3', transInfo: '存储服务', transDate: '2023-07-02', state: '2' }, + { id: '4', userId: '50000', supplierId: '1', transInfo: '计算服务', transDate: '2023-07-02', state: '0' }, + { id: '5', userId: '60000', supplierId: '1', transInfo: '存储服务', transDate: '2023-07-02', state: '1' }, + { id: '6', userId: '80000', supplierId: '1', transInfo: '存储服务', transDate: '2023-07-02', state: '2' }, + { id: '7', userId: '10000', supplierId: '1', transInfo: '计算服务', transDate: '2023-07-02', state: '1' } + ] + }) + }, + async getSupplierList() { + await testApi.getSupplierList().then((res) => { + this.supplierList = res.data + this.initData() + }).catch((error) => { + this.supplierList = [ + { id: 0, name: '阿里云', hostNum: 100, normalHostNum: 95, abnormalHostNum: 5, computeCredenceValue: 90, memoryCredenceValue: 80, transactionCredenceValue: 90, compreCredenceValue: 85 }, + { id: 1, name: '联通云', hostNum: 100, normalHostNum: 98, abnormalHostNum: 2, computeCredenceValue: 95, memoryCredenceValue: 85, transactionCredenceValue: 95, compreCredenceValue: 90 }, + { id: 2, name: '华为云', hostNum: 100, normalHostNum: 89, abnormalHostNum: 11, computeCredenceValue: 90, memoryCredenceValue: 90, transactionCredenceValue: 90, compreCredenceValue: 90 }, + { id: 3, name: '火山云', hostNum: 100, normalHostNum: 93, abnormalHostNum: 7, computeCredenceValue: 85, memoryCredenceValue: 85, transactionCredenceValue: 90, compreCredenceValue: 85 } + ] + this.initData() + }) + }, + async changeSupplier() { + await testApi.getSupplierInfoById({ id: this.supplierSelected }).then((res) => { + this.supplierInfoShow = res.data + }).catch((error) => { + for (let i = 0; i < this.supplierList.length; i++) { + if (this.supplierList[i].id === this.supplierSelected) { + this.supplierInfoShow = this.supplierList[i] + } + } + }) + }, + initData() { + if (this.supplierList.length > 0) { + this.supplierSelected = this.supplierList[0].id + this.supplierInfoShow = this.supplierList[0] + } + }, + fullScreen() { + const element = document.getElementById('entirety') + if (element.requestFullscreen) { + element.requestFullscreen() + } else if (element.mozRequestFullScreen) { + element.mozRequestFullScreen() + } else if (element.webkitRequestFullscreen) { + element.webkitRequestFullscreen() + } else if (element.msRequestFullscreen) { + element.msRequestFullscreen() + } + }, + getStateText(state) { + switch (state) { + case '0': + return { + label: '已完成', + type: '' + } + case '1': + return { + label: '未完成', + type: 'warning' + } + case '2': + return { + label: '进行中', + type: 'success' + } + } + } + + } +}