Browse Source

Merge branch 'dev' of ssh://10.10.100.21/source/saas-platform into dev

rainco 7 years ago
parent
commit
7fbdf38f33
79 changed files with 1975 additions and 172 deletions
  1. 42 14
      applications/commons/commons-server/src/main/java/com/usoftchina/saas/commons/service/impl/ExcelServiceImpl.java
  2. 3 3
      applications/money/money-server/src/main/java/com/usoftchina/saas/money/service/impl/VerificationServiceImpl.java
  3. 5 0
      applications/purchase/purchase-server/pom.xml
  4. 0 1
      applications/purchase/purchase-server/src/main/java/com/usoftchina/saas/purchase/PurchaseApplication.java
  5. 3 0
      applications/purchase/purchase-server/src/main/resources/application.yml
  6. 3 0
      applications/purchase/purchase-server/src/main/resources/config/application-docker-prod.yml
  7. 0 45
      applications/purchase/purchase-server/src/test/PurchaseTest.java
  8. 33 0
      applications/purchase/purchase-server/src/test/java/com/usoftchina/saas/purchase/controller/PurchaseControllerTest.java
  9. 77 0
      base-servers/account/account-api/src/main/java/com/usoftchina/saas/account/cache/CompanyCache.java
  10. 9 0
      base-servers/account/account-dto/src/main/java/com/usoftchina/saas/account/dto/AccountDTO.java
  11. 10 0
      base-servers/account/account-dto/src/main/java/com/usoftchina/saas/account/dto/CompanyBaseDTO.java
  12. 14 0
      base-servers/account/account-dto/src/main/java/com/usoftchina/saas/account/dto/CompanyDTO.java
  13. 12 0
      base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/po/Company.java
  14. 10 0
      base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/vo/CompanyBaseVO.java
  15. 15 4
      base-servers/account/account-server/src/main/resources/mapper/CompanyMapper.xml
  16. 32 0
      base-servers/datacenter/datacenter-api/pom.xml
  17. 26 0
      base-servers/datacenter/datacenter-api/src/main/java/com/usoftchina/saas/dc/api/DataSourceApi.java
  18. 69 0
      base-servers/datacenter/datacenter-api/src/main/java/com/usoftchina/saas/dc/cache/DataSourceCache.java
  19. 35 0
      base-servers/datacenter/datacenter-client/pom.xml
  20. 28 0
      base-servers/datacenter/datacenter-client/src/main/java/com/usoftchina/saas/dc/client/configuration/DataCenterAutoConfiguration.java
  21. 52 0
      base-servers/datacenter/datacenter-client/src/main/java/com/usoftchina/saas/dc/client/interceptor/DataSourceInterceptor.java
  22. 3 0
      base-servers/datacenter/datacenter-client/src/main/resources/META-INF/spring.factories
  23. 22 0
      base-servers/datacenter/datacenter-dto/pom.xml
  24. 109 0
      base-servers/datacenter/datacenter-dto/src/main/java/com/usoftchina/saas/dc/dto/DataSourceInfoDTO.java
  25. 54 0
      base-servers/datacenter/datacenter-server/pom.xml
  26. 6 0
      base-servers/datacenter/datacenter-server/src/main/docker/Dockerfile
  27. 20 0
      base-servers/datacenter/datacenter-server/src/main/java/com/usoftchina/saas/dc/DatacenterApplication.java
  28. 28 0
      base-servers/datacenter/datacenter-server/src/main/java/com/usoftchina/saas/dc/controller/DataSourceInfoController.java
  29. 21 0
      base-servers/datacenter/datacenter-server/src/main/java/com/usoftchina/saas/dc/mapper/DataSourceInfoMapper.java
  30. 82 0
      base-servers/datacenter/datacenter-server/src/main/java/com/usoftchina/saas/dc/po/DataSourceInfo.java
  31. 19 0
      base-servers/datacenter/datacenter-server/src/main/java/com/usoftchina/saas/dc/service/DataSourceInfoService.java
  32. 23 0
      base-servers/datacenter/datacenter-server/src/main/java/com/usoftchina/saas/dc/service/impl/DataSourceInfoServiceImpl.java
  33. 81 0
      base-servers/datacenter/datacenter-server/src/main/resources/application.yml
  34. 12 0
      base-servers/datacenter/datacenter-server/src/main/resources/config/application-docker-dev.yml
  35. 23 0
      base-servers/datacenter/datacenter-server/src/main/resources/config/application-docker-prod.yml
  36. 12 0
      base-servers/datacenter/datacenter-server/src/main/resources/config/application-docker-test.yml
  37. 10 0
      base-servers/datacenter/datacenter-server/src/main/resources/config/application-docker.yml
  38. 131 0
      base-servers/datacenter/datacenter-server/src/main/resources/logback-spring.xml
  39. 20 0
      base-servers/datacenter/datacenter-server/src/main/resources/mapper/DataSourceInfoMapper.xml
  40. 22 0
      base-servers/datacenter/pom.xml
  41. 1 0
      base-servers/pom.xml
  42. 1 1
      base-servers/ui-server/src/main/resources/application.yml
  43. 16 0
      framework/core/src/main/java/com/usoftchina/saas/jdbc/Connectable.java
  44. 143 0
      framework/core/src/main/java/com/usoftchina/saas/jdbc/DynamicDataSource.java
  45. 25 0
      framework/core/src/main/java/com/usoftchina/saas/jdbc/DynamicDataSourceContextHolder.java
  46. 42 0
      framework/core/src/main/java/com/usoftchina/saas/jdbc/DynamicDataSourceRegister.java
  47. 60 0
      framework/core/src/main/java/com/usoftchina/saas/jdbc/JdbcUrl.java
  48. 17 2
      framework/core/src/main/java/com/usoftchina/saas/utils/RegexpUtils.java
  49. 5 0
      framework/server-starter/pom.xml
  50. 4 1
      framework/server-starter/src/main/java/com/usoftchina/saas/server/ServerAutoConfiguration.java
  51. 1 1
      framework/server-starter/src/main/java/com/usoftchina/saas/server/error/DefaultErrorConfig.java
  52. 140 0
      framework/server-starter/src/main/java/com/usoftchina/saas/server/jdbc/DynamicDataSourceConfig.java
  53. 17 0
      framework/server-starter/src/main/java/com/usoftchina/saas/server/jdbc/EnableDynamicDataSource.java
  54. 2 1
      frontend/saas-portal-web/src/components/conenter/addenterprise.vue
  55. 1 1
      frontend/saas-portal-web/src/components/conenter/company.vue
  56. 2 4
      frontend/saas-portal-web/src/components/conenter/details.vue
  57. 5 3
      frontend/saas-portal-web/src/components/conenter/enterprise.vue
  58. 8 0
      frontend/saas-portal-web/src/pages/index/index.js
  59. 28 2
      frontend/saas-portal-web/src/pages/index/index.vue
  60. 1 1
      frontend/saas-portal-web/src/store/index.js
  61. 48 15
      frontend/saas-web/app/view/core/base/ImportWindow.js
  62. 1 1
      frontend/saas-web/app/view/core/chart/ChartBase.js
  63. 1 1
      frontend/saas-web/app/view/home/Home.js
  64. 13 0
      frontend/saas-web/app/view/home/charts/KeyData.scss
  65. 17 25
      frontend/saas-web/app/view/home/charts/MonthIO.js
  66. 11 0
      frontend/saas-web/app/view/home/charts/MonthIO.scss
  67. 4 4
      frontend/saas-web/app/view/home/charts/MonthPurchase.js
  68. 5 21
      frontend/saas-web/app/view/home/charts/MonthSale.js
  69. 4 4
      frontend/saas-web/app/view/home/charts/ProfitDetail.js
  70. 4 4
      frontend/saas-web/app/view/home/charts/PurchaseTrend.js
  71. 5 5
      frontend/saas-web/app/view/home/charts/SaleTrend.js
  72. 29 0
      frontend/saas-web/app/view/home/charts/SaleTrend.scss
  73. 4 4
      frontend/saas-web/app/view/home/charts/StockAmount.js
  74. 1 0
      frontend/saas-web/electron/lib/sockjs.min.js
  75. 7 0
      frontend/saas-web/electron/lib/stomp.min.js
  76. 60 0
      frontend/saas-web/electron/login.html
  77. 33 4
      frontend/saas-web/electron/main.js
  78. 15 0
      pom.xml
  79. 18 0
      script/mysql/init/manage.sql

+ 42 - 14
applications/commons/commons-server/src/main/java/com/usoftchina/saas/commons/service/impl/ExcelServiceImpl.java

@@ -14,6 +14,7 @@ import com.usoftchina.saas.context.BaseContextHolder;
 import com.usoftchina.saas.exception.BizException;
 import com.usoftchina.saas.utils.CollectionUtils;
 import com.usoftchina.saas.utils.DateUtils;
+import com.usoftchina.saas.utils.RegexpUtils;
 import com.usoftchina.saas.utils.StringUtils;
 import org.apache.poi.hssf.util.HSSFColor;
 import org.apache.poi.ss.usermodel.*;
@@ -163,27 +164,28 @@ public class ExcelServiceImpl implements ExcelService{
                        List<TempletSet> main = columns.get(position);
                        for (TempletSet set : main) {
                            //取excel值
-                           if ("true".equals(set.getNecessary())) {
+                           value = data.get(set.getDescription());
+                          /* if ("true".equals(set.getNecessary())) {
                                value = data.get("*" + set.getDescription());
                            } else {
                                value = data.get(set.getDescription());
-                           }
+                           }*/
                            //取编号字段
-                           if (set.isCodefield() && !data.get("*" + set.getDescription()).equals("")) {
+                           if (set.isCodefield() && !"".equals(value)) {
                                mainData = new JSONObject();
                                codeValue = value;
                                difference = true;
                                validateCode.add(codeValue);
                            }
                            //检查是否是同一单
-                           if (set.isCodefield() && data.get("*" + set.getDescription()).equals("")) {
+                           if (set.isCodefield() && "".equals(value)) {
                                difference = false;
                                break;
                            }
                            //检查主表必填字段是否完整
                            if (difference) {
                                dd.setDd_codevalue(codeValue);
-                               if ("true".equals(set.getNecessary()) && data.get("*" + set.getDescription()).equals("")) {
+                               if ("true".equals(set.getNecessary()) && "".equals(value)) {
                                    err.append("第" + (i + 3) + "行 " + set.getDescription() + " 必填字段未填写!<br/> ");
                                    break;
                                }
@@ -196,9 +198,18 @@ public class ExcelServiceImpl implements ExcelService{
                                    break;
                                }
                            }
-                           //如果为数字类型且为空默认赋值0
-                           if ("number".equals(set.getType()) && StringUtils.isEmpty(value)) {
-                                value = "0";
+                           //如果为数字类型且为空默认赋值0,检测是否为数字类型
+                           if ("number".equals(set.getType())) {
+                               if (StringUtils.isEmpty(value)) {
+                                   value = "0";
+                               } else {
+                                   Boolean numner = isNumner(value);
+                                   if (!numner) {
+                                       err.append("第" + (i + 3) + "行 " + set.getDescription() + " 数字格式不正确!<br/> ");
+                                       break;
+                                   }
+                               }
+
                            }
                            //插入主表数据
                            if (null != mainData) {
@@ -216,11 +227,12 @@ public class ExcelServiceImpl implements ExcelService{
                        detailData = new JSONObject();
                        for (TempletSet set : detail) {
                            //取excel值
-                           if ("true".equals(set.getNecessary())) {
+                           value = data.get(set.getDescription());
+                          /* if ("true".equals(set.getNecessary())) {
                                value = data.get("*" + set.getDescription());
                            } else {
                                value = data.get(set.getDescription());
-                           }
+                           }*/
                            if ("true".equals(set.getNecessary()) && value.equals("")) {
                                detailData = null;
                                //err.append("第" + (i + 3) + "行 " + set.getDescription() + " 必填字段未填写!<br/> ");
@@ -284,6 +296,19 @@ public class ExcelServiceImpl implements ExcelService{
     }
 
 
+    private Boolean isNumner(String str) {
+        for(int i=str.length() - 1; i>=0; i--){
+            int chr=str.charAt(i);
+            if(chr < 48 || chr >  57) {
+                if (chr == 46) {
+                    continue;
+                }
+                return false;
+            }
+
+        }
+        return true;
+    }
 
 
     private boolean validateDateFormat(String date) {
@@ -333,6 +358,7 @@ public class ExcelServiceImpl implements ExcelService{
             switch(cell.getCellType()){
                 case Cell.CELL_TYPE_NUMERIC:{
                     cellValue = String.valueOf(cell.getNumericCellValue());
+                    cellValue = RegexpUtils.replaceSpecialCharacter(cellValue);
                     //判断是否为INT类型
                     if (Double.valueOf(cellValue.toString()).intValue() - Double.valueOf(cellValue.toString()) == 0) {
                         return cellValue.toString().substring(0, cellValue.toString().indexOf("."));
@@ -344,14 +370,17 @@ public class ExcelServiceImpl implements ExcelService{
                     if(DateUtil.isCellDateFormatted(cell)){
                         //转换为日期格式YYYY-mm-dd
                         cellValue = cell.getDateCellValue();
+                        cellValue = RegexpUtils.replaceSpecialCharacter(cellValue);
                     }else{
                         //数字
                         cellValue = String.valueOf(cell.getNumericCellValue());
+                        cellValue = RegexpUtils.replaceSpecialCharacter(cellValue);
                     }
                     break;
                 }
                 case Cell.CELL_TYPE_STRING:{
                     cellValue = cell.getRichStringCellValue().getString();
+                    cellValue = RegexpUtils.replaceSpecialCharacter(cellValue);
                     break;
                 }
                 default:
@@ -464,16 +493,15 @@ public class ExcelServiceImpl implements ExcelService{
 
     private CellStyle getCellStyle(SXSSFWorkbook workbook, boolean main) {
         CellStyle style = workbook.createCellStyle();
-        style.setFillBackgroundColor((short) 18);
-        style.setFillPattern(FillPatternType.LEAST_DOTS);
         Font font = workbook.createFont();
         font.setFontName("仿宋_GB2312");// 字体
         font.setFontHeightInPoints((short) 12);// 字号
+        font.setBold(true);
         font.setColor((short)64);// 颜色
         style.setFont(font);
+        style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
         style.setAlignment(HorizontalAlignment.CENTER);
-        style.setFillForegroundColor(HSSFColor.GREEN.index);
-        style.setFillBackgroundColor(main ? HSSFColor.LIGHT_YELLOW.index : HSSFColor.PALE_BLUE.index);
+        style.setFillForegroundColor(main ? HSSFColor.LIGHT_YELLOW.index : HSSFColor.PALE_BLUE.index);
         style.setBorderBottom(BorderStyle.MEDIUM);
         style.setBorderLeft(BorderStyle.MEDIUM);
         style.setBorderRight(BorderStyle.MEDIUM);

+ 3 - 3
applications/money/money-server/src/main/java/com/usoftchina/saas/money/service/impl/VerificationServiceImpl.java

@@ -291,8 +291,6 @@ public class VerificationServiceImpl extends CommonBaseServiceImpl<VerificationM
             subledger.setSl_custid(verification.getVc_custid());
             subledger.setSl_vendid(0);
         }
-        kind = transferKind(kind);
-        subledger.setSl_kind(kind);
         Double amount1 = verification.getVc_amount1()==null?new Double(0):verification.getVc_amount1();
         Double amount2 = verification.getVc_amount2()==null?new Double(0):verification.getVc_amount2();
         subledger.setSl_date(verification.getVc_date());
@@ -306,8 +304,10 @@ public class VerificationServiceImpl extends CommonBaseServiceImpl<VerificationM
         if(kind.equals("receipts_offset_receivable") || kind.equals("prepaid_offset_payable")){
             subledger.setSl_preamount(-amount1);
         }else{
-            subledger.setSl_preamount(amount1);
+            subledger.setSl_preamount(new Double(0));
         }
+        kind = transferKind(kind);
+        subledger.setSl_kind(kind);
         return subledger;
     }
     /**

+ 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;
+        }
+    }
+}

+ 17 - 2
framework/core/src/main/java/com/usoftchina/saas/utils/RegexpUtils.java

@@ -12,7 +12,7 @@ public class RegexpUtils {
     private static final String HK_MOBILE_EXP = "^(5|6|8|9)\\d{7}$";
     private static final String EMAIL_EXP = "^\\w+([-.]\\w+)*@\\w+([-]\\w+)*\\.(\\w+([-]\\w+)*\\.)*[a-z]{2,4}$";
     private static final String SPECCHARACTER_EXP = "[\\\\s~·`!!@#¥$%^……&*(())\\\\-——\\\\-_=+【\\\\[\\\\]】{{}}\\\\|、\\\\\\\\;;::‘'“”\\\",,《<。.》>、/??]";
-
+    private static final String SPECIALCHARACTER_EXP = "[`~!@#$%^&*()+=|{}':;',\\[\\]<>/?~!@#¥%……&*()——+|{}【】‘;:”“’。,、?]";
     /**
      * 匹配手机号码
      *
@@ -57,7 +57,7 @@ public class RegexpUtils {
     }
 
     /**
-     * 特殊字符替换
+     * 特殊字符替换含小写字母
      * @param str
      * @return
      */
@@ -66,4 +66,19 @@ public class RegexpUtils {
         Matcher m = p.matcher(str);
         return m.replaceAll("");
     }
+
+
+    /**
+     * 特殊字符替换
+     * @param obj
+     * @return
+     */
+    public static String replaceSpecialCharacter(Object obj){
+        if (StringUtils.isEmpty(obj)) {
+            return "";
+        }
+        Pattern p = Pattern.compile(SPECIALCHARACTER_EXP);
+        Matcher m = p.matcher(obj.toString());
+        return m.replaceAll("");
+    }
 }

+ 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 {
+}

+ 2 - 1
frontend/saas-portal-web/src/components/conenter/addenterprise.vue

@@ -48,7 +48,7 @@
                             <span class="qy-biaoti left"><span class="xingxing">*</span>公司地址</span>
                             <div class="addbiaoqian">
                                 <!-- <v-distpicker @province= 'qyprovince' @city= 'qycity' @area= 'qyarea'></v-distpicker> -->
-                                <v-distpicker @selected= 'selected'></v-distpicker>
+                                <v-distpicker @selected= 'selected' province="广东省" city="广州市" area="荔湾区" placeholders= '{}'></v-distpicker>
                                 <input ref="address" @change="address" :disabled="disabled" class="qy-xiangxi" type="text" placeholder="输入企业详细地址">
                                 
                             </div>
@@ -104,6 +104,7 @@ import { setTimeout } from 'timers';
                 isemail: true,//验证邮箱
                 disabled: true,
                 isadd: false,//添加成功弹窗
+                placeholder:{province:"省", city:"市", area:"区"}
             }
         },
         components:{

+ 1 - 1
frontend/saas-portal-web/src/components/conenter/company.vue

@@ -73,7 +73,7 @@
                     </div>
                     <div>
                         <span v-if = "d.default_" class="gs-btn2 gs-btn3">默认企业</span>
-                        <span v-if="d.saas_" @click="showDefaultWin(i)" class="gs-btn2 xs">设为默认</span>
+                        <span v-else @click="showDefaultWin(i)" class="gs-btn2 xs">设为默认</span>
                     </div>
                 </div>
             </div>

+ 2 - 4
frontend/saas-portal-web/src/components/conenter/details.vue

@@ -87,7 +87,7 @@
                         <li>
                             <span class="qy-biaoti">邮箱</span>
                             <input @change="email" ref="email" type="text" value="">
-                            <dir class="qy-Tips"><span style="color:red">{{Email}}</span></dir>
+                            <div class="qy-Tips"><span style="color:red">{{Email}}</span></div>
                         </li>
                     </ul>
                 </div>
@@ -233,9 +233,7 @@
     height: 20px;
 }
 .qy-Tips {
-    margin: 0;
-    text-align: left;
     height: 26px;
-    margin-left: 16%;
+    margin-left: 21%;
 }
 </style>

+ 5 - 3
frontend/saas-portal-web/src/components/conenter/enterprise.vue

@@ -55,7 +55,9 @@
                         <div class="left gr-right">
                             <ul>
                                 <li>
-                                    <span>姓名</span><span>{{mytoken.realname}}</span>
+                                    <span>姓名</span>
+                                    <span>{{mytoken.realname}}</span>
+                                    <!-- <input type="text" :value="mytoken.realname"> -->
                                 </li>
                                 <li>
                                     <span>UU号</span><span>{{mytoken.uu}}</span>
@@ -135,11 +137,11 @@ import { setTimeout } from 'timers';
             },
             //修改密码
             changpasd(){
-                window.location.href = 'https://sso.ubtob.com/reset/forgetPasswordValidationAccount?returnURL='+this.$url.web+'&appId=sp'
+                window.location.href = this.$url.sso+'/reset/forgetPasswordValidationAccount?returnURL='+this.$url.web+'&appId=sp'
             },
             //修改手机号
             changphone(){
-                window.location.href = 'https://sso.ubtob.com/validation/phoneValidation?returnURL='+this.$url.web+'&appId=sp'
+                window.location.href = this.$url.sso+'/validation/phoneValidation?returnURL='+this.$url.web+'&appId=sp'
             }
         },
     }

+ 8 - 0
frontend/saas-portal-web/src/pages/index/index.js

@@ -6,6 +6,14 @@ import Axios from 'axios'
 import store from '../../store'
 
 Axios.defaults.withCredentials=true;
+Axios.interceptors.request.use(function (config) {
+  store.state.isloading = true
+  return config;
+})
+Axios.interceptors.response.use(function (config) {
+  store.state.isloading = false
+  return config;
+})
 Vue.prototype.$ajax = Axios;
 Vue.config.productionTip = false
 

+ 28 - 2
frontend/saas-portal-web/src/pages/index/index.vue

@@ -1,20 +1,46 @@
 <template>
   <div id="app">
+    <div v-if="isloading" class="loading">
+      <div class="loadingtxt">加载中...</div>
+    </div>
     <router-view/>
     <footers></footers>
   </div>
 </template>
 <script>
-// import footers from './components/footer/footer.vue';
 import footers from '../../components/footer/footer.vue';
 export default {
   name: 'App',
+  data(){
+    return {
+
+    }
+  },
   components: {
       footers
   },
+  computed:{
+    isloading:function(){
+      return this.$store.state.isloading
+    }
+  }
 }
 </script>
 
 <style>
-
+.loading {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background: rgba(0, 0, 0, 0.7);
+  text-align: center;
+}
+.loadingtxt {
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%, -50%);
+}
 </style>

+ 1 - 1
frontend/saas-portal-web/src/store/index.js

@@ -4,10 +4,10 @@ Vue.use(Vuex)
 
 export default new Vuex.Store({
     state:{
-        data: [],
         email:'',
         isAutoLogin: false,
         isproblem: '',//常见问题
+        isloading:false,
     },
     mutations:{
          Logintrue(state) {

+ 48 - 15
frontend/saas-web/app/view/core/base/ImportWindow.js

@@ -75,6 +75,7 @@ Ext.define('saas.view.core.base.ImportWindow', {
         hidden:true,
         name:'over',
         text:'完成',
+        disabled:true,
         handler:function(b){
             var form = b.ownerCt.ownerCt;
             var id = form.importId;
@@ -96,7 +97,10 @@ Ext.define('saas.view.core.base.ImportWindow', {
                         form.setLoading(false);
                         var res = Ext.decode(response.responseText);
                         if(!res.success){
-                            saas.util.BaseUtil.showErrorToast('导入数据失败:' + res.message);
+                            var upload = form.down('[name=upload]');
+                            upload.reset();
+                            form.dockedItems.items[1].down('[name=over]').setDisabled(true);
+                            form.down('[name=messagedetail]').setHtml(res.message + '</br></br><span style="color:#ff0000">请修改后重新上传</span>');
                         }else{
                             //刷新界面
                             var g = form.ownerCt.down('grid');
@@ -106,8 +110,12 @@ Ext.define('saas.view.core.base.ImportWindow', {
                     },
                     failure: function (response, opts) {
                         form.setLoading(false);
+                        var upload = form.down('[name=upload]');
+                        upload.reset();
+                        form.dockedItems.items[1].down('[name=over]').setDisabled(true);
                         var res = Ext.decode(response.responseText);
                         saas.util.BaseUtil.showErrorToast('导入数据失败: ' + res.message);
+                        
                     }
                 });
             }else{
@@ -160,7 +168,7 @@ Ext.define('saas.view.core.base.ImportWindow', {
                     width: 240,
                     labelWidth:160,
                     buttonOnly:true,
-                    fieldLabel:'客户导入模板下载',
+                    fieldLabel:'导入模板下载',
                     name:'download',
                     xtype: 'filefield',
                     buttonText: '下载模板',
@@ -181,7 +189,8 @@ Ext.define('saas.view.core.base.ImportWindow', {
                     collapsible: false,
                     items: [{
                         xtype : 'fieldcontainer',
-                        html: me.showDetail?me.showDetail:'请配置showDetail属性',
+                        html: '1.下载模板后打开</br>2.查看模板内说明、并修改模板内容,保存后点击下一步按钮</br>3.上传Excel文件,此时会校验文件的字段格式,有问题则需要修改错误数据后再次上传文件</br>4.校验成功后'+
+                        '点击完成按钮将数据进行数据库校验'
                     }]
                 },{
                     hidden:true,
@@ -205,14 +214,38 @@ Ext.define('saas.view.core.base.ImportWindow', {
                         }).on('change', me.onFileChange, me);
                     },
                     listeners: {
-                        afterrender:function(field,ops){
-                            // var fileEl = field.fileInputEl.dom;
-                            // fileEl.setAttribute("accept","image/*");
-                        },
                         change: function(field){
                             var form = field.ownerCt.ownerCt;
                             var myForm = field.ownerCt;
                             var fileEl = field.fileInputEl.dom;
+                            var file = fileEl.files[0];
+                            var type = file.name.substring(file.name.lastIndexOf('.'),file.name.length);
+                            if(type.indexOf('xlsx')<0||type.indexOf('xls')<0){
+                                saas.util.BaseUtil.showErrorToast('文件格式不正确,只能选择xlsx或xls格式的文件进行上传');
+                                field.reset();
+                                return;
+                            }
+
+                            //导入权限校验
+                            var hasPower = false;
+                            Ext.Ajax.request({
+                                url: 'http://192.168.253.41:8560/api/commons/'+form.ownerCt.caller+'/import',
+                                async:false,
+                                method: 'GET',
+                                success: function(response, opts) {
+                                    var data = Ext.decode(response.responseText);
+                                    if(data.success){
+                                        hasPower = true
+                                    }
+                                },
+                                failure: function(response, opts) {}
+                            }); 
+                            if(!hasPower){
+                                field.reset();
+                                saas.util.BaseUtil.showErrorToast('上传失败:没有 导入 权限');
+                                return false;
+                            }
+                            
                             var fd = new FormData();
                             fd.append('file', fileEl.files[0]);
                             fd.append('caller', myForm.ownerCt.ownerCt.caller);
@@ -235,22 +268,21 @@ Ext.define('saas.view.core.base.ImportWindow', {
                                     if(res.success){
                                         var id = res.data;
                                         if(id){
-                                            saas.util.BaseUtil.showSuccessToast('上传成功');
+                                            field.ownerCt.down('[name=messagedetail]').setHtml('文件名: ' + file.name + ' 导入格式校验成功!</br></br>请点击 <span style="color:#35baf6">完成</span> 执行数据库校验');
                                             form.importId = id;
+                                            field.ownerCt.ownerCt.dockedItems.items[1].down('[name=over]').setDisabled(false)
                                         }else{
-                                            field.setValue('');
+                                            field.reset();
                                             saas.util.BaseUtil.showErrorToast('上传失败:后台未返回信息');
                                         }
                                     }else{
-                                        field.setValue('');
-                                        field.setRawValue('');
-                                        field.value = '';
-                                        saas.util.BaseUtil.showErrorToast('上传失败: ' + res.message);
+                                        field.reset();
+                                        field.ownerCt.down('[name=messagedetail]').setHtml(res.message + '</br><span style="color:#ff0000">请修改后重新上传</span>');
                                     }
                                 },
                                 failure: function (response, opts) {
                                     form.setLoading(false);
-                                    field.setValue('');
+                                    field.reset();
                                     var res = Ext.decode(response.responseText);
                                     saas.util.BaseUtil.showErrorToast('上传失败: ' + res.message);
                                 }
@@ -265,8 +297,9 @@ Ext.define('saas.view.core.base.ImportWindow', {
                     name:'message',
                     collapsible: false,
                     items: [{
+                        name:'messagedetail',
                         xtype : 'fieldcontainer',
-                        html: '暂无',
+                        html:'暂无导入信息'
                     }]
                 }]
             }]

+ 1 - 1
frontend/saas-web/app/view/core/chart/ChartBase.js

@@ -6,7 +6,7 @@ Ext.define('saas.view.core.chart.ChartBase', {
     ],
 
     height: 300,
-    bodyPadding: 16,
+    bodyPadding: '16 16 16 0',
 
     layout: 'fit',
 

+ 1 - 1
frontend/saas-web/app/view/home/Home.js

@@ -52,7 +52,7 @@ Ext.define('saas.view.home.Home', {
         }, {
             xtype: 'key-data',
             margin: 0,
-            padding: '0 0 0 14',
+            padding: '0 0 8 14',
         }]
     }, {
         xtype: 'panel',

+ 13 - 0
frontend/saas-web/app/view/home/charts/KeyData.scss

@@ -13,13 +13,26 @@
             border-style: solid;
             border-color: #1EC09F;
             cursor: pointer;
+            transition: all 0.4s ease;
 
             &:nth-of-type(odd) {
                 margin: 8px 8px 8px 0;
+
+                &:hover {
+                    position: relative;
+                    margin: 2px 8px 14px 0;
+                    box-shadow: 0 2px 2px 2px rgba(0, 0, 0, 0.12);
+                }
             }
 
             &:nth-of-type(even) {
                 margin: 8px 0 8px 8px;
+
+                &:hover {
+                    position: relative;
+                    margin: 2px 0 14px 8px;
+                    box-shadow: 0 1px 1px 1px rgba(0, 0, 0, 0.12);
+                }
             }
 
             .x-box-content {

+ 17 - 25
frontend/saas-web/app/view/home/charts/MonthIO.js

@@ -3,9 +3,10 @@ Ext.define('saas.view.home.charts.MonthIO', {
     xtype: 'month-io',
     id: 'month_io',
 
-    bind: {
-        title: '本月收入支出额(万元)<div style="text-align: right;"><span style="font-weight:bold;">收入:{month_in}</span><span style="font-weight:bold;margin-left: 10px;">支出:{month_out}</span></div>'
-    },
+    // bind: {
+    //     title: '本月收入支出额(万元)<div style="text-align: right;"><span style="font-weight:bold;">收入:{month_in}</span><span style="font-weight:bold;margin-left: 10px;">支出:{month_out}</span></div>'
+    // },
+    title: '本月收入支出额(万元)',
 
     initComponent: function () {
         var me = this;
@@ -18,27 +19,18 @@ Ext.define('saas.view.home.charts.MonthIO', {
                     '#82CCFF'
                 ],
                 bind: {
-                    // captions: {
-                    //     title: {
-                    //         text: '收入:{month_in}\t支出:{month_out}',
-                    //         style: {
-                    //             'color': '#485465',
-                    //             'letter-spacing': '-0.07px'
-                    //         },
-                    //         align: 'left'
-                    //     },
-                    // },
+                    legend: {
+                        type: 'dom',
+                        docked: 'top',
+                        padding: 0,
+                        bodyPadding: 0,
+                        border: 0,
+                        cls: 'x-monthio-legend',
+                        html: '<div class="sumtip"><span>收入:{month_in}</span><span>支出:{month_out}</span></div>'
+                    },
                     insetPadding: '{insetPadding}',
                     store: '{month_io}'
                 },
-                legend: {
-                    type: 'dom',
-                    docked: 'top',
-                    padding: 0,
-                    bodyPadding: 0,
-                    border: 0,
-                    cls: 'x-monthio-legend'
-                },
                 axes: [{
                     type: 'numeric',
                     position: 'left',
@@ -51,10 +43,10 @@ Ext.define('saas.view.home.charts.MonthIO', {
                     },
                     grid: {
                         even: {
-                            stroke: '#E2E7ED'
+                            stroke: '#E5EAEF'
                         },
                         odd: {
-                            stroke: '#E2E7ED',
+                            stroke: '#E5EAEF',
                         }
                     },
                     style: {
@@ -70,7 +62,7 @@ Ext.define('saas.view.home.charts.MonthIO', {
                         fillStyle: '#485465'
                     },
                     style: {
-                        fill: '#E2E7ED',
+                        fill: '#E5EAEF',
                         strokeStyle: 'transparent'
                     },
                 }],
@@ -102,6 +94,6 @@ Ext.define('saas.view.home.charts.MonthIO', {
         var fieldIndex = Ext.Array.indexOf(item.series.getYField(), item.field),
         type = item.series.getTitle()[fieldIndex];
 
-        tooltip.setHtml(type + record.get('x') + record.get(item.field));
+        tooltip.setHtml(type + record.get('x') + ': ' + Ext.util.Format.number(record.get(item.field), '0,000.00') + '万元');
     },
 });

+ 11 - 0
frontend/saas-web/app/view/home/charts/MonthIO.scss

@@ -5,10 +5,21 @@
 
 .x-monthio-legend {
 
+    .sumtip {
+        position: absolute;
+        right: 0;
+        top: 8px;
+        font-size: 12px;
+
+        span:last-child {
+            margin-left: 12px;
+        }
+    }
     .x-legend-inner {
         padding: 0;
 
         .x-legend-container {
+            text-align: left;
 
             .x-legend-item {
                 font-size: 12px;

+ 4 - 4
frontend/saas-web/app/view/home/charts/MonthPurchase.js

@@ -30,7 +30,7 @@ Ext.define('saas.view.home.charts.MonthPurchase', {
                         fillStyle: '#485465',
                     },
                     style: {
-                        fill: '#E2E7ED',
+                        fill: '#E5EAEF',
                         strokeStyle: 'transparent'
                     },
                     renderer: me.onCategoryLabelRender
@@ -41,10 +41,10 @@ Ext.define('saas.view.home.charts.MonthPurchase', {
                     adjustByMajorUnit: true,
                     grid: {
                         even: {
-                            stroke: '#E2E7ED'
+                            stroke: '#E5EAEF'
                         },
                         odd: {
-                            stroke: '#E2E7ED',
+                            stroke: '#E5EAEF',
                         }
                     },
                     label: {
@@ -85,7 +85,7 @@ Ext.define('saas.view.home.charts.MonthPurchase', {
     },
 
     onBarTipRender: function (tooltip, record, item) {
-        tooltip.setHtml(record.get('x') + ': ' + record.get('y') + '万元');
+        tooltip.setHtml(record.get('x') + ': ' + Ext.util.Format.number(record.get('y'), '0,000.00') + '万元');
     },
 
 });

+ 5 - 21
frontend/saas-web/app/view/home/charts/MonthSale.js

@@ -20,28 +20,12 @@ Ext.define('saas.view.home.charts.MonthSale', {
                     store: '{month_sale}',
                 },
                 colors: [
-                    '#2C82BE',
+                    '#1EC09F',
                     '#27A7FF',
-                    '#82CCFF',
-                    '#53A8E2',
+                    '#4E84F5',
+                    '#FDC200',
                     '#76DDFB',
-                    '#DBECF8',
-                    '#426AB3',
-                    '#228FBD',
-                    '#2A5CAA',
-                    '#7BBFEA',
-                    '#2A8FBD',
-                    '#4C556E',
-                    '#9999FF',
-                    '#CCCCFF',
-                    '#06B9D1',
-                    '#005A78',
-                    '#00466B',
-                    '#005687',
-                    '#05173B',
-                    '#1A4FA3',
-                    '#1E90FF',
-                    '#B0E0E6'
+                    '#FE7D6B',
                 ],
                 interactions: ['rotate', 'itemhighlight'],
                 innerPadding: 2,
@@ -102,6 +86,6 @@ Ext.define('saas.view.home.charts.MonthSale', {
     },
 
     onSeriesTooltipRender: function (tooltip, record, item) {
-        tooltip.setHtml(record.get('x') + ': ' + record.get('y'));
+        tooltip.setHtml(record.get('x') + ': ' + Ext.util.Format.number(record.get('y'), '0,000.00'));
     }
 });

+ 4 - 4
frontend/saas-web/app/view/home/charts/ProfitDetail.js

@@ -30,7 +30,7 @@ Ext.define('saas.view.home.charts.ProfitDetail', {
                         fillStyle: '#485465',
                     },
                     style: {
-                        fill: '#E2E7ED',
+                        fill: '#E5EAEF',
                         strokeStyle: 'transparent'
                     },
                     renderer: me.onCategoryLabelRender
@@ -41,10 +41,10 @@ Ext.define('saas.view.home.charts.ProfitDetail', {
                     adjustByMajorUnit: true,
                     grid: {
                         even: {
-                            stroke: '#E2E7ED'
+                            stroke: '#E5EAEF'
                         },
                         odd: {
-                            stroke: '#E2E7ED',
+                            stroke: '#E5EAEF',
                         }
                     },
                     label: {
@@ -85,7 +85,7 @@ Ext.define('saas.view.home.charts.ProfitDetail', {
     },
 
     onBarTipRender: function (tooltip, record, item) {
-        tooltip.setHtml(record.get('x') + ': ' + record.get('y') + '万元');
+        tooltip.setHtml(record.get('x') + ': ' + Ext.util.Format.number(record.get('y'), '0,000.00') + '万元');
     },
 
 });

+ 4 - 4
frontend/saas-web/app/view/home/charts/PurchaseTrend.js

@@ -34,7 +34,7 @@ Ext.define('saas.view.home.charts.PurchaseTrend', {
                         fillStyle: '#485465'
                     },
                     style: {
-                        fill: '#E2E7ED',
+                        fill: '#E5EAEF',
                         strokeStyle: 'transparent'
                     },
                     renderer: me.categoryRender
@@ -44,10 +44,10 @@ Ext.define('saas.view.home.charts.PurchaseTrend', {
                     position: 'left',
                     grid: {
                         even: {
-                            stroke: '#E2E7ED'
+                            stroke: '#E5EAEF'
                         },
                         odd: {
-                            stroke: '#E2E7ED',
+                            stroke: '#E5EAEF',
                         }
                     },
                     label: {
@@ -88,7 +88,7 @@ Ext.define('saas.view.home.charts.PurchaseTrend', {
     },
 
     onBarTipRender: function (tooltip, record, item) {
-        tooltip.setHtml(record.get('x') + '月: ' + record.get('y') + '万元');
+        tooltip.setHtml(record.get('x') + '月: ' + Ext.util.Format.number(record.get('y'), '0,000.00') + '万元');
     },
 
     categoryRender: function(axis, label, layoutContext, lastLabel) {

+ 5 - 5
frontend/saas-web/app/view/home/charts/SaleTrend.js

@@ -40,7 +40,7 @@ Ext.define('saas.view.home.charts.SaleTrend', {
                         fillStyle: '#485465',
                     },
                     style: {
-                        fill: '#E2E7ED',
+                        fill: '#E5EAEF',
                         strokeStyle: 'transparent'
                     },
                     renderer: me.categoryRender
@@ -50,10 +50,10 @@ Ext.define('saas.view.home.charts.SaleTrend', {
                     position: 'left',
                     grid: {
                         even: {
-                            stroke: '#E2E7ED',
+                            stroke: '#E5EAEF',
                         },
                         odd: {
-                            stroke: '#E2E7ED',
+                            stroke: '#E5EAEF',
                         }
                     },
                     label: {
@@ -137,7 +137,7 @@ Ext.define('saas.view.home.charts.SaleTrend', {
                     padding: 0,
                     bodyPadding: 0,
                     border: 0,
-                    cls: 'x-monthio-legend'
+                    cls: 'x-saletrend-legend'
                 },
                 listeners: {
                     itemhighlightchange: me.itemhighlightchange
@@ -173,7 +173,7 @@ Ext.define('saas.view.home.charts.SaleTrend', {
     },
 
     onSeriesTooltipRender: function (tooltip, record, item) {
-        tooltip.setHtml(record.get('x') + '月: ' + record.get(item.series.getYField()) + '万元');
+        tooltip.setHtml(record.get('x') + '月: ' + Ext.util.Format.number(record.get(item.series.getYField()), '0,000.00') + '万元');
     },
 
     categoryRender: function(axis, label, layoutContext, lastLabel) {

+ 29 - 0
frontend/saas-web/app/view/home/charts/SaleTrend.scss

@@ -0,0 +1,29 @@
+.quick-graph-panel .x-panel-header-default .x-panel-header-title-default > .x-title-text-default {
+    display: grid;
+    grid-template-columns: 1fr 1fr;
+}
+
+.x-saletrend-legend {
+
+    .x-legend-inner {
+        padding: 0;
+
+        .x-legend-container {
+            text-align: left;
+
+            .x-legend-item {
+                font-size: 12px;
+                border: none !important;
+                outline: none !important;
+                text-align: left;
+
+                .x-legend-item-marker {
+                    box-shadow: none;
+                    height: 2px;
+                    margin-top: 5px;
+                    width: 10px;
+                }
+            }
+        }
+    }
+}

+ 4 - 4
frontend/saas-web/app/view/home/charts/StockAmount.js

@@ -34,7 +34,7 @@ Ext.define('saas.view.home.charts.StockAmount', {
                         fillStyle: '#485465'
                     },
                     style: {
-                        fill: '#E2E7ED',
+                        fill: '#E5EAEF',
                         strokeStyle: 'transparent'
                     },
                     renderer: me.categoryRender
@@ -44,10 +44,10 @@ Ext.define('saas.view.home.charts.StockAmount', {
                     position: 'left',
                     grid: {
                         even: {
-                            stroke: '#E2E7ED',
+                            stroke: '#E5EAEF',
                         },
                         odd: {
-                            stroke: '#E2E7ED',
+                            stroke: '#E5EAEF',
                         }
                     },
                     label: {
@@ -82,7 +82,7 @@ Ext.define('saas.view.home.charts.StockAmount', {
     },
 
     onBarTipRender: function (tooltip, record, item) {
-        tooltip.setHtml(record.get('x') + '月: ' + record.get('y') + '万元');
+        tooltip.setHtml(record.get('x') + '月: ' + Ext.util.Format.number(record.get('y'), '0,000.00') + '万元');
     },
 
     categoryRender: function(axis, label, layoutContext, lastLabel) {

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

@@ -319,6 +319,21 @@
                 <artifactId>sms-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