Browse Source

第三方对接

yingp 6 years ago
parent
commit
33e3805d46
64 changed files with 2459 additions and 95 deletions
  1. 9 1
      README.md
  2. 24 0
      apis/account-api/src/main/java/com/usoftchina/uu/account/context/PlatformHolder.java
  3. 15 3
      apis/app-api/src/main/java/com/usoftchina/uu/app/api/AppConfigApi.java
  4. 93 2
      apis/message-api/src/main/java/com/usoftchina/uu/message/api/MessageApi.java
  5. 14 0
      apis/message-api/src/main/java/com/usoftchina/uu/message/dto/MessageDTO.java
  6. 12 0
      apis/message-api/src/main/java/com/usoftchina/uu/message/dto/SendMessageDTO.java
  7. 25 16
      apis/open-grpc-api/src/main/proto/app.proto
  8. 21 3
      apis/open-grpc-api/src/main/proto/base.proto
  9. 9 1
      apis/open-grpc-api/src/main/proto/message.proto
  10. 1 0
      external/README.md
  11. 5 0
      external/baidu-push-feign-client/src/main/java/com/usoftchina/uu/baidu/push/dto/AndroidNotification.java
  12. 1 1
      external/baidu-push-feign-client/src/test/resources/application.yml
  13. 5 0
      external/open-sdk/build.gradle
  14. 69 0
      external/open-sdk/src/main/java/com/usoftchina/uu/open/sdk/base/Platform.java
  15. 28 0
      external/open-sdk/src/main/java/com/usoftchina/uu/open/sdk/client/UuClient.java
  16. 23 0
      external/open-sdk/src/main/java/com/usoftchina/uu/open/sdk/client/UuClientProperties.java
  17. 63 0
      external/open-sdk/src/main/java/com/usoftchina/uu/open/sdk/client/UuClientSignatureInterceptor.java
  18. 20 0
      external/open-sdk/src/main/java/com/usoftchina/uu/open/sdk/config/UuAutoConfiguration.java
  19. 40 0
      external/open-sdk/src/main/java/com/usoftchina/uu/open/sdk/context/UuSecretHolder.java
  20. 40 0
      external/open-sdk/src/main/java/com/usoftchina/uu/open/sdk/crypto/Hex.java
  21. 97 0
      external/open-sdk/src/main/java/com/usoftchina/uu/open/sdk/crypto/HmacEncoder.java
  22. 9 0
      external/open-sdk/src/main/java/com/usoftchina/uu/open/sdk/crypto/HmacSHA256Encoder.java
  23. 48 0
      external/open-sdk/src/main/java/com/usoftchina/uu/open/sdk/crypto/HmacUtils.java
  24. 132 0
      external/open-sdk/src/main/java/com/usoftchina/uu/open/sdk/dto/UuAccount.java
  25. 33 0
      external/open-sdk/src/main/java/com/usoftchina/uu/open/sdk/dto/UuAccountLoginInfo.java
  26. 71 0
      external/open-sdk/src/main/java/com/usoftchina/uu/open/sdk/dto/UuMessage.java
  27. 65 0
      external/open-sdk/src/main/java/com/usoftchina/uu/open/sdk/dto/UuPage.java
  28. 31 0
      external/open-sdk/src/main/java/com/usoftchina/uu/open/sdk/dto/UuUnreadMessageCount.java
  29. 35 0
      external/open-sdk/src/main/java/com/usoftchina/uu/open/sdk/exception/UuException.java
  30. 29 0
      external/open-sdk/src/main/java/com/usoftchina/uu/open/sdk/server/UuServerProperties.java
  31. 75 0
      external/open-sdk/src/main/java/com/usoftchina/uu/open/sdk/service/UuAccountService.java
  32. 127 0
      external/open-sdk/src/main/java/com/usoftchina/uu/open/sdk/service/UuMessageService.java
  33. 18 0
      external/open-sdk/src/main/java/com/usoftchina/uu/open/sdk/util/SignatureUtils.java
  34. 78 0
      external/open-sdk/src/main/java/com/usoftchina/uu/open/sdk/util/UuBeanMapper.java
  35. 3 0
      external/open-sdk/src/main/resources/META-INF/spring.factories
  36. 4 0
      external/sso-service/build.gradle
  37. 1 1
      runtime/admin-server/src/main/resources/application.yml
  38. 1 1
      runtime/mobile-grpc-server/src/main/resources/application.yml
  39. 9 0
      runtime/open-grpc-server/build.gradle
  40. 6 0
      runtime/open-grpc-server/src/main/java/com/usoftchina/uu/open/grpc/OpenGrpcApplication.java
  41. 1 1
      runtime/open-grpc-server/src/main/resources/application.yml
  42. 4 4
      services/account-service/src/main/java/com/usoftchina/uu/account/service/impl/AccountServiceImpl.java
  43. 10 0
      services/app-service/src/main/java/com/usoftchina/uu/app/mapper/AppConfigMapper.java
  44. 44 24
      services/app-service/src/main/java/com/usoftchina/uu/app/service/impl/AppConfigServiceImpl.java
  45. 19 5
      services/app-service/src/main/resources/mapper/AppConfigMapper.xml
  46. 30 2
      services/message-service/src/main/java/com/usoftchina/uu/message/mapper/MessageHistoryMapper.java
  47. 27 0
      services/message-service/src/main/java/com/usoftchina/uu/message/mapper/MessageMapper.java
  48. 65 0
      services/message-service/src/main/java/com/usoftchina/uu/message/mapper/MessageViewMapper.java
  49. 110 11
      services/message-service/src/main/java/com/usoftchina/uu/message/service/impl/MessageServiceImpl.java
  50. 16 0
      services/message-service/src/main/resources/mapper/MessageHistoryMapper.xml
  51. 16 0
      services/message-service/src/main/resources/mapper/MessageMapper.xml
  52. 77 0
      services/message-service/src/main/resources/mapper/MessageViewMapper.xml
  53. 0 1
      services/mobile-grpc-service/src/main/java/com/usoftchina/uu/mobile/grpc/service/impl/MobileAppServiceImpl.java
  54. 9 1
      services/mobile-grpc-service/src/main/java/com/usoftchina/uu/mobile/grpc/service/impl/MobileMessageServiceImpl.java
  55. 1 1
      services/mobile-grpc-service/src/main/java/com/usoftchina/uu/mobile/grpc/util/MobileBeanMapper.java
  56. 50 2
      services/open-grpc-service/src/main/java/com/usoftchina/uu/open/grpc/interceptor/OpenSignatureInterceptor.java
  57. 76 2
      services/open-grpc-service/src/main/java/com/usoftchina/uu/open/grpc/service/impl/OpenAccountServiceImpl.java
  58. 147 9
      services/open-grpc-service/src/main/java/com/usoftchina/uu/open/grpc/service/impl/OpenAppServiceImpl.java
  59. 116 1
      services/open-grpc-service/src/main/java/com/usoftchina/uu/open/grpc/service/impl/OpenMessageServiceImpl.java
  60. 179 0
      services/open-grpc-service/src/main/java/com/usoftchina/uu/open/grpc/util/OpenBeanMapper.java
  61. 41 0
      services/open-grpc-service/src/main/java/com/usoftchina/uu/open/grpc/util/ResponseUtils.java
  62. 18 0
      services/open-grpc-service/src/main/java/com/usoftchina/uu/open/grpc/util/SignatureUtils.java
  63. 2 0
      settings.gradle
  64. 12 2
      shared/core/src/main/java/com/usoftchina/uu/exception/ExceptionCode.java

+ 9 - 1
README.md

@@ -51,6 +51,14 @@ gradlew build -x test
 | ---- | :----: | :----: | :----: | ---- |
 | redis | 10.1.81.82:6379 | | | 内存数据库 |
 
-> 部署
+> 本地编译
+
+```
+gradle build -x test
+# deploy
+gradle build uploadArchives -x test
+```
+
+> 测试环境部署
 
 * [jenkins](http://10.1.81.61:8080/job/uu/view/test) **账号** admin **密码** select123***

+ 24 - 0
apis/account-api/src/main/java/com/usoftchina/uu/account/context/PlatformHolder.java

@@ -0,0 +1,24 @@
+package com.usoftchina.uu.account.context;
+
+import com.alibaba.ttl.TransmittableThreadLocal;
+import com.usoftchina.uu.base.Platform;
+
+/**
+ * @author yingp
+ * @date 2019/4/18
+ */
+public class PlatformHolder {
+    private static final ThreadLocal<Platform> threadLocal = new TransmittableThreadLocal<>();
+
+    public static void set(Platform platform) {
+        threadLocal.set(platform);
+    }
+
+    public static Platform get() {
+        return threadLocal.get();
+    }
+
+    public static void clear() {
+        threadLocal.remove();
+    }
+}

+ 15 - 3
apis/app-api/src/main/java/com/usoftchina/uu/app/api/AppConfigApi.java

@@ -17,9 +17,9 @@ public interface AppConfigApi {
      * 保存
      *
      * @param appConfigDTO
-     * @return
+     * @return appConfig.id
      */
-    boolean save(AppConfigDTO appConfigDTO);
+    int save(AppConfigDTO appConfigDTO);
 
     /**
      * 批量保存
@@ -93,7 +93,7 @@ public interface AppConfigApi {
     List<AppGroupDTO> findByCompanyRelate(CompanyRelate companyRelate);
 
     /**
-     * 查找属于该公司的应用配置(包括私有配置 + 公有配置),结果进行分组
+     * 查找应用配置(包括私有配置 + 公有配置),结果进行分组;如果已分配或归属该公司,则标记enable = true
      * <pre>
      *     例如 uas系统里面获取全部配置,来进行启用、禁用、编辑等操作
      * </pre>
@@ -111,6 +111,18 @@ public interface AppConfigApi {
      */
     List<AppGroupDTO> findEnabledByScopeCompanyId(Long scopeCompanyId);
 
+    /**
+     * 查找指定平台的应用配置(包括私有配置 + 公有配置),结果进行分组;如果已分配或归属该公司,则标记enable = true
+     * <pre>
+     *     例如 uas系统里面获取全部配置,来进行启用、禁用、编辑等操作
+     * </pre>
+     *
+     * @param platform
+     * @param scopeCompanyId
+     * @return
+     */
+    List<CompanyAppGroupDTO> findByScopePlatformAndCompanyId(Platform platform, Long scopeCompanyId);
+
     /**
      * 查找指定平台的属于该公司的且已启用的应用配置(包括私有配置 + 公有配置),结果进行分组
      *

+ 93 - 2
apis/message-api/src/main/java/com/usoftchina/uu/message/api/MessageApi.java

@@ -72,7 +72,7 @@ public interface MessageApi {
     boolean setRead(List<Long> messageIds, Platform platform);
 
     /**
-     * 分页查找某个消息类的消息
+     * 分页查找某个消息类的未读消息
      *
      * @param pageRequest
      * @param code
@@ -80,5 +80,96 @@ public interface MessageApi {
      * @param companyId
      * @return
      */
-    PageInfo<MessageDTO> findByPageAndCode(PageRequest pageRequest, String code, Long accountId, Long companyId);
+    PageInfo<MessageDTO> findUnreadByPageAndCode(PageRequest pageRequest, String code, Long accountId, Long companyId);
+
+    /**
+     * 分页查找某个消息类的已读消息
+     *
+     * @param pageRequest
+     * @param code
+     * @param accountId
+     * @param companyId
+     * @return
+     */
+    PageInfo<MessageDTO> findReadByPageAndCode(PageRequest pageRequest, String code, Long accountId, Long companyId);
+
+    /**
+     * 分页查找某个消息类的全部消息
+     *
+     * @param pageRequest
+     * @param code
+     * @param accountId
+     * @param companyId
+     * @return
+     */
+    PageInfo<MessageDTO> findAllByPageAndCode(PageRequest pageRequest, String code, Long accountId, Long companyId);
+
+    /**
+     * 分页查找某个平台的未读消息
+     * <pre>未绑定企业<pre/>
+     *
+     * @param pageRequest
+     * @param platform
+     * @param accountId
+     * @return
+     */
+    PageInfo<MessageDTO> findUnreadByPageAndPlatform(PageRequest pageRequest, Platform platform, Long accountId);
+
+    /**
+     * 分页查找某个平台的已读消息
+     * <pre>未绑定企业<pre/>
+     *
+     * @param pageRequest
+     * @param platform
+     * @param accountId
+     * @return
+     */
+    PageInfo<MessageDTO> findReadByPageAndPlatform(PageRequest pageRequest, Platform platform, Long accountId);
+
+    /**
+     * 分页查找某个平台的全部消息
+     * <pre>未绑定企业<pre/>
+     *
+     * @param pageRequest
+     * @param platform
+     * @param accountId
+     * @return
+     */
+    PageInfo<MessageDTO> findAllByPageAndPlatform(PageRequest pageRequest, Platform platform, Long accountId);
+
+    /**
+     * 分页查找某个平台的未读消息
+     *
+     * @param pageRequest
+     * @param platform
+     * @param code
+     * @param accountId
+     * @param companyId
+     * @return
+     */
+    PageInfo<MessageDTO> findUnreadByPageAndCodeAndPlatform(PageRequest pageRequest, Platform platform, String code, Long accountId, Long companyId);
+
+    /**
+     * 分页查找某个平台的已读消息
+     *
+     * @param pageRequest
+     * @param platform
+     * @param code
+     * @param accountId
+     * @param companyId
+     * @return
+     */
+    PageInfo<MessageDTO> findReadByPageAndCodeAndPlatform(PageRequest pageRequest, Platform platform, String code, Long accountId, Long companyId);
+
+    /**
+     * 分页查找某个平台的全部消息
+     *
+     * @param pageRequest
+     * @param platform
+     * @param code
+     * @param accountId
+     * @param companyId
+     * @return
+     */
+    PageInfo<MessageDTO> findAllByPageAndCodeAndPlatform(PageRequest pageRequest, Platform platform, String code, Long accountId, Long companyId);
 }

+ 14 - 0
apis/message-api/src/main/java/com/usoftchina/uu/message/dto/MessageDTO.java

@@ -1,6 +1,7 @@
 package com.usoftchina.uu.message.dto;
 
 import java.io.Serializable;
+import java.util.Date;
 
 /**
  * @author yingp
@@ -24,6 +25,10 @@ public class MessageDTO implements Serializable {
      * 企业ID
      */
     protected Long companyId;
+    /**
+     * 阅读时间
+     */
+    private Date readTime;
 
     public Long getId() {
         return id;
@@ -65,6 +70,14 @@ public class MessageDTO implements Serializable {
         this.companyId = companyId;
     }
 
+    public Date getReadTime() {
+        return readTime;
+    }
+
+    public void setReadTime(Date readTime) {
+        this.readTime = readTime;
+    }
+
     @Override
     public String toString() {
         return "MessageDTO{" +
@@ -73,6 +86,7 @@ public class MessageDTO implements Serializable {
                 ", body='" + body + '\'' +
                 ", accountId=" + accountId +
                 ", companyId=" + companyId +
+                ", readTime=" + readTime +
                 '}';
     }
 }

+ 12 - 0
apis/message-api/src/main/java/com/usoftchina/uu/message/dto/SendMessageDTO.java

@@ -1,6 +1,8 @@
 package com.usoftchina.uu.message.dto;
 
 import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -25,6 +27,16 @@ public class SendMessageDTO implements Serializable {
      */
     private Long companyId;
 
+    public SendMessageDTO() {
+    }
+
+    public SendMessageDTO(String code, String body, Long accountId, Long companyId) {
+        this.code = code;
+        this.body = body;
+        this.accountIds = new ArrayList<>(Arrays.asList(new Long[]{accountId}));
+        this.companyId = companyId;
+    }
+
     public String getCode() {
         return code;
     }

+ 25 - 16
apis/open-grpc-api/src/main/proto/app.proto

@@ -13,22 +13,17 @@ service AppService {
     rpc getConfig (GetAppConfigRequest) returns (GetAppConfigResponse) {};
     // 启用/禁用 应用
     rpc setEnable (SetEnableRequest) returns (SetEnableResponse) {};
-    // 保存应用配置
-    rpc saveConfig (SaveConfigRequest) returns (SaveConfigResponse) {};
-    // 删除应用配置
-    rpc deleteConfig (DeleteConfigRequest) returns (DeleteCOnfigResponse) {};
+    // 新增自定义应用配置
+    rpc newConfig (NewConfigRequest) returns (NewConfigResponse) {};
+    // 修改应用配置(限定自定义的应用,标准的应用不允许修改)
+    rpc modifyConfig (ModifyConfigRequest) returns (ModifyConfigResponse) {};
+    // 删除应用配置(限定自定义的应用,标准的应用不允许删除)
+    rpc deleteConfig (DeleteConfigRequest) returns (DeleteConfigResponse) {};
 }
 
 // 获取应用配置请求参数
 message GetAppConfigRequest {
     Platform platform = 1;
-    enum Platform {
-        // uas、uas saas
-        UAS = 0;
-        // 简易版saas
-        SAAS = 1;
-        LUBAN = 2;
-    }
 }
 
 // 获取应用配置响应参数
@@ -48,13 +43,27 @@ message SetEnableResponse {
     ResponseHeader responseHeader = 1;
 }
 
-// 保存应用配置请求参数
-message SaveConfigRequest {
+// 新增应用配置请求参数
+message NewConfigRequest {
     AppConfig appConfig = 1;
+    // 分组
+    int32 groupId = 2;
 }
 
-// 保存应用配置响应参数
-message SaveConfigResponse {
+// 新增应用配置响应参数
+message NewConfigResponse {
+    ResponseHeader responseHeader = 1;
+    // 返回新增的应用ID
+    int32 appId = 2;
+}
+
+// 修改应用配置请求参数
+message ModifyConfigRequest {
+    AppConfig appConfig = 1;
+}
+
+// 修改应用配置响应参数
+message ModifyConfigResponse {
     ResponseHeader responseHeader = 1;
 }
 
@@ -64,6 +73,6 @@ message DeleteConfigRequest {
 }
 
 // 删除应用配置响应参数
-message DeleteCOnfigResponse {
+message DeleteConfigResponse {
     ResponseHeader responseHeader = 1;
 }

+ 21 - 3
apis/open-grpc-api/src/main/proto/base.proto

@@ -20,7 +20,7 @@ message Paging {
     // 总页数
     int32 totalPage = 1;
     // 总条数
-    int32 totalCount = 2;
+    int64 totalCount = 2;
     // 每页条数
     int32 pageSize = 3;
     // 当前页
@@ -59,8 +59,6 @@ message AppConfig {
     string webUrl = 7;
     // 是否启用
     bool enable = 8;
-    // 是否企业私有应用配置
-    bool private = 9;
 }
 
 // 未读消息统计
@@ -85,6 +83,8 @@ message MessageInfo {
         // 已读
         READ = 1;
     }
+    // 阅读时间
+    int64 readTime = 4;
 }
 
 // 账户
@@ -113,4 +113,22 @@ message Company {
     int64 id = 1;
     // 名称
     string name = 2;
+}
+
+// 第三方平台
+enum Platform {
+    // 全部平台通用,与平台无关
+    ALL = 0;
+    // UAS v1.0系统
+    UAS = 1;
+    // saas进销存版
+    SAAS_TRADE = 2;
+    // b2b
+    B2B = 3;
+    // 商城
+    MALL = 4;
+    // uu互联
+    UU = 5;
+    // UAS v2.0系统
+    UAS_2 = 6;
 }

+ 9 - 1
apis/open-grpc-api/src/main/proto/message.proto

@@ -25,6 +25,8 @@ message SendMessageRequest {
     string code = 1;
     // 消息内容
     string message = 2;
+    // 到指定用户
+    int64 accountId = 3;
 }
 
 // 发送消息响应参数
@@ -34,6 +36,8 @@ message SendMessageResponse {
 
 // 未读消息统计请求参数
 message GetUnreadMessageCountRequest {
+    // 指定用户(的消息)
+    int64 accountId = 1;
 }
 
 // 未读消息统计响应参数
@@ -48,8 +52,10 @@ message GetMessagesRequest {
     int32 pageSize = 1;
     // 当前页
     int32 pageNumber = 2;
+    // 消息编码
+    string code = 3;
     // 消息状态
-    Status status = 3;
+    Status status = 4;
 
     enum Status {
         // 未读
@@ -59,6 +65,8 @@ message GetMessagesRequest {
         // 全部
         ALL = 2;
     }
+    // 指定用户(的消息)
+    int64 accountId = 5;
 }
 
 // 获取消息响应参数

+ 1 - 0
external/README.md

@@ -12,5 +12,6 @@
 │  ├─baidu-push-feign-client------------------百度推送rest接口的feign封装  
 │  ├─sso-feign-client-------------------------sso.ubtob.com账户中心rest接口的feign封装
 │  ├─sso-service------------------------------sso.ubtob.com账户中心基于数据库的实现
+│  ├─open-sdk---------------------------------提供给第三方使用的sdk,已经与spring boot整合,开箱即用
 │  │
 ```

+ 5 - 0
external/baidu-push-feign-client/src/main/java/com/usoftchina/uu/baidu/push/dto/AndroidNotification.java

@@ -48,6 +48,11 @@ public class AndroidNotification implements Notification{
     private AndroidNotification() {
     }
 
+    public AndroidNotification(String title, String description) {
+        this.title = title;
+        this.description = description;
+    }
+
     private AndroidNotification(String title, String description, Integer notification_builder_id, Integer notification_basic_style, Integer open_type, String url, String pkg_content, String custom_content) {
         this.title = title;
         this.description = description;

+ 1 - 1
external/baidu-push-feign-client/src/test/resources/application.yml

@@ -1,6 +1,6 @@
 baidu:
   push:
-    base-url: http://api.tuisong.baidu.com
+    base-url: http://service.tuisong.baidu.com
     android:
       api-key: EmEVqG9NiKchcSbkoGkiyG2F2rp8YNmf
       secret-key: vys9xmWtx2Oerv83usNtU64OEWOpz0Gq

+ 5 - 0
external/open-sdk/build.gradle

@@ -0,0 +1,5 @@
+dependencies {
+    compile 'org.springframework.boot:spring-boot-starter'
+    compile project(':apis:open-grpc-api')
+    compile "$alibabaThreadLocal"
+}

+ 69 - 0
external/open-sdk/src/main/java/com/usoftchina/uu/open/sdk/base/Platform.java

@@ -0,0 +1,69 @@
+package com.usoftchina.uu.open.sdk.base;
+
+/**
+ * @author yingp
+ * @date 2019/4/19
+ */
+public enum Platform {
+    /**
+     * 未知
+     */
+    UNKNOWN(-1),
+
+    /**
+     * 全部平台通用,与平台无关
+     */
+    ALL(0),
+
+    /**
+     * UAS系统
+     */
+    UAS(1),
+
+    /**
+     * saas进销存版
+     */
+    SAAS_TRADE(2),
+
+    /**
+     * b2b
+     */
+    B2B(3),
+
+    /**
+     * 商城
+     */
+    MALL(4),
+    /**
+     * uu互联
+     */
+    UU(5);
+
+    private final int value;
+
+    Platform(int value) {
+        this.value = value;
+    }
+
+    public int getValue() {
+        return value;
+    }
+
+    public static Platform of(int value) {
+        for (Platform p : values()) {
+            if (p.value == value) {
+                return p;
+            }
+        }
+        return UNKNOWN;
+    }
+
+    public static Platform ofString(String platform) {
+        for (Platform p : values()) {
+            if (p.name().equalsIgnoreCase(platform)) {
+                return p;
+            }
+        }
+        return UNKNOWN;
+    }
+}

+ 28 - 0
external/open-sdk/src/main/java/com/usoftchina/uu/open/sdk/client/UuClient.java

@@ -0,0 +1,28 @@
+package com.usoftchina.uu.open.sdk.client;
+
+import com.usoftchina.uu.open.sdk.server.UuServerProperties;
+import io.grpc.ManagedChannel;
+import io.grpc.ManagedChannelBuilder;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author yingp
+ * @date 2019/4/12
+ */
+@Component
+public class UuClient {
+    private final ManagedChannel channel;
+
+    @Autowired
+    public UuClient(UuServerProperties serverProperties, UuClientProperties clientProperties) {
+        channel = ManagedChannelBuilder.forAddress(serverProperties.getHost(), serverProperties.getPort())
+                .intercept(new UuClientSignatureInterceptor(clientProperties))
+                .usePlaintext()
+                .build();
+    }
+
+    public ManagedChannel getChannel() {
+        return channel;
+    }
+}

+ 23 - 0
external/open-sdk/src/main/java/com/usoftchina/uu/open/sdk/client/UuClientProperties.java

@@ -0,0 +1,23 @@
+package com.usoftchina.uu.open.sdk.client;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+/**
+ * @author yingp
+ * @date 2019/4/12
+ */
+@ConfigurationProperties(prefix = "uu.client")
+public class UuClientProperties {
+    /**
+     * 第三方平台类型
+     */
+    private String platform;
+
+    public String getPlatform() {
+        return platform;
+    }
+
+    public void setPlatform(String platform) {
+        this.platform = platform;
+    }
+}

+ 63 - 0
external/open-sdk/src/main/java/com/usoftchina/uu/open/sdk/client/UuClientSignatureInterceptor.java

@@ -0,0 +1,63 @@
+package com.usoftchina.uu.open.sdk.client;
+
+import com.usoftchina.uu.open.sdk.base.Platform;
+import com.usoftchina.uu.open.sdk.context.UuSecretHolder;
+import com.usoftchina.uu.open.sdk.util.SignatureUtils;
+import io.grpc.*;
+
+/**
+ * 客户端请求前签名
+ *
+ * @author yingp
+ * @date 2019/4/19
+ */
+public class UuClientSignatureInterceptor implements ClientInterceptor {
+
+    private final Platform platform;
+
+    public UuClientSignatureInterceptor(UuClientProperties properties) {
+        this.platform = Platform.ofString(properties.getPlatform());
+    }
+
+    /**
+     * 第三方平台类型
+     */
+    private static Metadata.Key<String> PT_HEADER_KEY =
+            Metadata.Key.of("platform", Metadata.ASCII_STRING_MARSHALLER);
+    /**
+     * 签名时间戳
+     */
+    private static Metadata.Key<String> TS_HEADER_KEY =
+            Metadata.Key.of("timestamp", Metadata.ASCII_STRING_MARSHALLER);
+    /**
+     * 签名公司ID
+     */
+    private static Metadata.Key<String> CMP_HEADER_KEY =
+            Metadata.Key.of("companyId", Metadata.ASCII_STRING_MARSHALLER);
+    /**
+     * 签名串
+     */
+    private static Metadata.Key<String> SIGN_HEADER_KEY =
+            Metadata.Key.of("signature", Metadata.ASCII_STRING_MARSHALLER);
+
+    @Override
+    public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) {
+        return new ForwardingClientCall.SimpleForwardingClientCall<ReqT, RespT>(next.newCall(method, callOptions)) {
+
+            @Override
+            public void start(Listener<RespT> responseListener, Metadata headers) {
+                headers.put(PT_HEADER_KEY, String.valueOf(platform.getValue()));
+                if (null != UuSecretHolder.getCompanyId()) {
+                    headers.put(CMP_HEADER_KEY, String.valueOf(UuSecretHolder.getCompanyId()));
+                }
+                String timestamp = String.valueOf(System.currentTimeMillis());
+                headers.put(TS_HEADER_KEY, timestamp);
+                if (null != UuSecretHolder.getSecret()) {
+                    headers.put(SIGN_HEADER_KEY,
+                            SignatureUtils.sign(UuSecretHolder.getSecret(), timestamp));
+                }
+                super.start(responseListener, headers);
+            }
+        };
+    }
+}

+ 20 - 0
external/open-sdk/src/main/java/com/usoftchina/uu/open/sdk/config/UuAutoConfiguration.java

@@ -0,0 +1,20 @@
+package com.usoftchina.uu.open.sdk.config;
+
+import com.usoftchina.uu.open.sdk.client.UuClientProperties;
+import com.usoftchina.uu.open.sdk.server.UuServerProperties;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @author yingp
+ * @date 2019/4/19
+ */
+@Configuration
+@ComponentScan(basePackages = "com.usoftchina.uu.open.sdk")
+@EnableConfigurationProperties({
+        UuServerProperties.class,
+        UuClientProperties.class
+})
+public class UuAutoConfiguration {
+}

+ 40 - 0
external/open-sdk/src/main/java/com/usoftchina/uu/open/sdk/context/UuSecretHolder.java

@@ -0,0 +1,40 @@
+package com.usoftchina.uu.open.sdk.context;
+
+import com.alibaba.ttl.TransmittableThreadLocal;
+
+/**
+ * 动态的在专属线程里面设置公司ID、私钥
+ *
+ * @author yingp
+ * @date 2019/4/12
+ */
+public class UuSecretHolder {
+    private static final ThreadLocal<Long> localCompanyId = new TransmittableThreadLocal<>();
+    private static final ThreadLocal<String> localSecret = new TransmittableThreadLocal<>();
+
+    public static void set(Long companyId, String secret) {
+        setCompanyId(companyId);
+        setSecret(secret);
+    }
+
+    public static void setCompanyId(Long companyId) {
+        localCompanyId.set(companyId);
+    }
+
+    public static Long getCompanyId() {
+        return localCompanyId.get();
+    }
+
+    public static void setSecret(String secret) {
+        localSecret.set(secret);
+    }
+
+    public static String getSecret() {
+        return localSecret.get();
+    }
+
+    public static void clear() {
+        localCompanyId.remove();
+        localSecret.remove();
+    }
+}

+ 40 - 0
external/open-sdk/src/main/java/com/usoftchina/uu/open/sdk/crypto/Hex.java

@@ -0,0 +1,40 @@
+package com.usoftchina.uu.open.sdk.crypto;
+
+public final class Hex {
+	private static final char[] HEX = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+
+	public static char[] encode(byte[] bytes) {
+		int nBytes = bytes.length;
+		char[] result = new char[2 * nBytes];
+
+		int j = 0;
+		for (int i = 0; i < nBytes; ++i) {
+			result[(j++)] = HEX[((0xF0 & bytes[i]) >>> 4)];
+
+			result[(j++)] = HEX[(0xF & bytes[i])];
+		}
+
+		return result;
+	}
+
+	public static byte[] decode(CharSequence s) {
+		int nChars = s.length();
+
+		if (nChars % 2 != 0) {
+			throw new IllegalArgumentException("Hex-encoded string must have an even number of characters");
+		}
+
+		byte[] result = new byte[nChars / 2];
+
+		for (int i = 0; i < nChars; i += 2) {
+			int msb = Character.digit(s.charAt(i), 16);
+			int lsb = Character.digit(s.charAt(i + 1), 16);
+
+			if ((msb < 0) || (lsb < 0)) {
+				throw new IllegalArgumentException("Non-hex character in input: " + s);
+			}
+			result[(i / 2)] = (byte) (msb << 4 | lsb);
+		}
+		return result;
+	}
+}

+ 97 - 0
external/open-sdk/src/main/java/com/usoftchina/uu/open/sdk/crypto/HmacEncoder.java

@@ -0,0 +1,97 @@
+package com.usoftchina.uu.open.sdk.crypto;
+
+import javax.crypto.KeyGenerator;
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * Hash-based message authentication code,利用哈希算法,以一个密钥和一个消息为输入,生成一个消息摘要作为输出
+ * 
+ * @author yingp
+ *
+ */
+public class HmacEncoder {
+
+	private final String algorithm;
+
+	public HmacEncoder(String algorithm) {
+		this.algorithm = algorithm;
+	}
+
+	/**
+	 * 根据给定密钥生成算法创建密钥
+	 * 
+	 * @return 密钥
+	 * @throws RuntimeException
+	 *             当 {@link NoSuchAlgorithmException} 发生时
+	 */
+	public byte[] getKey() {
+		// 初始化KeyGenerator
+		KeyGenerator keyGenerator = null;
+		try {
+			keyGenerator = KeyGenerator.getInstance(algorithm);
+		} catch (NoSuchAlgorithmException e) {
+			throw new RuntimeException(e.getMessage());
+		}
+		// 产生密钥
+		SecretKey secretKey = keyGenerator.generateKey();
+		// 获得密钥
+		return secretKey.getEncoded();
+	}
+
+	/**
+	 * 转换密钥
+	 * 
+	 * @param key
+	 *            二进制密钥
+	 * @param algorithm
+	 *            密钥算法
+	 * @return 密钥
+	 */
+	private static Key toKey(byte[] key, String algorithm) {
+		// 生成密钥
+		return new SecretKeySpec(key, algorithm);
+	}
+
+	/**
+	 * 使用指定消息摘要算法计算消息摘要
+	 * 
+	 * @param data
+	 *            做消息摘要的数据
+	 * @param key
+	 *            密钥
+	 * @return 消息摘要(长度为16的字节数组)
+	 */
+	public byte[] encode(byte[] data, Key key) {
+		Mac mac = null;
+		try {
+			mac = Mac.getInstance(algorithm);
+			mac.init(key);
+		} catch (NoSuchAlgorithmException e) {
+			e.printStackTrace();
+			return new byte[0];
+		} catch (InvalidKeyException e) {
+			e.printStackTrace();
+			return new byte[0];
+		}
+		return mac.doFinal(data);
+	}
+
+	/**
+	 * 使用指定消息摘要算法计算消息摘要
+	 * 
+	 * @param data
+	 *            做消息摘要的数据
+	 * @param key
+	 *            密钥
+	 * @return 消息摘要(长度为16的字节数组)
+	 */
+	public byte[] encode(byte[] data, byte[] key) {
+		return encode(data, toKey(key, algorithm));
+	}
+
+}

+ 9 - 0
external/open-sdk/src/main/java/com/usoftchina/uu/open/sdk/crypto/HmacSHA256Encoder.java

@@ -0,0 +1,9 @@
+package com.usoftchina.uu.open.sdk.crypto;
+
+public class HmacSHA256Encoder extends HmacEncoder {
+
+	public HmacSHA256Encoder() {
+		super("HmacSHA256");
+	}
+
+}

+ 48 - 0
external/open-sdk/src/main/java/com/usoftchina/uu/open/sdk/crypto/HmacUtils.java

@@ -0,0 +1,48 @@
+package com.usoftchina.uu.open.sdk.crypto;
+
+/**
+ * Hmac加密工具
+ * 
+ * @author yingp
+ *
+ */
+public class HmacUtils {
+
+	private static HmacEncoder hmacEncoder;
+
+	/**
+	 * 默认约定密钥
+	 */
+	private final static byte[] key = { 104, 116, 116, 112, 58, 47, 47, 119, 119, 119, 46, 117, 98, 116, 111, 98, 46, 99, 111, 109, 47,
+			101, 114, 112, 47, 115, 97, 108, 101, 47, 111, 114, 100, 101, 114, 115, 63, 115, 111, 109, 101, 116, 104, 105, 110, 103 };
+
+	static {
+		// default algorithm: HmacSHA256
+		hmacEncoder = new HmacSHA256Encoder();
+	}
+
+	/**
+	 * 
+	 * @param message
+	 *            明文
+	 * @return 16进制密文
+	 */
+	public static String encode(Object message) {
+		byte[] encodeData = hmacEncoder.encode(String.valueOf(message).getBytes(), key);
+		return new String(Hex.encode(encodeData));
+	}
+
+	/**
+	 * 
+	 * @param message
+	 *            明文
+	 * @param key
+	 *            密钥
+	 * @return 16进制密文
+	 */
+	public static String encode(Object message, String key) {
+		byte[] encodeData = hmacEncoder.encode(String.valueOf(message).getBytes(), key.getBytes());
+		return new String(Hex.encode(encodeData));
+	}
+
+}

+ 132 - 0
external/open-sdk/src/main/java/com/usoftchina/uu/open/sdk/dto/UuAccount.java

@@ -0,0 +1,132 @@
+package com.usoftchina.uu.open.sdk.dto;
+
+import java.io.Serializable;
+
+/**
+ * 个人账户
+ *
+ * @author yingp
+ * @date 2019/4/16
+ */
+public class UuAccount implements Serializable {
+    private Long id;
+    /**
+     * 真实姓名(考虑是企业应用,非社交类型,不需要昵称)
+     */
+    private String realname;
+    /**
+     * 手机号
+     */
+    private String mobile;
+    /**
+     * 邮箱
+     */
+    private String email;
+    /**
+     * 头像
+     */
+    private String avatarUrl;
+    /**
+     * 性别
+     */
+    private Integer sex;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getRealname() {
+        return realname;
+    }
+
+    public void setRealname(String realname) {
+        this.realname = realname;
+    }
+
+    public String getMobile() {
+        return mobile;
+    }
+
+    public void setMobile(String mobile) {
+        this.mobile = mobile;
+    }
+
+    public String getEmail() {
+        return email;
+    }
+
+    public void setEmail(String email) {
+        this.email = email;
+    }
+
+    public String getAvatarUrl() {
+        return avatarUrl;
+    }
+
+    public void setAvatarUrl(String avatarUrl) {
+        this.avatarUrl = avatarUrl;
+    }
+
+    public Integer getSex() {
+        return sex;
+    }
+
+    public void setSex(Integer sex) {
+        this.sex = sex;
+    }
+
+    public boolean isFemale() {
+        return null != sex && Sex.valueOf(sex) == Sex.FEMALE;
+    }
+
+    @Override
+    public String toString() {
+        return "AccountDTO{" +
+                "id=" + id +
+                ", realname='" + realname + '\'' +
+                ", mobile='" + mobile + '\'' +
+                ", email='" + email + '\'' +
+                ", avatarUrl='" + avatarUrl + '\'' +
+                ", sex=" + sex +
+                '}';
+    }
+
+    public enum Sex {
+        /**
+         * 女
+         */
+        FEMALE(0),
+        /**
+         * 男
+         */
+        MALE(1);
+
+        private final int value;
+
+        Sex (int value) {
+            this.value = value;
+        }
+
+        public int getValue() {
+            return value;
+        }
+
+        public static Sex of(String name) {
+            String nameUpper = name.toUpperCase();
+            boolean isFemale = nameUpper.equals("F") || nameUpper.equals(FEMALE.name()) || nameUpper.equals("女");
+            return isFemale ? FEMALE : MALE;
+        }
+
+        public static Sex valueOf(int value) {
+            return FEMALE.value == value ? FEMALE : MALE;
+        }
+
+        public static boolean isFemale(int value) {
+            return valueOf(value) == FEMALE;
+        }
+    }
+}

+ 33 - 0
external/open-sdk/src/main/java/com/usoftchina/uu/open/sdk/dto/UuAccountLoginInfo.java

@@ -0,0 +1,33 @@
+package com.usoftchina.uu.open.sdk.dto;
+
+/**
+ * 登录信息
+ *
+ * @author yingp
+ * @date 2019/4/16
+ */
+public class UuAccountLoginInfo {
+    private UuAccount account;
+    private Long activeCompanyId;
+
+    public UuAccountLoginInfo(UuAccount account, Long companyId) {
+        this.account = account;
+        this.activeCompanyId = companyId;
+    }
+
+    public UuAccount getAccount() {
+        return account;
+    }
+
+    public void setAccount(UuAccount account) {
+        this.account = account;
+    }
+
+    public Long getActiveCompanyId() {
+        return activeCompanyId;
+    }
+
+    public void setActiveCompanyId(Long companyId) {
+        this.activeCompanyId = companyId;
+    }
+}

+ 71 - 0
external/open-sdk/src/main/java/com/usoftchina/uu/open/sdk/dto/UuMessage.java

@@ -0,0 +1,71 @@
+package com.usoftchina.uu.open.sdk.dto;
+
+import java.util.Date;
+
+/**
+ * @author yingp
+ * @date 2019/4/19
+ */
+public class UuMessage {
+
+    private Long id;
+    /**
+     * 消息内容
+     */
+    private String body;
+    /**
+     * 是否阅读
+     */
+    private Boolean read;
+    /**
+     * 阅读时间
+     */
+    private Date readTime;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getBody() {
+        return body;
+    }
+
+    public void setBody(String body) {
+        this.body = body;
+    }
+
+    public Boolean getRead() {
+        return read;
+    }
+
+    public void setRead(Boolean read) {
+        this.read = read;
+    }
+
+    public Date getReadTime() {
+        return readTime;
+    }
+
+    public void setReadTime(Date readTime) {
+        this.readTime = readTime;
+    }
+
+    public enum Status {
+        /**
+         * 已读
+         */
+        READ,
+        /**
+         * 未读
+         */
+        UNREAD,
+        /**
+         * 全部
+         */
+        ALL
+    }
+}

+ 65 - 0
external/open-sdk/src/main/java/com/usoftchina/uu/open/sdk/dto/UuPage.java

@@ -0,0 +1,65 @@
+package com.usoftchina.uu.open.sdk.dto;
+
+import com.usoftchina.uu.open.grpc.api.Paging;
+
+import java.util.List;
+
+/**
+ * @author yingp
+ * @date 2019/4/12
+ */
+public class UuPage<T> {
+    private int pageNumber;
+    private int pageSize;
+    private long totalCount;
+    private int totalPage;
+    private List<T> data;
+
+    public UuPage(Paging paging, List<T> data) {
+        this.pageNumber = paging.getPageNumber();
+        this.pageSize = paging.getPageSize();
+        this.totalCount = paging.getTotalCount();
+        this.totalPage = paging.getTotalPage();
+        this.data = data;
+    }
+
+    public int getPageNumber() {
+        return pageNumber;
+    }
+
+    public void setPageNumber(int pageNumber) {
+        this.pageNumber = pageNumber;
+    }
+
+    public int getPageSize() {
+        return pageSize;
+    }
+
+    public void setPageSize(int pageSize) {
+        this.pageSize = pageSize;
+    }
+
+    public long getTotalCount() {
+        return totalCount;
+    }
+
+    public void setTotalCount(long totalCount) {
+        this.totalCount = totalCount;
+    }
+
+    public int getTotalPage() {
+        return totalPage;
+    }
+
+    public void setTotalPage(int totalPage) {
+        this.totalPage = totalPage;
+    }
+
+    public List<T> getData() {
+        return data;
+    }
+
+    public void setData(List<T> data) {
+        this.data = data;
+    }
+}

+ 31 - 0
external/open-sdk/src/main/java/com/usoftchina/uu/open/sdk/dto/UuUnreadMessageCount.java

@@ -0,0 +1,31 @@
+package com.usoftchina.uu.open.sdk.dto;
+
+/**
+ * @author yingp
+ * @date 2019/4/12
+ */
+public class UuUnreadMessageCount {
+    private String code;
+    private Integer count;
+
+    public UuUnreadMessageCount(String code, Integer count) {
+        this.code = code;
+        this.count = count;
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    public void setCode(String code) {
+        this.code = code;
+    }
+
+    public Integer getCount() {
+        return count;
+    }
+
+    public void setCount(Integer count) {
+        this.count = count;
+    }
+}

+ 35 - 0
external/open-sdk/src/main/java/com/usoftchina/uu/open/sdk/exception/UuException.java

@@ -0,0 +1,35 @@
+package com.usoftchina.uu.open.sdk.exception;
+
+import com.usoftchina.uu.open.grpc.api.ResponseHeader;
+import io.grpc.StatusRuntimeException;
+
+/**
+ * 业务异常
+ *
+ * @author yingp
+ * @date 2018/9/29
+ */
+public class UuException extends RuntimeException{
+    private int code;
+    private String message;
+
+    public UuException(int code, String message, Object... args) {
+        super(String.format(message, args));
+        this.code = code;
+        this.message = String.format(message, args);
+    }
+
+    public UuException(ResponseHeader header) {
+        this(header.getCode(), header.getMessage());
+    }
+
+    public UuException(StatusRuntimeException e) {
+        // StatusRuntimeException的错误码从 1 ~ 16,为防止与ResponseHeader返回的错误码冲突,这里乘以1000处理
+        this(e.getStatus().getCode().value() + 1000, e.getStatus().getCode().name() + " " + e.getStatus().getDescription());
+    }
+
+    @Override
+    public String toString() {
+        return "UuException [message=" + message + ", code=" + code + "]";
+    }
+}

+ 29 - 0
external/open-sdk/src/main/java/com/usoftchina/uu/open/sdk/server/UuServerProperties.java

@@ -0,0 +1,29 @@
+package com.usoftchina.uu.open.sdk.server;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+/**
+ * @author yingp
+ * @date 2019/4/12
+ */
+@ConfigurationProperties(prefix = "uu.server")
+public class UuServerProperties {
+    private String host;
+    private int port;
+
+    public String getHost() {
+        return host;
+    }
+
+    public void setHost(String host) {
+        this.host = host;
+    }
+
+    public int getPort() {
+        return port;
+    }
+
+    public void setPort(int port) {
+        this.port = port;
+    }
+}

+ 75 - 0
external/open-sdk/src/main/java/com/usoftchina/uu/open/sdk/service/UuAccountService.java

@@ -0,0 +1,75 @@
+package com.usoftchina.uu.open.sdk.service;
+
+import com.usoftchina.uu.open.grpc.api.*;
+import com.usoftchina.uu.open.sdk.dto.UuAccount;
+import com.usoftchina.uu.open.sdk.dto.UuAccountLoginInfo;
+import com.usoftchina.uu.open.sdk.exception.UuException;
+import com.usoftchina.uu.open.sdk.client.UuClient;
+import com.usoftchina.uu.open.sdk.util.UuBeanMapper;
+import io.grpc.StatusRuntimeException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ * 账户服务
+ *
+ * @author yingp
+ * @date 2019/4/12
+ */
+@Component
+public class UuAccountService {
+    private final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub;
+
+    @Autowired
+    public UuAccountService(UuClient client) {
+        this.accountServiceBlockingStub = AccountServiceGrpc.newBlockingStub(client.getChannel());
+    }
+
+    /**
+     * 按token取用户登录信息
+     *
+     * @param token
+     * @return
+     */
+    public UuAccountLoginInfo getInfoByToken(String token) {
+        GetAccountInfoByTokenRequest.Builder builder = GetAccountInfoByTokenRequest.newBuilder();
+        builder.setToken(token);
+        try {
+            GetAccountInfoByTokenResponse response = accountServiceBlockingStub.getInfoByToken(builder.build());
+            ResponseHeader header = response.getResponseHeader();
+            if (header.getSuccess()) {
+                return new UuAccountLoginInfo(UuBeanMapper.map(response.getAccount()),
+                        response.getActiveCompanyId());
+            } else {
+                throw new UuException(header);
+            }
+        } catch (StatusRuntimeException e) {
+            throw new UuException(e);
+        }
+    }
+
+    /**
+     * 保存用户基本信息
+     * <pre>不包括密码、手机号、邮箱</pre>
+     *
+     * @param account
+     */
+    public void saveInfo(UuAccount account) {
+        SaveAccountInfoRequest.Builder builder = SaveAccountInfoRequest.newBuilder();
+        builder.setId(account.getId())
+                .setAvatarUrl(account.getAvatarUrl())
+                .setRealname(account.getRealname());
+        if (null != account.getSex()) {
+            builder.setSex(account.isFemale() ? AccountInfo.Sex.FEMALE : AccountInfo.Sex.MALE);
+        }
+        try {
+            SaveAccountInfoResponse response = accountServiceBlockingStub.saveInfo(builder.build());
+            ResponseHeader header = response.getResponseHeader();
+            if (!header.getSuccess()) {
+                throw new UuException(header);
+            }
+        } catch (StatusRuntimeException e) {
+            throw new UuException(e);
+        }
+    }
+}

+ 127 - 0
external/open-sdk/src/main/java/com/usoftchina/uu/open/sdk/service/UuMessageService.java

@@ -0,0 +1,127 @@
+package com.usoftchina.uu.open.sdk.service;
+
+import com.usoftchina.uu.open.grpc.api.*;
+import com.usoftchina.uu.open.sdk.dto.UuMessage;
+import com.usoftchina.uu.open.sdk.dto.UuPage;
+import com.usoftchina.uu.open.sdk.dto.UuUnreadMessageCount;
+import com.usoftchina.uu.open.sdk.exception.UuException;
+import com.usoftchina.uu.open.sdk.client.UuClient;
+import com.usoftchina.uu.open.sdk.util.UuBeanMapper;
+import io.grpc.StatusRuntimeException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * 消息服务
+ *
+ * @author yingp
+ * @date 2019/4/12
+ */
+@Component
+public class UuMessageService {
+    private final MessageServiceGrpc.MessageServiceBlockingStub messageServiceBlockingStub;
+
+    @Autowired
+    public UuMessageService(UuClient client) {
+        this.messageServiceBlockingStub = MessageServiceGrpc.newBlockingStub(client.getChannel());
+    }
+
+    /**
+     * 发送消息
+     *
+     * @param code      消息类
+     * @param body      消息内容
+     * @param accountId 接收账号
+     */
+    public void send(String code, String body, Long accountId) {
+        SendMessageRequest.Builder builder = SendMessageRequest.newBuilder();
+        try {
+            builder.setCode(code)
+                    .setMessage(body)
+                    .setAccountId(accountId);
+            SendMessageResponse response = messageServiceBlockingStub.send(builder.build());
+            ResponseHeader header = response.getResponseHeader();
+            if (!header.getSuccess()) {
+                throw new UuException(header);
+            }
+        } catch (StatusRuntimeException e) {
+            throw new UuException(e);
+        }
+    }
+
+    /**
+     * 未读消息统计
+     *
+     * @param accountId 指定接收用户的
+     * @return
+     */
+    public List<UuUnreadMessageCount> getUnreadCount(Long accountId) {
+        GetUnreadMessageCountRequest.Builder builder = GetUnreadMessageCountRequest.newBuilder();
+        try {
+            builder.setAccountId(accountId);
+            GetUnreadMessageCountResponse response = messageServiceBlockingStub.getUnreadCount(builder.build());
+            ResponseHeader header = response.getResponseHeader();
+            if (header.getSuccess()) {
+                if (response.getUnreadCount() > 0) {
+                    return UuBeanMapper.mapMessageCounts(response.getUnreadList());
+                } else {
+                    return null;
+                }
+            } else {
+                throw new UuException(header);
+            }
+        } catch (StatusRuntimeException e) {
+            throw new UuException(e);
+        }
+    }
+
+    /**
+     * 分页获取消息
+     *
+     * @param code      消息类型
+     * @param accountId 指定接收用户的
+     * @param page      页码
+     * @param pageSize  每页消息数量
+     * @param status    消息状态
+     */
+    public UuPage<UuMessage> getMessages(String code, Long accountId, int page, int pageSize, UuMessage.Status status) {
+        GetMessagesRequest.Builder builder = GetMessagesRequest.newBuilder();
+        try {
+            builder.setCode(code)
+                    .setAccountId(accountId)
+                    .setPageNumber(page)
+                    .setPageSize(pageSize);
+            GetMessagesResponse response = messageServiceBlockingStub.getMessages(builder.build());
+            ResponseHeader header = response.getResponseHeader();
+            if (header.getSuccess()) {
+                List<UuMessage> messages = UuBeanMapper.mapMessages(response.getMessageList());
+                return new UuPage(response.getPaging(), messages);
+            } else {
+                throw new UuException(header);
+            }
+        } catch (StatusRuntimeException e) {
+            throw new UuException(e);
+        }
+    }
+
+    /**
+     * 设置消息为已读
+     *
+     * @param messageId
+     */
+    public void setRead(Long messageId) {
+        SetMessageReadRequest.Builder builder = SetMessageReadRequest.newBuilder();
+        try {
+            builder.setMessageId(messageId);
+            SetMessageReadResponse response = messageServiceBlockingStub.setRead(builder.build());
+            ResponseHeader header = response.getResponseHeader();
+            if (!header.getSuccess()) {
+                throw new UuException(header);
+            }
+        } catch (StatusRuntimeException e) {
+            throw new UuException(e);
+        }
+    }
+}

+ 18 - 0
external/open-sdk/src/main/java/com/usoftchina/uu/open/sdk/util/SignatureUtils.java

@@ -0,0 +1,18 @@
+package com.usoftchina.uu.open.sdk.util;
+
+import com.usoftchina.uu.open.sdk.crypto.HmacUtils;
+
+/**
+ * @author yingp
+ * @date 2019/4/18
+ */
+public class SignatureUtils {
+
+    public static String sign(String secretKey, String timestamp) {
+        return HmacUtils.encode(timestamp, secretKey);
+    }
+
+    public static boolean verify(String secretKey, String timestamp, String signature) {
+        return HmacUtils.encode(timestamp, secretKey).equals(signature);
+    }
+}

+ 78 - 0
external/open-sdk/src/main/java/com/usoftchina/uu/open/sdk/util/UuBeanMapper.java

@@ -0,0 +1,78 @@
+package com.usoftchina.uu.open.sdk.util;
+
+import com.usoftchina.uu.open.grpc.api.AccountInfo;
+import com.usoftchina.uu.open.grpc.api.MessageInfo;
+import com.usoftchina.uu.open.grpc.api.UnreadMessageCount;
+import com.usoftchina.uu.open.sdk.dto.UuAccount;
+import com.usoftchina.uu.open.sdk.dto.UuMessage;
+import com.usoftchina.uu.open.sdk.dto.UuUnreadMessageCount;
+
+import java.util.Date;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * @author yingp
+ * @date 2019/4/12
+ */
+public class UuBeanMapper {
+
+    /**
+     * 远程AccountInfo转本地UuAccount对象
+     *
+     * @param account
+     * @return
+     */
+    public static UuAccount map(AccountInfo account) {
+        UuAccount uuAccount = new UuAccount();
+        uuAccount.setId(account.getId());
+        uuAccount.setAvatarUrl(account.getAvatarUrl());
+        uuAccount.setEmail(account.getEmail());
+        uuAccount.setMobile(account.getMobile());
+        uuAccount.setRealname(account.getRealname());
+        UuAccount.Sex sex = AccountInfo.Sex.MALE == account.getSex() ? UuAccount.Sex.MALE : UuAccount.Sex.FEMALE;
+        uuAccount.setSex(sex.getValue());
+        return uuAccount;
+    }
+
+    public static UuUnreadMessageCount map(UnreadMessageCount count) {
+        return new UuUnreadMessageCount(count.getCode(), count.getCount());
+    }
+
+    /**
+     * 远程UnreadMessageCount转本地UuUnreadMessageCount对象
+     *
+     * @param counts
+     * @return
+     */
+    public static List<UuUnreadMessageCount> mapMessageCounts(List<UnreadMessageCount> counts) {
+        return counts.stream().map(count -> map(count)).collect(Collectors.toList());
+    }
+
+    /**
+     * 远程MessageInfo转本地UuMessage对象
+     *
+     * @param message
+     * @return
+     */
+    public static UuMessage map(MessageInfo message) {
+        UuMessage uuMessage = new UuMessage();
+        uuMessage.setId(message.getId());
+        uuMessage.setBody(message.getBody());
+        uuMessage.setRead(MessageInfo.Status.READ == message.getStatus());
+        if (uuMessage.getRead()) {
+            uuMessage.setReadTime(new Date(message.getReadTime()));
+        }
+        return uuMessage;
+    }
+
+    /**
+     * 远程UnreadMessageCount转本地UuUnreadMessageCount对象
+     *
+     * @param messages
+     * @return
+     */
+    public static List<UuMessage> mapMessages(List<MessageInfo> messages) {
+        return messages.stream().map(message -> map(message)).collect(Collectors.toList());
+    }
+}

+ 3 - 0
external/open-sdk/src/main/resources/META-INF/spring.factories

@@ -0,0 +1,3 @@
+# Auto Configuration
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+com.usoftchina.uu.open.sdk.config.UuAutoConfiguration

+ 4 - 0
external/sso-service/build.gradle

@@ -0,0 +1,4 @@
+dependencies {
+    compile "$mybatisSpringBoot"
+    compile project(':shared:core')
+}

+ 1 - 1
runtime/admin-server/src/main/resources/application.yml

@@ -5,7 +5,7 @@ sso:
 
 baidu:
   push:
-    base-url: http://api.tuisong.baidu.com
+    base-url: http://service.tuisong.baidu.com
     android:
       api-key: EmEVqG9NiKchcSbkoGkiyG2F2rp8YNmf
       secret-key: vys9xmWtx2Oerv83usNtU64OEWOpz0Gq

+ 1 - 1
runtime/mobile-grpc-server/src/main/resources/application.yml

@@ -5,7 +5,7 @@ sso:
 
 baidu:
   push:
-    base-url: http://api.tuisong.baidu.com
+    base-url: http://service.tuisong.baidu.com
     android:
       api-key: EmEVqG9NiKchcSbkoGkiyG2F2rp8YNmf
       secret-key: vys9xmWtx2Oerv83usNtU64OEWOpz0Gq

+ 9 - 0
runtime/open-grpc-server/build.gradle

@@ -1,4 +1,5 @@
 apply plugin: 'org.springframework.boot'
+apply plugin: 'com.palantir.docker'
 
 dependencies {
     compile "$grpcSpringBoot"
@@ -8,4 +9,12 @@ dependencies {
     compile project(':services:app-service')
     compile project(':services:home-service')
     compile project(':services:message-service')
+    compile 'org.springframework.boot:spring-boot-starter-data-redis'
+    testCompile 'org.springframework.boot:spring-boot-starter-test'
 }
+
+docker {
+    dockerfile file("${projectDir}/src/main/docker/Dockerfile")
+    name "${dockerRegistry}/${dockerGroup}/${project.name}:${project.version}"
+    files "${buildDir}/libs/${project.name}-${project.version}.jar"
+}.dependsOn build

+ 6 - 0
runtime/open-grpc-server/src/main/java/com/usoftchina/uu/open/grpc/OpenGrpcApplication.java

@@ -3,7 +3,10 @@ package com.usoftchina.uu.open.grpc;
 import org.mybatis.spring.annotation.MapperScan;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cache.annotation.EnableCaching;
 import org.springframework.cloud.openfeign.EnableFeignClients;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.annotation.EnableScheduling;
 import org.springframework.transaction.annotation.EnableTransactionManagement;
 
 /**
@@ -19,6 +22,9 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
         "com.usoftchina.uu.message.mapper"
 })
 @EnableTransactionManagement
+@EnableCaching
+@EnableScheduling
+@EnableAsync
 public class OpenGrpcApplication {
 
     public static void main(String[] args) {

+ 1 - 1
runtime/open-grpc-server/src/main/resources/application.yml

@@ -5,7 +5,7 @@ sso:
 
 baidu:
   push:
-    base-url: http://api.tuisong.baidu.com
+    base-url: http://service.tuisong.baidu.com
     android:
       api-key: EmEVqG9NiKchcSbkoGkiyG2F2rp8YNmf
       secret-key: vys9xmWtx2Oerv83usNtU64OEWOpz0Gq

+ 4 - 4
services/account-service/src/main/java/com/usoftchina/uu/account/service/impl/AccountServiceImpl.java

@@ -135,7 +135,7 @@ public class AccountServiceImpl implements AccountApi {
                 return true;
             }
         } else {
-            logger.error("SSO Server Error, api: getUserByMobile, code: {}, message: {}", result.getErrCode(), result.getErrMsg());
+            logger.error("SSO Server Error, service: getUserByMobile, code: {}, message: {}", result.getErrCode(), result.getErrMsg());
             ExceptionCode.UNKNOWN_ERROR.occur("账户中心异常: %s", result.getErrMsg());
         }
         return false;
@@ -164,7 +164,7 @@ public class AccountServiceImpl implements AccountApi {
             // 使用账户中心UU作为个人账户ID
             accountDTO.setId(user.getUserUU());
         } else {
-            logger.error("SSO Server Error, api: save, code: {}, message: {}", result.getErrCode(), result.getErrMsg());
+            logger.error("SSO Server Error, service: save, code: {}, message: {}", result.getErrCode(), result.getErrMsg());
             ExceptionCode.UNKNOWN_ERROR.occur("账户中心异常: %s", result.getErrMsg());
         }
         return accountDTO;
@@ -184,7 +184,7 @@ public class AccountServiceImpl implements AccountApi {
                 return convertSsoUserToAccount(user);
             }
         } else {
-            logger.error("SSO Server Error, api: getUserByUu, code: {}, message: {}", result.getErrCode(), result.getErrMsg());
+            logger.error("SSO Server Error, service: getUserByUu, code: {}, message: {}", result.getErrCode(), result.getErrMsg());
             // 此处可以忽略账户中心的错误
         }
         return null;
@@ -204,7 +204,7 @@ public class AccountServiceImpl implements AccountApi {
                 return convertSsoUserToAccount(user);
             }
         } else {
-            logger.error("SSO Server Error, api: getUserByMobile, code: {}, message: {}", result.getErrCode(), result.getErrMsg());
+            logger.error("SSO Server Error, service: getUserByMobile, code: {}, message: {}", result.getErrCode(), result.getErrMsg());
             // 此处可以忽略账户中心的错误
         }
         return null;

+ 10 - 0
services/app-service/src/main/java/com/usoftchina/uu/app/mapper/AppConfigMapper.java

@@ -140,6 +140,16 @@ public interface AppConfigMapper {
      */
     List<AppConfigVO> selectEnabledByScopeCompanyId(@Param("scopeCompanyId") Long scopeCompanyId);
 
+    /**
+     * 按所属平台+公司查找配置
+     *
+     * @param scopePlatform
+     * @param scopeCompanyId
+     * @return
+     */
+    List<CompanyAppConfigVO> selectByScopePlatformAndCompanyId(@Param("scopePlatform") Integer scopePlatform,
+                                                               @Param("scopeCompanyId") Long scopeCompanyId);
+
     /**
      * 按所属平台+公司查找已启用配置
      *

+ 44 - 24
services/app-service/src/main/java/com/usoftchina/uu/app/service/impl/AppConfigServiceImpl.java

@@ -34,9 +34,9 @@ public class AppConfigServiceImpl implements AppConfigApi {
     private AppCompanyMapper appCompanyMapper;
 
     @Override
-    public boolean save(AppConfigDTO appConfigDTO) {
+    public int save(AppConfigDTO appConfigDTO) {
         AppConfig appConfig = BeanMapper.map(appConfigDTO, AppConfig.class);
-        return appConfigMapper.insertSelective(appConfig) > 0;
+        return appConfigMapper.insertSelective(appConfig);
     }
 
     @Override
@@ -100,7 +100,7 @@ public class AppConfigServiceImpl implements AppConfigApi {
     public List<AppGroupDTO> findByScopePlatform(Platform platform) {
         List<AppConfigVO> appConfigVOList = appConfigMapper.selectByScopePlatform(platform.getValue());
         if (!CollectionUtils.isEmpty(appConfigVOList)) {
-            return group(appConfigVOList);
+            return groupAppConfig(appConfigVOList);
         }
         return null;
     }
@@ -111,7 +111,7 @@ public class AppConfigServiceImpl implements AppConfigApi {
      * @param appConfigVOList
      * @return
      */
-    private List<AppGroupDTO> group(List<AppConfigVO> appConfigVOList) {
+    private List<AppGroupDTO> groupAppConfig(List<AppConfigVO> appConfigVOList) {
         Map<Integer, AppGroupDTO> appGroupDTOMap = new HashMap<>(1);
         List<AppGroupDTO> appGroupDTOList = new ArrayList<>();
         appConfigVOList.forEach(appConfigVO -> {
@@ -133,7 +133,17 @@ public class AppConfigServiceImpl implements AppConfigApi {
     public List<AppGroupDTO> findByCompanyRelate(CompanyRelate companyRelate) {
         List<AppConfigVO> appConfigVOList = appConfigMapper.selectByCompanyRelate(companyRelate.getValue());
         if (!CollectionUtils.isEmpty(appConfigVOList)) {
-            return group(appConfigVOList);
+            return groupAppConfig(appConfigVOList);
+        }
+        return null;
+    }
+
+    @Override
+    public List<CompanyAppGroupDTO> findByScopePlatformAndCompanyId(Platform platform, Long scopeCompanyId) {
+        List<CompanyAppConfigVO> companyAppConfigVOList = appConfigMapper.selectByScopePlatformAndCompanyId(
+                platform.getValue(), scopeCompanyId);
+        if (!CollectionUtils.isEmpty(companyAppConfigVOList)) {
+            return groupCompanyAppConfig(companyAppConfigVOList);
         }
         return null;
     }
@@ -143,7 +153,7 @@ public class AppConfigServiceImpl implements AppConfigApi {
         List<AppConfigVO> appConfigVOList = appConfigMapper.selectEnabledByScopePlatformAndCompanyId(
                 platform.getValue(), scopeCompanyId);
         if (!CollectionUtils.isEmpty(appConfigVOList)) {
-            return group(appConfigVOList);
+            return groupAppConfig(appConfigVOList);
         }
         return null;
     }
@@ -157,7 +167,7 @@ public class AppConfigServiceImpl implements AppConfigApi {
                         return !appConfigVO.companyRelate().onlyCompany() &&
                                 null == appConfigVO.getScopeCompanyId();
                     }).collect(Collectors.toList());
-            return group(appConfigVOList);
+            return groupAppConfig(appConfigVOList);
         }
         return null;
     }
@@ -171,7 +181,7 @@ public class AppConfigServiceImpl implements AppConfigApi {
                         return !appConfigVO.companyRelate().onlyCompany() &&
                                 null == appConfigVO.getScopeCompanyId();
                     }).collect(Collectors.toList());
-            return group(appConfigVOList);
+            return groupAppConfig(appConfigVOList);
         }
         return null;
     }
@@ -180,30 +190,40 @@ public class AppConfigServiceImpl implements AppConfigApi {
     public List<CompanyAppGroupDTO> findByScopeCompanyId(Long scopeCompanyId) {
         List<CompanyAppConfigVO> companyAppConfigVOList = appConfigMapper.selectByScopeCompanyId(scopeCompanyId);
         if (!CollectionUtils.isEmpty(companyAppConfigVOList)) {
-            Map<Integer, CompanyAppGroupDTO> appGroupDTOMap = new HashMap<>();
-            List<CompanyAppGroupDTO> appGroupDTOList = new ArrayList<>();
-            companyAppConfigVOList.forEach(companyAppConfigVO -> {
-                CompanyAppGroupDTO appGroupDTO;
-                if (!appGroupDTOMap.containsKey(companyAppConfigVO.getGroupId())) {
-                    appGroupDTO = new CompanyAppGroupDTO(companyAppConfigVO.getGroupId(), companyAppConfigVO.getGroupName());
-                    appGroupDTOMap.put(appGroupDTO.getId(), appGroupDTO);
-                    appGroupDTOList.add(appGroupDTO);
-                } else {
-                    appGroupDTO = appGroupDTOMap.get(companyAppConfigVO.getGroupId());
-                }
-                CompanyAppConfigDTO appConfigDTO = BeanMapper.map(companyAppConfigVO, CompanyAppConfigDTO.class);
-                appGroupDTO.getAppConfigList().add(appConfigDTO);
-            });
-            return appGroupDTOList;
+            return groupCompanyAppConfig(companyAppConfigVOList);
         }
         return null;
     }
 
+    /**
+     * 按appGroup分组
+     *
+     * @param companyAppConfigVOList
+     * @return
+     */
+    private List<CompanyAppGroupDTO> groupCompanyAppConfig(List<CompanyAppConfigVO> companyAppConfigVOList) {
+        Map<Integer, CompanyAppGroupDTO> appGroupDTOMap = new HashMap<>();
+        List<CompanyAppGroupDTO> appGroupDTOList = new ArrayList<>();
+        companyAppConfigVOList.forEach(companyAppConfigVO -> {
+            CompanyAppGroupDTO appGroupDTO;
+            if (!appGroupDTOMap.containsKey(companyAppConfigVO.getGroupId())) {
+                appGroupDTO = new CompanyAppGroupDTO(companyAppConfigVO.getGroupId(), companyAppConfigVO.getGroupName());
+                appGroupDTOMap.put(appGroupDTO.getId(), appGroupDTO);
+                appGroupDTOList.add(appGroupDTO);
+            } else {
+                appGroupDTO = appGroupDTOMap.get(companyAppConfigVO.getGroupId());
+            }
+            CompanyAppConfigDTO appConfigDTO = BeanMapper.map(companyAppConfigVO, CompanyAppConfigDTO.class);
+            appGroupDTO.getAppConfigList().add(appConfigDTO);
+        });
+        return appGroupDTOList;
+    }
+
     @Override
     public List<AppGroupDTO> findEnabledByScopeCompanyId(Long scopeCompanyId) {
         List<AppConfigVO> appConfigVOList = appConfigMapper.selectEnabledByScopeCompanyId(scopeCompanyId);
         if (!CollectionUtils.isEmpty(appConfigVOList)) {
-            return group(appConfigVOList);
+            return groupAppConfig(appConfigVOList);
         }
         return null;
     }

+ 19 - 5
services/app-service/src/main/resources/mapper/AppConfigMapper.xml

@@ -321,10 +321,11 @@
     </resultMap>
     <select id="selectByScopeCompanyId" resultMap="CompanyAppConfigVOResultMap" parameterType="java.lang.Long">
         select <include refid="appConfigVOColumns"/>,
-        case when app_company.company_id is null then true else false enable
+        case when app_company.company_id is null then false else true enable
         from app_group, app_config left join app_company
-        on app_config.id=app_company.app_id and app_company.company_id=#{scopeCompanyId,jdbcType=BIGINT}
+        on app_config.id=app_company.app_id
         where app_config.group_id=app_group.id and
+        (app_company.company_id is null or app_company.company_id=#{scopeCompanyId,jdbcType=BIGINT}) and
         (app_config.scope_company_id is null or app_config.scope_company_id=#{scopeCompanyId,jdbcType=BIGINT})
         order by app_group.order_num, app_config.order_num
     </select>
@@ -334,19 +335,32 @@
         (app_config.scope_company_id is null or app_config.scope_company_id=#{scopeCompanyId,jdbcType=BIGINT})
         order by app_config.order_num
     </select>
+    <select id="selectByScopePlatformAndCompanyId" resultMap="CompanyAppConfigVOResultMap">
+        select <include refid="appConfigVOColumns"/>,
+        case when app_company.company_id is null then false else true enable
+        from app_group, app_config left join app_company
+        on app_config.id=app_company.app_id
+        where app_config.group_id=app_group.id and
+        (app_company.company_id is null or app_company.company_id=#{scopeCompanyId,jdbcType=BIGINT}) and
+        app_config.scope_platform=#{scopePlatform,jdbcType=INTEGER} and
+        (app_config.scope_company_id is null or app_config.scope_company_id=#{scopeCompanyId,jdbcType=BIGINT})
+        order by app_group.order_num, app_config.order_num
+    </select>
     <select id="selectEnabledByScopePlatformAndCompanyId" resultMap="AppConfigVOResultMap">
         select <include refid="appConfigVOColumns"/> from app_group,app_config,app_company where app_config.group_id=app_group.id and
         app_config.id=app_company.app_id and app_company.company_id=#{scopeCompanyId,jdbcType=BIGINT} and
         app_config.scope_platform=#{scopePlatform,jdbcType=INTEGER} and
         (app_config.scope_company_id is null or app_config.scope_company_id=#{scopeCompanyId,jdbcType=BIGINT})
-        order by app_config.order_num
+        order by app_group.order_num, app_config.order_num
     </select>
     <select id="selectByCompanyRelate" resultMap="AppConfigVOResultMap" parameterType="java.lang.Integer">
         select <include refid="appConfigVOColumns"/> from app_group,app_config where
-        app_config.group_id=app_group.id and company_relate=#{companyRelate,jdbcType=INTEGER} order by app_config.order_num
+        app_config.group_id=app_group.id and company_relate=#{companyRelate,jdbcType=INTEGER}
+        order by app_group.order_num, app_config.order_num
     </select>
     <select id="selectAll" resultMap="AppConfigVOResultMap">
         select <include refid="appConfigVOColumns"/> from app_group,app_config where
-        app_config.group_id=app_group.id order by app_config.order_num
+        app_config.group_id=app_group.id
+        order by app_group.order_num, app_config.order_num
     </select>
 </mapper>

+ 30 - 2
services/message-service/src/main/java/com/usoftchina/uu/message/mapper/MessageHistoryMapper.java

@@ -37,6 +37,7 @@ public interface MessageHistoryMapper {
 
     /**
      * 按消息类+账户查找
+     * <pre>账户未绑定企业</pre>
      *
      * @param code
      * @param accountId
@@ -53,8 +54,8 @@ public interface MessageHistoryMapper {
      * @return
      */
     List<MessageHistory> selectByCodeAndAccountIdAndCompanyId(@Param("code") String code,
-                                                       @Param("accountId") Long accountId,
-                                                       @Param("companyId") Long companyId);
+                                                              @Param("accountId") Long accountId,
+                                                              @Param("companyId") Long companyId);
 
     /**
      * 删除
@@ -63,4 +64,31 @@ public interface MessageHistoryMapper {
      * @return
      */
     int deleteByPrimaryKey(@Param("id") Long id);
+
+    /**
+     * 按消息所属平台+账户查找
+     * <pre>
+     *     没绑定公司时使用
+     * </pre>
+     *
+     * @param platform
+     * @param accountId
+     * @return
+     */
+    List<MessageHistory> selectByPlatformAndAccountId(@Param("platform") int platform,
+                                                      @Param("accountId") Long accountId);
+
+    /**
+     * 按消息所属平台+账户查找
+     *
+     * @param platform
+     * @param code
+     * @param accountId
+     * @param companyId
+     * @return
+     */
+    List<MessageHistory> selectByPlatformAndCodeAndAccountIdAndCompanyId(@Param("platform") int platform,
+                                                                  @Param("code") String code,
+                                                                  @Param("accountId") Long accountId,
+                                                                  @Param("companyId") Long companyId);
 }

+ 27 - 0
services/message-service/src/main/java/com/usoftchina/uu/message/mapper/MessageMapper.java

@@ -97,6 +97,33 @@ public interface MessageMapper {
                                                        @Param("accountId") Long accountId,
                                                        @Param("companyId") Long companyId);
 
+    /**
+     * 按消息所属平台+账户查找
+     * <pre>
+     *     没绑定公司时使用
+     * </pre>
+     *
+     * @param platform
+     * @param accountId
+     * @return
+     */
+    List<Message> selectByPlatformAndAccountId(@Param("platform") int platform,
+                                                       @Param("accountId") Long accountId);
+
+    /**
+     * 按消息所属平台+账户+公司查找
+     *
+     * @param platform
+     * @param code
+     * @param accountId
+     * @param companyId
+     * @return
+     */
+    List<Message> selectByPlatformAndCodeAndAccountIdAndCompanyId(@Param("platform") int platform,
+                                                           @Param("code") String code,
+                                                           @Param("accountId") Long accountId,
+                                                           @Param("companyId") Long companyId);
+
     /**
      * 删除
      *

+ 65 - 0
services/message-service/src/main/java/com/usoftchina/uu/message/mapper/MessageViewMapper.java

@@ -0,0 +1,65 @@
+package com.usoftchina.uu.message.mapper;
+
+import com.usoftchina.uu.message.po.MessageHistory;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * @author yingp
+ * @date 2019/4/23
+ */
+public interface MessageViewMapper {
+    /**
+     * 按消息类+账户查找
+     * <pre>取已读+未读</pre>
+     * <pre>账户未绑定企业</pre>
+     *
+     * @param code
+     * @param accountId
+     * @return
+     */
+    List<MessageHistory> selectByCodeAndAccountId(@Param("code") String code, @Param("accountId") Long accountId);
+
+    /**
+     * 按消息类+账户+公司查找
+     * <pre>取已读+未读</pre>
+     *
+     * @param code
+     * @param accountId
+     * @param companyId
+     * @return
+     */
+    List<MessageHistory> selectByCodeAndAccountIdAndCompanyId(@Param("code") String code,
+                                                              @Param("accountId") Long accountId,
+                                                              @Param("companyId") Long companyId);
+
+    /**
+     * 按消息所属平台+账户查找
+     * <pre>取已读+未读</pre>
+     * <pre>
+     *     没绑定公司时使用
+     * </pre>
+     *
+     * @param platform
+     * @param accountId
+     * @return
+     */
+    List<MessageHistory> selectByPlatformAndAccountId(@Param("platform") int platform,
+                                                      @Param("accountId") Long accountId);
+
+    /**
+     * 按消息所属平台+账户查找
+     * <pre>取已读+未读</pre>
+     *
+     * @param platform
+     * @param code
+     * @param accountId
+     * @param companyId
+     * @return
+     */
+    List<MessageHistory> selectByPlatformAndCodeAndAccountIdAndCompanyId(@Param("platform") int platform,
+                                                                  @Param("code") String code,
+                                                                  @Param("accountId") Long accountId,
+                                                                  @Param("companyId") Long companyId);
+}

+ 110 - 11
services/message-service/src/main/java/com/usoftchina/uu/message/service/impl/MessageServiceImpl.java

@@ -17,6 +17,7 @@ import com.usoftchina.uu.message.event.MessageCreatedEvent;
 import com.usoftchina.uu.message.event.MessageReadEvent;
 import com.usoftchina.uu.message.mapper.MessageHistoryMapper;
 import com.usoftchina.uu.message.mapper.MessageMapper;
+import com.usoftchina.uu.message.mapper.MessageViewMapper;
 import com.usoftchina.uu.message.po.Message;
 import com.usoftchina.uu.message.po.MessageHistory;
 import com.usoftchina.uu.page.PageRequest;
@@ -43,6 +44,9 @@ public class MessageServiceImpl implements MessageApi {
     @Autowired
     private MessageHistoryMapper messageHistoryMapper;
 
+    @Autowired
+    private MessageViewMapper messageViewMapper;
+
     @Autowired
     private MessageConfigApi messageConfigApi;
 
@@ -57,7 +61,7 @@ public class MessageServiceImpl implements MessageApi {
      *
      * @param sendMessageDTO
      * @return
-     * @see com.usoftchina.uu.message.base.CompanyRelate
+     * @see com.usoftchina.uu.base.CompanyRelate
      */
     private List<Message> fromSendMessageDTO(final SendMessageDTO sendMessageDTO) {
         MessageConfigDTO messageConfigDTO = messageConfigApi.findByCode(sendMessageDTO.getCode());
@@ -187,21 +191,33 @@ public class MessageServiceImpl implements MessageApi {
         return false;
     }
 
+    /**
+     * 校验消息配置
+     *
+     * @param messageConfigDTO
+     * @param companyId
+     */
+    private void checkCompanyRelate(MessageConfigDTO messageConfigDTO, Long companyId) {
+        if (messageConfigDTO.companyRelate().onlyCompany() && null == companyId) {
+            // 仅针对企业的消息配置
+            ExceptionCode.MESSAGE_MUST_HAS_COMPANY_ID.occur();
+        }
+        if (null != messageConfigDTO.getScopeCompanyId() &&
+                !messageConfigDTO.getScopeCompanyId().equals(companyId)) {
+            // 企业私有的消息配置
+            ExceptionCode.MESSAGE_CODE_ILLEGAL.occur();
+        }
+    }
+
     @Override
-    public PageInfo<MessageDTO> findByPageAndCode(PageRequest pageRequest, String code, Long accountId, Long companyId) {
+    public PageInfo<MessageDTO> findUnreadByPageAndCode(PageRequest pageRequest, String code, Long accountId, Long companyId) {
         MessageConfigDTO messageConfigDTO = messageConfigApi.findByCode(code);
         if (null != messageConfigDTO) {
             PageHelper.startPage(pageRequest.getNumber(), pageRequest.getSize());
-            if (messageConfigDTO.companyRelate().onlyCompany() && null == companyId) {
-                // 仅针对企业的消息配置
-                ExceptionCode.MESSAGE_MUST_HAS_COMPANY_ID.occur();
-            }
-            if (null != messageConfigDTO.getScopeCompanyId() &&
-                    !messageConfigDTO.getScopeCompanyId().equals(companyId)) {
-                // 企业私有的消息配置
-                ExceptionCode.MESSAGE_CODE_ILLEGAL.occur();
-            }
+            // 校验消息配置
+            checkCompanyRelate(messageConfigDTO, companyId);
             List<Message> messageList;
+            // 查找未读消息
             if (messageConfigDTO.companyRelate().onlyPersonal()) {
                 messageList = messageMapper.selectByCodeAndAccountId(code, accountId);
             } else {
@@ -211,4 +227,87 @@ public class MessageServiceImpl implements MessageApi {
         }
         return null;
     }
+
+    @Override
+    public PageInfo<MessageDTO> findReadByPageAndCode(PageRequest pageRequest, String code, Long accountId, Long companyId) {
+        MessageConfigDTO messageConfigDTO = messageConfigApi.findByCode(code);
+        if (null != messageConfigDTO) {
+            PageHelper.startPage(pageRequest.getNumber(), pageRequest.getSize());
+            // 校验消息配置
+            checkCompanyRelate(messageConfigDTO, companyId);
+            List<MessageHistory> messageList;
+            // 查找已读消息
+            if (messageConfigDTO.companyRelate().onlyPersonal()) {
+                messageList = messageHistoryMapper.selectByCodeAndAccountId(code, accountId);
+            } else {
+                messageList = messageHistoryMapper.selectByCodeAndAccountIdAndCompanyId(code, accountId, companyId);
+            }
+            return new PageInfo<>(BeanMapper.mapList(messageList, MessageDTO.class));
+        }
+        return null;
+    }
+
+    @Override
+    public PageInfo<MessageDTO> findAllByPageAndCode(PageRequest pageRequest, String code, Long accountId, Long companyId) {
+        MessageConfigDTO messageConfigDTO = messageConfigApi.findByCode(code);
+        if (null != messageConfigDTO) {
+            PageHelper.startPage(pageRequest.getNumber(), pageRequest.getSize());
+            // 校验消息配置
+            checkCompanyRelate(messageConfigDTO, companyId);
+            List<MessageHistory> messageList;
+            // 查找已读+未读消息
+            if (messageConfigDTO.companyRelate().onlyPersonal()) {
+                messageList = messageViewMapper.selectByCodeAndAccountId(code, accountId);
+            } else {
+                messageList = messageViewMapper.selectByCodeAndAccountIdAndCompanyId(code, accountId, companyId);
+            }
+            return new PageInfo<>(BeanMapper.mapList(messageList, MessageDTO.class));
+        }
+        return null;
+    }
+
+    @Override
+    public PageInfo<MessageDTO> findUnreadByPageAndPlatform(PageRequest pageRequest, Platform platform, Long accountId) {
+        PageHelper.startPage(pageRequest.getNumber(), pageRequest.getSize());
+        List<Message> messageList = messageMapper.selectByPlatformAndAccountId(platform.getValue(), accountId);
+        return new PageInfo<>(BeanMapper.mapList(messageList, MessageDTO.class));
+    }
+
+    @Override
+    public PageInfo<MessageDTO> findReadByPageAndPlatform(PageRequest pageRequest, Platform platform, Long accountId) {
+        PageHelper.startPage(pageRequest.getNumber(), pageRequest.getSize());
+        List<MessageHistory> messageList = messageHistoryMapper.selectByPlatformAndAccountId(platform.getValue(), accountId);
+        return new PageInfo<>(BeanMapper.mapList(messageList, MessageDTO.class));
+    }
+
+    @Override
+    public PageInfo<MessageDTO> findAllByPageAndPlatform(PageRequest pageRequest, Platform platform, Long accountId) {
+        PageHelper.startPage(pageRequest.getNumber(), pageRequest.getSize());
+        List<MessageHistory> messageList = messageViewMapper.selectByPlatformAndAccountId(platform.getValue(), accountId);
+        return new PageInfo<>(BeanMapper.mapList(messageList, MessageDTO.class));
+    }
+
+    @Override
+    public PageInfo<MessageDTO> findUnreadByPageAndCodeAndPlatform(PageRequest pageRequest, Platform platform, String code, Long accountId, Long companyId) {
+        PageHelper.startPage(pageRequest.getNumber(), pageRequest.getSize());
+        List<Message> messageList = messageMapper.selectByPlatformAndCodeAndAccountIdAndCompanyId(platform.getValue(),
+                code, accountId, companyId);
+        return new PageInfo<>(BeanMapper.mapList(messageList, MessageDTO.class));
+    }
+
+    @Override
+    public PageInfo<MessageDTO> findReadByPageAndCodeAndPlatform(PageRequest pageRequest, Platform platform, String code, Long accountId, Long companyId) {
+        PageHelper.startPage(pageRequest.getNumber(), pageRequest.getSize());
+        List<MessageHistory> messageList = messageHistoryMapper.selectByPlatformAndCodeAndAccountIdAndCompanyId(platform.getValue(),
+                code, accountId, companyId);
+        return new PageInfo<>(BeanMapper.mapList(messageList, MessageDTO.class));
+    }
+
+    @Override
+    public PageInfo<MessageDTO> findAllByPageAndCodeAndPlatform(PageRequest pageRequest, Platform platform, String code, Long accountId, Long companyId) {
+        PageHelper.startPage(pageRequest.getNumber(), pageRequest.getSize());
+        List<MessageHistory> messageList = messageViewMapper.selectByPlatformAndCodeAndAccountIdAndCompanyId(platform.getValue(),
+                code, accountId, companyId);
+        return new PageInfo<>(BeanMapper.mapList(messageList, MessageDTO.class));
+    }
 }

+ 16 - 0
services/message-service/src/main/resources/mapper/MessageHistoryMapper.xml

@@ -179,9 +179,25 @@
     <select id="selectByCodeAndAccountId" resultMap="BaseResultMap">
         select * from message_history where code=#{code,jdbcType=VARCHAR} and
         account_id=#{accountId,jdbcType=BIGINT}
+        order by id
     </select>
     <select id="selectByCodeAndAccountIdAndCompanyId" resultMap="BaseResultMap">
         select * from message_history where code=#{code,jdbcType=VARCHAR} and
         account_id=#{accountId,jdbcType=BIGINT} and company_id=#{companyId,jdbcType=BIGINT}
+        order by id
+    </select>
+    <select id="selectByPlatformAndAccountId" resultMap="BaseResultMap">
+        select message_history.*
+        from message_history,message_config where message_history.code = message_config.code and
+        message_config.scope_platform=#{platform,jdbcType=INTEGER} and
+        message_history.account_id=#{accountId,jdbcType=BIGINT} order by id
+    </select>
+    <select id="selectByPlatformAndCodeAndAccountIdAndCompanyId" resultMap="BaseResultMap">
+        select message_history.*
+        from message_history,message_config where message_history.code = message_config.code and
+        message_config.scope_platform=#{platform,jdbcType=INTEGER} and
+        message_config.code=#{code,jdbcType=VARCHAR} and
+        message_history.account_id=#{accountId,jdbcType=BIGINT} and
+        message_history.company_id=#{companyId,jdbcType=BIGINT} order by id
     </select>
 </mapper>

+ 16 - 0
services/message-service/src/main/resources/mapper/MessageMapper.xml

@@ -198,4 +198,20 @@
     <select id="selectByPrimaryKeys" resultMap="BaseResultMap" parameterType="java.lang.String">
         select * from message_unread where id in (#{ids,jdbcType=VARCHAR})
     </select>
+    <select id="selectByPlatformAndAccountId" resultMap="BaseResultMap">
+        select message_unread.* from message_unread,message_config
+        where message_unread.code = message_config.code and
+        message_config.scope_platform=#{platform,jdbcType=INTEGER} and
+        message_unread.account_id=#{accountId,jdbcType=BIGINT}
+        order by message_unread.id
+    </select>
+    <select id="selectByPlatformAndCodeAndAccountIdAndCompanyId" resultMap="BaseResultMap">
+        select message_unread.* from message_unread,message_config
+        where message_unread.code = message_config.code and
+        message_config.scope_platform=#{platform,jdbcType=INTEGER} and
+        message_config.code=#{code,jdbcType=VARCHAR} and
+        message_unread.account_id=#{accountId,jdbcType=BIGINT} and
+        message_unread.company_id=#{companyId,jdbcType=BIGINT}
+        order by message_unread.id
+    </select>
 </mapper>

+ 77 - 0
services/message-service/src/main/resources/mapper/MessageViewMapper.xml

@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
+<mapper namespace="com.usoftchina.uu.message.mapper.MessageViewMapper">
+    <resultMap id="BaseResultMap" type="com.usoftchina.uu.message.po.MessageHistory">
+        <id column="id" jdbcType="BIGINT" property="id"/>
+        <result column="code" jdbcType="VARCHAR" property="code"/>
+        <result column="body" jdbcType="VARCHAR" property="body"/>
+        <result column="account_id" jdbcType="BIGINT" property="accountId"/>
+        <result column="company_id" jdbcType="BIGINT" property="companyId"/>
+        <result column="create_time" jdbcType="TIMESTAMP" property="createTime"/>
+        <result column="push_time" jdbcType="TIMESTAMP" property="pushTime"/>
+        <result column="push_error_code" jdbcType="INTEGER" property="pushErrorCode"/>
+        <result column="push_error_msg" jdbcType="VARCHAR" property="pushErrorMsg"/>
+        <result column="read_time" jdbcType="TIMESTAMP" property="readTime"/>
+        <result column="read_platform" jdbcType="INTEGER" property="readPlatform"/>
+    </resultMap>
+    <select id="selectByCodeAndAccountId" resultMap="BaseResultMap">
+        select * from (
+        select id,code,body,account_id,company_id,create_time,push_time,push_error_code,push_error_msg,read_time,read_platform
+        from message_history where code=#{code,jdbcType=VARCHAR} and account_id=#{accountId,jdbcType=BIGINT}
+        union all
+        select id,code,body,account_id,company_id,create_time,push_time,push_error_code,push_error_msg,null read_time,null read_platform
+        from message_unread where code=#{code,jdbcType=VARCHAR} and account_id=#{accountId,jdbcType=BIGINT}
+        ) order by id
+    </select>
+    <select id="selectByCodeAndAccountIdAndCompanyId" resultMap="BaseResultMap">
+        select * from (
+        select id,code,body,account_id,company_id,create_time,push_time,push_error_code,push_error_msg,read_time,read_platform
+        from message_history where code=#{code,jdbcType=VARCHAR} and
+        account_id=#{accountId,jdbcType=BIGINT} and company_id=#{companyId,jdbcType=BIGINT}
+        union all
+        select id,code,body,account_id,company_id,create_time,push_time,push_error_code,push_error_msg,null read_time,null read_platform
+        from message_unread where code=#{code,jdbcType=VARCHAR} and
+        account_id=#{accountId,jdbcType=BIGINT} and company_id=#{companyId,jdbcType=BIGINT}
+        ) order by id
+    </select>
+    <select id="selectByPlatformAndAccountId" resultMap="BaseResultMap">
+        select * from (
+        select message_history.id,message_history.code,message_history.body,message_history.account_id,
+        message_history.company_id,message_history.create_time,message_history.push_time,
+        message_history.push_error_code,message_history.push_error_msg,message_history.read_time,
+        message_history.read_platform
+        from message_history,message_config where message_history.code = message_config.code and
+        message_config.scope_platform=#{platform,jdbcType=INTEGER} and
+        message_history.account_id=#{accountId,jdbcType=BIGINT}
+        union all
+        select message_unread.id,message_unread.code,message_unread.body,message_unread.account_id,
+        message_unread.company_id,message_unread.create_time,message_unread.push_time,
+        message_unread.push_error_code,message_unread.push_error_msg,null read_time,null read_platform
+        from message_unread,message_config where message_unread.code = message_config.code and
+        message_config.scope_platform=#{platform,jdbcType=INTEGER} and
+        message_unread.account_id=#{accountId,jdbcType=BIGINT}
+        ) order by id
+    </select>
+    <select id="selectByPlatformAndCodeAndAccountIdAndCompanyId" resultMap="BaseResultMap">
+        select * from (
+        select message_history.id,message_history.code,message_history.body,message_history.account_id,
+        message_history.company_id,message_history.create_time,message_history.push_time,
+        message_history.push_error_code,message_history.push_error_msg,message_history.read_time,
+        message_history.read_platform
+        from message_history,message_config where message_history.code = message_config.code and
+        message_config.scope_platform=#{platform,jdbcType=INTEGER} and
+        message_config.code=#{code,jdbcType=VARCHAR} and
+        message_history.account_id=#{accountId,jdbcType=BIGINT} and
+        message_history.company_id=#{companyId,jdbcType=BIGINT}
+        union all
+        select message_unread.id,message_unread.code,message_unread.body,message_unread.account_id,
+        message_unread.company_id,message_unread.create_time,message_unread.push_time,
+        message_unread.push_error_code,message_unread.push_error_msg,null read_time,null read_platform
+        from message_unread,message_config where message_unread.code = message_config.code and
+        message_config.scope_platform=#{platform,jdbcType=INTEGER} and
+        message_config.code=#{code,jdbcType=VARCHAR} and
+        message_unread.account_id=#{accountId,jdbcType=BIGINT} and
+        message_unread.company_id=#{companyId,jdbcType=BIGINT}
+        ) order by id
+    </select>
+</mapper>

+ 0 - 1
services/mobile-grpc-service/src/main/java/com/usoftchina/uu/mobile/grpc/service/impl/MobileAppServiceImpl.java

@@ -33,7 +33,6 @@ public class MobileAppServiceImpl extends AppServiceGrpc.AppServiceImplBase{
     public void getConfig(GetAppConfigRequest request, StreamObserver<GetAppConfigResponse> responseObserver) {
         GetAppConfigResponse.Builder builder = GetAppConfigResponse.newBuilder();
         try {
-
             Long companyId = TokenHolder.get().getCompanyId();
             List<AppGroupDTO> appGroupDTOList = appConfigApi.findEnabled(companyId);
             if (!CollectionUtils.isEmpty(appGroupDTOList)) {

+ 9 - 1
services/mobile-grpc-service/src/main/java/com/usoftchina/uu/mobile/grpc/service/impl/MobileMessageServiceImpl.java

@@ -84,7 +84,15 @@ public class MobileMessageServiceImpl extends MessageServiceGrpc.MessageServiceI
             Long accountId = TokenHolder.get().getAccountId();
             Long companyId = TokenHolder.get().getCompanyId();
             PageRequest pageRequest = new PageRequest(request.getPageNumber(), request.getPageSize());
-            PageInfo<MessageDTO> pageInfo = messageApi.findByPageAndCode(pageRequest, request.getCode(), accountId, companyId);
+            PageInfo<MessageDTO> pageInfo = null;
+            // 获取消息
+            if (request.getStatus() == GetMessagesRequest.Status.UNREAD) {
+                pageInfo = messageApi.findUnreadByPageAndCode(pageRequest, request.getCode(), accountId, companyId);
+            } else if (request.getStatus() == GetMessagesRequest.Status.READ) {
+                pageInfo = messageApi.findReadByPageAndCode(pageRequest, request.getCode(), accountId, companyId);
+            } else {
+                pageInfo = messageApi.findAllByPageAndCode(pageRequest, request.getCode(), accountId, companyId);
+            }
             builder.setPaging(MobileBeanMapper.map(pageInfo));
             List<MessageDTO> messageDTOList = pageInfo.getList();
             if (!CollectionUtils.isEmpty(messageDTOList)) {

+ 1 - 1
services/mobile-grpc-service/src/main/java/com/usoftchina/uu/mobile/grpc/util/MobileBeanMapper.java

@@ -239,7 +239,7 @@ public class MobileBeanMapper {
         if (null != messageDTO.getId()) {
             builder.setId(messageDTO.getId());
         }
-        builder.setStatus(MessageInfo.Status.UNREAD);
+        builder.setStatus(null != messageDTO.getReadTime() ? MessageInfo.Status.READ : MessageInfo.Status.UNREAD);
         return builder.build();
     }
 

+ 50 - 2
services/open-grpc-service/src/main/java/com/usoftchina/uu/open/grpc/interceptor/OpenSignatureInterceptor.java

@@ -1,8 +1,15 @@
 package com.usoftchina.uu.open.grpc.interceptor;
 
+import com.usoftchina.uu.account.api.CompanyApi;
+import com.usoftchina.uu.account.context.CompanyHolder;
+import com.usoftchina.uu.account.context.PlatformHolder;
+import com.usoftchina.uu.account.dto.CompanyDTO;
+import com.usoftchina.uu.base.Platform;
+import com.usoftchina.uu.open.grpc.util.SignatureUtils;
 import io.grpc.*;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
 /**
@@ -14,6 +21,11 @@ import org.springframework.stereotype.Component;
 @Component
 public class OpenSignatureInterceptor implements ServerInterceptor {
     private final static Logger logger = LoggerFactory.getLogger(OpenSignatureInterceptor.class);
+    /**
+     * 第三方平台类型
+     */
+    private static Metadata.Key<String> PT_HEADER_KEY =
+            Metadata.Key.of("platform", Metadata.ASCII_STRING_MARSHALLER);
     /**
      * 签名时间戳
      */
@@ -30,13 +42,49 @@ public class OpenSignatureInterceptor implements ServerInterceptor {
     private static Metadata.Key<String> SIGN_HEADER_KEY =
             Metadata.Key.of("signature", Metadata.ASCII_STRING_MARSHALLER);
 
+    @Autowired
+    private CompanyApi companyApi;
+
     @Override
     public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
             ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) {
         if (headers.containsKey(SIGN_HEADER_KEY) && headers.containsKey(CMP_HEADER_KEY)
                 && headers.containsKey(TS_HEADER_KEY)) {
-            // TODO
-            return next.startCall(call, headers);
+            try {
+                // 第三方公司平台类型
+                if (headers.containsKey(PT_HEADER_KEY)) {
+                    PlatformHolder.set(Platform.of(Integer.valueOf(headers.get(PT_HEADER_KEY))));
+                } else {
+                    PlatformHolder.set(Platform.ALL);
+                }
+                // 第三方公司ID
+                Long companyId = Long.parseLong(headers.get(CMP_HEADER_KEY));
+                CompanyDTO companyDTO = companyApi.findByPrimaryKey(companyId);
+                if (null != companyDTO) {
+                    // 验证签名
+                    boolean verified = SignatureUtils.verify(companyDTO.getSecretKey(),
+                            headers.get(TS_HEADER_KEY), headers.get(SIGN_HEADER_KEY));
+                    if (verified) {
+                        CompanyHolder.set(companyDTO);
+                        return next.startCall(call, headers);
+                    } else {
+                        logger.error("Invalid Signature On Call {}", call.getMethodDescriptor().getFullMethodName());
+                        call.close(Status.INVALID_ARGUMENT.withDescription("非法签名信息"), headers);
+                        return new ServerCall.Listener<ReqT>() {
+                        };
+                    }
+                } else {
+                    logger.error("Invalid Company ID On Call {}", call.getMethodDescriptor().getFullMethodName());
+                    call.close(Status.INVALID_ARGUMENT.withDescription("无效公司ID"), headers);
+                    return new ServerCall.Listener<ReqT>() {
+                    };
+                }
+            } catch (Exception e) {
+                logger.error("UnKnown Error On Call {}", call.getMethodDescriptor().getFullMethodName(), e);
+                call.close(Status.UNKNOWN.withDescription(e.getMessage()), headers);
+                return new ServerCall.Listener<ReqT>() {
+                };
+            }
         } else {
             logger.error("No Signature Provided On Call {}", call.getMethodDescriptor().getFullMethodName());
             call.close(Status.DATA_LOSS.withDescription("没有签名信息"), headers);

+ 76 - 2
services/open-grpc-service/src/main/java/com/usoftchina/uu/open/grpc/service/impl/OpenAccountServiceImpl.java

@@ -1,12 +1,86 @@
 package com.usoftchina.uu.open.grpc.service.impl;
 
-import com.usoftchina.uu.open.grpc.api.AccountServiceGrpc;
+import com.usoftchina.uu.account.api.AccountApi;
+import com.usoftchina.uu.account.api.TokenApi;
+import com.usoftchina.uu.account.dto.AccountDTO;
+import com.usoftchina.uu.account.dto.TokenDTO;
+import com.usoftchina.uu.exception.ExceptionCode;
+import com.usoftchina.uu.open.grpc.api.*;
+import com.usoftchina.uu.open.grpc.interceptor.OpenSignatureInterceptor;
+import com.usoftchina.uu.open.grpc.util.OpenBeanMapper;
+import com.usoftchina.uu.open.grpc.util.ResponseUtils;
+import io.grpc.stub.StreamObserver;
 import org.lognet.springboot.grpc.GRpcService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
 
 /**
  * @author yingp
  * @date 2019/4/17
  */
-@GRpcService
+@GRpcService(interceptors = OpenSignatureInterceptor.class)
 public class OpenAccountServiceImpl extends AccountServiceGrpc.AccountServiceImplBase {
+
+    private final static Logger logger = LoggerFactory.getLogger(OpenAccountServiceImpl.class);
+
+    @Autowired
+    private TokenApi tokenApi;
+
+    @Autowired
+    private AccountApi accountApi;
+
+    @Override
+    public void getInfoByToken(GetAccountInfoByTokenRequest request, StreamObserver<GetAccountInfoByTokenResponse> responseObserver) {
+        GetAccountInfoByTokenResponse.Builder builder = GetAccountInfoByTokenResponse.newBuilder();
+        try {
+            String tokenKey = request.getToken();
+            TokenDTO tokenDTO = tokenApi.findByPrimaryKey(tokenKey);
+            if (null != tokenDTO) {
+                AccountDTO accountDTO = accountApi.findByPrimaryKey(tokenDTO.getAccountId());
+                builder.setAccount(OpenBeanMapper.map(accountDTO))
+                        .setActiveCompanyId(tokenDTO.getCompanyId())
+                        .setResponseHeader(ResponseUtils.success());
+            } else {
+                logger.error(ExceptionCode.TOKEN_EXPIRED_OR_ILLEGAL.toString() + " token " + tokenKey);
+                builder.setResponseHeader(ResponseUtils.fail(ExceptionCode.TOKEN_EXPIRED_OR_ILLEGAL));
+            }
+        } catch (Exception e) {
+            logger.error("getInfoByToken error", e);
+            builder.setResponseHeader(ResponseUtils.error(e));
+        }
+        responseObserver.onNext(builder.build());
+        responseObserver.onCompleted();
+    }
+
+    @Override
+    public void saveInfo(SaveAccountInfoRequest request, StreamObserver<SaveAccountInfoResponse> responseObserver) {
+        SaveAccountInfoResponse.Builder builder = SaveAccountInfoResponse.newBuilder();
+        try {
+            AccountDTO accountDTO = accountApi.findByPrimaryKey(request.getId());
+            if (null != accountDTO) {
+                if (null != request.getAvatarUrl()) {
+                    accountDTO.setAvatarUrl(request.getAvatarUrl());
+                }
+                if (null != request.getRealname()) {
+                    accountDTO.setRealname(request.getRealname());
+                }
+                if (null != request.getSex()) {
+                    AccountDTO.Sex sex = request.getSex() == AccountInfo.Sex.FEMALE ? AccountDTO.Sex.FEMALE : AccountDTO.Sex.MALE;
+                    accountDTO.setSex(sex.getValue());
+                }
+                accountApi.update(accountDTO);
+
+                builder.setResponseHeader(ResponseUtils.success());
+            } else {
+                logger.error(ExceptionCode.USER_NOT_EXIST.toString() + " id " + request.getId());
+                builder.setResponseHeader(ResponseUtils.fail(ExceptionCode.USER_NOT_EXIST));
+            }
+        } catch (Exception e) {
+            logger.error("saveInfo error", e);
+            builder.setResponseHeader(ResponseUtils.error(e));
+        }
+        responseObserver.onNext(builder.build());
+        responseObserver.onCompleted();
+    }
 }

+ 147 - 9
services/open-grpc-service/src/main/java/com/usoftchina/uu/open/grpc/service/impl/OpenAppServiceImpl.java

@@ -1,10 +1,25 @@
 package com.usoftchina.uu.open.grpc.service.impl;
 
+import com.usoftchina.uu.account.context.CompanyHolder;
+import com.usoftchina.uu.account.context.PlatformHolder;
+import com.usoftchina.uu.account.dto.CompanyDTO;
+import com.usoftchina.uu.app.api.AppConfigApi;
+import com.usoftchina.uu.app.dto.AppConfigDTO;
+import com.usoftchina.uu.app.dto.CompanyAppGroupDTO;
+import com.usoftchina.uu.base.Platform;
+import com.usoftchina.uu.exception.ExceptionCode;
 import com.usoftchina.uu.open.grpc.api.*;
 import com.usoftchina.uu.open.grpc.interceptor.OpenSignatureInterceptor;
-import io.grpc.Status;
+import com.usoftchina.uu.open.grpc.util.OpenBeanMapper;
+import com.usoftchina.uu.open.grpc.util.ResponseUtils;
 import io.grpc.stub.StreamObserver;
 import org.lognet.springboot.grpc.GRpcService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.CollectionUtils;
+
+import java.util.List;
 
 /**
  * @author yingp
@@ -13,28 +28,151 @@ import org.lognet.springboot.grpc.GRpcService;
 @GRpcService(interceptors = OpenSignatureInterceptor.class)
 public class OpenAppServiceImpl extends AppServiceGrpc.AppServiceImplBase{
 
+    private final static Logger logger = LoggerFactory.getLogger(OpenAppServiceImpl.class);
+
+    @Autowired
+    private AppConfigApi appConfigApi;
+
     @Override
     public void getConfig(GetAppConfigRequest request, StreamObserver<GetAppConfigResponse> responseObserver) {
-
+        GetAppConfigResponse.Builder builder = GetAppConfigResponse.newBuilder();
+        try {
+            CompanyDTO companyDTO = CompanyHolder.get();
+            Platform platform = PlatformHolder.get();
+            // 指定平台的应用配置;如果已分配或归属该公司,则标记enable = true
+            List<CompanyAppGroupDTO> appGroupDTOList = appConfigApi.findByScopePlatformAndCompanyId(platform, companyDTO.getId());
+            if (!CollectionUtils.isEmpty(appGroupDTOList)) {
+                appGroupDTOList.forEach(appGroupDTO -> {
+                    builder.addConfig(OpenBeanMapper.map(appGroupDTO));
+                });
+            }
+            builder.setResponseHeader(ResponseUtils.success());
+        } catch (Exception e) {
+            logger.error("getConfig error", e);
+            builder.setResponseHeader(ResponseUtils.error(e));
+        }
+        responseObserver.onNext(builder.build());
+        responseObserver.onCompleted();
     }
 
     @Override
     public void setEnable(SetEnableRequest request, StreamObserver<SetEnableResponse> responseObserver) {
+        SetEnableResponse.Builder builder = SetEnableResponse.newBuilder();
         try {
-            ResponseHeader header = ResponseHeader.newBuilder().setSuccess(true).build();
-            responseObserver.onNext(SetEnableResponse.newBuilder().setResponseHeader(header).build());
-            responseObserver.onCompleted();
+            CompanyDTO companyDTO = CompanyHolder.get();
+            Integer appId = request.getAppId();
+            Boolean enable = request.getEnable();
+            if (null != enable && enable.booleanValue()) {
+                // 启用
+                appConfigApi.bindCompany(appId, companyDTO.getId());
+            } else {
+                // 禁用
+                appConfigApi.unbindCompany(appId, companyDTO.getId());
+            }
+            builder.setResponseHeader(ResponseUtils.success());
         } catch (Exception e) {
-            responseObserver.onError(Status.INTERNAL.withDescription(e.getMessage()).asRuntimeException());
+            logger.error("setEnable error", e);
+            builder.setResponseHeader(ResponseUtils.error(e));
         }
+        responseObserver.onNext(builder.build());
+        responseObserver.onCompleted();
     }
 
     @Override
-    public void saveConfig(SaveConfigRequest request, StreamObserver<SaveConfigResponse> responseObserver) {
+    public void newConfig(NewConfigRequest request, StreamObserver<NewConfigResponse> responseObserver) {
+        NewConfigResponse.Builder builder = NewConfigResponse.newBuilder();
+        try {
+            CompanyDTO companyDTO = CompanyHolder.get();
+            Platform platform = PlatformHolder.get();
+            AppConfigDTO appConfigDTO = OpenBeanMapper.map(request.getAppConfig());
+            appConfigDTO.setGroupId(request.getGroupId());
+            appConfigDTO.setId(null);
+            // 标记为专属应用
+            appConfigDTO.setScopeCompanyId(companyDTO.getId());
+            appConfigDTO.setScopePlatform(platform.getValue());
+            int appId = appConfigApi.save(appConfigDTO);
+
+            Boolean enable = request.getAppConfig().getEnable();
+            if (null != enable && enable.booleanValue()) {
+                appConfigApi.bindCompany(appId, companyDTO.getId());
+            }
+
+            builder.setAppId(appId)
+                    .setResponseHeader(ResponseUtils.success());
+        } catch (Exception e) {
+            logger.error("saveConfig error", e);
+            builder.setResponseHeader(ResponseUtils.error(e));
+        }
+        responseObserver.onNext(builder.build());
+        responseObserver.onCompleted();
     }
 
     @Override
-    public void deleteConfig(DeleteConfigRequest request, StreamObserver<DeleteCOnfigResponse> responseObserver) {
-        super.deleteConfig(request, responseObserver);
+    public void modifyConfig(ModifyConfigRequest request, StreamObserver<ModifyConfigResponse> responseObserver) {
+        ModifyConfigResponse.Builder builder = ModifyConfigResponse.newBuilder();
+        try {
+            CompanyDTO companyDTO = CompanyHolder.get();
+            AppConfigDTO appConfigDTO = OpenBeanMapper.map(request.getAppConfig());
+            AppConfigDTO oldOne = appConfigApi.findByPrimaryKey(appConfigDTO.getId());
+            if (null != oldOne) {
+                // 只能修改自定义的
+                if (null != oldOne.getScopeCompanyId() && oldOne.getScopeCompanyId().equals(companyDTO.getId())) {
+                    // 标记为专属应用
+                    appConfigDTO.setScopeCompanyId(oldOne.getScopeCompanyId());
+                    appConfigDTO.setScopePlatform(oldOne.getScopePlatform());
+                    appConfigApi.updateByPrimaryKey(appConfigDTO);
+
+                    Boolean enable = request.getAppConfig().getEnable();
+                    if (null != enable && enable.booleanValue()) {
+                        appConfigApi.bindCompany(appConfigDTO.getId(), companyDTO.getId());
+                    } else {
+                        appConfigApi.unbindCompany(appConfigDTO.getId(), companyDTO.getId());
+                    }
+
+                    builder.setResponseHeader(ResponseUtils.success());
+                } else {
+                    logger.error(ExceptionCode.APP_NO_PERMISSION.toString() + " appId " + appConfigDTO.getId());
+                    builder.setResponseHeader(ResponseUtils.fail(ExceptionCode.APP_NO_PERMISSION.getCode(),
+                            ExceptionCode.APP_NO_PERMISSION.getMessage() + " 如果需要禁用/启用该应用,请使用setEnable接口"));
+                }
+            } else {
+                logger.error(ExceptionCode.APP_NOT_FOUND.toString() + " appId " + appConfigDTO.getId());
+                builder.setResponseHeader(ResponseUtils.fail(ExceptionCode.APP_NOT_FOUND));
+            }
+        } catch (Exception e) {
+            logger.error("modifyConfig error", e);
+            builder.setResponseHeader(ResponseUtils.error(e));
+        }
+        responseObserver.onNext(builder.build());
+        responseObserver.onCompleted();
+    }
+
+    @Override
+    public void deleteConfig(DeleteConfigRequest request, StreamObserver<DeleteConfigResponse> responseObserver) {
+        DeleteConfigResponse.Builder builder = DeleteConfigResponse.newBuilder();
+        try {
+            CompanyDTO companyDTO = CompanyHolder.get();
+            int appId = request.getAppId();
+            AppConfigDTO appConfigDTO = appConfigApi.findByPrimaryKey(appId);
+            if (null != appConfigDTO) {
+                // 只能删除自定义的
+                if (null != appConfigDTO.getScopeCompanyId() && appConfigDTO.getScopeCompanyId().equals(companyDTO.getId())) {
+                    appConfigApi.unbindCompany(appId, companyDTO.getId());
+                    appConfigApi.removeByPrimaryKey(appId);
+                    builder.setResponseHeader(ResponseUtils.success());
+                } else {
+                    logger.error(ExceptionCode.APP_NO_PERMISSION.toString() + " appId " + request.getAppId());
+                    builder.setResponseHeader(ResponseUtils.fail(ExceptionCode.APP_NO_PERMISSION));
+                }
+            } else {
+                logger.error(ExceptionCode.APP_NOT_FOUND.toString() + " appId " + request.getAppId());
+                builder.setResponseHeader(ResponseUtils.fail(ExceptionCode.APP_NOT_FOUND));
+            }
+        } catch (Exception e) {
+            logger.error("deleteConfig error", e);
+            builder.setResponseHeader(ResponseUtils.error(e));
+        }
+        responseObserver.onNext(builder.build());
+        responseObserver.onCompleted();
     }
 }

+ 116 - 1
services/open-grpc-service/src/main/java/com/usoftchina/uu/open/grpc/service/impl/OpenMessageServiceImpl.java

@@ -1,8 +1,27 @@
 package com.usoftchina.uu.open.grpc.service.impl;
 
-import com.usoftchina.uu.open.grpc.api.MessageServiceGrpc;
+import com.github.pagehelper.PageInfo;
+import com.usoftchina.uu.account.context.CompanyHolder;
+import com.usoftchina.uu.account.context.PlatformHolder;
+import com.usoftchina.uu.account.dto.CompanyDTO;
+import com.usoftchina.uu.base.Platform;
+import com.usoftchina.uu.message.api.MessageApi;
+import com.usoftchina.uu.message.dto.MessageDTO;
+import com.usoftchina.uu.message.dto.SendMessageDTO;
+import com.usoftchina.uu.message.dto.UnreadMessageCountDTO;
+import com.usoftchina.uu.open.grpc.api.*;
 import com.usoftchina.uu.open.grpc.interceptor.OpenSignatureInterceptor;
+import com.usoftchina.uu.open.grpc.util.OpenBeanMapper;
+import com.usoftchina.uu.open.grpc.util.ResponseUtils;
+import com.usoftchina.uu.page.PageRequest;
+import io.grpc.stub.StreamObserver;
 import org.lognet.springboot.grpc.GRpcService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.CollectionUtils;
+
+import java.util.List;
 
 /**
  * @author yingp
@@ -10,4 +29,100 @@ import org.lognet.springboot.grpc.GRpcService;
  */
 @GRpcService(interceptors = OpenSignatureInterceptor.class)
 public class OpenMessageServiceImpl extends MessageServiceGrpc.MessageServiceImplBase {
+
+    private final static Logger logger = LoggerFactory.getLogger(OpenMessageServiceImpl.class);
+
+    @Autowired
+    private MessageApi messageApi;
+
+    @Override
+    public void send(SendMessageRequest request, StreamObserver<SendMessageResponse> responseObserver) {
+        SendMessageResponse.Builder builder = SendMessageResponse.newBuilder();
+        try {
+            CompanyDTO companyDTO = CompanyHolder.get();
+            SendMessageDTO messageDTO = new SendMessageDTO(request.getCode(), request.getMessage(),
+                    request.getAccountId(), companyDTO.getId());
+            // TODO 校验 message code对应的platform与当前第三方平台PlatformHolder.get()是否一致
+            // TODO 校验accountId与companyId是否存在绑定关系
+            messageApi.send(messageDTO);
+            builder.setResponseHeader(ResponseUtils.success());
+        } catch (Exception e) {
+            logger.error("send error", e);
+            builder.setResponseHeader(ResponseUtils.error(e));
+        }
+        responseObserver.onNext(builder.build());
+        responseObserver.onCompleted();
+    }
+
+    @Override
+    public void getUnreadCount(GetUnreadMessageCountRequest request, StreamObserver<GetUnreadMessageCountResponse> responseObserver) {
+        GetUnreadMessageCountResponse.Builder builder = GetUnreadMessageCountResponse.newBuilder();
+        try {
+            CompanyDTO companyDTO = CompanyHolder.get();
+            Platform platform = PlatformHolder.get();
+            // TODO 校验accountId与companyId是否存在绑定关系
+            List<UnreadMessageCountDTO> unreadMessageCountDTOList =
+                    messageApi.getAllUnreadCountByScopePlatform(platform, request.getAccountId(), companyDTO.getId());
+            if (!CollectionUtils.isEmpty(unreadMessageCountDTOList)) {
+                unreadMessageCountDTOList.forEach(unreadMessageCountDTO -> {
+                    builder.addUnread(OpenBeanMapper.map(unreadMessageCountDTO));
+                });
+            }
+            builder.setResponseHeader(ResponseUtils.success());
+        } catch (Exception e) {
+            logger.error("getUnreadCount error", e);
+            builder.setResponseHeader(ResponseUtils.error(e));
+        }
+        responseObserver.onNext(builder.build());
+        responseObserver.onCompleted();
+    }
+
+    @Override
+    public void getMessages(GetMessagesRequest request, StreamObserver<GetMessagesResponse> responseObserver) {
+        GetMessagesResponse.Builder builder = GetMessagesResponse.newBuilder();
+        try {
+            CompanyDTO companyDTO = CompanyHolder.get();
+            Platform platform = PlatformHolder.get();
+            PageRequest pageRequest = new PageRequest(request.getPageNumber(), request.getPageSize());
+            PageInfo<MessageDTO> pageInfo = null;
+            // 获取消息
+            if (request.getStatus() == GetMessagesRequest.Status.UNREAD) {
+                pageInfo = messageApi.findUnreadByPageAndCodeAndPlatform(pageRequest, platform,
+                        request.getCode(), request.getAccountId(), companyDTO.getId());
+            } else if (request.getStatus() == GetMessagesRequest.Status.READ) {
+                pageInfo = messageApi.findReadByPageAndCodeAndPlatform(pageRequest, platform,
+                        request.getCode(), request.getAccountId(), companyDTO.getId());
+            } else {
+                pageInfo = messageApi.findAllByPageAndCodeAndPlatform(pageRequest, platform,
+                        request.getCode(), request.getAccountId(), companyDTO.getId());
+            }
+            builder.setPaging(OpenBeanMapper.map(pageInfo));
+            List<MessageDTO> messageDTOList = pageInfo.getList();
+            if (!CollectionUtils.isEmpty(messageDTOList)) {
+                messageDTOList.forEach(messageDTO -> {
+                    builder.addMessage(OpenBeanMapper.map(messageDTO));
+                });
+            }
+            builder.setResponseHeader(ResponseUtils.success());
+        } catch (Exception e) {
+            logger.error("getMessages error", e);
+            builder.setResponseHeader(ResponseUtils.error(e));
+        }
+        responseObserver.onNext(builder.build());
+        responseObserver.onCompleted();
+    }
+
+    @Override
+    public void setRead(SetMessageReadRequest request, StreamObserver<SetMessageReadResponse> responseObserver) {
+        SetMessageReadResponse.Builder builder = SetMessageReadResponse.newBuilder();
+        try {
+            messageApi.setRead(request.getMessageId(), PlatformHolder.get());
+            builder.setResponseHeader(ResponseUtils.success());
+        } catch (Exception e) {
+            logger.error("setRead error", e);
+            builder.setResponseHeader(ResponseUtils.error(e));
+        }
+        responseObserver.onNext(builder.build());
+        responseObserver.onCompleted();
+    }
 }

+ 179 - 0
services/open-grpc-service/src/main/java/com/usoftchina/uu/open/grpc/util/OpenBeanMapper.java

@@ -0,0 +1,179 @@
+package com.usoftchina.uu.open.grpc.util;
+
+import com.github.pagehelper.PageInfo;
+import com.usoftchina.uu.account.dto.AccountDTO;
+import com.usoftchina.uu.app.dto.AppConfigDTO;
+import com.usoftchina.uu.app.dto.CompanyAppConfigDTO;
+import com.usoftchina.uu.app.dto.CompanyAppGroupDTO;
+import com.usoftchina.uu.message.dto.MessageDTO;
+import com.usoftchina.uu.message.dto.UnreadMessageCountDTO;
+import com.usoftchina.uu.open.grpc.api.*;
+import org.springframework.util.CollectionUtils;
+
+import java.util.List;
+
+/**
+ * @author yingp
+ * @date 2019/4/18
+ */
+public class OpenBeanMapper {
+
+    /**
+     * 本地account对象转远程account对象
+     *
+     * @param accountDTO
+     * @return
+     */
+    public static AccountInfo map(AccountDTO accountDTO) {
+        AccountInfo.Builder builder = AccountInfo.newBuilder();
+
+        AccountInfo.Sex sex;
+        if (null != accountDTO.getSex() && AccountDTO.Sex.isFemale(accountDTO.getSex())) {
+            sex = AccountInfo.Sex.FEMALE;
+        } else {
+            sex = AccountInfo.Sex.MALE;
+        }
+        builder.setSex(sex);
+
+        if (null != accountDTO.getAvatarUrl()) {
+            builder.setAvatarUrl(accountDTO.getAvatarUrl());
+        }
+        if (null != accountDTO.getEmail()) {
+            builder.setEmail(accountDTO.getEmail());
+        }
+        if (null != accountDTO.getId()) {
+            builder.setId(accountDTO.getId());
+        }
+        if (null != accountDTO.getMobile()) {
+            builder.setMobile(accountDTO.getMobile());
+        }
+        if (null != accountDTO.getRealname()) {
+            builder.setRealname(accountDTO.getRealname());
+        }
+        return builder.build();
+    }
+
+    /**
+     * 本地UnreadMessageCount对象转远程UnreadMessageCount对象
+     *
+     * @param unreadMessageCountDTO
+     * @return
+     */
+    public static UnreadMessageCount map(UnreadMessageCountDTO unreadMessageCountDTO) {
+        UnreadMessageCount.Builder builder = UnreadMessageCount.newBuilder();
+        if (null != unreadMessageCountDTO.getCode()) {
+            builder.setCode(unreadMessageCountDTO.getCode());
+        }
+        if (null != unreadMessageCountDTO.getCount()) {
+            builder.setCount(unreadMessageCountDTO.getCount());
+        }
+        return builder.build();
+    }
+
+    /**
+     * 本地PageInfo对象转远程Paging对象
+     *
+     * @param pageInfo
+     * @return
+     */
+    public static Paging map(PageInfo pageInfo) {
+        return Paging.newBuilder()
+                .setPageNumber(pageInfo.getPageNum())
+                .setPageSize(pageInfo.getPageSize())
+                .setTotalCount(pageInfo.getTotal())
+                .setTotalPage(pageInfo.getPages()).build();
+    }
+
+    /**
+     * 本地Message对象转远程Message对象
+     *
+     * @param messageDTO
+     * @return
+     */
+    public static MessageInfo map(MessageDTO messageDTO) {
+        MessageInfo.Builder builder = MessageInfo.newBuilder();
+        if (null != messageDTO.getBody()) {
+            builder.setBody(messageDTO.getBody());
+        }
+        if (null != messageDTO.getId()) {
+            builder.setId(messageDTO.getId());
+        }
+        if (null != messageDTO.getReadTime()) {
+            builder.setReadTime(messageDTO.getReadTime().getTime());
+        }
+        builder.setStatus(null != messageDTO.getReadTime() ? MessageInfo.Status.READ : MessageInfo.Status.UNREAD);
+        return builder.build();
+    }
+
+    /**
+     * 本地appConfig对象转远程appConfig对象
+     *
+     * @param appConfigDTO
+     * @return
+     */
+    public static AppConfig map(CompanyAppConfigDTO appConfigDTO) {
+        AppConfig.Builder builder = AppConfig.newBuilder();
+
+        ViewType type = appConfigDTO.viewType().isWidget() ? ViewType.WIDGET : ViewType.WEB;
+        builder.setViewType(type);
+        if (null != appConfigDTO.getAndroidWidget()) {
+            builder.setAndroidWidget(appConfigDTO.getAndroidWidget());
+        }
+        if (null != appConfigDTO.getIcon()) {
+            builder.setIcon(appConfigDTO.getIcon());
+        }
+        if (null != appConfigDTO.getId()) {
+            builder.setId(appConfigDTO.getId());
+        }
+        if (null != appConfigDTO.getIosWidget()) {
+            builder.setIosWidget(appConfigDTO.getIosWidget());
+        }
+        if (null != appConfigDTO.getName()) {
+            builder.setName(appConfigDTO.getName());
+        }
+        if (null != appConfigDTO.getWebUrl()) {
+            builder.setWebUrl(appConfigDTO.getWebUrl());
+        }
+        if (null != appConfigDTO.getEnable()) {
+            builder.setEnable(appConfigDTO.getEnable().booleanValue());
+        }
+        return builder.build();
+    }
+
+    /**
+     * 本地appGroup对象转远程appGroup对象
+     *
+     * @param appGroupDTO
+     * @return
+     */
+    public static AppGroupConfig map(CompanyAppGroupDTO appGroupDTO) {
+        AppGroupConfig.Builder builder = AppGroupConfig.newBuilder().setName(appGroupDTO.getName());
+        List<CompanyAppConfigDTO> appConfigDTOList = appGroupDTO.getAppConfigList();
+        if (!CollectionUtils.isEmpty(appConfigDTOList)) {
+            appConfigDTOList.forEach(appConfigDTO -> {
+                builder.addAppConfig(map(appConfigDTO));
+            });
+        }
+        return builder.build();
+    }
+
+    /**
+     * 远程appConfig转本地appConfig对象
+     *
+     * @param appConfig
+     * @return
+     */
+    public static AppConfigDTO map(AppConfig appConfig) {
+        AppConfigDTO appConfigDTO = new AppConfigDTO();
+        appConfigDTO.setId(appConfig.getId());
+        appConfigDTO.setAndroidWidget(appConfig.getAndroidWidget());
+        appConfigDTO.setIcon(appConfig.getIcon());
+        appConfigDTO.setIosWidget(appConfig.getIosWidget());
+        appConfigDTO.setName(appConfig.getName());
+        appConfigDTO.setWebUrl(appConfig.getWebUrl());
+        com.usoftchina.uu.base.ViewType type = ViewType.WEB == appConfig.getViewType() ?
+                com.usoftchina.uu.base.ViewType.WEB : com.usoftchina.uu.base.ViewType.WIDGET;
+        appConfigDTO.setViewType(type.getValue());
+        return appConfigDTO;
+    }
+}

+ 41 - 0
services/open-grpc-service/src/main/java/com/usoftchina/uu/open/grpc/util/ResponseUtils.java

@@ -0,0 +1,41 @@
+package com.usoftchina.uu.open.grpc.util;
+
+import com.usoftchina.uu.exception.BaseException;
+import com.usoftchina.uu.exception.BaseExceptionCode;
+import com.usoftchina.uu.exception.ExceptionCode;
+import com.usoftchina.uu.open.grpc.api.ResponseHeader;
+import com.usoftchina.uu.util.StringUtils;
+
+/**
+ * @author yingp
+ * @date 2019/4/18
+ */
+public class ResponseUtils {
+    public static ResponseHeader success() {
+        return ResponseHeader.newBuilder()
+                .setSuccess(true).build();
+    }
+
+    public static ResponseHeader fail(int code, String message) {
+        return ResponseHeader.newBuilder()
+                .setSuccess(false)
+                .setCode(code)
+                .setMessage(message).build();
+    }
+
+    public static ResponseHeader fail(BaseExceptionCode e) {
+        return fail(e.getCode(), e.getMessage());
+    }
+
+    public static ResponseHeader fail(BaseException e) {
+        return fail(e.getCode(), e.getMessage());
+    }
+
+    public static ResponseHeader error(Exception e) {
+        if (e instanceof BaseException) {
+            return fail((BaseException) e);
+        }
+        ExceptionCode exceptionCode = ExceptionCode.UNKNOWN_ERROR;
+        return fail(exceptionCode.getCode(), StringUtils.nullIf(e.getMessage(), exceptionCode.getMessage()));
+    }
+}

+ 18 - 0
services/open-grpc-service/src/main/java/com/usoftchina/uu/open/grpc/util/SignatureUtils.java

@@ -0,0 +1,18 @@
+package com.usoftchina.uu.open.grpc.util;
+
+import com.usoftchina.uu.crypto.HmacUtils;
+
+/**
+ * @author yingp
+ * @date 2019/4/18
+ */
+public class SignatureUtils {
+
+    public static String sign(String secretKey, String timestamp) {
+        return HmacUtils.encode(timestamp, secretKey);
+    }
+
+    public static boolean verify(String secretKey, String timestamp, String signature) {
+        return HmacUtils.encode(timestamp, secretKey).equals(signature);
+    }
+}

+ 2 - 0
settings.gradle

@@ -39,4 +39,6 @@ include 'runtime:admin-server'
 findProject(':runtime:admin-server')?.name = 'admin-server'
 include 'external:sso-service'
 findProject(':external:sso-service')?.name = 'sso-service'
+include 'external:open-sdk'
+findProject(':external:open-sdk')?.name = 'open-sdk'
 

+ 12 - 2
shared/core/src/main/java/com/usoftchina/uu/exception/ExceptionCode.java

@@ -15,11 +15,12 @@ public enum ExceptionCode implements BaseExceptionCode {
     ILLEGAL_ARGUMENTS(20001, "参数错误"),
     // jwt token 相关 start
     // 过期
-    TOKEN_EXPIRED(40001, "登录超时,请重新登录"),
+    TOKEN_EXPIRED(40001, "登录超时"),
     // 签名错误
     SIGNATURE_ILLEGAL(40002, "不合法的签名"),
     // token 为空
     TOKEN_EMPTY(40003, "缺少身份信息"),
+    TOKEN_EXPIRED_OR_ILLEGAL(40004, "非法Token,或登录超时"),
 
     // authorize相关
     AUTH_MAX_ERRORS(43001, "超过登录次数限制,账户已被冻结,请30分钟后再尝试"),
@@ -53,7 +54,11 @@ public enum ExceptionCode implements BaseExceptionCode {
     MESSAGE_CODE_EXIST(60000, "消息编号已存在"),
     MESSAGE_CODE_ILLEGAL(60001, "无法使用该消息编号"),
     MESSAGE_MUST_HAS_COMPANY_ID(61001, "未提供公司ID"),
-    MESSAGE_OPERATION_NOT_PERMITTED(61002, "不允许操作该消息")
+    MESSAGE_OPERATION_NOT_PERMITTED(61002, "不允许操作该消息"),
+
+    // 应用相关
+    APP_NOT_FOUND(70000, "应用不存在"),
+    APP_NO_PERMISSION(70001, "无权操作该应用")
     ;
 
     private int code;
@@ -74,6 +79,11 @@ public enum ExceptionCode implements BaseExceptionCode {
         return message;
     }
 
+    @Override
+    public String toString() {
+        return "[ " + code + " ] " + message;
+    }
+
     public void occur() throws BizException{
         throw new BizException(code, message);
     }