zhuth 7 years ago
parent
commit
66490fd36e
95 changed files with 2076 additions and 743 deletions
  1. 8 0
      README.md
  2. 2 0
      applications/commons/commons-server/src/main/java/com/usoftchina/saas/commons/CommonsApplication.java
  3. 2 1
      applications/document/document-server/src/main/java/com/usoftchina/saas/document/DocumentApplication.java
  4. 3 3
      applications/document/document-server/src/test/java/com/usoftchina/saas/document/mapper/CustomercontactMapperTest.java
  5. 1 1
      applications/document/document-server/src/test/java/com/usoftchina/saas/document/service/CustomerServiceTest.java
  6. 2 1
      applications/money/money-server/src/main/java/com/usoftchina/saas/money/MoneyApplicatiion.java
  7. 2 1
      applications/purchase/purchase-server/src/main/java/com/usoftchina/saas/purchase/PurchaseApplication.java
  8. 2 0
      applications/sale/sale-server/src/main/java/com/usoftchina/saas/sale/SaleApplication.java
  9. 2 1
      applications/storage/storage-server/src/main/java/com/usoftchina/saas/storage/StorageApplication.java
  10. 2 2
      base-servers/account/README.md
  11. 4 0
      base-servers/account/account-api/pom.xml
  12. 10 0
      base-servers/account/account-api/src/main/java/com/usoftchina/saas/account/api/AccountApi.java
  13. 26 0
      base-servers/account/account-api/src/main/java/com/usoftchina/saas/account/api/ResourceApi.java
  14. 65 0
      base-servers/account/account-api/src/main/java/com/usoftchina/saas/account/cache/AccountCache.java
  15. 68 0
      base-servers/account/account-api/src/main/java/com/usoftchina/saas/account/cache/ResourceCache.java
  16. 54 6
      base-servers/account/account-dto/src/main/java/com/usoftchina/saas/account/dto/AccountDTO.java
  17. 27 1
      base-servers/account/account-dto/src/main/java/com/usoftchina/saas/account/dto/RoleBaseDTO.java
  18. 11 1
      base-servers/account/account-dto/src/main/java/com/usoftchina/saas/account/dto/UrlResourceDTO.java
  19. 1 1
      base-servers/account/account-server/pom.xml
  20. 74 7
      base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/controller/AccountController.java
  21. 1 1
      base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/controller/ResourceController.java
  22. 22 0
      base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/controller/RoleController.java
  23. 10 0
      base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/mapper/AccountMapper.java
  24. 9 1
      base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/mapper/AccountRoleMapper.java
  25. 16 0
      base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/mapper/ResourceMapper.java
  26. 11 0
      base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/mapper/RoleMapper.java
  27. 22 1
      base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/mapper/RoleResourceMapper.java
  28. 12 0
      base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/po/Account.java
  29. 14 0
      base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/po/AccountRole.java
  30. 14 0
      base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/po/Resource.java
  31. 14 0
      base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/po/ResourceGroup.java
  32. 26 0
      base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/po/RoleResource.java
  33. 33 0
      base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/service/AccountService.java
  34. 16 0
      base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/service/ResourceService.java
  35. 12 1
      base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/service/RoleService.java
  36. 37 1
      base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/service/impl/AccountServiceImpl.java
  37. 14 0
      base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/service/impl/ResourceServiceImpl.java
  38. 24 1
      base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/service/impl/RoleServiceImpl.java
  39. 3 0
      base-servers/account/account-server/src/main/resources/application.yml
  40. 7 1
      base-servers/account/account-server/src/main/resources/mapper/AccountMapper.xml
  41. 4 1
      base-servers/account/account-server/src/main/resources/mapper/AccountRoleMapper.xml
  42. 9 1
      base-servers/account/account-server/src/main/resources/mapper/ResourceMapper.xml
  43. 6 1
      base-servers/account/account-server/src/main/resources/mapper/RoleMapper.xml
  44. 15 1
      base-servers/account/account-server/src/main/resources/mapper/RoleResourceMapper.xml
  45. 65 6
      base-servers/account/account-server/src/test/java/com/usoftchina/saas/account/controller/AccountControllerTest.java
  46. 0 4
      base-servers/auth/auth-api/pom.xml
  47. 8 0
      base-servers/auth/auth-api/src/main/java/com/usoftchina/saas/auth/api/AuthApi.java
  48. 0 47
      base-servers/auth/auth-api/src/main/java/com/usoftchina/saas/auth/cache/TokenCache.java
  49. 19 0
      base-servers/auth/auth-server/src/main/java/com/usoftchina/saas/auth/controller/AuthController.java
  50. 4 0
      base-servers/gateway-server/pom.xml
  51. 4 1
      base-servers/gateway-server/src/main/java/com/usoftchina/saas/gateway/GatewayApplication.java
  52. 53 1
      base-servers/gateway-server/src/main/java/com/usoftchina/saas/gateway/config/AuthFilter.java
  53. 18 0
      base-servers/gateway-server/src/main/java/com/usoftchina/saas/gateway/context/SpringContextListener.java
  54. 20 0
      base-servers/gateway-server/src/main/java/com/usoftchina/saas/gateway/error/PermissionException.java
  55. 177 0
      base-servers/gateway-server/src/main/java/com/usoftchina/saas/gateway/util/AntPathRequestMatcher.java
  56. 3 0
      base-servers/gateway-server/src/main/resources/application.yml
  57. 2 0
      base-servers/ui-server/src/main/java/com/usoftchina/saas/ui/UiApplication.java
  58. 0 4
      base-servers/ui-server/src/main/java/com/usoftchina/saas/ui/controller/co/CoViewController.java
  59. 4 2
      framework/core/pom.xml
  60. 8 11
      framework/core/src/main/java/com/usoftchina/saas/base/Result.java
  61. 1 0
      framework/core/src/main/java/com/usoftchina/saas/exception/ExceptionCode.java
  62. 112 5
      framework/core/src/main/java/com/usoftchina/saas/utils/CollectionUtils.java
  63. 44 1
      framework/core/src/main/java/com/usoftchina/saas/utils/JsonUtils.java
  64. 0 1
      frontend/saas-web/app/Application.scss
  65. 0 26
      frontend/saas-web/app/model/Account.js
  66. 73 21
      frontend/saas-web/app/model/Session.js
  67. 21 0
      frontend/saas-web/app/model/stock/Makematerial.js
  68. 9 0
      frontend/saas-web/app/store/Company.js
  69. 37 0
      frontend/saas-web/app/view/auth/CompanyPicker.js
  70. 38 0
      frontend/saas-web/app/view/auth/CompanyPicker.scss
  71. 54 26
      frontend/saas-web/app/view/auth/LoginController.js
  72. 7 10
      frontend/saas-web/app/view/document/bom/FormPanel.js
  73. 85 41
      frontend/saas-web/app/view/document/customer/FormPanel.js
  74. 4 2
      frontend/saas-web/app/view/document/kind/Kind.js
  75. 30 7
      frontend/saas-web/app/view/document/product/FormPanel.js
  76. 2 1
      frontend/saas-web/app/view/document/vendor/FormModel.js
  77. 80 23
      frontend/saas-web/app/view/document/vendor/FormPanel.js
  78. 0 15
      frontend/saas-web/app/view/main/List.js
  79. 27 11
      frontend/saas-web/app/view/main/Main.js
  80. 29 0
      frontend/saas-web/app/view/main/MainController.js
  81. 0 1
      frontend/saas-web/app/view/main/MainModel.js
  82. 28 19
      frontend/saas-web/app/view/money/payBalance/FormPanel.js
  83. 3 3
      frontend/saas-web/app/view/money/payBalance/QueryPanel.js
  84. 2 0
      frontend/saas-web/app/view/purchase/purchase/FormPanelController.js
  85. 62 68
      frontend/saas-web/app/view/stock/make/FormPanel.js
  86. 65 66
      frontend/saas-web/app/view/stock/make/FormPanelController.js
  87. 5 5
      frontend/saas-web/app/view/stock/make/QueryPanel.js
  88. 1 150
      frontend/saas-web/app/view/sys/config/FormPanelController.js
  89. 12 5
      frontend/saas-web/app/view/sys/messagelog/DataList.js
  90. 39 53
      frontend/saas-web/app/view/viewport/ViewportController.js
  91. 14 1
      frontend/saas-web/app/view/viewport/ViewportModel.js
  92. 10 1
      frontend/saas-web/overrides/data/Connection.js
  93. 1 0
      frontend/saas-web/packages/local/theme-default/sass/var/all.scss
  94. 6 0
      pom.xml
  95. 73 68
      script/mysql/init/account.sql

+ 8 - 0
README.md

@@ -13,12 +13,20 @@
 │  |  |  |─document-dto-----------------------基础资料数据传输对象
 │  |  |  |─document-server--------------------基础资料服务
 │  |  ├─money---------------------------------资金
+│  |  |  |─money-api--------------------------资金服务api
+│  |  |  |─money-dto--------------------------资金服务数据传输对象
+│  |  |  |─money-server-----------------------资金服务
 │  |  ├─purchase------------------------------采购
 │  |  |  |─purchase-api-----------------------采购服务api
 │  |  |  |─purchase-dto-----------------------采购服务数据传输对象
 │  |  |  |─purchase-server--------------------采购服务
 │  |  ├─sale----------------------------------销售
+│  |  |  |─sale-dto---------------------------销售服务数据传输对象
+│  |  |  |─sale-server------------------------销售服务
 │  |  ├─storage-------------------------------库存
+│  |  |  |─storage-api------------------------库存服务api
+│  |  |  |─storage-dto------------------------库存服务数据传输对象
+│  |  |  |─storage-server---------------------库存服务
 │  │ 
 │  ├─base-servers-----------------------------基础服务
 │  |  ├─admin-server--------------------------spring-boot-admin监控中心

+ 2 - 0
applications/commons/commons-server/src/main/java/com/usoftchina/saas/commons/CommonsApplication.java

@@ -1,5 +1,6 @@
 package com.usoftchina.saas.commons;
 
+import com.usoftchina.saas.auth.client.EnableAuthClient;
 import org.mybatis.spring.annotation.MapperScan;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
@@ -16,6 +17,7 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
 @EnableTransactionManagement
 @EnableFeignClients("com.usoftchina.saas")
 @MapperScan("com.usoftchina.saas.commons.mapper")
+@EnableAuthClient
 public class CommonsApplication {
     public static void main(String[] args) {
         SpringApplication.run(CommonsApplication.class, args);

+ 2 - 1
applications/document/document-server/src/main/java/com/usoftchina/saas/document/DocumentApplication.java

@@ -1,5 +1,6 @@
 package com.usoftchina.saas.document;
 
+import com.usoftchina.saas.auth.client.EnableAuthClient;
 import org.mybatis.spring.annotation.MapperScan;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
@@ -9,7 +10,7 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
 
 @SpringBootApplication
 @EnableEurekaClient
-//@EnableAuthClient
+@EnableAuthClient
 @EnableTransactionManagement
 @EnableFeignClients("com.usoftchina.saas")
 @MapperScan("com.usoftchina.saas.document.mapper")

+ 3 - 3
applications/document/document-server/src/test/java/com/usoftchina/saas/document/mapper/CustomercontactMapperTest.java

@@ -51,13 +51,13 @@ public class CustomercontactMapperTest {
         customercontact.setCc_cuid(new Long(1));
         customercontact.setCc_detno(1);
         customercontact.setCc_name("zdw");
-        customercontact.setCc_tel(1881);
+        customercontact.setCc_tel("1881");
 
         Customercontact customercontact1 = new Customercontact();
         customercontact1.setCc_cuid(new Long(1));
         customercontact1.setCc_detno(1);
         customercontact1.setCc_name("zdw");
-        customercontact1.setCc_tel(1881);
+        customercontact1.setCc_tel("1881");
 
         insertDetail1.add(customercontact);
         insertDetail1.add(customercontact1);
@@ -71,7 +71,7 @@ public class CustomercontactMapperTest {
         customercontact.setCc_cuid(new Long(1));
         customercontact.setCc_detno(1);
         customercontact.setCc_name("ssszdw");
-        customercontact.setCc_tel(1881);
+        customercontact.setCc_tel("1881");
         customercontact.setCc_qq("qq");
         customercontact.setCc_email("@qq");
         customercontact.setCompanyId(1);

+ 1 - 1
applications/document/document-server/src/test/java/com/usoftchina/saas/document/service/CustomerServiceTest.java

@@ -44,7 +44,7 @@ public class CustomerServiceTest {
         CustomercontactDTO item = new CustomercontactDTO();
         item.setCc_name("联系人1");
         item.setCc_detno(1);
-        item.setCc_tel(112L);
+        item.setCc_tel("");
         item.setCc_qq("396996717");
         item.setCc_email("@qq.com");
 

+ 2 - 1
applications/money/money-server/src/main/java/com/usoftchina/saas/money/MoneyApplicatiion.java

@@ -1,5 +1,6 @@
 package com.usoftchina.saas.money;
 
+import com.usoftchina.saas.auth.client.EnableAuthClient;
 import org.mybatis.spring.annotation.MapperScan;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
@@ -16,7 +17,7 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
 @EnableEurekaClient
 @EnableTransactionManagement
 @EnableFeignClients("com.usoftchina.saas")
-//@EnableAuthClient
+@EnableAuthClient
 @MapperScan("com.usoftchina.saas.money.mapper")
 public class MoneyApplicatiion {
     public static void main(String[] args) {

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

@@ -1,6 +1,7 @@
 package com.usoftchina.saas.purchase;
 
 
+import com.usoftchina.saas.auth.client.EnableAuthClient;
 import org.mybatis.spring.annotation.MapperScan;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
@@ -18,7 +19,7 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter
 @EnableEurekaClient
 @EnableTransactionManagement
 @EnableFeignClients("com.usoftchina.saas")
-//@EnableAuthClient
+@EnableAuthClient
 @MapperScan("com.usoftchina.saas.purchase.mapper")
 public class PurchaseApplication   extends WebMvcConfigurerAdapter{
     public static void main(String[] args) {

+ 2 - 0
applications/sale/sale-server/src/main/java/com/usoftchina/saas/sale/SaleApplication.java

@@ -1,5 +1,6 @@
 package com.usoftchina.saas.sale;
 
+import com.usoftchina.saas.auth.client.EnableAuthClient;
 import org.mybatis.spring.annotation.MapperScan;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
@@ -15,6 +16,7 @@ import org.springframework.cloud.openfeign.EnableFeignClients;
 @MapperScan("com.usoftchina.saas.sale.mapper")
 @EnableEurekaClient
 @EnableFeignClients("com.usoftchina.saas")
+@EnableAuthClient
 public class SaleApplication{
     public static void main(String[] args) {
         SpringApplication.run(SaleApplication.class, args);

+ 2 - 1
applications/storage/storage-server/src/main/java/com/usoftchina/saas/storage/StorageApplication.java

@@ -1,5 +1,6 @@
 package com.usoftchina.saas.storage;
 
+import com.usoftchina.saas.auth.client.EnableAuthClient;
 import org.mybatis.spring.annotation.MapperScan;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
@@ -9,7 +10,7 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
 
 @SpringBootApplication
 @EnableEurekaClient
-//@EnableAuthClient
+@EnableAuthClient
 @EnableTransactionManagement
 @EnableFeignClients("com.usoftchina.saas")
 @MapperScan("com.usoftchina.saas.storage.mapper")

+ 2 - 2
base-servers/account/README.md

@@ -21,8 +21,8 @@
 | :----:   | ---------  |
 | id | 主键,自动生成  |
 | group_id | 资源分组ID,参考ac_resource_group、ac_resource_module表 |
-| name | 名称,统一叫法:查询、新增、修改、审核、反审核、删除、打印、(其它,字数≤5) |
+| name | 名称,统一叫法:查询、新增、修改、审核、反审核、删除、打印、导入、导出、(其它,字数≤5) |
 | type | 类型,MENU(Navigation的资源)、BUTTON (页面按钮资源)、API... |
 | url | 请求url |
 | method | http请求头方法:GET, POST, PUT, DELETE |
-| classify | 归类:QUERY, ADD, UPDATE, AUDIT, UNAUDIT, DELETE, PRINT, OTHER  |
+| classify | 归类:QUERY, ADD, UPDATE, AUDIT, UNAUDIT, DELETE, PRINT, IMPORT, EXPORT, OTHER  |

+ 4 - 0
base-servers/account/account-api/pom.xml

@@ -25,6 +25,10 @@
             <groupId>com.usoftchina.saas</groupId>
             <artifactId>core</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis</artifactId>
+        </dependency>
     </dependencies>
 
 </project>

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

@@ -4,6 +4,7 @@ import com.usoftchina.saas.account.dto.AccountDTO;
 import com.usoftchina.saas.base.Result;
 import org.springframework.cloud.openfeign.FeignClient;
 import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestParam;
 
 /**
@@ -30,4 +31,13 @@ public interface AccountApi {
      */
     @GetMapping(value = "/account/read")
     Result<AccountDTO> getAccount(@RequestParam(value = "username") String username);
+
+    /**
+     * 按用户名查找账户
+     *
+     * @param id
+     * @return
+     */
+    @GetMapping(value = "/account/{id}")
+    Result<AccountDTO> getAccountById(@PathVariable("id") Long id);
 }

+ 26 - 0
base-servers/account/account-api/src/main/java/com/usoftchina/saas/account/api/ResourceApi.java

@@ -0,0 +1,26 @@
+package com.usoftchina.saas.account.api;
+
+import com.usoftchina.saas.account.dto.UrlResourceDTO;
+import com.usoftchina.saas.base.Result;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+
+import java.util.List;
+
+/**
+ * @author yingp
+ * @date 2018/11/1
+ */
+@FeignClient(name = "account-server")
+public interface ResourceApi {
+
+    /**
+     * 查找应用的全部url资源
+     *
+     * @param appId
+     * @return
+     */
+    @GetMapping(value = "/resource/url/list")
+    Result<List<UrlResourceDTO>> getUrlResourcesByAppId(@RequestParam(value = "appId") String appId);
+}

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

@@ -0,0 +1,65 @@
+package com.usoftchina.saas.account.cache;
+
+import com.usoftchina.saas.account.api.AccountApi;
+import com.usoftchina.saas.account.dto.AccountDTO;
+import com.usoftchina.saas.base.Result;
+import com.usoftchina.saas.cache.RedisHashCache;
+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 AccountCache extends RedisHashCache<String, String, String> {
+    private final long id;
+
+    private static final RedisTemplate<String, String> REDIS_TEMPLATE = SpringContextHolder.getBean("redisTemplate", RedisTemplate.class);
+    private AccountApi accountApi;
+
+    private AccountCache(long id) {
+        super(() -> REDIS_TEMPLATE);
+        this.id = id;
+    }
+
+    public static AccountCache of(long id) {
+        return new AccountCache(id);
+    }
+
+    @Override
+    protected String field() {
+        return String.valueOf(id);
+    }
+
+    @Override
+    protected String key() {
+        return generateKey("account", "account");
+    }
+
+    @Override
+    protected Supplier<String> getSupplier() {
+        return () -> {
+            if (null == accountApi) {
+                accountApi = SpringContextHolder.getBean(AccountApi.class);
+            }
+            Result<AccountDTO> result = accountApi.getAccountById(id);
+            if (result.isSuccess()) {
+                return JsonUtils.toJsonString(result.getData());
+            }
+            throw new BizException(result.getCode(), result.getMessage());
+        };
+    }
+
+    public AccountDTO getAccount() {
+        Optional<String> value = get();
+        if (value.isPresent()) {
+            return JsonUtils.fromJsonString(value.get(), AccountDTO.class);
+        }
+        return null;
+    }
+}

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

@@ -0,0 +1,68 @@
+package com.usoftchina.saas.account.cache;
+
+import com.usoftchina.saas.account.api.ResourceApi;
+import com.usoftchina.saas.account.dto.UrlResourceDTO;
+import com.usoftchina.saas.base.Result;
+import com.usoftchina.saas.cache.RedisHashCache;
+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.List;
+import java.util.Optional;
+import java.util.function.Supplier;
+
+/**
+ * @author yingp
+ * @date 2018/11/1
+ */
+public class ResourceCache extends RedisHashCache<String, String, String> {
+
+    private final String appId;
+
+    private static final RedisTemplate<String, String> REDIS_TEMPLATE = SpringContextHolder.getBean("redisTemplate", RedisTemplate.class);
+    private ResourceApi resourceApi;
+
+    private ResourceCache(String appId) {
+        super(() -> REDIS_TEMPLATE);
+        this.appId = appId;
+    }
+
+    public static ResourceCache of(String appId) {
+        return new ResourceCache(appId);
+    }
+
+    @Override
+    protected String field() {
+        return appId;
+    }
+
+    @Override
+    protected String key() {
+        return generateKey("account", "resource");
+    }
+
+    @Override
+    protected Supplier<String> getSupplier() {
+        return () -> {
+			if (null == resourceApi) {
+				resourceApi = SpringContextHolder.getBean(ResourceApi.class);
+			}
+            Result<List<UrlResourceDTO>> result = resourceApi.getUrlResourcesByAppId(appId);
+            if (result.isSuccess()) {
+                return JsonUtils.toJsonString(result.getData());
+            }
+            throw new BizException(result.getCode(), result.getMessage());
+        };
+    }
+
+    public List<UrlResourceDTO> getUrlResources() {
+        Optional<String> value = get();
+        if (value.isPresent()) {
+            return JsonUtils.fromJsonArray(value.get(), UrlResourceDTO.class);
+        }
+        return null;
+    }
+
+}

+ 54 - 6
base-servers/account/account-dto/src/main/java/com/usoftchina/saas/account/dto/AccountDTO.java

@@ -1,10 +1,14 @@
 package com.usoftchina.saas.account.dto;
 
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.usoftchina.saas.account.constant.AccountType;
+import com.usoftchina.saas.utils.CollectionUtils;
 import io.swagger.annotations.ApiModel;
 
 import java.io.Serializable;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * @author yingp
@@ -17,6 +21,10 @@ public class AccountDTO implements Serializable {
     private String realname;
     private String email;
     private String mobile;
+    /**
+     * 头像
+     */
+    private String avatarUrl;
     /**
      * 账号类型 0 - 管理员
      */
@@ -32,9 +40,13 @@ public class AccountDTO implements Serializable {
     /**
      * 所拥有的资源
      *
+     * <String> appId
+     * <Long> companyId
+     * <Long> resourceId
+     *
      * @return
      */
-    private Map<Long, List<UrlResourceDTO>> resourcesMap;
+    private Map<String, Map<Long, Set<Long>>> resourcesTable;
 
     public Long getId() {
         return id;
@@ -76,6 +88,14 @@ public class AccountDTO implements Serializable {
         this.mobile = mobile;
     }
 
+    public String getAvatarUrl() {
+        return avatarUrl;
+    }
+
+    public void setAvatarUrl(String avatarUrl) {
+        this.avatarUrl = avatarUrl;
+    }
+
     public Integer getType() {
         return type;
     }
@@ -100,12 +120,39 @@ public class AccountDTO implements Serializable {
         this.rolesMap = rolesMap;
     }
 
-    public Map<Long, List<UrlResourceDTO>> getResourcesMap() {
-        return resourcesMap;
+    public Map<String, Map<Long, Set<Long>>> getResourcesTable() {
+        return resourcesTable;
     }
 
-    public void setResourcesMap(Map<Long, List<UrlResourceDTO>> resourcesMap) {
-        this.resourcesMap = resourcesMap;
+    public void setResourcesTable(Map<String, Map<Long, Set<Long>>> resourcesTable) {
+        this.resourcesTable = resourcesTable;
+    }
+
+    /**
+     * 是否管理账户
+     *
+     * @param companyId
+     * @return
+     */
+    @JsonIgnore
+    public boolean isAdmin(Long companyId) {
+        boolean is = null != type && type == AccountType.ADMIN.getType();
+        if (!is && null != companyId && null != rolesMap) {
+            // 是否管理角色
+            List<RoleBaseDTO> roles = rolesMap.get(companyId);
+            if (!CollectionUtils.isEmpty(roles)) {
+                return roles.stream().anyMatch(RoleBaseDTO::isAdmin);
+            }
+        }
+        return is;
+    }
+
+    @JsonIgnore
+    public Set<Long> getResources(String appId, Long companyId) {
+        if (null != resourcesTable && resourcesTable.containsKey(appId)) {
+            return resourcesTable.get(appId).get(companyId);
+        }
+        return null;
     }
 
     @Override
@@ -116,10 +163,11 @@ public class AccountDTO implements Serializable {
                 ", realname='" + realname + '\'' +
                 ", email='" + email + '\'' +
                 ", mobile='" + mobile + '\'' +
+                ", avatarUrl='" + avatarUrl + '\'' +
                 ", type=" + type +
                 ", companies=" + companies +
                 ", rolesMap=" + rolesMap +
-                ", resourcesMap=" + resourcesMap +
+                ", resourcesTable=" + resourcesTable +
                 '}';
     }
 }

+ 27 - 1
base-servers/account/account-dto/src/main/java/com/usoftchina/saas/account/dto/RoleBaseDTO.java

@@ -1,5 +1,8 @@
 package com.usoftchina.saas.account.dto;
 
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.usoftchina.saas.account.constant.RoleType;
+
 import java.io.Serializable;
 
 /**
@@ -8,7 +11,7 @@ import java.io.Serializable;
  * @author yingp
  * @date 2018/10/10
  */
-public class RoleBaseDTO implements Serializable{
+public class RoleBaseDTO implements Serializable {
     private Long id;
     /**
      * 公司
@@ -22,6 +25,10 @@ public class RoleBaseDTO implements Serializable{
      * 角色名称
      */
     private String name;
+    /**
+     * 类型
+     */
+    private Integer type;
 
     public Long getId() {
         return id;
@@ -55,6 +62,24 @@ public class RoleBaseDTO implements Serializable{
         this.name = name;
     }
 
+    public Integer getType() {
+        return type;
+    }
+
+    public void setType(Integer type) {
+        this.type = type;
+    }
+
+    /**
+     * 是否管理角色
+     *
+     * @return
+     */
+    @JsonIgnore
+    public boolean isAdmin() {
+        return null != type && type == RoleType.ADMIN.getType();
+    }
+
     @Override
     public String toString() {
         return "RoleBaseDTO{" +
@@ -62,6 +87,7 @@ public class RoleBaseDTO implements Serializable{
                 ", companyId=" + companyId +
                 ", code='" + code + '\'' +
                 ", name='" + name + '\'' +
+                ", type=" + type +
                 '}';
     }
 }

+ 11 - 1
base-servers/account/account-dto/src/main/java/com/usoftchina/saas/account/dto/UrlResourceDTO.java

@@ -9,6 +9,7 @@ import java.io.Serializable;
  * @date 2018/10/26
  */
 public class UrlResourceDTO implements Serializable{
+    private Long id;
     /**
      * 名称
      */
@@ -22,6 +23,14 @@ public class UrlResourceDTO implements Serializable{
      */
     private String method;
 
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
     public String getName() {
         return name;
     }
@@ -49,7 +58,8 @@ public class UrlResourceDTO implements Serializable{
     @Override
     public String toString() {
         return "UrlResourceDTO{" +
-                "name='" + name + '\'' +
+                "id=" + id +
+                ", name='" + name + '\'' +
                 ", url='" + url + '\'' +
                 ", method='" + method + '\'' +
                 '}';

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

@@ -19,7 +19,7 @@
         </dependency>
         <dependency>
             <groupId>com.usoftchina.saas</groupId>
-            <artifactId>account-dto</artifactId>
+            <artifactId>account-api</artifactId>
         </dependency>
         <!-- db -->
         <dependency>

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

@@ -1,20 +1,28 @@
 package com.usoftchina.saas.account.controller;
 
+import com.usoftchina.saas.account.cache.AccountCache;
 import com.usoftchina.saas.account.dto.AccountDTO;
 import com.usoftchina.saas.account.dto.AccountRegDTO;
 import com.usoftchina.saas.account.dto.CompanyBaseDTO;
+import com.usoftchina.saas.account.dto.RoleBaseDTO;
 import com.usoftchina.saas.account.po.Account;
+import com.usoftchina.saas.account.po.RoleResource;
 import com.usoftchina.saas.account.service.AccountService;
 import com.usoftchina.saas.account.service.CompanyService;
+import com.usoftchina.saas.account.service.RoleService;
 import com.usoftchina.saas.account.vo.CompanyBaseVO;
 import com.usoftchina.saas.base.Result;
+import com.usoftchina.saas.context.BaseContextHolder;
 import com.usoftchina.saas.exception.ExceptionCode;
 import com.usoftchina.saas.utils.BeanMapper;
+import com.usoftchina.saas.utils.CollectionUtils;
 import com.usoftchina.saas.utils.RegexpUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 /**
  * @author yingp
@@ -30,6 +38,9 @@ public class AccountController {
     @Autowired
     private CompanyService companyService;
 
+    @Autowired
+    private RoleService roleService;
+
     /**
      * 注册
      *
@@ -84,14 +95,41 @@ public class AccountController {
             return Result.error(ExceptionCode.USER_PWD_ERROR);
         }
 
+        return Result.success(getAccountDTO(account));
+    }
+
+    /**
+     * 账户 + 账户的全部绑定信息
+     *
+     * @param account
+     * @return
+     */
+    private AccountDTO getAccountDTO(Account account) {
         AccountDTO accountDTO = BeanMapper.map(account, AccountDTO.class);
         // 绑定的公司
         List<CompanyBaseVO> companyBaseVOS = companyService.findBaseByAccountId(account.getId());
         accountDTO.setCompanies(BeanMapper.mapList(companyBaseVOS, CompanyBaseDTO.class));
-
-        return Result.success(accountDTO);
+        List<RoleBaseDTO> roleBaseDTOS = roleService.findByAccountId(accountDTO.getId());
+        if (!CollectionUtils.isEmpty(roleBaseDTOS)) {
+            // 绑定的 公司->角色
+            accountDTO.setRolesMap(CollectionUtils.groupBy(roleBaseDTOS, RoleBaseDTO::getCompanyId));
+            List<RoleResource> roleResourceVOS = accountService.findRoleResourcesByAccountId(accountDTO.getId());
+            if (!CollectionUtils.isEmpty(roleBaseDTOS)) {
+                // 绑定的 应用+公司->资源
+                Map<String, Map<Long, Set<Long>>> resourcesTable = CollectionUtils.distinctBy(roleResourceVOS,
+                        RoleResource::getAppId, RoleResource::getCompanyId, RoleResource::getResourceId);
+                accountDTO.setResourcesTable(resourcesTable);
+            }
+        }
+        return accountDTO;
     }
 
+    /**
+     * 匹配前端多种输入账号类型
+     *
+     * @param username
+     * @return
+     */
     private Account getAccountByUsername(String username) {
         Account account;
         if (RegexpUtils.isMobile(username)) {
@@ -117,12 +155,23 @@ public class AccountController {
             return Result.error(ExceptionCode.USER_NOT_EXIST);
         }
 
-        AccountDTO accountDTO = BeanMapper.map(account, AccountDTO.class);
-        // 绑定的公司
-        List<CompanyBaseVO> companyBaseVOS = companyService.findBaseByAccountId(account.getId());
-        accountDTO.setCompanies(BeanMapper.mapList(companyBaseVOS, CompanyBaseDTO.class));
+        return Result.success(getAccountDTO(account));
+    }
+
+    /**
+     * 按ID查找账户
+     *
+     * @param id
+     * @return
+     */
+    @GetMapping("/{id}")
+    public Result<AccountDTO> getAccountById(@PathVariable Long id) {
+        Account account = accountService.findByPrimaryKey(id);
+        if (null == account) {
+            return Result.error(ExceptionCode.USER_NOT_EXIST);
+        }
 
-        return Result.success(accountDTO);
+        return Result.success(getAccountDTO(account));
     }
 
     /**
@@ -135,6 +184,7 @@ public class AccountController {
     @PostMapping("/bind/company")
     public Result bindCompany(@RequestParam long accountId, @RequestParam long companyId) {
         accountService.bindCompany(accountId, companyId);
+        accountService.clearCache(accountId);
         return Result.success();
     }
 
@@ -148,6 +198,7 @@ public class AccountController {
     @PostMapping("/unbind/company")
     public Result unbindCompany(@RequestParam long accountId, @RequestParam long companyId) {
         accountService.unbindCompany(accountId, companyId);
+        accountService.clearCache(accountId);
         return Result.success();
     }
 
@@ -161,6 +212,7 @@ public class AccountController {
     @PostMapping("/bind/role")
     public Result bindRole(@RequestParam long accountId, @RequestParam long roleId) {
         accountService.bindRole(accountId, roleId);
+        accountService.clearCache(accountId);
         return Result.success();
     }
 
@@ -174,6 +226,7 @@ public class AccountController {
     @PostMapping("/unbind/role")
     public Result unbindRole(@RequestParam long accountId, @RequestParam long roleId) {
         accountService.unbindRole(accountId, roleId);
+        accountService.clearCache(accountId);
         return Result.success();
     }
 
@@ -186,6 +239,7 @@ public class AccountController {
     @PostMapping("/disable")
     public Result disableAccount(@RequestParam long accountId) {
         accountService.disable(accountId);
+        accountService.clearCache(accountId);
         return Result.success();
     }
 
@@ -198,6 +252,7 @@ public class AccountController {
     @PostMapping("/enable")
     public Result enableAccount(@RequestParam long accountId) {
         accountService.enable(accountId);
+        accountService.clearCache(accountId);
         return Result.success();
     }
 
@@ -209,7 +264,19 @@ public class AccountController {
      */
     @PostMapping("/delete/{id}")
     public Result deleteAccount(@PathVariable Long id) {
+        accountService.clearCache(id);
         accountService.removeByPrimaryKey(id);
         return Result.success();
     }
+
+    /**
+     * 账户缓存清除
+     *
+     * @return
+     */
+    @GetMapping("/cache/clear")
+    public Result clearCache() {
+        accountService.clearCache(BaseContextHolder.getUserId());
+        return Result.success();
+    }
 }

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

@@ -19,7 +19,7 @@ import java.util.List;
  */
 @RestController
 @RequestMapping("/resource")
-public class ResourcesController {
+public class ResourceController {
 
     @Autowired
     private ResourceService resourcesService;

+ 22 - 0
base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/controller/RoleController.java

@@ -1,9 +1,12 @@
 package com.usoftchina.saas.account.controller;
 
+import com.usoftchina.saas.account.cache.AccountCache;
 import com.usoftchina.saas.account.dto.RoleDTO;
 import com.usoftchina.saas.account.dto.RoleSaveDTO;
 import com.usoftchina.saas.account.dto.RoleUpdateDTO;
+import com.usoftchina.saas.account.po.Account;
 import com.usoftchina.saas.account.po.Role;
+import com.usoftchina.saas.account.service.AccountService;
 import com.usoftchina.saas.account.service.RoleService;
 import com.usoftchina.saas.base.Result;
 import com.usoftchina.saas.commons.dto.DocBaseDTO;
@@ -27,6 +30,9 @@ public class RoleController {
     @Autowired
     private RoleService roleService;
 
+    @Autowired
+    private AccountService accountService;
+
     /**
      * 保存
      *
@@ -50,6 +56,7 @@ public class RoleController {
     public Result update(@RequestBody RoleUpdateDTO roleUpdateDTO) {
         Role role = BeanMapper.map(roleUpdateDTO, Role.class);
         roleService.updateByPrimaryKey(role);
+        clearAccountCacheByRoleId(roleUpdateDTO.getId());
         return Result.success();
     }
 
@@ -61,6 +68,7 @@ public class RoleController {
      */
     @PostMapping("/delete/{id}")
     public Result delete(@PathVariable Long id) {
+        clearAccountCacheByRoleId(id);
         roleService.removeByPrimaryKey(id);
         return Result.success();
     }
@@ -104,6 +112,7 @@ public class RoleController {
     @PostMapping("/bind/resource")
     public Result bindResource(@RequestParam long roleId, @RequestParam long resourceId) {
         roleService.bindResource(roleId, resourceId);
+        clearAccountCacheByRoleId(roleId);
         return Result.success();
     }
 
@@ -117,6 +126,19 @@ public class RoleController {
     @PostMapping("/unbind/resource")
     public Result unbindResource(@RequestParam long roleId, @RequestParam long resourceId) {
         roleService.unbindResource(roleId, resourceId);
+        clearAccountCacheByRoleId(roleId);
         return Result.success();
     }
+
+    /**
+     * 清除角色相关所有账户的缓存
+     *
+     * @param roleId
+     */
+    private void clearAccountCacheByRoleId(long roleId) {
+        List<Account> accounts = accountService.findByRoleId(roleId);
+        if (!CollectionUtils.isEmpty(accounts)) {
+            accounts.forEach(account -> accountService.clearCache(account.getId()));
+        }
+    }
 }

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

@@ -3,6 +3,8 @@ package com.usoftchina.saas.account.mapper;
 import com.usoftchina.saas.account.po.Account;
 import org.apache.ibatis.annotations.Param;
 
+import java.util.List;
+
 /**
  * @author yingp
  * @date 2018/10/2
@@ -56,6 +58,14 @@ public interface AccountMapper {
      */
     Account selectByEmail(@Param("email") String email);
 
+    /**
+     * 按角色查找
+     *
+     * @param roleId
+     * @return
+     */
+    List<Account> selectByRoleId(@Param("roleId") Long roleId);
+
     /**
      * 删除
      *

+ 9 - 1
base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/mapper/AccountRoleMapper.java

@@ -10,11 +10,12 @@ public interface AccountRoleMapper {
     /**
      * 新增
      *
+     * @param companyId
      * @param accountId
      * @param roleId
      * @return
      */
-    int insert(@Param("accountId") Long accountId, @Param("roleId") Long roleId);
+    int insert(@Param("companyId") Long companyId, @Param("accountId") Long accountId, @Param("roleId") Long roleId);
 
     /**
      * 删除
@@ -31,6 +32,13 @@ public interface AccountRoleMapper {
      */
     void deleteByAccountId(@Param("accountId") Long accountId);
 
+    /**
+     * 按公司删除
+     *
+     * @param companyId
+     */
+    void deleteByCompanyId(@Param("companyId") Long companyId);
+
     /**
      * 按角色删除
      *

+ 16 - 0
base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/mapper/ResourceMapper.java

@@ -26,4 +26,20 @@ public interface ResourceMapper {
      * @return
      */
     List<Resource> selectByAppId(@Param("appId") String appId);
+
+    /**
+     * 按角色查找
+     *
+     * @param roleId
+     * @return
+     */
+    List<Resource> selectByRoleId(@Param("roleId") Long roleId);
+
+    /**
+     * 按主键查找
+     *
+     * @param id
+     * @return
+     */
+    Resource selectByPrimaryKey(@Param("id") Long id);
 }

+ 11 - 0
base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/mapper/RoleMapper.java

@@ -2,10 +2,21 @@ package com.usoftchina.saas.account.mapper;
 
 import com.usoftchina.saas.account.po.Role;
 import com.usoftchina.saas.base.mapper.CommonBaseMapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
 
 /**
  * @author yingp
  * @date 2018/10/2
  */
 public interface RoleMapper extends CommonBaseMapper<Role> {
+
+    /**
+     * 按账户查找绑定的角色
+     *
+     * @param accountId
+     * @return
+     */
+    List<Role> selectByAccountId(@Param("accountId") long accountId);
 }

+ 22 - 1
base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/mapper/RoleResourceMapper.java

@@ -1,7 +1,10 @@
 package com.usoftchina.saas.account.mapper;
 
+import com.usoftchina.saas.account.po.RoleResource;
 import org.apache.ibatis.annotations.Param;
 
+import java.util.List;
+
 /**
  * @author yingp
  * @date 2018/10/2
@@ -10,11 +13,13 @@ public interface RoleResourceMapper {
     /**
      * 新增
      *
+     * @param appId
+     * @param companyId
      * @param roleId
      * @param resourceId
      * @return
      */
-    int insert(@Param("roleId") Long roleId, @Param("resourceId") Long resourceId);
+    int insert(@Param("appId") String appId, @Param("companyId") Long companyId, @Param("roleId") Long roleId, @Param("resourceId") Long resourceId);
 
     /**
      * 删除
@@ -32,4 +37,20 @@ public interface RoleResourceMapper {
      * @return
      */
     int deleteByRoleId(@Param("roleId") Long roleId);
+
+    /**
+     * 按公司删除
+     *
+     * @param companyId
+     * @return
+     */
+    int deleteByCompanyId(@Param("companyId") Long companyId);
+
+    /**
+     * 按账户查询绑定的角色+资源
+     *
+     * @param accountId
+     * @return
+     */
+    List<RoleResource> selectByAccountId(@Param("accountId") Long accountId);
 }

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

@@ -17,6 +17,10 @@ public class Account implements Serializable {
     private String realname;
     private String email;
     private String mobile;
+    /**
+     * 头像
+     */
+    private String avatarUrl;
     /**
      * 账号类型 0 - 管理员
      */
@@ -75,6 +79,14 @@ public class Account implements Serializable {
         this.mobile = mobile;
     }
 
+    public String getAvatarUrl() {
+        return avatarUrl;
+    }
+
+    public void setAvatarUrl(String avatarUrl) {
+        this.avatarUrl = avatarUrl;
+    }
+
     public Integer getType() {
         return type;
     }

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

@@ -11,6 +11,12 @@ import java.io.Serializable;
 public class AccountRole implements Serializable {
     private long accountId;
     private long roleId;
+    /**
+     * 角色所属公司
+     *
+     * <p>冗余字段,方便查询</p>
+     */
+    private long companyId;
 
     public long getAccountId() {
         return accountId;
@@ -27,4 +33,12 @@ public class AccountRole implements Serializable {
     public void setRoleId(long roleId) {
         this.roleId = roleId;
     }
+
+    public long getCompanyId() {
+        return companyId;
+    }
+
+    public void setCompanyId(long companyId) {
+        this.companyId = companyId;
+    }
 }

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

@@ -10,6 +10,12 @@ import java.io.Serializable;
  */
 public class Resource implements Serializable {
     private Long id;
+    /**
+     * 归属应用
+     *
+     * <p>冗余字段,方便查询</p>
+     */
+    private String appId;
     /**
      * 资源组
      */
@@ -51,6 +57,14 @@ public class Resource implements Serializable {
         this.id = id;
     }
 
+    public String getAppId() {
+        return appId;
+    }
+
+    public void setAppId(String appId) {
+        this.appId = appId;
+    }
+
     public String getType() {
         return type;
     }

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

@@ -11,6 +11,12 @@ import java.io.Serializable;
  */
 public class ResourceGroup implements Serializable {
     private Long id;
+    /**
+     * 归属应用
+     *
+     * <p>冗余字段,方便查询</p>
+     */
+    private String appId;
     private Long moduleId;
     private String name;
 
@@ -22,6 +28,14 @@ public class ResourceGroup implements Serializable {
         this.id = id;
     }
 
+    public String getAppId() {
+        return appId;
+    }
+
+    public void setAppId(String appId) {
+        this.appId = appId;
+    }
+
     public Long getModuleId() {
         return moduleId;
     }

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

@@ -10,7 +10,33 @@ import java.io.Serializable;
  */
 public class RoleResource implements Serializable {
     private long roleId;
+    /**
+     * 资源所属应用
+     * <p>冗余字段,方便查询</p>
+     */
+    private String appId;
     private long resourceId;
+    /**
+     * 角色所属公司
+     * <p>冗余字段,方便查询</p>
+     */
+    private long companyId;
+
+    public String getAppId() {
+        return appId;
+    }
+
+    public void setAppId(String appId) {
+        this.appId = appId;
+    }
+
+    public long getCompanyId() {
+        return companyId;
+    }
+
+    public void setCompanyId(long companyId) {
+        this.companyId = companyId;
+    }
 
     public long getRoleId() {
         return roleId;

+ 33 - 0
base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/service/AccountService.java

@@ -1,6 +1,9 @@
 package com.usoftchina.saas.account.service;
 
 import com.usoftchina.saas.account.po.Account;
+import com.usoftchina.saas.account.po.RoleResource;
+
+import java.util.List;
 
 /**
  * @author yingp
@@ -39,6 +42,22 @@ public interface AccountService {
      */
     Account findByEmail(String email);
 
+    /**
+     * 按主键查找
+     *
+     * @param id
+     * @return
+     */
+    Account findByPrimaryKey(Long id);
+
+    /**
+     * 按角色查找
+     *
+     * @param roleId
+     * @return
+     */
+    List<Account> findByRoleId(Long roleId);
+
     /**
      * 校验密码
      *
@@ -111,4 +130,18 @@ public interface AccountService {
      */
     void enable(Long accountId);
 
+    /**
+     * 按账户查找绑定的角色+资源
+     *
+     * @param accountId
+     * @return
+     */
+    List<RoleResource> findRoleResourcesByAccountId(Long accountId);
+
+    /**
+     * 清除账户相关缓存
+     *
+     * @param accountId
+     */
+    void clearCache(Long accountId);
 }

+ 16 - 0
base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/service/ResourceService.java

@@ -26,4 +26,20 @@ public interface ResourceService {
      * @return
      */
     List<UrlResourceDTO> findUrlResourcesByAppId(String appId);
+
+    /**
+     * 按角色查找绑定的资源
+     *
+     * @param roleId
+     * @return
+     */
+    List<UrlResourceDTO> findUrlResourcesByRoleId(Long roleId);
+
+    /**
+     * 按主键查找资源
+     *
+     * @param id
+     * @return
+     */
+    Resource findByPrimaryKey(Long id);
 }

+ 12 - 1
base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/service/RoleService.java

@@ -1,14 +1,17 @@
 package com.usoftchina.saas.account.service;
 
+import com.usoftchina.saas.account.dto.RoleBaseDTO;
 import com.usoftchina.saas.account.mapper.RoleMapper;
 import com.usoftchina.saas.account.po.Role;
 import com.usoftchina.saas.base.service.CommonBaseService;
 
+import java.util.List;
+
 /**
  * @author yingp
  * @date 2018/10/24
  */
-public interface RoleService extends CommonBaseService<RoleMapper, Role>{
+public interface RoleService extends CommonBaseService<RoleMapper, Role> {
 
     /**
      * 角色绑定资源
@@ -25,4 +28,12 @@ public interface RoleService extends CommonBaseService<RoleMapper, Role>{
      * @param resourceId
      */
     void unbindResource(long roleId, long resourceId);
+
+    /**
+     * 按账户查找绑定角色
+     *
+     * @param accountId
+     * @return
+     */
+    List<RoleBaseDTO> findByAccountId(Long accountId);
 }

+ 37 - 1
base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/service/impl/AccountServiceImpl.java

@@ -1,15 +1,22 @@
 package com.usoftchina.saas.account.service.impl;
 
+import com.usoftchina.saas.account.cache.AccountCache;
 import com.usoftchina.saas.account.mapper.AccountCompanyMapper;
 import com.usoftchina.saas.account.mapper.AccountMapper;
 import com.usoftchina.saas.account.mapper.AccountRoleMapper;
+import com.usoftchina.saas.account.mapper.RoleResourceMapper;
 import com.usoftchina.saas.account.po.Account;
+import com.usoftchina.saas.account.po.Role;
+import com.usoftchina.saas.account.po.RoleResource;
 import com.usoftchina.saas.account.service.AccountService;
+import com.usoftchina.saas.account.service.RoleService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.DigestUtils;
 
+import java.util.List;
+
 /**
  * @author yingp
  * @date 2018/10/2
@@ -26,6 +33,12 @@ public class AccountServiceImpl implements AccountService {
     @Autowired
     private AccountRoleMapper accountRoleMapper;
 
+    @Autowired
+    private RoleService roleService;
+
+    @Autowired
+    private RoleResourceMapper roleResourceMapper;
+
     @Override
     public boolean save(Account account) {
         return accountMapper.insert(account) > 0;
@@ -46,6 +59,11 @@ public class AccountServiceImpl implements AccountService {
         return accountMapper.selectByEmail(email);
     }
 
+    @Override
+    public Account findByPrimaryKey(Long id) {
+        return accountMapper.selectByPrimaryKey(id);
+    }
+
     @Override
     public boolean checkPwd(Account account, String plainPassword) {
         return getEncryptedPassword(plainPassword, account.getSalt()).equals(account.getPassword());
@@ -75,7 +93,10 @@ public class AccountServiceImpl implements AccountService {
 
     @Override
     public void bindRole(Long accountId, Long roleId) {
-        accountRoleMapper.insert(accountId, roleId);
+        Role role = roleService.findByPrimaryKey(roleId);
+        if (null != role) {
+            accountRoleMapper.insert(role.getCompanyId(), accountId, roleId);
+        }
     }
 
     @Override
@@ -106,4 +127,19 @@ public class AccountServiceImpl implements AccountService {
             accountMapper.updateEnabled(accountId, false);
         }
     }
+
+    @Override
+    public List<RoleResource> findRoleResourcesByAccountId(Long accountId) {
+        return roleResourceMapper.selectByAccountId(accountId);
+    }
+
+    @Override
+    public List<Account> findByRoleId(Long roleId) {
+        return accountMapper.selectByRoleId(roleId);
+    }
+
+    @Override
+    public void clearCache(Long accountId) {
+        AccountCache.of(accountId).hdel();
+    }
 }

+ 14 - 0
base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/service/impl/ResourceServiceImpl.java

@@ -101,4 +101,18 @@ public class ResourceServiceImpl implements ResourceService {
         }
         return null;
     }
+
+    @Override
+    public List<UrlResourceDTO> findUrlResourcesByRoleId(Long roleId) {
+        List<Resource> resources = resourceMapper.selectByRoleId(roleId);
+        if (!CollectionUtils.isEmpty(resources)) {
+            return BeanMapper.mapList(resources, UrlResourceDTO.class);
+        }
+        return null;
+    }
+
+    @Override
+    public Resource findByPrimaryKey(Long id) {
+        return resourceMapper.selectByPrimaryKey(id);
+    }
 }

+ 24 - 1
base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/service/impl/RoleServiceImpl.java

@@ -1,18 +1,25 @@
 package com.usoftchina.saas.account.service.impl;
 
 import com.usoftchina.saas.account.constant.RoleType;
+import com.usoftchina.saas.account.dto.RoleBaseDTO;
 import com.usoftchina.saas.account.mapper.AccountRoleMapper;
 import com.usoftchina.saas.account.mapper.RoleMapper;
 import com.usoftchina.saas.account.mapper.RoleResourceMapper;
+import com.usoftchina.saas.account.po.Resource;
 import com.usoftchina.saas.account.po.Role;
+import com.usoftchina.saas.account.service.ResourceService;
 import com.usoftchina.saas.account.service.RoleService;
 import com.usoftchina.saas.base.service.CommonBaseServiceImpl;
+import com.usoftchina.saas.utils.BeanMapper;
+import com.usoftchina.saas.utils.CollectionUtils;
 import com.usoftchina.saas.utils.StringUtils;
 import org.apache.commons.lang.RandomStringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
+import java.util.List;
+
 /**
  * @author yingp
  * @date 2018/10/24
@@ -28,6 +35,9 @@ public class RoleServiceImpl extends CommonBaseServiceImpl<RoleMapper, Role> imp
     @Autowired
     private AccountRoleMapper accountRoleMapper;
 
+    @Autowired
+    private ResourceService resourceService;
+
     @Override
     public boolean save(Role role) {
         if (StringUtils.isEmpty(role.getCode())) {
@@ -40,7 +50,11 @@ public class RoleServiceImpl extends CommonBaseServiceImpl<RoleMapper, Role> imp
 
     @Override
     public void bindResource(long roleId, long resourceId) {
-        roleResourceMapper.insert(roleId, resourceId);
+        Role role = findByPrimaryKey(roleId);
+        Resource resource = resourceService.findByPrimaryKey(resourceId);
+        if (null != role && null != resource) {
+            roleResourceMapper.insert(resource.getAppId(), role.getCompanyId(), roleId, resourceId);
+        }
     }
 
     @Override
@@ -55,4 +69,13 @@ public class RoleServiceImpl extends CommonBaseServiceImpl<RoleMapper, Role> imp
         roleResourceMapper.deleteByRoleId(id);
         return super.removeByPrimaryKey(id);
     }
+
+    @Override
+    public List<RoleBaseDTO> findByAccountId(Long accountId) {
+        List<Role> roles = getMapper().selectByAccountId(accountId);
+        if (!CollectionUtils.isEmpty(roles)) {
+            return BeanMapper.mapList(roles, RoleBaseDTO.class);
+        }
+        return null;
+    }
 }

+ 3 - 0
base-servers/account/account-server/src/main/resources/application.yml

@@ -35,6 +35,9 @@ spring:
       connection-timeout: 30000
   messages:
     basename: i18n/messages
+  redis:
+    host: 192.168.253.12
+    port: 6379
 eureka:
   instance:
     leaseRenewalIntervalInSeconds: 10

+ 7 - 1
base-servers/account/account-server/src/main/resources/mapper/AccountMapper.xml

@@ -17,7 +17,9 @@
         <result column="update_time" jdbcType="TIMESTAMP" property="updateTime"/>
     </resultMap>
     <sql id="baseColumns">
-        id,username,password,salt,realname,email,mobile,type,enabled,creator_id,create_time,updater_id,update_time
+        ac_account.id,ac_account.username,ac_account.password,ac_account.salt,ac_account.realname,ac_account.email,
+        ac_account.mobile,ac_account.type,ac_account.enabled,ac_account.creator_id,ac_account.create_time,
+        ac_account.updater_id,ac_account.update_time
     </sql>
     <insert id="insert" parameterType="com.usoftchina.saas.account.po.Account">
         insert into ac_account(username,password,salt,realname,email,mobile,type,enabled,creator_id,create_time,updater_id,update_time)
@@ -116,6 +118,10 @@
     <select id="selectByEmail" parameterType="java.lang.String" resultMap="BaseResultMap">
         select <include refid="baseColumns"/> from ac_account where email=#{email}
     </select>
+    <select id="selectByRoleId" parameterType="java.lang.Long" resultMap="BaseResultMap">
+        select <include refid="baseColumns"/> from ac_account,ac_account_role where
+        ac_account.id=ac_account_role.account_id and ac_account_role.role_id=#{roleId,jdbcType=BIGINT}
+    </select>
     <delete id="deleteByPrimaryKey" parameterType="java.lang.Long">
         delete from ac_account where id=#{id}
     </delete>

+ 4 - 1
base-servers/account/account-server/src/main/resources/mapper/AccountRoleMapper.xml

@@ -2,7 +2,7 @@
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
 <mapper namespace="com.usoftchina.saas.account.mapper.AccountRoleMapper">
     <insert id="insert">
-        insert into ac_account_role(account_id,role_id) values (#{accountId}, #{roleId})
+        insert into ac_account_role(company_id,account_id,role_id) values (#{companyId}, #{accountId}, #{roleId})
     </insert>
     <delete id="delete">
         delete from ac_account_role where account_id=#{accountId} and role_id=#{roleId}
@@ -13,4 +13,7 @@
     <delete id="deleteByRoleId" parameterType="java.lang.Long">
         delete from ac_account_role where role_id=#{roleId}
     </delete>
+    <delete id="deleteByCompanyId" parameterType="java.lang.Long">
+        delete from ac_account_role where company_id=#{companyId}
+    </delete>
 </mapper>

+ 9 - 1
base-servers/account/account-server/src/main/resources/mapper/ResourceMapper.xml

@@ -3,6 +3,7 @@
 <mapper namespace="com.usoftchina.saas.account.mapper.ResourceMapper">
     <resultMap id="BaseResultMap" type="com.usoftchina.saas.account.po.Resource">
         <id column="id" jdbcType="BIGINT" property="id"/>
+        <result column="app_id" jdbcType="VARCHAR" property="appId"/>
         <result column="name" jdbcType="VARCHAR" property="name"/>
         <result column="group_id" jdbcType="BIGINT" property="groupId"/>
         <result column="type" jdbcType="VARCHAR" property="type"/>
@@ -11,7 +12,7 @@
         <result column="classify" jdbcType="VARCHAR" property="classify"/>
     </resultMap>
     <sql id="baseColumns">
-        ac_resource.id,ac_resource.name,ac_resource.group_id,ac_resource.type,ac_resource.url,ac_resource.method,ac_resource.classify
+        ac_resource.id,ac_resource.app_id,ac_resource.name,ac_resource.group_id,ac_resource.type,ac_resource.url,ac_resource.method,ac_resource.classify
     </sql>
     <select id="selectByGroupId" parameterType="java.lang.Long" resultMap="BaseResultMap">
         select <include refid="baseColumns"/> from ac_resource where group_id=#{groupId,jdbcType=BIGINT}
@@ -21,4 +22,11 @@
         ac_resource_module.id=ac_resource_group.module_id and ac_resource_group.id=ac_resource.group_id
         and ac_resource_module.app_id=#{appId,jdbcType=VARCHAR}
     </select>
+    <select id="selectByRoleId" parameterType="java.lang.Long" resultMap="BaseResultMap">
+        select <include refid="baseColumns"/> from ac_resource,ac_role_resource where
+        ac_resource.id=ac_role_resource.resource_id and ac_role_resource.role_id=#{roleId,jdbcType=BIGINT}
+    </select>
+    <select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
+        select <include refid="baseColumns"/> from ac_resource where id=#{id,jdbcType=BIGINT}
+    </select>
 </mapper>

+ 6 - 1
base-servers/account/account-server/src/main/resources/mapper/RoleMapper.xml

@@ -14,7 +14,8 @@
         <result column="update_time" jdbcType="TIMESTAMP" property="updateTime"/>
     </resultMap>
     <sql id="baseColumns">
-        id,code,name,description,type,company_id,creator_id,create_time,updater_id,update_time
+        ac_role.id,ac_role.code,ac_role.name,ac_role.description,ac_role.type,ac_role.company_id,
+        ac_role.creator_id,ac_role.create_time,ac_role.updater_id,ac_role.update_time
     </sql>
     <insert id="insert" parameterType="com.usoftchina.saas.account.po.Role"
             useGeneratedKeys="true" keyProperty="id">
@@ -99,4 +100,8 @@
     <select id="selectByCompanyId" parameterType="java.lang.Long" resultMap="BaseResultMap">
         select <include refid="baseColumns"/> from ac_role where company_id=#{companyId,jdbcType=BIGINT}
     </select>
+    <select id="selectByAccountId" parameterType="java.lang.Long" resultMap="BaseResultMap">
+        select <include refid="baseColumns"/> from ac_role,ac_account_role
+        where ac_role.id=ac_account_role.role_id and ac_account_role.account_id=#{accountId,jdbcType=BIGINT}
+    </select>
 </mapper>

+ 15 - 1
base-servers/account/account-server/src/main/resources/mapper/RoleResourceMapper.xml

@@ -2,7 +2,7 @@
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
 <mapper namespace="com.usoftchina.saas.account.mapper.RoleResourceMapper">
     <insert id="insert">
-        insert into ac_role_resource(role_id,resource_id) values (#{roleId}, #{resourceId})
+        insert into ac_role_resource(app_id,company_id,role_id,resource_id) values (#{appId}, #{companyId}, #{roleId}, #{resourceId})
     </insert>
     <delete id="delete">
         delete from ac_role_resource where role_id=#{roleId} and resource_id=#{resourceId}
@@ -10,4 +10,18 @@
     <delete id="deleteByRoleId" parameterType="java.lang.Long">
         delete from ac_role_resource where role_id=#{roleId}
     </delete>
+    <delete id="deleteByCompanyId" parameterType="java.lang.Long">
+        delete from ac_role_resource where company_id=#{companyId}
+    </delete>
+    <resultMap id="BaseResultMap" type="com.usoftchina.saas.account.po.RoleResource">
+        <result column="role_id" jdbcType="BIGINT" property="roleId"/>
+        <result column="app_id" jdbcType="VARCHAR" property="appId"/>
+        <result column="company_id" jdbcType="BIGINT" property="companyId"/>
+        <result column="resource_id" jdbcType="BIGINT" property="resourceId"/>
+    </resultMap>
+    <select id="selectByAccountId" parameterType="java.lang.Long" resultMap="BaseResultMap">
+        select ac_role_resource.* from ac_account_role,ac_role_resource
+        where ac_account_role.role_id=ac_role_resource.role_id
+        and ac_account_role.account_id=#{accountId,jdbcType=BIGINT}
+    </select>
 </mapper>

+ 65 - 6
base-servers/account/account-server/src/test/java/com/usoftchina/saas/account/controller/AccountControllerTest.java

@@ -3,7 +3,9 @@ package com.usoftchina.saas.account.controller;
 import com.usoftchina.saas.account.constant.AccountType;
 import com.usoftchina.saas.account.dto.AccountDTO;
 import com.usoftchina.saas.account.dto.AccountRegDTO;
+import com.usoftchina.saas.account.dto.RoleSaveDTO;
 import com.usoftchina.saas.base.Result;
+import com.usoftchina.saas.commons.dto.DocBaseDTO;
 import com.usoftchina.saas.test.BaseControllerTest;
 import com.usoftchina.saas.test.TestConstant;
 import org.junit.*;
@@ -58,14 +60,53 @@ public class AccountControllerTest extends BaseControllerTest {
                 .andExpect(isFail());
     }
 
-    @Test
-    public void testD_getAccount() throws Exception {
-        AccountDTO accountDTO = getAccountDTO();
-        Assert.assertEquals(accountDTO.getMobile(), mobile);
+    private DocBaseDTO createRole() throws Exception {
+        RoleSaveDTO role = new RoleSaveDTO();
+        role.setName("Tester");
+        role.setDescription("测试员");
+
+        MvcResult mvcResult = mockMvc.perform(requestBody("/role/save", role))
+                .andExpect(isSuccess())
+                .andReturn();
+        Result<DocBaseDTO> result = result(mvcResult, DocBaseDTO.class);
+        return result.getData();
+    }
+
+    private void deleteRole(Long roleId) throws Exception {
+        mockMvc.perform(post("/role/delete/{id}", roleId))
+                .andExpect(isSuccess());
+    }
+
+    private void roleBindResource(Long roleId, Long resourceId) throws Exception{
+        mockMvc.perform(post("/role/bind/resource")
+                .param("roleId", String.valueOf(roleId))
+                .param("resourceId", String.valueOf(resourceId)))
+                .andExpect(isSuccess());
+    }
+
+    private void roleUnbindResource(Long roleId, Long resourceId) throws Exception{
+        mockMvc.perform(post("/role/unbind/resource")
+                .param("roleId", String.valueOf(roleId))
+                .param("resourceId", String.valueOf(resourceId)))
+                .andExpect(isSuccess());
+    }
+
+    private void accountBindRole(Long accountId, Long roleId) throws Exception{
+        mockMvc.perform(post("/account/bind/role")
+                .param("accountId", String.valueOf(accountId))
+                .param("roleId", String.valueOf(roleId)))
+                .andExpect(isSuccess());
+    }
+
+    private void accountUnbindRole(Long accountId, Long roleId) throws Exception{
+        mockMvc.perform(post("/account/unbind/role")
+                .param("accountId", String.valueOf(accountId))
+                .param("roleId", String.valueOf(roleId)))
+                .andExpect(isSuccess());
     }
 
     private AccountDTO getAccountDTO() throws Exception {
-        MvcResult mvcResult = mockMvc.perform(get("/account")
+        MvcResult mvcResult = mockMvc.perform(get("/account/read")
                 .param("username", mobile))
                 .andExpect(isSuccess())
                 .andReturn();
@@ -75,7 +116,7 @@ public class AccountControllerTest extends BaseControllerTest {
     }
 
     @Test
-    public void testE_bindCompany() throws Exception {
+    public void testD_bindCompany() throws Exception {
         AccountDTO accountDTO = getAccountDTO();
         mockMvc.perform(post("/account/bind/company")
                 .param("accountId", String.valueOf(accountDTO.getId()))
@@ -83,6 +124,24 @@ public class AccountControllerTest extends BaseControllerTest {
                 .andExpect(isSuccess());
     }
 
+    @Test
+    public void testE_bindRole() throws Exception {
+        AccountDTO accountDTO = getAccountDTO();
+        DocBaseDTO role = createRole();
+        roleBindResource(role.getId(), 1L);
+        roleBindResource(role.getId(), 2L);
+        accountBindRole(accountDTO.getId(), role.getId());
+
+        getAccountDTO();
+
+        accountUnbindRole(accountDTO.getId(), role.getId());
+        roleUnbindResource(role.getId(), 2L);
+        roleUnbindResource(role.getId(), 1L);
+        deleteRole(role.getId());
+
+        getAccountDTO();
+    }
+
     @Test
     public void testF_unbindCompany() throws Exception {
         AccountDTO accountDTO = getAccountDTO();

+ 0 - 4
base-servers/auth/auth-api/pom.xml

@@ -25,10 +25,6 @@
             <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>

+ 8 - 0
base-servers/auth/auth-api/src/main/java/com/usoftchina/saas/auth/api/AuthApi.java

@@ -32,4 +32,12 @@ public interface AuthApi {
      */
     @GetMapping(value = "/api/auth/switch/company")
     Result<TokenDTO> switchCompany(@RequestParam(value = "companyId") String companyId);
+
+    /**
+     * 获取auth信息
+     *
+     * @return
+     */
+    @GetMapping("/info")
+    Result<AuthDTO> getInfo();
 }

+ 0 - 47
base-servers/auth/auth-api/src/main/java/com/usoftchina/saas/auth/cache/TokenCache.java

@@ -1,47 +0,0 @@
-package com.usoftchina.saas.auth.cache;
-
-import com.usoftchina.saas.auth.api.AuthApi;
-import com.usoftchina.saas.cache.RedisHashCache;
-import com.usoftchina.saas.context.SpringContextHolder;
-import org.springframework.data.redis.core.RedisTemplate;
-
-import java.util.function.Supplier;
-
-/**
- * @author yingp
- * @date 2018/9/30
- */
-public class TokenCache extends RedisHashCache<String, String, String> {
-
-    private final String account;
-
-    private final String password;
-
-    private final AuthApi authApi;
-
-    private TokenCache(String account, String password) {
-        super(() -> SpringContextHolder.getBean(RedisTemplate.class));
-        this.account = account;
-        this.password = password;
-        this.authApi = SpringContextHolder.getBean(AuthApi.class);
-    }
-
-    public static TokenCache of(String account, String password) {
-        return new TokenCache(account, password);
-    }
-
-    @Override
-    protected String key() {
-        return generateKey("auth", "token");
-    }
-
-    @Override
-    protected String field() {
-        return String.valueOf(account);
-    }
-
-    @Override
-    protected Supplier<String> getSupplier() {
-        return () -> authApi.authorize(account, password).getData().getToken().getToken();
-    }
-}

+ 19 - 0
base-servers/auth/auth-server/src/main/java/com/usoftchina/saas/auth/controller/AuthController.java

@@ -87,6 +87,25 @@ public class AuthController {
         return Result.error(ExceptionCode.COMPANY_NOT_BIND);
     }
 
+    /**
+     * 获取auth信息
+     *
+     * @param request
+     * @return
+     */
+    @GetMapping("/info")
+    public Result<AuthDTO> getInfo(HttpServletRequest request) {
+        String token = request.getHeader(authHeader);
+        JwtInfo infoFromToken = JwtHelper.getInfoFromToken(token, publicKeyPath);
+        Result<AccountDTO> result = accountApi.getAccount(infoFromToken.getUserName());
+        if (result.isSuccess()) {
+            TokenVO tokenVO = JwtHelper.generateToken(infoFromToken, privateKeyPath, expire);
+            TokenDTO tokenDTO = BeanMapper.map(tokenVO, TokenDTO.class);
+            return Result.success(new AuthDTO(tokenDTO, result.getData()));
+        }
+        return Result.error(result);
+    }
+
     /**
      * 指定公司是否可用:
      * 公司是否存在 + 是否已绑定

+ 4 - 0
base-servers/gateway-server/pom.xml

@@ -68,6 +68,10 @@
             <groupId>com.usoftchina.saas</groupId>
             <artifactId>auth-common</artifactId>
         </dependency>
+        <dependency>
+            <groupId>com.usoftchina.saas</groupId>
+            <artifactId>account-api</artifactId>
+        </dependency>
     </dependencies>
     <build>
         <plugins>

+ 4 - 1
base-servers/gateway-server/src/main/java/com/usoftchina/saas/gateway/GatewayApplication.java

@@ -11,7 +11,10 @@ import org.springframework.cloud.openfeign.EnableFeignClients;
  */
 @SpringBootApplication
 @EnableEurekaClient
-@EnableFeignClients(basePackages = "com.usoftchina.saas.auth.api")
+@EnableFeignClients(basePackages = {
+        "com.usoftchina.saas.account.api",
+        "com.usoftchina.saas.auth.api"
+})
 public class GatewayApplication {
     public static void main(String[] args) {
         SpringApplication.run(GatewayApplication.class, args);

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

@@ -1,9 +1,16 @@
 package com.usoftchina.saas.gateway.config;
 
+import com.usoftchina.saas.account.cache.AccountCache;
+import com.usoftchina.saas.account.cache.ResourceCache;
+import com.usoftchina.saas.account.dto.AccountDTO;
+import com.usoftchina.saas.account.dto.UrlResourceDTO;
 import com.usoftchina.saas.auth.common.jwt.JwtHelper;
 import com.usoftchina.saas.auth.common.jwt.JwtInfo;
 import com.usoftchina.saas.exception.BizException;
 import com.usoftchina.saas.exception.ExceptionCode;
+import com.usoftchina.saas.gateway.error.PermissionException;
+import com.usoftchina.saas.gateway.util.AntPathRequestMatcher;
+import com.usoftchina.saas.utils.CollectionUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.cloud.gateway.filter.GatewayFilterChain;
@@ -15,6 +22,8 @@ import org.springframework.web.server.ServerWebExchange;
 import reactor.core.publisher.Mono;
 
 import java.util.List;
+import java.util.Set;
+import java.util.stream.Stream;
 
 /**
  * 全局过滤器鉴权
@@ -33,12 +42,55 @@ public class AuthFilter implements GlobalFilter, Ordered {
     public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
         if (!isIgnore(exchange.getRequest())) {
             // 鉴别身份信息
-//            JwtInfo jwt = getJwtInfoFromHeader(exchange.getRequest());
+            JwtInfo jwt = getJwtInfoFromHeader(exchange.getRequest());
+            AccountDTO accountDTO = AccountCache.of(jwt.getUserId()).getAccount();
+            if (null == accountDTO) {
+                throw new BizException(ExceptionCode.USER_NOT_EXIST);
+            }
             // 鉴别角色权限
+            checkPermission(exchange.getRequest(), jwt, accountDTO);
         }
         return chain.filter(exchange);
     }
 
+    /**
+     * 鉴别角色权限
+     *
+     * @param request
+     * @param jwt
+     * @param accountDTO
+     */
+    private void checkPermission(ServerHttpRequest request, JwtInfo jwt, AccountDTO accountDTO) {
+        if (!accountDTO.isAdmin(jwt.getCompanyId())) {
+            // 非管理账户,需要鉴权
+            List<UrlResourceDTO> resources = ResourceCache.of(jwt.getAppId()).getUrlResources();
+            if (!CollectionUtils.isEmpty(resources)) {
+                // 本次请求相关的资源
+                Stream<UrlResourceDTO> permissions = resources.parallelStream().filter(resource -> {
+                    AntPathRequestMatcher matcher = new AntPathRequestMatcher(resource.getUrl(), resource.getMethod());
+                    return matcher.matches(request);
+                });
+                if (permissions.count() > 0) {
+                    Set<Long> resourceIds = accountDTO.getResources(jwt.getAppId(), jwt.getCompanyId());
+                    boolean permitted = false;
+                    if (null != resourceIds) {
+                        // 权限匹配
+                        permitted = permissions.anyMatch(resource -> resourceIds.contains(resource.getId()));
+                    }
+                    if (!permitted) {
+                        throw new PermissionException(permissions.findFirst().get());
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * 是否设置为忽略鉴权的请求
+     *
+     * @param request
+     * @return
+     */
     private boolean isIgnore(ServerHttpRequest request) {
         String path = request.getPath().value();
         return authConfig.getIgnores().stream().anyMatch(ignore -> ignore.equals(path));

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

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

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

@@ -0,0 +1,20 @@
+package com.usoftchina.saas.gateway.error;
+
+import com.usoftchina.saas.account.dto.UrlResourceDTO;
+import com.usoftchina.saas.exception.BaseUncheckedException;
+import com.usoftchina.saas.exception.ExceptionCode;
+
+/**
+ * @author yingp
+ * @date 2018/11/2
+ */
+public class PermissionException extends BaseUncheckedException {
+    public PermissionException(int code, String message) {
+        super(code, message);
+    }
+
+    public PermissionException(UrlResourceDTO resource) {
+        super(ExceptionCode.MISSING_PERMISSIONS.getCode(),
+                String.format("没有 %s 权限", resource.getName()));
+    }
+}

+ 177 - 0
base-servers/gateway-server/src/main/java/com/usoftchina/saas/gateway/util/AntPathRequestMatcher.java

@@ -0,0 +1,177 @@
+package com.usoftchina.saas.gateway.util;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.util.AntPathMatcher;
+import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
+
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * @author yingp
+ * @date 2018/11/1
+ */
+public final class AntPathRequestMatcher {
+    private static final Log logger = LogFactory.getLog(AntPathRequestMatcher.class);
+    private static final String MATCH_ALL = "/**";
+    private final Matcher matcher;
+    private final String pattern;
+    private final HttpMethod httpMethod;
+    private final boolean caseSensitive;
+
+    public AntPathRequestMatcher(String pattern) {
+        this(pattern, null);
+    }
+
+    public AntPathRequestMatcher(String pattern, String httpMethod) {
+        this(pattern, httpMethod, true);
+    }
+
+    public AntPathRequestMatcher(String pattern, String httpMethod, boolean caseSensitive) {
+        Assert.hasText(pattern, "Pattern cannot be null or empty");
+        this.caseSensitive = caseSensitive;
+
+        if (pattern.equals(MATCH_ALL) || pattern.equals("**")) {
+            pattern = MATCH_ALL;
+            this.matcher = null;
+        }
+        else {
+            // If the pattern ends with {@code /**} and has no other wildcards or path
+            // variables, then optimize to a sub-path match
+            if (pattern.endsWith(MATCH_ALL)
+                    && (pattern.indexOf('?') == -1 && pattern.indexOf('{') == -1
+                    && pattern.indexOf('}') == -1)
+                    && pattern.indexOf("*") == pattern.length() - 2) {
+                this.matcher = new SubpathMatcher(pattern.substring(0, pattern.length() - 3), caseSensitive);
+            }
+            else {
+                this.matcher = new SpringAntMatcher(pattern, caseSensitive);
+            }
+        }
+
+        this.pattern = pattern;
+        this.httpMethod = StringUtils.hasText(httpMethod) ? HttpMethod.resolve(httpMethod)
+                : null;
+    }
+
+    public boolean matches(ServerHttpRequest request) {
+        if (this.httpMethod != null && this.httpMethod != request.getMethod()) {
+            if (logger.isDebugEnabled()) {
+                logger.debug("Request '" + request.getMethodValue() + " "
+                        + getRequestPath(request) + "'" + " doesn't match '"
+                        + this.httpMethod + " " + this.pattern);
+            }
+            return false;
+        }
+
+        if (this.pattern.equals(MATCH_ALL)) {
+            if (logger.isDebugEnabled()) {
+                logger.debug("Request '" + getRequestPath(request)
+                        + "' matched by universal pattern '/**'");
+            }
+
+            return true;
+        }
+
+        String url = getRequestPath(request);
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("Checking match of request : '" + url + "'; against '"
+                    + this.pattern + "'");
+        }
+
+        return this.matcher.matches(url);
+    }
+
+    private String getRequestPath(ServerHttpRequest request) {
+        return request.getPath().value();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof AntPathRequestMatcher)) {
+            return false;
+        }
+
+        AntPathRequestMatcher other = (AntPathRequestMatcher) obj;
+        return this.pattern.equals(other.pattern) && this.httpMethod == other.httpMethod
+                && this.caseSensitive == other.caseSensitive;
+    }
+
+    @Override
+    public int hashCode() {
+        int code = 31 ^ this.pattern.hashCode();
+        if (this.httpMethod != null) {
+            code ^= this.httpMethod.hashCode();
+        }
+        return code;
+    }
+
+    private interface Matcher {
+        boolean matches(String path);
+
+        Map<String, String> extractUriTemplateVariables(String path);
+    }
+
+    /**
+     * Optimized matcher for trailing wildcards
+     */
+    private static class SubpathMatcher implements Matcher {
+        private final String subpath;
+        private final int length;
+        private final boolean caseSensitive;
+
+        private SubpathMatcher(String subpath, boolean caseSensitive) {
+            assert!subpath.contains("*");
+            this.subpath = caseSensitive ? subpath : subpath.toLowerCase();
+            this.length = subpath.length();
+            this.caseSensitive = caseSensitive;
+        }
+
+        @Override
+        public boolean matches(String path) {
+            if (!this.caseSensitive) {
+                path = path.toLowerCase();
+            }
+            return path.startsWith(this.subpath)
+                    && (path.length() == this.length || path.charAt(this.length) == '/');
+        }
+
+        @Override
+        public Map<String, String> extractUriTemplateVariables(String path) {
+            return Collections.emptyMap();
+        }
+    }
+
+    private static class SpringAntMatcher implements Matcher {
+        private final AntPathMatcher antMatcher;
+
+        private final String pattern;
+
+        private SpringAntMatcher(String pattern, boolean caseSensitive) {
+            this.pattern = pattern;
+            this.antMatcher = createMatcher(caseSensitive);
+        }
+
+        @Override
+        public boolean matches(String path) {
+            return this.antMatcher.match(this.pattern, path);
+        }
+
+        @Override
+        public Map<String, String> extractUriTemplateVariables(String path) {
+            return this.antMatcher.extractUriTemplateVariables(this.pattern, path);
+        }
+
+        private static AntPathMatcher createMatcher(boolean caseSensitive) {
+            AntPathMatcher matcher = new AntPathMatcher();
+            matcher.setTrimTokens(false);
+            matcher.setCaseSensitive(caseSensitive);
+            return matcher;
+        }
+    }
+}

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

@@ -104,6 +104,9 @@ spring:
         - Path=/api/commons/**
         filters:
         - RewritePath=/api/commons/(?<segment>.*), /$\{segment}
+  redis:
+    host: 192.168.253.12
+    port: 6379
 server:
   port: 8560
   tomcat:

+ 2 - 0
base-servers/ui-server/src/main/java/com/usoftchina/saas/ui/UiApplication.java

@@ -1,5 +1,6 @@
 package com.usoftchina.saas.ui;
 
+import com.usoftchina.saas.auth.client.EnableAuthClient;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.cache.annotation.EnableCaching;
@@ -14,6 +15,7 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
 @EnableEurekaClient
 @EnableTransactionManagement
 @EnableCaching
+@EnableAuthClient
 public class UiApplication {
     public static void main(String[] args) {
         SpringApplication.run(UiApplication.class, args);

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

@@ -40,10 +40,6 @@ public class CoViewController {
             coViewService.cacheEvict(name);
             viewService.cacheEvict(name);
         }
-        // TODO 开发默认设置,及时去掉
-        if (null == BaseContextHolder.getCompanyId()) {
-            BaseContextHolder.setCompanyId(0);
-        }
         Object config = coViewService.getDeepConfig(name);
         if (null == config && null != useDefault && useDefault) {
             // 企业配置不存在则取标准配置

+ 4 - 2
framework/core/pom.xml

@@ -47,12 +47,14 @@
         <dependency>
             <groupId>com.fasterxml.jackson.core</groupId>
             <artifactId>jackson-core</artifactId>
-            <scope>provided</scope>
         </dependency>
         <dependency>
             <groupId>com.fasterxml.jackson.core</groupId>
             <artifactId>jackson-databind</artifactId>
-            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
         </dependency>
     </dependencies>
 

+ 8 - 11
framework/core/src/main/java/com/usoftchina/saas/base/Result.java

@@ -1,13 +1,10 @@
 package com.usoftchina.saas.base;
 
-import com.fasterxml.jackson.core.type.TypeReference;
 import com.usoftchina.saas.exception.BaseException;
 import com.usoftchina.saas.exception.BaseExceptionCode;
 import com.usoftchina.saas.utils.JsonUtils;
-import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;
 
 import java.io.Serializable;
-import java.lang.reflect.Type;
 
 /**
  * 结果
@@ -89,6 +86,13 @@ public class Result<T> implements Serializable {
         return result;
     }
 
+    public static Result error(Result from) {
+        Result result = error();
+        result.setCode(from.getCode());
+        result.setMessage(from.getMessage());
+        return result;
+    }
+
     public static Result error(int code, String message) {
         Result result = error();
         result.setCode(code);
@@ -135,13 +139,6 @@ public class Result<T> implements Serializable {
      * @return
      */
     public static <T> Result<T> fromJsonString(String jsonString, Class<T> targetCls) {
-        Type[] types = new Type[]{targetCls};
-        final ParameterizedTypeImpl type = ParameterizedTypeImpl.make(Result.class, types, Result.class.getDeclaringClass());
-        return JsonUtils.fromJsonString(jsonString, new TypeReference<Result<T>>() {
-            @Override
-            public Type getType() {
-                return type;
-            }
-        });
+        return JsonUtils.fromJsonString(jsonString, Result.class, targetCls);
     }
 }

+ 1 - 0
framework/core/src/main/java/com/usoftchina/saas/exception/ExceptionCode.java

@@ -35,6 +35,7 @@ public enum ExceptionCode implements BaseExceptionCode {
     USER_NOT_EXIST(53005, "用户不存在"),
     USER_NOT_ENABLE(53006, "用户禁止使用"),
     ROLE_NOT_EXIST(53020, "角色不存在"),
+    MISSING_PERMISSIONS(53030, "权限缺失"),
 
     // 文件相关
     FOLDER_NULL(55000, "文件夹为空"),

+ 112 - 5
framework/core/src/main/java/com/usoftchina/saas/utils/CollectionUtils.java

@@ -1,16 +1,18 @@
 package com.usoftchina.saas.utils;
 
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import com.google.common.collect.HashBasedTable;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import com.google.common.collect.Table;
+
+import java.util.*;
 import java.util.function.Function;
 
 /**
  * @author yingp
  * @date 2018/10/10
  */
-public abstract class CollectionUtils extends org.springframework.util.CollectionUtils{
+public abstract class CollectionUtils extends org.springframework.util.CollectionUtils {
     /**
      * 按指定方法,将list分组返回map
      *
@@ -36,4 +38,109 @@ public abstract class CollectionUtils extends org.springframework.util.Collectio
         }
         return map;
     }
+
+    /**
+     * 按两个指定方法,将list分组返回复合键map
+     *
+     * @param sources
+     * @param rKeyGetter
+     * @param cKeyGetter
+     * @param <R>
+     * @param <C>
+     * @param <V>
+     * @return
+     */
+    public static <R, C, V> Table<R, C, List<V>> groupBy(List<V> sources, Function<V, R> rKeyGetter, Function<V, C> cKeyGetter) {
+        Table<R, C, List<V>> table = HashBasedTable.create();
+        if (!isEmpty(sources)) {
+            sources.forEach(source -> {
+                R r = rKeyGetter.apply(source);
+                C c = cKeyGetter.apply(source);
+                if (table.contains(r, c)) {
+                    table.get(r, c).add(source);
+                } else {
+                    List<V> childList = new ArrayList<>();
+                    childList.add(source);
+                    table.put(r, c, childList);
+                }
+            });
+        }
+        return table;
+    }
+
+    /**
+     * 按三个指定方法,将list分组返回复合键map
+     *
+     * @param sources
+     * @param rKeyGetter
+     * @param cKeyGetter
+     * @param valueGetter
+     * @param <R>
+     * @param <C>
+     * @param <V>
+     * @param <S>
+     * @return
+     */
+    public static <R, C, V, S> Map<R, Map<C, List<V>>> groupBy(List<S> sources, Function<S, R> rKeyGetter, Function<S, C> cKeyGetter, Function<S, V> valueGetter) {
+        Map<R, Map<C, List<V>>> table = new HashMap<>(1);
+        if (!isEmpty(sources)) {
+            sources.forEach(source -> {
+                R r = rKeyGetter.apply(source);
+                C c = cKeyGetter.apply(source);
+                V v = valueGetter.apply(source);
+
+                if (table.containsKey(r)) {
+                    Map<C, List<V>> rows = table.get(r);
+                    if (rows.containsKey(c)) {
+                        rows.get(c).add(v);
+                    } else {
+                        rows.put(c, Lists.newArrayList(v));
+                    }
+                } else {
+                    Map<C, List<V>> rows = new HashMap<>(1);
+                    rows.put(c, Lists.newArrayList(v));
+                    table.put(r, rows);
+                }
+            });
+        }
+        return table;
+    }
+
+    /**
+     * 按三个指定方法,将list分组返回复合键map
+     *
+     * @param sources
+     * @param rKeyGetter
+     * @param cKeyGetter
+     * @param valueGetter
+     * @param <R>
+     * @param <C>
+     * @param <V>
+     * @param <S>
+     * @return
+     */
+    public static <R, C, V, S> Map<R, Map<C, Set<V>>> distinctBy(List<S> sources, Function<S, R> rKeyGetter, Function<S, C> cKeyGetter, Function<S, V> valueGetter) {
+        Map<R, Map<C, Set<V>>> table = new HashMap<>(1);
+        if (!isEmpty(sources)) {
+            sources.forEach(source -> {
+                R r = rKeyGetter.apply(source);
+                C c = cKeyGetter.apply(source);
+                V v = valueGetter.apply(source);
+
+                if (table.containsKey(r)) {
+                    Map<C, Set<V>> rows = table.get(r);
+                    if (rows.containsKey(c)) {
+                        rows.get(c).add(v);
+                    } else {
+                        rows.put(c, Sets.newHashSet(v));
+                    }
+                } else {
+                    Map<C, Set<V>> rows = new HashMap<>(1);
+                    rows.put(c, Sets.newHashSet(v));
+                    table.put(r, rows);
+                }
+            });
+        }
+        return table;
+    }
 }

+ 44 - 1
framework/core/src/main/java/com/usoftchina/saas/utils/JsonUtils.java

@@ -1,8 +1,10 @@
 package com.usoftchina.saas.utils;
 
 import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.JavaType;
 import com.fasterxml.jackson.databind.ObjectMapper;
-import org.springframework.util.StringUtils;
+
+import java.util.List;
 
 /**
  * @author yingp
@@ -38,6 +40,47 @@ public class JsonUtils {
         try {
             return mapper.readValue(json, valueTypeRef);
         } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    public static JavaType getJavaType(Class<?> targetClass, Class<?>... elementClasses) {
+        return mapper.getTypeFactory().constructParametricType(targetClass, elementClasses);
+    }
+
+    public static <T> T fromJsonString(String json, Class<?> targetClass, Class<?>... elementClasses) {
+        if (StringUtils.isEmpty(json)) {
+            return null;
+        }
+        try {
+            return mapper.readValue(json, getJavaType(targetClass, elementClasses));
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    public static <T> T fromJsonString(String json, JavaType type) {
+        if (StringUtils.isEmpty(json)) {
+            return null;
+        }
+        try {
+            return mapper.readValue(json, type);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    public static <T> List<T> fromJsonArray(String json, Class<T> targetCls) {
+        if (StringUtils.isEmpty(json)) {
+            return null;
+        }
+        try {
+            return mapper.readValue(json, getJavaType(List.class, targetCls));
+        } catch (Exception e) {
+            e.printStackTrace();
             return null;
         }
     }

+ 0 - 1
frontend/saas-web/app/Application.scss

@@ -63,7 +63,6 @@ html, body {
   
   .x-fa {
     display: inline-block;
-    font: normal normal normal 14px/1 $font-family;
   }
   
   .alignRight {

+ 0 - 26
frontend/saas-web/app/model/Account.js

@@ -1,26 +0,0 @@
-Ext.define('saas.model.Account', {
-    extend: 'saas.model.Base',
-
-    fields: [
-        { name: 'username', type: 'string' },
-        { name: 'realname', type: 'string' },
-        { name: 'email', type: 'string' },
-        { name: 'mobile', type: 'string' }
-    ],
-
-    proxy: {
-        api: {
-            prefix: 'Server.people'
-        }
-    },
-
-    statics: {
-        load: function(id, options, session) {
-            var record = Ext.create('saas.model.Account');
-            record.setSession(session);
-            record.load(
-                Ext.apply({ params: { id: id } }, options)
-            );
-        }
-    }
-});

+ 73 - 21
frontend/saas-web/app/model/Session.js

@@ -3,43 +3,95 @@ Ext.define('saas.model.Session', {
 
     fields: [
         { name: 'token', type: 'string' },
-        { name: 'expires', type: 'number' },
+        { name: 'expire', type: 'number' },
         { name: 'timestamp', type: 'number' },
-        { name: 'account', reference: 'Account' }
+        { name: 'span', type: 'number' },
+        { name: 'account' }
     ],
 
     statics: {
         login: function(username, password) {
             return new Ext.Promise(function (resolve, reject) {
-                Server.auth.login({
-                    username: username,
-                    password: password
-                }, function(result, response, success) {
-                    if (!success) {
-                        return reject(result.message);
+				Ext.Ajax.request({
+                    url: '/api/auth/authorize',
+                    params: {
+                        username: username,
+                        password: password
+                    },
+                    method: 'POST',
+                    success: function (response) {
+                        var res = Ext.decode(response.responseText);
+                        if (res.success) {
+                            var session = saas.model.Session.loadData(res.data.token);
+                            session.set('account', res.data.account);
+                            // 服务端与本地存在时间差
+                            session.set('span', session.get('timestamp') - new Date().getTime());
+                            if (!session.isValid()) {
+                                reject({
+                                    message: '登录失败,无效身份令牌'
+                                });
+                            } else {
+                                resolve(session);
+                            }
+                        } else {
+                            Ext.log.error('request failure with code: ', res.code, ', message: ', res.message);
+                            reject(res);
+                        }
+                    },
+                    failure: function (response) {
+                        Ext.log.error('server-side failure with status code: ', response.status);
+                        reject(response);
                     }
-
-                    var session = saas.model.Session.loadData(result);
-                    if (!session.isValid()) {
-                        return reject({ errors: {
-                            username: 'Login failed: invalid session'
-                        }});
-                    }
-
-                    resolve(session);
                 });
             });
+        },
+        switchCompany: function(oldSession, companyId) {
+            return new Ext.Promise(function (resolve, reject) {
+				Ext.Ajax.request({
+                    url: '/api/auth/switch/company',
+                    params: {
+                        companyId: companyId
+                    },
+                    method: 'GET',
+                    headers: {
+                        'Authorization': oldSession.get('token')
+                    },
+                    success: function (response) {
+                        var res = Ext.decode(response.responseText);
+                        if (res.success) {
+                            var newSession = saas.model.Session.loadData(res.data);
+                            newSession.set('account', oldSession.get('account'));
+                            // 服务端与本地存在时间差
+                            newSession.set('span', newSession.get('timestamp') - new Date().getTime());
+                            if (!newSession.isValid()) {
+                                reject({
+                                    message: '切换失败,无效身份令牌'
+                                });
+                            } else {
+                                resolve(newSession);
+                            }
+                        } else {
+                            Ext.log.error('request failure with code: ', res.code, ', message: ', res.message);
+                            reject(res);
+                        }
+                    },
+                    failure: function (response) {
+                        Ext.log.error('server-side failure with status code: ', response.status);
+                        reject(response);
+                    }
+                });
+            });            
         }
     },
 
     isValid: function() {
-        return !Ext.isEmpty(this.get('token'))
-            && this.get('timestamp') + this.get('expires') * 1000 > new Date().getTime();
+        return !Ext.isEmpty(this.get('token')) && this.get('token').length > 128
+            && this.get('timestamp') + this.get('expire') * 1000 > new Date().getTime() + this.get('span');
     },
-
+    
     logout: function() {
         return new Ext.Promise(function (resolve, reject) {
-            Server.auth.logout({}, resolve);
+            resolve({});
         });
     }
 });

+ 21 - 0
frontend/saas-web/app/model/stock/Makematerial.js

@@ -0,0 +1,21 @@
+Ext.define('saas.model.stock.Makematerial', {
+    extend: 'saas.model.Base',
+
+    fields: [
+
+        { name: 'id', type: 'int' },
+        { name: 'mm_maid', type: 'int' },
+        { name: 'mm_detno', type: 'int' },
+        { name: 'mm_prodid', type: 'int' },
+        { name: 'mm_prodcode', type: 'string' },
+        { name: 'mm_whid', type: 'int' },
+        { name: 'mm_whcode', type: 'string' },
+        { name: 'mm_whname', type: 'string' },
+        { name: 'mm_price', type: 'float' },
+        { name: 'mm_oneuseqty', type: 'float' },
+        { name: 'mm_qty', type: 'float' },
+        { name: 'mm_amount', type: 'float' },
+        { name: 'mm_repprodcode', type: 'string' },
+        { name: 'mm_remark', type: 'string' }
+        ]
+});

+ 9 - 0
frontend/saas-web/app/store/Company.js

@@ -0,0 +1,9 @@
+Ext.define('saas.store.Company', {
+    extend: 'Ext.data.Store',
+
+    fields: [
+        { name: 'id', type: 'int' },
+        { name: 'name', type: 'string' },
+        { name: 'logoUrl', type: 'string' }
+    ]
+});

+ 37 - 0
frontend/saas-web/app/view/auth/CompanyPicker.js

@@ -0,0 +1,37 @@
+Ext.define('saas.view.auth.CompanyPicker', {
+    extend: 'Ext.window.Window',
+    xtype: 'companypicker',
+    controller: 'login',
+
+    title: '选择登录公司',
+    width: 500,
+    scrollable: true,
+    resizable: false,
+    autoShow: true,
+    bodyPadding: 10,
+    modal: true,
+    closable: false,
+
+    items: [{
+        xtype: 'dataview',
+        id: 'companies',
+        bind: {
+            store: '{companies}'
+        },
+        tpl: new Ext.XTemplate(
+            '<tpl for=".">',
+                '<div class="thumb">',
+                    '<img src="{logoUrl}" />',
+                    '<strong>{name}</strong>',
+                '</div>',
+            '</tpl>'
+        ),
+        itemSelector: 'div.thumb',
+        overItemCls: 'thumb-hover',
+        listeners: {
+            select: 'selectCompany'
+        }
+    }],
+
+    renderTo: Ext.getBody()
+});

+ 38 - 0
frontend/saas-web/app/view/auth/CompanyPicker.scss

@@ -0,0 +1,38 @@
+#companies {
+    background-color: #fff;
+    text-shadow: #fff 0 1px 0;
+    position: relative;
+    display: block;
+    height: auto;
+
+    div.thumb {
+        @include box-shadow(rgba(0,0,0,0.2) 0 0 8px);
+        float: left;
+        padding: 15px;
+        margin: 5px;
+        text-align: center;
+        line-height: 14px;
+        height: 120px;
+        width: 140px;
+        color: #333;
+        overflow: hidden;
+        border-top: 1px solid transparent;
+        cursor: pointer;
+
+        img {
+            margin-bottom: 10px;
+        }
+
+        strong {
+            display: block;
+        }
+    }
+
+    div.thumb-hover {
+        background-color: $base-over-color;
+    }
+
+    .x-item-selected {
+        background-color: #d3e1f1 !important;
+    }
+}

+ 54 - 26
frontend/saas-web/app/view/auth/LoginController.js

@@ -7,38 +7,66 @@ Ext.define('saas.view.auth.LoginController', {
     },
 
     onLoginButton: function() {
-        // var me = this,
-        //     form = me.lookup('authdialog'),
-        //     values = form.getValues();
-
-        // me.getView().setMasked(true);
-
-        // saas.model.Session.login(values.username, values.password)
-        //     .then(function(session) {
-        //         me.fireEvent('login', session);
-        //     })
-        //     .catch(function(error) {
-        //         showToast(error);
-        //     })
-        //     .then(function(session) {
-        //         me.getView().setMasked(false);
-        //     });
-        this.fireEvent('login', new saas.model.Session({
-            token: 'XXXXXX',
-            expires: 180000,
-            timestamp: new Date().getTime(),
-            account: new saas.model.Account({
-                username: 'XXXXXX',
-                realname: '张三',
-                email: 'zhangs@gmail.com',
-                mobile: '15812345678'
+        var me = this, view = me.getView(),
+            form = me.lookup('authdialog'),
+            values = form.getValues();
+
+        view.mask('请稍等...');
+
+        saas.model.Session.login(values.username, values.password)
+            .then(function(session) {
+                view.isMasked() && view.unmask();
+                me.getViewModel().set('session', session);
+				var cos = session.get("account").companies;
+				if (cos && cos.length) {
+                    if (cos.length == 1) {
+                        session.get('account').companyId = cos[0].id;
+                        me.fireEvent('login', session);
+                    } else {
+                        Ext.create({
+                            xtype: 'companypicker',
+                            viewModel: {
+                                stores: {
+                                    companies: Ext.create('saas.store.Company', {
+                                        data: cos
+                                    })
+                                }
+                            }
+                        });
+                    }
+				} else {
+                    me.fireEvent('login', session);
+                }
             })
-        }));
+            .catch(function(error) {
+                view.isMasked() && view.unmask();
+                showToast(error.message);
+            });
     },
 
     onWeixinLogin : function() {
     },
 
     onNewAccount:  function() {
+    },
+
+    selectCompany: function(view, record) {
+        var me = this, view = me.getView(), oldSession = me.getViewModel().get('session'),
+            companyId = record.get('id');
+        
+        view.mask('请稍等...');
+
+        saas.model.Session.switchCompany(oldSession, companyId)
+            .then(function(newSession) {
+                newSession.get('account').companyId = companyId;
+				me.fireEvent('login', newSession);
+            })
+            .catch(function(error) {
+                showToast(error.message);
+            })
+            .then(function() {
+                view.isMasked() && view.unmask();
+                view.ownerCt.destroy();
+            });        
     }
 });

+ 7 - 10
frontend/saas-web/app/view/document/bom/FormPanel.js

@@ -8,7 +8,7 @@ Ext.define('saas.view.document.bom.FormPanel', {
     caller:'Bom',
 
     //字段属性
-    _title:'客户资料',
+    _title:'BOM资料',
     _idField: 'id',
     _codeField: 'bo_mothercode',
     _statusField: 'bo_status',
@@ -33,17 +33,10 @@ Ext.define('saas.view.document.bom.FormPanel', {
     }, {
         xtype: 'button',
         text: '保存',
-        handler: 'onSave',
-        // bind: {
-        //     // disabled: '{!base.valid || '+ me._statusCodeField + ' == "AUDITED" || ' + me._statusCodeField + ' == "OPEN"}'
-        //     disabled: '{!base.valid || '+ me._statusCodeField + ' == "' + me.auditTexts.auditCode + '"}'
-        // }
+        handler: 'onSave'
     }, {
         xtype: 'button',
         text: '删除',
-        // bind: {
-        //     hidden: '{deleteHidden || ' + me._statusCodeField + '=="' + me.auditTexts.auditCode + '"}'
-        // },
         handler: 'delete'
     }, {
         xtype: 'button',
@@ -107,6 +100,7 @@ Ext.define('saas.view.document.bom.FormPanel', {
                 xtype : "numbercolumn"
             },
             {
+                allowBlank:false,
                 text : "子件编号", 
                 editor : {
                     xtype : "textfield"
@@ -114,6 +108,7 @@ Ext.define('saas.view.document.bom.FormPanel', {
                 dataIndex : "bd_soncode", 
             },
             {
+                allowBlank:false,
                 text : "单位", 
                 editor : {
                     xtype : "textfield"
@@ -121,6 +116,7 @@ Ext.define('saas.view.document.bom.FormPanel', {
                 dataIndex : "bd_unit", 
             },
             {
+                allowBlank:false,
                 text : "单位用量", 
                 editor : {
                     xtype : "textfield"
@@ -128,6 +124,7 @@ Ext.define('saas.view.document.bom.FormPanel', {
                 dataIndex : "bd_baseqty", 
             },
             {
+                allowBlank:false,
                 text : "替代料", 
                 editor : {
                     xtype : "textfield"
@@ -141,7 +138,7 @@ Ext.define('saas.view.document.bom.FormPanel', {
         auditText: '已关闭',
         unAuditCode: 'OPEN',
         unAuditText: '已开启',
-        auditBtnText: '关闭',
+        auditBtnText: '禁用',
         unAuditBtnText: '启用',
     }
 });

+ 85 - 41
frontend/saas-web/app/view/document/customer/FormPanel.js

@@ -33,25 +33,13 @@ Ext.define('saas.view.document.customer.FormPanel', {
         name: 'cu_name',
         fieldLabel: '客户名称',
         allowBlank: false,
-        columnWidth: 0.25
+        columnWidth: 0.5
     },{
         xtype: 'textfield',
         name: 'cu_code',
         fieldLabel: '客户编号',
         allowBlank: true,
         columnWidth: 0.25
-    },{
-        xtype: 'textfield',
-        name: 'cu_status',
-        fieldLabel: '状态',
-        allowBlank: true,
-        columnWidth: 0.25
-    },{
-        xtype: 'hidden',
-        name: 'cu_statuscode',
-        fieldLabel: '状态码',
-        allowBlank: true,
-        columnWidth: 0.25
     },{
         editable:false,
         xtype : "remotecombo", 
@@ -79,11 +67,17 @@ Ext.define('saas.view.document.customer.FormPanel', {
             this.dialog.show();
         }
     },{
-        xtype : "datefield", 
-        name : "createTime", 
-        fieldLabel : "创建时间", 
-        allowBlank : true, 
-        columnWidth : 0.25
+        xtype: 'hidden',
+        name: 'cu_status',
+        fieldLabel: '状态',
+        allowBlank: true,
+        columnWidth: 0
+    },{
+        xtype: 'hidden',
+        name: 'cu_statuscode',
+        fieldLabel: '状态码',
+        allowBlank: true,
+        columnWidth: 0.25
     },{
         xtype : "datefield", 
         name : "cu_begindate", 
@@ -104,39 +98,90 @@ Ext.define('saas.view.document.customer.FormPanel', {
         fieldLabel : "期初预收", 
         allowBlank : true, 
         columnWidth : 0.25    
+    },{
+        xtype : "numberfield", 
+        name : "cu_taxrate", 
+        fieldLabel : "税率", 
+        allowBlank : false, 
+        columnWidth : 0.25   
     },{ 
         xtype : "numberfield", 
         hideTrigger:true,
         name : "cu_promisedays", 
         fieldLabel : "承付天数", 
         allowBlank : true, 
-        columnWidth : 0.25      
+        columnWidth : 0.25        
     },{
         xtype : "numberfield", 
-        name : "cu_taxrate", 
-        fieldLabel : "税率", 
-        allowBlank : false, 
-        columnWidth : 0.25   
+        hideTrigger:true,
+        name : "cu_credit", 
+        fieldLabel : "额度", 
+        allowBlank : true, 
+        columnWidth : 0.25
     },{
-        // xtype : "numberfield", 
-        // name : "cu_ta", 
-        // fieldLabel : "应收款余额", 
-        // allowBlank : true, 
-        // readOnly:true,
-        // editable:false,
-        // columnWidth : 0.25     
+        xtype : "textfield", 
+        name : "cu_sellername", 
+        fieldLabel : "业务员", 
+        ignore:true,
+        editable:false,
+        readOnly:true,
+        allowBlank : true, 
+        columnWidth : 0.25
+    },{
+        xtype : "textfield", 
+        name : "cu_1", 
+        fieldLabel : "纳税人识别号", 
+        ignore:true,
+        editable:false,
+        readOnly:true,
+        allowBlank : true, 
+        columnWidth : 0.25
+    },{
+        xtype : "textfield", 
+        name : "cu_2", 
+        fieldLabel : "开户银行", 
+        ignore:true,
+        editable:false,
+        readOnly:true,
+        allowBlank : true, 
+        columnWidth : 0.25
+    },{
+        xtype : "textfield", 
+        name : "cu_3", 
+        fieldLabel : "银行账户", 
+        ignore:true,
+        editable:false,
+        readOnly:true,
+        allowBlank : true, 
+        columnWidth : 0.25
     },{
         xtype:'textfield',
         name : "cu_uu", 
         fieldLabel : "客户UU", 
         allowBlank : true, 
         columnWidth : 0.25
+    },{
+        xtype:'textfield',
+        name : "cu_leftamount", 
+        fieldLabel : "应收款余额", 
+        allowBlank : true, 
+        readOnly:true,
+        ignore:true,
+        columnWidth : 0.25
+    },{
+        xtype : "datefield", 
+        name : "createTime", 
+        fieldLabel : "创建时间", 
+        allowBlank : true, 
+        hidden:true,
+        columnWidth : 0
     },{  
         xtype : "datefield", 
         name : "updateTime", 
         fieldLabel : "更新时间", 
         allowBlank : true, 
-        columnWidth : 0.25
+        hidden:true,
+        columnWidth : 0
     }, {
         height: 169,
         xtype : "detailGridField", 
@@ -158,6 +203,7 @@ Ext.define('saas.view.document.customer.FormPanel', {
                 xtype : "numbercolumn"
             },
             {
+                allowBlank:false,
                 text : "联系人", 
                 editor : {
                     xtype : "textfield"
@@ -167,16 +213,18 @@ Ext.define('saas.view.document.customer.FormPanel', {
                 items : null
             },
             {
+                allowBlank:false,
                 text : "电话", 
                 editor : {
                     hideTrigger:true,
-                    xtype : "numberfield"
+                    xtype : "textfield"
                 },
                 dataIndex : "cc_tel", 
                 xtype : "", 
                 items : null
             },
             {
+                allowBlank:false,
                 text : "微信/QQ", 
                 editor : {
                     xtype : "textfield"
@@ -262,6 +310,7 @@ Ext.define('saas.view.document.customer.FormPanel', {
                 xtype : "numbercolumn"
             },
             {
+                allowBlank:false,
                 text : "送货地址", 
                 editor : {
                     xtype : "textfield"
@@ -271,6 +320,7 @@ Ext.define('saas.view.document.customer.FormPanel', {
                 items : null
             },  
             {
+                allowBlank:false,
                 text : "联系人", 
                 editor : {
                     xtype : "textfield"
@@ -280,9 +330,10 @@ Ext.define('saas.view.document.customer.FormPanel', {
                 items : null   
             },
             {
+                allowBlank:false,
                 text : "联系电话", 
                 editor : {
-                    xtype : "numberfield",
+                    xtype : "textfield",
                     hideTrigger:true,
                 },
                 dataIndex : "ca_phone", 
@@ -348,16 +399,9 @@ Ext.define('saas.view.document.customer.FormPanel', {
         xtype: 'button',
         text: '保存',
         handler: 'onSave',
-        // bind: {
-        //     // disabled: '{!base.valid || '+ me._statusCodeField + ' == "AUDITED" || ' + me._statusCodeField + ' == "OPEN"}'
-        //     disabled: '{!base.valid || '+ me._statusCodeField + ' == "' + me.auditTexts.auditCode + '"}'
-        // }
     }, {
         xtype: 'button',
         text: '删除',
-        // bind: {
-        //     hidden: '{deleteHidden || ' + me._statusCodeField + '=="' + me.auditTexts.auditCode + '"}'
-        // },
         handler: 'delete'
     }, {
         xtype: 'button',
@@ -371,7 +415,7 @@ Ext.define('saas.view.document.customer.FormPanel', {
         auditText: '已关闭',
         unAuditCode: 'OPEN',
         unAuditText: '已开启',
-        auditBtnText: '关闭',
+        auditBtnText: '禁用',
         unAuditBtnText: '启用',
     }
 });

+ 4 - 2
frontend/saas-web/app/view/document/kind/Kind.js

@@ -37,7 +37,7 @@ Ext.define('saas.view.document.kind.Kind', {
         }, {
             text: '收支',
             value: 'inoutkind',
-            typeText:'收支类'
+            typeText:'收支类'
         }],
         listeners: {
             toggle: 'onKindToggle'
@@ -130,11 +130,13 @@ Ext.define('saas.view.document.kind.Kind', {
                 dataIndex: 'bk_bankname',
                 flex: 1
             },{
+                hidden:true,
                 xtype:'datecolumn',
                 text: '日期',
                 dataIndex: 'bk_date',
                 flex: 1
             },{
+                hidden:true,
                 text: '账户类型',
                 dataIndex: 'bk_type',
                 flex: 1
@@ -153,7 +155,7 @@ Ext.define('saas.view.document.kind.Kind', {
         },
         inoutkind:{
             columns: [{
-                text: '收支类',
+                text: '收支类',
                 dataIndex: 'ft_name',
                 flex: 1
             }],

+ 30 - 7
frontend/saas-web/app/view/document/product/FormPanel.js

@@ -178,12 +178,35 @@ Ext.define('saas.view.document.product.FormPanel', {
         columnWidth : 0.25
     }],
 
+    defaultButtons:[{
+        cls: 'x-formpanel-btn-orange',
+        xtype: 'button',
+        text: '新增',
+        bind: {
+            hidden: '{!id}'
+        },
+        handler: 'add'
+    }, {
+        xtype: 'button',
+        text: '保存',
+        handler: 'onSave',
+    }, {
+        xtype: 'button',
+        text: '删除',
+        handler: 'delete'
+    }, {
+        xtype: 'button',
+        bind: {
+            text: '{auditBtnText}'
+        },
+        handler: "auditBtnClick",
+    }],
     auditTexts: {
-        auditCode: 'OPEN',
-        auditText: '已开启',
-        unAuditCode: 'CLOSE',
-        unAuditText: '已关闭',
-        auditBtnText: '启用',
-        unAuditBtnText: '关闭',
-    },
+        auditCode: 'CLOSE',
+        auditText: '已关闭',
+        unAuditCode: 'OPEN',
+        unAuditText: '已开启',
+        auditBtnText: '用',
+        unAuditBtnText: '启用',
+    }
 });

+ 2 - 1
frontend/saas-web/app/view/document/vendor/FormModel.js

@@ -3,7 +3,8 @@ Ext.define('saas.view.document.vendor.FormModel', {
     alias: 'viewmodel.document-vendor-formpanel',
 
     data: {
-        id: 0
+        id: 0,
+        ve_begindate:new Date()
     },
 
     formulas:{

+ 80 - 23
frontend/saas-web/app/view/document/vendor/FormPanel.js

@@ -33,7 +33,7 @@ Ext.define('saas.view.document.vendor.FormPanel', {
         name: 've_name',
         fieldLabel: '供应商名称',
         allowBlank: false,
-        columnWidth: 0.25
+        columnWidth: 0.5
     },{
         xtype: 'textfield',
         name: 've_code',
@@ -41,7 +41,7 @@ Ext.define('saas.view.document.vendor.FormPanel', {
         allowBlank: true,
         columnWidth: 0.25
     },{
-        xtype: 'textfield',
+        xtype: 'hidden',
         name: 've_status',
         fieldLabel: '状态',
         allowBlank: true,
@@ -82,10 +82,12 @@ Ext.define('saas.view.document.vendor.FormPanel', {
         name : "createTime", 
         fieldLabel : "创建时间", 
         allowBlank : true, 
-        columnWidth : 0.25
+        hidden:true,
+        columnWidth : 0
     },{
         xtype : "datefield", 
         name : "ve_begindate", 
+        format:'Y-m-d',
         fieldLabel : "期初日期", 
         allowBlank : true, 
         columnWidth : 0.25  
@@ -103,6 +105,13 @@ Ext.define('saas.view.document.vendor.FormPanel', {
         fieldLabel : "期初预收", 
         allowBlank : true, 
         columnWidth : 0.25    
+    },{
+        xtype : "numberfield", 
+        name : "ve_taxrate", 
+        hideTrigger:true,
+        fieldLabel : "税率", 
+        allowBlank : false, 
+        columnWidth : 0.25   
     },{ 
         xtype : "numberfield", 
         hideTrigger:true,
@@ -111,19 +120,32 @@ Ext.define('saas.view.document.vendor.FormPanel', {
         allowBlank : true, 
         columnWidth : 0.25      
     },{
-        xtype : "numberfield", 
-        name : "ve_taxrate", 
-        fieldLabel : "税率", 
-        allowBlank : false, 
-        columnWidth : 0.25   
+        xtype : "textfield", 
+        name : "ve_1", 
+        fieldLabel : "纳税人识别号", 
+        ignore:true,
+        editable:false,
+        readOnly:true,
+        allowBlank : true, 
+        columnWidth : 0.25
     },{
-        // xtype : "numberfield", 
-        // name : "ve_ta", 
-        // fieldLabel : "应收款余额", 
-        // allowBlank : true, 
-        // readOnly:true,
-        // editable:false,
-        // columnWidth : 0.25     
+        xtype : "textfield", 
+        name : "ve_2", 
+        fieldLabel : "开户银行", 
+        ignore:true,
+        editable:false,
+        readOnly:true,
+        allowBlank : true, 
+        columnWidth : 0.25
+    },{
+        xtype : "textfield", 
+        name : "ve_3", 
+        fieldLabel : "银行账户", 
+        ignore:true,
+        editable:false,
+        readOnly:true,
+        allowBlank : true, 
+        columnWidth : 0.25
     },{
         xtype:'textfield',
         name : "ve_uu", 
@@ -135,7 +157,16 @@ Ext.define('saas.view.document.vendor.FormPanel', {
         name : "updateTime", 
         fieldLabel : "更新时间", 
         allowBlank : true, 
-        columnWidth : 0.25
+        hidden:true,
+        columnWidth : 0.25  
+    },{  
+        xtype : "numberfield", 
+        name : "ve_leftamount", 
+        fieldLabel : "应付款金额", 
+        allowBlank : true, 
+        readOnly:true,
+        ignore:true,
+        columnWidth : 0.25  
     }, {
         xtype : "detailGridField", 
         storeModel:'saas.model.document.vendorcontact',
@@ -156,6 +187,7 @@ Ext.define('saas.view.document.vendor.FormPanel', {
                 xtype : "numbercolumn"
             },
             {
+                allowBlank:false,
                 text : "联系人", 
                 editor : {
                     xtype : "textfield"
@@ -164,6 +196,7 @@ Ext.define('saas.view.document.vendor.FormPanel', {
                 xtype : "", 
             },
             {
+                allowBlank:false,
                 text : "电话", 
                 editor : {
                     hideTrigger:true,
@@ -173,6 +206,7 @@ Ext.define('saas.view.document.vendor.FormPanel', {
                 xtype : "", 
             },
             {
+                allowBlank:false,
                 text : "微信/QQ", 
                 editor : {
                     xtype : "textfield"
@@ -237,12 +271,35 @@ Ext.define('saas.view.document.vendor.FormPanel', {
         }
     ],
 
+    defaultButtons:[{
+        cls: 'x-formpanel-btn-orange',
+        xtype: 'button',
+        text: '新增',
+        bind: {
+            hidden: '{!id}'
+        },
+        handler: 'add'
+    }, {
+        xtype: 'button',
+        text: '保存',
+        handler: 'onSave',
+    }, {
+        xtype: 'button',
+        text: '删除',
+        handler: 'delete'
+    }, {
+        xtype: 'button',
+        bind: {
+            text: '{auditBtnText}'
+        },
+        handler: "auditBtnClick",
+    }],
     auditTexts: {
-        auditCode: 'OPEN',
-        auditText: '已开启',
-        unAuditCode: 'CLOSE',
-        unAuditText: '已关闭',
-        auditBtnText: '启用',
-        unAuditBtnText: '关闭',
-    },
+        auditCode: 'CLOSE',
+        auditText: '已关闭',
+        unAuditCode: 'OPEN',
+        unAuditText: '已开启',
+        auditBtnText: '用',
+        unAuditBtnText: '启用',
+    }
 });

+ 0 - 15
frontend/saas-web/app/view/main/List.js

@@ -1,15 +0,0 @@
-/**
- * This view is an example list of people.
- */
-Ext.define('saas.view.main.List', {
-    extend: 'Ext.grid.Panel',
-    xtype: 'mainlist',
-
-    title: 'Personnel',
-
-    columns: [
-        { text: 'Name',  dataIndex: 'name' },
-        { text: 'Email', dataIndex: 'email', flex: 1 },
-        { text: 'Phone', dataIndex: 'phone', flex: 1 }
-    ]
-});

+ 27 - 11
frontend/saas-web/app/view/main/Main.js

@@ -40,39 +40,55 @@ Ext.define('saas.view.main.Main', {
                     id: 'main-navigation-btn',
                     handler: 'onToggleNavigationSize'
                 },
+                {
+                    margin: '0 0 0 8',
+                    xtype: 'tbtext',
+                    bind: {
+                        html: '{company.name}'
+                    }
+                },
                 '->',
                 {
                     iconCls:'x-fa fa-search',
                     ui: 'header',
-                    tooltip: 'See latest search'
+                    tooltip: '搜索'
                 },
                 {
                     iconCls:'x-fa fa-bell',
                     ui: 'header',
-                    tooltip: 'Check your messages'
+                    tooltip: '消息'
                 },
                 {
                     iconCls:'x-fa fa-question',
                     ui: 'header',
-                    tooltip: 'Help / FAQ\'s'
+                    tooltip: '帮助'
                 },
                 {
+                    reference: 'mainprofile',
                     iconCls:'x-fa fa-th-large',
+                    arrowVisible: false,
                     ui: 'header',
-                    tooltip: 'See your profile'
-                },
-                {
-                    xtype: 'tbtext',
-                    text: 'administrator',
-                    cls: 'top-user-name'
+                    tooltip: '账户',
+                    bind: {
+                        text: '{account.realname}'
+                    },
+                    menu: {
+                        items: [{
+                            xtype: 'menuseparator'
+                        }, {
+                            text: '退出',
+                            handler: 'onLogout'
+                        }]
+                    }
                 },
                 {
                     xtype: 'image',
                     cls: 'header-right-profile-image',
                     height: 35,
                     width: 35,
-                    alt:'current user image',
-                    src: 'resources/images/default/user-profile-default.png'
+                    bind: {
+                        src: '{avatarUrl}'
+                    }
                 }
             ]
         },

+ 29 - 0
frontend/saas-web/app/view/main/MainController.js

@@ -7,6 +7,27 @@ Ext.define('saas.view.main.MainController', {
 
     alias: 'controller.main',
 
+    init: function() {
+        this.setCompanyMenu();
+    },
+
+    setCompanyMenu: function() {
+        var me = this, view = me.getView(), viewModel = me.getViewModel(),
+            account = viewModel.get('account'), companies = account && account.companies,
+            companyMenu = view.lookup('mainprofile').getMenu(), items = [];
+        if (companies) {
+            items = companies.map(function(c){
+                return {
+                    text: c.name,
+                    value: c.id,
+                    handler: 'selectCompany',
+                    iconCls: c.id == account.companyId ? 'x-fa fa-check' : ''
+                }
+            });
+        }
+        companyMenu.insert(0, items);
+    },
+
     onToggleNavigationSize: function () {
         var me = this,
         refs = me.getReferences(),
@@ -22,5 +43,13 @@ Ext.define('saas.view.main.MainController', {
         navigationList.el[ope]('nav-collapsed');
 
         navigationList.navCollapsed = navCollapsed;
+    },
+
+    selectCompany: function(item) {
+        this.fireEvent('selectCompany', item.value);
+    },
+
+    onLogout: function() {
+        this.fireEvent('logout');
     }
 });

+ 0 - 1
frontend/saas-web/app/view/main/MainModel.js

@@ -7,6 +7,5 @@ Ext.define('saas.view.main.MainModel', {
     alias: 'viewmodel.main',
 
     data: {
-        
     }
 });

+ 28 - 19
frontend/saas-web/app/view/money/payBalance/FormPanel.js

@@ -83,41 +83,44 @@ Ext.define('saas.view.money.payBalance.FormPanel', {
     }, {
         xtype: "detailGridField",
         storeModel: 'saas.model.money.PayBalance1',
-        detnoColumn: 'pb_detno',
+        detnoColumn: 'pd_detno',
         deleteDetailUrl: '/api/money/paybalance/deleteDetail1/',
         columns: [{
             text: 'ID',
             dataIndex: 'id',
             hidden: true
         }, {
-            // text: '期间',
-            // dataIndex: 'pb_ym'
+            text: '期间',
+            dataIndex: 'pd_ym',
             // editor: {
             //     xtype: 'hidden',
-            //     // xtype: 'textfield,
-            //     // xtype: 'datefield''
+                // xtype: 'textfield,
+                // xtype: 'datefield''
             // }
-        // }, {
-        //     text: '结算账户ID',
-        //     dataIndex: 'pb_bankid'
-        // }, {
+        }, {
+            text: '结算账户ID',
+            dataIndex: 'pd_bankid',
+            editor:{
+                xtype:'hidden'
+            }
+        }, {
         //     text: '结算账户编号',
         //     dataIndex: 'paybalancedet'
         // }, {
             text: '结算账户',
-            dataIndex: 'pb_bankname'
+            dataIndex: 'pd_bankname'
         }, {
             text: "付款金额",
-            dataIndex: "pb_amount",
+            dataIndex: "pd_amount",
             editor:{
                 xtype: 'numberfield'
             }
         }, {
             text: "结算方式",
-            dataIndex: "pb_paymethod"
+            dataIndex: "pd_paymethod"
         }, {
             text: "结算号",
-            dataIndex: "pb_paycode"
+            dataIndex: "pd_paycode"
         }, {
             text: "备注",
             dataIndex: "pb_remark",
@@ -135,12 +138,18 @@ Ext.define('saas.view.money.payBalance.FormPanel', {
             dataIndex: 'id',
             hidden: true
         }, {
-        //     text: '期间',
-        //     dataIndex: 'pbd_ym'
-        // }, {
-        //     text: '来源ID',
-        //     dataIndex: 'pbd_slid'
-        // }, {
+            text: '期间',
+            dataIndex: 'pbd_ym',
+            editor:{
+                xtype:'hidden'
+            }
+        }, {
+            text: '来源ID',
+            dataIndex: 'pbd_slid',
+            editor:{
+                xtype:'hidden'
+            }
+        }, {
             text: '源单编号',
             dataIndex: 'pbd_slcode'
         // }, {

+ 3 - 3
frontend/saas-web/app/view/money/payBalance/QueryPanel.js

@@ -42,16 +42,16 @@ Ext.define('saas.view.money.paybalance.QueryPanel', {
         }, {
             text: '供应商编号',
             dataIndex: 'pb_vendcode',
-            width: 200
+            width: 200,
+            hidden:true
         }, {
             text: '供应商名称',
             dataIndex: 'pb_vendname',
-            width: 120
+            width: 120,
         }, {
             text: '收款人',
             dataIndex: 'pb_manname',
             width: 120,
-            hidden:true
         }, {
             text: '付款金额',
             dataIndex: 'pb_pdamount',

+ 2 - 0
frontend/saas-web/app/view/purchase/purchase/FormPanelController.js

@@ -92,6 +92,8 @@ Ext.define('saas.view.purchase.purchase.FormPanelController', {
                         // dataUrl:'http://192.168.253.31:9480/product/getProductsByCondition',
                         // dataUrl:'http://localhost:9480/product/list',
                         dataUrl: '/api/document/product/list',
+                        addXtype: 'document-product-formpanel',
+                        addTitle: '物料资料',
                         defaultCondition:"pr_statuscode='OPEN'",
                         //放大镜赋值设置
                         dbfinds:[{

+ 62 - 68
frontend/saas-web/app/view/stock/make/FormPanel.js

@@ -16,11 +16,11 @@ Ext.define('saas.view.stock.make.FormPanel', {
      _statusCodeField: 'ma_statuscode',
      _relationColumn: 'mm_maid',
 
-     _readUrl:'/api/purchase/purchase/read/',
-     _saveUrl:'/api/purchase/purchase/save',
-     _auditUrl:'/api/purchase/purchase/audit',
-     _deleteUrl:'/api/purchase/purchase/delete/',
-     _turnInUrl:'/api/purchase/prodinout/turnProdin/',
+     _readUrl:'/api/storage/make/read/',
+     _saveUrl:'/api/storage/make/save',
+     _auditUrl:'/api/storage/make/audit',
+    _unAuditUrl: '/api/storage/make/unAudit/',
+     _deleteUrl:'/api/storage/make/delete/',
      initId:0,
  
      toolBtns: [],
@@ -82,16 +82,22 @@ Ext.define('saas.view.stock.make.FormPanel', {
         fieldLabel : "仓库id",
         columnWidth: 0.2
     },{
-        xtype : "textfield", 
+        xtype : "hidden",
         name : "ma_whcode", 
-        fieldLabel : "仓库",
+        fieldLabel : "仓库编号",
         columnWidth: 0.2
     },{
+        xtype : "dbfindtrigger",
+        name : "ma_whname",
+        fieldLabel : "仓库名称",
+        columnWidth: 0.2
+        },
+        {
         name : "detailGridField", 
         xtype : "detailGridField", 
-        storeModel:'saas.model.document.Product',
-        detnoColumn:  'pd_detno',
-        deleteDetailUrl:'/api/purchase/purchase/deleteDetail/',
+        storeModel:'saas.model.stock.Makematerial',
+        detnoColumn:  'mm_detno',
+        deleteDetailUrl:'/api/storage/make/deleteDetail/',
         columns : [
             {
                 text : "id", 
@@ -120,25 +126,36 @@ Ext.define('saas.view.stock.make.FormPanel', {
                     queryMode : "local", 
                     store : null, 
                     valueField : "value", 
-                    xtype : "dbfindtrigger"
+                    xtype : "multidbfindtrigger"
                 }
             },
             {
                 text : "名称", 
                 dataIndex : "pr_detail",
+                width : 100.0,
                 ignore:true,
                 renderer: function (v, m, r) {
-                    return r.data["product"]?r.data["product"][m.column.dataIndex]:'';
+                    return r.data["productDTO"]?r.data["productDTO"][m.column.dataIndex]:v;
                 }
             },
             {
                 text : "规格", 
                 dataIndex : "pr_spec",
+                width : 100.0,
                 ignore:true,
                 renderer: function (v, m, r) {
-                    return r.data["product"]?r.data["product"][m.column.dataIndex]:'';
+                    return r.data["productDTO"]?r.data["productDTO"][m.column.dataIndex]:v;
                 }
             },
+            {
+                text : "单位", 
+                dataIndex : "pr_unit",
+                width : 100.0,
+                ignore:true,
+                renderer: function (v, m, r) {
+                    return r.data["productDTO"]?r.data["productDTO"][m.column.dataIndex]:'';
+                }
+            },            
             {
                 text : "数量", 
                 dataIndex : "pd_qty", 
@@ -150,69 +167,46 @@ Ext.define('saas.view.stock.make.FormPanel', {
                 format:'0',
                 items : null,
                 summaryType: 'sum'
+            },{
+                text : "仓库id",
+                dataIndex : "mm_whid",
+                xtype : "numbercolumn",
+                hidden:true
             },
             {
-                text : "已转数", 
-                dataIndex : "pd_yqty", 
-                editor : {
-                    xtype : "numberfield"
-                },
-                width : 120.0, 
-                xtype : "numbercolumn", 
-                format:'0',
-                items : null,
-                summaryType: 'sum'
+                text : "仓库编号",
+                dataIndex : "mm_whcode",
+                hidden:true
             },
             {
-                text : "单价", 
-                editor : {
-                    xtype : "numberfield"
-                },
-                format:'0,000.00',
-                dataIndex : "pd_price", 
-                width : 120.0, 
-                xtype : "numbercolumn",
+                text : "仓库",
+                dataIndex : "mm_whname",
+                width : 120.0,
                 items : null,
-                summaryType: 'sum'
-            }, 
-            {
-                text : "税率", 
                 editor : {
-                    xtype : "numberfield"
-                },
-                dataIndex : "pd_taxrate", 
-                width : 120.0, 
-                xtype : "numbercolumn", 
-                items : null
-            },
-            {
-                text : "含税金额", 
-                dataIndex : "pd_total", 
-                width : 120.0, 
-                xtype : "numbercolumn"
-            }, 
-            {
-                text : "未税金额", 
-                dataIndex : "pd_taxtotal", 
-                xtype : "numbercolumn"
-            },{
-                text : "需求日期", 
-                dataIndex : "pd_delivery", 
-                flex : 1.0, 
-                xtype:'datecolumn',
-                editor : {
-                    xtype : "datefield",
-                    editable : true, 
-                    hideTrigger : false
+                    displayField : "display",
+                    editable : true,
+                    format : "",
+                    hideTrigger : false,
+                    maxLength : 100.0,
+                    minValue : null,
+                    positiveNum : false,
+                    queryMode : "local",
+                    store : null,
+                    valueField : "value",
+                    xtype : "dbfindtrigger"
                 }
-            },
-            {
-                text : "关联销售单号", 
-                dataIndex : "pd_salecode", 
+            },{
+                text : "替代料",
+                dataIndex : "mm_repprodcode",
+                width : 200.0,
+                hidden:true
+            }, {
+                text : "备注",
+                dataIndex : "mm_remark",
                 width : 120.0,
-                flex : 1.0
-            }
-        ]
+                hidden:true
+            }]
     },{
         xtype : "datefield", 
         name : "createTime", 

+ 65 - 66
frontend/saas-web/app/view/stock/make/FormPanelController.js

@@ -5,72 +5,71 @@ Ext.define('saas.view.stock.make.FormPanelController', {
         var me = this;
         this.control({
             //主表单选放大镜模板
-            'dbfindtrigger[name=pu_vendcode]':{
-                beforerender:function(f){
-                    Ext.apply(f,{
-                        //数据接口
-                        dataUrl:'/api/document/vendor/list',
-                        addXtype: 'document-vendor-formpanel',
-                        addTitle: '供应商资料',
-                        //赋值 
-                        dbfinds:[{
-                            from:'ve_code',to:'pu_vendcode'
-                        },{
-                            from:'ve_name',to:'pu_vendname'
-                        }],
-                        //联想设置
-                        dbtpls:[{
-                            field:'ve_code',width:100
-                        },{
-                            field:'ve_name',width:100
-                        }],
-                        defaultCondition:"ve_statuscode='OPEN'",
-                        //放大镜窗口字段
-                        dbSearchFields:[{
-                            emptyText:'输入仓库编号或名称',
-                            xtype : "textfield", 
-                            name : "name", 
-                            allowBlank : true, 
-                            columnWidth : 0.25,
-                            getCondition:function(v){
-                                return "upper(ve_code) like '%"+v.toUpperCase()+"%' or upper(ve_name) like '%"+v.toUpperCase()+"%'";
-                            }
-                        }],
-                        //放大镜窗口列表
-                        dbColumns:[{
-                            "text": "供应商ID",
-                            "flex": 0,
-                            "dataIndex": "ve_id",
-                            "width": 0,
-                            "xtype": "",
-                            "items": null
-                        },{
-                            "text": "供应商编号",
-                            "flex": 1,
-                            "dataIndex": "ve_code",
-                            "width": 100,
-                            "xtype": "",
-                            "items": null
-                        }, {
-                            "text": "供应商名称",
-                            "flex": 1,
-                            "dataIndex": "ve_name",
-                            "xtype": "",
-                            "items": null
-                        }, {
-                            "text": "供应商类型",
-                            "flex": 0,
-                            "dataIndex": "ve_type",
-                            "width": 200,
-                            "xtype": "",
-                            "items": null
-                        }]
-                    }) ;   
-
-                }
-            },
+            // 'dbfindtrigger[name=pu_vendcode]':{
+            //     beforerender:function(f){
+            //         Ext.apply(f,{
+            //             //数据接口
+            //             dataUrl:'/api/document/vendor/list',
+            //             addXtype: 'document-vendor-formpanel',
+            //             addTitle: '供应商资料',
+            //             //赋值 
+            //             dbfinds:[{
+            //                 from:'ve_code',to:'pu_vendcode'
+            //             },{
+            //                 from:'ve_name',to:'pu_vendname'
+            //             }],
+            //             //联想设置
+            //             dbtpls:[{
+            //                 field:'ve_code',width:100
+            //             },{
+            //                 field:'ve_name',width:100
+            //             }],
+            //             defaultCondition:"ve_statuscode='OPEN'",
+            //             //放大镜窗口字段
+            //             dbSearchFields:[{
+            //                 emptyText:'输入仓库编号或名称',
+            //                 xtype : "textfield", 
+            //                 name : "name", 
+            //                 allowBlank : true, 
+            //                 columnWidth : 0.25,
+            //                 getCondition:function(v){
+            //                     return "upper(ve_code) like '%"+v.toUpperCase()+"%' or upper(ve_name) like '%"+v.toUpperCase()+"%'";
+            //                 }
+            //             }],
+            //             //放大镜窗口列表
+            //             dbColumns:[{
+            //                 "text": "供应商ID",
+            //                 "flex": 0,
+            //                 "dataIndex": "ve_id",
+            //                 "width": 0,
+            //                 "xtype": "",
+            //                 "items": null
+            //             },{
+            //                 "text": "供应商编号",
+            //                 "flex": 1,
+            //                 "dataIndex": "ve_code",
+            //                 "width": 100,
+            //                 "xtype": "",
+            //                 "items": null
+            //             }, {
+            //                 "text": "供应商名称",
+            //                 "flex": 1,
+            //                 "dataIndex": "ve_name",
+            //                 "xtype": "",
+            //                 "items": null
+            //             }, {
+            //                 "text": "供应商类型",
+            //                 "flex": 0,
+            //                 "dataIndex": "ve_type",
+            //                 "width": 200,
+            //                 "xtype": "",
+            //                 "items": null
+            //             }]
+            //         }) ;   
+            //     }
+            // },
             //从表单选放大镜赋值关系 以及 tpl模板
-            'dbfindtrigger[name=pd_prodcode]':{
+            'dbfindtrigger[name=mm_prodcode]':{
                 beforerender:function(f){
                     Ext.apply(f,{
                         //数据接口
@@ -79,7 +78,7 @@ Ext.define('saas.view.stock.make.FormPanelController', {
                         addTitle: '物料资料',
                         //放大镜赋值设置
                         dbfinds:[{
-                            from:'pr_code',to:'pd_prodcode'
+                            from:'pr_code',to:'mm_prodcode'
                         },{
                             from:'pr_unit',to:'pd_unit'
                         }],

+ 5 - 5
frontend/saas-web/app/view/stock/make/QueryPanel.js

@@ -99,12 +99,12 @@ Ext.define('saas.view.stock.make.QueryPanel', {
         columnWidth: 1
     }],
     queryGridConfig: {
-        idField: 'pu_id',
-        codeField: 'pu_code',
-        addTitle: '采购单',
-        addXtype: 'purchase-purchase-formpanel',
+        idField: 'id',
+        codeField: 'ma_code',
+        addTitle: '制造单',
+        addXtype: 'stock-make-formpanel',
         defaultCondition:'',
-        baseVastUrl: '/api/purchase/purchase/',
+        baseVastUrl: '/api/storage/make/',
         baseColumn: [{
             text: 'id',
             dataIndex: 'pu_id',

+ 1 - 150
frontend/saas-web/app/view/sys/config/FormPanelController.js

@@ -5,48 +5,6 @@ Ext.define('saas.view.sys.config.FormPanelController', {
     BaseUtil: Ext.create('saas.util.BaseUtil'),
     FormUtil: Ext.create('saas.util.FormUtil'),
 
-    auditBtnClick: function() {
-        var me = this,
-        form = me.getView(),
-        statusCodeField = form._statusCodeField,
-        viewModel = me.getViewModel(),
-        status = viewModel.get(statusCodeField);
-
-        status == 'AUDITED' ? me.unAudit() : me.audit();
-    },
-
-    add: function(){
-        var form = this.getView();
-        var id = form.xtype + '-add';
-        openTab(form.xtype,'新增' + form._title, id);
-    },
-    
-    delete: function(){
-        var me = this;
-        var form = this.getView();
-        var viewModel = me.getViewModel();
-        var id = viewModel.get(form._idField);
-        var code = viewModel.get(form._codeField);
-        if(id&&id.value!=0){
-            me.BaseUtil.request({
-                url: form._deleteUrl+id,
-                method: 'POST',
-            })
-            .then(function(localJson) {
-                if(localJson.success){
-                    var mainTab = Ext.getCmp('main-tab-panel');
-                    mainTab.getActiveTab().close();
-                    //解析参数
-                    showToast('删除成功');
-                }
-            })
-            .catch(function(res) {
-                console.error(res);
-                showToast('删除失败: ' + res.message);
-            });
-        }
-    },
-
     onSave: function() {
         var me = this,
         form = this.getView();
@@ -111,113 +69,6 @@ Ext.define('saas.view.sys.config.FormPanelController', {
             console.error(res);
             showToast('保存失败: ' + res.message);
         });
-    },
-
-    audit: function(){
-        var me = this,
-        form = this.getView(),
-        detailCount = form.detailCount,
-        viewModel = me.getViewModel(),
-        modelData = viewModel.getData();
-
-        var valid = form.isValid();
-
-        if(!valid) {
-            showToast('表单校验有误,请检查');
-            return false;
-        }
-        
-        if(form.getForm().wasDirty==false){
-            showToast('未修改数据,请修改后保存');
-            return false;
-        }
-        //form里面数据
-        var formData = form.getFormData();
-        var params = {
-            main: formData.main
-        };
-
-        for(var i = 0; i < detailCount; i++) {
-            params['items' + ( i + 1)] = formData['detail' + i];
-        }
-
-        // 只有一个从表时从表字段改为items
-        if(detailCount == 1) {
-            params.items = params.items1;
-            delete params.items1;
-        }
-
-        me.BaseUtil.request({
-            url: form._auditUrl,
-            params: JSON.stringify(params),
-            method: 'POST',
-        })
-        .then(function(localJson) {
-            if(localJson.success){
-                // 未保存直接审核会返回id
-                if(localJson.data) {
-                    var id = localJson.data.id;
-                    var code = localJson.data.code;
-                    
-                    form.initId = id;
-
-                    var newId = form.xtype + '-' + id;
-                    var newTitle = form._title + '(' + code + ')';
-
-                    refreshTabTitle(newId, newTitle);
-                }
-                form.FormUtil.loadData(form);
-                form.setEditable(false);
-                showToast('审核成功');
-            }
-        })
-        .catch(function(res) {
-            console.error(res);
-            showToast('审核失败: ' + res.message);
-        });
-    },
-    unAudit: function() {
-        var me = this;
-        var form = this.getView();
-        var viewModel = me.getViewModel();
-        var id = viewModel.get(form._idField);
-        var code = viewModel.get(form._codeField);
-        if(id&&id.value!=0){
-            me.BaseUtil.request({
-                url: form._unAuditUrl+id,
-                method: 'POST',
-            })
-            .then(function(localJson) {
-                if(localJson.success){
-                    //解析参数
-                    showToast('反审核成功');
-                    form.FormUtil.loadData(form);
-                }
-            })
-            .catch(function(res) {
-                console.error(res);
-                showToast('反审核失败: ' + res.message);
-            });
-        }
-    },
-    codeEditorBlur: function(e) {
-        var me = this,
-        viewModel = me.getViewModel(),
-        targetEl = event.target,
-        faEl = targetEl.getElementsByClassName('fa')[0];
-
-        if(faEl && faEl.classList.contains('fa-check-circle')) {
-            // 处理重复触发事件
-            // viewModel.set('base.codeEditable', false);
-        }else {
-            viewModel.set('base.codeEditable', false);
-        }
-    },
-    codeEditorClick: function() {
-        var me = this,
-        viewModel = me.getViewModel(),
-        codeEditable = viewModel.get('base.codeEditable');
-
-        viewModel.set('base.codeEditable', !codeEditable);
     }
+
 });

+ 12 - 5
frontend/saas-web/app/view/sys/messagelog/DataList.js

@@ -9,9 +9,9 @@ Ext.define('saas.view.sys.messagelog.DataList', {
 
     tbar: [{
         width: 150,
-        name: 'ml_caller',
+        name: 'ml_name',
         xtype: 'textfield',
-        emptyText : '单据Caller'
+        emptyText : '单据类别'
     },{
         width: 150,
         name: 'ml_code',
@@ -52,9 +52,9 @@ Ext.define('saas.view.sys.messagelog.DataList', {
         dataIndex : "id", 
         xtype : "numbercolumn",   
     },{
-        text:'单据Caller',
-        dataIndex : "ml_caller",
-        width : 200.0, 
+        text:'单据类别',
+        dataIndex : "ml_name",
+        width : 120.0, 
     },{
         text : "单据编号", 
         width : 200.0, 
@@ -65,6 +65,13 @@ Ext.define('saas.view.sys.messagelog.DataList', {
         dataIndex : "ml_content", 
         width : 220.0, 
     }, 
+    {
+        xtype:'datecolumn',
+        format:'Y-m-d H:i:s',
+        text : "操作时间", 
+        dataIndex : "createTime", 
+        width : 200.0, 
+    },
     {
         text : "结果", 
         dataIndex : "ml_result", 

+ 39 - 53
frontend/saas-web/app/view/viewport/ViewportController.js

@@ -7,6 +7,7 @@ Ext.define('saas.view.viewport.ViewportController', {
             '*': {
                 login: 'onLogin',
                 logout: 'onLogout',
+                selectCompany: 'onSelectCompany',
                 unmatchedroute: 'handleUnmatchedRoute'
             }
         }
@@ -17,7 +18,7 @@ Ext.define('saas.view.viewport.ViewportController', {
     },
 
     init: function() {
-        this.initDirect();
+        this.originalRoute = saas.getApplication().getDefaultToken();
         this.restoreSession();
     },
 
@@ -41,7 +42,8 @@ Ext.define('saas.view.viewport.ViewportController', {
     },
 
     showMain: function() {
-        this.showView('main');
+        var me = this;
+        me.showView('main');
     },
 
     // ROUTING
@@ -75,51 +77,14 @@ Ext.define('saas.view.viewport.ViewportController', {
         }
     },
 
-    // EXT DIRECT
-
-    initDirect: function() {
-        // var api = Server.API;
-        // if (!api) {
-        //     Ext.raise('Failed to load Direct API');
-        // }
-
-        // Ext.direct.Manager.addProvider(Ext.applyIf({
-        //     id: 'server',
-        //     listeners: {
-        //         data: 'onDirectData',
-        //         scope: this
-        //     }
-        // }, api));
-    },
-
-    setDirectToken: function(token) {
-        // var provider = Ext.direct.Manager.getProvider('server'),
-        //     headers = provider.getHeaders() || {};
-
-        // if (token) {
-        //     headers['Authorization'] = 'Bearer ' + token;
-        // } else {
-        //     delete headers['Authorization'];
-        // }
-
-        // provider.setHeaders(headers);
-    },
-
-    onDirectData: function(provider, e) {
-        if (e.type !== 'exception') {
-            return;
-        }
-
-        var message = e.message || {};
-        switch (message.code) {
-        case -32098:    // AuthTokenExpired
-        case -32097:    // AuthTokenInvalid
-            // Defer user deauthentication until the current direct transaction is done.
-            Ext.asap(this.terminateSession, this);
-            break;
-        default:
-            break;
+    setRequestToken: function(token) {
+        var headers = Ext.Ajax.getDefaultHeaders() || {};
+        if (token) {
+            headers['Authorization'] = token;
+        } else {
+            delete headers['Authorization'];
         }
+        Ext.Ajax.setDefaultHeaders(headers);
     },
 
     // SESSION MANAGEMENT
@@ -138,20 +103,20 @@ Ext.define('saas.view.viewport.ViewportController', {
     },
 
     initiateSession: function(session) {
-        this.setDirectToken(session.get('token'));
+        this.setRequestToken(session.get('token'));
         this.saveSession(session);
         this.showMain();
     },
 
     terminateSession: function() {
-        this.setDirectToken(null);
+        this.setRequestToken(null);
         this.saveSession(null);
         this.showAuth();
     },
 
     saveSession: function(session) {
         saas.util.State.set('session', session && session.getData(true));
-        // this.getViewModel().set('account', session && session.getAccount(false).getData(true));
+        this.getViewModel().set('account', session && session.get('account'));
         this.session = session;
     },
 
@@ -175,15 +140,36 @@ Ext.define('saas.view.viewport.ViewportController', {
             return false;
         }
 
-        view.setMasked(true);
-        session.logout().catch(function() {
-            // TODO handle errors
+        view.mask();
+        session.logout().catch(function(error) {
+            showToast(error.message);
         }).then(function() {
             me.originalRoute = Ext.History.getToken();
             me.terminateSession();
-            view.setMasked(false);
+            view.unmask();
             me.redirectTo('login', {replace: true});
         });
+    },
+
+    onSelectCompany: function(companyId) {
+        var me = this, view = me.getView(), viewModel = me.getViewModel(), 
+            oldSession = me.session, company = viewModel.get('company');
+
+        if (company.id != companyId) {
+            view.mask('请稍等...');
+
+            saas.model.Session.switchCompany(oldSession, companyId)
+                .then(function(newSession) {
+                    newSession.get('account').companyId = companyId;
+                    me.initiateSession(newSession);
+                })
+                .catch(function(error) {
+                    showToast(error.message);
+                })
+                .then(function() {
+                    view.isMasked() && view.unmask();
+                }); 
+        }        
     }
 });
 

+ 14 - 1
frontend/saas-web/app/view/viewport/ViewportModel.js

@@ -4,5 +4,18 @@ Ext.define('saas.view.viewport.ViewportModel', {
 
     data: {
         account: null
-    }
+    },
+
+    formulas: {
+        company: function (get) {
+            var account = get('account');
+            return account && account.companies.find(function(c){
+                return c.id == account.companyId;
+            });
+        },
+        avatarUrl: function (get) {
+            var account = get('account');
+            return (account && account.avatarUrl) || 'resources/images/default/user-profile-default.png'
+        }
+    }    
 });

+ 10 - 1
frontend/saas-web/overrides/data/Connection.js

@@ -7,13 +7,22 @@ Ext.define('saas.override.data.Connection', {
  
     urlRegexp: /(http|ftp|https):\/\//,
 
+    config: {
+        /**
+         * @cfg {Object} defaultServerHeaders
+         * 与defaultHeaders有区别,只在调用server api的时候才添加的headers
+         */
+        defaultServerHeaders: null
+    },
+
     privates: {
         setupServerOptions: function(options) {
             var serverOptions = Ext.manifest.server, originUrl = options.url;
             if (serverOptions && serverOptions.basePath && !this.urlRegexp.test(originUrl) &&
               (!serverOptions.urlPattern || new RegExp(serverOptions.urlPattern).test(originUrl))) {
-                Ext.apply(options, {
+                Ext.Object.merge(options, {
                     url: serverOptions.basePath + (originUrl.indexOf('/') == 0 ? '' : '/') + originUrl,
+                    headers: this.getDefaultServerHeaders()
                 });
             }
         }

+ 1 - 0
frontend/saas-web/packages/local/theme-default/sass/var/all.scss

@@ -53,3 +53,4 @@ $blank-page-color: dynamic(#cacaca);
 $lightest-color: dynamic(#fff);
 $circle-border-radius: dynamic(50%);
 $base-border-color: dynamic(#ddd);
+$base-over-color: dynamic(#eee);

+ 6 - 0
pom.xml

@@ -40,6 +40,7 @@
         <commons.fileupload.version>1.3.3</commons.fileupload.version>
         <docker.repository>192.168.253.3:4000</docker.repository>
         <docker.registry.name>saas</docker.registry.name>
+        <guava.version>18.0</guava.version>
     </properties>
 
     <repositories>
@@ -322,6 +323,11 @@
                 <artifactId>fastjson</artifactId>
                 <version>${fastjson.version}</version>
             </dependency>
+            <dependency>
+                <groupId>com.google.guava</groupId>
+                <artifactId>guava</artifactId>
+                <version>${guava.version}</version>
+            </dependency>
         </dependencies>
     </dependencyManagement>
 

+ 73 - 68
script/mysql/init/account.sql

@@ -55,6 +55,7 @@ create table `ac_role` (
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='角色';
 
 create table `ac_account_role` (
+  company_id int unsigned,
   account_id int unsigned,
   role_id int unsigned,
   primary key(account_id, role_id)
@@ -68,12 +69,14 @@ create table `ac_resource_module` (
 
 create table `ac_resource_group` (
   id int unsigned primary key not null auto_increment,
+  app_id varchar(100) comment '应用',
   module_id int unsigned not null,
   name varchar(100) comment '名称'
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='资源分组';
 
 create table `ac_resource` (
   id int unsigned primary key not null auto_increment,
+  app_id varchar(100) comment '应用',
   group_id int unsigned not null,
   name varchar(1000) comment '名称',
   type varchar(100) comment '类型 MENU,BUTTON,API',
@@ -83,6 +86,8 @@ create table `ac_resource` (
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='资源';
 
 create table `ac_role_resource` (
+  app_id varchar(100) comment '应用',
+  company_id int unsigned,
   role_id int unsigned,
   resource_id int unsigned,
   primary key(role_id, resource_id)
@@ -108,7 +113,7 @@ insert into ac_role(id,code,name,description,type,company_id,creator_id,create_t
     values (1, 'ROLE_ADMIN', '管理员', '公司管理人员,拥有所有权限', 0, 1, 1, now(), 1, now());
 insert into ac_role(id,code,name,description,type,company_id,creator_id,create_time,updater_id,update_time)
 values (2, 'ROLE_NORMAL', '普通用户', '公司普通用户', 0, 1, 1, now(), 1, now());
-insert into ac_account_role(account_id, role_id) values (1, 1);
+insert into ac_account_role(company_id, account_id, role_id) values (1, 1, 1);
 
 # 资源模块
 insert into ac_resource_module(id, app_id, name) values (1, 'trade-app', '采购');
@@ -117,72 +122,72 @@ insert into ac_resource_module(id, app_id, name) values (3, 'trade-app', '库存
 insert into ac_resource_module(id, app_id, name) values (4, 'trade-app', '资金');
 insert into ac_resource_module(id, app_id, name) values (5, 'trade-app', '资料');
 # 资源组
-insert into ac_resource_group(id, module_id, name) values (1, 1, '采购单');
-insert into ac_resource_group(id, module_id, name) values (2, 1, '采购验收单');
-insert into ac_resource_group(id, module_id, name) values (3, 1, '采购验退单');
-insert into ac_resource_group(id, module_id, name) values (4, 1, '采购询价单');
-insert into ac_resource_group(id, module_id, name) values (5, 1, '采购明细表');
-insert into ac_resource_group(id, module_id, name) values (6, 1, '采购付款一览表');
-insert into ac_resource_group(id, module_id, name) values (7, 2, '销售订单');
-insert into ac_resource_group(id, module_id, name) values (8, 2, '销售出货单');
-insert into ac_resource_group(id, module_id, name) values (9, 2, '销售退货单');
-insert into ac_resource_group(id, module_id, name) values (10, 2, '以销定购');
-insert into ac_resource_group(id, module_id, name) values (11, 2, '销售明细表');
-insert into ac_resource_group(id, module_id, name) values (12, 2, '销售收款一览表');
-insert into ac_resource_group(id, module_id, name) values (13, 2, '销售利润表');
-insert into ac_resource_group(id, module_id, name) values (14, 3, '调拨单');
-insert into ac_resource_group(id, module_id, name) values (15, 3, '制造单');
-insert into ac_resource_group(id, module_id, name) values (16, 3, '其它入库单');
-insert into ac_resource_group(id, module_id, name) values (17, 3, '其它出库单');
-insert into ac_resource_group(id, module_id, name) values (18, 3, '盘点单');
-insert into ac_resource_group(id, module_id, name) values (19, 3, '物料出入库明细表');
-insert into ac_resource_group(id, module_id, name) values (20, 3, '物料收发汇总表');
-insert into ac_resource_group(id, module_id, name) values (21, 3, '物料库存数量金额表');
-insert into ac_resource_group(id, module_id, name) values (22, 4, '付款单');
-insert into ac_resource_group(id, module_id, name) values (23, 4, '收款单');
-insert into ac_resource_group(id, module_id, name) values (24, 4, '核销单');
-insert into ac_resource_group(id, module_id, name) values (25, 4, '其它收支单');
-insert into ac_resource_group(id, module_id, name) values (26, 4, '资金转存');
-insert into ac_resource_group(id, module_id, name) values (27, 4, '供应商对账单');
-insert into ac_resource_group(id, module_id, name) values (28, 4, '应付账款明细表');
-insert into ac_resource_group(id, module_id, name) values (29, 4, '客户对账单');
-insert into ac_resource_group(id, module_id, name) values (30, 4, '应收款明细表');
-insert into ac_resource_group(id, module_id, name) values (31, 4, '资金账户余额表');
-insert into ac_resource_group(id, module_id, name) values (32, 5, '客户资料');
-insert into ac_resource_group(id, module_id, name) values (33, 5, '供应商管理');
-insert into ac_resource_group(id, module_id, name) values (34, 5, '商品管理');
-insert into ac_resource_group(id, module_id, name) values (35, 5, '仓库管理');
-insert into ac_resource_group(id, module_id, name) values (36, 5, '职员管理');
-insert into ac_resource_group(id, module_id, name) values (37, 5, '账户管理');
-insert into ac_resource_group(id, module_id, name) values (38, 5, '发货地址管理');
-insert into ac_resource_group(id, module_id, name) values (39, 5, '客户类别');
-insert into ac_resource_group(id, module_id, name) values (40, 5, '供应商类别');
-insert into ac_resource_group(id, module_id, name) values (41, 5, '商品类别');
-insert into ac_resource_group(id, module_id, name) values (42, 5, '支出类别');
-insert into ac_resource_group(id, module_id, name) values (43, 5, '收入类别');
-insert into ac_resource_group(id, module_id, name) values (44, 5, '物料品牌');
-insert into ac_resource_group(id, module_id, name) values (45, 5, '计量单位');
-insert into ac_resource_group(id, module_id, name) values (46, 5, '结算方式');
-insert into ac_resource_group(id, module_id, name) values (47, 5, '辅助属性');
-insert into ac_resource_group(id, module_id, name) values (48, 5, '客户物料编码');
-insert into ac_resource_group(id, module_id, name) values (49, 5, '单据编码规则');
+insert into ac_resource_group(id, app_id, module_id, name) values (1, 'trade-app', 1, '采购单');
+insert into ac_resource_group(id, app_id, module_id, name) values (2, 'trade-app', 1, '采购验收单');
+insert into ac_resource_group(id, app_id, module_id, name) values (3, 'trade-app', 1, '采购验退单');
+insert into ac_resource_group(id, app_id, module_id, name) values (4, 'trade-app', 1, '采购询价单');
+insert into ac_resource_group(id, app_id, module_id, name) values (5, 'trade-app', 1, '采购明细表');
+insert into ac_resource_group(id, app_id, module_id, name) values (6, 'trade-app', 1, '采购付款一览表');
+insert into ac_resource_group(id, app_id, module_id, name) values (7, 'trade-app', 2, '销售订单');
+insert into ac_resource_group(id, app_id, module_id, name) values (8, 'trade-app', 2, '销售出货单');
+insert into ac_resource_group(id, app_id, module_id, name) values (9, 'trade-app', 2, '销售退货单');
+insert into ac_resource_group(id, app_id, module_id, name) values (10, 'trade-app', 2, '以销定购');
+insert into ac_resource_group(id, app_id, module_id, name) values (11, 'trade-app', 2, '销售明细表');
+insert into ac_resource_group(id, app_id, module_id, name) values (12, 'trade-app', 2, '销售收款一览表');
+insert into ac_resource_group(id, app_id, module_id, name) values (13, 'trade-app', 2, '销售利润表');
+insert into ac_resource_group(id, app_id, module_id, name) values (14, 'trade-app', 3, '调拨单');
+insert into ac_resource_group(id, app_id, module_id, name) values (15, 'trade-app', 3, '制造单');
+insert into ac_resource_group(id, app_id, module_id, name) values (16, 'trade-app', 3, '其它入库单');
+insert into ac_resource_group(id, app_id, module_id, name) values (17, 'trade-app', 3, '其它出库单');
+insert into ac_resource_group(id, app_id, module_id, name) values (18, 'trade-app', 3, '盘点单');
+insert into ac_resource_group(id, app_id, module_id, name) values (19, 'trade-app', 3, '物料出入库明细表');
+insert into ac_resource_group(id, app_id, module_id, name) values (20, 'trade-app', 3, '物料收发汇总表');
+insert into ac_resource_group(id, app_id, module_id, name) values (21, 'trade-app', 3, '物料库存数量金额表');
+insert into ac_resource_group(id, app_id, module_id, name) values (22, 'trade-app', 4, '付款单');
+insert into ac_resource_group(id, app_id, module_id, name) values (23, 'trade-app', 4, '收款单');
+insert into ac_resource_group(id, app_id, module_id, name) values (24, 'trade-app', 4, '核销单');
+insert into ac_resource_group(id, app_id, module_id, name) values (25, 'trade-app', 4, '其它收支单');
+insert into ac_resource_group(id, app_id, module_id, name) values (26, 'trade-app', 4, '资金转存');
+insert into ac_resource_group(id, app_id, module_id, name) values (27, 'trade-app', 4, '供应商对账单');
+insert into ac_resource_group(id, app_id, module_id, name) values (28, 'trade-app', 4, '应付账款明细表');
+insert into ac_resource_group(id, app_id, module_id, name) values (29, 'trade-app', 4, '客户对账单');
+insert into ac_resource_group(id, app_id, module_id, name) values (30, 'trade-app', 4, '应收款明细表');
+insert into ac_resource_group(id, app_id, module_id, name) values (31, 'trade-app', 4, '资金账户余额表');
+insert into ac_resource_group(id, app_id, module_id, name) values (32, 'trade-app', 5, '客户资料');
+insert into ac_resource_group(id, app_id, module_id, name) values (33, 'trade-app', 5, '供应商管理');
+insert into ac_resource_group(id, app_id, module_id, name) values (34, 'trade-app', 5, '商品管理');
+insert into ac_resource_group(id, app_id, module_id, name) values (35, 'trade-app', 5, '仓库管理');
+insert into ac_resource_group(id, app_id, module_id, name) values (36, 'trade-app', 5, '职员管理');
+insert into ac_resource_group(id, app_id, module_id, name) values (37, 'trade-app', 5, '账户管理');
+insert into ac_resource_group(id, app_id, module_id, name) values (38, 'trade-app', 5, '发货地址管理');
+insert into ac_resource_group(id, app_id, module_id, name) values (39, 'trade-app', 5, '客户类别');
+insert into ac_resource_group(id, app_id, module_id, name) values (40, 'trade-app', 5, '供应商类别');
+insert into ac_resource_group(id, app_id, module_id, name) values (41, 'trade-app', 5, '商品类别');
+insert into ac_resource_group(id, app_id, module_id, name) values (42, 'trade-app', 5, '支出类别');
+insert into ac_resource_group(id, app_id, module_id, name) values (43, 'trade-app', 5, '收入类别');
+insert into ac_resource_group(id, app_id, module_id, name) values (44, 'trade-app', 5, '物料品牌');
+insert into ac_resource_group(id, app_id, module_id, name) values (45, 'trade-app', 5, '计量单位');
+insert into ac_resource_group(id, app_id, module_id, name) values (46, 'trade-app', 5, '结算方式');
+insert into ac_resource_group(id, app_id, module_id, name) values (47, 'trade-app', 5, '辅助属性');
+insert into ac_resource_group(id, app_id, module_id, name) values (48, 'trade-app', 5, '客户物料编码');
+insert into ac_resource_group(id, app_id, module_id, name) values (49, 'trade-app', 5, '单据编码规则');
 # 资源
-insert into ac_resource(id, name, group_id, type, url, method, classify) values
-  (1,'查询',1,'MENU','/api/purchase/purchase/list','GET','QUERY');
-insert into ac_resource(id, name, group_id, type, url, method, classify) values
-  (2,'新增',1,'MENU','/api/purchase/purchase/save','POST','ADD');
-insert into ac_resource(id, name, group_id, type, url, method, classify) values
-  (3,'修改',1,'BUTTON','/api/purchase/purchase/save','POST','UPDATE');
-insert into ac_resource(id, name, group_id, type, url, method, classify) values
-  (4,'审核',1,'BUTTON','/api/purchase/purchase/audit','POST','AUDIT');
-insert into ac_resource(id, name, group_id, type, url, method, classify) values
-  (5,'审核',1,'BUTTON','/api/purchase/purchase/batchAudit','POST','AUDIT');
-insert into ac_resource(id, name, group_id, type, url, method, classify) values
-  (6,'反审核',1,'BUTTON','/api/purchase/purchase/unAudit','POST','UNAUDIT');
-insert into ac_resource(id, name, group_id, type, url, method, classify) values
-  (7,'反审核',1,'BUTTON','/api/purchase/purchase/batchUnAudit','POST','UNAUDIT');
-insert into ac_resource(id, name, group_id, type, url, method, classify) values
-  (8,'删除',1,'BUTTON','/api/purchase/purchase/delete','GET','DELETE');
-insert into ac_resource(id, name, group_id, type, url, method, classify) values
-  (9,'打印',1,'BUTTON','/api/purchase/purchase/print','GET','PRINT');
+insert into ac_resource(id, app_id, name, group_id, type, url, method, classify) values
+  (1,'trade-app', '查询',1,'MENU','/api/purchase/purchase/list','GET','QUERY');
+insert into ac_resource(id, app_id, name, group_id, type, url, method, classify) values
+  (2,'trade-app', '新增',1,'MENU','/api/purchase/purchase/save','POST','ADD');
+insert into ac_resource(id, app_id, name, group_id, type, url, method, classify) values
+  (3,'trade-app', '修改',1,'BUTTON','/api/purchase/purchase/save','POST','UPDATE');
+insert into ac_resource(id, app_id, name, group_id, type, url, method, classify) values
+  (4,'trade-app', '审核',1,'BUTTON','/api/purchase/purchase/audit','POST','AUDIT');
+insert into ac_resource(id, app_id, name, group_id, type, url, method, classify) values
+  (5,'trade-app', '审核',1,'BUTTON','/api/purchase/purchase/batchAudit','POST','AUDIT');
+insert into ac_resource(id, app_id, name, group_id, type, url, method, classify) values
+  (6,'trade-app', '反审核',1,'BUTTON','/api/purchase/purchase/unAudit','POST','UNAUDIT');
+insert into ac_resource(id, app_id, name, group_id, type, url, method, classify) values
+  (7,'trade-app', '反审核',1,'BUTTON','/api/purchase/purchase/batchUnAudit','POST','UNAUDIT');
+insert into ac_resource(id, app_id, name, group_id, type, url, method, classify) values
+  (8,'trade-app', '删除',1,'BUTTON','/api/purchase/purchase/delete','GET','DELETE');
+insert into ac_resource(id, app_id, name, group_id, type, url, method, classify) values
+  (9,'trade-app', '打印',1,'BUTTON','/api/purchase/purchase/print','GET','PRINT');