Browse Source

分数据中心功能

yingp 7 years ago
parent
commit
c451cefc6d
56 changed files with 1720 additions and 57 deletions
  1. 5 0
      applications/purchase/purchase-server/pom.xml
  2. 0 1
      applications/purchase/purchase-server/src/main/java/com/usoftchina/saas/purchase/PurchaseApplication.java
  3. 3 0
      applications/purchase/purchase-server/src/main/resources/application.yml
  4. 3 0
      applications/purchase/purchase-server/src/main/resources/config/application-docker-prod.yml
  5. 0 45
      applications/purchase/purchase-server/src/test/PurchaseTest.java
  6. 33 0
      applications/purchase/purchase-server/src/test/java/com/usoftchina/saas/purchase/controller/PurchaseControllerTest.java
  7. 77 0
      base-servers/account/account-api/src/main/java/com/usoftchina/saas/account/cache/CompanyCache.java
  8. 9 0
      base-servers/account/account-dto/src/main/java/com/usoftchina/saas/account/dto/AccountDTO.java
  9. 10 0
      base-servers/account/account-dto/src/main/java/com/usoftchina/saas/account/dto/CompanyBaseDTO.java
  10. 14 0
      base-servers/account/account-dto/src/main/java/com/usoftchina/saas/account/dto/CompanyDTO.java
  11. 12 0
      base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/po/Company.java
  12. 10 0
      base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/vo/CompanyBaseVO.java
  13. 15 4
      base-servers/account/account-server/src/main/resources/mapper/CompanyMapper.xml
  14. 32 0
      base-servers/datacenter/datacenter-api/pom.xml
  15. 26 0
      base-servers/datacenter/datacenter-api/src/main/java/com/usoftchina/saas/dc/api/DataSourceApi.java
  16. 69 0
      base-servers/datacenter/datacenter-api/src/main/java/com/usoftchina/saas/dc/cache/DataSourceCache.java
  17. 35 0
      base-servers/datacenter/datacenter-client/pom.xml
  18. 28 0
      base-servers/datacenter/datacenter-client/src/main/java/com/usoftchina/saas/dc/client/configuration/DataCenterAutoConfiguration.java
  19. 52 0
      base-servers/datacenter/datacenter-client/src/main/java/com/usoftchina/saas/dc/client/interceptor/DataSourceInterceptor.java
  20. 3 0
      base-servers/datacenter/datacenter-client/src/main/resources/META-INF/spring.factories
  21. 22 0
      base-servers/datacenter/datacenter-dto/pom.xml
  22. 109 0
      base-servers/datacenter/datacenter-dto/src/main/java/com/usoftchina/saas/dc/dto/DataSourceInfoDTO.java
  23. 54 0
      base-servers/datacenter/datacenter-server/pom.xml
  24. 6 0
      base-servers/datacenter/datacenter-server/src/main/docker/Dockerfile
  25. 20 0
      base-servers/datacenter/datacenter-server/src/main/java/com/usoftchina/saas/dc/DatacenterApplication.java
  26. 28 0
      base-servers/datacenter/datacenter-server/src/main/java/com/usoftchina/saas/dc/controller/DataSourceInfoController.java
  27. 21 0
      base-servers/datacenter/datacenter-server/src/main/java/com/usoftchina/saas/dc/mapper/DataSourceInfoMapper.java
  28. 82 0
      base-servers/datacenter/datacenter-server/src/main/java/com/usoftchina/saas/dc/po/DataSourceInfo.java
  29. 19 0
      base-servers/datacenter/datacenter-server/src/main/java/com/usoftchina/saas/dc/service/DataSourceInfoService.java
  30. 23 0
      base-servers/datacenter/datacenter-server/src/main/java/com/usoftchina/saas/dc/service/impl/DataSourceInfoServiceImpl.java
  31. 81 0
      base-servers/datacenter/datacenter-server/src/main/resources/application.yml
  32. 12 0
      base-servers/datacenter/datacenter-server/src/main/resources/config/application-docker-dev.yml
  33. 23 0
      base-servers/datacenter/datacenter-server/src/main/resources/config/application-docker-prod.yml
  34. 12 0
      base-servers/datacenter/datacenter-server/src/main/resources/config/application-docker-test.yml
  35. 10 0
      base-servers/datacenter/datacenter-server/src/main/resources/config/application-docker.yml
  36. 131 0
      base-servers/datacenter/datacenter-server/src/main/resources/logback-spring.xml
  37. 20 0
      base-servers/datacenter/datacenter-server/src/main/resources/mapper/DataSourceInfoMapper.xml
  38. 22 0
      base-servers/datacenter/pom.xml
  39. 1 0
      base-servers/pom.xml
  40. 1 1
      base-servers/ui-server/src/main/resources/application.yml
  41. 16 0
      framework/core/src/main/java/com/usoftchina/saas/jdbc/Connectable.java
  42. 143 0
      framework/core/src/main/java/com/usoftchina/saas/jdbc/DynamicDataSource.java
  43. 25 0
      framework/core/src/main/java/com/usoftchina/saas/jdbc/DynamicDataSourceContextHolder.java
  44. 42 0
      framework/core/src/main/java/com/usoftchina/saas/jdbc/DynamicDataSourceRegister.java
  45. 60 0
      framework/core/src/main/java/com/usoftchina/saas/jdbc/JdbcUrl.java
  46. 5 0
      framework/server-starter/pom.xml
  47. 4 1
      framework/server-starter/src/main/java/com/usoftchina/saas/server/ServerAutoConfiguration.java
  48. 1 1
      framework/server-starter/src/main/java/com/usoftchina/saas/server/error/DefaultErrorConfig.java
  49. 140 0
      framework/server-starter/src/main/java/com/usoftchina/saas/server/jdbc/DynamicDataSourceConfig.java
  50. 17 0
      framework/server-starter/src/main/java/com/usoftchina/saas/server/jdbc/EnableDynamicDataSource.java
  51. 1 0
      frontend/saas-web/electron/lib/sockjs.min.js
  52. 7 0
      frontend/saas-web/electron/lib/stomp.min.js
  53. 60 0
      frontend/saas-web/electron/login.html
  54. 33 4
      frontend/saas-web/electron/main.js
  55. 15 0
      pom.xml
  56. 18 0
      script/mysql/init/manage.sql

+ 5 - 0
applications/purchase/purchase-server/pom.xml

@@ -81,6 +81,11 @@
             <groupId>com.usoftchina.saas</groupId>
             <artifactId>commons-api</artifactId>
         </dependency>
+
+        <dependency>
+            <groupId>com.usoftchina.saas</groupId>
+            <artifactId>datacenter-client</artifactId>
+        </dependency>
     </dependencies>
 
     <build>

+ 0 - 1
applications/purchase/purchase-server/src/main/java/com/usoftchina/saas/purchase/PurchaseApplication.java

@@ -1,6 +1,5 @@
 package com.usoftchina.saas.purchase;
 
-
 import com.usoftchina.saas.auth.client.EnableAuthClient;
 import org.mybatis.spring.annotation.MapperScan;
 import org.springframework.boot.SpringApplication;

+ 3 - 0
applications/purchase/purchase-server/src/main/resources/application.yml

@@ -35,6 +35,9 @@ spring:
   sleuth:
     sampler:
       probability: 1.0
+  redis:
+    host: 192.168.0.182
+    port: 6379
 eureka:
   instance:
     leaseRenewalIntervalInSeconds: 10

+ 3 - 0
applications/purchase/purchase-server/src/main/resources/config/application-docker-prod.yml

@@ -16,5 +16,8 @@ spring:
     url: jdbc:mysql://10.10.100.18:3306/saas_biz?characterEncoding=utf-8&useSSL=false&allowMultiQueries=true
     username: saas
     password: select111***
+  redis:
+    host: 10.10.100.173
+    port: 6379
 logging:
   destination: 10.10.100.160:5000

+ 0 - 45
applications/purchase/purchase-server/src/test/PurchaseTest.java

@@ -1,45 +0,0 @@
-import com.usoftchina.saas.purchase.PurchaseApplication;
-import com.usoftchina.saas.purchase.mapper.ProdIODetailMapper;
-import com.usoftchina.saas.purchase.po.ProdIODetail;
-import com.usoftchina.saas.purchase.service.ProdInOutService;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
-
-import java.util.ArrayList;
-import java.util.List;
-
-@RunWith(SpringJUnit4ClassRunner.class)
-@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = PurchaseApplication.class)
-@EnableAutoConfiguration
-public class PurchaseTest {
-
-    @Autowired
-    private ProdInOutService prodInOutService;
-    @Autowired
-    private ProdIODetailMapper prodIODetailMapper;
-
-
-    @Test
-    public void test(){
-        List<ProdIODetail> list = new ArrayList<>();
-        ProdIODetail prodIODetail1 = new ProdIODetail();
-        prodIODetail1.setPd_piid(new Long(1));
-        prodIODetail1.setCompanyId(-1);
-        prodIODetail1.setPd_inoutno("test01");
-
-
-        ProdIODetail prodIODetail2 = new ProdIODetail();
-        prodIODetail2.setPd_piid(new Long(1));
-        prodIODetail2.setCompanyId(-1);
-        prodIODetail2.setPd_inoutno("test02");
-
-        list.add(prodIODetail1);
-        list.add(prodIODetail2);
-
-        prodIODetailMapper.batchInsert(list);
-    }
-}

+ 33 - 0
applications/purchase/purchase-server/src/test/java/com/usoftchina/saas/purchase/controller/PurchaseControllerTest.java

@@ -0,0 +1,33 @@
+package com.usoftchina.saas.purchase.controller;
+
+import com.usoftchina.saas.account.cache.AccountCache;
+import com.usoftchina.saas.account.cache.CompanyCache;
+import com.usoftchina.saas.base.Result;
+import com.usoftchina.saas.purchase.dto.PurchaseFormDTO;
+import com.usoftchina.saas.test.BaseControllerTest;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.servlet.MvcResult;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class PurchaseControllerTest extends BaseControllerTest {
+
+    @Test
+    public void testA_getFormData() throws Exception {
+        CompanyCache.of(2L).clear();
+        MvcResult mvcResult = mockMvc.perform(get("/purchase/read/200")
+                // 测试登录演示账套后,自动切换数据源
+                .header("Authorization","eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ2aXJ0dWFsMDUxNDA1IiwiYXBwSWQiOiJ0cmFkZS1hcHAiLCJ1c2VySWQiOi05OTk5OSwiY29tcGFueUlkIjoyLCJ1c2VyTmFtZSI6InZpcnR1YWwwNTE0MDUiLCJyZWFsTmFtZSI6Iua4uOWuojA1MTQwNSIsImV4cCI6MTU0NjMxOTY0NX0.kLfFDiHUT4Dc1X9DFDDBBl3BFYKEZz_uTHefc9bP1l-7eLsluGwFGDQyD6LjT67NWQrr1pZIEmk-ySOZyBbMmArW-7I2bXBo-OXlq-KIUGI3w5DhP_zHJGT3ctrR9R_O6vmOIzC-9rB7FowXQWEPLAtRqrcOSmqnXpmeuXNk4Z8"))
+                .andExpect(isSuccess())
+                .andReturn();
+        Result<PurchaseFormDTO> result = result(mvcResult, PurchaseFormDTO.class);
+        System.out.println(result.getData());
+    }
+
+}

+ 77 - 0
base-servers/account/account-api/src/main/java/com/usoftchina/saas/account/cache/CompanyCache.java

@@ -0,0 +1,77 @@
+package com.usoftchina.saas.account.cache;
+
+import com.usoftchina.saas.account.api.CompanyApi;
+import com.usoftchina.saas.account.dto.CompanyDTO;
+import com.usoftchina.saas.base.Result;
+import com.usoftchina.saas.cache.RedisHashCache;
+import com.usoftchina.saas.context.BaseContextHolder;
+import com.usoftchina.saas.context.SpringContextHolder;
+import com.usoftchina.saas.exception.BizException;
+import com.usoftchina.saas.utils.JsonUtils;
+import org.springframework.data.redis.core.RedisTemplate;
+
+import java.util.Optional;
+import java.util.function.Supplier;
+
+/**
+ * @author yingp
+ * @date 2018/11/1
+ */
+public class CompanyCache extends RedisHashCache<String, String, String> {
+
+    private final Long companyId;
+
+    private static final RedisTemplate<String, String> REDIS_TEMPLATE = SpringContextHolder.getBean("redisTemplate", RedisTemplate.class);
+    private CompanyApi companyApi;
+
+    private CompanyCache(Long companyId) {
+        super(() -> REDIS_TEMPLATE);
+        this.companyId = companyId;
+    }
+
+    public static CompanyCache of(Long companyId) {
+        return new CompanyCache(companyId);
+    }
+
+    /**
+     * 当前登录公司的缓存
+     *
+     * @return
+     */
+    public static CompanyCache current() {
+        return new CompanyCache(BaseContextHolder.getCompanyId());
+    }
+
+    @Override
+    protected String field() {
+        return String.valueOf(companyId);
+    }
+
+    @Override
+    protected String key() {
+        return generatePublicKey("account", "company");
+    }
+
+    @Override
+    protected Supplier<String> getSupplier() {
+        return () -> {
+			if (null == companyApi) {
+                companyApi = SpringContextHolder.getBean(CompanyApi.class);
+			}
+            Result<CompanyDTO> result = companyApi.getCompanyById(companyId);
+            if (result.isSuccess()) {
+                return JsonUtils.toJsonString(result.getData());
+            }
+            throw new BizException(result.getCode(), result.getMessage());
+        };
+    }
+
+    public CompanyDTO getCompany() {
+        Optional<String> value = get();
+        if (value.isPresent()) {
+            return JsonUtils.fromJsonString(value.get(), CompanyDTO.class);
+        }
+        return null;
+    }
+
+}

+ 9 - 0
base-servers/account/account-dto/src/main/java/com/usoftchina/saas/account/dto/AccountDTO.java

@@ -167,6 +167,15 @@ public class AccountDTO implements Serializable {
         return null;
     }
 
+    @JsonIgnore
+    public CompanyBaseDTO getCompany(Long companyId) {
+        if (null != companies) {
+            return companies.stream().filter(company -> company.getId().equals(companyId))
+                    .findFirst().orElse(null);
+        }
+        return null;
+    }
+
     @Override
     public String toString() {
         return "AccountDTO{" +

+ 10 - 0
base-servers/account/account-dto/src/main/java/com/usoftchina/saas/account/dto/CompanyBaseDTO.java

@@ -11,6 +11,7 @@ import java.io.Serializable;
 public class CompanyBaseDTO implements Serializable{
     private Long id;
     private String name;
+    private String dcName;
     private String logoUrl;
 
     public Long getId() {
@@ -29,6 +30,14 @@ public class CompanyBaseDTO implements Serializable{
         this.name = name;
     }
 
+    public String getDcName() {
+        return dcName;
+    }
+
+    public void setDcName(String dcName) {
+        this.dcName = dcName;
+    }
+
     public String getLogoUrl() {
         return logoUrl;
     }
@@ -42,6 +51,7 @@ public class CompanyBaseDTO implements Serializable{
         return "CompanyBaseDTO{" +
                 "id=" + id +
                 ", name='" + name + '\'' +
+                ", dcName='" + dcName + '\'' +
                 ", logoUrl='" + logoUrl + '\'' +
                 '}';
     }

+ 14 - 0
base-servers/account/account-dto/src/main/java/com/usoftchina/saas/account/dto/CompanyDTO.java

@@ -24,6 +24,7 @@ public class CompanyDTO implements Serializable{
     private String businessCode;
     private String address;
     private String logoUrl;
+    private String dcName;
     private String tel;
     private String fax;
     private String signet;      //电子章
@@ -37,6 +38,14 @@ public class CompanyDTO implements Serializable{
         this.fileInfoList = fileInfoList;
     }
 
+    public String getDcName() {
+        return dcName;
+    }
+
+    public void setDcName(String dcName) {
+        this.dcName = dcName;
+    }
+
     public String getTel() {
         return tel;
     }
@@ -118,6 +127,11 @@ public class CompanyDTO implements Serializable{
                 ", businessCode='" + businessCode + '\'' +
                 ", address='" + address + '\'' +
                 ", logoUrl='" + logoUrl + '\'' +
+                ", dcName='" + dcName + '\'' +
+                ", tel='" + tel + '\'' +
+                ", fax='" + fax + '\'' +
+                ", signet='" + signet + '\'' +
+                ", fileInfoList=" + fileInfoList +
                 ", uu=" + uu +
                 '}';
     }

+ 12 - 0
base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/po/Company.java

@@ -31,6 +31,10 @@ public class Company implements Serializable {
      * 所属行业
      */
     private String type;
+    /**
+     * 数据中心
+     */
+    private String dcName;
 
     public String getType() {
         return type;
@@ -149,4 +153,12 @@ public class Company implements Serializable {
     public void setUu(Long uu) {
         this.uu = uu;
     }
+
+    public String getDcName() {
+        return dcName;
+    }
+
+    public void setDcName(String dcName) {
+        this.dcName = dcName;
+    }
 }

+ 10 - 0
base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/vo/CompanyBaseVO.java

@@ -7,6 +7,7 @@ package com.usoftchina.saas.account.vo;
 public class CompanyBaseVO {
     private Long id;
     private String name;
+    private String dcName;
     private String logoUrl;
 
     public Long getId() {
@@ -25,6 +26,14 @@ public class CompanyBaseVO {
         this.name = name;
     }
 
+    public String getDcName() {
+        return dcName;
+    }
+
+    public void setDcName(String dcName) {
+        this.dcName = dcName;
+    }
+
     public String getLogoUrl() {
         return logoUrl;
     }
@@ -38,6 +47,7 @@ public class CompanyBaseVO {
         return "CompanyBaseVO{" +
                 "id=" + id +
                 ", name='" + name + '\'' +
+                ", dcName='" + dcName + '\'' +
                 ", logoUrl='" + logoUrl + '\'' +
                 '}';
     }

+ 15 - 4
base-servers/account/account-server/src/main/resources/mapper/CompanyMapper.xml

@@ -17,14 +17,16 @@
         <result column="signet" jdbcType="VARCHAR" property="signet"/>
         <result column="uu" jdbcType="VARCHAR" property="uu"/>
         <result column="logo_url" jdbcType="VARCHAR" property="logoUrl"/>
+        <result column="dc_name" jdbcType="VARCHAR" property="dcName"/>
     </resultMap>
     <resultMap id="BaseResultMap" type="com.usoftchina.saas.account.vo.CompanyBaseVO">
         <id column="id" jdbcType="BIGINT" property="id"/>
         <result column="name" jdbcType="VARCHAR" property="name"/>
         <result column="logo_url" jdbcType="VARCHAR" property="logoUrl"/>
+        <result column="dc_name" jdbcType="VARCHAR" property="dcName"/>
     </resultMap>
     <sql id="baseColumns">
-        id,name,business_code,address,uu,creator_id,create_time,updater_id,update_time,fax,tel,signet,uu,logo_url
+        id,name,business_code,address,uu,creator_id,create_time,updater_id,update_time,fax,tel,signet,uu,logo_url,dc_name
     </sql>
     <insert id="insert" parameterType="com.usoftchina.saas.account.po.Company"
             useGeneratedKeys="true" keyProperty="id">
@@ -32,12 +34,12 @@
             SELECT LAST_INSERT_ID() AS ID
         </selectKey>
         insert into ac_company(name, business_code, address, logo_url, creator_id, create_time, updater_id, update_time,
-        tel, fax, signet, uu, type)
+        tel, fax, signet, uu, type, dc_name)
         values (#{name,jdbcType=VARCHAR}, #{businessCode,jdbcType=VARCHAR},
         #{address,jdbcType=VARCHAR}, #{logoUrl,jdbcType=VARCHAR}, #{creatorId,jdbcType=BIGINT},
         #{createTime,jdbcType=TIMESTAMP}, #{updaterId,jdbcType=BIGINT}, #{updateTime,jdbcType=TIMESTAMP},
         #{tel,jdbcType=VARCHAR}, #{fax,jdbcType=VARCHAR}, #{signet,jdbcType=VARCHAR}, #{uu,jdbcType=VARCHAR},
-        #{type,jdbcType=VARCHAR})
+        #{type,jdbcType=VARCHAR}, #{dcName,jdbcType=VARCHAR})
     </insert>
     <insert id="insertSelective" parameterType="com.usoftchina.saas.account.po.Company"
             useGeneratedKeys="true" keyProperty="id">
@@ -55,6 +57,9 @@
             <if test="logoUrl != null">
                 logo_url,
             </if>
+            <if test="dcName != null">
+                dc_name,
+            </if>
             <if test="uu != null">
                 uu,
             </if>
@@ -84,6 +89,9 @@
             <if test="logoUrl != null">
                 #{logoUrl,jdbcType=VARCHAR},
             </if>
+            <if test="dcName != null">
+                #{dcName,jdbcType=VARCHAR},
+            </if>
             <if test="uu != null">
                 #{uu,jdbcType=BIGINT},
             </if>
@@ -114,7 +122,7 @@
         select <include refid="baseColumns"/> from ac_company where business_code=#{businessCode,jdbcType=VARCHAR}
     </select>
     <select id="selectBaseByAccountId" parameterType="java.lang.Long" resultMap="BaseResultMap">
-        select ac_company.id,ac_company.name,ac_company.logo_url from ac_account_company,ac_company
+        select ac_company.id,ac_company.name,ac_company.logo_url,ac_company.dc_name from ac_account_company,ac_company
         where ac_account_company.company_id=ac_company.id and ac_account_company.account_id=#{accountId,jdbcType=BIGINT}
     </select>
     <delete id="deleteByPrimaryKey" parameterType="java.lang.Long">
@@ -144,6 +152,9 @@
             <if test="type != null">
                 type = #{type,jdbcType=VARCHAR}
             </if>
+            <if test="dcName != null">
+                dc_name = #{dcName,jdbcType=VARCHAR}
+            </if>
         </set>
         where id = #{id}
     </update>

+ 32 - 0
base-servers/datacenter/datacenter-api/pom.xml

@@ -0,0 +1,32 @@
+<?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>datacenter</artifactId>
+        <groupId>com.usoftchina.saas</groupId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>datacenter-api</artifactId>
+    <description>datacenter api</description>
+    <dependencies>
+        <dependency>
+            <groupId>com.usoftchina.saas</groupId>
+            <artifactId>core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.usoftchina.saas</groupId>
+            <artifactId>datacenter-dto</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-openfeign</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis</artifactId>
+        </dependency>
+    </dependencies>
+</project>

+ 26 - 0
base-servers/datacenter/datacenter-api/src/main/java/com/usoftchina/saas/dc/api/DataSourceApi.java

@@ -0,0 +1,26 @@
+package com.usoftchina.saas.dc.api;
+
+import com.usoftchina.saas.base.Result;
+import com.usoftchina.saas.dc.dto.DataSourceInfoDTO;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+
+/**
+ * @author yingp
+ * @date 2018/12/10
+ */
+@FeignClient("datacenter-server")
+public interface DataSourceApi {
+
+    /**
+     * 获取数据源配置
+     *
+     * @param dcName datacenter name
+     * @param dbName database name
+     * @return
+     */
+    @GetMapping("/ds")
+    Result<DataSourceInfoDTO> getDataSourceInfo(@RequestParam("dcName") String dcName,
+                                            @RequestParam("dbName") String dbName);
+}

+ 69 - 0
base-servers/datacenter/datacenter-api/src/main/java/com/usoftchina/saas/dc/cache/DataSourceCache.java

@@ -0,0 +1,69 @@
+package com.usoftchina.saas.dc.cache;
+
+import com.usoftchina.saas.base.Result;
+import com.usoftchina.saas.cache.RedisHashCache;
+import com.usoftchina.saas.context.SpringContextHolder;
+import com.usoftchina.saas.dc.api.DataSourceApi;
+import com.usoftchina.saas.dc.dto.DataSourceInfoDTO;
+import com.usoftchina.saas.exception.BizException;
+import com.usoftchina.saas.utils.JsonUtils;
+import org.springframework.data.redis.core.RedisTemplate;
+
+import java.util.Optional;
+import java.util.function.Supplier;
+
+/**
+ * @author yingp
+ * @date 2018/11/1
+ */
+public class DataSourceCache extends RedisHashCache<String, String, String> {
+
+    private final String dcName;
+    private final String dbName;
+
+    private static final RedisTemplate<String, String> REDIS_TEMPLATE = SpringContextHolder.getBean("redisTemplate", RedisTemplate.class);
+    private DataSourceApi dataSourceApi;
+
+    private DataSourceCache(String dcName, String dbName) {
+        super(() -> REDIS_TEMPLATE);
+        this.dcName = dcName;
+        this.dbName = dbName;
+    }
+
+    public static DataSourceCache of(String dcName, String dbName) {
+        return new DataSourceCache(dcName, dbName);
+    }
+
+    @Override
+    protected String field() {
+        return dcName + ":" + dbName;
+    }
+
+    @Override
+    protected String key() {
+        return generatePublicKey("datasource");
+    }
+
+    @Override
+    protected Supplier<String> getSupplier() {
+        return () -> {
+			if (null == dataSourceApi) {
+                dataSourceApi = SpringContextHolder.getBean(DataSourceApi.class);
+			}
+            Result<DataSourceInfoDTO> result = dataSourceApi.getDataSourceInfo(dcName, dbName);
+            if (result.isSuccess()) {
+                return JsonUtils.toJsonString(result.getData());
+            }
+            throw new BizException(result.getCode(), result.getMessage());
+        };
+    }
+
+    public DataSourceInfoDTO getDataSourceInfo() {
+        Optional<String> value = get();
+        if (value.isPresent()) {
+            return JsonUtils.fromJsonString(value.get(), DataSourceInfoDTO.class);
+        }
+        return null;
+    }
+
+}

+ 35 - 0
base-servers/datacenter/datacenter-client/pom.xml

@@ -0,0 +1,35 @@
+<?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>datacenter</artifactId>
+        <groupId>com.usoftchina.saas</groupId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>datacenter-client</artifactId>
+    <description>datacenter client</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.usoftchina.saas</groupId>
+            <artifactId>datacenter-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.usoftchina.saas</groupId>
+            <artifactId>account-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-webmvc</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+            <scope>compile</scope>
+            <optional>true</optional>
+        </dependency>
+    </dependencies>
+</project>

+ 28 - 0
base-servers/datacenter/datacenter-client/src/main/java/com/usoftchina/saas/dc/client/configuration/DataCenterAutoConfiguration.java

@@ -0,0 +1,28 @@
+package com.usoftchina.saas.dc.client.configuration;
+
+import com.usoftchina.saas.dc.client.interceptor.DataSourceInterceptor;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+/**
+ * @author yingp
+ * @date 2018/12/11
+ */
+@Configuration
+@EnableFeignClients("com.usoftchina.saas.dc.api")
+public class DataCenterAutoConfiguration implements WebMvcConfigurer {
+
+    @Bean
+    public DataSourceInterceptor dataSourceInterceptor() {
+        return new DataSourceInterceptor();
+    }
+
+    @Override
+    public void addInterceptors(InterceptorRegistry registry) {
+        registry.addInterceptor(dataSourceInterceptor()).order(1);
+        // order顺序在AuthRestInterceptor后面
+    }
+}

+ 52 - 0
base-servers/datacenter/datacenter-client/src/main/java/com/usoftchina/saas/dc/client/interceptor/DataSourceInterceptor.java

@@ -0,0 +1,52 @@
+package com.usoftchina.saas.dc.client.interceptor;
+
+import com.usoftchina.saas.account.cache.CompanyCache;
+import com.usoftchina.saas.account.dto.CompanyDTO;
+import com.usoftchina.saas.context.BaseContextHolder;
+import com.usoftchina.saas.dc.cache.DataSourceCache;
+import com.usoftchina.saas.dc.dto.DataSourceInfoDTO;
+import com.usoftchina.saas.jdbc.DynamicDataSourceContextHolder;
+import com.usoftchina.saas.jdbc.DynamicDataSourceRegister;
+import com.usoftchina.saas.utils.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.lang.Nullable;
+import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * @author yingp
+ * @date 2018/12/11
+ */
+public class DataSourceInterceptor extends HandlerInterceptorAdapter {
+
+    @Autowired
+    private DynamicDataSourceRegister dataSourceRegister;
+
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+        long companyId = BaseContextHolder.getCompanyId();
+        if (companyId > 0) {
+            CompanyDTO company = CompanyCache.current().getCompany();
+            String dcName = company.getDcName();
+            // 该公司绑定到了指定数据中心
+            if (StringUtils.hasText(dcName)) {
+                String dbName = dataSourceRegister.getDefaultDatabaseName();
+                DataSourceInfoDTO dataSourceInfoDTO = DataSourceCache.of(dcName, dbName).getDataSourceInfo();
+                if (null != dataSourceInfoDTO) {
+                    // 如果不存在数据源则自动创建
+                    dataSourceRegister.createDataSource(dataSourceInfoDTO);
+                    DynamicDataSourceContextHolder.set(dataSourceInfoDTO);
+                }
+            }
+        }
+        return super.preHandle(request, response, handler);
+    }
+
+    @Override
+    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
+        DynamicDataSourceContextHolder.clear();
+        super.afterCompletion(request, response, handler, ex);
+    }
+}

+ 3 - 0
base-servers/datacenter/datacenter-client/src/main/resources/META-INF/spring.factories

@@ -0,0 +1,3 @@
+# Auto Configuration
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+com.usoftchina.saas.dc.client.configuration.DataCenterAutoConfiguration

+ 22 - 0
base-servers/datacenter/datacenter-dto/pom.xml

@@ -0,0 +1,22 @@
+<?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>datacenter</artifactId>
+        <groupId>com.usoftchina.saas</groupId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>datacenter-dto</artifactId>
+    <description>datacenter data transfer object</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.usoftchina.saas</groupId>
+            <artifactId>core</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>

+ 109 - 0
base-servers/datacenter/datacenter-dto/src/main/java/com/usoftchina/saas/dc/dto/DataSourceInfoDTO.java

@@ -0,0 +1,109 @@
+package com.usoftchina.saas.dc.dto;
+
+import com.usoftchina.saas.jdbc.Connectable;
+
+import java.io.Serializable;
+
+/**
+ * @author yingp
+ * @date 2018/12/10
+ */
+public class DataSourceInfoDTO implements Connectable, Serializable{
+    private String dcName;
+    private String dbName;
+    private String dbHost;
+    private int dbPort;
+    private String dbRealName;
+    private String dbUserName;
+    private String dbPassword;
+
+    public String getDcName() {
+        return dcName;
+    }
+
+    public void setDcName(String dcName) {
+        this.dcName = dcName;
+    }
+
+    public String getDbName() {
+        return dbName;
+    }
+
+    public void setDbName(String dbName) {
+        this.dbName = dbName;
+    }
+
+    public String getDbHost() {
+        return dbHost;
+    }
+
+    public void setDbHost(String dbHost) {
+        this.dbHost = dbHost;
+    }
+
+    public int getDbPort() {
+        return dbPort;
+    }
+
+    public void setDbPort(int dbPort) {
+        this.dbPort = dbPort;
+    }
+
+    public String getDbRealName() {
+        return dbRealName;
+    }
+
+    public void setDbRealName(String dbRealName) {
+        this.dbRealName = dbRealName;
+    }
+
+    public String getDbUserName() {
+        return dbUserName;
+    }
+
+    public void setDbUserName(String dbUserName) {
+        this.dbUserName = dbUserName;
+    }
+
+    public String getDbPassword() {
+        return dbPassword;
+    }
+
+    public void setDbPassword(String dbPassword) {
+        this.dbPassword = dbPassword;
+    }
+
+    @Override
+    public String toString() {
+        return "DataSourceInfoDTO{" +
+                "dcName='" + dcName + '\'' +
+                ", dbName='" + dbName + '\'' +
+                ", dbHost='" + dbHost + '\'' +
+                ", dbPort=" + dbPort +
+                ", dbRealName='" + dbRealName + '\'' +
+                ", dbUserName='" + dbUserName + '\'' +
+                ", dbPassword='" + dbPassword + '\'' +
+                '}';
+    }
+
+    @Override
+    public String qualifier() {
+        return this.dcName + ":" + this.dbName;
+    }
+
+    @Override
+    public String url() {
+        return String.format("jdbc:mysql://%s:%s/%s?characterEncoding=utf-8&useSSL=false",
+                dbHost, dbPort, dbRealName);
+    }
+
+    @Override
+    public String username() {
+        return this.dbUserName;
+    }
+
+    @Override
+    public String password() {
+        return this.dbPassword;
+    }
+}

+ 54 - 0
base-servers/datacenter/datacenter-server/pom.xml

@@ -0,0 +1,54 @@
+<?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>datacenter</artifactId>
+        <groupId>com.usoftchina.saas</groupId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>datacenter-server</artifactId>
+    <description>datacenter server</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.usoftchina.saas</groupId>
+            <artifactId>server-starter</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.usoftchina.saas</groupId>
+            <artifactId>datacenter-dto</artifactId>
+        </dependency>
+        <!-- db -->
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.mybatis.spring.boot</groupId>
+            <artifactId>mybatis-spring-boot-starter</artifactId>
+        </dependency>
+        <!-- sleuth -->
+        <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>
+            <groupId>net.logstash.logback</groupId>
+            <artifactId>logstash-logback-encoder</artifactId>
+        </dependency>
+        <!-- test -->
+        <dependency>
+            <groupId>com.usoftchina.saas</groupId>
+            <artifactId>test-starter</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+</project>

+ 6 - 0
base-servers/datacenter/datacenter-server/src/main/docker/Dockerfile

@@ -0,0 +1,6 @@
+FROM frolvlad/alpine-oraclejdk8:slim
+VOLUME /tmp
+ADD manage-server-1.0.0-SNAPSHOT.jar app.jar
+RUN sh -c 'touch /app.jar'
+ENV JAVA_OPTS=""
+ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar" ]

+ 20 - 0
base-servers/datacenter/datacenter-server/src/main/java/com/usoftchina/saas/dc/DatacenterApplication.java

@@ -0,0 +1,20 @@
+package com.usoftchina.saas.dc;
+
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
+
+/**
+ * @author yingp
+ * @date 2018/12/10
+ */
+@EnableEurekaClient
+@SpringBootApplication
+@MapperScan("com.usoftchina.saas.dc.mapper")
+public class DatacenterApplication {
+
+    public static void main(String[] args) {
+        SpringApplication.run(DatacenterApplication.class, args);
+    }
+}

+ 28 - 0
base-servers/datacenter/datacenter-server/src/main/java/com/usoftchina/saas/dc/controller/DataSourceInfoController.java

@@ -0,0 +1,28 @@
+package com.usoftchina.saas.dc.controller;
+
+import com.usoftchina.saas.base.Result;
+import com.usoftchina.saas.dc.dto.DataSourceInfoDTO;
+import com.usoftchina.saas.dc.service.DataSourceInfoService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author yingp
+ * @date 2018/12/10
+ */
+@RestController
+@RequestMapping("/ds")
+public class DataSourceInfoController {
+
+    @Autowired
+    private DataSourceInfoService dataSourceInfoService;
+
+    @GetMapping
+    public Result<DataSourceInfoDTO> getDataSourceInfo(@RequestParam("dcName") String dcName,
+                                                @RequestParam("dbName") String dbName) {
+        return Result.success(dataSourceInfoService.findDataSourceInfo(dcName, dbName));
+    }
+}

+ 21 - 0
base-servers/datacenter/datacenter-server/src/main/java/com/usoftchina/saas/dc/mapper/DataSourceInfoMapper.java

@@ -0,0 +1,21 @@
+package com.usoftchina.saas.dc.mapper;
+
+import com.usoftchina.saas.dc.po.DataSourceInfo;
+import org.apache.ibatis.annotations.Param;
+
+
+/**
+ * @author yingp
+ * @date 2018/12/10
+ */
+public interface DataSourceInfoMapper {
+
+    /**
+     * 查找
+     *
+     * @param dcName datacenter name
+     * @param dbName database name
+     * @return
+     */
+    DataSourceInfo selectByDcNameAndDbName(@Param("dcName") String dcName, @Param("dbName") String dbName);
+}

+ 82 - 0
base-servers/datacenter/datacenter-server/src/main/java/com/usoftchina/saas/dc/po/DataSourceInfo.java

@@ -0,0 +1,82 @@
+package com.usoftchina.saas.dc.po;
+
+import java.io.Serializable;
+
+/**
+ * @author yingp
+ * @date 2018/12/10
+ */
+public class DataSourceInfo implements Serializable{
+    private Integer id;
+    private String dcName;
+    private String dbName;
+    private String dbRealName;
+    private String dbHost;
+    private Integer dbPort;
+    private String dbUserName;
+    private String dbPassword;
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getDcName() {
+        return dcName;
+    }
+
+    public void setDcName(String dcName) {
+        this.dcName = dcName;
+    }
+
+    public String getDbName() {
+        return dbName;
+    }
+
+    public void setDbName(String dbName) {
+        this.dbName = dbName;
+    }
+
+    public String getDbRealName() {
+        return dbRealName;
+    }
+
+    public void setDbRealName(String dbRealName) {
+        this.dbRealName = dbRealName;
+    }
+
+    public String getDbHost() {
+        return dbHost;
+    }
+
+    public void setDbHost(String dbHost) {
+        this.dbHost = dbHost;
+    }
+
+    public Integer getDbPort() {
+        return dbPort;
+    }
+
+    public void setDbPort(Integer dbPort) {
+        this.dbPort = dbPort;
+    }
+
+    public String getDbUserName() {
+        return dbUserName;
+    }
+
+    public void setDbUserName(String dbUserName) {
+        this.dbUserName = dbUserName;
+    }
+
+    public String getDbPassword() {
+        return dbPassword;
+    }
+
+    public void setDbPassword(String dbPassword) {
+        this.dbPassword = dbPassword;
+    }
+}

+ 19 - 0
base-servers/datacenter/datacenter-server/src/main/java/com/usoftchina/saas/dc/service/DataSourceInfoService.java

@@ -0,0 +1,19 @@
+package com.usoftchina.saas.dc.service;
+
+import com.usoftchina.saas.dc.po.DataSourceInfo;
+
+/**
+ * @author yingp
+ * @date 2018/12/10
+ */
+public interface DataSourceInfoService {
+
+    /**
+     * 查找
+     *
+     * @param dcName datacenter name
+     * @param dbName database name
+     * @return
+     */
+    DataSourceInfo findDataSourceInfo(String dcName, String dbName);
+}

+ 23 - 0
base-servers/datacenter/datacenter-server/src/main/java/com/usoftchina/saas/dc/service/impl/DataSourceInfoServiceImpl.java

@@ -0,0 +1,23 @@
+package com.usoftchina.saas.dc.service.impl;
+
+import com.usoftchina.saas.dc.mapper.DataSourceInfoMapper;
+import com.usoftchina.saas.dc.po.DataSourceInfo;
+import com.usoftchina.saas.dc.service.DataSourceInfoService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author yingp
+ * @date 2018/12/10
+ */
+@Service
+public class DataSourceInfoServiceImpl implements DataSourceInfoService{
+
+    @Autowired
+    private DataSourceInfoMapper dataSourceInfoMapper;
+
+    @Override
+    public DataSourceInfo findDataSourceInfo(String dcName, String dbName) {
+        return dataSourceInfoMapper.selectByDcNameAndDbName(dcName, dbName);
+    }
+}

+ 81 - 0
base-servers/datacenter/datacenter-server/src/main/resources/application.yml

@@ -0,0 +1,81 @@
+spring:
+  profiles:
+    active: dev
+  application:
+    name: datacenter-server
+  security:
+    user:
+      name: admin
+      password: select111***
+  rabbitmq:
+    host: 192.168.0.176
+    port: 5672
+    virtual-host: dev
+    username: saas
+    password: select123***
+  zipkin:
+    sender:
+      type: rabbit
+    locator:
+      discovery:
+        enabled: true
+  sleuth:
+    sampler:
+      probability: 1.0
+  datasource:
+    driver-class-name: com.mysql.cj.jdbc.Driver
+    url: jdbc:mysql://192.168.0.172:3306/saas_manage?characterEncoding=utf-8&useSSL=false
+    username: root
+    password: select111***
+    hikari:
+      minimum-idle: 5
+      maximum-pool-size: 50
+      idle-timeout: 30000
+      max-lifetime: 1800000
+      connection-timeout: 30000
+  messages:
+    basename: i18n/messages
+  redis:
+    host: 192.168.0.182
+    port: 6379
+  jackson:
+    date-format: yyyy-MM-dd HH:mm:ss
+    time-zone: GMT+8
+eureka:
+  instance:
+    leaseRenewalIntervalInSeconds: 10
+    health-check-url-path: /actuator/health
+    status-page-url-path: /actuator/info
+    prefer-ip-address: true
+    metadata-map:
+      user.name: ${spring.security.user.name}
+      user.password: ${spring.security.user.password}
+  client:
+    registryFetchIntervalSeconds: 5
+    serviceUrl:
+      defaultZone: http://${spring.security.user.name}:${spring.security.user.password}@192.168.0.181:8510/eureka/
+server:
+  port: 8700
+  tomcat:
+    uri-encoding: UTF-8
+management:
+  endpoints:
+    web:
+      exposure:
+        include: "*"
+  endpoint:
+    health:
+      show-details: always
+    shutdown:
+      enabled: true
+    restart:
+      enabled: true
+info:
+  name: '@project.artifactId@'
+  description: '@project.description@'
+  version: '@project.version@'
+  spring-boot-version: '@spring.boot.version@'
+  spring-cloud-version: '@spring.cloud.version@'
+mybatis:
+  type-aliases-package: com.usoftchina.saas.dc.po
+  mapper-locations: classpath:mapper/*.xml

+ 12 - 0
base-servers/datacenter/datacenter-server/src/main/resources/config/application-docker-dev.yml

@@ -0,0 +1,12 @@
+eureka:
+  instance:
+    hostname: saas-datacenter-server-dev
+    prefer-ip-address: false
+  client:
+    serviceUrl:
+      defaultZone: http://${spring.security.user.name}:${spring.security.user.password}@saas-eureka-server-dev:8510/eureka/
+spring:
+  rabbitmq:
+    virtual-host: dev
+server:
+  port: 8710

+ 23 - 0
base-servers/datacenter/datacenter-server/src/main/resources/config/application-docker-prod.yml

@@ -0,0 +1,23 @@
+eureka:
+  instance:
+    hostname: saas-datacenter-server
+    prefer-ip-address: false
+  client:
+    serviceUrl:
+      defaultZone: http://${spring.security.user.name}:${spring.security.user.password}@saas-eureka-server:8500/eureka/
+spring:
+  rabbitmq:
+    host: 10.10.100.103
+    port: 5672
+    virtual-host: docker
+    username: saas
+    password: select123***
+  datasource:
+    url: jdbc:mysql://10.10.100.18:3306/saas_manage?characterEncoding=utf-8&useSSL=false
+    username: saas
+    password: select111***
+  redis:
+    host: 10.10.100.173
+    port: 6379
+logging:
+  destination: 10.10.100.160:5000

+ 12 - 0
base-servers/datacenter/datacenter-server/src/main/resources/config/application-docker-test.yml

@@ -0,0 +1,12 @@
+eureka:
+  instance:
+    hostname: saas-datacenter-server-test
+    prefer-ip-address: false
+  client:
+    serviceUrl:
+      defaultZone: http://${spring.security.user.name}:${spring.security.user.password}@saas-eureka-server-test:8515/eureka/
+spring:
+  rabbitmq:
+    virtual-host: test
+server:
+  port: 8715

+ 10 - 0
base-servers/datacenter/datacenter-server/src/main/resources/config/application-docker.yml

@@ -0,0 +1,10 @@
+eureka:
+  instance:
+    hostname: saas-datacenter-server
+    prefer-ip-address: false
+  client:
+    serviceUrl:
+      defaultZone: http://${spring.security.user.name}:${spring.security.user.password}@saas-eureka-server:8500/eureka/
+spring:
+  rabbitmq:
+    virtual-host: docker

+ 131 - 0
base-servers/datacenter/datacenter-server/src/main/resources/logback-spring.xml

@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+    <include resource="org/springframework/boot/logging/logback/base.xml" />
+    <jmxConfigurator/>
+
+    <!--
+    %m
+    输出代码中指定的消息
+    %p
+    输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL
+    %r
+    输出自应用启动到输出该log信息耗费的毫秒数
+    %c
+    输出所属的类目,通常就是所在类的全名
+    %t
+    输出产生该日志事件的线程名
+    %n
+    输出一个回车换行符,Windows平台为“\r\n”,Unix平台为“\n”
+    %d
+    输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyy MMM dd HH:mm:ss,SSS},
+    输出类似:2002年10月18日 22:10:28,921
+    %l
+    输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数。举例:Testlog4.main(TestLog4.java:10)
+    -->
+
+    <springProperty scope="context" name="log.path" source="logging.path" defaultValue="/var/log/saas/datacenter-server"/>
+    <springProperty scope="context" name="spring.application.name" source="spring.application.name" defaultValue="datacenter-server"/>
+    <springProperty scope="context" name="spring.profiles.active" source="spring.profiles.active" defaultValue="dev"/>
+    <springProperty scope="context" name="common-pattern" source="logging.common-pattern" defaultValue="%d{yyyy-MM-dd HH:mm:ss.SSS}:[%5p] [%t:%r] [%C{1}:%M:%L] --> %m%n"/>
+    <springProperty scope="context" name="log.level.console" source="logging.level.console" defaultValue="INFO"/>
+    <springProperty scope="context" name="log.destination" source="logging.destination" defaultValue="192.168.0.63:5000"/>
+
+    <contextName>${spring.application.name}-${spring.profiles.active}-logback</contextName>
+
+    <appender name="CONSOLE_APPENDER" class="ch.qos.logback.core.ConsoleAppender">
+        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+            <level>${log.level.console}</level>
+        </filter>
+        <encoder>
+            <pattern>${common-pattern}</pattern>
+        </encoder>
+    </appender>
+
+    <appender name="ROOT_APPENDER" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${log.path}/root.log</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+            <fileNamePattern>${log.path}/%d{yyyy-MM}/root-%d{yyyy-MM-dd}-%i.log.gz</fileNamePattern>
+            <maxFileSize>128MB</maxFileSize>
+            <maxHistory>7</maxHistory>
+            <totalSizeCap>20GB</totalSizeCap>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${common-pattern}</pattern>
+        </encoder>
+    </appender>
+
+    <!-- Appender to log in a JSON format -->
+    <appender name="JSON_APPENDER" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
+        <destination>${log.destination}</destination>
+        <encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
+            <providers>
+                <pattern>
+                    <pattern>
+                        {
+                        "severity": "%level",
+                        "service": "${spring.application.name:-}",
+                        "trace": "%X{X-B3-TraceId:-}",
+                        "span": "%X{X-B3-SpanId:-}",
+                        "parent": "%X{X-B3-ParentSpanId:-}",
+                        "exportable": "%X{X-Span-Export:-}",
+                        "pid": "${PID:-}",
+                        "thread": "%thread",
+                        "class": "%logger{40}",
+                        "rest": "%message"
+                        }
+                    </pattern>
+                </pattern>
+            </providers>
+        </encoder>
+    </appender>
+
+    <logger name="org.springframework" level="INFO"/>
+    <logger name="com.usoftchina.saas" level="INFO"/>
+
+    <springProfile name="dev">
+        <root level="INFO">
+            <appender-ref ref="CONSOLE_APPENDER"/>
+        </root>
+    </springProfile>
+
+    <springProfile name="test">
+        <root level="INFO">
+            <appender-ref ref="CONSOLE_APPENDER"/>
+            <appender-ref ref="ROOT_APPENDER"/>
+        </root>
+    </springProfile>
+
+    <springProfile name="docker">
+        <logger name="org.springframework" level="WARN"/>
+        <logger name="com.usoftchina.saas" level="WARN"/>
+        <root level="WARN">
+            <appender-ref ref="CONSOLE_APPENDER"/>
+            <appender-ref ref="JSON_APPENDER"/>
+        </root>
+    </springProfile>
+
+    <springProfile name="docker-dev">
+        <root level="INFO">
+            <appender-ref ref="CONSOLE_APPENDER"/>
+        </root>
+    </springProfile>
+
+    <springProfile name="docker-test">
+        <logger name="org.springframework" level="WARN"/>
+        <logger name="com.usoftchina.saas" level="WARN"/>
+        <root level="WARN">
+            <appender-ref ref="CONSOLE_APPENDER"/>
+            <appender-ref ref="JSON_APPENDER"/>
+        </root>
+    </springProfile>
+
+    <springProfile name="docker-prod">
+        <logger name="org.springframework" level="WARN"/>
+        <logger name="com.usoftchina.saas" level="WARN"/>
+        <root level="WARN">
+            <appender-ref ref="CONSOLE_APPENDER"/>
+            <appender-ref ref="JSON_APPENDER"/>
+        </root>
+    </springProfile>
+
+</configuration>

+ 20 - 0
base-servers/datacenter/datacenter-server/src/main/resources/mapper/DataSourceInfoMapper.xml

@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
+<mapper namespace="com.usoftchina.saas.dc.mapper.DataSourceInfoMapper">
+    <resultMap id="BaseResultMap" type="com.usoftchina.saas.dc.po.DataSourceInfo">
+        <id column="id" jdbcType="INTEGER" property="id"/>
+        <result column="dc_name" jdbcType="VARCHAR" property="dcName"/>
+        <result column="db_name" jdbcType="VARCHAR" property="dbName"/>
+        <result column="db_realname" jdbcType="VARCHAR" property="dbRealName"/>
+        <result column="db_host" jdbcType="VARCHAR" property="dbHost"/>
+        <result column="db_port" jdbcType="INTEGER" property="dbPort"/>
+        <result column="db_username" jdbcType="VARCHAR" property="dbUserName"/>
+        <result column="db_password" jdbcType="VARCHAR" property="dbPassword"/>
+    </resultMap>
+    <sql id="baseColumns">
+        id,dc_name,db_name,db_realname,db_host,db_port,db_username,db_password
+    </sql>
+    <select id="selectByDcNameAndDbName" resultMap="BaseResultMap">
+        select <include refid="baseColumns"/> from datasource where dc_name=#{dcName} and db_name=#{dbName}
+    </select>
+</mapper>

+ 22 - 0
base-servers/datacenter/pom.xml

@@ -0,0 +1,22 @@
+<?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>base-servers</artifactId>
+        <groupId>com.usoftchina.saas</groupId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>datacenter</artifactId>
+    <packaging>pom</packaging>
+    <description>datacenter</description>
+    <modules>
+        <module>datacenter-api</module>
+        <module>datacenter-server</module>
+        <module>datacenter-dto</module>
+        <module>datacenter-client</module>
+    </modules>
+
+</project>

+ 1 - 0
base-servers/pom.xml

@@ -23,6 +23,7 @@
         <module>account</module>
         <module>ui-server</module>
         <module>socket</module>
+        <module>datacenter</module>
     </modules>
 
 </project>

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

@@ -44,7 +44,7 @@ eureka:
   client:
     registryFetchIntervalSeconds: 5
     serviceUrl:
-      defaultZone: http://${spring.security.user.name}:${spring.security.user.password}@127.0.0.1:8500/eureka/
+      defaultZone: http://${spring.security.user.name}:${spring.security.user.password}@192.168.0.181:8510/eureka/
 management:
   endpoints:
     web:

+ 16 - 0
framework/core/src/main/java/com/usoftchina/saas/jdbc/Connectable.java

@@ -0,0 +1,16 @@
+package com.usoftchina.saas.jdbc;
+
+/**
+ * Created by Pro1 on 2017/7/27.
+ */
+public interface Connectable {
+
+    String qualifier();
+
+    String url();
+
+    String username();
+
+    String password();
+
+}

+ 143 - 0
framework/core/src/main/java/com/usoftchina/saas/jdbc/DynamicDataSource.java

@@ -0,0 +1,143 @@
+package com.usoftchina.saas.jdbc;
+
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.jdbc.datasource.AbstractDataSource;
+import org.springframework.jdbc.datasource.lookup.DataSourceLookup;
+import org.springframework.jdbc.datasource.lookup.JndiDataSourceLookup;
+import org.springframework.util.Assert;
+
+import javax.sql.DataSource;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * Created by Pro1 on 2017/7/27.
+ * 基于{@AbstractRoutingDataSource},解决不能动态添加新数据源的问题
+ */
+public class DynamicDataSource extends AbstractDataSource implements InitializingBean {
+
+    private Map<Object, Object> targetDataSources;
+    private Object defaultTargetDataSource;
+    private boolean lenientFallback = true;
+    private DataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
+    private Map<Object, DataSource> resolvedDataSources;
+    private DataSource resolvedDefaultDataSource;
+
+    public void setTargetDataSources(Map<Object, Object> targetDataSources) {
+        this.targetDataSources = targetDataSources;
+    }
+
+    public void setDefaultTargetDataSource(Object defaultTargetDataSource) {
+        this.defaultTargetDataSource = defaultTargetDataSource;
+    }
+
+    public void setLenientFallback(boolean lenientFallback) {
+        this.lenientFallback = lenientFallback;
+    }
+
+    public void setDataSourceLookup(DataSourceLookup dataSourceLookup) {
+        this.dataSourceLookup = (DataSourceLookup)(dataSourceLookup != null?dataSourceLookup:new JndiDataSourceLookup());
+    }
+
+    @Override
+    public void afterPropertiesSet() {
+        if(this.targetDataSources == null) {
+            throw new IllegalArgumentException("Property \'targetDataSources\' is required");
+        } else {
+            this.resolvedDataSources = new HashMap(this.targetDataSources.size());
+            Iterator var1 = this.targetDataSources.entrySet().iterator();
+
+            while(var1.hasNext()) {
+                Map.Entry entry = (Map.Entry)var1.next();
+                Object lookupKey = this.resolveSpecifiedLookupKey(entry.getKey());
+                DataSource dataSource = this.resolveSpecifiedDataSource(entry.getValue());
+                this.resolvedDataSources.put(lookupKey, dataSource);
+            }
+
+            if(this.defaultTargetDataSource != null) {
+                this.resolvedDefaultDataSource = this.resolveSpecifiedDataSource(this.defaultTargetDataSource);
+            }
+
+        }
+    }
+
+    protected Object resolveSpecifiedLookupKey(Object lookupKey) {
+        return lookupKey;
+    }
+
+    protected DataSource resolveSpecifiedDataSource(Object dataSource) throws IllegalArgumentException {
+        if(dataSource instanceof DataSource) {
+            return (DataSource)dataSource;
+        } else if(dataSource instanceof String) {
+            return this.dataSourceLookup.getDataSource((String)dataSource);
+        } else {
+            throw new IllegalArgumentException("Illegal data source value - only [javax.sql.DataSource] and String supported: " + dataSource);
+        }
+    }
+
+    @Override
+    public Connection getConnection() throws SQLException {
+        return this.determineTargetDataSource().getConnection();
+    }
+
+    @Override
+    public Connection getConnection(String username, String password) throws SQLException {
+        return this.determineTargetDataSource().getConnection(username, password);
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public <T> T unwrap(Class<T> iface) throws SQLException {
+        if (iface.isInstance(this)) {
+            return (T) this;
+        }
+        return determineTargetDataSource().unwrap(iface);
+    }
+
+    @Override
+    public boolean isWrapperFor(Class<?> iface) throws SQLException {
+        return (iface.isInstance(this) || determineTargetDataSource().isWrapperFor(iface));
+    }
+
+    protected DataSource determineTargetDataSource() {
+        Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
+        Object lookupKey = this.determineCurrentLookupKey();
+        DataSource dataSource = (DataSource)this.resolvedDataSources.get(lookupKey);
+        if(dataSource == null && (this.lenientFallback || lookupKey == null)) {
+            dataSource = this.resolvedDefaultDataSource;
+        }
+
+        if(dataSource == null) {
+            throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
+        } else {
+            return dataSource;
+        }
+    }
+
+    protected Object determineCurrentLookupKey() {
+        return DynamicDataSourceContextHolder.get();
+    }
+
+    public boolean containsDataSource(Object key) {
+        Object lookupKey = this.resolveSpecifiedLookupKey(key);
+        return this.resolvedDataSources.containsKey(lookupKey);
+    }
+
+    public void addDataSource(Object key, Object value) {
+        Object lookupKey = this.resolveSpecifiedLookupKey(key);
+        if (!containsDataSource(lookupKey)) {
+            DataSource dataSource = this.resolveSpecifiedDataSource(value);
+            this.resolvedDataSources.put(lookupKey, dataSource);
+        }
+    }
+
+    public void removeDataSource(Object key) {
+        Object lookupKey = this.resolveSpecifiedLookupKey(key);
+        if (!containsDataSource(lookupKey)) {
+            this.resolvedDataSources.remove(key);
+        }
+    }
+}

+ 25 - 0
framework/core/src/main/java/com/usoftchina/saas/jdbc/DynamicDataSourceContextHolder.java

@@ -0,0 +1,25 @@
+package com.usoftchina.saas.jdbc;
+
+/**
+ * Created by Pro1 on 2017/7/27.
+ */
+public class DynamicDataSourceContextHolder {
+
+    private static final ThreadLocal<String> contextHolder = new InheritableThreadLocal<>();
+
+    public static void set(String dataSource) {
+        contextHolder.set(dataSource);
+    }
+
+    public static void set(Connectable connectable) {
+        contextHolder.set(connectable.qualifier());
+    }
+
+    public static String get() {
+        return contextHolder.get();
+    }
+
+    public static void clear() {
+        contextHolder.remove();
+    }
+}

+ 42 - 0
framework/core/src/main/java/com/usoftchina/saas/jdbc/DynamicDataSourceRegister.java

@@ -0,0 +1,42 @@
+package com.usoftchina.saas.jdbc;
+
+import com.zaxxer.hikari.HikariDataSource;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
+import org.springframework.boot.jdbc.DataSourceBuilder;
+
+/**
+ * Created by Pro1 on 2017/7/27.
+ */
+public class DynamicDataSourceRegister {
+
+    private DataSourceProperties defaultProperties;
+    private final String defaultDatabaseName;
+    private DynamicDataSource dynamicDataSource;
+
+    public DynamicDataSourceRegister(DataSourceProperties defaultProperties, DynamicDataSource dynamicDataSource) {
+        this.defaultProperties = defaultProperties;
+        this.defaultDatabaseName = JdbcUrl.getDatabaseName(defaultProperties.determineUrl());
+        this.dynamicDataSource = dynamicDataSource;
+    }
+
+    public void createDataSource(Connectable connectable) {
+        if (!dynamicDataSource.containsDataSource(connectable.qualifier())) {
+            HikariDataSource dataSource = DataSourceBuilder.create(defaultProperties.getClassLoader())
+                    .type(HikariDataSource.class)
+                    .driverClassName(defaultProperties.determineDriverClassName())
+                    .url(connectable.url())
+                    .username(connectable.username())
+                    .password(connectable.password())
+                    .build();
+            dynamicDataSource.addDataSource(connectable.qualifier(), dataSource);
+        }
+    }
+
+    public void unregister(Connectable connectable) {
+        dynamicDataSource.removeDataSource(connectable.qualifier());
+    }
+
+    public String getDefaultDatabaseName() {
+        return defaultDatabaseName;
+    }
+}

+ 60 - 0
framework/core/src/main/java/com/usoftchina/saas/jdbc/JdbcUrl.java

@@ -0,0 +1,60 @@
+package com.usoftchina.saas.jdbc;
+
+import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @author yingp
+ * @date 2018/12/10
+ */
+public class JdbcUrl {
+
+    public static String getDatabaseName(String url) {
+        Type type = Type.fromJdbcUrl(url);
+        Pattern pattern = type.getNamePattern();
+        if (null != pattern) {
+            Matcher matcher = pattern.matcher(url);
+            if (matcher.find()) {
+                return matcher.group(1);
+            }
+        }
+        return null;
+    }
+
+    public enum Type {
+        MYSQL(".+\\/([^\\/]+)\\?.+"),
+        POSTGRESQL(null),
+        ORACLE(null),
+        UNKNOWN(null);
+
+        private Pattern namePattern;
+
+        Type(String nameRegexp) {
+            if (null != nameRegexp) {
+                this.namePattern = Pattern.compile(nameRegexp);
+            }
+        }
+
+        protected String getUrlPrefix() {
+            return this.name().toLowerCase(Locale.ENGLISH);
+        }
+
+        public static Type fromJdbcUrl(String url) {
+            if (null != url) {
+                String urlWithoutPrefix = url.substring("jdbc".length()).toLowerCase(Locale.ENGLISH);
+                for (Type type : Type.values()) {
+                    String prefix = ":" + type.getUrlPrefix() + ":";
+                    if (type != Type.UNKNOWN && urlWithoutPrefix.startsWith(prefix)) {
+                        return type;
+                    }
+                }
+            }
+            return Type.UNKNOWN;
+        }
+
+        public Pattern getNamePattern() {
+            return namePattern;
+        }
+    }
+}

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

@@ -33,6 +33,11 @@
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-web</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.mybatis.spring.boot</groupId>
+            <artifactId>mybatis-spring-boot-starter</artifactId>
+            <scope>provided</scope>
+        </dependency>
         <dependency>
             <groupId>javax.servlet</groupId>
             <artifactId>javax.servlet-api</artifactId>

+ 4 - 1
framework/server-starter/src/main/java/com/usoftchina/saas/server/ServerAutoConfiguration.java

@@ -21,7 +21,10 @@ import org.springframework.context.annotation.Configuration;
  * @date 2018/10/13
  */
 @Configuration
-@ComponentScan(basePackages = {"com.usoftchina.saas.server"})
+@ComponentScan(basePackages = {
+        "com.usoftchina.saas.server.error",
+        "com.usoftchina.saas.server.web"
+})
 public class ServerAutoConfiguration {
 
 //    private Logger logger = LoggerFactory.getLogger(UnCaughtErrorFilter.class);

+ 1 - 1
framework/server-starter/src/main/java/com/usoftchina/saas/server/error/ErrorConfig.java → framework/server-starter/src/main/java/com/usoftchina/saas/server/error/DefaultErrorConfig.java

@@ -12,7 +12,7 @@ import javax.servlet.DispatcherType;
  * @date 2018/10/16
  */
 @Configuration
-public class ErrorConfig {
+public class DefaultErrorConfig {
 
     @Bean
     public FilterRegistrationBean<UnCaughtErrorFilter> unCaughtErrorFilter() {

+ 140 - 0
framework/server-starter/src/main/java/com/usoftchina/saas/server/jdbc/DynamicDataSourceConfig.java

@@ -0,0 +1,140 @@
+package com.usoftchina.saas.server.jdbc;
+
+import com.usoftchina.saas.jdbc.DynamicDataSource;
+import com.usoftchina.saas.jdbc.DynamicDataSourceRegister;
+import com.zaxxer.hikari.HikariDataSource;
+import org.apache.ibatis.mapping.DatabaseIdProvider;
+import org.apache.ibatis.plugin.Interceptor;
+import org.apache.ibatis.session.ExecutorType;
+import org.apache.ibatis.session.SqlSessionFactory;
+import org.mybatis.spring.SqlSessionFactoryBean;
+import org.mybatis.spring.SqlSessionTemplate;
+import org.mybatis.spring.boot.autoconfigure.ConfigurationCustomizer;
+import org.mybatis.spring.boot.autoconfigure.MybatisProperties;
+import org.mybatis.spring.boot.autoconfigure.SpringBootVFS;
+import org.springframework.beans.factory.ObjectProvider;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+import org.springframework.core.io.ResourceLoader;
+import org.springframework.jdbc.datasource.DataSourceTransactionManager;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.ObjectUtils;
+import org.springframework.util.StringUtils;
+
+import javax.sql.DataSource;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author yingp
+ * @date 2018/12/10
+ */
+@Configuration
+@ConditionalOnClass({HikariDataSource.class})
+@ConditionalOnMissingBean({DataSource.class})
+@EnableConfigurationProperties({
+        DataSourceProperties.class,
+        MybatisProperties.class
+})
+public class DynamicDataSourceConfig {
+
+    @Autowired
+    private DataSourceProperties properties;
+
+    @Autowired
+    private MybatisProperties mybatisProperties;
+
+    @Primary
+    @Bean(name = "defaultDataSource")
+    public HikariDataSource defaultDataSource() {
+        HikariDataSource dataSource = properties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
+        if (StringUtils.hasText(properties.getName())) {
+            dataSource.setPoolName(properties.getName());
+        }
+
+        return dataSource;
+    }
+
+    @Bean(name = "dynamicDataSource")
+    public DynamicDataSource dynamicDataSource(@Qualifier("defaultDataSource") DataSource defaultDataSource) {
+        Map<Object, Object> targetDataSources = new HashMap<>(1);
+        targetDataSources.put("defaultDataSource", defaultDataSource);
+        DynamicDataSource dataSource = new DynamicDataSource();
+        dataSource.setDefaultTargetDataSource(defaultDataSource);
+        dataSource.setTargetDataSources(targetDataSources);
+        return dataSource;
+    }
+
+    @Bean
+    public SqlSessionFactory sqlSessionFactory(@Qualifier("dynamicDataSource") DataSource dataSource,
+                                               ObjectProvider<Interceptor[]> interceptorsProvider,
+                                               ResourceLoader resourceLoader,
+                                               ObjectProvider<DatabaseIdProvider> databaseIdProvider,
+                                               ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider) throws Exception {
+        SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
+        factory.setDataSource(dataSource);
+        factory.setVfs(SpringBootVFS.class);
+        if (StringUtils.hasText(mybatisProperties.getConfigLocation())) {
+            factory.setConfigLocation(resourceLoader.getResource(mybatisProperties.getConfigLocation()));
+        }
+        org.apache.ibatis.session.Configuration configuration = mybatisProperties.getConfiguration();
+        if (configuration == null && !StringUtils.hasText(mybatisProperties.getConfigLocation())) {
+            configuration = new org.apache.ibatis.session.Configuration();
+        }
+        List<ConfigurationCustomizer> configurationCustomizers = configurationCustomizersProvider.getIfAvailable();
+        if (configuration != null && !CollectionUtils.isEmpty(configurationCustomizers)) {
+            for (ConfigurationCustomizer customizer : configurationCustomizers) {
+                customizer.customize(configuration);
+            }
+        }
+        factory.setConfiguration(configuration);
+        if (mybatisProperties.getConfigurationProperties() != null) {
+            factory.setConfigurationProperties(mybatisProperties.getConfigurationProperties());
+        }
+        if (!ObjectUtils.isEmpty(interceptorsProvider)) {
+            factory.setPlugins(interceptorsProvider.getIfAvailable());
+        }
+        if (databaseIdProvider != null) {
+            factory.setDatabaseIdProvider(databaseIdProvider.getIfAvailable());
+        }
+        if (StringUtils.hasLength(mybatisProperties.getTypeAliasesPackage())) {
+            factory.setTypeAliasesPackage(mybatisProperties.getTypeAliasesPackage());
+        }
+        if (StringUtils.hasLength(mybatisProperties.getTypeHandlersPackage())) {
+            factory.setTypeHandlersPackage(mybatisProperties.getTypeHandlersPackage());
+        }
+        if (!ObjectUtils.isEmpty(mybatisProperties.resolveMapperLocations())) {
+            factory.setMapperLocations(mybatisProperties.resolveMapperLocations());
+        }
+
+        return factory.getObject();
+    }
+
+    @Bean
+    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
+        ExecutorType executorType = mybatisProperties.getExecutorType();
+        if (executorType != null) {
+            return new SqlSessionTemplate(sqlSessionFactory, executorType);
+        } else {
+            return new SqlSessionTemplate(sqlSessionFactory);
+        }
+    }
+
+    @Bean
+    public DataSourceTransactionManager transactionManager(@Qualifier("dynamicDataSource") DynamicDataSource dataSource) throws Exception {
+        return new DataSourceTransactionManager(dataSource);
+    }
+
+    @Bean
+    public DynamicDataSourceRegister dataSourceRegister(@Qualifier("dynamicDataSource") DynamicDataSource dataSource) {
+        return new DynamicDataSourceRegister(properties, dataSource);
+    }
+}

+ 17 - 0
framework/server-starter/src/main/java/com/usoftchina/saas/server/jdbc/EnableDynamicDataSource.java

@@ -0,0 +1,17 @@
+package com.usoftchina.saas.server.jdbc;
+
+import org.springframework.context.annotation.Import;
+
+import java.lang.annotation.*;
+
+/**
+ * @author yingp
+ * @date 2018/12/11
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Import(DynamicDataSourceConfig.class)
+@Documented
+@Inherited
+public @interface EnableDynamicDataSource {
+}

File diff suppressed because it is too large
+ 1 - 0
frontend/saas-web/electron/lib/sockjs.min.js


File diff suppressed because it is too large
+ 7 - 0
frontend/saas-web/electron/lib/stomp.min.js


+ 60 - 0
frontend/saas-web/electron/login.html

@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<html lang="zh_CN">
+    <head>
+        <title>账户登录 - U企云服</title>
+        <style>
+            body {
+                margin: 0;
+            }
+            iframe {
+                border: 0;
+            }
+        </style>
+        <script src="./lib/sockjs.min.js"></script>
+        <script src="./lib/stomp.min.js"></script>
+        <script>
+            var Socket = (function(){
+                return {
+                    subscribe: function(clientId, dest) {
+                        return new Promise((resolve, reject) => {
+                            const socket = new SockJS('https://saas-api.usoftchina.com/ws');
+                            stompClient = Stomp.over(socket);
+                            stompClient.connect({}, function (frame) {
+                                stompClient.subscribe('/clients/' + clientId + dest, function (message) {
+                                    stompClient.disconnect(function () {
+                                        resolve(JSON.parse(message.body));
+                                    });
+                                });
+                            });
+                        })
+                    },
+                    disconnect: function(resolve) {
+                        if (stompClient) {
+                            stompClient.disconnect(resolve);
+                        } else {
+                            resolve();
+                        }
+                    }
+                }
+            })();
+        </script>
+    </head>
+    <body>
+        <script>
+            const ipc = require('electron').ipcRenderer;
+            var clientId = Math.random().toString(36).substr(2);
+            Socket.subscribe(clientId, '/sso/callback').then(function(data){
+                var session = data.token, account = data.account;
+                account.companies = account.companies || [];
+                session.account = account;
+                ipc.send('session.change', session);
+            });
+            var frame = document.createElement("iframe");
+            frame.setAttribute("src", "https://sso.ubtob.com/sassLogin?appId=sp&baseUrl=" + 
+                encodeURIComponent('https://saas-api.usoftchina.com/api/auth/sso/callback/' + clientId));
+            frame.setAttribute("width", "430");
+            frame.setAttribute("height", "504");
+            document.body.appendChild(frame);
+        </script>       
+    </body>
+</html>

+ 33 - 4
frontend/saas-web/electron/main.js

@@ -1,11 +1,40 @@
-const { app, BrowserWindow } = require('electron');
+const { app, BrowserWindow, ipcMain } = require('electron');
 const path = require('path');
 const url = require('url');
 const isLocal = process.argv[process.argv.length - 1] === 'local';
 const extDir = isLocal ? '../build/production/saas' : 'dist';
+let loginWindow;
 let mainWindow;
 
-function createWindow () {
+// 监听session变化
+ipcMain.on('session.change', (event, arg) => {
+    if (arg) {
+        loginWindow && loginWindow.close();
+        createMainWindow();
+    } else {
+        mainWindow && mainWindow.close();
+        createLoginWindow();
+    }
+});
+
+function createLoginWindow() {
+    loginWindow = new BrowserWindow({ width: 453, height: 513, fullscreenable: false, maximizable: false, resizable: false });
+    loginWindow.loadURL(url.format({
+        pathname: path.join(__dirname, 'login.html'),
+        protocol: 'file:',
+        slashes: true
+    }));
+
+    if (isLocal) {
+        loginWindow.webContents.openDevTools();
+    }
+
+    loginWindow.on('closed', function () {
+        loginWindow = null;
+    });
+}
+
+function createMainWindow () {
     mainWindow = new BrowserWindow({ width: 1280, height: 720, show: false });
     mainWindow.once('ready-to-show', function(){
         mainWindow.maximize();
@@ -26,7 +55,7 @@ function createWindow () {
     });
 }
 
-app.on('ready', createWindow);
+app.on('ready', createLoginWindow);
 
 // Quit when all windows are closed, except for Mac users
 app.on('window-all-closed', function () {
@@ -37,6 +66,6 @@ app.on('window-all-closed', function () {
 
 app.on('activate', function () {
     if (mainWindow === null) {
-        createWindow();
+        createMainWindow();
     }
 });

+ 15 - 0
pom.xml

@@ -309,6 +309,21 @@
                 <artifactId>socket-api</artifactId>
                 <version>${project.release.version}</version>
             </dependency>
+            <dependency>
+                <groupId>com.usoftchina.saas</groupId>
+                <artifactId>datacenter-dto</artifactId>
+                <version>${project.release.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.usoftchina.saas</groupId>
+                <artifactId>datacenter-api</artifactId>
+                <version>${project.release.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.usoftchina.saas</groupId>
+                <artifactId>datacenter-client</artifactId>
+                <version>${project.release.version}</version>
+            </dependency>
             <!-- file upload -->
             <dependency>
                 <groupId>io.github.openfeign.form</groupId>

+ 18 - 0
script/mysql/init/manage.sql

@@ -0,0 +1,18 @@
+CREATE DATABASE `saas_manage` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
+use `saas_manage`;
+
+create table `datacenter` (
+  name varchar(30) primary key not null,
+  description varchar(300) comment '描述'
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='数据中心';
+
+create table `datasource` (
+  id int primary key not null auto_increment,
+  dc_name varchar(30) not null,
+  db_name varchar(30) not null comment '数据库名',
+	db_realname varchar(30) comment '数据库真实名',
+  db_host varchar(100) comment '数据库服务器',
+  db_port int comment '数据库端口',
+  db_username varchar(30) comment '数据库登录账号',
+	db_password varchar(64) comment '数据库登录密码'
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='数据连接';

Some files were not shown because too many files changed in this diff