Browse Source

冲突解决

will.chen 7 years ago
parent
commit
998cda136f
45 changed files with 832 additions and 427 deletions
  1. 2 10
      applications/purchase/purchase-server/pom.xml
  2. 2 2
      base-servers/account/account-api/src/main/java/com/usoftchina/saas/account/api/AccountApi.java
  3. 1 9
      base-servers/account/account-server/pom.xml
  4. 1 1
      base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/controller/AccountController.java
  5. 1 1
      base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/controller/CompanyController.java
  6. 0 15
      base-servers/account/account-server/src/main/resources/banner.txt
  7. 3 0
      base-servers/admin-server/src/main/resources/config/application-docker.yml
  8. 2 29
      base-servers/auth/auth-server/pom.xml
  9. 2 0
      base-servers/auth/auth-server/src/main/java/com/usoftchina/saas/auth/AuthApplication.java
  10. 0 15
      base-servers/auth/auth-server/src/main/resources/banner.txt
  11. 12 14
      base-servers/gateway-server/pom.xml
  12. 0 2
      base-servers/gateway-server/src/main/java/com/usoftchina/saas/gateway/GatewayApplication.java
  13. 0 103
      base-servers/gateway-server/src/main/java/com/usoftchina/saas/gateway/config/AccessFilter.java
  14. 25 0
      base-servers/gateway-server/src/main/java/com/usoftchina/saas/gateway/config/AuthFilter.java
  15. 65 0
      base-servers/gateway-server/src/main/java/com/usoftchina/saas/gateway/config/ErrorConfig.java
  16. 0 18
      base-servers/gateway-server/src/main/java/com/usoftchina/saas/gateway/config/SecurityConfig.java
  17. 49 0
      base-servers/gateway-server/src/main/java/com/usoftchina/saas/gateway/config/WebConfig.java
  18. 0 47
      base-servers/gateway-server/src/main/java/com/usoftchina/saas/gateway/config/ZuulConfig.java
  19. 99 0
      base-servers/gateway-server/src/main/java/com/usoftchina/saas/gateway/error/MyExceptionHandler.java
  20. 103 39
      base-servers/gateway-server/src/main/resources/application.yml
  21. 0 15
      base-servers/gateway-server/src/main/resources/banner.txt
  22. 3 0
      base-servers/gateway-server/src/main/resources/config/application-docker.yml
  23. 4 11
      base-servers/ui-server/pom.xml
  24. 15 10
      base-servers/ui-server/src/main/java/com/usoftchina/saas/ui/controller/co/CoViewController.java
  25. 2 0
      base-servers/ui-server/src/main/resources/application.yml
  26. 0 15
      base-servers/ui-server/src/main/resources/banner.txt
  27. 3 0
      base-servers/zipkin-server/src/main/resources/config/application-docker.yml
  28. 1 0
      framework/pom.xml
  29. 44 0
      framework/server-starter/pom.xml
  30. 14 0
      framework/server-starter/src/main/java/com/usoftchina/saas/server/ServerAutoConfiguration.java
  31. 16 0
      framework/server-starter/src/main/java/com/usoftchina/saas/server/context/SpringContextListener.java
  32. 41 0
      framework/server-starter/src/main/java/com/usoftchina/saas/server/error/MyErrorController.java
  33. 7 0
      framework/server-starter/src/main/resources/META-INF/spring.factories
  34. BIN
      framework/server-starter/src/main/resources/auth/pub.key
  35. 0 0
      framework/server-starter/src/main/resources/banner.txt
  36. 1 9
      frontend/saas-web/app/util/FormUtil.js
  37. 15 5
      frontend/saas-web/app/view/core/dbfind/DbfindTrigger.js
  38. 32 0
      frontend/saas-web/app/view/core/form/DateField.js
  39. 71 29
      frontend/saas-web/app/view/core/form/FormPanel.js
  40. 122 3
      frontend/saas-web/app/view/core/form/FormPanelController.js
  41. 49 21
      frontend/saas-web/app/view/core/form/field/DetailGridField.js
  42. 4 0
      frontend/saas-web/app/view/main/Main.scss
  43. 1 3
      frontend/saas-web/app/view/test/order/FormController.js
  44. 14 0
      frontend/saas-web/app/view/test/order/FormPanel.js
  45. 6 1
      pom.xml

+ 2 - 10
applications/purchase/purchase-server/pom.xml

@@ -13,10 +13,6 @@
     <description>purchase server</description>
 
     <dependencies>
-        <dependency>
-            <groupId>com.usoftchina.saas</groupId>
-            <artifactId>core</artifactId>
-        </dependency>
         <dependency>
             <groupId>com.usoftchina.saas</groupId>
             <artifactId>purchase-dto</artifactId>
@@ -31,12 +27,8 @@
             <artifactId>auth-client</artifactId>
         </dependency>
         <dependency>
-            <groupId>org.springframework.cloud</groupId>
-            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-actuator</artifactId>
+            <groupId>com.usoftchina.saas</groupId>
+            <artifactId>server-starter</artifactId>
         </dependency>
         <!-- db -->
         <dependency>

+ 2 - 2
base-servers/account/account-api/src/main/java/com/usoftchina/saas/account/api/AccountApi.java

@@ -19,7 +19,7 @@ public interface AccountApi {
      * @param password 明文密码
      * @return
      */
-    @GetMapping(value = "/api/account/pwd/check")
+    @GetMapping(value = "/pwd/check")
     Result<AccountDTO> validByUsernameAndPwd(@RequestParam(value = "username") String username, @RequestParam(value = "password") String password);
 
     /**
@@ -28,6 +28,6 @@ public interface AccountApi {
      * @param username
      * @return
      */
-    @GetMapping(value = "/api/account")
+    @GetMapping
     Result<AccountDTO> getAccount(@RequestParam(value = "username") String username);
 }

+ 1 - 9
base-servers/account/account-server/pom.xml

@@ -15,20 +15,12 @@
     <dependencies>
         <dependency>
             <groupId>com.usoftchina.saas</groupId>
-            <artifactId>core</artifactId>
+            <artifactId>server-starter</artifactId>
         </dependency>
         <dependency>
             <groupId>com.usoftchina.saas</groupId>
             <artifactId>account-dto</artifactId>
         </dependency>
-        <dependency>
-            <groupId>org.springframework.cloud</groupId>
-            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-actuator</artifactId>
-        </dependency>
         <!-- db -->
         <dependency>
             <groupId>mysql</groupId>

+ 1 - 1
base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/controller/AccountController.java

@@ -21,7 +21,7 @@ import java.util.List;
  * @date 2018/10/2
  */
 @RestController
-@RequestMapping("/api/account")
+@RequestMapping("/account")
 public class AccountController {
 
     @Autowired

+ 1 - 1
base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/controller/CompanyController.java

@@ -14,7 +14,7 @@ import org.springframework.web.bind.annotation.*;
  * @date 2018/10/2
  */
 @RestController
-@RequestMapping("/api/account/company")
+@RequestMapping("/company")
 public class CompanyController {
 
     @Autowired

+ 0 - 15
base-servers/account/account-server/src/main/resources/banner.txt

@@ -1,15 +0,0 @@
-${AnsiColor.BRIGHT_YELLOW}
-
-88        88   ad88888ba     ,ad8888ba,    88888888888  888888888888  ,ad8888ba,   88        88  88  888b      88         db
-88        88  d8"     "8b   d8"'    `"8b   88                88      d8"'    `"8b  88        88  88  8888b     88        d88b
-88        88  Y8,          d8'        `8b  88                88     d8'            88        88  88  88 `8b    88       d8'`8b
-88        88  `Y8aaaaa,    88          88  88aaaaa           88     88             88aaaaaaaa88  88  88  `8b   88      d8'  `8b
-88        88    `"""""8b,  88          88  88"""""           88     88             88""""""""88  88  88   `8b  88     d8YaaaaY8b
-88        88          `8b  Y8,        ,8P  88                88     Y8,            88        88  88  88    `8b 88    d8""""""""8b
-Y8a.    .a8P  Y8a     a8P   Y8a.    .a8P   88                88      Y8a.    .a8P  88        88  88  88     `8888   d8'        `8b
- `"Y8888Y"'    "Y88888P"     `"Y8888Y"'    88                88       `"Y8888Y"'   88        88  88  88      `888  d8'          `8b
-
-
-Application Version: ${application.version}${application.formatted-version}
-Spring Boot Version: ${spring-boot.version}${spring-boot.formatted-version}
-${AnsiColor.DEFAULT}

+ 3 - 0
base-servers/admin-server/src/main/resources/config/application-docker.yml

@@ -1,4 +1,7 @@
 eureka:
+  instance:
+    hostname: saas-admin-server
+    prefer-ip-address: false
   client:
     serviceUrl:
       defaultZone: http://${spring.security.user.name}:${spring.security.user.password}@saas-eureka-server:8500/eureka/

+ 2 - 29
base-servers/auth/auth-server/pom.xml

@@ -21,40 +21,13 @@
             <groupId>com.usoftchina.saas</groupId>
             <artifactId>auth-dto</artifactId>
         </dependency>
-        <dependency>
-            <groupId>com.usoftchina.saas</groupId>
-            <artifactId>core</artifactId>
-        </dependency>
         <dependency>
             <groupId>com.usoftchina.saas</groupId>
             <artifactId>account-api</artifactId>
         </dependency>
         <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-web</artifactId>
-            <exclusions>
-                <exclusion>
-                    <groupId>org.hibernate</groupId>
-                    <artifactId>hibernate-validator</artifactId>
-                </exclusion>
-                <exclusion>
-                    <groupId>org.apache.tomcat.embed</groupId>
-                    <artifactId>tomcat-embed-websocket</artifactId>
-                </exclusion>
-            </exclusions>
-        </dependency>
-        <!-- management -->
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-actuator</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-security</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.springframework.cloud</groupId>
-            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
+            <groupId>com.usoftchina.saas</groupId>
+            <artifactId>server-starter</artifactId>
         </dependency>
         <!-- db -->
         <dependency>

+ 2 - 0
base-servers/auth/auth-server/src/main/java/com/usoftchina/saas/auth/AuthApplication.java

@@ -3,6 +3,7 @@ package com.usoftchina.saas.auth;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
+import org.springframework.cloud.openfeign.EnableFeignClients;
 
 /**
  * @author yingp
@@ -10,6 +11,7 @@ import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
  */
 @SpringBootApplication
 @EnableEurekaClient
+@EnableFeignClients(basePackages = "com.usoftchina.saas.account.api")
 public class AuthApplication {
     public static void main(String[] args) {
         SpringApplication.run(AuthApplication.class, args);

+ 0 - 15
base-servers/auth/auth-server/src/main/resources/banner.txt

@@ -1,15 +0,0 @@
-${AnsiColor.BRIGHT_YELLOW}
-
-88        88   ad88888ba     ,ad8888ba,    88888888888  888888888888  ,ad8888ba,   88        88  88  888b      88         db
-88        88  d8"     "8b   d8"'    `"8b   88                88      d8"'    `"8b  88        88  88  8888b     88        d88b
-88        88  Y8,          d8'        `8b  88                88     d8'            88        88  88  88 `8b    88       d8'`8b
-88        88  `Y8aaaaa,    88          88  88aaaaa           88     88             88aaaaaaaa88  88  88  `8b   88      d8'  `8b
-88        88    `"""""8b,  88          88  88"""""           88     88             88""""""""88  88  88   `8b  88     d8YaaaaY8b
-88        88          `8b  Y8,        ,8P  88                88     Y8,            88        88  88  88    `8b 88    d8""""""""8b
-Y8a.    .a8P  Y8a     a8P   Y8a.    .a8P   88                88      Y8a.    .a8P  88        88  88  88     `8888   d8'        `8b
- `"Y8888Y"'    "Y88888P"     `"Y8888Y"'    88                88       `"Y8888Y"'   88        88  88  88      `888  d8'          `8b
-
-
-Application Version: ${application.version}${application.formatted-version}
-Spring Boot Version: ${spring-boot.version}${spring-boot.formatted-version}
-${AnsiColor.DEFAULT}

+ 12 - 14
base-servers/gateway-server/pom.xml

@@ -16,43 +16,41 @@
     </properties>
 
     <dependencies>
-        <!-- management -->
         <dependency>
-            <groupId>org.springframework.cloud</groupId>
-            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-actuator</artifactId>
+            <groupId>com.usoftchina.saas</groupId>
+            <artifactId>server-starter</artifactId>
         </dependency>
-        <dependency>
+        <!--<dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-security</artifactId>
+        </dependency>-->
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-gateway</artifactId>
         </dependency>
-        <!-- zuul -->
         <dependency>
             <groupId>org.springframework.cloud</groupId>
-            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
+            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
         </dependency>
         <!-- rate limit -->
-        <dependency>
+        <!--<dependency>
             <groupId>com.marcosbarbero.cloud</groupId>
             <artifactId>spring-cloud-zuul-ratelimit</artifactId>
             <version>${ratelimit.version}</version>
-        </dependency>
+        </dependency>-->
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-data-redis</artifactId>
         </dependency>
         <!-- sleuth -->
-        <dependency>
+        <!--<dependency>
             <groupId>org.springframework.cloud</groupId>
             <artifactId>spring-cloud-starter-zipkin</artifactId>
         </dependency>
         <dependency>
             <groupId>org.springframework.amqp</groupId>
             <artifactId>spring-rabbit</artifactId>
-        </dependency>
+        </dependency>-->
         <!-- auth -->
         <dependency>
             <groupId>com.usoftchina.saas</groupId>

+ 0 - 2
base-servers/gateway-server/src/main/java/com/usoftchina/saas/gateway/GatewayApplication.java

@@ -3,7 +3,6 @@ package com.usoftchina.saas.gateway;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
-import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
 import org.springframework.cloud.openfeign.EnableFeignClients;
 
 /**
@@ -11,7 +10,6 @@ import org.springframework.cloud.openfeign.EnableFeignClients;
  * @date 2018/9/30
  */
 @SpringBootApplication
-@EnableZuulProxy
 @EnableEurekaClient
 @EnableFeignClients(basePackages = "com.usoftchina.saas.auth.api")
 public class GatewayApplication {

+ 0 - 103
base-servers/gateway-server/src/main/java/com/usoftchina/saas/gateway/config/AccessFilter.java

@@ -1,103 +0,0 @@
-package com.usoftchina.saas.gateway.config;
-
-import com.netflix.zuul.ZuulFilter;
-import com.netflix.zuul.context.RequestContext;
-import com.netflix.zuul.exception.ZuulException;
-import com.usoftchina.saas.auth.api.AuthApi;
-import com.usoftchina.saas.auth.common.jwt.JwtHelper;
-import com.usoftchina.saas.auth.common.jwt.JwtInfo;
-import com.usoftchina.saas.base.Result;
-import com.usoftchina.saas.exception.BizException;
-import com.usoftchina.saas.exception.ExceptionCode;
-import com.usoftchina.saas.utils.JsonUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.http.MediaType;
-import org.springframework.util.CollectionUtils;
-import org.springframework.util.StringUtils;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-/**
- * @author yingp
- * @date 2018/9/30
- */
-public class AccessFilter extends ZuulFilter {
-
-    @Autowired
-    private AuthApi authApi;
-
-    @Autowired
-    private AuthConfig authConfig;
-
-    @Override
-    public String filterType() {
-        return "pre";
-    }
-
-    @Override
-    public int filterOrder() {
-        return 1;
-    }
-
-    @Override
-    public boolean shouldFilter() {
-        return true;
-    }
-
-    /**
-     * 具体过滤逻辑
-     *
-     * @return
-     * @throws ZuulException
-     */
-    @Override
-    public Object run() throws ZuulException {
-        RequestContext ctx = RequestContext.getCurrentContext();
-        HttpServletRequest request = ctx.getRequest();
-        if (isIgnore(request.getRequestURI())) {
-            return null;
-        }
-        String token = request.getHeader(authConfig.getAuthHeader());
-        if (StringUtils.isEmpty(token)) {
-            setFailedRequest(ctx, 401, Result.error(ExceptionCode.JWT_ILLEGAL_ARGUMENT));
-            return null;
-        }
-        try {
-            JwtInfo infoFromToken = JwtHelper.getInfoFromToken(token, authConfig.getPublicKey());
-            // TODO resource + role
-        } catch (BizException e) {
-            setFailedRequest(ctx, 401, Result.error(e));
-            return null;
-        }
-        return null;
-    }
-
-    private boolean isIgnore(String requestUri) {
-        if (!CollectionUtils.isEmpty(authConfig.getIgnores())) {
-            for (String ignore : authConfig.getIgnores()) {
-                if (requestUri.startsWith(ignore)) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    /**
-     * 异常请求
-     *
-     * @param ctx
-     * @param code
-     * @param result
-     */
-    public void setFailedRequest(RequestContext ctx, int code, Result result) {
-        ctx.setSendZuulResponse(false);
-        HttpServletResponse httpResponse = ctx.getResponse();
-        httpResponse.setCharacterEncoding("UTF-8");
-        httpResponse.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
-        httpResponse.setStatus(code);
-        ctx.setResponseBody(JsonUtils.toJsonString(result));
-        ctx.setResponse(httpResponse);
-    }
-}

+ 25 - 0
base-servers/gateway-server/src/main/java/com/usoftchina/saas/gateway/config/AuthFilter.java

@@ -0,0 +1,25 @@
+package com.usoftchina.saas.gateway.config;
+
+import org.springframework.cloud.gateway.filter.GatewayFilterChain;
+import org.springframework.cloud.gateway.filter.GlobalFilter;
+import org.springframework.core.Ordered;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Mono;
+
+/**
+ * 全局过滤器鉴权
+ *
+ * @author yingp
+ * @date 2018/10/13
+ */
+public class AuthFilter implements GlobalFilter, Ordered {
+    @Override
+    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
+        return chain.filter(exchange);
+    }
+
+    @Override
+    public int getOrder() {
+        return -100;
+    }
+}

+ 65 - 0
base-servers/gateway-server/src/main/java/com/usoftchina/saas/gateway/config/ErrorConfig.java

@@ -0,0 +1,65 @@
+package com.usoftchina.saas.gateway.config;
+
+import com.usoftchina.saas.gateway.error.MyExceptionHandler;
+import org.springframework.beans.factory.ObjectProvider;
+import org.springframework.boot.autoconfigure.web.ResourceProperties;
+import org.springframework.boot.autoconfigure.web.ServerProperties;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.boot.web.reactive.error.ErrorAttributes;
+import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.Ordered;
+import org.springframework.core.annotation.Order;
+import org.springframework.http.codec.ServerCodecConfigurer;
+import org.springframework.web.reactive.result.view.ViewResolver;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author yingp
+ * @date 2018/10/13
+ */
+@Configuration
+@EnableConfigurationProperties({ServerProperties.class, ResourceProperties.class})
+public class ErrorConfig {
+
+    private final ServerProperties serverProperties;
+
+    private final ApplicationContext applicationContext;
+
+    private final ResourceProperties resourceProperties;
+
+    private final List<ViewResolver> viewResolvers;
+
+    private final ServerCodecConfigurer serverCodecConfigurer;
+
+    public ErrorConfig(ServerProperties serverProperties,
+                                     ResourceProperties resourceProperties,
+                                     ObjectProvider<List<ViewResolver>> viewResolversProvider,
+                                     ServerCodecConfigurer serverCodecConfigurer,
+                                     ApplicationContext applicationContext) {
+        this.serverProperties = serverProperties;
+        this.applicationContext = applicationContext;
+        this.resourceProperties = resourceProperties;
+        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
+        this.serverCodecConfigurer = serverCodecConfigurer;
+    }
+
+
+    @Bean
+    @Order(Ordered.HIGHEST_PRECEDENCE)
+    public ErrorWebExceptionHandler errorWebExceptionHandler(ErrorAttributes errorAttributes) {
+        MyExceptionHandler exceptionHandler = new MyExceptionHandler(
+                errorAttributes,
+                this.resourceProperties,
+                this.serverProperties.getError(),
+                this.applicationContext);
+        exceptionHandler.setViewResolvers(this.viewResolvers);
+        exceptionHandler.setMessageWriters(this.serverCodecConfigurer.getWriters());
+        exceptionHandler.setMessageReaders(this.serverCodecConfigurer.getReaders());
+        return exceptionHandler;
+    }
+}

+ 0 - 18
base-servers/gateway-server/src/main/java/com/usoftchina/saas/gateway/config/SecurityConfig.java

@@ -1,18 +0,0 @@
-package com.usoftchina.saas.gateway.config;
-
-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;
-
-/**
- * @author yingp
- * @date 2018/10/10
- */
-@EnableWebSecurity
-public class SecurityConfig extends WebSecurityConfigurerAdapter {
-
-    @Override
-    public void configure(WebSecurity web) throws Exception {
-        web.ignoring().antMatchers("/api/**");
-    }
-}

+ 49 - 0
base-servers/gateway-server/src/main/java/com/usoftchina/saas/gateway/config/WebConfig.java

@@ -0,0 +1,49 @@
+package com.usoftchina.saas.gateway.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.http.server.reactive.ServerHttpResponse;
+import org.springframework.web.cors.reactive.CorsUtils;
+import org.springframework.web.server.ServerWebExchange;
+import org.springframework.web.server.WebFilter;
+import org.springframework.web.server.WebFilterChain;
+import reactor.core.publisher.Mono;
+
+/**
+ * @author yingp
+ * @date 2018/10/13
+ */
+@Configuration
+public class WebConfig {
+
+    @Bean
+    public WebFilter corsFilter() {
+        return (ServerWebExchange ctx, WebFilterChain chain) -> {
+            ServerHttpRequest request = ctx.getRequest();
+            if (!CorsUtils.isCorsRequest(request)) {
+                return chain.filter(ctx);
+            }
+            HttpHeaders requestHeaders = request.getHeaders();
+            ServerHttpResponse response = ctx.getResponse();
+            HttpMethod requestMethod = requestHeaders.getAccessControlRequestMethod();
+            HttpHeaders headers = response.getHeaders();
+            headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, requestHeaders.getOrigin());
+            headers.addAll(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, requestHeaders.getAccessControlRequestHeaders());
+            if (requestMethod != null) {
+                headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, requestMethod.name());
+            }
+            headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
+            headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, "*");
+            headers.add(HttpHeaders.ACCESS_CONTROL_MAX_AGE, "18000L");
+            if (request.getMethod() == HttpMethod.OPTIONS) {
+                response.setStatusCode(HttpStatus.OK);
+                return Mono.empty();
+            }
+            return chain.filter(ctx);
+        };
+    }
+}

+ 0 - 47
base-servers/gateway-server/src/main/java/com/usoftchina/saas/gateway/config/ZuulConfig.java

@@ -1,47 +0,0 @@
-package com.usoftchina.saas.gateway.config;
-
-import org.springframework.boot.context.properties.EnableConfigurationProperties;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.web.cors.CorsConfiguration;
-import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
-import org.springframework.web.filter.CorsFilter;
-
-/**
- * @author yingp
- * @date 2018/9/30
- */
-@Configuration
-@EnableConfigurationProperties(AuthConfig.class)
-public class ZuulConfig {
-
-    @Bean
-    public CorsFilter corsFilter() {
-        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
-        CorsConfiguration config = new CorsConfiguration();
-        // 允许cookies跨域
-        config.setAllowCredentials(true);
-        // 允许向该服务器提交请求的URI,*表示全部允许,在SpringMVC中,如果设成*,会自动转成当前请求头中的Origin
-        config.addAllowedOrigin("*");
-        // 允许访问的头信息,*表示全部
-        config.addAllowedHeader("*");
-        // 预检请求的缓存时间(秒),即在这个时间段里,对于相同的跨域请求不会再预检了
-        config.setMaxAge(18000L);
-        // 允许提交请求的方法,*表示全部允许
-        config.addAllowedMethod("OPTIONS");
-        config.addAllowedMethod("HEAD");
-        // 允许Get的请求方法
-        config.addAllowedMethod("GET");
-        config.addAllowedMethod("PUT");
-        config.addAllowedMethod("POST");
-        config.addAllowedMethod("DELETE");
-        config.addAllowedMethod("PATCH");
-        source.registerCorsConfiguration("/**", config);
-        return new CorsFilter(source);
-    }
-
-    @Bean
-    public AccessFilter accessFilter() {
-        return new AccessFilter();
-    }
-}

+ 99 - 0
base-servers/gateway-server/src/main/java/com/usoftchina/saas/gateway/error/MyExceptionHandler.java

@@ -0,0 +1,99 @@
+package com.usoftchina.saas.gateway.error;
+
+import org.springframework.boot.autoconfigure.web.ErrorProperties;
+import org.springframework.boot.autoconfigure.web.ResourceProperties;
+import org.springframework.boot.autoconfigure.web.reactive.error.DefaultErrorWebExceptionHandler;
+import org.springframework.boot.web.reactive.error.ErrorAttributes;
+import org.springframework.cloud.gateway.support.NotFoundException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.reactive.function.server.*;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 自定义异常处理
+ *
+ * @author yingp
+ * @date 2018/10/13
+ */
+public class MyExceptionHandler extends DefaultErrorWebExceptionHandler {
+
+    public MyExceptionHandler(ErrorAttributes errorAttributes, ResourceProperties resourceProperties,
+                              ErrorProperties errorProperties, ApplicationContext applicationContext) {
+        super(errorAttributes, resourceProperties, errorProperties, applicationContext);
+    }
+
+    /**
+     * 获取异常属性
+     */
+    @Override
+    protected Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace) {
+        int code = 500;
+        Throwable error = super.getError(request);
+        if (error instanceof NotFoundException) {
+            code = 404;
+        }
+        return response(code, this.buildMessage(request, error));
+    }
+
+    /**
+     * 构建返回的JSON数据格式
+     *
+     * @param status  状态码
+     * @param message 异常信息
+     * @return
+     */
+    private Map<String, Object> response(int status, String message) {
+        Map<String, Object> map = new HashMap<>(4);
+        map.put("success", false);
+        map.put("code", status);
+        map.put("message", message);
+        map.put("data", null);
+        return map;
+
+    }
+
+    /**
+     * 构建异常信息
+     *
+     * @param request
+     * @param ex
+     * @return
+     */
+    private String buildMessage(ServerRequest request, Throwable ex) {
+        StringBuilder message = new StringBuilder("Failed to handle request [");
+        message.append(request.methodName());
+        message.append(" ");
+        message.append(request.uri());
+        message.append("]");
+        if (ex != null) {
+            message.append(": ");
+            message.append(ex.getMessage());
+        }
+        return message.toString();
+    }
+
+    /**
+     * 根据code获取对应的HttpStatus
+     *
+     * @param errorAttributes
+     */
+    @Override
+    protected HttpStatus getHttpStatus(Map<String, Object> errorAttributes) {
+        int statusCode = (int) errorAttributes.get("code");
+        return HttpStatus.valueOf(statusCode);
+    }
+
+    /**
+     * 指定响应处理方法为JSON处理的方法
+     *
+     * @param errorAttributes
+     */
+    @Override
+    protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
+        return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
+    }
+
+}

+ 103 - 39
base-servers/gateway-server/src/main/resources/application.yml

@@ -17,9 +17,87 @@ spring:
   zipkin:
     sender:
       type: rabbit
+    service:
+      name: ${spring.application.name}
   sleuth:
     sampler:
-      percentage: 1.0
+      percentage: 1
+    integration:
+      enabled: false
+    scheduled:
+      skip-pattern: "^org.*HystrixStreamTask$"
+  cloud:
+    gateway:
+      discovery:
+        locator:
+          enabled: true
+      routes:
+      - id: UI-SERVER
+        uri: lb://UI-SERVER
+        predicates:
+        - Path=/api/ui/**
+        filters:
+        - RewritePath=/api/ui/(?<segment>.*), /$\{segment}
+      - id: ACCOUNT-SERVER
+        uri: lb://ACCOUNT-SERVER
+        predicates:
+        - Path=/api/account/**
+        filters:
+        - RewritePath=/api/account/(?<segment>.*), /$\{segment}
+      - id: AUTH-SERVER
+        uri: lb://AUTH-SERVER
+        predicates:
+        - Path=/api/auth/**
+        filters:
+        - RewritePath=/api/auth/(?<segment>.*), /$\{segment}
+      - id: FILE-SERVER
+        uri: lb://FILE-SERVER
+        predicates:
+        - Path=/api/file/**
+        filters:
+        - RewritePath=/api/file/(?<segment>.*), /$\{segment}
+      - id: MAIL-SERVER
+        uri: lb://MAIL-SERVER
+        predicates:
+        - Path=/api/mail/**
+        filters:
+        - RewritePath=/api/mail/(?<segment>.*), /$\{segment}
+      - id: SMS-SERVER
+        uri: lb://SMS-SERVER
+        predicates:
+        - Path=/api/sms/**
+        filters:
+        - RewritePath=/api/sms/(?<segment>.*), /$\{segment}
+      - id: PURCHASE-SERVER
+        uri: lb://PURCHASE-SERVER
+        predicates:
+        - Path=/api/purchase/**
+        filters:
+        - RewritePath=/api/purchase/(?<segment>.*), /$\{segment}
+      - id: SALE-SERVER
+        uri: lb://SALE-SERVER
+        predicates:
+        - Path=/api/sale/**
+        filters:
+        - RewritePath=/api/sale/(?<segment>.*), /$\{segment}
+      - id: STORAGE-SERVER
+        uri: lb://STORAGE-SERVER
+        predicates:
+        - Path=/api/storage/**
+        filters:
+        - RewritePath=/api/storage/(?<segment>.*), /$\{segment}
+      - id: DOCUMENT-SERVER
+        uri: lb://DOCUMENT-SERVER
+        predicates:
+        - Path=/api/document/**
+        filters:
+        - RewritePath=/api/document/(?<segment>.*), /$\{segment}
+      - id: MONEY-SERVER
+        uri: lb://MONEY-SERVER
+        predicates:
+        - Path=/api/money/**
+        filters:
+        - RewritePath=/api/money/(?<segment>.*), /$\{segment}
 server:
   port: 8560
   tomcat:
@@ -38,6 +116,8 @@ eureka:
     serviceUrl:
       defaultZone: http://${spring.security.user.name}:${spring.security.user.password}@192.168.0.181:8500/eureka/
 feign:
+  hystrix:
+    enabled: true
   compression:
     request:
       enabled: true
@@ -45,44 +125,21 @@ feign:
       min-request-size: 2048
     response:
       enabled: true
-zuul:
-  ignored-services: "*"   # 忽略eureka上的所有服务
-  sensitive-headers:      # 一些比较敏感的请求头,不想通过zuul传递过去, 可以通过该属性进行设置
-  prefix: /api            # 公共的前缀
-  routes:  # 路由配置
-    ui:
-      path: /ui/**
-      serviceId: ui-server
-    account:
-      path: /account/**
-      serviceId: account-server
-    auth:
-      path: /auth/**
-      serviceId: auth-server
-    file:
-      path: /file/**
-      serviceId: file-server
-    mail:
-      path: /mail/**
-      serviceId: mail-server
-    sms:
-      path: /sms/**
-      serviceId: sms-server
-    purchase:
-      path: /purchase/**
-      serviceId: purchase-server
-    sale:
-      path: /sale/**
-      serviceId: sale-server
-    storage:
-      path: /storage/**
-      serviceId: storage-server
-    document:
-      path: /document/**
-      serviceId: document-server
-    money:
-      path: /money/**
-      serviceId: money-server                       
+hystrix:
+  command:
+    default:
+      execution:
+        isolation:
+          thread:
+            timeoutInMilliseconds: 3000
+ribbon:
+  eureka:
+    enabled: true
+  ReadTimeout: 60000
+  ConnectTimeout: 60000
+  MaxAutoRetries: 0
+  MaxAutoRetriesNextServer: 1
+  OkToRetryOnAllOperations: false
 info:
   name: '@project.artifactId@'
   description: '@project.description@'
@@ -95,3 +152,10 @@ auth:
     - /api/auth
     # 忽略全部
     - /
+logging:
+  level:
+    org.springframework.cloud.gateway: debug
+    org.springframework.http.server.reactive: debug
+    org.springframework.web.reactive: debug
+    org.springframework.security: debug
+    reactor.ipc.netty: debug

+ 0 - 15
base-servers/gateway-server/src/main/resources/banner.txt

@@ -1,15 +0,0 @@
-${AnsiColor.BRIGHT_YELLOW}
-
-88        88   ad88888ba     ,ad8888ba,    88888888888  888888888888  ,ad8888ba,   88        88  88  888b      88         db
-88        88  d8"     "8b   d8"'    `"8b   88                88      d8"'    `"8b  88        88  88  8888b     88        d88b
-88        88  Y8,          d8'        `8b  88                88     d8'            88        88  88  88 `8b    88       d8'`8b
-88        88  `Y8aaaaa,    88          88  88aaaaa           88     88             88aaaaaaaa88  88  88  `8b   88      d8'  `8b
-88        88    `"""""8b,  88          88  88"""""           88     88             88""""""""88  88  88   `8b  88     d8YaaaaY8b
-88        88          `8b  Y8,        ,8P  88                88     Y8,            88        88  88  88    `8b 88    d8""""""""8b
-Y8a.    .a8P  Y8a     a8P   Y8a.    .a8P   88                88      Y8a.    .a8P  88        88  88  88     `8888   d8'        `8b
- `"Y8888Y"'    "Y88888P"     `"Y8888Y"'    88                88       `"Y8888Y"'   88        88  88  88      `888  d8'          `8b
-
-
-Application Version: ${application.version}${application.formatted-version}
-Spring Boot Version: ${spring-boot.version}${spring-boot.formatted-version}
-${AnsiColor.DEFAULT}

+ 3 - 0
base-servers/gateway-server/src/main/resources/config/application-docker.yml

@@ -1,4 +1,7 @@
 eureka:
+  instance:
+    hostname: saas-gateway-server
+    prefer-ip-address: false
   client:
     serviceUrl:
       defaultZone: http://${spring.security.user.name}:${spring.security.user.password}@saas-eureka-server:8500/eureka/

+ 4 - 11
base-servers/ui-server/pom.xml

@@ -15,38 +15,31 @@
     <dependencies>
         <dependency>
             <groupId>com.usoftchina.saas</groupId>
-            <artifactId>core</artifactId>
+            <artifactId>server-starter</artifactId>
         </dependency>
         <dependency>
             <groupId>com.usoftchina.saas</groupId>
             <artifactId>auth-client</artifactId>
         </dependency>
-        <dependency>
-            <groupId>org.springframework.cloud</groupId>
-            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-actuator</artifactId>
-        </dependency>
         <!-- db -->
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-data-mongodb</artifactId>
         </dependency>
         <!-- sleuth -->
-        <dependency>
+        <!--<dependency>
             <groupId>org.springframework.cloud</groupId>
             <artifactId>spring-cloud-starter-zipkin</artifactId>
         </dependency>
         <dependency>
             <groupId>org.springframework.amqp</groupId>
             <artifactId>spring-rabbit</artifactId>
-        </dependency>
+        </dependency>-->
 
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
         </dependency>
     </dependencies>
     <build>

+ 15 - 10
base-servers/ui-server/src/main/java/com/usoftchina/saas/ui/controller/co/CoViewController.java

@@ -1,6 +1,7 @@
 package com.usoftchina.saas.ui.controller.co;
 
 import com.usoftchina.saas.base.Result;
+import com.usoftchina.saas.context.BaseContextHolder;
 import com.usoftchina.saas.ui.service.ViewService;
 import com.usoftchina.saas.ui.service.co.CoViewService;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -26,20 +27,24 @@ public class CoViewController {
      * 获取企业视图配置
      *
      * @param name
-     * @param refresh 是否强制刷新
+     * @param refresh    是否强制刷新
+     * @param useDefault 当自定义配置为空时,是否返回标准配置
      * @return
      */
     @GetMapping("/config")
-    public Result<Object> getViewConfig(@RequestParam String name, Boolean refresh) {
-//        if (null != refresh && refresh) {
-//            coViewService.cacheEvict(name);
-//            viewService.cacheEvict(name);
-//        }
-        coViewService.cacheEvict(name);// 暂时强制刷新
-        viewService.cacheEvict(name);
-
+    public Result<Object> getViewConfig(@RequestParam String name,
+                                        @RequestParam(required = false, defaultValue = "true") Boolean refresh,
+                                        @RequestParam(required = false, defaultValue = "false") Boolean useDefault) {
+        if (null != refresh && refresh) {
+            coViewService.cacheEvict(name);
+            viewService.cacheEvict(name);
+        }
+        // TODO 开发默认设置,及时去掉
+        if (null == BaseContextHolder.getCompanyId()) {
+            BaseContextHolder.setCompanyId(0);
+        }
         Object config = coViewService.getDeepConfig(name);
-        if (null == config) {
+        if (null == config && null != useDefault && useDefault) {
             // 企业配置不存在则取标准配置
             config = viewService.getDeepConfig(name);
         }

+ 2 - 0
base-servers/ui-server/src/main/resources/application.yml

@@ -13,6 +13,8 @@ spring:
   zipkin:
     sender:
       type: rabbit
+    service:
+      name: ${spring.application.name}
   sleuth:
     sampler:
       percentage: 1.0

+ 0 - 15
base-servers/ui-server/src/main/resources/banner.txt

@@ -1,15 +0,0 @@
-${AnsiColor.BRIGHT_YELLOW}
-
-88        88   ad88888ba     ,ad8888ba,    88888888888  888888888888  ,ad8888ba,   88        88  88  888b      88         db
-88        88  d8"     "8b   d8"'    `"8b   88                88      d8"'    `"8b  88        88  88  8888b     88        d88b
-88        88  Y8,          d8'        `8b  88                88     d8'            88        88  88  88 `8b    88       d8'`8b
-88        88  `Y8aaaaa,    88          88  88aaaaa           88     88             88aaaaaaaa88  88  88  `8b   88      d8'  `8b
-88        88    `"""""8b,  88          88  88"""""           88     88             88""""""""88  88  88   `8b  88     d8YaaaaY8b
-88        88          `8b  Y8,        ,8P  88                88     Y8,            88        88  88  88    `8b 88    d8""""""""8b
-Y8a.    .a8P  Y8a     a8P   Y8a.    .a8P   88                88      Y8a.    .a8P  88        88  88  88     `8888   d8'        `8b
- `"Y8888Y"'    "Y88888P"     `"Y8888Y"'    88                88       `"Y8888Y"'   88        88  88  88      `888  d8'          `8b
-
-
-Application Version: ${application.version}${application.formatted-version}
-Spring Boot Version: ${spring-boot.version}${spring-boot.formatted-version}
-${AnsiColor.DEFAULT}

+ 3 - 0
base-servers/zipkin-server/src/main/resources/config/application-docker.yml

@@ -1,4 +1,7 @@
 eureka:
+  instance:
+    hostname: saas-zipkin-server
+    prefer-ip-address: false
   client:
     serviceUrl:
       defaultZone: http://${spring.security.user.name}:${spring.security.user.password}@saas-eureka-server:8500/eureka/

+ 1 - 0
framework/pom.xml

@@ -14,6 +14,7 @@
     <description>saas framework</description>
     <modules>
         <module>core</module>
+        <module>server-starter</module>
     </modules>
 
 </project>

+ 44 - 0
framework/server-starter/pom.xml

@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>framework</artifactId>
+        <groupId>com.usoftchina.saas</groupId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>server-starter</artifactId>
+    <description>server starter</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.usoftchina.saas</groupId>
+            <artifactId>core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-actuator</artifactId>
+        </dependency>
+<!--        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-security</artifactId>
+        </dependency>-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+            <scope>compile</scope>
+            <optional>true</optional>
+        </dependency>
+    </dependencies>
+
+</project>

+ 14 - 0
framework/server-starter/src/main/java/com/usoftchina/saas/server/ServerAutoConfiguration.java

@@ -0,0 +1,14 @@
+package com.usoftchina.saas.server;
+
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @author yingp
+ * @date 2018/10/13
+ */
+@Configuration
+@ComponentScan(basePackages = {"com.usoftchina.saas.server"})
+public class ServerAutoConfiguration {
+    
+}

+ 16 - 0
framework/server-starter/src/main/java/com/usoftchina/saas/server/context/SpringContextListener.java

@@ -0,0 +1,16 @@
+package com.usoftchina.saas.server.context;
+
+import com.usoftchina.saas.context.SpringContextHolder;
+import org.springframework.context.ApplicationListener;
+import org.springframework.context.event.ContextRefreshedEvent;
+
+/**
+ * @author yingp
+ * @date 2018/7/18
+ */
+public class SpringContextListener implements ApplicationListener<ContextRefreshedEvent> {
+    @Override
+    public void onApplicationEvent(ContextRefreshedEvent event) {
+        SpringContextHolder.setContext(event.getApplicationContext());
+    }
+}

+ 41 - 0
framework/server-starter/src/main/java/com/usoftchina/saas/server/error/MyErrorController.java

@@ -0,0 +1,41 @@
+package com.usoftchina.saas.server.error;
+
+import org.springframework.boot.autoconfigure.web.ServerProperties;
+import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController;
+import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Controller;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author yingp
+ * @date 2018/10/13
+ */
+@Controller
+public class MyErrorController extends BasicErrorController {
+
+    public MyErrorController(ServerProperties serverProperties) {
+        super(new DefaultErrorAttributes(), serverProperties.getError());
+    }
+
+    /**
+     * 覆盖默认的错误响应
+     */
+    @Override
+    public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
+        Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL));
+        HttpStatus status = getStatus(request);
+        // 输出自定义格式
+        Map<String, Object> map = new HashMap<>(4);
+        map.put("success", false);
+        map.put("code", body.get("status"));
+        map.put("message", body.get("message"));
+        map.put("data", null);
+        return new ResponseEntity<>(map, status);
+    }
+}

+ 7 - 0
framework/server-starter/src/main/resources/META-INF/spring.factories

@@ -0,0 +1,7 @@
+# Auto Configuration
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+com.usoftchina.saas.server.ServerAutoConfiguration
+
+# Application Listeners
+org.springframework.context.ApplicationListener=\
+com.usoftchina.saas.server.context.SpringContextListener

BIN
framework/server-starter/src/main/resources/auth/pub.key


+ 0 - 0
applications/purchase/purchase-server/src/main/resources/banner.txt → framework/server-starter/src/main/resources/banner.txt


+ 1 - 9
frontend/saas-web/app/util/FormUtil.js

@@ -25,15 +25,7 @@ Ext.define('saas.util.FormUtil', {
                 var config = res.data, items = [];
                 if(config) {
                     items = config.items,
-                    bindFields = items.filter(function(item) {
-                        return !!item.bind;
-                    }).map(function(item) {
-                        return item.bind.replace(/[{ | }]/g, '');
-                    }),
-                    viewModel = form.getViewModel();
-    
-                    form.bindFields = bindFields;
-                    form.add(items);
+                    form.addItems(items);
                 }
 
                 form.fireEvent('afterSetItems', form, items);

+ 15 - 5
frontend/saas-web/app/view/core/dbfind/DbfindTrigger.js

@@ -14,7 +14,7 @@ Ext.define('saas.view.core.dbfind.DbfindTrigger', {
         var me = this;
         this.displayField='display';	   
 		this.valueField='value';
-		this.queryMode='local';
+        this.queryMode='local';
 		if(me.value){
 			me.store=Ext.create('Ext.data.Store', {
 				   fields: ['display','value'],
@@ -23,7 +23,7 @@ Ext.define('saas.view.core.dbfind.DbfindTrigger', {
 					   'value':me.value
 				   }]
 			});
-		}
+        }
         this.callParent();
     },
     //输入值之后进行模糊查询
@@ -108,7 +108,7 @@ Ext.define('saas.view.core.dbfind.DbfindTrigger', {
     },
     onTriggerClick:function(f){
         //判断dbfindtrigger归属
-        f.judge(f);
+        var belong = f.judge(f);
         var win = Ext.create('Ext.window.Window', {   
             trigger:f,
             belong:f.ownerCt,  
@@ -121,7 +121,7 @@ Ext.define('saas.view.core.dbfind.DbfindTrigger', {
             constrain: true,
             closable: true,
             layout:'fit',
-            renderTo:f.ownerCt.getEl(),
+            renderTo:Ext.getCmp('main-tab-panel').getActiveTab().getEl(),
             items:[{
                 xtype:'dbfindgridpanel',
                 configUrl: f.configUrl,
@@ -133,10 +133,14 @@ Ext.define('saas.view.core.dbfind.DbfindTrigger', {
     },
 
     judge:function(f){
-        if(f.ownerCt.xtype.indexOf('FormPanel')>-1){
+        if(f.ownerCt.xtype.trim().toUpperCase().indexOf('FORMPANEL')>-1){
             f.belong = 'form';
             return 'form'
         }
+        else if(f.column){
+            f.belong = 'grid';
+            return 'grid'
+        }
     },
 
     listeners: {
@@ -160,6 +164,12 @@ Ext.define('saas.view.core.dbfind.DbfindTrigger', {
                 }
             }
         }
+    },
+
+    getValue: function(f) {
+        var me = this,val = me.rawToValue(me.processRawValue(me.getRawValue()));
+        me.value = val;
+        return val;
     }
 
 });

+ 32 - 0
frontend/saas-web/app/view/core/form/DateField.js

@@ -0,0 +1,32 @@
+Ext.define('saas.view.core.form.DateField', {
+    extend: 'Ext.form.field.Date',
+    alias: 'widget.datetimefield',
+
+    setValue: function(v) {
+        var me = this,
+            utilDate = Ext.Date,
+            rawDate;
+        me.lastValue = me.rawDateText;
+        me.lastDate = me.rawDate;
+        if (Ext.isDate(v)) {
+            rawDate = me.rawDate = v;
+            me.rawDateText = me.formatDate(v);
+        } else {
+            rawDate = me.rawDate = me.rawToValue(v);
+            me.rawDateText = me.formatDate(v);
+            if (rawDate === v) {
+                if(v&&v.indexOf('.')>-1&&v.indexOf('T')>-1){
+                     v = v.substring(0,v.indexOf('T'));
+                    rawDate = null;
+                }else{
+                    rawDate = me.rawDate = null;
+                    me.rawDateText = '';
+                }     
+            }
+        }
+        if (rawDate && !utilDate.formatContainsHourInfo(me.format)) {
+            me.rawDate = utilDate.clearTime(rawDate, true);
+        }
+        me.callParent(arguments);
+    }
+});

+ 71 - 29
frontend/saas-web/app/view/core/form/FormPanel.js

@@ -1,12 +1,20 @@
 Ext.define('saas.view.core.form.FormPanel', {
     extend: 'Ext.form.Panel',
     xtype: 'core-formpanel',
+    controller: 'core-form-formpanel',
+    viewModel: 'core-form-formpanel',
 
+    //工具类
     FormUtil: Ext.create('saas.util.FormUtil'),
+    BaseUtil: Ext.create('saas.util.BaseUtil'),
 
-    controller: 'core-form-formpanel',
-    viewModel: 'core-form-formpanel',
+    //字段属性
+    _codeField: '',
+    _statusField: '',
+    _idField: '',
 
+    //基础属性
+    initId: 0,
     layout: 'column',
     autoScroll: true,
     border: 1,
@@ -20,41 +28,75 @@ Ext.define('saas.view.core.form.FormPanel', {
         blankText: '该字段不能为空'
     },
 
-    dockedItems: [{
-        xtype: 'toolbar',
-        dock: 'top',
-        style: {
-            'border-bottom': '1px solid #35baf6 !important'
-        },
-        items: [{
-            xtype: 'tbtext',
-            bind: '单据编号:{id}'
-        }, '->', {
-            xtype: 'button',
-            text: '新增',
-            handler: 'add'
-        }, {
-            xtype: 'button',
-            text: '保存',
-            handler: 'save'
-        }, {
-            xtype: 'button',
-            text: '审核',
-            handler: "audit"
-        }]
-    }],
-
     items: [],
 
     remoteConfig: true, // 是否需要从远端读取form配置
     bindFields: [], // 已绑定字段(需要保存到数据库)
+    toolBtns: [], // 自定义按钮
 
     initComponent: function() {
-        var me = this,
-        remoteConfig = me.remoteConfig;
+
+        var me = this;
+
+        //判断是否加载数据
+        if(me.initId&&me.initId!=0){
+            var url = 'http://192.168.253.58:8800/purchase/form?id={id}',async=false;
+            url = url.replace(/(.*){id}(.*)/g, '$1' + me.initId);
+            me.BaseUtil.request({url,async })
+            .then(function(response) {
+                var res = Ext.decode(response.responseText);
+                if(res.success) {
+                    viewModel = me.getViewModel();
+                    viewModel.data = res.data.main;
+                    viewModel._data = res.data.main;
+                    viewModel.data.detailGridField = res.data.items;                   
+                    viewModel._data.detailGridField = res.data.items;
+                }
+            })
+            .catch(function(response) {
+            });
+        }else if(me.initId==0){
+            me.getViewModel().setData({detailGridField:[{},{},{},{},{},{},{},{},{},{}]})
+        }
+
+        Ext.apply(me, {
+            remoteConfig: me.remoteConfig,
+            dockedItems: [{
+                xtype: 'toolbar',
+                dock: 'top',
+                style: {
+                    'border-bottom': '1px solid #35baf6 !important'
+                },
+                items: ['->'].concat(me.toolBtns.map(function(btn){
+                    btn.cls += ' x-formpanel-btn-blue';
+                    return btn;
+                }).concat([{
+                    cls:'x-formpanel-btn-orange',
+                    xtype: 'button',
+                    text: '新增',
+                    handler: 'add'
+                }, {
+                    xtype: 'button',
+                    text: '保存',
+                    handler: 'save'
+                }, {
+                    xtype: 'button',
+                    text: '删除',
+                    handler: 'delete'
+                }, {
+                    xtype: 'button',
+                    text: '审核',
+                    handler: "audit"
+                }]))
+            }]
+        });
         
-        remoteConfig && me.FormUtil.setItems(me);
+        me.remoteConfig && me.FormUtil.setItems(me);
         me.callParent(arguments);
+    },
+
+    addItems: function(items) {
+        this.add(items);
     }
     
 });

+ 122 - 3
frontend/saas-web/app/view/core/form/FormPanelController.js

@@ -4,8 +4,127 @@ Ext.define('saas.view.core.form.FormPanelController', {
 
     FormUtil: Ext.create('saas.util.FormUtil'),
 
-    add: Ext.emptyFn,
-    save: Ext.emptyFn,
-    audit: Ext.emptyFn,
+    add: function(btn){
+        var form = btn.ownerCt.ownerCt;
+        var mainTab = Ext.getCmp('main-tab-panel');
+        var title = '新增' + btn.ownerCt.ownerCt._title;
+        var id = form.xtype + '_add';
+        existingItem = mainTab.down('[id=' + id + ']');
 
+        lastView = mainTab.getActiveTab();
+
+        if (!existingItem) {
+            var form = {
+                id,
+                title,
+                xtype:form.xtype
+            };
+            mainTab.setActiveTab(mainTab.add(form));
+        }
+    },
+    delete: Ext.emptyFn,
+    save:function(btn){
+        var form = btn.ownerCt.ownerCt;
+        if(form.getForm().wasDirty==false){
+            Ext.Msg.alert('提示','未修改数据,请修改后保存');
+            return false;
+        }
+        if(form.getForm().wasValid==false){
+            Ext.Msg.alert('提示','表单校验有误,请检查');
+            return false;
+        }
+        //form里面数据
+        var formData = form.viewModel.data;
+        var gridData = [];
+        var newGridData = [];
+        if(formData.detailGridField){
+            gridData = formData.detailGridField;
+        }
+        var grid = form.query('detailGridField')[0];
+        var updRecords = grid.store.getUpdatedRecords();
+        if(updRecords.length>0){
+            Ext.each(updRecords, function(rec){
+                Ext.each(gridData, function(data,index){
+                    if(data.id==rec.id){
+                        if(data.id.indexOf('extMode')>-1){
+                            data.id = '';
+                            data[form._detnoColumn] = index + 1;
+                        }
+                        newGridData.push(data);
+                    }
+                });
+            });
+        }   
+        var params = {
+            main:formData,
+            items:newGridData
+        }
+        Ext.Ajax.request({
+            url: 'http://192.168.253.58:8800/purchase/form',
+            params: JSON.stringify(params),
+            method: 'POST',
+            headers: {
+                'Access-Control-Allow-Origin': '*',
+                "Content-Type": 'application/json;charset=UTF-8' 
+            },
+            success: function (response, opts) {
+                //解析参数
+                Ext.Msg.alert('提示','保存成功');
+            },
+            failure: function (response, opts) {
+                //失败
+                Ext.Msg.alert('提示','保存失败');
+            }
+        });
+    },
+    audit: function(btn){
+        var form = btn.ownerCt.ownerCt;
+        if(form.getForm().wasValid==false){
+            Ext.Msg.alert('提示','表单校验有误,请检查');
+            return false;
+        }
+        //form里面数据
+        var formData = form.viewModel.data;
+        var gridData = [];
+        var newGridData = [];
+        if(formData.detailGridField){
+            gridData = formData.detailGridField;
+        }
+        var grid = form.query('detailGridField')[0];
+        var updRecords = grid.store.getUpdatedRecords();
+        if(updRecords.length>0){
+            Ext.each(updRecords, function(rec){
+                Ext.each(gridData, function(data,index){
+                    if(data.id==rec.id){
+                        if(data.id.indexOf('extMode')>-1){
+                            data.id = '';
+                            data[form._detnoColumn] = index + 1;
+                        }
+                        newGridData.push(data);
+                    }
+                });
+            });
+        }   
+        var params = {
+            main:formData,
+            items:newGridData
+        }
+        Ext.Ajax.request({
+            url: 'http://192.168.253.58:8800/purchase/audit',
+            params: JSON.stringify(params),
+            method: 'POST',
+            headers: {
+                'Access-Control-Allow-Origin': '*',
+                "Content-Type": 'application/json;charset=UTF-8' 
+            },
+            success: function (response, opts) {
+                //解析参数
+                Ext.Msg.alert('提示','审核成功');
+            },
+            failure: function (response, opts) {
+                //失败
+                Ext.Msg.alert('提示','审核失败');
+            }
+        });
+    }
 });

+ 49 - 21
frontend/saas-web/app/view/core/form/field/DetailGridField.js

@@ -37,16 +37,24 @@ Ext.define('saas.view.core.form.field.DetailGridField', {
                 dock: 'bottom',
                 items: [{
                     iconCls: 'x-fa fa-plus-circle',
-                    handler: me.addDetail
+                    handler: function() {
+                        me.addDetail();
+                    }
                 }, {
                     iconCls: 'x-fa fa-trash',
-                    handler: me.deleteDetail
+                    handler: function() {
+                        me.deleteDetail();
+                    }
                 }, {
                     iconCls: 'x-fa fa-arrow-up',
-                    handler: me.swapUp
+                    handler: function() {
+                        me.swapUp();
+                    }
                 }, {
                     iconCls: 'x-fa fa-arrow-down',
-                    handler: me.swapDown
+                    handler: function() {
+                        me.swapDown();
+                    }
                 }]
             }]
         });
@@ -54,11 +62,6 @@ Ext.define('saas.view.core.form.field.DetailGridField', {
     },
 
     listeners: {
-        afterrender: function(grid) {
-            if(grid.store.count()==0){
-                grid.add10EmptyRow();
-            }
-        },
         itemClick: function(tableView, record, item, index, e, eOpts) {
             var grid = tableView.up('grid'),
                 store = grid.store,
@@ -83,31 +86,56 @@ Ext.define('saas.view.core.form.field.DetailGridField', {
     },
 
     addDetail: function() {
-        var grid = this.up('grid'),
-            store = grid.getStore(),
-            selectedRecord = grid.selModel.lastSelected;
+        var me = this,
+            store = me.getStore(),
+            selectedRecord = me.selModel.lastSelected;
 
         store.insert(store.indexOf(selectedRecord) + 1, {});
     },
 
     deleteDetail: function() {
-        var grid = this.up('grid'),
-            store = grid.getStore(),
-            selectedRecord = grid.selModel.lastSelected;
+        var me = this,
+            store = me.getStore(),
+            selectedRecord = me.selModel.lastSelected;
 
         store.remove(selectedRecord);
     },
 
     swapUp: function() {
-        var grid = this.up('grid'),
-            store = grid.getStore(),
-            record = grid.selModel.lastSelected,
-            selectedIdx = store.indexOf(record),
-            to = store.getAt(selectedIdx - 1);
-
+        var me = this,
+            store = me.getStore(),
+            record = me.selModel.lastSelected,
+            selectedIdx = store.indexOf(record);
+            
+            me.swap(record, selectedIdx, -1);
     },
 
     swapDown: function() {
+        var me = this,
+        store = me.getStore(),
+        record = me.selModel.lastSelected,
+        selectedIdx = store.indexOf(record);
+        
+        me.swap(record, selectedIdx, 1);
+    },
 
+    swap: function(from, index, dir) {
+        var me = this,
+        store = me.getStore(),
+        to = store.getAt(index + dir);
+
+        if(from && to) {
+            var keys = me.getColumns().map(function(c) { return c.dataIndex }),
+            data = from.getData(),
+            toData = to.getData();
+
+            Ext.each(keys, function(key, index) {
+                to.set(key, null);
+                from.set(key, toData[key]);
+                to.set(key, data[key]);
+            });
+            //聚焦目标行
+            me.selModel.select(to);
+        }
     }
 });

+ 4 - 0
frontend/saas-web/app/view/main/Main.scss

@@ -106,3 +106,7 @@ $treelist-nav-ui: (
 .x-html-editor-input {
     border: 1px solid #ccc;
 }
+
+.x-formpanel-btn-orange {
+    background:#b36c1b !important;
+}

+ 1 - 3
frontend/saas-web/app/view/test/order/FormController.js

@@ -1,7 +1,7 @@
 Ext.define('saas.view.test.order.FormController', {
     extend: 'saas.view.core.form.FormPanelController',
     alias: 'controller.test-order-formcontroller',
-    init: function (view) {
+    init: function (form) {
         var me = this;
         this.control({
             /**放大镜新增demo*/
@@ -56,6 +56,4 @@ Ext.define('saas.view.test.order.FormController', {
         }).show();
 
     }
-
-
 });

+ 14 - 0
frontend/saas-web/app/view/test/order/FormPanel.js

@@ -4,5 +4,19 @@ Ext.define('saas.view.test.order.FormPanel', {
 
     controller: 'test-order-formcontroller',
     viewModel: 'test-order-formmodel',
+    
+    _title:'采购单',
+    _codeField: 'puCode',
+    _statusField: 'puStatuscode',
+    _idField: 'id',
+    _detnoColumn:  'pdDetno',
+    initId:0,
 
+    toolBtns: [{
+        xtype: 'button',
+        text: '转单按钮',
+        handler: function() {
+            console.log('11');
+        }
+    }]
 });

+ 6 - 1
pom.xml

@@ -16,7 +16,7 @@
     <groupId>com.usoftchina.saas</groupId>
     <artifactId>saas-platform</artifactId>
     <packaging>pom</packaging>
-    <version>${project.release.version}</version>
+    <version>1.0.0-SNAPSHOT</version>
     <description>saas framework parent</description>
 
     <properties>
@@ -190,6 +190,11 @@
                 <artifactId>core</artifactId>
                 <version>${project.release.version}</version>
             </dependency>
+            <dependency>
+                <groupId>com.usoftchina.saas</groupId>
+                <artifactId>server-starter</artifactId>
+                <version>${project.release.version}</version>
+            </dependency>
             <dependency>
                 <groupId>com.usoftchina.saas</groupId>
                 <artifactId>file-dto</artifactId>