Browse Source

公共模块:feign 统一异常处理 restful,code != 200,@EnableCommonFeignExceptionHandle

wangPH 2 years ago
parent
commit
b66440a6f7
22 changed files with 501 additions and 33 deletions
  1. 11 2
      pdf-office-account/pom.xml
  2. 25 0
      pdf-office-account/src/main/java/cn/kdan/cloud/pdf/office/account/config/ResourceServerConfig.java
  3. 39 0
      pdf-office-account/src/main/java/cn/kdan/cloud/pdf/office/account/config/WebSecurityConfig.java
  4. 3 2
      pdf-office-account/src/main/java/cn/kdan/cloud/pdf/office/account/controller/UserController.java
  5. 3 1
      pdf-office-api/pdf-office-api-account/src/main/java/cn/kdan/cloud/pdf/office/api/account/feign/UserApi.java
  6. 21 0
      pdf-office-common/src/main/java/cn/kdan/cloud/pdf/office/common/annotation/EnableCommonFeignExceptionHandle.java
  7. 10 1
      pdf-office-common/src/main/java/cn/kdan/cloud/pdf/office/common/aop/ControllerExceptionHandler.java
  8. 49 0
      pdf-office-common/src/main/java/cn/kdan/cloud/pdf/office/common/aop/FeignExceptionAspect.java
  9. 27 0
      pdf-office-common/src/main/java/cn/kdan/cloud/pdf/office/common/config/CloudServerConfiguration.java
  10. 31 0
      pdf-office-common/src/main/java/cn/kdan/cloud/pdf/office/common/config/FeignConfig.java
  11. 79 0
      pdf-office-common/src/main/java/cn/kdan/cloud/pdf/office/common/config/RestTemplateConfig.java
  12. 39 10
      pdf-office-common/src/main/java/cn/kdan/cloud/pdf/office/common/enums/EmailCodeTypeEnum.java
  13. 20 0
      pdf-office-common/src/main/java/cn/kdan/cloud/pdf/office/common/exception/FeignBackendRuntimeException.java
  14. 4 0
      pdf-office-common/src/main/java/cn/kdan/cloud/pdf/office/common/utils/CommonUtils.java
  15. 6 4
      pdf-office-payment/src/main/java/cn/kdan/cloud/pdf/office/payment/PDFOfficePaymentApplication.java
  16. 2 0
      pdf-office-product/src/main/java/cn/kdan/cloud/pdf/office/product/PDFOfficeProductApplication.java
  17. 2 0
      pdf-office-product/src/main/java/cn/kdan/cloud/pdf/office/product/controller/ProductController.java
  18. 1 0
      pdf-office-product/src/main/java/cn/kdan/cloud/pdf/office/product/service/impl/ProductServiceImpl.java
  19. 28 0
      pdf-office-sso/src/main/java/cn/kdan/cloud/pdf/office/sso/constant/AuthConstant.java
  20. 27 8
      pdf-office-sso/src/main/java/cn/kdan/cloud/pdf/office/sso/controller/AuthController.java
  21. 15 2
      pdf-office-sso/src/main/java/cn/kdan/cloud/pdf/office/sso/service/AuthService.java
  22. 59 3
      pdf-office-sso/src/main/java/cn/kdan/cloud/pdf/office/sso/service/impl/AuthServiceImpl.java

+ 11 - 2
pdf-office-account/pom.xml

@@ -103,8 +103,17 @@
             </exclusions>
         </dependency>
         <dependency>
-            <groupId>org.springframework.security.oauth</groupId>
-            <artifactId>spring-security-oauth2</artifactId>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-security</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.security.oauth.boot</groupId>
+            <artifactId>spring-security-oauth2-autoconfigure</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-security</artifactId>
         </dependency>
     </dependencies>
 

+ 25 - 0
pdf-office-account/src/main/java/cn/kdan/cloud/pdf/office/account/config/ResourceServerConfig.java

@@ -0,0 +1,25 @@
+package cn.kdan.cloud.pdf.office.account.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.annotation.Order;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.http.SessionCreationPolicy;
+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;
+
+/**
+ * 资源提供端的配置
+ */
+@Configuration
+@EnableResourceServer //开启资源提供服务的配置  是默认情况下spring security oauth2的http配置会被WebSecurityConfigurerAdapter的配置覆盖
+@Order(3)
+public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
+
+    @Override
+    public void configure(HttpSecurity http) throws Exception {
+        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
+        http.authorizeRequests().anyRequest().authenticated();
+    }
+
+}

+ 39 - 0
pdf-office-account/src/main/java/cn/kdan/cloud/pdf/office/account/config/WebSecurityConfig.java

@@ -0,0 +1,39 @@
+package cn.kdan.cloud.pdf.office.account.config;
+
+
+import cn.kdan.cloud.pdf.office.common.constant.CommonConstant;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.annotation.Order;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.builders.WebSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+
+/**
+ * 是默认情况下spring security的http配置 优于ResourceServerConfigurerAdapter的配置
+ */
+@Configuration
+@EnableWebSecurity
+@EnableAutoConfiguration(exclude = {org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class})
+@Order(1)
+public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
+
+    @Value("${httpMatchers.request}")
+    private String httpRequestMatchers;
+    @Value("${httpMatchers.web}")
+    private String httpWebMatchers;
+    @Override
+    protected void configure(HttpSecurity http) throws Exception {
+        http.csrf().disable();
+        http.authorizeRequests().antMatchers(httpRequestMatchers.split(CommonConstant.STRING_SIGN_COMMA)).permitAll();
+        http.requestMatchers().antMatchers(httpRequestMatchers.split(CommonConstant.STRING_SIGN_COMMA));
+    }
+    @Override
+    public void configure(WebSecurity web) {
+        web.ignoring()
+                .antMatchers(httpWebMatchers.split(CommonConstant.STRING_SIGN_COMMA));
+    }
+}

+ 3 - 2
pdf-office-account/src/main/java/cn/kdan/cloud/pdf/office/account/controller/UserController.java

@@ -14,6 +14,7 @@ import cn.kdan.cloud.pdf.office.account.service.UserService;
 
 import com.github.pagehelper.PageInfo;
 import org.springframework.beans.factory.annotation.Autowired;
+//import org.springframework.security.oauth2.provider.OAuth2Authentication;
 import org.springframework.security.oauth2.provider.OAuth2Authentication;
 import org.springframework.web.bind.annotation.*;
 
@@ -171,8 +172,8 @@ public class UserController {
      */
     @PostMapping("/logOffForUser")
     public ResultMap<Boolean> logOffForUser(@RequestParam String code, Principal principal) {
-        String userId = me(principal);
-        userService.logOffForUser(code,userId);
+//        String userId = me(principal);
+        userService.logOffForUser(code,"userId");
         return new ResultMap<>(CommonConstant.SUCCESS, CommonConstant.RESULT_SUCCESS,Boolean.TRUE);
     }
 

+ 3 - 1
pdf-office-api/pdf-office-api-account/src/main/java/cn/kdan/cloud/pdf/office/api/account/feign/UserApi.java

@@ -4,7 +4,9 @@ import cn.kdan.cloud.pdf.office.api.account.dto.PermissionGiftDTO;
 import cn.kdan.cloud.pdf.office.api.account.dto.PermissionTransferDTO;
 import cn.kdan.cloud.pdf.office.api.account.dto.UpdateUserForPaySuccessDTO;
 import cn.kdan.cloud.pdf.office.api.account.dto.UpdateUserForRefundDTO;
+import cn.kdan.cloud.pdf.office.api.account.feign.hystrix.OauthClientDetailsApiHystrix;
 import cn.kdan.cloud.pdf.office.api.account.feign.hystrix.UserHystrix;
+import cn.kdan.cloud.pdf.office.common.config.FeignConfig;
 import cn.kdan.cloud.pdf.office.common.constant.CommonConstant;
 import cn.kdan.cloud.pdf.office.common.dto.UserRegisterDTO;
 import cn.kdan.cloud.pdf.office.common.pojo.ResultMap;
@@ -18,7 +20,7 @@ import org.springframework.web.bind.annotation.RequestParam;
 
 
 @Component
-@FeignClient(value = CommonConstant.ACCOUNT_PROVIDER_NAME + "/user" , fallback = UserHystrix.class)
+@FeignClient(value = CommonConstant.ACCOUNT_PROVIDER_NAME + "/user" , configuration = FeignConfig.class, fallback = UserHystrix.class)
 public interface UserApi {
 
 

+ 21 - 0
pdf-office-common/src/main/java/cn/kdan/cloud/pdf/office/common/annotation/EnableCommonFeignExceptionHandle.java

@@ -0,0 +1,21 @@
+package cn.kdan.cloud.pdf.office.common.annotation;
+
+import cn.kdan.cloud.pdf.office.common.aop.FeignExceptionAspect;
+import cn.kdan.cloud.pdf.office.common.config.CloudServerConfiguration;
+import org.springframework.context.annotation.Import;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 组合标签,推荐用这个
+ * 将日志\ 异常处理\ 方法超时提醒注解组合使用
+ */
+@Target(value = ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Import({ CloudServerConfiguration.class, FeignExceptionAspect.class})
+public @interface EnableCommonFeignExceptionHandle {
+
+}

+ 10 - 1
pdf-office-common/src/main/java/cn/kdan/cloud/pdf/office/common/aop/ControllerExceptionHandler.java

@@ -1,8 +1,9 @@
 package cn.kdan.cloud.pdf.office.common.aop;
 
-import cn.kdan.cloud.pdf.office.common.pojo.ResultMap;
 import cn.kdan.cloud.pdf.office.common.constant.CommonConstant;
 import cn.kdan.cloud.pdf.office.common.exception.BackendRuntimeException;
+import cn.kdan.cloud.pdf.office.common.exception.FeignBackendRuntimeException;
+import cn.kdan.cloud.pdf.office.common.pojo.ResultMap;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.validation.BindingResult;
@@ -40,9 +41,17 @@ public class ControllerExceptionHandler {
     @ExceptionHandler(BackendRuntimeException.class)
     @ResponseBody
     public ResultMap handlerRuntimeException(BackendRuntimeException e){
+        logger.error("业务异常:"+e.getMessage(),e);
         return new ResultMap(CommonConstant.EXCEPTION_CODE_RUNTIME_ERROR,e.getMessage());
     }
 
+    @ExceptionHandler(FeignBackendRuntimeException.class)
+    @ResponseBody
+    public ResultMap handlerFeignRuntimeException(FeignBackendRuntimeException e){
+        logger.error("feign 业务异常:"+e.getMessage(),e);
+        return new ResultMap(e.getCode(),e.getMessage());
+    }
+
     @ExceptionHandler(Exception.class)
     @ResponseBody
     public ResultMap handlerException(Exception e){

+ 49 - 0
pdf-office-common/src/main/java/cn/kdan/cloud/pdf/office/common/aop/FeignExceptionAspect.java

@@ -0,0 +1,49 @@
+package cn.kdan.cloud.pdf.office.common.aop;
+
+import cn.kdan.cloud.pdf.office.common.exception.BackendRuntimeException;
+import cn.kdan.cloud.pdf.office.common.exception.FeignBackendRuntimeException;
+import cn.kdan.cloud.pdf.office.common.pojo.ResultMap;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.springframework.core.Ordered;
+import org.springframework.core.annotation.Order;
+
+/**
+ * Title: FeignExceptionAspect
+ * Description: Feign统一异常处理
+ */
+@Aspect
+@Order(Ordered.LOWEST_PRECEDENCE - 100)
+public class FeignExceptionAspect {
+
+    /**
+     * Pointcut注解声明切点
+     * 配置切入点,该方法无方法体,主要为方便同类中其他方法使用此处配置的切入点
+     *
+     * @within 对类起作用,@annotation 对方法起作用
+     */
+    @Pointcut("@within(org.springframework.cloud.openfeign.FeignClient)")
+    public void feignClientPointCut() {
+    }
+
+    /**
+     * 配置前置通知,使用在方法aspect()上注册的切入点
+     * 同时接受JoinPoint切入点对象,可以没有该参数
+     *
+     * @param proceedingJoinPoint
+     * @throws ClassNotFoundException
+     */
+    @Around("feignClientPointCut()")
+    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
+        Object object = proceedingJoinPoint.proceed();
+        if (object instanceof ResultMap<?>) {
+            ResultMap<?> responseDTO = (ResultMap<?>) object;
+            if (responseDTO.getCode() != 200) {
+                throw new FeignBackendRuntimeException(responseDTO.getCode(),responseDTO.getMsg());
+            }
+        }
+        return object;
+    }
+}

+ 27 - 0
pdf-office-common/src/main/java/cn/kdan/cloud/pdf/office/common/config/CloudServerConfiguration.java

@@ -0,0 +1,27 @@
+package cn.kdan.cloud.pdf.office.common.config;
+
+import cn.kdan.cloud.pdf.office.common.aop.FeignExceptionAspect;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @author ComPDFKit-WPH 2023/2/8
+ */
+@Configuration
+public class CloudServerConfiguration {
+
+//    @Bean
+//    public HeaderInterceptor headerInterceptor() {
+//        //微服务权限拦截处理
+//        return new HeaderInterceptor();
+//    }
+
+    @Bean
+    @ConditionalOnProperty(name = "system.cloud.feign.exception.enabled", havingValue="true", matchIfMissing=true)
+    public FeignExceptionAspect feignExceptionAspect() {
+        //微服务权限拦截处理
+        return new FeignExceptionAspect();
+    }
+
+}

+ 31 - 0
pdf-office-common/src/main/java/cn/kdan/cloud/pdf/office/common/config/FeignConfig.java

@@ -0,0 +1,31 @@
+package cn.kdan.cloud.pdf.office.common.config;
+
+import feign.RequestInterceptor;
+import feign.RequestTemplate;
+import org.apache.http.HttpHeaders;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.util.ObjectUtils;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * feign
+ *
+ * @author tangxiangan
+ */
+@Configuration
+public class FeignConfig implements RequestInterceptor {
+
+    @Override
+    public void apply(RequestTemplate requestTemplate) {
+        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+        if(!ObjectUtils.isEmpty(attributes)){
+            HttpServletRequest request = attributes.getRequest();
+            String token = request.getHeader(HttpHeaders.AUTHORIZATION);
+            requestTemplate.header(HttpHeaders.AUTHORIZATION, token);
+        }
+    }
+
+}

+ 79 - 0
pdf-office-common/src/main/java/cn/kdan/cloud/pdf/office/common/config/RestTemplateConfig.java

@@ -0,0 +1,79 @@
+package cn.kdan.cloud.pdf.office.common.config;
+
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import org.apache.http.conn.ssl.NoopHostnameVerifier;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.conn.ssl.TrustStrategy;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.springframework.cloud.client.loadbalancer.LoadBalanced;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
+import org.springframework.web.client.RestTemplate;
+
+import javax.net.ssl.SSLContext;
+import java.security.KeyManagementException;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.X509Certificate;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * RestTemplate配置类
+ */
+@Configuration
+public class RestTemplateConfig {
+
+    @Bean
+    @LoadBalanced
+    public RestTemplate restTemplate() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
+        TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;
+
+        SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom()
+                .loadTrustMaterial(null, acceptingTrustStrategy)
+                .build();
+
+        SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext,NoopHostnameVerifier.INSTANCE);
+
+        CloseableHttpClient httpClient = HttpClients.custom()
+                .setSSLSocketFactory(csf)
+                .build();
+
+        HttpComponentsClientHttpRequestFactory requestFactory =
+                new HttpComponentsClientHttpRequestFactory();
+
+        requestFactory.setHttpClient(httpClient);
+
+        RestTemplate restTemplate = new RestTemplate(requestFactory);
+        //替换自定义的MappingJackson2HttpMessageConverter
+        List<HttpMessageConverter<?>> httpMessageConverters = new ArrayList<>();
+        restTemplate.getMessageConverters().stream().forEach(httpMessageConverter -> {
+            if(!(httpMessageConverter instanceof MappingJackson2HttpMessageConverter)){
+                httpMessageConverters.add(httpMessageConverter);
+            }
+        });
+        httpMessageConverters.add(getMappingJackson2HttpMessageConverter());
+        restTemplate.setMessageConverters(httpMessageConverters);
+        return restTemplate;
+    }
+
+    @Bean
+    public MappingJackson2HttpMessageConverter getMappingJackson2HttpMessageConverter() {
+        MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
+        //设置日期格式
+        ObjectMapper objectMapper = new ObjectMapper();
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        objectMapper.setDateFormat(sdf);
+        objectMapper.configure(SerializationFeature.FAIL_ON_UNWRAPPED_TYPE_IDENTIFIERS, false);
+        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+        mappingJackson2HttpMessageConverter.setObjectMapper(objectMapper);
+        return mappingJackson2HttpMessageConverter;
+    }
+}

+ 39 - 10
pdf-office-common/src/main/java/cn/kdan/cloud/pdf/office/common/enums/EmailCodeTypeEnum.java

@@ -1,28 +1,57 @@
 package cn.kdan.cloud.pdf.office.common.enums;
 
 public enum EmailCodeTypeEnum {
+    /**
+     * 登录
+     */
+    LOGIN("login","邮箱验证码登录","邮箱验证码登录",""),
+    /**
+     * 注册
+     */
+    USER_REGISTER("user_register","注册验证码","注册验证码",""),
+    /**
+     * 用户注销
+     */
+    USER_LOG_OFF("user_log_off","注销验证码","注销验证码","");
 
-    LOGIN("login"),
-    USER_REGISTER("user_register"),
-    USER_LOG_OFF("user_log_off");
 
+    private final String value;
+    private final String action;
 
-    private String value;
+    private final String subject;
 
-    EmailCodeTypeEnum(String value){
-        this.value = value;
+    private final String text;
+
+    public String subject() {
+        return subject;
     }
 
-    public String value() {
-        return value;
+    public String text() {
+        return text;
+    }
+
+    EmailCodeTypeEnum(String value, String action, String subject, String text) {
+        this.value = value;
+        this.action = action;
+        this.subject = subject;
+        this.text = text;
     }
 
     public static EmailCodeTypeEnum getEnumByValue(String value) {
-        for (EmailCodeTypeEnum modeEnum: EmailCodeTypeEnum.values()) {
+        for (EmailCodeTypeEnum modeEnum : EmailCodeTypeEnum.values()) {
             if (modeEnum.value().equals(value)) {
                 return modeEnum;
             }
         }
-        return EmailCodeTypeEnum.LOGIN;
+        return null;
     }
+
+    public String value() {
+        return value;
+    }
+
+    public String action() {
+        return action;
+    }
+
 }

+ 20 - 0
pdf-office-common/src/main/java/cn/kdan/cloud/pdf/office/common/exception/FeignBackendRuntimeException.java

@@ -0,0 +1,20 @@
+package cn.kdan.cloud.pdf.office.common.exception;
+
+import lombok.Getter;
+
+/**
+ * @author tangxiangan
+ */
+@Getter
+public class FeignBackendRuntimeException extends RuntimeException{
+    private final Integer code;
+
+    public FeignBackendRuntimeException(String message) {
+        super(message);
+        this.code = 500;
+    }
+    public FeignBackendRuntimeException(Integer code ,String message) {
+        super(message);
+        this.code = code;
+    }
+}

+ 4 - 0
pdf-office-common/src/main/java/cn/kdan/cloud/pdf/office/common/utils/CommonUtils.java

@@ -228,4 +228,8 @@ public class CommonUtils {
 		return stringBuilder.toString();
 	}
 
+	public static String generateVerifyCode() {
+		return String.valueOf(new Random().nextInt(899999) + 100000);
+	}
+
 }

+ 6 - 4
pdf-office-payment/src/main/java/cn/kdan/cloud/pdf/office/payment/PDFOfficePaymentApplication.java

@@ -1,9 +1,8 @@
 package cn.kdan.cloud.pdf.office.payment;
 
+import cn.kdan.cloud.pdf.office.api.account.annotation.EnableAccountApiClient;
 import cn.kdan.cloud.pdf.office.api.product.annotation.EnableProductApiClient;
-import cn.kdan.cloud.pdf.office.common.annotation.EnableCommonTools;
-import cn.kdan.cloud.pdf.office.common.annotation.EnableMyRabbitMqHandler;
-import cn.kdan.cloud.pdf.office.common.annotation.EnableMybatisPlusConfig;
+import cn.kdan.cloud.pdf.office.common.annotation.*;
 import cn.kdan.cloud.pdf.office.payment.properties.PaddleProperties;
 import cn.kdan.cloud.pdf.office.payment.properties.PaypalProperties;
 import cn.kdan.cloud.pdf.office.payment.verifier.Verifier;
@@ -16,7 +15,6 @@ import org.springframework.cache.annotation.EnableCaching;
 import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
 import org.springframework.cloud.netflix.hystrix.EnableHystrix;
 import org.springframework.context.annotation.Bean;
-import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;
 
 /**
  * @author ComPDFKit-WPH 2023/1/12
@@ -33,6 +31,9 @@ import org.springframework.data.redis.repository.configuration.EnableRedisReposi
 @EnableCaching
 @EnableCommonTools
 @EnableProductApiClient
+@EnableAccountApiClient
+@EnableCommonFeignExceptionHandle
+@EnableControllerExceptionHandler
 public class PDFOfficePaymentApplication {
 
     public static void main(String[] args) {
@@ -57,4 +58,5 @@ public class PDFOfficePaymentApplication {
                 "-----END PUBLIC KEY-----");
     }
 
+
 }

+ 2 - 0
pdf-office-product/src/main/java/cn/kdan/cloud/pdf/office/product/PDFOfficeProductApplication.java

@@ -1,5 +1,6 @@
 package cn.kdan.cloud.pdf.office.product;
 
+import cn.kdan.cloud.pdf.office.common.annotation.EnableControllerExceptionHandler;
 import cn.kdan.cloud.pdf.office.common.annotation.EnableMybatisPlusConfig;
 import org.mybatis.spring.annotation.MapperScan;
 import org.springframework.boot.SpringApplication;
@@ -16,6 +17,7 @@ import org.springframework.cloud.netflix.hystrix.EnableHystrix;
 @EnableHystrix
 @EnableMybatisPlusConfig
 @MapperScan("cn.kdan.cloud.pdf.office.product.mapper")
+@EnableControllerExceptionHandler
 public class PDFOfficeProductApplication {
 
     public static void main(String[] args) {

+ 2 - 0
pdf-office-product/src/main/java/cn/kdan/cloud/pdf/office/product/controller/ProductController.java

@@ -3,6 +3,7 @@ package cn.kdan.cloud.pdf.office.product.controller;
 import cn.kdan.cloud.pdf.office.api.product.dto.ProductListDTO;
 import cn.kdan.cloud.pdf.office.api.product.vo.ProductPriceVo;
 import cn.kdan.cloud.pdf.office.api.product.vo.ProductVO;
+import cn.kdan.cloud.pdf.office.common.exception.BackendRuntimeException;
 import cn.kdan.cloud.pdf.office.common.pojo.ResultMap;
 import cn.kdan.cloud.pdf.office.product.service.ProductService;
 import com.baomidou.mybatisplus.core.metadata.IPage;
@@ -28,6 +29,7 @@ public class ProductController {
     @GetMapping("/getProductPrice")
     public ResultMap<ProductPriceVo> getProductPrice(@Validated @NotNull(message = "产品id不能为空") @RequestParam("productId") String productId,
                                                      @NotNull(message = "userId不能为空") @RequestParam("userId") String userId) {
+
         return ResultMap.success(productService.getProductPrice(productId, userId));
     }
 

+ 1 - 0
pdf-office-product/src/main/java/cn/kdan/cloud/pdf/office/product/service/impl/ProductServiceImpl.java

@@ -4,6 +4,7 @@ import cn.kdan.cloud.pdf.office.api.product.dto.ProductListDTO;
 import cn.kdan.cloud.pdf.office.api.product.vo.ActivityInfoVO;
 import cn.kdan.cloud.pdf.office.api.product.vo.ProductPriceVo;
 import cn.kdan.cloud.pdf.office.api.product.vo.ProductVO;
+import cn.kdan.cloud.pdf.office.common.exception.BackendRuntimeException;
 import cn.kdan.cloud.pdf.office.common.utils.JsonUtils;
 import cn.kdan.cloud.pdf.office.product.entity.Product;
 import cn.kdan.cloud.pdf.office.product.mapper.ProductMapper;

+ 28 - 0
pdf-office-sso/src/main/java/cn/kdan/cloud/pdf/office/sso/constant/AuthConstant.java

@@ -67,4 +67,32 @@ public class AuthConstant {
 
     public static final String EMAIL_REGISTER_ERROR = "Register error!";
 
+
+    public static final String EXCEPTION_MSG_CODE_ACTION_ERROR = "operation action error";
+
+    public static final String EXCEPTION_MSG_CODE_TYPE_ERROR = "operation action error";
+
+    public static final String SUCCESS_MSG_CODE_SEND = "code send success";
+
+    public static final String SUCCESS_MSG_CODE_SEND_MAIL = "mail send success";
+    public static final String EXCEPTION_MSG_CODE_SEND_ERROR = "code send error";
+    public static final String EXCEPTION_MAIL_CODE_SEND_ERROR = "mail send error";
+
+    public static final String VERIFY_CODE_KEY = "pdf_tech_verifyCode_";
+
+    public static final String VERIFY_CODE_TIME_OUT = "pdf_tech_verifyCode_timeout_";
+
+    public static final Long VERIFY_CODE_KEY_EXPIRE_TIME = 15 * 60L;
+
+    /**
+     * 邀请链接过期时间24小时
+     */
+    public static final Long INVITE_VERIFY_CODE_KEY_EXPIRE_TIME = 72 * 60 * 60L;
+
+    public static final Long VERIFY_CODE_KEY_RESEND_TIME = 60L;
+
+    public static final String VERIFY_CODE_SEND_TOO_QUICKLY = "code send too quickly";
+
+
+
 }

+ 27 - 8
pdf-office-sso/src/main/java/cn/kdan/cloud/pdf/office/sso/controller/AuthController.java

@@ -5,6 +5,7 @@ import cn.kdan.cloud.pdf.office.api.email.feign.EmailApi;
 import cn.kdan.cloud.pdf.office.sso.constant.AuthConstant;
 import cn.kdan.cloud.pdf.office.common.enums.EmailCodeTypeEnum;
 import cn.kdan.cloud.pdf.office.common.dto.UserRegisterDTO;
+import cn.kdan.cloud.pdf.office.sso.enums.VerifyTypeEnum;
 import cn.kdan.cloud.pdf.office.sso.service.AuthService;
 import cn.kdan.cloud.pdf.office.common.constant.CommonConstant;
 import cn.kdan.cloud.pdf.office.common.exception.BackendRuntimeException;
@@ -18,6 +19,7 @@ import org.springframework.stereotype.Controller;
 import org.springframework.web.bind.annotation.*;
 
 import javax.servlet.http.HttpServletRequest;
+import javax.validation.constraints.NotBlank;
 import java.security.Principal;
 import java.util.HashMap;
 import java.util.Map;
@@ -76,29 +78,29 @@ public class AuthController {
     /**
      * 邮箱验证码登录
      * @param email 邮箱
-     * @param code 验证码1
+     * @param password 验证码1
      * @param appId appid
      * @param platformType 平台类型 1 官网 2 后台
      * @param request
      * @return
      */
     @GetMapping("/emailLogin")
-    public ResultMap<TokenVO> emailLogin(@RequestParam String email, @RequestParam String code, @RequestParam String appId,
+    public ResultMap<TokenVO> emailLogin(@RequestParam String email, @RequestParam String password, @RequestParam String appId,
                                        @RequestParam String platformType,  @RequestParam String deviceSign,HttpServletRequest request) {
-        return new ResultMap<>(CommonConstant.SUCCESS, CommonConstant.RESULT_SUCCESS, authService.emailLogin(email,code,appId,platformType,deviceSign,request));
+        return new ResultMap<>(CommonConstant.SUCCESS, CommonConstant.RESULT_SUCCESS, authService.emailLogin(email,password,appId,platformType,deviceSign,request));
     }
 
-    @GetMapping("/emailRegister")
+    @PostMapping("/emailRegister")
     public ResultMap<TokenVO> emailRegister(@RequestBody UserRegisterDTO userRegisterDTO) {
         return new ResultMap<>(CommonConstant.SUCCESS, CommonConstant.RESULT_SUCCESS, authService.emailRegister(userRegisterDTO));
     }
 
-    @PostMapping("/sendCode")
+    @PostMapping("/sendVerifyCode")
     public ResultMap<Boolean> sendCode(@RequestParam String email, @RequestParam String appId) {
         EmailSendBO bo = new EmailSendBO();
-        bo.setEmailTitle("test");
-        bo.setToEmail("316531990@qq.com");
-        bo.setTemplateId("1");
+//        bo.setEmailTitle("test");
+//        bo.setToEmail("316531990@qq.com");
+//        bo.setTemplateId("1");
         Map<String,String> map = new HashMap<>();
         map.put("@username@","wph");
         bo.setSendContent(map);
@@ -106,6 +108,23 @@ public class AuthController {
         return new ResultMap<>(CommonConstant.SUCCESS, CommonConstant.RESULT_SUCCESS, Boolean.TRUE);
     }
 
+    @GetMapping("/getVerifyCode")
+    public ResultMap<Boolean> getVerifyCode(@RequestParam String action,@RequestParam Integer type,@NotBlank(message = "接收者不能为空") @RequestParam String receiver,@RequestParam String appId) {
+        EmailCodeTypeEnum actionEnum = EmailCodeTypeEnum.getEnumByValue(action);
+        if (actionEnum == null) {
+            return new ResultMap<>(CommonConstant.ERROR, AuthConstant.EXCEPTION_MSG_CODE_ACTION_ERROR, false);
+        }
+        VerifyTypeEnum verifyTypeEnum = VerifyTypeEnum.getEnumByCode(type);
+        if (verifyTypeEnum == null) {
+            return new ResultMap<>(CommonConstant.ERROR, AuthConstant.EXCEPTION_MSG_CODE_TYPE_ERROR, false);
+        }
+        if (authService.getVerifyCode(actionEnum, verifyTypeEnum, receiver,appId)) {
+            return new ResultMap<>(CommonConstant.SUCCESS, VerifyTypeEnum.PHONE.code().equals(type) ?AuthConstant.SUCCESS_MSG_CODE_SEND:AuthConstant.SUCCESS_MSG_CODE_SEND_MAIL, true);
+        } else {
+            return new ResultMap<>(CommonConstant.ERROR, VerifyTypeEnum.PHONE.code().equals(type) ?AuthConstant.EXCEPTION_MSG_CODE_SEND_ERROR:AuthConstant.EXCEPTION_MAIL_CODE_SEND_ERROR, false);
+        }
+    }
+
 
     /**
      * 实时检查邮件验证码

+ 15 - 2
pdf-office-sso/src/main/java/cn/kdan/cloud/pdf/office/sso/service/AuthService.java

@@ -4,6 +4,7 @@ package cn.kdan.cloud.pdf.office.sso.service;
 import cn.kdan.cloud.pdf.office.common.dto.UserRegisterDTO;
 import cn.kdan.cloud.pdf.office.common.enums.EmailCodeTypeEnum;
 import cn.kdan.cloud.pdf.office.common.vo.TokenVO;
+import cn.kdan.cloud.pdf.office.sso.enums.VerifyTypeEnum;
 
 import javax.servlet.http.HttpServletRequest;
 import java.security.Principal;
@@ -19,14 +20,25 @@ public interface AuthService {
      * 邮箱登录
      *
      * @param email 邮箱
-     * @param code 验证码
+     * @param password 验证码
      * @param appId appid
      * @param platformType  1官网 2后台
      * @param request
      * @return TokenVO
      */
-    TokenVO emailLogin(String email, String code, String appId, String platformType, String deviceSign, HttpServletRequest request);
+    TokenVO emailLogin(String email, String password, String appId, String platformType, String deviceSign, HttpServletRequest request);
+
 
+    /**
+     * 发送验证码
+     *
+     * @param action  EmailCodeTypeEnum
+     * @param type VerifyTypeEnum
+     * @param receiver 邮箱
+     * @param appId appId 应用id
+     * @return boolean
+     */
+    boolean getVerifyCode(EmailCodeTypeEnum action, VerifyTypeEnum type, String receiver, String appId);
 
     /**
      * 登出(token失效,清除登录设备信息)
@@ -53,4 +65,5 @@ public interface AuthService {
      * @return
      */
     TokenVO emailRegister(UserRegisterDTO userRegisterDTO);
+
 }

+ 59 - 3
pdf-office-sso/src/main/java/cn/kdan/cloud/pdf/office/sso/service/impl/AuthServiceImpl.java

@@ -5,6 +5,8 @@ import cn.kdan.cloud.pdf.office.api.account.feign.OauthClientDetailsApi;
 import cn.kdan.cloud.pdf.office.api.account.feign.UserApi;
 import cn.kdan.cloud.pdf.office.api.account.vo.LoginDevice;
 import cn.kdan.cloud.pdf.office.api.account.vo.OauthClientDetails;
+import cn.kdan.cloud.pdf.office.api.email.bo.EmailSendBO;
+import cn.kdan.cloud.pdf.office.api.email.feign.EmailApi;
 import cn.kdan.cloud.pdf.office.common.constant.CommonConstant;
 import cn.kdan.cloud.pdf.office.common.dto.UserRegisterDTO;
 import cn.kdan.cloud.pdf.office.common.enums.EmailCodeTypeEnum;
@@ -16,6 +18,7 @@ import cn.kdan.cloud.pdf.office.common.utils.RedisUtils;
 import cn.kdan.cloud.pdf.office.common.vo.TokenVO;
 import cn.kdan.cloud.pdf.office.common.vo.UserInfoVO;
 import cn.kdan.cloud.pdf.office.sso.constant.AuthConstant;
+import cn.kdan.cloud.pdf.office.sso.enums.VerifyTypeEnum;
 import cn.kdan.cloud.pdf.office.sso.service.AuthService;
 import cn.kdan.cloud.pdf.office.sso.utils.TokenUtils;
 import com.alibaba.nacos.common.utils.CollectionUtils;
@@ -26,6 +29,7 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.cloud.context.config.annotation.RefreshScope;
 import org.springframework.context.annotation.Lazy;
+import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 import org.springframework.security.core.authority.SimpleGrantedAuthority;
@@ -73,6 +77,8 @@ public class AuthServiceImpl implements AuthService {
 
     @Autowired
     private TokenEndpoint tokenEndpoint;
+    @Autowired
+    private EmailApi emailApi;
 
 
     @Value("${security.oauth2.client.user-authorization-uri}")
@@ -124,16 +130,16 @@ public class AuthServiceImpl implements AuthService {
     }
 
     @Override
-    public TokenVO emailLogin(String email, String code, String appId, String platformType, String deviceSign,HttpServletRequest request) {
+    public TokenVO emailLogin(String email, String password, String appId, String platformType, String deviceSign,HttpServletRequest request) {
         //如果是管理平台就去查管理平台的用户表
         UserInfoVO userInfoVO = userApi.getByAppAccount(email,appId,platformType).getResult();
         //检查用户存在
         checkUser(userInfoVO);
         //检查邮件验证码
-        checkEmailCodeValid(EmailCodeTypeEnum.LOGIN,email,code);
+        //checkEmailCodeValid(EmailCodeTypeEnum.LOGIN,email,code);
         // 检查设备是否达到上限
         checkLoginDeviceNum(userInfoVO.getId(), deviceSign);
-        TokenVO vo = getTokenByUser(code, userInfoVO);
+        TokenVO vo = getTokenByUser(password, userInfoVO);
         //关联设备登录
         relateTokenAndDevice(vo.getAccess_token(), userInfoVO.getId(), deviceSign, appId);
         return vo;
@@ -156,6 +162,56 @@ public class AuthServiceImpl implements AuthService {
         throw new BackendRuntimeException(AuthConstant.EMAIL_REGISTER_ERROR);
     }
 
+    @Override
+    public boolean getVerifyCode(EmailCodeTypeEnum action, VerifyTypeEnum type, String receiver, String appId) {
+        boolean flag = false;
+        if (redisUtils.hexists(AuthConstant.VERIFY_CODE_TIME_OUT + action.value() + CommonConstant.STRIKE_THROUGH + appId, receiver)) {
+            throw new BackendRuntimeException(AuthConstant.VERIFY_CODE_SEND_TOO_QUICKLY);
+        }
+        String code = CommonUtils.generateVerifyCode();
+        switch (type) {
+            //发送手机验证码
+            case PHONE:
+                //忘记密码时手机验证 发送手机短信
+                break;
+            //发送邮件
+            case EMAIL:
+                //创建账号
+                EmailSendBO bo = new EmailSendBO();
+                bo.setToEmail(receiver);
+                handleEmailSendBO(action,bo,code);
+                if(emailApi.sendEmail(bo).getCode() == HttpStatus.OK.value()){
+                    flag = true;
+                };
+                break;
+            default:
+                break;
+        }
+        //存入缓存
+        if (flag) {
+            redisUtils.hset(AuthConstant.VERIFY_CODE_KEY + action.value() + CommonConstant.STRIKE_THROUGH + appId, receiver, code, AuthConstant.VERIFY_CODE_KEY_EXPIRE_TIME);
+            //再次发送计时
+            redisUtils.hset(AuthConstant.VERIFY_CODE_TIME_OUT + action.value() + CommonConstant.STRIKE_THROUGH + appId , receiver, "1", AuthConstant.VERIFY_CODE_KEY_RESEND_TIME);
+        }
+        return flag;
+    }
+
+    void handleEmailSendBO (EmailCodeTypeEnum action,EmailSendBO bo,String code) {
+        //设置事件
+        bo.setUseEvent(action.value());
+        //设置title
+        Map<String,String> titleMap = new HashMap<>();
+        //设置内容
+        Map<String,String> contentMap = new HashMap<>();
+        //注册
+        if(action.value().equals(EmailCodeTypeEnum.USER_REGISTER.value())){
+            contentMap.put("@code@",code);
+        }
+        bo.setSendTitleContent(titleMap);
+        //设置内容
+        bo.setSendContent(contentMap);
+    }
+
     @Resource
     @Lazy
     private TokenStore tokenStore;