丁云鹏 пре 1 година
комит
c97841013b
48 измењених фајлова са 6669 додато и 0 уклоњено
  1. 50 0
      .gitignore
  2. 26 0
      pom.xml
  3. 33 0
      recommend-feature-client/.gitignore
  4. 99 0
      recommend-feature-client/pom.xml
  5. 77 0
      recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/server/client/FeatureClient.java
  6. 23 0
      recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/server/client/ProtobufUtils.java
  7. 49 0
      recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/server/gen/common/Common.java
  8. 637 0
      recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/server/gen/common/Result.java
  9. 31 0
      recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/server/gen/common/ResultOrBuilder.java
  10. 96 0
      recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/server/gen/feature/Feature.java
  11. 308 0
      recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/server/gen/feature/FeatureService.java
  12. 355 0
      recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/server/gen/feature/FeatureServiceGrpc.java
  13. 557 0
      recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/server/gen/feature/GetUserFeatureRequest.java
  14. 21 0
      recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/server/gen/feature/GetUserFeatureRequestOrBuilder.java
  15. 607 0
      recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/server/gen/feature/GetUserFeatureResponse.java
  16. 24 0
      recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/server/gen/feature/GetUserFeatureResponseOrBuilder.java
  17. 557 0
      recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/server/gen/feature/GetVideoFeatureRequest.java
  18. 21 0
      recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/server/gen/feature/GetVideoFeatureRequestOrBuilder.java
  19. 607 0
      recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/server/gen/feature/GetVideoFeatureResponse.java
  20. 24 0
      recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/server/gen/feature/GetVideoFeatureResponseOrBuilder.java
  21. 308 0
      recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/server/gen/feature/RecommendService.java
  22. 355 0
      recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/server/gen/feature/RecommendServiceGrpc.java
  23. 9 0
      recommend-feature-client/src/main/proto/com/tzld/piaoquan/recommend/feature/common.proto
  24. 31 0
      recommend-feature-client/src/main/proto/com/tzld/piaoquan/recommend/feature/feature.proto
  25. 150 0
      recommend-feature-service/pom.xml
  26. 24 0
      recommend-feature-service/src/main/java/com/tzld/piaoquan/recommend/feature/Application.java
  27. 40 0
      recommend-feature-service/src/main/java/com/tzld/piaoquan/recommend/feature/common/CommonThreadPoolExecutor.java
  28. 358 0
      recommend-feature-service/src/main/java/com/tzld/piaoquan/recommend/feature/common/RecommendLoghubAppender.java
  29. 37 0
      recommend-feature-service/src/main/java/com/tzld/piaoquan/recommend/feature/common/RecommendLoghubAppenderCallback.java
  30. 37 0
      recommend-feature-service/src/main/java/com/tzld/piaoquan/recommend/feature/common/ThreadPoolFactory.java
  31. 79 0
      recommend-feature-service/src/main/java/com/tzld/piaoquan/recommend/feature/common/enums/AppTypeEnum.java
  32. 67 0
      recommend-feature-service/src/main/java/com/tzld/piaoquan/recommend/feature/config/RedisTemplateConfig.java
  33. 27 0
      recommend-feature-service/src/main/java/com/tzld/piaoquan/recommend/feature/grpcservice/FeatureGrpcService.java
  34. 23 0
      recommend-feature-service/src/main/java/com/tzld/piaoquan/recommend/feature/service/WarmUpService.java
  35. 28 0
      recommend-feature-service/src/main/java/com/tzld/piaoquan/recommend/feature/util/CommonCollectionUtils.java
  36. 30 0
      recommend-feature-service/src/main/java/com/tzld/piaoquan/recommend/feature/util/DateUtils.java
  37. 96 0
      recommend-feature-service/src/main/java/com/tzld/piaoquan/recommend/feature/util/HttpClientFactory.java
  38. 38 0
      recommend-feature-service/src/main/java/com/tzld/piaoquan/recommend/feature/util/JSONUtils.java
  39. 48 0
      recommend-feature-service/src/main/java/com/tzld/piaoquan/recommend/feature/util/TraceUtils.java
  40. 42 0
      recommend-feature-service/src/main/java/com/tzld/piaoquan/recommend/feature/web/ControllerAspect.java
  41. 94 0
      recommend-feature-service/src/main/java/com/tzld/piaoquan/recommend/feature/web/RecommendController.java
  42. 43 0
      recommend-feature-service/src/main/resources/application-dev.yml
  43. 35 0
      recommend-feature-service/src/main/resources/application-pre.yml
  44. 35 0
      recommend-feature-service/src/main/resources/application-prod.yml
  45. 35 0
      recommend-feature-service/src/main/resources/application-test.yml
  46. 16 0
      recommend-feature-service/src/main/resources/application.yml
  47. 328 0
      recommend-feature-service/src/main/resources/logback-spring.xml
  48. 54 0
      recommend-feature-service/src/main/resources/mybatis-generator-config.xml

+ 50 - 0
.gitignore

@@ -0,0 +1,50 @@
+# ---> Java
+*.class
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.jar
+*.war
+*.ear
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
+
+### log ###
+logs/*

+ 26 - 0
pom.xml

@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+    <packaging>pom</packaging>
+    <parent>
+        <groupId>com.tzld.commons</groupId>
+        <artifactId>supom</artifactId>
+        <version>1.0.9</version>
+    </parent>
+    <groupId>com.tzld.piaoquan</groupId>
+    <artifactId>recommend-feature</artifactId>
+    <version>1.0.0</version>
+    <name>recommend-feature</name>
+    <description>recommend-feature</description>
+
+    <modules>
+        <module>recommend-feature-service</module>
+        <module>recommend-feature-client</module>
+    </modules>
+
+    <dependencies>
+    </dependencies>
+
+</project>

+ 33 - 0
recommend-feature-client/.gitignore

@@ -0,0 +1,33 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/

+ 99 - 0
recommend-feature-client/pom.xml

@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>recommend-feature</artifactId>
+        <groupId>com.tzld.piaoquan</groupId>
+        <version>1.0.0</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>recommend-feature-client</artifactId>
+    <version>1.0.0</version>
+
+    <dependencies>
+        <dependency>
+            <groupId>net.devh</groupId>
+            <artifactId>grpc-client-spring-boot-starter</artifactId>
+            <version>2.9.0.RELEASE</version>
+        </dependency>
+        <dependency>
+            <groupId>com.google.protobuf</groupId>
+            <artifactId>protobuf-java</artifactId>
+            <version>3.12.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <version>1.16.8</version>
+        </dependency>
+        <dependency>
+            <groupId>com.google.protobuf</groupId>
+            <artifactId>protobuf-java-util</artifactId>
+            <version>3.6.0</version>
+            <exclusions>
+                <exclusion>
+                    <artifactId>protobuf-java</artifactId>
+                    <groupId>com.google.protobuf</groupId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+    </dependencies>
+    <build>
+        <extensions>
+            <extension>
+                <groupId>kr.motd.maven</groupId>
+                <artifactId>os-maven-plugin</artifactId>
+                <version>1.6.0</version>
+            </extension>
+        </extensions>
+        <plugins>
+            <plugin>
+                <groupId>org.xolstice.maven.plugins</groupId>
+                <artifactId>protobuf-maven-plugin</artifactId>
+                <version>0.6.1</version>
+                <configuration>
+                    <protocArtifact>com.google.protobuf:protoc:3.12.0:exe:${os.detected.classifier}</protocArtifact>
+                    <pluginId>grpc-java</pluginId>
+                    <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.34.1:exe:${os.detected.classifier}</pluginArtifact>
+                    <!--设置grpc生成代码到指定路径-->
+                    <outputDirectory>${project.basedir}/src/main/java</outputDirectory>
+                    <!--生成代码前是否清空目录-->
+                    <clearOutputDirectory>false</clearOutputDirectory>
+                </configuration>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>compile</goal>
+                            <goal>compile-custom</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <!-- 设置多个源文件夹 -->
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>build-helper-maven-plugin</artifactId>
+                <version>3.0.0</version>
+                <executions>
+                    <!-- 添加主源码目录 -->
+                    <execution>
+                        <id>add-source</id>
+                        <phase>generate-sources</phase>
+                        <goals>
+                            <goal>add-source</goal>
+                        </goals>
+                        <configuration>
+                            <sources>
+                                <source>${project.basedir}/src/main/java</source>
+                            </sources>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+
+        </plugins>
+    </build>
+</project>

+ 77 - 0
recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/server/client/FeatureClient.java

@@ -0,0 +1,77 @@
+package com.tzld.piaoquan.recommend.server.client;
+
+import com.tzld.piaoquan.recommend.server.gen.feature.FeatureServiceGrpc;
+import net.devh.boot.grpc.client.inject.GrpcClient;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author dyp
+ */
+@Component
+public class FeatureClient {
+    @GrpcClient("recommend-server")
+    private FeatureServiceGrpc.FeatureServiceBlockingStub client;
+
+//    public List<VideoProto> homepageRecommend(RecommendRequest request) {
+//
+//        RecommendResponse response = client.homepageRecommend(request);
+//        if (response == null
+//                || !response.hasResult()) {
+//            log.info("homepageRecommend grpc error");
+//            return Collections.emptyList();
+//        }
+//        if (response.getResult().getCode() != 1) {
+//            log.info("homepageRecommend grpc code={}, msg={}", response.getResult().getCode(),
+//                    response.getResult().getMessage());
+//            return Collections.emptyList();
+//        }
+//        return response.getVideoCount() > 0 ? response.getVideoList() : Collections.emptyList();
+//    }
+//
+//    public List<VideoProto> relevantRecommend(RecommendRequest request) {
+//        RecommendResponse response = client.relevantRecommend(request);
+//        if (response == null
+//                || !response.hasResult()) {
+//            log.info("relevantRecommend grpc error");
+//            return Collections.emptyList();    @GrpcClient("recommend-server")
+//            private RecommendServiceGrpc.RecommendServiceBlockingStub client;
+//
+//            public List<VideoProto> homepageRecommend(RecommendRequest request) {
+//
+//                RecommendResponse response = client.homepageRecommend(request);
+//                if (response == null
+//                        || !response.hasResult()) {
+//                    log.info("homepageRecommend grpc error");
+//                    return Collections.emptyList();
+//                }
+//                if (response.getResult().getCode() != 1) {
+//                    log.info("homepageRecommend grpc code={}, msg={}", response.getResult().getCode(),
+//                            response.getResult().getMessage());
+//                    return Collections.emptyList();
+//                }
+//                return response.getVideoCount() > 0 ? response.getVideoList() : Collections.emptyList();
+//            }
+//
+//            public List<VideoProto> relevantRecommend(RecommendRequest request) {
+//                RecommendResponse response = client.relevantRecommend(request);
+//                if (response == null
+//                        || !response.hasResult()) {
+//                    log.info("relevantRecommend grpc error");
+//                    return Collections.emptyList();
+//                }
+//                if (response.getResult().getCode() != 1) {
+//                    log.info("relevantRecommend grpc code={}, msg={}", response.getResult().getCode(),
+//                            response.getResult().getMessage());
+//                    return Collections.emptyList();
+//                }
+//                return response.getVideoCount() > 0 ? response.getVideoList() : Collections.emptyList();
+//            }
+//        }
+//        if (response.getResult().getCode() != 1) {
+//            log.info("relevantRecommend grpc code={}, msg={}", response.getResult().getCode(),
+//                    response.getResult().getMessage());
+//            return Collections.emptyList();
+//        }
+//        return response.getVideoCount() > 0 ? response.getVideoList() : Collections.emptyList();
+//    }
+}

+ 23 - 0
recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/server/client/ProtobufUtils.java

@@ -0,0 +1,23 @@
+package com.tzld.piaoquan.recommend.server.client;
+
+import com.google.protobuf.InvalidProtocolBufferException;
+import com.google.protobuf.Message;
+import com.google.protobuf.MessageOrBuilder;
+import com.google.protobuf.util.JsonFormat;
+
+/**
+ * @author dyp
+ */
+public class ProtobufUtils {
+    public static String toJson(MessageOrBuilder message) throws InvalidProtocolBufferException {
+        if (message == null) {
+            return "";
+        }
+        return JsonFormat.printer().includingDefaultValueFields().print(message);
+    }
+
+    public static void fromJson(String json, Message.Builder builder) throws InvalidProtocolBufferException {
+        JsonFormat.parser().merge(json, builder);
+    }
+
+}

+ 49 - 0
recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/server/gen/common/Common.java

@@ -0,0 +1,49 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: com/tzld/piaoquan/recommend/feature/common.proto
+
+package com.tzld.piaoquan.recommend.server.gen.common;
+
+public final class Common {
+  private Common() {}
+  public static void registerAllExtensions(
+      com.google.protobuf.ExtensionRegistryLite registry) {
+  }
+
+  public static void registerAllExtensions(
+      com.google.protobuf.ExtensionRegistry registry) {
+    registerAllExtensions(
+        (com.google.protobuf.ExtensionRegistryLite) registry);
+  }
+  static final com.google.protobuf.Descriptors.Descriptor
+    internal_static_Result_descriptor;
+  static final 
+    com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internal_static_Result_fieldAccessorTable;
+
+  public static com.google.protobuf.Descriptors.FileDescriptor
+      getDescriptor() {
+    return descriptor;
+  }
+  private static  com.google.protobuf.Descriptors.FileDescriptor
+      descriptor;
+  static {
+    java.lang.String[] descriptorData = {
+      "\n0com/tzld/piaoquan/recommend/feature/co" +
+      "mmon.proto\"\'\n\006Result\022\014\n\004code\030\001 \001(\005\022\017\n\007me" +
+      "ssage\030\002 \001(\tB2\n.com.tzld.piaoquan.recomme" +
+      "nd.feature.gen.commonP\001b\006proto3"
+    };
+    descriptor = com.google.protobuf.Descriptors.FileDescriptor
+      .internalBuildGeneratedFileFrom(descriptorData,
+        new com.google.protobuf.Descriptors.FileDescriptor[] {
+        });
+    internal_static_Result_descriptor =
+      getDescriptor().getMessageTypes().get(0);
+    internal_static_Result_fieldAccessorTable = new
+      com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
+        internal_static_Result_descriptor,
+        new java.lang.String[] { "Code", "Message", });
+  }
+
+  // @@protoc_insertion_point(outer_class_scope)
+}

+ 637 - 0
recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/server/gen/common/Result.java

@@ -0,0 +1,637 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: com/tzld/piaoquan/recommend/feature/common.proto
+
+package com.tzld.piaoquan.recommend.server.gen.common;
+
+/**
+ * Protobuf type {@code Result}
+ */
+public final class Result extends
+    com.google.protobuf.GeneratedMessageV3 implements
+    // @@protoc_insertion_point(message_implements:Result)
+    ResultOrBuilder {
+private static final long serialVersionUID = 0L;
+  // Use Result.newBuilder() to construct.
+  private Result(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
+    super(builder);
+  }
+  private Result() {
+    message_ = "";
+  }
+
+  @java.lang.Override
+  @SuppressWarnings({"unused"})
+  protected java.lang.Object newInstance(
+      UnusedPrivateParameter unused) {
+    return new Result();
+  }
+
+  @java.lang.Override
+  public final com.google.protobuf.UnknownFieldSet
+  getUnknownFields() {
+    return this.unknownFields;
+  }
+  private Result(
+      com.google.protobuf.CodedInputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    this();
+    if (extensionRegistry == null) {
+      throw new java.lang.NullPointerException();
+    }
+    com.google.protobuf.UnknownFieldSet.Builder unknownFields =
+        com.google.protobuf.UnknownFieldSet.newBuilder();
+    try {
+      boolean done = false;
+      while (!done) {
+        int tag = input.readTag();
+        switch (tag) {
+          case 0:
+            done = true;
+            break;
+          case 8: {
+
+            code_ = input.readInt32();
+            break;
+          }
+          case 18: {
+            java.lang.String s = input.readStringRequireUtf8();
+
+            message_ = s;
+            break;
+          }
+          default: {
+            if (!parseUnknownField(
+                input, unknownFields, extensionRegistry, tag)) {
+              done = true;
+            }
+            break;
+          }
+        }
+      }
+    } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+      throw e.setUnfinishedMessage(this);
+    } catch (java.io.IOException e) {
+      throw new com.google.protobuf.InvalidProtocolBufferException(
+          e).setUnfinishedMessage(this);
+    } finally {
+      this.unknownFields = unknownFields.build();
+      makeExtensionsImmutable();
+    }
+  }
+  public static final com.google.protobuf.Descriptors.Descriptor
+      getDescriptor() {
+    return com.tzld.piaoquan.recommend.server.gen.common.Common.internal_static_Result_descriptor;
+  }
+
+  @java.lang.Override
+  protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internalGetFieldAccessorTable() {
+    return com.tzld.piaoquan.recommend.server.gen.common.Common.internal_static_Result_fieldAccessorTable
+        .ensureFieldAccessorsInitialized(
+            com.tzld.piaoquan.recommend.server.gen.common.Result.class, com.tzld.piaoquan.recommend.server.gen.common.Result.Builder.class);
+  }
+
+  public static final int CODE_FIELD_NUMBER = 1;
+  private int code_;
+  /**
+   * <pre>
+   * 1 成功
+   * </pre>
+   *
+   * <code>int32 code = 1;</code>
+   * @return The code.
+   */
+  @java.lang.Override
+  public int getCode() {
+    return code_;
+  }
+
+  public static final int MESSAGE_FIELD_NUMBER = 2;
+  private volatile java.lang.Object message_;
+  /**
+   * <code>string message = 2;</code>
+   * @return The message.
+   */
+  @java.lang.Override
+  public java.lang.String getMessage() {
+    java.lang.Object ref = message_;
+    if (ref instanceof java.lang.String) {
+      return (java.lang.String) ref;
+    } else {
+      com.google.protobuf.ByteString bs = 
+          (com.google.protobuf.ByteString) ref;
+      java.lang.String s = bs.toStringUtf8();
+      message_ = s;
+      return s;
+    }
+  }
+  /**
+   * <code>string message = 2;</code>
+   * @return The bytes for message.
+   */
+  @java.lang.Override
+  public com.google.protobuf.ByteString
+      getMessageBytes() {
+    java.lang.Object ref = message_;
+    if (ref instanceof java.lang.String) {
+      com.google.protobuf.ByteString b = 
+          com.google.protobuf.ByteString.copyFromUtf8(
+              (java.lang.String) ref);
+      message_ = b;
+      return b;
+    } else {
+      return (com.google.protobuf.ByteString) ref;
+    }
+  }
+
+  private byte memoizedIsInitialized = -1;
+  @java.lang.Override
+  public final boolean isInitialized() {
+    byte isInitialized = memoizedIsInitialized;
+    if (isInitialized == 1) return true;
+    if (isInitialized == 0) return false;
+
+    memoizedIsInitialized = 1;
+    return true;
+  }
+
+  @java.lang.Override
+  public void writeTo(com.google.protobuf.CodedOutputStream output)
+                      throws java.io.IOException {
+    if (code_ != 0) {
+      output.writeInt32(1, code_);
+    }
+    if (!getMessageBytes().isEmpty()) {
+      com.google.protobuf.GeneratedMessageV3.writeString(output, 2, message_);
+    }
+    unknownFields.writeTo(output);
+  }
+
+  @java.lang.Override
+  public int getSerializedSize() {
+    int size = memoizedSize;
+    if (size != -1) return size;
+
+    size = 0;
+    if (code_ != 0) {
+      size += com.google.protobuf.CodedOutputStream
+        .computeInt32Size(1, code_);
+    }
+    if (!getMessageBytes().isEmpty()) {
+      size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, message_);
+    }
+    size += unknownFields.getSerializedSize();
+    memoizedSize = size;
+    return size;
+  }
+
+  @java.lang.Override
+  public boolean equals(final java.lang.Object obj) {
+    if (obj == this) {
+     return true;
+    }
+    if (!(obj instanceof com.tzld.piaoquan.recommend.server.gen.common.Result)) {
+      return super.equals(obj);
+    }
+    com.tzld.piaoquan.recommend.server.gen.common.Result other = (com.tzld.piaoquan.recommend.server.gen.common.Result) obj;
+
+    if (getCode()
+        != other.getCode()) return false;
+    if (!getMessage()
+        .equals(other.getMessage())) return false;
+    if (!unknownFields.equals(other.unknownFields)) return false;
+    return true;
+  }
+
+  @java.lang.Override
+  public int hashCode() {
+    if (memoizedHashCode != 0) {
+      return memoizedHashCode;
+    }
+    int hash = 41;
+    hash = (19 * hash) + getDescriptor().hashCode();
+    hash = (37 * hash) + CODE_FIELD_NUMBER;
+    hash = (53 * hash) + getCode();
+    hash = (37 * hash) + MESSAGE_FIELD_NUMBER;
+    hash = (53 * hash) + getMessage().hashCode();
+    hash = (29 * hash) + unknownFields.hashCode();
+    memoizedHashCode = hash;
+    return hash;
+  }
+
+  public static com.tzld.piaoquan.recommend.server.gen.common.Result parseFrom(
+      java.nio.ByteBuffer data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.common.Result parseFrom(
+      java.nio.ByteBuffer data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.common.Result parseFrom(
+      com.google.protobuf.ByteString data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.common.Result parseFrom(
+      com.google.protobuf.ByteString data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.common.Result parseFrom(byte[] data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.common.Result parseFrom(
+      byte[] data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.common.Result parseFrom(java.io.InputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.common.Result parseFrom(
+      java.io.InputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input, extensionRegistry);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.common.Result parseDelimitedFrom(java.io.InputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseDelimitedWithIOException(PARSER, input);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.common.Result parseDelimitedFrom(
+      java.io.InputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseDelimitedWithIOException(PARSER, input, extensionRegistry);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.common.Result parseFrom(
+      com.google.protobuf.CodedInputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.common.Result parseFrom(
+      com.google.protobuf.CodedInputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input, extensionRegistry);
+  }
+
+  @java.lang.Override
+  public Builder newBuilderForType() { return newBuilder(); }
+  public static Builder newBuilder() {
+    return DEFAULT_INSTANCE.toBuilder();
+  }
+  public static Builder newBuilder(com.tzld.piaoquan.recommend.server.gen.common.Result prototype) {
+    return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
+  }
+  @java.lang.Override
+  public Builder toBuilder() {
+    return this == DEFAULT_INSTANCE
+        ? new Builder() : new Builder().mergeFrom(this);
+  }
+
+  @java.lang.Override
+  protected Builder newBuilderForType(
+      com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+    Builder builder = new Builder(parent);
+    return builder;
+  }
+  /**
+   * Protobuf type {@code Result}
+   */
+  public static final class Builder extends
+      com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
+      // @@protoc_insertion_point(builder_implements:Result)
+      com.tzld.piaoquan.recommend.server.gen.common.ResultOrBuilder {
+    public static final com.google.protobuf.Descriptors.Descriptor
+        getDescriptor() {
+      return com.tzld.piaoquan.recommend.server.gen.common.Common.internal_static_Result_descriptor;
+    }
+
+    @java.lang.Override
+    protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+        internalGetFieldAccessorTable() {
+      return com.tzld.piaoquan.recommend.server.gen.common.Common.internal_static_Result_fieldAccessorTable
+          .ensureFieldAccessorsInitialized(
+              com.tzld.piaoquan.recommend.server.gen.common.Result.class, com.tzld.piaoquan.recommend.server.gen.common.Result.Builder.class);
+    }
+
+    // Construct using com.tzld.piaoquan.recommend.feature.gen.common.Result.newBuilder()
+    private Builder() {
+      maybeForceBuilderInitialization();
+    }
+
+    private Builder(
+        com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+      super(parent);
+      maybeForceBuilderInitialization();
+    }
+    private void maybeForceBuilderInitialization() {
+      if (com.google.protobuf.GeneratedMessageV3
+              .alwaysUseFieldBuilders) {
+      }
+    }
+    @java.lang.Override
+    public Builder clear() {
+      super.clear();
+      code_ = 0;
+
+      message_ = "";
+
+      return this;
+    }
+
+    @java.lang.Override
+    public com.google.protobuf.Descriptors.Descriptor
+        getDescriptorForType() {
+      return com.tzld.piaoquan.recommend.server.gen.common.Common.internal_static_Result_descriptor;
+    }
+
+    @java.lang.Override
+    public com.tzld.piaoquan.recommend.server.gen.common.Result getDefaultInstanceForType() {
+      return com.tzld.piaoquan.recommend.server.gen.common.Result.getDefaultInstance();
+    }
+
+    @java.lang.Override
+    public com.tzld.piaoquan.recommend.server.gen.common.Result build() {
+      com.tzld.piaoquan.recommend.server.gen.common.Result result = buildPartial();
+      if (!result.isInitialized()) {
+        throw newUninitializedMessageException(result);
+      }
+      return result;
+    }
+
+    @java.lang.Override
+    public com.tzld.piaoquan.recommend.server.gen.common.Result buildPartial() {
+      com.tzld.piaoquan.recommend.server.gen.common.Result result = new com.tzld.piaoquan.recommend.server.gen.common.Result(this);
+      result.code_ = code_;
+      result.message_ = message_;
+      onBuilt();
+      return result;
+    }
+
+    @java.lang.Override
+    public Builder clone() {
+      return super.clone();
+    }
+    @java.lang.Override
+    public Builder setField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        java.lang.Object value) {
+      return super.setField(field, value);
+    }
+    @java.lang.Override
+    public Builder clearField(
+        com.google.protobuf.Descriptors.FieldDescriptor field) {
+      return super.clearField(field);
+    }
+    @java.lang.Override
+    public Builder clearOneof(
+        com.google.protobuf.Descriptors.OneofDescriptor oneof) {
+      return super.clearOneof(oneof);
+    }
+    @java.lang.Override
+    public Builder setRepeatedField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        int index, java.lang.Object value) {
+      return super.setRepeatedField(field, index, value);
+    }
+    @java.lang.Override
+    public Builder addRepeatedField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        java.lang.Object value) {
+      return super.addRepeatedField(field, value);
+    }
+    @java.lang.Override
+    public Builder mergeFrom(com.google.protobuf.Message other) {
+      if (other instanceof com.tzld.piaoquan.recommend.server.gen.common.Result) {
+        return mergeFrom((com.tzld.piaoquan.recommend.server.gen.common.Result)other);
+      } else {
+        super.mergeFrom(other);
+        return this;
+      }
+    }
+
+    public Builder mergeFrom(com.tzld.piaoquan.recommend.server.gen.common.Result other) {
+      if (other == com.tzld.piaoquan.recommend.server.gen.common.Result.getDefaultInstance()) return this;
+      if (other.getCode() != 0) {
+        setCode(other.getCode());
+      }
+      if (!other.getMessage().isEmpty()) {
+        message_ = other.message_;
+        onChanged();
+      }
+      this.mergeUnknownFields(other.unknownFields);
+      onChanged();
+      return this;
+    }
+
+    @java.lang.Override
+    public final boolean isInitialized() {
+      return true;
+    }
+
+    @java.lang.Override
+    public Builder mergeFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      com.tzld.piaoquan.recommend.server.gen.common.Result parsedMessage = null;
+      try {
+        parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
+      } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+        parsedMessage = (com.tzld.piaoquan.recommend.server.gen.common.Result) e.getUnfinishedMessage();
+        throw e.unwrapIOException();
+      } finally {
+        if (parsedMessage != null) {
+          mergeFrom(parsedMessage);
+        }
+      }
+      return this;
+    }
+
+    private int code_ ;
+    /**
+     * <pre>
+     * 1 成功
+     * </pre>
+     *
+     * <code>int32 code = 1;</code>
+     * @return The code.
+     */
+    @java.lang.Override
+    public int getCode() {
+      return code_;
+    }
+    /**
+     * <pre>
+     * 1 成功
+     * </pre>
+     *
+     * <code>int32 code = 1;</code>
+     * @param value The code to set.
+     * @return This builder for chaining.
+     */
+    public Builder setCode(int value) {
+      
+      code_ = value;
+      onChanged();
+      return this;
+    }
+    /**
+     * <pre>
+     * 1 成功
+     * </pre>
+     *
+     * <code>int32 code = 1;</code>
+     * @return This builder for chaining.
+     */
+    public Builder clearCode() {
+      
+      code_ = 0;
+      onChanged();
+      return this;
+    }
+
+    private java.lang.Object message_ = "";
+    /**
+     * <code>string message = 2;</code>
+     * @return The message.
+     */
+    public java.lang.String getMessage() {
+      java.lang.Object ref = message_;
+      if (!(ref instanceof java.lang.String)) {
+        com.google.protobuf.ByteString bs =
+            (com.google.protobuf.ByteString) ref;
+        java.lang.String s = bs.toStringUtf8();
+        message_ = s;
+        return s;
+      } else {
+        return (java.lang.String) ref;
+      }
+    }
+    /**
+     * <code>string message = 2;</code>
+     * @return The bytes for message.
+     */
+    public com.google.protobuf.ByteString
+        getMessageBytes() {
+      java.lang.Object ref = message_;
+      if (ref instanceof String) {
+        com.google.protobuf.ByteString b = 
+            com.google.protobuf.ByteString.copyFromUtf8(
+                (java.lang.String) ref);
+        message_ = b;
+        return b;
+      } else {
+        return (com.google.protobuf.ByteString) ref;
+      }
+    }
+    /**
+     * <code>string message = 2;</code>
+     * @param value The message to set.
+     * @return This builder for chaining.
+     */
+    public Builder setMessage(
+        java.lang.String value) {
+      if (value == null) {
+    throw new NullPointerException();
+  }
+  
+      message_ = value;
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>string message = 2;</code>
+     * @return This builder for chaining.
+     */
+    public Builder clearMessage() {
+      
+      message_ = getDefaultInstance().getMessage();
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>string message = 2;</code>
+     * @param value The bytes for message to set.
+     * @return This builder for chaining.
+     */
+    public Builder setMessageBytes(
+        com.google.protobuf.ByteString value) {
+      if (value == null) {
+    throw new NullPointerException();
+  }
+  checkByteStringIsUtf8(value);
+      
+      message_ = value;
+      onChanged();
+      return this;
+    }
+    @java.lang.Override
+    public final Builder setUnknownFields(
+        final com.google.protobuf.UnknownFieldSet unknownFields) {
+      return super.setUnknownFields(unknownFields);
+    }
+
+    @java.lang.Override
+    public final Builder mergeUnknownFields(
+        final com.google.protobuf.UnknownFieldSet unknownFields) {
+      return super.mergeUnknownFields(unknownFields);
+    }
+
+
+    // @@protoc_insertion_point(builder_scope:Result)
+  }
+
+  // @@protoc_insertion_point(class_scope:Result)
+  private static final com.tzld.piaoquan.recommend.server.gen.common.Result DEFAULT_INSTANCE;
+  static {
+    DEFAULT_INSTANCE = new com.tzld.piaoquan.recommend.server.gen.common.Result();
+  }
+
+  public static com.tzld.piaoquan.recommend.server.gen.common.Result getDefaultInstance() {
+    return DEFAULT_INSTANCE;
+  }
+
+  private static final com.google.protobuf.Parser<Result>
+      PARSER = new com.google.protobuf.AbstractParser<Result>() {
+    @java.lang.Override
+    public Result parsePartialFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return new Result(input, extensionRegistry);
+    }
+  };
+
+  public static com.google.protobuf.Parser<Result> parser() {
+    return PARSER;
+  }
+
+  @java.lang.Override
+  public com.google.protobuf.Parser<Result> getParserForType() {
+    return PARSER;
+  }
+
+  @java.lang.Override
+  public com.tzld.piaoquan.recommend.server.gen.common.Result getDefaultInstanceForType() {
+    return DEFAULT_INSTANCE;
+  }
+
+}
+

+ 31 - 0
recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/server/gen/common/ResultOrBuilder.java

@@ -0,0 +1,31 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: com/tzld/piaoquan/recommend/feature/common.proto
+
+package com.tzld.piaoquan.recommend.server.gen.common;
+
+public interface ResultOrBuilder extends
+    // @@protoc_insertion_point(interface_extends:Result)
+    com.google.protobuf.MessageOrBuilder {
+
+  /**
+   * <pre>
+   * 1 成功
+   * </pre>
+   *
+   * <code>int32 code = 1;</code>
+   * @return The code.
+   */
+  int getCode();
+
+  /**
+   * <code>string message = 2;</code>
+   * @return The message.
+   */
+  java.lang.String getMessage();
+  /**
+   * <code>string message = 2;</code>
+   * @return The bytes for message.
+   */
+  com.google.protobuf.ByteString
+      getMessageBytes();
+}

+ 96 - 0
recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/server/gen/feature/Feature.java

@@ -0,0 +1,96 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: com/tzld/piaoquan/recommend/feature/feature.proto
+
+package com.tzld.piaoquan.recommend.server.gen.feature;
+
+public final class Feature {
+  private Feature() {}
+  public static void registerAllExtensions(
+      com.google.protobuf.ExtensionRegistryLite registry) {
+  }
+
+  public static void registerAllExtensions(
+      com.google.protobuf.ExtensionRegistry registry) {
+    registerAllExtensions(
+        (com.google.protobuf.ExtensionRegistryLite) registry);
+  }
+  static final com.google.protobuf.Descriptors.Descriptor
+    internal_static_GetUserFeatureRequest_descriptor;
+  static final 
+    com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internal_static_GetUserFeatureRequest_fieldAccessorTable;
+  static final com.google.protobuf.Descriptors.Descriptor
+    internal_static_GetUserFeatureResponse_descriptor;
+  static final 
+    com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internal_static_GetUserFeatureResponse_fieldAccessorTable;
+  static final com.google.protobuf.Descriptors.Descriptor
+    internal_static_GetVideoFeatureRequest_descriptor;
+  static final 
+    com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internal_static_GetVideoFeatureRequest_fieldAccessorTable;
+  static final com.google.protobuf.Descriptors.Descriptor
+    internal_static_GetVideoFeatureResponse_descriptor;
+  static final 
+    com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internal_static_GetVideoFeatureResponse_fieldAccessorTable;
+
+  public static com.google.protobuf.Descriptors.FileDescriptor
+      getDescriptor() {
+    return descriptor;
+  }
+  private static  com.google.protobuf.Descriptors.FileDescriptor
+      descriptor;
+  static {
+    java.lang.String[] descriptorData = {
+      "\n1com/tzld/piaoquan/recommend/feature/fe" +
+      "ature.proto\032\031google/protobuf/any.proto\0320" +
+              "com/tzld/piaoquan/recommend/server/comm" +
+      "on.proto\"$\n\025GetUserFeatureRequest\022\013\n\003uid" +
+      "\030\001 \001(\t\"1\n\026GetUserFeatureResponse\022\027\n\006resu" +
+      "lt\030\001 \001(\0132\007.Result\"%\n\026GetVideoFeatureRequ" +
+      "est\022\013\n\003uid\030\001 \001(\t\"2\n\027GetVideoFeatureRespo" +
+      "nse\022\027\n\006result\030\001 \001(\0132\007.Result2\231\001\n\016Feature" +
+      "Service\022A\n\016GetUserFeature\022\026.GetUserFeatu" +
+      "reRequest\032\027.GetUserFeatureResponse\022D\n\017Ge" +
+      "tVideoFeature\022\027.GetVideoFeatureRequest\032\030" +
+      ".GetVideoFeatureResponseB6\n/com.tzld.pia" +
+      "oquan.recommend.feature.gen.featureP\001\210\001\001" +
+      "b\006proto3"
+    };
+    descriptor = com.google.protobuf.Descriptors.FileDescriptor
+      .internalBuildGeneratedFileFrom(descriptorData,
+        new com.google.protobuf.Descriptors.FileDescriptor[] {
+          com.google.protobuf.AnyProto.getDescriptor(),
+          com.tzld.piaoquan.recommend.server.gen.common.Common.getDescriptor(),
+        });
+    internal_static_GetUserFeatureRequest_descriptor =
+      getDescriptor().getMessageTypes().get(0);
+    internal_static_GetUserFeatureRequest_fieldAccessorTable = new
+      com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
+        internal_static_GetUserFeatureRequest_descriptor,
+        new java.lang.String[] { "Uid", });
+    internal_static_GetUserFeatureResponse_descriptor =
+      getDescriptor().getMessageTypes().get(1);
+    internal_static_GetUserFeatureResponse_fieldAccessorTable = new
+      com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
+        internal_static_GetUserFeatureResponse_descriptor,
+        new java.lang.String[] { "Result", });
+    internal_static_GetVideoFeatureRequest_descriptor =
+      getDescriptor().getMessageTypes().get(2);
+    internal_static_GetVideoFeatureRequest_fieldAccessorTable = new
+      com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
+        internal_static_GetVideoFeatureRequest_descriptor,
+        new java.lang.String[] { "Uid", });
+    internal_static_GetVideoFeatureResponse_descriptor =
+      getDescriptor().getMessageTypes().get(3);
+    internal_static_GetVideoFeatureResponse_fieldAccessorTable = new
+      com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
+        internal_static_GetVideoFeatureResponse_descriptor,
+        new java.lang.String[] { "Result", });
+    com.google.protobuf.AnyProto.getDescriptor();
+    com.tzld.piaoquan.recommend.server.gen.common.Common.getDescriptor();
+  }
+
+  // @@protoc_insertion_point(outer_class_scope)
+}

+ 308 - 0
recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/server/gen/feature/FeatureService.java

@@ -0,0 +1,308 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: com/tzld/piaoquan/recommend/feature/feature.proto
+
+package com.tzld.piaoquan.recommend.server.gen.feature;
+
+/**
+ * Protobuf service {@code FeatureService}
+ */
+public  abstract class FeatureService
+    implements com.google.protobuf.Service {
+  protected FeatureService() {}
+
+  public interface Interface {
+    /**
+     * <code>rpc GetUserFeature(.GetUserFeatureRequest) returns (.GetUserFeatureResponse);</code>
+     */
+    public abstract void getUserFeature(
+        com.google.protobuf.RpcController controller,
+        com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest request,
+        com.google.protobuf.RpcCallback<com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse> done);
+
+    /**
+     * <code>rpc GetVideoFeature(.GetVideoFeatureRequest) returns (.GetVideoFeatureResponse);</code>
+     */
+    public abstract void getVideoFeature(
+        com.google.protobuf.RpcController controller,
+        com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest request,
+        com.google.protobuf.RpcCallback<com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse> done);
+
+  }
+
+  public static com.google.protobuf.Service newReflectiveService(
+      final Interface impl) {
+    return new FeatureService() {
+      @java.lang.Override
+      public  void getUserFeature(
+          com.google.protobuf.RpcController controller,
+          com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest request,
+          com.google.protobuf.RpcCallback<com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse> done) {
+        impl.getUserFeature(controller, request, done);
+      }
+
+      @java.lang.Override
+      public  void getVideoFeature(
+          com.google.protobuf.RpcController controller,
+          com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest request,
+          com.google.protobuf.RpcCallback<com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse> done) {
+        impl.getVideoFeature(controller, request, done);
+      }
+
+    };
+  }
+
+  public static com.google.protobuf.BlockingService
+      newReflectiveBlockingService(final BlockingInterface impl) {
+    return new com.google.protobuf.BlockingService() {
+      public final com.google.protobuf.Descriptors.ServiceDescriptor
+          getDescriptorForType() {
+        return getDescriptor();
+      }
+
+      public final com.google.protobuf.Message callBlockingMethod(
+          com.google.protobuf.Descriptors.MethodDescriptor method,
+          com.google.protobuf.RpcController controller,
+          com.google.protobuf.Message request)
+          throws com.google.protobuf.ServiceException {
+        if (method.getService() != getDescriptor()) {
+          throw new java.lang.IllegalArgumentException(
+            "Service.callBlockingMethod() given method descriptor for " +
+            "wrong service type.");
+        }
+        switch(method.getIndex()) {
+          case 0:
+            return impl.getUserFeature(controller, (com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest)request);
+          case 1:
+            return impl.getVideoFeature(controller, (com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest)request);
+          default:
+            throw new java.lang.AssertionError("Can't get here.");
+        }
+      }
+
+      public final com.google.protobuf.Message
+          getRequestPrototype(
+          com.google.protobuf.Descriptors.MethodDescriptor method) {
+        if (method.getService() != getDescriptor()) {
+          throw new java.lang.IllegalArgumentException(
+            "Service.getRequestPrototype() given method " +
+            "descriptor for wrong service type.");
+        }
+        switch(method.getIndex()) {
+          case 0:
+            return com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest.getDefaultInstance();
+          case 1:
+            return com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest.getDefaultInstance();
+          default:
+            throw new java.lang.AssertionError("Can't get here.");
+        }
+      }
+
+      public final com.google.protobuf.Message
+          getResponsePrototype(
+          com.google.protobuf.Descriptors.MethodDescriptor method) {
+        if (method.getService() != getDescriptor()) {
+          throw new java.lang.IllegalArgumentException(
+            "Service.getResponsePrototype() given method " +
+            "descriptor for wrong service type.");
+        }
+        switch(method.getIndex()) {
+          case 0:
+            return com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse.getDefaultInstance();
+          case 1:
+            return com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse.getDefaultInstance();
+          default:
+            throw new java.lang.AssertionError("Can't get here.");
+        }
+      }
+
+    };
+  }
+
+  /**
+   * <code>rpc GetUserFeature(.GetUserFeatureRequest) returns (.GetUserFeatureResponse);</code>
+   */
+  public abstract void getUserFeature(
+      com.google.protobuf.RpcController controller,
+      com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest request,
+      com.google.protobuf.RpcCallback<com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse> done);
+
+  /**
+   * <code>rpc GetVideoFeature(.GetVideoFeatureRequest) returns (.GetVideoFeatureResponse);</code>
+   */
+  public abstract void getVideoFeature(
+      com.google.protobuf.RpcController controller,
+      com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest request,
+      com.google.protobuf.RpcCallback<com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse> done);
+
+  public static final
+      com.google.protobuf.Descriptors.ServiceDescriptor
+      getDescriptor() {
+    return com.tzld.piaoquan.recommend.server.gen.feature.Feature.getDescriptor().getServices().get(0);
+  }
+  public final com.google.protobuf.Descriptors.ServiceDescriptor
+      getDescriptorForType() {
+    return getDescriptor();
+  }
+
+  public final void callMethod(
+      com.google.protobuf.Descriptors.MethodDescriptor method,
+      com.google.protobuf.RpcController controller,
+      com.google.protobuf.Message request,
+      com.google.protobuf.RpcCallback<
+        com.google.protobuf.Message> done) {
+    if (method.getService() != getDescriptor()) {
+      throw new java.lang.IllegalArgumentException(
+        "Service.callMethod() given method descriptor for wrong " +
+        "service type.");
+    }
+    switch(method.getIndex()) {
+      case 0:
+        this.getUserFeature(controller, (com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest)request,
+          com.google.protobuf.RpcUtil.<com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse>specializeCallback(
+            done));
+        return;
+      case 1:
+        this.getVideoFeature(controller, (com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest)request,
+          com.google.protobuf.RpcUtil.<com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse>specializeCallback(
+            done));
+        return;
+      default:
+        throw new java.lang.AssertionError("Can't get here.");
+    }
+  }
+
+  public final com.google.protobuf.Message
+      getRequestPrototype(
+      com.google.protobuf.Descriptors.MethodDescriptor method) {
+    if (method.getService() != getDescriptor()) {
+      throw new java.lang.IllegalArgumentException(
+        "Service.getRequestPrototype() given method " +
+        "descriptor for wrong service type.");
+    }
+    switch(method.getIndex()) {
+      case 0:
+        return com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest.getDefaultInstance();
+      case 1:
+        return com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest.getDefaultInstance();
+      default:
+        throw new java.lang.AssertionError("Can't get here.");
+    }
+  }
+
+  public final com.google.protobuf.Message
+      getResponsePrototype(
+      com.google.protobuf.Descriptors.MethodDescriptor method) {
+    if (method.getService() != getDescriptor()) {
+      throw new java.lang.IllegalArgumentException(
+        "Service.getResponsePrototype() given method " +
+        "descriptor for wrong service type.");
+    }
+    switch(method.getIndex()) {
+      case 0:
+        return com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse.getDefaultInstance();
+      case 1:
+        return com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse.getDefaultInstance();
+      default:
+        throw new java.lang.AssertionError("Can't get here.");
+    }
+  }
+
+  public static Stub newStub(
+      com.google.protobuf.RpcChannel channel) {
+    return new Stub(channel);
+  }
+
+  public static final class Stub extends com.tzld.piaoquan.recommend.server.gen.feature.FeatureService implements Interface {
+    private Stub(com.google.protobuf.RpcChannel channel) {
+      this.channel = channel;
+    }
+
+    private final com.google.protobuf.RpcChannel channel;
+
+    public com.google.protobuf.RpcChannel getChannel() {
+      return channel;
+    }
+
+    public  void getUserFeature(
+        com.google.protobuf.RpcController controller,
+        com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest request,
+        com.google.protobuf.RpcCallback<com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse> done) {
+      channel.callMethod(
+        getDescriptor().getMethods().get(0),
+        controller,
+        request,
+        com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse.getDefaultInstance(),
+        com.google.protobuf.RpcUtil.generalizeCallback(
+          done,
+          com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse.class,
+          com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse.getDefaultInstance()));
+    }
+
+    public  void getVideoFeature(
+        com.google.protobuf.RpcController controller,
+        com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest request,
+        com.google.protobuf.RpcCallback<com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse> done) {
+      channel.callMethod(
+        getDescriptor().getMethods().get(1),
+        controller,
+        request,
+        com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse.getDefaultInstance(),
+        com.google.protobuf.RpcUtil.generalizeCallback(
+          done,
+          com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse.class,
+          com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse.getDefaultInstance()));
+    }
+  }
+
+  public static BlockingInterface newBlockingStub(
+      com.google.protobuf.BlockingRpcChannel channel) {
+    return new BlockingStub(channel);
+  }
+
+  public interface BlockingInterface {
+    public com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse getUserFeature(
+        com.google.protobuf.RpcController controller,
+        com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest request)
+        throws com.google.protobuf.ServiceException;
+
+    public com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse getVideoFeature(
+        com.google.protobuf.RpcController controller,
+        com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest request)
+        throws com.google.protobuf.ServiceException;
+  }
+
+  private static final class BlockingStub implements BlockingInterface {
+    private BlockingStub(com.google.protobuf.BlockingRpcChannel channel) {
+      this.channel = channel;
+    }
+
+    private final com.google.protobuf.BlockingRpcChannel channel;
+
+    public com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse getUserFeature(
+        com.google.protobuf.RpcController controller,
+        com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest request)
+        throws com.google.protobuf.ServiceException {
+      return (com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse) channel.callBlockingMethod(
+        getDescriptor().getMethods().get(0),
+        controller,
+        request,
+        com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse.getDefaultInstance());
+    }
+
+
+    public com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse getVideoFeature(
+        com.google.protobuf.RpcController controller,
+        com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest request)
+        throws com.google.protobuf.ServiceException {
+      return (com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse) channel.callBlockingMethod(
+        getDescriptor().getMethods().get(1),
+        controller,
+        request,
+        com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse.getDefaultInstance());
+    }
+
+  }
+
+  // @@protoc_insertion_point(class_scope:FeatureService)
+}
+

+ 355 - 0
recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/server/gen/feature/FeatureServiceGrpc.java

@@ -0,0 +1,355 @@
+package com.tzld.piaoquan.recommend.server.gen.feature;
+
+import static io.grpc.MethodDescriptor.generateFullMethodName;
+import static io.grpc.stub.ClientCalls.asyncUnaryCall;
+import static io.grpc.stub.ClientCalls.blockingServerStreamingCall;
+import static io.grpc.stub.ClientCalls.blockingUnaryCall;
+import static io.grpc.stub.ClientCalls.futureUnaryCall;
+import static io.grpc.stub.ServerCalls.asyncUnaryCall;
+import static io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall;
+
+/**
+ */
+@javax.annotation.Generated(
+    value = "by gRPC proto compiler (version 1.34.1)",
+    comments = "Source: com/tzld/piaoquan/recommend/feature/feature.proto")
+public final class FeatureServiceGrpc {
+
+  private FeatureServiceGrpc() {}
+
+  public static final String SERVICE_NAME = "FeatureService";
+
+  // Static method descriptors that strictly reflect the proto.
+  private static volatile io.grpc.MethodDescriptor<com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest,
+      com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse> getGetUserFeatureMethod;
+
+  @io.grpc.stub.annotations.RpcMethod(
+      fullMethodName = SERVICE_NAME + '/' + "GetUserFeature",
+      requestType = com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest.class,
+      responseType = com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse.class,
+      methodType = io.grpc.MethodDescriptor.MethodType.UNARY)
+  public static io.grpc.MethodDescriptor<com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest,
+      com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse> getGetUserFeatureMethod() {
+    io.grpc.MethodDescriptor<com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest, com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse> getGetUserFeatureMethod;
+    if ((getGetUserFeatureMethod = FeatureServiceGrpc.getGetUserFeatureMethod) == null) {
+      synchronized (FeatureServiceGrpc.class) {
+        if ((getGetUserFeatureMethod = FeatureServiceGrpc.getGetUserFeatureMethod) == null) {
+          FeatureServiceGrpc.getGetUserFeatureMethod = getGetUserFeatureMethod =
+              io.grpc.MethodDescriptor.<com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest, com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse>newBuilder()
+              .setType(io.grpc.MethodDescriptor.MethodType.UNARY)
+              .setFullMethodName(generateFullMethodName(SERVICE_NAME, "GetUserFeature"))
+              .setSampledToLocalTracing(true)
+              .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+                  com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest.getDefaultInstance()))
+              .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+                  com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse.getDefaultInstance()))
+              .setSchemaDescriptor(new FeatureServiceMethodDescriptorSupplier("GetUserFeature"))
+              .build();
+        }
+      }
+    }
+    return getGetUserFeatureMethod;
+  }
+
+  private static volatile io.grpc.MethodDescriptor<com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest,
+      com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse> getGetVideoFeatureMethod;
+
+  @io.grpc.stub.annotations.RpcMethod(
+      fullMethodName = SERVICE_NAME + '/' + "GetVideoFeature",
+      requestType = com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest.class,
+      responseType = com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse.class,
+      methodType = io.grpc.MethodDescriptor.MethodType.UNARY)
+  public static io.grpc.MethodDescriptor<com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest,
+      com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse> getGetVideoFeatureMethod() {
+    io.grpc.MethodDescriptor<com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest, com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse> getGetVideoFeatureMethod;
+    if ((getGetVideoFeatureMethod = FeatureServiceGrpc.getGetVideoFeatureMethod) == null) {
+      synchronized (FeatureServiceGrpc.class) {
+        if ((getGetVideoFeatureMethod = FeatureServiceGrpc.getGetVideoFeatureMethod) == null) {
+          FeatureServiceGrpc.getGetVideoFeatureMethod = getGetVideoFeatureMethod =
+              io.grpc.MethodDescriptor.<com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest, com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse>newBuilder()
+              .setType(io.grpc.MethodDescriptor.MethodType.UNARY)
+              .setFullMethodName(generateFullMethodName(SERVICE_NAME, "GetVideoFeature"))
+              .setSampledToLocalTracing(true)
+              .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+                  com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest.getDefaultInstance()))
+              .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+                  com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse.getDefaultInstance()))
+              .setSchemaDescriptor(new FeatureServiceMethodDescriptorSupplier("GetVideoFeature"))
+              .build();
+        }
+      }
+    }
+    return getGetVideoFeatureMethod;
+  }
+
+  /**
+   * Creates a new async stub that supports all call types for the service
+   */
+  public static FeatureServiceStub newStub(io.grpc.Channel channel) {
+    io.grpc.stub.AbstractStub.StubFactory<FeatureServiceStub> factory =
+      new io.grpc.stub.AbstractStub.StubFactory<FeatureServiceStub>() {
+        @java.lang.Override
+        public FeatureServiceStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+          return new FeatureServiceStub(channel, callOptions);
+        }
+      };
+    return FeatureServiceStub.newStub(factory, channel);
+  }
+
+  /**
+   * Creates a new blocking-style stub that supports unary and streaming output calls on the service
+   */
+  public static FeatureServiceBlockingStub newBlockingStub(
+      io.grpc.Channel channel) {
+    io.grpc.stub.AbstractStub.StubFactory<FeatureServiceBlockingStub> factory =
+      new io.grpc.stub.AbstractStub.StubFactory<FeatureServiceBlockingStub>() {
+        @java.lang.Override
+        public FeatureServiceBlockingStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+          return new FeatureServiceBlockingStub(channel, callOptions);
+        }
+      };
+    return FeatureServiceBlockingStub.newStub(factory, channel);
+  }
+
+  /**
+   * Creates a new ListenableFuture-style stub that supports unary calls on the service
+   */
+  public static FeatureServiceFutureStub newFutureStub(
+      io.grpc.Channel channel) {
+    io.grpc.stub.AbstractStub.StubFactory<FeatureServiceFutureStub> factory =
+      new io.grpc.stub.AbstractStub.StubFactory<FeatureServiceFutureStub>() {
+        @java.lang.Override
+        public FeatureServiceFutureStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+          return new FeatureServiceFutureStub(channel, callOptions);
+        }
+      };
+    return FeatureServiceFutureStub.newStub(factory, channel);
+  }
+
+  /**
+   */
+  public static abstract class FeatureServiceImplBase implements io.grpc.BindableService {
+
+    /**
+     */
+    public void getUserFeature(com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest request,
+        io.grpc.stub.StreamObserver<com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse> responseObserver) {
+      asyncUnimplementedUnaryCall(getGetUserFeatureMethod(), responseObserver);
+    }
+
+    /**
+     */
+    public void getVideoFeature(com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest request,
+        io.grpc.stub.StreamObserver<com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse> responseObserver) {
+      asyncUnimplementedUnaryCall(getGetVideoFeatureMethod(), responseObserver);
+    }
+
+    @java.lang.Override public final io.grpc.ServerServiceDefinition bindService() {
+      return io.grpc.ServerServiceDefinition.builder(getServiceDescriptor())
+          .addMethod(
+            getGetUserFeatureMethod(),
+            asyncUnaryCall(
+              new MethodHandlers<
+                com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest,
+                com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse>(
+                  this, METHODID_GET_USER_FEATURE)))
+          .addMethod(
+            getGetVideoFeatureMethod(),
+            asyncUnaryCall(
+              new MethodHandlers<
+                com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest,
+                com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse>(
+                  this, METHODID_GET_VIDEO_FEATURE)))
+          .build();
+    }
+  }
+
+  /**
+   */
+  public static final class FeatureServiceStub extends io.grpc.stub.AbstractAsyncStub<FeatureServiceStub> {
+    private FeatureServiceStub(
+        io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+      super(channel, callOptions);
+    }
+
+    @java.lang.Override
+    protected FeatureServiceStub build(
+        io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+      return new FeatureServiceStub(channel, callOptions);
+    }
+
+    /**
+     */
+    public void getUserFeature(com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest request,
+        io.grpc.stub.StreamObserver<com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse> responseObserver) {
+      asyncUnaryCall(
+          getChannel().newCall(getGetUserFeatureMethod(), getCallOptions()), request, responseObserver);
+    }
+
+    /**
+     */
+    public void getVideoFeature(com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest request,
+        io.grpc.stub.StreamObserver<com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse> responseObserver) {
+      asyncUnaryCall(
+          getChannel().newCall(getGetVideoFeatureMethod(), getCallOptions()), request, responseObserver);
+    }
+  }
+
+  /**
+   */
+  public static final class FeatureServiceBlockingStub extends io.grpc.stub.AbstractBlockingStub<FeatureServiceBlockingStub> {
+    private FeatureServiceBlockingStub(
+        io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+      super(channel, callOptions);
+    }
+
+    @java.lang.Override
+    protected FeatureServiceBlockingStub build(
+        io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+      return new FeatureServiceBlockingStub(channel, callOptions);
+    }
+
+    /**
+     */
+    public com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse getUserFeature(com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest request) {
+      return blockingUnaryCall(
+          getChannel(), getGetUserFeatureMethod(), getCallOptions(), request);
+    }
+
+    /**
+     */
+    public com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse getVideoFeature(com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest request) {
+      return blockingUnaryCall(
+          getChannel(), getGetVideoFeatureMethod(), getCallOptions(), request);
+    }
+  }
+
+  /**
+   */
+  public static final class FeatureServiceFutureStub extends io.grpc.stub.AbstractFutureStub<FeatureServiceFutureStub> {
+    private FeatureServiceFutureStub(
+        io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+      super(channel, callOptions);
+    }
+
+    @java.lang.Override
+    protected FeatureServiceFutureStub build(
+        io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+      return new FeatureServiceFutureStub(channel, callOptions);
+    }
+
+    /**
+     */
+    public com.google.common.util.concurrent.ListenableFuture<com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse> getUserFeature(
+        com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest request) {
+      return futureUnaryCall(
+          getChannel().newCall(getGetUserFeatureMethod(), getCallOptions()), request);
+    }
+
+    /**
+     */
+    public com.google.common.util.concurrent.ListenableFuture<com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse> getVideoFeature(
+        com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest request) {
+      return futureUnaryCall(
+          getChannel().newCall(getGetVideoFeatureMethod(), getCallOptions()), request);
+    }
+  }
+
+  private static final int METHODID_GET_USER_FEATURE = 0;
+  private static final int METHODID_GET_VIDEO_FEATURE = 1;
+
+  private static final class MethodHandlers<Req, Resp> implements
+      io.grpc.stub.ServerCalls.UnaryMethod<Req, Resp>,
+      io.grpc.stub.ServerCalls.ServerStreamingMethod<Req, Resp>,
+      io.grpc.stub.ServerCalls.ClientStreamingMethod<Req, Resp>,
+      io.grpc.stub.ServerCalls.BidiStreamingMethod<Req, Resp> {
+    private final FeatureServiceImplBase serviceImpl;
+    private final int methodId;
+
+    MethodHandlers(FeatureServiceImplBase serviceImpl, int methodId) {
+      this.serviceImpl = serviceImpl;
+      this.methodId = methodId;
+    }
+
+    @java.lang.Override
+    @java.lang.SuppressWarnings("unchecked")
+    public void invoke(Req request, io.grpc.stub.StreamObserver<Resp> responseObserver) {
+      switch (methodId) {
+        case METHODID_GET_USER_FEATURE:
+          serviceImpl.getUserFeature((com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest) request,
+              (io.grpc.stub.StreamObserver<com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse>) responseObserver);
+          break;
+        case METHODID_GET_VIDEO_FEATURE:
+          serviceImpl.getVideoFeature((com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest) request,
+              (io.grpc.stub.StreamObserver<com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse>) responseObserver);
+          break;
+        default:
+          throw new AssertionError();
+      }
+    }
+
+    @java.lang.Override
+    @java.lang.SuppressWarnings("unchecked")
+    public io.grpc.stub.StreamObserver<Req> invoke(
+        io.grpc.stub.StreamObserver<Resp> responseObserver) {
+      switch (methodId) {
+        default:
+          throw new AssertionError();
+      }
+    }
+  }
+
+  private static abstract class FeatureServiceBaseDescriptorSupplier
+      implements io.grpc.protobuf.ProtoFileDescriptorSupplier, io.grpc.protobuf.ProtoServiceDescriptorSupplier {
+    FeatureServiceBaseDescriptorSupplier() {}
+
+    @java.lang.Override
+    public com.google.protobuf.Descriptors.FileDescriptor getFileDescriptor() {
+      return com.tzld.piaoquan.recommend.server.gen.feature.Feature.getDescriptor();
+    }
+
+    @java.lang.Override
+    public com.google.protobuf.Descriptors.ServiceDescriptor getServiceDescriptor() {
+      return getFileDescriptor().findServiceByName("FeatureService");
+    }
+  }
+
+  private static final class FeatureServiceFileDescriptorSupplier
+      extends FeatureServiceBaseDescriptorSupplier {
+    FeatureServiceFileDescriptorSupplier() {}
+  }
+
+  private static final class FeatureServiceMethodDescriptorSupplier
+      extends FeatureServiceBaseDescriptorSupplier
+      implements io.grpc.protobuf.ProtoMethodDescriptorSupplier {
+    private final String methodName;
+
+    FeatureServiceMethodDescriptorSupplier(String methodName) {
+      this.methodName = methodName;
+    }
+
+    @java.lang.Override
+    public com.google.protobuf.Descriptors.MethodDescriptor getMethodDescriptor() {
+      return getServiceDescriptor().findMethodByName(methodName);
+    }
+  }
+
+  private static volatile io.grpc.ServiceDescriptor serviceDescriptor;
+
+  public static io.grpc.ServiceDescriptor getServiceDescriptor() {
+    io.grpc.ServiceDescriptor result = serviceDescriptor;
+    if (result == null) {
+      synchronized (FeatureServiceGrpc.class) {
+        result = serviceDescriptor;
+        if (result == null) {
+          serviceDescriptor = result = io.grpc.ServiceDescriptor.newBuilder(SERVICE_NAME)
+              .setSchemaDescriptor(new FeatureServiceFileDescriptorSupplier())
+              .addMethod(getGetUserFeatureMethod())
+              .addMethod(getGetVideoFeatureMethod())
+              .build();
+        }
+      }
+    }
+    return result;
+  }
+}

+ 557 - 0
recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/server/gen/feature/GetUserFeatureRequest.java

@@ -0,0 +1,557 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: com/tzld/piaoquan/recommend/feature/feature.proto
+
+package com.tzld.piaoquan.recommend.server.gen.feature;
+
+/**
+ * Protobuf type {@code GetUserFeatureRequest}
+ */
+public final class GetUserFeatureRequest extends
+    com.google.protobuf.GeneratedMessageV3 implements
+    // @@protoc_insertion_point(message_implements:GetUserFeatureRequest)
+    GetUserFeatureRequestOrBuilder {
+private static final long serialVersionUID = 0L;
+  // Use GetUserFeatureRequest.newBuilder() to construct.
+  private GetUserFeatureRequest(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
+    super(builder);
+  }
+  private GetUserFeatureRequest() {
+    uid_ = "";
+  }
+
+  @java.lang.Override
+  @SuppressWarnings({"unused"})
+  protected java.lang.Object newInstance(
+      UnusedPrivateParameter unused) {
+    return new GetUserFeatureRequest();
+  }
+
+  @java.lang.Override
+  public final com.google.protobuf.UnknownFieldSet
+  getUnknownFields() {
+    return this.unknownFields;
+  }
+  private GetUserFeatureRequest(
+      com.google.protobuf.CodedInputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    this();
+    if (extensionRegistry == null) {
+      throw new java.lang.NullPointerException();
+    }
+    com.google.protobuf.UnknownFieldSet.Builder unknownFields =
+        com.google.protobuf.UnknownFieldSet.newBuilder();
+    try {
+      boolean done = false;
+      while (!done) {
+        int tag = input.readTag();
+        switch (tag) {
+          case 0:
+            done = true;
+            break;
+          case 10: {
+            java.lang.String s = input.readStringRequireUtf8();
+
+            uid_ = s;
+            break;
+          }
+          default: {
+            if (!parseUnknownField(
+                input, unknownFields, extensionRegistry, tag)) {
+              done = true;
+            }
+            break;
+          }
+        }
+      }
+    } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+      throw e.setUnfinishedMessage(this);
+    } catch (java.io.IOException e) {
+      throw new com.google.protobuf.InvalidProtocolBufferException(
+          e).setUnfinishedMessage(this);
+    } finally {
+      this.unknownFields = unknownFields.build();
+      makeExtensionsImmutable();
+    }
+  }
+  public static final com.google.protobuf.Descriptors.Descriptor
+      getDescriptor() {
+    return com.tzld.piaoquan.recommend.server.gen.feature.Feature.internal_static_GetUserFeatureRequest_descriptor;
+  }
+
+  @java.lang.Override
+  protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internalGetFieldAccessorTable() {
+    return com.tzld.piaoquan.recommend.server.gen.feature.Feature.internal_static_GetUserFeatureRequest_fieldAccessorTable
+        .ensureFieldAccessorsInitialized(
+            com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest.class, com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest.Builder.class);
+  }
+
+  public static final int UID_FIELD_NUMBER = 1;
+  private volatile java.lang.Object uid_;
+  /**
+   * <code>string uid = 1;</code>
+   * @return The uid.
+   */
+  @java.lang.Override
+  public java.lang.String getUid() {
+    java.lang.Object ref = uid_;
+    if (ref instanceof java.lang.String) {
+      return (java.lang.String) ref;
+    } else {
+      com.google.protobuf.ByteString bs = 
+          (com.google.protobuf.ByteString) ref;
+      java.lang.String s = bs.toStringUtf8();
+      uid_ = s;
+      return s;
+    }
+  }
+  /**
+   * <code>string uid = 1;</code>
+   * @return The bytes for uid.
+   */
+  @java.lang.Override
+  public com.google.protobuf.ByteString
+      getUidBytes() {
+    java.lang.Object ref = uid_;
+    if (ref instanceof java.lang.String) {
+      com.google.protobuf.ByteString b = 
+          com.google.protobuf.ByteString.copyFromUtf8(
+              (java.lang.String) ref);
+      uid_ = b;
+      return b;
+    } else {
+      return (com.google.protobuf.ByteString) ref;
+    }
+  }
+
+  private byte memoizedIsInitialized = -1;
+  @java.lang.Override
+  public final boolean isInitialized() {
+    byte isInitialized = memoizedIsInitialized;
+    if (isInitialized == 1) return true;
+    if (isInitialized == 0) return false;
+
+    memoizedIsInitialized = 1;
+    return true;
+  }
+
+  @java.lang.Override
+  public void writeTo(com.google.protobuf.CodedOutputStream output)
+                      throws java.io.IOException {
+    if (!getUidBytes().isEmpty()) {
+      com.google.protobuf.GeneratedMessageV3.writeString(output, 1, uid_);
+    }
+    unknownFields.writeTo(output);
+  }
+
+  @java.lang.Override
+  public int getSerializedSize() {
+    int size = memoizedSize;
+    if (size != -1) return size;
+
+    size = 0;
+    if (!getUidBytes().isEmpty()) {
+      size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, uid_);
+    }
+    size += unknownFields.getSerializedSize();
+    memoizedSize = size;
+    return size;
+  }
+
+  @java.lang.Override
+  public boolean equals(final java.lang.Object obj) {
+    if (obj == this) {
+     return true;
+    }
+    if (!(obj instanceof com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest)) {
+      return super.equals(obj);
+    }
+    com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest other = (com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest) obj;
+
+    if (!getUid()
+        .equals(other.getUid())) return false;
+    if (!unknownFields.equals(other.unknownFields)) return false;
+    return true;
+  }
+
+  @java.lang.Override
+  public int hashCode() {
+    if (memoizedHashCode != 0) {
+      return memoizedHashCode;
+    }
+    int hash = 41;
+    hash = (19 * hash) + getDescriptor().hashCode();
+    hash = (37 * hash) + UID_FIELD_NUMBER;
+    hash = (53 * hash) + getUid().hashCode();
+    hash = (29 * hash) + unknownFields.hashCode();
+    memoizedHashCode = hash;
+    return hash;
+  }
+
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest parseFrom(
+      java.nio.ByteBuffer data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest parseFrom(
+      java.nio.ByteBuffer data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest parseFrom(
+      com.google.protobuf.ByteString data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest parseFrom(
+      com.google.protobuf.ByteString data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest parseFrom(byte[] data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest parseFrom(
+      byte[] data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest parseFrom(java.io.InputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest parseFrom(
+      java.io.InputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input, extensionRegistry);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest parseDelimitedFrom(java.io.InputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseDelimitedWithIOException(PARSER, input);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest parseDelimitedFrom(
+      java.io.InputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseDelimitedWithIOException(PARSER, input, extensionRegistry);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest parseFrom(
+      com.google.protobuf.CodedInputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest parseFrom(
+      com.google.protobuf.CodedInputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input, extensionRegistry);
+  }
+
+  @java.lang.Override
+  public Builder newBuilderForType() { return newBuilder(); }
+  public static Builder newBuilder() {
+    return DEFAULT_INSTANCE.toBuilder();
+  }
+  public static Builder newBuilder(com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest prototype) {
+    return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
+  }
+  @java.lang.Override
+  public Builder toBuilder() {
+    return this == DEFAULT_INSTANCE
+        ? new Builder() : new Builder().mergeFrom(this);
+  }
+
+  @java.lang.Override
+  protected Builder newBuilderForType(
+      com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+    Builder builder = new Builder(parent);
+    return builder;
+  }
+  /**
+   * Protobuf type {@code GetUserFeatureRequest}
+   */
+  public static final class Builder extends
+      com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
+      // @@protoc_insertion_point(builder_implements:GetUserFeatureRequest)
+      com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequestOrBuilder {
+    public static final com.google.protobuf.Descriptors.Descriptor
+        getDescriptor() {
+      return com.tzld.piaoquan.recommend.server.gen.feature.Feature.internal_static_GetUserFeatureRequest_descriptor;
+    }
+
+    @java.lang.Override
+    protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+        internalGetFieldAccessorTable() {
+      return com.tzld.piaoquan.recommend.server.gen.feature.Feature.internal_static_GetUserFeatureRequest_fieldAccessorTable
+          .ensureFieldAccessorsInitialized(
+              com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest.class, com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest.Builder.class);
+    }
+
+    // Construct using com.tzld.piaoquan.recommend.feature.gen.feature.GetUserFeatureRequest.newBuilder()
+    private Builder() {
+      maybeForceBuilderInitialization();
+    }
+
+    private Builder(
+        com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+      super(parent);
+      maybeForceBuilderInitialization();
+    }
+    private void maybeForceBuilderInitialization() {
+      if (com.google.protobuf.GeneratedMessageV3
+              .alwaysUseFieldBuilders) {
+      }
+    }
+    @java.lang.Override
+    public Builder clear() {
+      super.clear();
+      uid_ = "";
+
+      return this;
+    }
+
+    @java.lang.Override
+    public com.google.protobuf.Descriptors.Descriptor
+        getDescriptorForType() {
+      return com.tzld.piaoquan.recommend.server.gen.feature.Feature.internal_static_GetUserFeatureRequest_descriptor;
+    }
+
+    @java.lang.Override
+    public com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest getDefaultInstanceForType() {
+      return com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest.getDefaultInstance();
+    }
+
+    @java.lang.Override
+    public com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest build() {
+      com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest result = buildPartial();
+      if (!result.isInitialized()) {
+        throw newUninitializedMessageException(result);
+      }
+      return result;
+    }
+
+    @java.lang.Override
+    public com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest buildPartial() {
+      com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest result = new com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest(this);
+      result.uid_ = uid_;
+      onBuilt();
+      return result;
+    }
+
+    @java.lang.Override
+    public Builder clone() {
+      return super.clone();
+    }
+    @java.lang.Override
+    public Builder setField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        java.lang.Object value) {
+      return super.setField(field, value);
+    }
+    @java.lang.Override
+    public Builder clearField(
+        com.google.protobuf.Descriptors.FieldDescriptor field) {
+      return super.clearField(field);
+    }
+    @java.lang.Override
+    public Builder clearOneof(
+        com.google.protobuf.Descriptors.OneofDescriptor oneof) {
+      return super.clearOneof(oneof);
+    }
+    @java.lang.Override
+    public Builder setRepeatedField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        int index, java.lang.Object value) {
+      return super.setRepeatedField(field, index, value);
+    }
+    @java.lang.Override
+    public Builder addRepeatedField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        java.lang.Object value) {
+      return super.addRepeatedField(field, value);
+    }
+    @java.lang.Override
+    public Builder mergeFrom(com.google.protobuf.Message other) {
+      if (other instanceof com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest) {
+        return mergeFrom((com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest)other);
+      } else {
+        super.mergeFrom(other);
+        return this;
+      }
+    }
+
+    public Builder mergeFrom(com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest other) {
+      if (other == com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest.getDefaultInstance()) return this;
+      if (!other.getUid().isEmpty()) {
+        uid_ = other.uid_;
+        onChanged();
+      }
+      this.mergeUnknownFields(other.unknownFields);
+      onChanged();
+      return this;
+    }
+
+    @java.lang.Override
+    public final boolean isInitialized() {
+      return true;
+    }
+
+    @java.lang.Override
+    public Builder mergeFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest parsedMessage = null;
+      try {
+        parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
+      } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+        parsedMessage = (com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest) e.getUnfinishedMessage();
+        throw e.unwrapIOException();
+      } finally {
+        if (parsedMessage != null) {
+          mergeFrom(parsedMessage);
+        }
+      }
+      return this;
+    }
+
+    private java.lang.Object uid_ = "";
+    /**
+     * <code>string uid = 1;</code>
+     * @return The uid.
+     */
+    public java.lang.String getUid() {
+      java.lang.Object ref = uid_;
+      if (!(ref instanceof java.lang.String)) {
+        com.google.protobuf.ByteString bs =
+            (com.google.protobuf.ByteString) ref;
+        java.lang.String s = bs.toStringUtf8();
+        uid_ = s;
+        return s;
+      } else {
+        return (java.lang.String) ref;
+      }
+    }
+    /**
+     * <code>string uid = 1;</code>
+     * @return The bytes for uid.
+     */
+    public com.google.protobuf.ByteString
+        getUidBytes() {
+      java.lang.Object ref = uid_;
+      if (ref instanceof String) {
+        com.google.protobuf.ByteString b = 
+            com.google.protobuf.ByteString.copyFromUtf8(
+                (java.lang.String) ref);
+        uid_ = b;
+        return b;
+      } else {
+        return (com.google.protobuf.ByteString) ref;
+      }
+    }
+    /**
+     * <code>string uid = 1;</code>
+     * @param value The uid to set.
+     * @return This builder for chaining.
+     */
+    public Builder setUid(
+        java.lang.String value) {
+      if (value == null) {
+    throw new NullPointerException();
+  }
+  
+      uid_ = value;
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>string uid = 1;</code>
+     * @return This builder for chaining.
+     */
+    public Builder clearUid() {
+      
+      uid_ = getDefaultInstance().getUid();
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>string uid = 1;</code>
+     * @param value The bytes for uid to set.
+     * @return This builder for chaining.
+     */
+    public Builder setUidBytes(
+        com.google.protobuf.ByteString value) {
+      if (value == null) {
+    throw new NullPointerException();
+  }
+  checkByteStringIsUtf8(value);
+      
+      uid_ = value;
+      onChanged();
+      return this;
+    }
+    @java.lang.Override
+    public final Builder setUnknownFields(
+        final com.google.protobuf.UnknownFieldSet unknownFields) {
+      return super.setUnknownFields(unknownFields);
+    }
+
+    @java.lang.Override
+    public final Builder mergeUnknownFields(
+        final com.google.protobuf.UnknownFieldSet unknownFields) {
+      return super.mergeUnknownFields(unknownFields);
+    }
+
+
+    // @@protoc_insertion_point(builder_scope:GetUserFeatureRequest)
+  }
+
+  // @@protoc_insertion_point(class_scope:GetUserFeatureRequest)
+  private static final com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest DEFAULT_INSTANCE;
+  static {
+    DEFAULT_INSTANCE = new com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest();
+  }
+
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest getDefaultInstance() {
+    return DEFAULT_INSTANCE;
+  }
+
+  private static final com.google.protobuf.Parser<GetUserFeatureRequest>
+      PARSER = new com.google.protobuf.AbstractParser<GetUserFeatureRequest>() {
+    @java.lang.Override
+    public GetUserFeatureRequest parsePartialFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return new GetUserFeatureRequest(input, extensionRegistry);
+    }
+  };
+
+  public static com.google.protobuf.Parser<GetUserFeatureRequest> parser() {
+    return PARSER;
+  }
+
+  @java.lang.Override
+  public com.google.protobuf.Parser<GetUserFeatureRequest> getParserForType() {
+    return PARSER;
+  }
+
+  @java.lang.Override
+  public com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest getDefaultInstanceForType() {
+    return DEFAULT_INSTANCE;
+  }
+
+}
+

+ 21 - 0
recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/server/gen/feature/GetUserFeatureRequestOrBuilder.java

@@ -0,0 +1,21 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: com/tzld/piaoquan/recommend/feature/feature.proto
+
+package com.tzld.piaoquan.recommend.server.gen.feature;
+
+public interface GetUserFeatureRequestOrBuilder extends
+    // @@protoc_insertion_point(interface_extends:GetUserFeatureRequest)
+    com.google.protobuf.MessageOrBuilder {
+
+  /**
+   * <code>string uid = 1;</code>
+   * @return The uid.
+   */
+  java.lang.String getUid();
+  /**
+   * <code>string uid = 1;</code>
+   * @return The bytes for uid.
+   */
+  com.google.protobuf.ByteString
+      getUidBytes();
+}

+ 607 - 0
recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/server/gen/feature/GetUserFeatureResponse.java

@@ -0,0 +1,607 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: com/tzld/piaoquan/recommend/feature/feature.proto
+
+package com.tzld.piaoquan.recommend.server.gen.feature;
+
+/**
+ * Protobuf type {@code GetUserFeatureResponse}
+ */
+public final class GetUserFeatureResponse extends
+    com.google.protobuf.GeneratedMessageV3 implements
+    // @@protoc_insertion_point(message_implements:GetUserFeatureResponse)
+    GetUserFeatureResponseOrBuilder {
+private static final long serialVersionUID = 0L;
+  // Use GetUserFeatureResponse.newBuilder() to construct.
+  private GetUserFeatureResponse(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
+    super(builder);
+  }
+  private GetUserFeatureResponse() {
+  }
+
+  @java.lang.Override
+  @SuppressWarnings({"unused"})
+  protected java.lang.Object newInstance(
+      UnusedPrivateParameter unused) {
+    return new GetUserFeatureResponse();
+  }
+
+  @java.lang.Override
+  public final com.google.protobuf.UnknownFieldSet
+  getUnknownFields() {
+    return this.unknownFields;
+  }
+  private GetUserFeatureResponse(
+      com.google.protobuf.CodedInputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    this();
+    if (extensionRegistry == null) {
+      throw new java.lang.NullPointerException();
+    }
+    com.google.protobuf.UnknownFieldSet.Builder unknownFields =
+        com.google.protobuf.UnknownFieldSet.newBuilder();
+    try {
+      boolean done = false;
+      while (!done) {
+        int tag = input.readTag();
+        switch (tag) {
+          case 0:
+            done = true;
+            break;
+          case 10: {
+            com.tzld.piaoquan.recommend.server.gen.common.Result.Builder subBuilder = null;
+            if (result_ != null) {
+              subBuilder = result_.toBuilder();
+            }
+            result_ = input.readMessage(com.tzld.piaoquan.recommend.server.gen.common.Result.parser(), extensionRegistry);
+            if (subBuilder != null) {
+              subBuilder.mergeFrom(result_);
+              result_ = subBuilder.buildPartial();
+            }
+
+            break;
+          }
+          default: {
+            if (!parseUnknownField(
+                input, unknownFields, extensionRegistry, tag)) {
+              done = true;
+            }
+            break;
+          }
+        }
+      }
+    } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+      throw e.setUnfinishedMessage(this);
+    } catch (java.io.IOException e) {
+      throw new com.google.protobuf.InvalidProtocolBufferException(
+          e).setUnfinishedMessage(this);
+    } finally {
+      this.unknownFields = unknownFields.build();
+      makeExtensionsImmutable();
+    }
+  }
+  public static final com.google.protobuf.Descriptors.Descriptor
+      getDescriptor() {
+    return com.tzld.piaoquan.recommend.server.gen.feature.Feature.internal_static_GetUserFeatureResponse_descriptor;
+  }
+
+  @java.lang.Override
+  protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internalGetFieldAccessorTable() {
+    return com.tzld.piaoquan.recommend.server.gen.feature.Feature.internal_static_GetUserFeatureResponse_fieldAccessorTable
+        .ensureFieldAccessorsInitialized(
+            com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse.class, com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse.Builder.class);
+  }
+
+  public static final int RESULT_FIELD_NUMBER = 1;
+  private com.tzld.piaoquan.recommend.server.gen.common.Result result_;
+  /**
+   * <code>.Result result = 1;</code>
+   * @return Whether the result field is set.
+   */
+  @java.lang.Override
+  public boolean hasResult() {
+    return result_ != null;
+  }
+  /**
+   * <code>.Result result = 1;</code>
+   * @return The result.
+   */
+  @java.lang.Override
+  public com.tzld.piaoquan.recommend.server.gen.common.Result getResult() {
+    return result_ == null ? com.tzld.piaoquan.recommend.server.gen.common.Result.getDefaultInstance() : result_;
+  }
+  /**
+   * <code>.Result result = 1;</code>
+   */
+  @java.lang.Override
+  public com.tzld.piaoquan.recommend.server.gen.common.ResultOrBuilder getResultOrBuilder() {
+    return getResult();
+  }
+
+  private byte memoizedIsInitialized = -1;
+  @java.lang.Override
+  public final boolean isInitialized() {
+    byte isInitialized = memoizedIsInitialized;
+    if (isInitialized == 1) return true;
+    if (isInitialized == 0) return false;
+
+    memoizedIsInitialized = 1;
+    return true;
+  }
+
+  @java.lang.Override
+  public void writeTo(com.google.protobuf.CodedOutputStream output)
+                      throws java.io.IOException {
+    if (result_ != null) {
+      output.writeMessage(1, getResult());
+    }
+    unknownFields.writeTo(output);
+  }
+
+  @java.lang.Override
+  public int getSerializedSize() {
+    int size = memoizedSize;
+    if (size != -1) return size;
+
+    size = 0;
+    if (result_ != null) {
+      size += com.google.protobuf.CodedOutputStream
+        .computeMessageSize(1, getResult());
+    }
+    size += unknownFields.getSerializedSize();
+    memoizedSize = size;
+    return size;
+  }
+
+  @java.lang.Override
+  public boolean equals(final java.lang.Object obj) {
+    if (obj == this) {
+     return true;
+    }
+    if (!(obj instanceof com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse)) {
+      return super.equals(obj);
+    }
+    com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse other = (com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse) obj;
+
+    if (hasResult() != other.hasResult()) return false;
+    if (hasResult()) {
+      if (!getResult()
+          .equals(other.getResult())) return false;
+    }
+    if (!unknownFields.equals(other.unknownFields)) return false;
+    return true;
+  }
+
+  @java.lang.Override
+  public int hashCode() {
+    if (memoizedHashCode != 0) {
+      return memoizedHashCode;
+    }
+    int hash = 41;
+    hash = (19 * hash) + getDescriptor().hashCode();
+    if (hasResult()) {
+      hash = (37 * hash) + RESULT_FIELD_NUMBER;
+      hash = (53 * hash) + getResult().hashCode();
+    }
+    hash = (29 * hash) + unknownFields.hashCode();
+    memoizedHashCode = hash;
+    return hash;
+  }
+
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse parseFrom(
+      java.nio.ByteBuffer data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse parseFrom(
+      java.nio.ByteBuffer data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse parseFrom(
+      com.google.protobuf.ByteString data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse parseFrom(
+      com.google.protobuf.ByteString data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse parseFrom(byte[] data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse parseFrom(
+      byte[] data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse parseFrom(java.io.InputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse parseFrom(
+      java.io.InputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input, extensionRegistry);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse parseDelimitedFrom(java.io.InputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseDelimitedWithIOException(PARSER, input);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse parseDelimitedFrom(
+      java.io.InputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseDelimitedWithIOException(PARSER, input, extensionRegistry);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse parseFrom(
+      com.google.protobuf.CodedInputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse parseFrom(
+      com.google.protobuf.CodedInputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input, extensionRegistry);
+  }
+
+  @java.lang.Override
+  public Builder newBuilderForType() { return newBuilder(); }
+  public static Builder newBuilder() {
+    return DEFAULT_INSTANCE.toBuilder();
+  }
+  public static Builder newBuilder(com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse prototype) {
+    return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
+  }
+  @java.lang.Override
+  public Builder toBuilder() {
+    return this == DEFAULT_INSTANCE
+        ? new Builder() : new Builder().mergeFrom(this);
+  }
+
+  @java.lang.Override
+  protected Builder newBuilderForType(
+      com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+    Builder builder = new Builder(parent);
+    return builder;
+  }
+  /**
+   * Protobuf type {@code GetUserFeatureResponse}
+   */
+  public static final class Builder extends
+      com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
+      // @@protoc_insertion_point(builder_implements:GetUserFeatureResponse)
+      com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponseOrBuilder {
+    public static final com.google.protobuf.Descriptors.Descriptor
+        getDescriptor() {
+      return com.tzld.piaoquan.recommend.server.gen.feature.Feature.internal_static_GetUserFeatureResponse_descriptor;
+    }
+
+    @java.lang.Override
+    protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+        internalGetFieldAccessorTable() {
+      return com.tzld.piaoquan.recommend.server.gen.feature.Feature.internal_static_GetUserFeatureResponse_fieldAccessorTable
+          .ensureFieldAccessorsInitialized(
+              com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse.class, com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse.Builder.class);
+    }
+
+    // Construct using com.tzld.piaoquan.recommend.feature.gen.feature.GetUserFeatureResponse.newBuilder()
+    private Builder() {
+      maybeForceBuilderInitialization();
+    }
+
+    private Builder(
+        com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+      super(parent);
+      maybeForceBuilderInitialization();
+    }
+    private void maybeForceBuilderInitialization() {
+      if (com.google.protobuf.GeneratedMessageV3
+              .alwaysUseFieldBuilders) {
+      }
+    }
+    @java.lang.Override
+    public Builder clear() {
+      super.clear();
+      if (resultBuilder_ == null) {
+        result_ = null;
+      } else {
+        result_ = null;
+        resultBuilder_ = null;
+      }
+      return this;
+    }
+
+    @java.lang.Override
+    public com.google.protobuf.Descriptors.Descriptor
+        getDescriptorForType() {
+      return com.tzld.piaoquan.recommend.server.gen.feature.Feature.internal_static_GetUserFeatureResponse_descriptor;
+    }
+
+    @java.lang.Override
+    public com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse getDefaultInstanceForType() {
+      return com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse.getDefaultInstance();
+    }
+
+    @java.lang.Override
+    public com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse build() {
+      com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse result = buildPartial();
+      if (!result.isInitialized()) {
+        throw newUninitializedMessageException(result);
+      }
+      return result;
+    }
+
+    @java.lang.Override
+    public com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse buildPartial() {
+      com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse result = new com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse(this);
+      if (resultBuilder_ == null) {
+        result.result_ = result_;
+      } else {
+        result.result_ = resultBuilder_.build();
+      }
+      onBuilt();
+      return result;
+    }
+
+    @java.lang.Override
+    public Builder clone() {
+      return super.clone();
+    }
+    @java.lang.Override
+    public Builder setField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        java.lang.Object value) {
+      return super.setField(field, value);
+    }
+    @java.lang.Override
+    public Builder clearField(
+        com.google.protobuf.Descriptors.FieldDescriptor field) {
+      return super.clearField(field);
+    }
+    @java.lang.Override
+    public Builder clearOneof(
+        com.google.protobuf.Descriptors.OneofDescriptor oneof) {
+      return super.clearOneof(oneof);
+    }
+    @java.lang.Override
+    public Builder setRepeatedField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        int index, java.lang.Object value) {
+      return super.setRepeatedField(field, index, value);
+    }
+    @java.lang.Override
+    public Builder addRepeatedField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        java.lang.Object value) {
+      return super.addRepeatedField(field, value);
+    }
+    @java.lang.Override
+    public Builder mergeFrom(com.google.protobuf.Message other) {
+      if (other instanceof com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse) {
+        return mergeFrom((com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse)other);
+      } else {
+        super.mergeFrom(other);
+        return this;
+      }
+    }
+
+    public Builder mergeFrom(com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse other) {
+      if (other == com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse.getDefaultInstance()) return this;
+      if (other.hasResult()) {
+        mergeResult(other.getResult());
+      }
+      this.mergeUnknownFields(other.unknownFields);
+      onChanged();
+      return this;
+    }
+
+    @java.lang.Override
+    public final boolean isInitialized() {
+      return true;
+    }
+
+    @java.lang.Override
+    public Builder mergeFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse parsedMessage = null;
+      try {
+        parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
+      } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+        parsedMessage = (com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse) e.getUnfinishedMessage();
+        throw e.unwrapIOException();
+      } finally {
+        if (parsedMessage != null) {
+          mergeFrom(parsedMessage);
+        }
+      }
+      return this;
+    }
+
+    private com.tzld.piaoquan.recommend.server.gen.common.Result result_;
+    private com.google.protobuf.SingleFieldBuilderV3<
+        com.tzld.piaoquan.recommend.server.gen.common.Result, com.tzld.piaoquan.recommend.server.gen.common.Result.Builder, com.tzld.piaoquan.recommend.server.gen.common.ResultOrBuilder> resultBuilder_;
+    /**
+     * <code>.Result result = 1;</code>
+     * @return Whether the result field is set.
+     */
+    public boolean hasResult() {
+      return resultBuilder_ != null || result_ != null;
+    }
+    /**
+     * <code>.Result result = 1;</code>
+     * @return The result.
+     */
+    public com.tzld.piaoquan.recommend.server.gen.common.Result getResult() {
+      if (resultBuilder_ == null) {
+        return result_ == null ? com.tzld.piaoquan.recommend.server.gen.common.Result.getDefaultInstance() : result_;
+      } else {
+        return resultBuilder_.getMessage();
+      }
+    }
+    /**
+     * <code>.Result result = 1;</code>
+     */
+    public Builder setResult(com.tzld.piaoquan.recommend.server.gen.common.Result value) {
+      if (resultBuilder_ == null) {
+        if (value == null) {
+          throw new NullPointerException();
+        }
+        result_ = value;
+        onChanged();
+      } else {
+        resultBuilder_.setMessage(value);
+      }
+
+      return this;
+    }
+    /**
+     * <code>.Result result = 1;</code>
+     */
+    public Builder setResult(
+        com.tzld.piaoquan.recommend.server.gen.common.Result.Builder builderForValue) {
+      if (resultBuilder_ == null) {
+        result_ = builderForValue.build();
+        onChanged();
+      } else {
+        resultBuilder_.setMessage(builderForValue.build());
+      }
+
+      return this;
+    }
+    /**
+     * <code>.Result result = 1;</code>
+     */
+    public Builder mergeResult(com.tzld.piaoquan.recommend.server.gen.common.Result value) {
+      if (resultBuilder_ == null) {
+        if (result_ != null) {
+          result_ =
+            com.tzld.piaoquan.recommend.server.gen.common.Result.newBuilder(result_).mergeFrom(value).buildPartial();
+        } else {
+          result_ = value;
+        }
+        onChanged();
+      } else {
+        resultBuilder_.mergeFrom(value);
+      }
+
+      return this;
+    }
+    /**
+     * <code>.Result result = 1;</code>
+     */
+    public Builder clearResult() {
+      if (resultBuilder_ == null) {
+        result_ = null;
+        onChanged();
+      } else {
+        result_ = null;
+        resultBuilder_ = null;
+      }
+
+      return this;
+    }
+    /**
+     * <code>.Result result = 1;</code>
+     */
+    public com.tzld.piaoquan.recommend.server.gen.common.Result.Builder getResultBuilder() {
+      
+      onChanged();
+      return getResultFieldBuilder().getBuilder();
+    }
+    /**
+     * <code>.Result result = 1;</code>
+     */
+    public com.tzld.piaoquan.recommend.server.gen.common.ResultOrBuilder getResultOrBuilder() {
+      if (resultBuilder_ != null) {
+        return resultBuilder_.getMessageOrBuilder();
+      } else {
+        return result_ == null ?
+            com.tzld.piaoquan.recommend.server.gen.common.Result.getDefaultInstance() : result_;
+      }
+    }
+    /**
+     * <code>.Result result = 1;</code>
+     */
+    private com.google.protobuf.SingleFieldBuilderV3<
+        com.tzld.piaoquan.recommend.server.gen.common.Result, com.tzld.piaoquan.recommend.server.gen.common.Result.Builder, com.tzld.piaoquan.recommend.server.gen.common.ResultOrBuilder>
+        getResultFieldBuilder() {
+      if (resultBuilder_ == null) {
+        resultBuilder_ = new com.google.protobuf.SingleFieldBuilderV3<
+            com.tzld.piaoquan.recommend.server.gen.common.Result, com.tzld.piaoquan.recommend.server.gen.common.Result.Builder, com.tzld.piaoquan.recommend.server.gen.common.ResultOrBuilder>(
+                getResult(),
+                getParentForChildren(),
+                isClean());
+        result_ = null;
+      }
+      return resultBuilder_;
+    }
+    @java.lang.Override
+    public final Builder setUnknownFields(
+        final com.google.protobuf.UnknownFieldSet unknownFields) {
+      return super.setUnknownFields(unknownFields);
+    }
+
+    @java.lang.Override
+    public final Builder mergeUnknownFields(
+        final com.google.protobuf.UnknownFieldSet unknownFields) {
+      return super.mergeUnknownFields(unknownFields);
+    }
+
+
+    // @@protoc_insertion_point(builder_scope:GetUserFeatureResponse)
+  }
+
+  // @@protoc_insertion_point(class_scope:GetUserFeatureResponse)
+  private static final com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse DEFAULT_INSTANCE;
+  static {
+    DEFAULT_INSTANCE = new com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse();
+  }
+
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse getDefaultInstance() {
+    return DEFAULT_INSTANCE;
+  }
+
+  private static final com.google.protobuf.Parser<GetUserFeatureResponse>
+      PARSER = new com.google.protobuf.AbstractParser<GetUserFeatureResponse>() {
+    @java.lang.Override
+    public GetUserFeatureResponse parsePartialFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return new GetUserFeatureResponse(input, extensionRegistry);
+    }
+  };
+
+  public static com.google.protobuf.Parser<GetUserFeatureResponse> parser() {
+    return PARSER;
+  }
+
+  @java.lang.Override
+  public com.google.protobuf.Parser<GetUserFeatureResponse> getParserForType() {
+    return PARSER;
+  }
+
+  @java.lang.Override
+  public com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse getDefaultInstanceForType() {
+    return DEFAULT_INSTANCE;
+  }
+
+}
+

+ 24 - 0
recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/server/gen/feature/GetUserFeatureResponseOrBuilder.java

@@ -0,0 +1,24 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: com/tzld/piaoquan/recommend/feature/feature.proto
+
+package com.tzld.piaoquan.recommend.server.gen.feature;
+
+public interface GetUserFeatureResponseOrBuilder extends
+    // @@protoc_insertion_point(interface_extends:GetUserFeatureResponse)
+    com.google.protobuf.MessageOrBuilder {
+
+  /**
+   * <code>.Result result = 1;</code>
+   * @return Whether the result field is set.
+   */
+  boolean hasResult();
+  /**
+   * <code>.Result result = 1;</code>
+   * @return The result.
+   */
+  com.tzld.piaoquan.recommend.server.gen.common.Result getResult();
+  /**
+   * <code>.Result result = 1;</code>
+   */
+  com.tzld.piaoquan.recommend.server.gen.common.ResultOrBuilder getResultOrBuilder();
+}

+ 557 - 0
recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/server/gen/feature/GetVideoFeatureRequest.java

@@ -0,0 +1,557 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: com/tzld/piaoquan/recommend/feature/feature.proto
+
+package com.tzld.piaoquan.recommend.server.gen.feature;
+
+/**
+ * Protobuf type {@code GetVideoFeatureRequest}
+ */
+public final class GetVideoFeatureRequest extends
+    com.google.protobuf.GeneratedMessageV3 implements
+    // @@protoc_insertion_point(message_implements:GetVideoFeatureRequest)
+    GetVideoFeatureRequestOrBuilder {
+private static final long serialVersionUID = 0L;
+  // Use GetVideoFeatureRequest.newBuilder() to construct.
+  private GetVideoFeatureRequest(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
+    super(builder);
+  }
+  private GetVideoFeatureRequest() {
+    uid_ = "";
+  }
+
+  @java.lang.Override
+  @SuppressWarnings({"unused"})
+  protected java.lang.Object newInstance(
+      UnusedPrivateParameter unused) {
+    return new GetVideoFeatureRequest();
+  }
+
+  @java.lang.Override
+  public final com.google.protobuf.UnknownFieldSet
+  getUnknownFields() {
+    return this.unknownFields;
+  }
+  private GetVideoFeatureRequest(
+      com.google.protobuf.CodedInputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    this();
+    if (extensionRegistry == null) {
+      throw new java.lang.NullPointerException();
+    }
+    com.google.protobuf.UnknownFieldSet.Builder unknownFields =
+        com.google.protobuf.UnknownFieldSet.newBuilder();
+    try {
+      boolean done = false;
+      while (!done) {
+        int tag = input.readTag();
+        switch (tag) {
+          case 0:
+            done = true;
+            break;
+          case 10: {
+            java.lang.String s = input.readStringRequireUtf8();
+
+            uid_ = s;
+            break;
+          }
+          default: {
+            if (!parseUnknownField(
+                input, unknownFields, extensionRegistry, tag)) {
+              done = true;
+            }
+            break;
+          }
+        }
+      }
+    } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+      throw e.setUnfinishedMessage(this);
+    } catch (java.io.IOException e) {
+      throw new com.google.protobuf.InvalidProtocolBufferException(
+          e).setUnfinishedMessage(this);
+    } finally {
+      this.unknownFields = unknownFields.build();
+      makeExtensionsImmutable();
+    }
+  }
+  public static final com.google.protobuf.Descriptors.Descriptor
+      getDescriptor() {
+    return com.tzld.piaoquan.recommend.server.gen.feature.Feature.internal_static_GetVideoFeatureRequest_descriptor;
+  }
+
+  @java.lang.Override
+  protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internalGetFieldAccessorTable() {
+    return com.tzld.piaoquan.recommend.server.gen.feature.Feature.internal_static_GetVideoFeatureRequest_fieldAccessorTable
+        .ensureFieldAccessorsInitialized(
+            com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest.class, com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest.Builder.class);
+  }
+
+  public static final int UID_FIELD_NUMBER = 1;
+  private volatile java.lang.Object uid_;
+  /**
+   * <code>string uid = 1;</code>
+   * @return The uid.
+   */
+  @java.lang.Override
+  public java.lang.String getUid() {
+    java.lang.Object ref = uid_;
+    if (ref instanceof java.lang.String) {
+      return (java.lang.String) ref;
+    } else {
+      com.google.protobuf.ByteString bs = 
+          (com.google.protobuf.ByteString) ref;
+      java.lang.String s = bs.toStringUtf8();
+      uid_ = s;
+      return s;
+    }
+  }
+  /**
+   * <code>string uid = 1;</code>
+   * @return The bytes for uid.
+   */
+  @java.lang.Override
+  public com.google.protobuf.ByteString
+      getUidBytes() {
+    java.lang.Object ref = uid_;
+    if (ref instanceof java.lang.String) {
+      com.google.protobuf.ByteString b = 
+          com.google.protobuf.ByteString.copyFromUtf8(
+              (java.lang.String) ref);
+      uid_ = b;
+      return b;
+    } else {
+      return (com.google.protobuf.ByteString) ref;
+    }
+  }
+
+  private byte memoizedIsInitialized = -1;
+  @java.lang.Override
+  public final boolean isInitialized() {
+    byte isInitialized = memoizedIsInitialized;
+    if (isInitialized == 1) return true;
+    if (isInitialized == 0) return false;
+
+    memoizedIsInitialized = 1;
+    return true;
+  }
+
+  @java.lang.Override
+  public void writeTo(com.google.protobuf.CodedOutputStream output)
+                      throws java.io.IOException {
+    if (!getUidBytes().isEmpty()) {
+      com.google.protobuf.GeneratedMessageV3.writeString(output, 1, uid_);
+    }
+    unknownFields.writeTo(output);
+  }
+
+  @java.lang.Override
+  public int getSerializedSize() {
+    int size = memoizedSize;
+    if (size != -1) return size;
+
+    size = 0;
+    if (!getUidBytes().isEmpty()) {
+      size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, uid_);
+    }
+    size += unknownFields.getSerializedSize();
+    memoizedSize = size;
+    return size;
+  }
+
+  @java.lang.Override
+  public boolean equals(final java.lang.Object obj) {
+    if (obj == this) {
+     return true;
+    }
+    if (!(obj instanceof com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest)) {
+      return super.equals(obj);
+    }
+    com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest other = (com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest) obj;
+
+    if (!getUid()
+        .equals(other.getUid())) return false;
+    if (!unknownFields.equals(other.unknownFields)) return false;
+    return true;
+  }
+
+  @java.lang.Override
+  public int hashCode() {
+    if (memoizedHashCode != 0) {
+      return memoizedHashCode;
+    }
+    int hash = 41;
+    hash = (19 * hash) + getDescriptor().hashCode();
+    hash = (37 * hash) + UID_FIELD_NUMBER;
+    hash = (53 * hash) + getUid().hashCode();
+    hash = (29 * hash) + unknownFields.hashCode();
+    memoizedHashCode = hash;
+    return hash;
+  }
+
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest parseFrom(
+      java.nio.ByteBuffer data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest parseFrom(
+      java.nio.ByteBuffer data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest parseFrom(
+      com.google.protobuf.ByteString data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest parseFrom(
+      com.google.protobuf.ByteString data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest parseFrom(byte[] data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest parseFrom(
+      byte[] data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest parseFrom(java.io.InputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest parseFrom(
+      java.io.InputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input, extensionRegistry);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest parseDelimitedFrom(java.io.InputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseDelimitedWithIOException(PARSER, input);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest parseDelimitedFrom(
+      java.io.InputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseDelimitedWithIOException(PARSER, input, extensionRegistry);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest parseFrom(
+      com.google.protobuf.CodedInputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest parseFrom(
+      com.google.protobuf.CodedInputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input, extensionRegistry);
+  }
+
+  @java.lang.Override
+  public Builder newBuilderForType() { return newBuilder(); }
+  public static Builder newBuilder() {
+    return DEFAULT_INSTANCE.toBuilder();
+  }
+  public static Builder newBuilder(com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest prototype) {
+    return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
+  }
+  @java.lang.Override
+  public Builder toBuilder() {
+    return this == DEFAULT_INSTANCE
+        ? new Builder() : new Builder().mergeFrom(this);
+  }
+
+  @java.lang.Override
+  protected Builder newBuilderForType(
+      com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+    Builder builder = new Builder(parent);
+    return builder;
+  }
+  /**
+   * Protobuf type {@code GetVideoFeatureRequest}
+   */
+  public static final class Builder extends
+      com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
+      // @@protoc_insertion_point(builder_implements:GetVideoFeatureRequest)
+      com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequestOrBuilder {
+    public static final com.google.protobuf.Descriptors.Descriptor
+        getDescriptor() {
+      return com.tzld.piaoquan.recommend.server.gen.feature.Feature.internal_static_GetVideoFeatureRequest_descriptor;
+    }
+
+    @java.lang.Override
+    protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+        internalGetFieldAccessorTable() {
+      return com.tzld.piaoquan.recommend.server.gen.feature.Feature.internal_static_GetVideoFeatureRequest_fieldAccessorTable
+          .ensureFieldAccessorsInitialized(
+              com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest.class, com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest.Builder.class);
+    }
+
+    // Construct using com.tzld.piaoquan.recommend.feature.gen.feature.GetVideoFeatureRequest.newBuilder()
+    private Builder() {
+      maybeForceBuilderInitialization();
+    }
+
+    private Builder(
+        com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+      super(parent);
+      maybeForceBuilderInitialization();
+    }
+    private void maybeForceBuilderInitialization() {
+      if (com.google.protobuf.GeneratedMessageV3
+              .alwaysUseFieldBuilders) {
+      }
+    }
+    @java.lang.Override
+    public Builder clear() {
+      super.clear();
+      uid_ = "";
+
+      return this;
+    }
+
+    @java.lang.Override
+    public com.google.protobuf.Descriptors.Descriptor
+        getDescriptorForType() {
+      return com.tzld.piaoquan.recommend.server.gen.feature.Feature.internal_static_GetVideoFeatureRequest_descriptor;
+    }
+
+    @java.lang.Override
+    public com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest getDefaultInstanceForType() {
+      return com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest.getDefaultInstance();
+    }
+
+    @java.lang.Override
+    public com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest build() {
+      com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest result = buildPartial();
+      if (!result.isInitialized()) {
+        throw newUninitializedMessageException(result);
+      }
+      return result;
+    }
+
+    @java.lang.Override
+    public com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest buildPartial() {
+      com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest result = new com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest(this);
+      result.uid_ = uid_;
+      onBuilt();
+      return result;
+    }
+
+    @java.lang.Override
+    public Builder clone() {
+      return super.clone();
+    }
+    @java.lang.Override
+    public Builder setField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        java.lang.Object value) {
+      return super.setField(field, value);
+    }
+    @java.lang.Override
+    public Builder clearField(
+        com.google.protobuf.Descriptors.FieldDescriptor field) {
+      return super.clearField(field);
+    }
+    @java.lang.Override
+    public Builder clearOneof(
+        com.google.protobuf.Descriptors.OneofDescriptor oneof) {
+      return super.clearOneof(oneof);
+    }
+    @java.lang.Override
+    public Builder setRepeatedField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        int index, java.lang.Object value) {
+      return super.setRepeatedField(field, index, value);
+    }
+    @java.lang.Override
+    public Builder addRepeatedField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        java.lang.Object value) {
+      return super.addRepeatedField(field, value);
+    }
+    @java.lang.Override
+    public Builder mergeFrom(com.google.protobuf.Message other) {
+      if (other instanceof com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest) {
+        return mergeFrom((com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest)other);
+      } else {
+        super.mergeFrom(other);
+        return this;
+      }
+    }
+
+    public Builder mergeFrom(com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest other) {
+      if (other == com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest.getDefaultInstance()) return this;
+      if (!other.getUid().isEmpty()) {
+        uid_ = other.uid_;
+        onChanged();
+      }
+      this.mergeUnknownFields(other.unknownFields);
+      onChanged();
+      return this;
+    }
+
+    @java.lang.Override
+    public final boolean isInitialized() {
+      return true;
+    }
+
+    @java.lang.Override
+    public Builder mergeFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest parsedMessage = null;
+      try {
+        parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
+      } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+        parsedMessage = (com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest) e.getUnfinishedMessage();
+        throw e.unwrapIOException();
+      } finally {
+        if (parsedMessage != null) {
+          mergeFrom(parsedMessage);
+        }
+      }
+      return this;
+    }
+
+    private java.lang.Object uid_ = "";
+    /**
+     * <code>string uid = 1;</code>
+     * @return The uid.
+     */
+    public java.lang.String getUid() {
+      java.lang.Object ref = uid_;
+      if (!(ref instanceof java.lang.String)) {
+        com.google.protobuf.ByteString bs =
+            (com.google.protobuf.ByteString) ref;
+        java.lang.String s = bs.toStringUtf8();
+        uid_ = s;
+        return s;
+      } else {
+        return (java.lang.String) ref;
+      }
+    }
+    /**
+     * <code>string uid = 1;</code>
+     * @return The bytes for uid.
+     */
+    public com.google.protobuf.ByteString
+        getUidBytes() {
+      java.lang.Object ref = uid_;
+      if (ref instanceof String) {
+        com.google.protobuf.ByteString b = 
+            com.google.protobuf.ByteString.copyFromUtf8(
+                (java.lang.String) ref);
+        uid_ = b;
+        return b;
+      } else {
+        return (com.google.protobuf.ByteString) ref;
+      }
+    }
+    /**
+     * <code>string uid = 1;</code>
+     * @param value The uid to set.
+     * @return This builder for chaining.
+     */
+    public Builder setUid(
+        java.lang.String value) {
+      if (value == null) {
+    throw new NullPointerException();
+  }
+  
+      uid_ = value;
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>string uid = 1;</code>
+     * @return This builder for chaining.
+     */
+    public Builder clearUid() {
+      
+      uid_ = getDefaultInstance().getUid();
+      onChanged();
+      return this;
+    }
+    /**
+     * <code>string uid = 1;</code>
+     * @param value The bytes for uid to set.
+     * @return This builder for chaining.
+     */
+    public Builder setUidBytes(
+        com.google.protobuf.ByteString value) {
+      if (value == null) {
+    throw new NullPointerException();
+  }
+  checkByteStringIsUtf8(value);
+      
+      uid_ = value;
+      onChanged();
+      return this;
+    }
+    @java.lang.Override
+    public final Builder setUnknownFields(
+        final com.google.protobuf.UnknownFieldSet unknownFields) {
+      return super.setUnknownFields(unknownFields);
+    }
+
+    @java.lang.Override
+    public final Builder mergeUnknownFields(
+        final com.google.protobuf.UnknownFieldSet unknownFields) {
+      return super.mergeUnknownFields(unknownFields);
+    }
+
+
+    // @@protoc_insertion_point(builder_scope:GetVideoFeatureRequest)
+  }
+
+  // @@protoc_insertion_point(class_scope:GetVideoFeatureRequest)
+  private static final com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest DEFAULT_INSTANCE;
+  static {
+    DEFAULT_INSTANCE = new com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest();
+  }
+
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest getDefaultInstance() {
+    return DEFAULT_INSTANCE;
+  }
+
+  private static final com.google.protobuf.Parser<GetVideoFeatureRequest>
+      PARSER = new com.google.protobuf.AbstractParser<GetVideoFeatureRequest>() {
+    @java.lang.Override
+    public GetVideoFeatureRequest parsePartialFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return new GetVideoFeatureRequest(input, extensionRegistry);
+    }
+  };
+
+  public static com.google.protobuf.Parser<GetVideoFeatureRequest> parser() {
+    return PARSER;
+  }
+
+  @java.lang.Override
+  public com.google.protobuf.Parser<GetVideoFeatureRequest> getParserForType() {
+    return PARSER;
+  }
+
+  @java.lang.Override
+  public com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest getDefaultInstanceForType() {
+    return DEFAULT_INSTANCE;
+  }
+
+}
+

+ 21 - 0
recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/server/gen/feature/GetVideoFeatureRequestOrBuilder.java

@@ -0,0 +1,21 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: com/tzld/piaoquan/recommend/feature/feature.proto
+
+package com.tzld.piaoquan.recommend.server.gen.feature;
+
+public interface GetVideoFeatureRequestOrBuilder extends
+    // @@protoc_insertion_point(interface_extends:GetVideoFeatureRequest)
+    com.google.protobuf.MessageOrBuilder {
+
+  /**
+   * <code>string uid = 1;</code>
+   * @return The uid.
+   */
+  java.lang.String getUid();
+  /**
+   * <code>string uid = 1;</code>
+   * @return The bytes for uid.
+   */
+  com.google.protobuf.ByteString
+      getUidBytes();
+}

+ 607 - 0
recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/server/gen/feature/GetVideoFeatureResponse.java

@@ -0,0 +1,607 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: com/tzld/piaoquan/recommend/feature/feature.proto
+
+package com.tzld.piaoquan.recommend.server.gen.feature;
+
+/**
+ * Protobuf type {@code GetVideoFeatureResponse}
+ */
+public final class GetVideoFeatureResponse extends
+    com.google.protobuf.GeneratedMessageV3 implements
+    // @@protoc_insertion_point(message_implements:GetVideoFeatureResponse)
+    GetVideoFeatureResponseOrBuilder {
+private static final long serialVersionUID = 0L;
+  // Use GetVideoFeatureResponse.newBuilder() to construct.
+  private GetVideoFeatureResponse(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
+    super(builder);
+  }
+  private GetVideoFeatureResponse() {
+  }
+
+  @java.lang.Override
+  @SuppressWarnings({"unused"})
+  protected java.lang.Object newInstance(
+      UnusedPrivateParameter unused) {
+    return new GetVideoFeatureResponse();
+  }
+
+  @java.lang.Override
+  public final com.google.protobuf.UnknownFieldSet
+  getUnknownFields() {
+    return this.unknownFields;
+  }
+  private GetVideoFeatureResponse(
+      com.google.protobuf.CodedInputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    this();
+    if (extensionRegistry == null) {
+      throw new java.lang.NullPointerException();
+    }
+    com.google.protobuf.UnknownFieldSet.Builder unknownFields =
+        com.google.protobuf.UnknownFieldSet.newBuilder();
+    try {
+      boolean done = false;
+      while (!done) {
+        int tag = input.readTag();
+        switch (tag) {
+          case 0:
+            done = true;
+            break;
+          case 10: {
+            com.tzld.piaoquan.recommend.server.gen.common.Result.Builder subBuilder = null;
+            if (result_ != null) {
+              subBuilder = result_.toBuilder();
+            }
+            result_ = input.readMessage(com.tzld.piaoquan.recommend.server.gen.common.Result.parser(), extensionRegistry);
+            if (subBuilder != null) {
+              subBuilder.mergeFrom(result_);
+              result_ = subBuilder.buildPartial();
+            }
+
+            break;
+          }
+          default: {
+            if (!parseUnknownField(
+                input, unknownFields, extensionRegistry, tag)) {
+              done = true;
+            }
+            break;
+          }
+        }
+      }
+    } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+      throw e.setUnfinishedMessage(this);
+    } catch (java.io.IOException e) {
+      throw new com.google.protobuf.InvalidProtocolBufferException(
+          e).setUnfinishedMessage(this);
+    } finally {
+      this.unknownFields = unknownFields.build();
+      makeExtensionsImmutable();
+    }
+  }
+  public static final com.google.protobuf.Descriptors.Descriptor
+      getDescriptor() {
+    return com.tzld.piaoquan.recommend.server.gen.feature.Feature.internal_static_GetVideoFeatureResponse_descriptor;
+  }
+
+  @java.lang.Override
+  protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+      internalGetFieldAccessorTable() {
+    return com.tzld.piaoquan.recommend.server.gen.feature.Feature.internal_static_GetVideoFeatureResponse_fieldAccessorTable
+        .ensureFieldAccessorsInitialized(
+            com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse.class, com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse.Builder.class);
+  }
+
+  public static final int RESULT_FIELD_NUMBER = 1;
+  private com.tzld.piaoquan.recommend.server.gen.common.Result result_;
+  /**
+   * <code>.Result result = 1;</code>
+   * @return Whether the result field is set.
+   */
+  @java.lang.Override
+  public boolean hasResult() {
+    return result_ != null;
+  }
+  /**
+   * <code>.Result result = 1;</code>
+   * @return The result.
+   */
+  @java.lang.Override
+  public com.tzld.piaoquan.recommend.server.gen.common.Result getResult() {
+    return result_ == null ? com.tzld.piaoquan.recommend.server.gen.common.Result.getDefaultInstance() : result_;
+  }
+  /**
+   * <code>.Result result = 1;</code>
+   */
+  @java.lang.Override
+  public com.tzld.piaoquan.recommend.server.gen.common.ResultOrBuilder getResultOrBuilder() {
+    return getResult();
+  }
+
+  private byte memoizedIsInitialized = -1;
+  @java.lang.Override
+  public final boolean isInitialized() {
+    byte isInitialized = memoizedIsInitialized;
+    if (isInitialized == 1) return true;
+    if (isInitialized == 0) return false;
+
+    memoizedIsInitialized = 1;
+    return true;
+  }
+
+  @java.lang.Override
+  public void writeTo(com.google.protobuf.CodedOutputStream output)
+                      throws java.io.IOException {
+    if (result_ != null) {
+      output.writeMessage(1, getResult());
+    }
+    unknownFields.writeTo(output);
+  }
+
+  @java.lang.Override
+  public int getSerializedSize() {
+    int size = memoizedSize;
+    if (size != -1) return size;
+
+    size = 0;
+    if (result_ != null) {
+      size += com.google.protobuf.CodedOutputStream
+        .computeMessageSize(1, getResult());
+    }
+    size += unknownFields.getSerializedSize();
+    memoizedSize = size;
+    return size;
+  }
+
+  @java.lang.Override
+  public boolean equals(final java.lang.Object obj) {
+    if (obj == this) {
+     return true;
+    }
+    if (!(obj instanceof com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse)) {
+      return super.equals(obj);
+    }
+    com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse other = (com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse) obj;
+
+    if (hasResult() != other.hasResult()) return false;
+    if (hasResult()) {
+      if (!getResult()
+          .equals(other.getResult())) return false;
+    }
+    if (!unknownFields.equals(other.unknownFields)) return false;
+    return true;
+  }
+
+  @java.lang.Override
+  public int hashCode() {
+    if (memoizedHashCode != 0) {
+      return memoizedHashCode;
+    }
+    int hash = 41;
+    hash = (19 * hash) + getDescriptor().hashCode();
+    if (hasResult()) {
+      hash = (37 * hash) + RESULT_FIELD_NUMBER;
+      hash = (53 * hash) + getResult().hashCode();
+    }
+    hash = (29 * hash) + unknownFields.hashCode();
+    memoizedHashCode = hash;
+    return hash;
+  }
+
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse parseFrom(
+      java.nio.ByteBuffer data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse parseFrom(
+      java.nio.ByteBuffer data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse parseFrom(
+      com.google.protobuf.ByteString data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse parseFrom(
+      com.google.protobuf.ByteString data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse parseFrom(byte[] data)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse parseFrom(
+      byte[] data,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws com.google.protobuf.InvalidProtocolBufferException {
+    return PARSER.parseFrom(data, extensionRegistry);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse parseFrom(java.io.InputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse parseFrom(
+      java.io.InputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input, extensionRegistry);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse parseDelimitedFrom(java.io.InputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseDelimitedWithIOException(PARSER, input);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse parseDelimitedFrom(
+      java.io.InputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseDelimitedWithIOException(PARSER, input, extensionRegistry);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse parseFrom(
+      com.google.protobuf.CodedInputStream input)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input);
+  }
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse parseFrom(
+      com.google.protobuf.CodedInputStream input,
+      com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+      throws java.io.IOException {
+    return com.google.protobuf.GeneratedMessageV3
+        .parseWithIOException(PARSER, input, extensionRegistry);
+  }
+
+  @java.lang.Override
+  public Builder newBuilderForType() { return newBuilder(); }
+  public static Builder newBuilder() {
+    return DEFAULT_INSTANCE.toBuilder();
+  }
+  public static Builder newBuilder(com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse prototype) {
+    return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
+  }
+  @java.lang.Override
+  public Builder toBuilder() {
+    return this == DEFAULT_INSTANCE
+        ? new Builder() : new Builder().mergeFrom(this);
+  }
+
+  @java.lang.Override
+  protected Builder newBuilderForType(
+      com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+    Builder builder = new Builder(parent);
+    return builder;
+  }
+  /**
+   * Protobuf type {@code GetVideoFeatureResponse}
+   */
+  public static final class Builder extends
+      com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
+      // @@protoc_insertion_point(builder_implements:GetVideoFeatureResponse)
+      com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponseOrBuilder {
+    public static final com.google.protobuf.Descriptors.Descriptor
+        getDescriptor() {
+      return com.tzld.piaoquan.recommend.server.gen.feature.Feature.internal_static_GetVideoFeatureResponse_descriptor;
+    }
+
+    @java.lang.Override
+    protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+        internalGetFieldAccessorTable() {
+      return com.tzld.piaoquan.recommend.server.gen.feature.Feature.internal_static_GetVideoFeatureResponse_fieldAccessorTable
+          .ensureFieldAccessorsInitialized(
+              com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse.class, com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse.Builder.class);
+    }
+
+    // Construct using com.tzld.piaoquan.recommend.feature.gen.feature.GetVideoFeatureResponse.newBuilder()
+    private Builder() {
+      maybeForceBuilderInitialization();
+    }
+
+    private Builder(
+        com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+      super(parent);
+      maybeForceBuilderInitialization();
+    }
+    private void maybeForceBuilderInitialization() {
+      if (com.google.protobuf.GeneratedMessageV3
+              .alwaysUseFieldBuilders) {
+      }
+    }
+    @java.lang.Override
+    public Builder clear() {
+      super.clear();
+      if (resultBuilder_ == null) {
+        result_ = null;
+      } else {
+        result_ = null;
+        resultBuilder_ = null;
+      }
+      return this;
+    }
+
+    @java.lang.Override
+    public com.google.protobuf.Descriptors.Descriptor
+        getDescriptorForType() {
+      return com.tzld.piaoquan.recommend.server.gen.feature.Feature.internal_static_GetVideoFeatureResponse_descriptor;
+    }
+
+    @java.lang.Override
+    public com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse getDefaultInstanceForType() {
+      return com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse.getDefaultInstance();
+    }
+
+    @java.lang.Override
+    public com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse build() {
+      com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse result = buildPartial();
+      if (!result.isInitialized()) {
+        throw newUninitializedMessageException(result);
+      }
+      return result;
+    }
+
+    @java.lang.Override
+    public com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse buildPartial() {
+      com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse result = new com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse(this);
+      if (resultBuilder_ == null) {
+        result.result_ = result_;
+      } else {
+        result.result_ = resultBuilder_.build();
+      }
+      onBuilt();
+      return result;
+    }
+
+    @java.lang.Override
+    public Builder clone() {
+      return super.clone();
+    }
+    @java.lang.Override
+    public Builder setField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        java.lang.Object value) {
+      return super.setField(field, value);
+    }
+    @java.lang.Override
+    public Builder clearField(
+        com.google.protobuf.Descriptors.FieldDescriptor field) {
+      return super.clearField(field);
+    }
+    @java.lang.Override
+    public Builder clearOneof(
+        com.google.protobuf.Descriptors.OneofDescriptor oneof) {
+      return super.clearOneof(oneof);
+    }
+    @java.lang.Override
+    public Builder setRepeatedField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        int index, java.lang.Object value) {
+      return super.setRepeatedField(field, index, value);
+    }
+    @java.lang.Override
+    public Builder addRepeatedField(
+        com.google.protobuf.Descriptors.FieldDescriptor field,
+        java.lang.Object value) {
+      return super.addRepeatedField(field, value);
+    }
+    @java.lang.Override
+    public Builder mergeFrom(com.google.protobuf.Message other) {
+      if (other instanceof com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse) {
+        return mergeFrom((com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse)other);
+      } else {
+        super.mergeFrom(other);
+        return this;
+      }
+    }
+
+    public Builder mergeFrom(com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse other) {
+      if (other == com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse.getDefaultInstance()) return this;
+      if (other.hasResult()) {
+        mergeResult(other.getResult());
+      }
+      this.mergeUnknownFields(other.unknownFields);
+      onChanged();
+      return this;
+    }
+
+    @java.lang.Override
+    public final boolean isInitialized() {
+      return true;
+    }
+
+    @java.lang.Override
+    public Builder mergeFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse parsedMessage = null;
+      try {
+        parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
+      } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+        parsedMessage = (com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse) e.getUnfinishedMessage();
+        throw e.unwrapIOException();
+      } finally {
+        if (parsedMessage != null) {
+          mergeFrom(parsedMessage);
+        }
+      }
+      return this;
+    }
+
+    private com.tzld.piaoquan.recommend.server.gen.common.Result result_;
+    private com.google.protobuf.SingleFieldBuilderV3<
+        com.tzld.piaoquan.recommend.server.gen.common.Result, com.tzld.piaoquan.recommend.server.gen.common.Result.Builder, com.tzld.piaoquan.recommend.server.gen.common.ResultOrBuilder> resultBuilder_;
+    /**
+     * <code>.Result result = 1;</code>
+     * @return Whether the result field is set.
+     */
+    public boolean hasResult() {
+      return resultBuilder_ != null || result_ != null;
+    }
+    /**
+     * <code>.Result result = 1;</code>
+     * @return The result.
+     */
+    public com.tzld.piaoquan.recommend.server.gen.common.Result getResult() {
+      if (resultBuilder_ == null) {
+        return result_ == null ? com.tzld.piaoquan.recommend.server.gen.common.Result.getDefaultInstance() : result_;
+      } else {
+        return resultBuilder_.getMessage();
+      }
+    }
+    /**
+     * <code>.Result result = 1;</code>
+     */
+    public Builder setResult(com.tzld.piaoquan.recommend.server.gen.common.Result value) {
+      if (resultBuilder_ == null) {
+        if (value == null) {
+          throw new NullPointerException();
+        }
+        result_ = value;
+        onChanged();
+      } else {
+        resultBuilder_.setMessage(value);
+      }
+
+      return this;
+    }
+    /**
+     * <code>.Result result = 1;</code>
+     */
+    public Builder setResult(
+        com.tzld.piaoquan.recommend.server.gen.common.Result.Builder builderForValue) {
+      if (resultBuilder_ == null) {
+        result_ = builderForValue.build();
+        onChanged();
+      } else {
+        resultBuilder_.setMessage(builderForValue.build());
+      }
+
+      return this;
+    }
+    /**
+     * <code>.Result result = 1;</code>
+     */
+    public Builder mergeResult(com.tzld.piaoquan.recommend.server.gen.common.Result value) {
+      if (resultBuilder_ == null) {
+        if (result_ != null) {
+          result_ =
+            com.tzld.piaoquan.recommend.server.gen.common.Result.newBuilder(result_).mergeFrom(value).buildPartial();
+        } else {
+          result_ = value;
+        }
+        onChanged();
+      } else {
+        resultBuilder_.mergeFrom(value);
+      }
+
+      return this;
+    }
+    /**
+     * <code>.Result result = 1;</code>
+     */
+    public Builder clearResult() {
+      if (resultBuilder_ == null) {
+        result_ = null;
+        onChanged();
+      } else {
+        result_ = null;
+        resultBuilder_ = null;
+      }
+
+      return this;
+    }
+    /**
+     * <code>.Result result = 1;</code>
+     */
+    public com.tzld.piaoquan.recommend.server.gen.common.Result.Builder getResultBuilder() {
+      
+      onChanged();
+      return getResultFieldBuilder().getBuilder();
+    }
+    /**
+     * <code>.Result result = 1;</code>
+     */
+    public com.tzld.piaoquan.recommend.server.gen.common.ResultOrBuilder getResultOrBuilder() {
+      if (resultBuilder_ != null) {
+        return resultBuilder_.getMessageOrBuilder();
+      } else {
+        return result_ == null ?
+            com.tzld.piaoquan.recommend.server.gen.common.Result.getDefaultInstance() : result_;
+      }
+    }
+    /**
+     * <code>.Result result = 1;</code>
+     */
+    private com.google.protobuf.SingleFieldBuilderV3<
+        com.tzld.piaoquan.recommend.server.gen.common.Result, com.tzld.piaoquan.recommend.server.gen.common.Result.Builder, com.tzld.piaoquan.recommend.server.gen.common.ResultOrBuilder>
+        getResultFieldBuilder() {
+      if (resultBuilder_ == null) {
+        resultBuilder_ = new com.google.protobuf.SingleFieldBuilderV3<
+            com.tzld.piaoquan.recommend.server.gen.common.Result, com.tzld.piaoquan.recommend.server.gen.common.Result.Builder, com.tzld.piaoquan.recommend.server.gen.common.ResultOrBuilder>(
+                getResult(),
+                getParentForChildren(),
+                isClean());
+        result_ = null;
+      }
+      return resultBuilder_;
+    }
+    @java.lang.Override
+    public final Builder setUnknownFields(
+        final com.google.protobuf.UnknownFieldSet unknownFields) {
+      return super.setUnknownFields(unknownFields);
+    }
+
+    @java.lang.Override
+    public final Builder mergeUnknownFields(
+        final com.google.protobuf.UnknownFieldSet unknownFields) {
+      return super.mergeUnknownFields(unknownFields);
+    }
+
+
+    // @@protoc_insertion_point(builder_scope:GetVideoFeatureResponse)
+  }
+
+  // @@protoc_insertion_point(class_scope:GetVideoFeatureResponse)
+  private static final com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse DEFAULT_INSTANCE;
+  static {
+    DEFAULT_INSTANCE = new com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse();
+  }
+
+  public static com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse getDefaultInstance() {
+    return DEFAULT_INSTANCE;
+  }
+
+  private static final com.google.protobuf.Parser<GetVideoFeatureResponse>
+      PARSER = new com.google.protobuf.AbstractParser<GetVideoFeatureResponse>() {
+    @java.lang.Override
+    public GetVideoFeatureResponse parsePartialFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return new GetVideoFeatureResponse(input, extensionRegistry);
+    }
+  };
+
+  public static com.google.protobuf.Parser<GetVideoFeatureResponse> parser() {
+    return PARSER;
+  }
+
+  @java.lang.Override
+  public com.google.protobuf.Parser<GetVideoFeatureResponse> getParserForType() {
+    return PARSER;
+  }
+
+  @java.lang.Override
+  public com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse getDefaultInstanceForType() {
+    return DEFAULT_INSTANCE;
+  }
+
+}
+

+ 24 - 0
recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/server/gen/feature/GetVideoFeatureResponseOrBuilder.java

@@ -0,0 +1,24 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: com/tzld/piaoquan/recommend/feature/feature.proto
+
+package com.tzld.piaoquan.recommend.server.gen.feature;
+
+public interface GetVideoFeatureResponseOrBuilder extends
+    // @@protoc_insertion_point(interface_extends:GetVideoFeatureResponse)
+    com.google.protobuf.MessageOrBuilder {
+
+  /**
+   * <code>.Result result = 1;</code>
+   * @return Whether the result field is set.
+   */
+  boolean hasResult();
+  /**
+   * <code>.Result result = 1;</code>
+   * @return The result.
+   */
+  com.tzld.piaoquan.recommend.server.gen.common.Result getResult();
+  /**
+   * <code>.Result result = 1;</code>
+   */
+  com.tzld.piaoquan.recommend.server.gen.common.ResultOrBuilder getResultOrBuilder();
+}

+ 308 - 0
recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/server/gen/feature/RecommendService.java

@@ -0,0 +1,308 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: com/tzld/piaoquan/recommend/feature/feature.proto
+
+package com.tzld.piaoquan.recommend.server.gen.feature;
+
+/**
+ * Protobuf service {@code RecommendService}
+ */
+public  abstract class RecommendService
+    implements com.google.protobuf.Service {
+  protected RecommendService() {}
+
+  public interface Interface {
+    /**
+     * <code>rpc GetUserFeature(.GetUserFeatureRequest) returns (.GetUserFeatureResponse);</code>
+     */
+    public abstract void getUserFeature(
+        com.google.protobuf.RpcController controller,
+        com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest request,
+        com.google.protobuf.RpcCallback<com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse> done);
+
+    /**
+     * <code>rpc GetVideoFeature(.GetVideoFeatureRequest) returns (.GetVideoFeatureResponse);</code>
+     */
+    public abstract void getVideoFeature(
+        com.google.protobuf.RpcController controller,
+        com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest request,
+        com.google.protobuf.RpcCallback<com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse> done);
+
+  }
+
+  public static com.google.protobuf.Service newReflectiveService(
+      final Interface impl) {
+    return new RecommendService() {
+      @java.lang.Override
+      public  void getUserFeature(
+          com.google.protobuf.RpcController controller,
+          com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest request,
+          com.google.protobuf.RpcCallback<com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse> done) {
+        impl.getUserFeature(controller, request, done);
+      }
+
+      @java.lang.Override
+      public  void getVideoFeature(
+          com.google.protobuf.RpcController controller,
+          com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest request,
+          com.google.protobuf.RpcCallback<com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse> done) {
+        impl.getVideoFeature(controller, request, done);
+      }
+
+    };
+  }
+
+  public static com.google.protobuf.BlockingService
+      newReflectiveBlockingService(final BlockingInterface impl) {
+    return new com.google.protobuf.BlockingService() {
+      public final com.google.protobuf.Descriptors.ServiceDescriptor
+          getDescriptorForType() {
+        return getDescriptor();
+      }
+
+      public final com.google.protobuf.Message callBlockingMethod(
+          com.google.protobuf.Descriptors.MethodDescriptor method,
+          com.google.protobuf.RpcController controller,
+          com.google.protobuf.Message request)
+          throws com.google.protobuf.ServiceException {
+        if (method.getService() != getDescriptor()) {
+          throw new java.lang.IllegalArgumentException(
+            "Service.callBlockingMethod() given method descriptor for " +
+            "wrong service type.");
+        }
+        switch(method.getIndex()) {
+          case 0:
+            return impl.getUserFeature(controller, (com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest)request);
+          case 1:
+            return impl.getVideoFeature(controller, (com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest)request);
+          default:
+            throw new java.lang.AssertionError("Can't get here.");
+        }
+      }
+
+      public final com.google.protobuf.Message
+          getRequestPrototype(
+          com.google.protobuf.Descriptors.MethodDescriptor method) {
+        if (method.getService() != getDescriptor()) {
+          throw new java.lang.IllegalArgumentException(
+            "Service.getRequestPrototype() given method " +
+            "descriptor for wrong service type.");
+        }
+        switch(method.getIndex()) {
+          case 0:
+            return com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest.getDefaultInstance();
+          case 1:
+            return com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest.getDefaultInstance();
+          default:
+            throw new java.lang.AssertionError("Can't get here.");
+        }
+      }
+
+      public final com.google.protobuf.Message
+          getResponsePrototype(
+          com.google.protobuf.Descriptors.MethodDescriptor method) {
+        if (method.getService() != getDescriptor()) {
+          throw new java.lang.IllegalArgumentException(
+            "Service.getResponsePrototype() given method " +
+            "descriptor for wrong service type.");
+        }
+        switch(method.getIndex()) {
+          case 0:
+            return com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse.getDefaultInstance();
+          case 1:
+            return com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse.getDefaultInstance();
+          default:
+            throw new java.lang.AssertionError("Can't get here.");
+        }
+      }
+
+    };
+  }
+
+  /**
+   * <code>rpc GetUserFeature(.GetUserFeatureRequest) returns (.GetUserFeatureResponse);</code>
+   */
+  public abstract void getUserFeature(
+      com.google.protobuf.RpcController controller,
+      com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest request,
+      com.google.protobuf.RpcCallback<com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse> done);
+
+  /**
+   * <code>rpc GetVideoFeature(.GetVideoFeatureRequest) returns (.GetVideoFeatureResponse);</code>
+   */
+  public abstract void getVideoFeature(
+      com.google.protobuf.RpcController controller,
+      com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest request,
+      com.google.protobuf.RpcCallback<com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse> done);
+
+  public static final
+      com.google.protobuf.Descriptors.ServiceDescriptor
+      getDescriptor() {
+    return com.tzld.piaoquan.recommend.server.gen.feature.Feature.getDescriptor().getServices().get(0);
+  }
+  public final com.google.protobuf.Descriptors.ServiceDescriptor
+      getDescriptorForType() {
+    return getDescriptor();
+  }
+
+  public final void callMethod(
+      com.google.protobuf.Descriptors.MethodDescriptor method,
+      com.google.protobuf.RpcController controller,
+      com.google.protobuf.Message request,
+      com.google.protobuf.RpcCallback<
+        com.google.protobuf.Message> done) {
+    if (method.getService() != getDescriptor()) {
+      throw new java.lang.IllegalArgumentException(
+        "Service.callMethod() given method descriptor for wrong " +
+        "service type.");
+    }
+    switch(method.getIndex()) {
+      case 0:
+        this.getUserFeature(controller, (com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest)request,
+          com.google.protobuf.RpcUtil.<com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse>specializeCallback(
+            done));
+        return;
+      case 1:
+        this.getVideoFeature(controller, (com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest)request,
+          com.google.protobuf.RpcUtil.<com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse>specializeCallback(
+            done));
+        return;
+      default:
+        throw new java.lang.AssertionError("Can't get here.");
+    }
+  }
+
+  public final com.google.protobuf.Message
+      getRequestPrototype(
+      com.google.protobuf.Descriptors.MethodDescriptor method) {
+    if (method.getService() != getDescriptor()) {
+      throw new java.lang.IllegalArgumentException(
+        "Service.getRequestPrototype() given method " +
+        "descriptor for wrong service type.");
+    }
+    switch(method.getIndex()) {
+      case 0:
+        return com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest.getDefaultInstance();
+      case 1:
+        return com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest.getDefaultInstance();
+      default:
+        throw new java.lang.AssertionError("Can't get here.");
+    }
+  }
+
+  public final com.google.protobuf.Message
+      getResponsePrototype(
+      com.google.protobuf.Descriptors.MethodDescriptor method) {
+    if (method.getService() != getDescriptor()) {
+      throw new java.lang.IllegalArgumentException(
+        "Service.getResponsePrototype() given method " +
+        "descriptor for wrong service type.");
+    }
+    switch(method.getIndex()) {
+      case 0:
+        return com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse.getDefaultInstance();
+      case 1:
+        return com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse.getDefaultInstance();
+      default:
+        throw new java.lang.AssertionError("Can't get here.");
+    }
+  }
+
+  public static Stub newStub(
+      com.google.protobuf.RpcChannel channel) {
+    return new Stub(channel);
+  }
+
+  public static final class Stub extends com.tzld.piaoquan.recommend.server.gen.feature.RecommendService implements Interface {
+    private Stub(com.google.protobuf.RpcChannel channel) {
+      this.channel = channel;
+    }
+
+    private final com.google.protobuf.RpcChannel channel;
+
+    public com.google.protobuf.RpcChannel getChannel() {
+      return channel;
+    }
+
+    public  void getUserFeature(
+        com.google.protobuf.RpcController controller,
+        com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest request,
+        com.google.protobuf.RpcCallback<com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse> done) {
+      channel.callMethod(
+        getDescriptor().getMethods().get(0),
+        controller,
+        request,
+        com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse.getDefaultInstance(),
+        com.google.protobuf.RpcUtil.generalizeCallback(
+          done,
+          com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse.class,
+          com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse.getDefaultInstance()));
+    }
+
+    public  void getVideoFeature(
+        com.google.protobuf.RpcController controller,
+        com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest request,
+        com.google.protobuf.RpcCallback<com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse> done) {
+      channel.callMethod(
+        getDescriptor().getMethods().get(1),
+        controller,
+        request,
+        com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse.getDefaultInstance(),
+        com.google.protobuf.RpcUtil.generalizeCallback(
+          done,
+          com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse.class,
+          com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse.getDefaultInstance()));
+    }
+  }
+
+  public static BlockingInterface newBlockingStub(
+      com.google.protobuf.BlockingRpcChannel channel) {
+    return new BlockingStub(channel);
+  }
+
+  public interface BlockingInterface {
+    public com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse getUserFeature(
+        com.google.protobuf.RpcController controller,
+        com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest request)
+        throws com.google.protobuf.ServiceException;
+
+    public com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse getVideoFeature(
+        com.google.protobuf.RpcController controller,
+        com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest request)
+        throws com.google.protobuf.ServiceException;
+  }
+
+  private static final class BlockingStub implements BlockingInterface {
+    private BlockingStub(com.google.protobuf.BlockingRpcChannel channel) {
+      this.channel = channel;
+    }
+
+    private final com.google.protobuf.BlockingRpcChannel channel;
+
+    public com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse getUserFeature(
+        com.google.protobuf.RpcController controller,
+        com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest request)
+        throws com.google.protobuf.ServiceException {
+      return (com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse) channel.callBlockingMethod(
+        getDescriptor().getMethods().get(0),
+        controller,
+        request,
+        com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse.getDefaultInstance());
+    }
+
+
+    public com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse getVideoFeature(
+        com.google.protobuf.RpcController controller,
+        com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest request)
+        throws com.google.protobuf.ServiceException {
+      return (com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse) channel.callBlockingMethod(
+        getDescriptor().getMethods().get(1),
+        controller,
+        request,
+        com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse.getDefaultInstance());
+    }
+
+  }
+
+  // @@protoc_insertion_point(class_scope:RecommendService)
+}
+

+ 355 - 0
recommend-feature-client/src/main/java/com/tzld/piaoquan/recommend/server/gen/feature/RecommendServiceGrpc.java

@@ -0,0 +1,355 @@
+package com.tzld.piaoquan.recommend.server.gen.feature;
+
+import static io.grpc.MethodDescriptor.generateFullMethodName;
+import static io.grpc.stub.ClientCalls.asyncUnaryCall;
+import static io.grpc.stub.ClientCalls.blockingServerStreamingCall;
+import static io.grpc.stub.ClientCalls.blockingUnaryCall;
+import static io.grpc.stub.ClientCalls.futureUnaryCall;
+import static io.grpc.stub.ServerCalls.asyncUnaryCall;
+import static io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall;
+
+/**
+ */
+@javax.annotation.Generated(
+    value = "by gRPC proto compiler (version 1.34.1)",
+    comments = "Source: com/tzld/piaoquan/recommend/feature/feature.proto")
+public final class RecommendServiceGrpc {
+
+  private RecommendServiceGrpc() {}
+
+  public static final String SERVICE_NAME = "RecommendService";
+
+  // Static method descriptors that strictly reflect the proto.
+  private static volatile io.grpc.MethodDescriptor<com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest,
+      com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse> getGetUserFeatureMethod;
+
+  @io.grpc.stub.annotations.RpcMethod(
+      fullMethodName = SERVICE_NAME + '/' + "GetUserFeature",
+      requestType = com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest.class,
+      responseType = com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse.class,
+      methodType = io.grpc.MethodDescriptor.MethodType.UNARY)
+  public static io.grpc.MethodDescriptor<com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest,
+      com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse> getGetUserFeatureMethod() {
+    io.grpc.MethodDescriptor<com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest, com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse> getGetUserFeatureMethod;
+    if ((getGetUserFeatureMethod = RecommendServiceGrpc.getGetUserFeatureMethod) == null) {
+      synchronized (RecommendServiceGrpc.class) {
+        if ((getGetUserFeatureMethod = RecommendServiceGrpc.getGetUserFeatureMethod) == null) {
+          RecommendServiceGrpc.getGetUserFeatureMethod = getGetUserFeatureMethod =
+              io.grpc.MethodDescriptor.<com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest, com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse>newBuilder()
+              .setType(io.grpc.MethodDescriptor.MethodType.UNARY)
+              .setFullMethodName(generateFullMethodName(SERVICE_NAME, "GetUserFeature"))
+              .setSampledToLocalTracing(true)
+              .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+                  com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest.getDefaultInstance()))
+              .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+                  com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse.getDefaultInstance()))
+              .setSchemaDescriptor(new RecommendServiceMethodDescriptorSupplier("GetUserFeature"))
+              .build();
+        }
+      }
+    }
+    return getGetUserFeatureMethod;
+  }
+
+  private static volatile io.grpc.MethodDescriptor<com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest,
+      com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse> getGetVideoFeatureMethod;
+
+  @io.grpc.stub.annotations.RpcMethod(
+      fullMethodName = SERVICE_NAME + '/' + "GetVideoFeature",
+      requestType = com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest.class,
+      responseType = com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse.class,
+      methodType = io.grpc.MethodDescriptor.MethodType.UNARY)
+  public static io.grpc.MethodDescriptor<com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest,
+      com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse> getGetVideoFeatureMethod() {
+    io.grpc.MethodDescriptor<com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest, com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse> getGetVideoFeatureMethod;
+    if ((getGetVideoFeatureMethod = RecommendServiceGrpc.getGetVideoFeatureMethod) == null) {
+      synchronized (RecommendServiceGrpc.class) {
+        if ((getGetVideoFeatureMethod = RecommendServiceGrpc.getGetVideoFeatureMethod) == null) {
+          RecommendServiceGrpc.getGetVideoFeatureMethod = getGetVideoFeatureMethod =
+              io.grpc.MethodDescriptor.<com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest, com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse>newBuilder()
+              .setType(io.grpc.MethodDescriptor.MethodType.UNARY)
+              .setFullMethodName(generateFullMethodName(SERVICE_NAME, "GetVideoFeature"))
+              .setSampledToLocalTracing(true)
+              .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+                  com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest.getDefaultInstance()))
+              .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
+                  com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse.getDefaultInstance()))
+              .setSchemaDescriptor(new RecommendServiceMethodDescriptorSupplier("GetVideoFeature"))
+              .build();
+        }
+      }
+    }
+    return getGetVideoFeatureMethod;
+  }
+
+  /**
+   * Creates a new async stub that supports all call types for the service
+   */
+  public static RecommendServiceStub newStub(io.grpc.Channel channel) {
+    io.grpc.stub.AbstractStub.StubFactory<RecommendServiceStub> factory =
+      new io.grpc.stub.AbstractStub.StubFactory<RecommendServiceStub>() {
+        @java.lang.Override
+        public RecommendServiceStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+          return new RecommendServiceStub(channel, callOptions);
+        }
+      };
+    return RecommendServiceStub.newStub(factory, channel);
+  }
+
+  /**
+   * Creates a new blocking-style stub that supports unary and streaming output calls on the service
+   */
+  public static RecommendServiceBlockingStub newBlockingStub(
+      io.grpc.Channel channel) {
+    io.grpc.stub.AbstractStub.StubFactory<RecommendServiceBlockingStub> factory =
+      new io.grpc.stub.AbstractStub.StubFactory<RecommendServiceBlockingStub>() {
+        @java.lang.Override
+        public RecommendServiceBlockingStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+          return new RecommendServiceBlockingStub(channel, callOptions);
+        }
+      };
+    return RecommendServiceBlockingStub.newStub(factory, channel);
+  }
+
+  /**
+   * Creates a new ListenableFuture-style stub that supports unary calls on the service
+   */
+  public static RecommendServiceFutureStub newFutureStub(
+      io.grpc.Channel channel) {
+    io.grpc.stub.AbstractStub.StubFactory<RecommendServiceFutureStub> factory =
+      new io.grpc.stub.AbstractStub.StubFactory<RecommendServiceFutureStub>() {
+        @java.lang.Override
+        public RecommendServiceFutureStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+          return new RecommendServiceFutureStub(channel, callOptions);
+        }
+      };
+    return RecommendServiceFutureStub.newStub(factory, channel);
+  }
+
+  /**
+   */
+  public static abstract class RecommendServiceImplBase implements io.grpc.BindableService {
+
+    /**
+     */
+    public void getUserFeature(com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest request,
+        io.grpc.stub.StreamObserver<com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse> responseObserver) {
+      asyncUnimplementedUnaryCall(getGetUserFeatureMethod(), responseObserver);
+    }
+
+    /**
+     */
+    public void getVideoFeature(com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest request,
+        io.grpc.stub.StreamObserver<com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse> responseObserver) {
+      asyncUnimplementedUnaryCall(getGetVideoFeatureMethod(), responseObserver);
+    }
+
+    @java.lang.Override public final io.grpc.ServerServiceDefinition bindService() {
+      return io.grpc.ServerServiceDefinition.builder(getServiceDescriptor())
+          .addMethod(
+            getGetUserFeatureMethod(),
+            asyncUnaryCall(
+              new MethodHandlers<
+                com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest,
+                com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse>(
+                  this, METHODID_GET_USER_FEATURE)))
+          .addMethod(
+            getGetVideoFeatureMethod(),
+            asyncUnaryCall(
+              new MethodHandlers<
+                com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest,
+                com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse>(
+                  this, METHODID_GET_VIDEO_FEATURE)))
+          .build();
+    }
+  }
+
+  /**
+   */
+  public static final class RecommendServiceStub extends io.grpc.stub.AbstractAsyncStub<RecommendServiceStub> {
+    private RecommendServiceStub(
+        io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+      super(channel, callOptions);
+    }
+
+    @java.lang.Override
+    protected RecommendServiceStub build(
+        io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+      return new RecommendServiceStub(channel, callOptions);
+    }
+
+    /**
+     */
+    public void getUserFeature(com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest request,
+        io.grpc.stub.StreamObserver<com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse> responseObserver) {
+      asyncUnaryCall(
+          getChannel().newCall(getGetUserFeatureMethod(), getCallOptions()), request, responseObserver);
+    }
+
+    /**
+     */
+    public void getVideoFeature(com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest request,
+        io.grpc.stub.StreamObserver<com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse> responseObserver) {
+      asyncUnaryCall(
+          getChannel().newCall(getGetVideoFeatureMethod(), getCallOptions()), request, responseObserver);
+    }
+  }
+
+  /**
+   */
+  public static final class RecommendServiceBlockingStub extends io.grpc.stub.AbstractBlockingStub<RecommendServiceBlockingStub> {
+    private RecommendServiceBlockingStub(
+        io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+      super(channel, callOptions);
+    }
+
+    @java.lang.Override
+    protected RecommendServiceBlockingStub build(
+        io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+      return new RecommendServiceBlockingStub(channel, callOptions);
+    }
+
+    /**
+     */
+    public com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse getUserFeature(com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest request) {
+      return blockingUnaryCall(
+          getChannel(), getGetUserFeatureMethod(), getCallOptions(), request);
+    }
+
+    /**
+     */
+    public com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse getVideoFeature(com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest request) {
+      return blockingUnaryCall(
+          getChannel(), getGetVideoFeatureMethod(), getCallOptions(), request);
+    }
+  }
+
+  /**
+   */
+  public static final class RecommendServiceFutureStub extends io.grpc.stub.AbstractFutureStub<RecommendServiceFutureStub> {
+    private RecommendServiceFutureStub(
+        io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+      super(channel, callOptions);
+    }
+
+    @java.lang.Override
+    protected RecommendServiceFutureStub build(
+        io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
+      return new RecommendServiceFutureStub(channel, callOptions);
+    }
+
+    /**
+     */
+    public com.google.common.util.concurrent.ListenableFuture<com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse> getUserFeature(
+        com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest request) {
+      return futureUnaryCall(
+          getChannel().newCall(getGetUserFeatureMethod(), getCallOptions()), request);
+    }
+
+    /**
+     */
+    public com.google.common.util.concurrent.ListenableFuture<com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse> getVideoFeature(
+        com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest request) {
+      return futureUnaryCall(
+          getChannel().newCall(getGetVideoFeatureMethod(), getCallOptions()), request);
+    }
+  }
+
+  private static final int METHODID_GET_USER_FEATURE = 0;
+  private static final int METHODID_GET_VIDEO_FEATURE = 1;
+
+  private static final class MethodHandlers<Req, Resp> implements
+      io.grpc.stub.ServerCalls.UnaryMethod<Req, Resp>,
+      io.grpc.stub.ServerCalls.ServerStreamingMethod<Req, Resp>,
+      io.grpc.stub.ServerCalls.ClientStreamingMethod<Req, Resp>,
+      io.grpc.stub.ServerCalls.BidiStreamingMethod<Req, Resp> {
+    private final RecommendServiceImplBase serviceImpl;
+    private final int methodId;
+
+    MethodHandlers(RecommendServiceImplBase serviceImpl, int methodId) {
+      this.serviceImpl = serviceImpl;
+      this.methodId = methodId;
+    }
+
+    @java.lang.Override
+    @java.lang.SuppressWarnings("unchecked")
+    public void invoke(Req request, io.grpc.stub.StreamObserver<Resp> responseObserver) {
+      switch (methodId) {
+        case METHODID_GET_USER_FEATURE:
+          serviceImpl.getUserFeature((com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureRequest) request,
+              (io.grpc.stub.StreamObserver<com.tzld.piaoquan.recommend.server.gen.feature.GetUserFeatureResponse>) responseObserver);
+          break;
+        case METHODID_GET_VIDEO_FEATURE:
+          serviceImpl.getVideoFeature((com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureRequest) request,
+              (io.grpc.stub.StreamObserver<com.tzld.piaoquan.recommend.server.gen.feature.GetVideoFeatureResponse>) responseObserver);
+          break;
+        default:
+          throw new AssertionError();
+      }
+    }
+
+    @java.lang.Override
+    @java.lang.SuppressWarnings("unchecked")
+    public io.grpc.stub.StreamObserver<Req> invoke(
+        io.grpc.stub.StreamObserver<Resp> responseObserver) {
+      switch (methodId) {
+        default:
+          throw new AssertionError();
+      }
+    }
+  }
+
+  private static abstract class RecommendServiceBaseDescriptorSupplier
+      implements io.grpc.protobuf.ProtoFileDescriptorSupplier, io.grpc.protobuf.ProtoServiceDescriptorSupplier {
+    RecommendServiceBaseDescriptorSupplier() {}
+
+    @java.lang.Override
+    public com.google.protobuf.Descriptors.FileDescriptor getFileDescriptor() {
+      return com.tzld.piaoquan.recommend.server.gen.feature.Feature.getDescriptor();
+    }
+
+    @java.lang.Override
+    public com.google.protobuf.Descriptors.ServiceDescriptor getServiceDescriptor() {
+      return getFileDescriptor().findServiceByName("RecommendService");
+    }
+  }
+
+  private static final class RecommendServiceFileDescriptorSupplier
+      extends RecommendServiceBaseDescriptorSupplier {
+    RecommendServiceFileDescriptorSupplier() {}
+  }
+
+  private static final class RecommendServiceMethodDescriptorSupplier
+      extends RecommendServiceBaseDescriptorSupplier
+      implements io.grpc.protobuf.ProtoMethodDescriptorSupplier {
+    private final String methodName;
+
+    RecommendServiceMethodDescriptorSupplier(String methodName) {
+      this.methodName = methodName;
+    }
+
+    @java.lang.Override
+    public com.google.protobuf.Descriptors.MethodDescriptor getMethodDescriptor() {
+      return getServiceDescriptor().findMethodByName(methodName);
+    }
+  }
+
+  private static volatile io.grpc.ServiceDescriptor serviceDescriptor;
+
+  public static io.grpc.ServiceDescriptor getServiceDescriptor() {
+    io.grpc.ServiceDescriptor result = serviceDescriptor;
+    if (result == null) {
+      synchronized (RecommendServiceGrpc.class) {
+        result = serviceDescriptor;
+        if (result == null) {
+          serviceDescriptor = result = io.grpc.ServiceDescriptor.newBuilder(SERVICE_NAME)
+              .setSchemaDescriptor(new RecommendServiceFileDescriptorSupplier())
+              .addMethod(getGetUserFeatureMethod())
+              .addMethod(getGetVideoFeatureMethod())
+              .build();
+        }
+      }
+    }
+    return result;
+  }
+}

+ 9 - 0
recommend-feature-client/src/main/proto/com/tzld/piaoquan/recommend/feature/common.proto

@@ -0,0 +1,9 @@
+syntax = "proto3";
+
+option java_multiple_files = true;
+option java_package = "com.tzld.piaoquan.recommend.feature.gen.common";
+
+message Result {
+  int32 code = 1; // 1 成功
+  string message = 2;
+}

+ 31 - 0
recommend-feature-client/src/main/proto/com/tzld/piaoquan/recommend/feature/feature.proto

@@ -0,0 +1,31 @@
+syntax = "proto3";
+
+import "google/protobuf/any.proto";
+import "com/tzld/piaoquan/recommend/feature/common.proto";
+
+option java_multiple_files = true;
+option java_package = "com.tzld.piaoquan.recommend.feature.gen.feature";
+option java_generic_services = true;
+
+
+message GetUserFeatureRequest {
+  string uid = 1;
+}
+
+message GetUserFeatureResponse {
+  Result result = 1;
+}
+
+message GetVideoFeatureRequest {
+  string uid = 1;
+}
+
+message GetVideoFeatureResponse {
+  Result result = 1;
+}
+
+service FeatureService {
+  rpc GetUserFeature (GetUserFeatureRequest) returns (GetUserFeatureResponse);
+  rpc GetVideoFeature (GetVideoFeatureRequest) returns (GetVideoFeatureResponse);
+
+}

+ 150 - 0
recommend-feature-service/pom.xml

@@ -0,0 +1,150 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.tzld.piaoquan</groupId>
+        <artifactId>recommend-feature</artifactId>
+        <version>1.0.0</version>
+    </parent>
+    <artifactId>recommend-feature-service</artifactId>
+    <name>recommend-feature-service</name>
+    <description>recommend-feature-service</description>
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-commons</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-pool2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.junit.vintage</groupId>
+                    <artifactId>junit-vintage-engine</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <!--        <dependency>-->
+        <!--            <groupId>mysql</groupId>-->
+        <!--            <artifactId>mysql-connector-java</artifactId>-->
+        <!--        </dependency>-->
+        <!--        <dependency>-->
+        <!--            <groupId>org.mybatis.spring.boot</groupId>-->
+        <!--            <artifactId>mybatis-spring-boot-starter</artifactId>-->
+        <!--        </dependency>-->
+        <!--        <dependency>-->
+        <!--            <groupId>org.mybatis.generator</groupId>-->
+        <!--            <artifactId>mybatis-generator-core</artifactId>-->
+        <!--        </dependency>-->
+        <!--        <dependency>-->
+        <!--            <groupId>com.github.pagehelper</groupId>-->
+        <!--            <artifactId>pagehelper-spring-boot-starter</artifactId>-->
+        <!--        </dependency>-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.ctrip.framework.apollo</groupId>
+            <artifactId>apollo-client</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba.csp</groupId>
+            <artifactId>sentinel-datasource-apollo</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.aliyun.openservices</groupId>
+            <artifactId>aliyun-log-logback-appender</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>net.devh</groupId>
+            <artifactId>grpc-server-spring-boot-starter</artifactId>
+            <version>2.9.0.RELEASE</version>
+        </dependency>
+        <dependency>
+            <groupId>com.tzld.piaoquan</groupId>
+            <artifactId>recommend-feature-client</artifactId>
+            <version>1.0.0</version>
+        </dependency>
+        <dependency>
+            <groupId>com.google.protobuf</groupId>
+            <artifactId>protobuf-java</artifactId>
+            <version>3.12.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <version>1.16.8</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.sun.jersey</groupId>
+            <artifactId>jersey-client</artifactId>
+            <version>1.9.1</version>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.jersey</groupId>
+            <artifactId>jersey-core</artifactId>
+            <version>1.9.1</version>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.jersey.contribs</groupId>
+            <artifactId>jersey-apache-client4</artifactId>
+            <version>1.9.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-collections4</artifactId>
+            <version>4.1</version>
+        </dependency>
+    </dependencies>
+    <build>
+        <finalName>recommend-feature-service</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>com.tzld.piaoquan.recommend.feature.Application</mainClass>
+                    <layout>ZIP</layout>
+                </configuration>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+
+    </build>
+
+</project>

+ 24 - 0
recommend-feature-service/src/main/java/com/tzld/piaoquan/recommend/feature/Application.java

@@ -0,0 +1,24 @@
+package com.tzld.piaoquan.recommend.feature;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.EnableAspectJAutoProxy;
+
+/**
+ * https://github.com/grpc-swagger/grpc-swagger/blob/master/README_CN.md
+ */
+@SpringBootApplication
+@ComponentScan({
+        "com.tzld.piaoquan.recommend.feature.service",
+        "com.tzld.piaoquan.recommend.feature.grpcservice",
+        "com.tzld.piaoquan.recommend.feature.config"
+})
+@EnableEurekaClient
+@EnableAspectJAutoProxy
+public class Application {
+    public static void main(String[] args) {
+        SpringApplication.run(Application.class, args);
+    }
+}

+ 40 - 0
recommend-feature-service/src/main/java/com/tzld/piaoquan/recommend/feature/common/CommonThreadPoolExecutor.java

@@ -0,0 +1,40 @@
+package com.tzld.piaoquan.recommend.feature.common;
+
+import org.slf4j.MDC;
+
+import java.util.Map;
+import java.util.concurrent.*;
+
+/**
+ * @author dyp
+ */
+public class CommonThreadPoolExecutor extends ThreadPoolExecutor {
+    public CommonThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
+        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
+    }
+
+    public CommonThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
+        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
+    }
+
+    public CommonThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
+        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
+    }
+
+    public CommonThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
+        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
+    }
+
+    @Override
+    public void execute(Runnable command) {
+        Map<String, String> mdcMap = MDC.getCopyOfContextMap();
+        super.execute(() -> {
+            if (mdcMap != null) {
+                MDC.setContextMap(mdcMap);
+            }
+            command.run();
+            MDC.clear();
+        });
+    }
+
+}

+ 358 - 0
recommend-feature-service/src/main/java/com/tzld/piaoquan/recommend/feature/common/RecommendLoghubAppender.java

@@ -0,0 +1,358 @@
+package com.tzld.piaoquan.recommend.feature.common;
+
+import ch.qos.logback.classic.spi.IThrowableProxy;
+import ch.qos.logback.classic.spi.LoggingEvent;
+import ch.qos.logback.classic.spi.StackTraceElementProxy;
+import ch.qos.logback.classic.spi.ThrowableProxyUtil;
+import ch.qos.logback.core.CoreConstants;
+import ch.qos.logback.core.UnsynchronizedAppenderBase;
+import ch.qos.logback.core.encoder.Encoder;
+import com.aliyun.openservices.aliyun.log.producer.LogProducer;
+import com.aliyun.openservices.aliyun.log.producer.Producer;
+import com.aliyun.openservices.aliyun.log.producer.ProducerConfig;
+import com.aliyun.openservices.aliyun.log.producer.ProjectConfig;
+import com.aliyun.openservices.aliyun.log.producer.errors.ProducerException;
+import com.aliyun.openservices.log.common.LogItem;
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.joda.time.format.DateTimeFormat;
+import org.joda.time.format.DateTimeFormatter;
+
+import java.time.Instant;
+import java.time.ZoneId;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * @author dyp
+ */
+public class RecommendLoghubAppender<E> extends UnsynchronizedAppenderBase<E> {
+    private String project;
+    private String endpoint;
+    private String accessKeyId;
+    private String accessKeySecret;
+    private String userAgent = "logback";
+    protected Encoder<E> encoder;
+    protected ProducerConfig producerConfig = new ProducerConfig();
+    protected ProjectConfig projectConfig;
+    protected Producer producer;
+    protected String logStore;
+    protected String topic = "";
+    protected String source = "";
+    protected String timeZone = "UTC";
+    protected String timeFormat = "yyyy-MM-dd'T'HH:mmZ";
+    protected DateTimeFormatter formatter;
+    protected java.time.format.DateTimeFormatter formatter1;
+    private String mdcFields;
+
+    public RecommendLoghubAppender() {
+    }
+
+    public void start() {
+        try {
+            this.doStart();
+        } catch (Exception var2) {
+            this.addError("Failed to start LoghubAppender.", var2);
+        }
+
+    }
+
+    private void doStart() {
+        try {
+            this.formatter = DateTimeFormat.forPattern(this.timeFormat).withZone(DateTimeZone.forID(this.timeZone));
+        } catch (Exception var2) {
+            this.formatter1 = java.time.format.DateTimeFormatter.ofPattern(this.timeFormat).withZone(ZoneId.of(this.timeZone));
+        }
+
+        this.producer = this.createProducer();
+        super.start();
+    }
+
+    public Producer createProducer() {
+        this.projectConfig = this.buildProjectConfig();
+        Producer producer = new LogProducer(this.producerConfig);
+        producer.putProjectConfig(this.projectConfig);
+        return producer;
+    }
+
+    private ProjectConfig buildProjectConfig() {
+        return new ProjectConfig(this.project, this.endpoint, this.accessKeyId, this.accessKeySecret, (String) null, this.userAgent);
+    }
+
+    public void stop() {
+        try {
+            this.doStop();
+        } catch (Exception var2) {
+            this.addError("Failed to stop LoghubAppender.", var2);
+        }
+
+    }
+
+    private void doStop() throws InterruptedException, ProducerException {
+        if (this.isStarted()) {
+            super.stop();
+            this.producer.close();
+        }
+    }
+
+    public void append(E eventObject) {
+        try {
+            this.appendEvent(eventObject);
+        } catch (Exception var3) {
+            this.addError("Failed to append event.", var3);
+        }
+
+    }
+
+    private void appendEvent(E eventObject) {
+        if (eventObject instanceof LoggingEvent) {
+            LoggingEvent event = (LoggingEvent) eventObject;
+            List<LogItem> logItems = new ArrayList();
+            LogItem item = new LogItem();
+            logItems.add(item);
+            item.SetTime((int) (event.getTimeStamp() / 1000L));
+            if (this.formatter != null) {
+                DateTime dateTime = new DateTime(event.getTimeStamp());
+                item.PushBack("time", dateTime.toString(this.formatter));
+            } else {
+                Instant instant = Instant.ofEpochMilli(event.getTimeStamp());
+                item.PushBack("time", this.formatter1.format(instant));
+            }
+
+            item.PushBack("level", event.getLevel().toString());
+            item.PushBack("thread", event.getThreadName());
+            StackTraceElement[] caller = event.getCallerData();
+            if (caller != null && caller.length > 0) {
+                item.PushBack("location", caller[0].toString());
+            }
+
+            String message = event.getFormattedMessage();
+            item.PushBack("message", message);
+            IThrowableProxy iThrowableProxy = event.getThrowableProxy();
+            if (iThrowableProxy != null) {
+                String throwable = this.getExceptionInfo(iThrowableProxy);
+                throwable = throwable + this.fullDump(event.getThrowableProxy().getStackTraceElementProxyArray());
+                item.PushBack("throwable", throwable);
+            }
+
+            if (this.encoder != null) {
+                item.PushBack("log", new String(this.encoder.encode(eventObject)));
+            }
+
+            Optional.ofNullable(this.mdcFields).ifPresent((f) -> {
+                event.getMDCPropertyMap().entrySet().stream().filter((v) -> {
+                    return Arrays.stream(f.split(",")).anyMatch((i) -> {
+                        return i.equals(v.getKey());
+                    });
+                }).forEach((map) -> {
+                    item.PushBack((String) map.getKey(), (String) map.getValue());
+                });
+            });
+
+            try {
+                this.producer.send(this.projectConfig.getProject(), this.logStore, this.topic, this.source, logItems,
+                        new RecommendLoghubAppenderCallback(this, this.projectConfig.getProject(), this.logStore,
+                                this.topic, this.source, logItems));
+            } catch (Exception var9) {
+                this.addError("Failed to send log, project=" + this.project + ", logStore=" + this.logStore + ", topic=" + this.topic + ", source=" + this.source + ", logItem=" + logItems, var9);
+            }
+
+        }
+    }
+
+    public String getTimeFormat() {
+        return this.timeFormat;
+    }
+
+    public void setTimeFormat(String timeFormat) {
+        this.timeFormat = timeFormat;
+    }
+
+    private String getExceptionInfo(IThrowableProxy iThrowableProxy) {
+        String s = iThrowableProxy.getClassName();
+        String message = iThrowableProxy.getMessage();
+        return message != null ? s + ": " + message : s;
+    }
+
+    private String fullDump(StackTraceElementProxy[] stackTraceElementProxyArray) {
+        StringBuilder builder = new StringBuilder();
+        StackTraceElementProxy[] var3 = stackTraceElementProxyArray;
+        int var4 = stackTraceElementProxyArray.length;
+
+        for (int var5 = 0; var5 < var4; ++var5) {
+            StackTraceElementProxy step = var3[var5];
+            builder.append(CoreConstants.LINE_SEPARATOR);
+            String string = step.toString();
+            builder.append('\t').append(string);
+            ThrowableProxyUtil.subjoinPackagingData(builder, step);
+        }
+
+        return builder.toString();
+    }
+
+    public String getLogStore() {
+        return this.logStore;
+    }
+
+    public void setLogStore(String logStore) {
+        this.logStore = logStore;
+    }
+
+    public String getTopic() {
+        return this.topic;
+    }
+
+    public void setTopic(String topic) {
+        this.topic = topic;
+    }
+
+    public String getSource() {
+        return this.source;
+    }
+
+    public void setSource(String source) {
+        this.source = source;
+    }
+
+    public String getTimeZone() {
+        return this.timeZone;
+    }
+
+    public void setTimeZone(String timeZone) {
+        this.timeZone = timeZone;
+    }
+
+    public String getProject() {
+        return this.project;
+    }
+
+    public void setProject(String project) {
+        this.project = project;
+    }
+
+    public String getEndpoint() {
+        return this.endpoint;
+    }
+
+    public void setEndpoint(String endpoint) {
+        this.endpoint = endpoint;
+    }
+
+    public String getAccessKeyId() {
+        return this.accessKeyId;
+    }
+
+    public void setAccessKeyId(String accessKeyId) {
+        this.accessKeyId = accessKeyId;
+    }
+
+    public String getAccessKeySecret() {
+        return this.accessKeySecret;
+    }
+
+    public void setAccessKeySecret(String accessKeySecret) {
+        this.accessKeySecret = accessKeySecret;
+    }
+
+    public String getUserAgent() {
+        return this.userAgent;
+    }
+
+    public void setUserAgent(String userAgent) {
+        this.userAgent = userAgent;
+    }
+
+    public int getTotalSizeInBytes() {
+        return this.producerConfig.getTotalSizeInBytes();
+    }
+
+    public void setTotalSizeInBytes(int totalSizeInBytes) {
+        this.producerConfig.setTotalSizeInBytes(totalSizeInBytes);
+    }
+
+    public long getMaxBlockMs() {
+        return this.producerConfig.getMaxBlockMs();
+    }
+
+    public void setMaxBlockMs(long maxBlockMs) {
+        this.producerConfig.setMaxBlockMs(maxBlockMs);
+    }
+
+    public int getIoThreadCount() {
+        return this.producerConfig.getIoThreadCount();
+    }
+
+    public void setIoThreadCount(int ioThreadCount) {
+        this.producerConfig.setIoThreadCount(ioThreadCount);
+    }
+
+    public int getBatchSizeThresholdInBytes() {
+        return this.producerConfig.getBatchSizeThresholdInBytes();
+    }
+
+    public void setBatchSizeThresholdInBytes(int batchSizeThresholdInBytes) {
+        this.producerConfig.setBatchSizeThresholdInBytes(batchSizeThresholdInBytes);
+    }
+
+    public int getBatchCountThreshold() {
+        return this.producerConfig.getBatchCountThreshold();
+    }
+
+    public void setBatchCountThreshold(int batchCountThreshold) {
+        this.producerConfig.setBatchCountThreshold(batchCountThreshold);
+    }
+
+    public int getLingerMs() {
+        return this.producerConfig.getLingerMs();
+    }
+
+    public void setLingerMs(int lingerMs) {
+        this.producerConfig.setLingerMs(lingerMs);
+    }
+
+    public int getRetries() {
+        return this.producerConfig.getRetries();
+    }
+
+    public void setRetries(int retries) {
+        this.producerConfig.setRetries(retries);
+    }
+
+    public int getMaxReservedAttempts() {
+        return this.producerConfig.getMaxReservedAttempts();
+    }
+
+    public void setMaxReservedAttempts(int maxReservedAttempts) {
+        this.producerConfig.setMaxReservedAttempts(maxReservedAttempts);
+    }
+
+    public long getBaseRetryBackoffMs() {
+        return this.producerConfig.getBaseRetryBackoffMs();
+    }
+
+    public void setBaseRetryBackoffMs(long baseRetryBackoffMs) {
+        this.producerConfig.setBaseRetryBackoffMs(baseRetryBackoffMs);
+    }
+
+    public long getMaxRetryBackoffMs() {
+        return this.producerConfig.getMaxRetryBackoffMs();
+    }
+
+    public void setMaxRetryBackoffMs(long maxRetryBackoffMs) {
+        this.producerConfig.setMaxRetryBackoffMs(maxRetryBackoffMs);
+    }
+
+    public Encoder<E> getEncoder() {
+        return this.encoder;
+    }
+
+    public void setEncoder(Encoder<E> encoder) {
+        this.encoder = encoder;
+    }
+
+    public void setMdcFields(String mdcFields) {
+        this.mdcFields = mdcFields;
+    }
+}

+ 37 - 0
recommend-feature-service/src/main/java/com/tzld/piaoquan/recommend/feature/common/RecommendLoghubAppenderCallback.java

@@ -0,0 +1,37 @@
+package com.tzld.piaoquan.recommend.feature.common;
+
+
+import com.aliyun.openservices.aliyun.log.producer.Callback;
+import com.aliyun.openservices.aliyun.log.producer.Result;
+import com.aliyun.openservices.log.common.LogItem;
+
+import java.util.List;
+
+/**
+ * @author dyp
+ */
+public class RecommendLoghubAppenderCallback<E> implements Callback {
+    protected RecommendLoghubAppender<E> loghubAppender;
+    protected String project;
+    protected String logstore;
+    protected String topic;
+    protected String source;
+    protected List<LogItem> logItems;
+
+    public RecommendLoghubAppenderCallback(RecommendLoghubAppender<E> loghubAppender, String project, String logstore,
+                                           String topic, String source, List<LogItem> logItems) {
+        this.loghubAppender = loghubAppender;
+        this.project = project;
+        this.logstore = logstore;
+        this.topic = topic;
+        this.source = source;
+        this.logItems = logItems;
+    }
+
+    public void onCompletion(Result result) {
+        if (!result.isSuccessful()) {
+            this.loghubAppender.addError("Failed to send log, project=" + this.project + ", logStore=" + this.logstore + ", topic=" + this.topic + ", source=" + this.source + ", logItem=" + this.logItems + ", errorCode=" + result.getErrorCode() + ", errorMessage=" + result.getErrorMessage());
+        }
+
+    }
+}

+ 37 - 0
recommend-feature-service/src/main/java/com/tzld/piaoquan/recommend/feature/common/ThreadPoolFactory.java

@@ -0,0 +1,37 @@
+package com.tzld.piaoquan.recommend.feature.common;
+
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author dyp
+ */
+public final class ThreadPoolFactory {
+    private final static ExecutorService DEFAULT = new CommonThreadPoolExecutor(
+            32,
+            32,
+            0L, TimeUnit.SECONDS,
+            new LinkedBlockingQueue<>(1000),
+            new ThreadFactoryBuilder().setNameFormat("DEFAULT-%d").build(),
+            new ThreadPoolExecutor.AbortPolicy());
+    public final static ExecutorService RECALL = new CommonThreadPoolExecutor(
+            128,
+            128,
+            0L, TimeUnit.SECONDS,
+            new LinkedBlockingQueue<>(1000),
+            new ThreadFactoryBuilder().setNameFormat("RecallService-%d").build(),
+            new ThreadPoolExecutor.AbortPolicy());
+
+    public static ExecutorService defaultPool() {
+        return DEFAULT;
+    }
+
+    public static ExecutorService recallPool() {
+        return RECALL;
+    }
+
+}

+ 79 - 0
recommend-feature-service/src/main/java/com/tzld/piaoquan/recommend/feature/common/enums/AppTypeEnum.java

@@ -0,0 +1,79 @@
+package com.tzld.piaoquan.recommend.feature.common.enums;
+
+/**
+ * AppType
+ * 目前只维护 0 1 4 13 4个应用
+ *
+ * @author supeng
+ * @date 2020/08/19
+ */
+public enum AppTypeEnum {
+    /**
+     * vlog小程序
+     */
+    VLOG(0, "VLOG"),
+    /**
+     * 票圈视频APP
+     */
+    QING_QU_APP(1, "轻趣视频APP"),
+    SMILE(2, "搞笑视频"),
+    LOVE_MOVIE(3, "爱电影"),
+    /**
+     * 票圈视频小程序
+     */
+    LOVE_LIVE(4, "爱生活"),
+    /**
+     * 长视频小程序
+     */
+    LONG_VIDEO(5, "长视频"),
+    SHORT_VIDEO(6, "短视频"),
+    SURPRISE(7, "惊奇视频"),
+    PC(8, "PC端"),
+    CIRCLE_APP(9, "票圈长视频APP"),
+    LONG_VIDEO_LITE(10, "票圈长视频lite"),
+    /**
+     * 票圈相册小程序
+     */
+    ALBUM(11, "票圈相册"),
+    H5(12, "H5端"),
+    /**
+     * 极速版APP
+     */
+    APP_SPEED(13, "APP极速"),
+    SMALL_SPEED(14, "小程序极速"),
+    WAN_NENG_VIDEO(17, "万能影视屋(票圈|信仰之路)"),
+    LAO_HAO_KAN_VIDEO(18, "老好看视频"),
+    ZUI_JING_QI(19, "票圈最惊奇"),
+    PIAO_QUAN_VIDEO_PLUS(21, "票圈视频+"),
+    JOURNEY(22, "票圈足迹"),
+    ADMIN_CRAWLER(888888, "后台-爬虫"),
+    OTHERS(999999, "其他的调用方,比如后台管理系统");
+
+    private Integer code;
+    private String desc;
+
+    public Integer getCode() {
+        return code;
+    }
+
+    public String getDesc() {
+        return desc;
+    }
+
+    private AppTypeEnum(Integer code, String desc) {
+        this.code = code;
+        this.desc = desc;
+    }
+
+    public static AppTypeEnum valueOf(Integer code) {
+        if (code == null) {
+            return null;
+        }
+        for (AppTypeEnum appTypeEnum : AppTypeEnum.values()) {
+            if (appTypeEnum.getCode().intValue() == code.intValue()) {
+                return appTypeEnum;
+            }
+        }
+        return null;
+    }
+}

+ 67 - 0
recommend-feature-service/src/main/java/com/tzld/piaoquan/recommend/feature/config/RedisTemplateConfig.java

@@ -0,0 +1,67 @@
+package com.tzld.piaoquan.recommend.feature.config;
+
+import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
+import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
+import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
+import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+
+/**
+ * @author supeng
+ * @date 2020/11/10
+ */
+@Configuration
+public class RedisTemplateConfig {
+
+    @Bean
+    @ConfigurationProperties(prefix = "spring.redis.lettuce.pool")
+    public GenericObjectPoolConfig<LettucePoolingClientConfiguration> redisPool() {
+        return new GenericObjectPoolConfig<>();
+    }
+
+    @Bean
+    @ConfigurationProperties(prefix = "spring.redis")
+    public RedisStandaloneConfiguration redisConfig() {
+        return new RedisStandaloneConfiguration();
+    }
+
+    @Bean("factory")
+    @Primary
+    public LettuceConnectionFactory factory(GenericObjectPoolConfig<LettucePoolingClientConfiguration> config,
+                                            RedisStandaloneConfiguration redisConfig) {
+        LettuceClientConfiguration lettuceClientConfiguration =
+                LettucePoolingClientConfiguration.builder().poolConfig(config).build();
+        return new LettuceConnectionFactory(redisConfig, lettuceClientConfiguration);
+    }
+
+    @Bean(name = "redisTemplate")
+    public RedisTemplate<String, String> getRedisTemplate(@Qualifier("factory") RedisConnectionFactory factory) {
+        return buildRedisTemplateByString(factory);
+    }
+
+    /**
+     * 构建redisTemplate 使用string序列化
+     *
+     * @param factory
+     * @return
+     */
+    public RedisTemplate<String, String> buildRedisTemplateByString(RedisConnectionFactory factory) {
+        RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
+        redisTemplate.setConnectionFactory(factory);
+        // key的序列化类型 保证可读性
+        redisTemplate.setKeySerializer(new StringRedisSerializer());
+        redisTemplate.setValueSerializer(new StringRedisSerializer());
+        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
+        redisTemplate.setHashValueSerializer(new StringRedisSerializer());
+        return redisTemplate;
+    }
+
+}

+ 27 - 0
recommend-feature-service/src/main/java/com/tzld/piaoquan/recommend/feature/grpcservice/FeatureGrpcService.java

@@ -0,0 +1,27 @@
+package com.tzld.piaoquan.recommend.feature.grpcservice;
+
+import com.tzld.piaoquan.recommend.server.gen.feature.*;
+import io.grpc.stub.StreamObserver;
+import net.devh.boot.grpc.server.service.GrpcService;
+
+/**
+ * @author dyp
+ */
+@GrpcService
+public class FeatureGrpcService extends FeatureServiceGrpc.FeatureServiceImplBase {
+
+    @Override
+    public void getUserFeature(GetUserFeatureRequest request, StreamObserver<GetUserFeatureResponse> responseObserver) {
+        GetUserFeatureResponse response = null;
+        responseObserver.onNext(response);
+        responseObserver.onCompleted();
+    }
+
+    @Override
+    public void getVideoFeature(GetVideoFeatureRequest request,
+                                StreamObserver<GetVideoFeatureResponse> responseObserver) {
+        GetVideoFeatureResponse response = null;
+        responseObserver.onNext(response);
+        responseObserver.onCompleted();
+    }
+}

+ 23 - 0
recommend-feature-service/src/main/java/com/tzld/piaoquan/recommend/feature/service/WarmUpService.java

@@ -0,0 +1,23 @@
+package com.tzld.piaoquan.recommend.feature.service;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.annotation.Order;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+
+/**
+ * @author dyp
+ */
+@Component
+@Order(1)
+public class WarmUpService {
+    @Autowired
+    private RedisTemplate<String, String> redisTemplate;
+
+    @PostConstruct
+    public void warmup() {
+        redisTemplate.opsForValue().get("");
+    }
+}

+ 28 - 0
recommend-feature-service/src/main/java/com/tzld/piaoquan/recommend/feature/util/CommonCollectionUtils.java

@@ -0,0 +1,28 @@
+package com.tzld.piaoquan.recommend.feature.util;
+
+import org.apache.commons.collections4.CollectionUtils;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+/**
+ * @author dyp
+ */
+public class CommonCollectionUtils {
+    public static <T, R> List<R> toList(List<T> list, Function<T, R> map) {
+        if (CollectionUtils.isEmpty(list)) {
+            return Collections.emptyList();
+        }
+        return list.stream().map(map).collect(Collectors.toList());
+    }
+
+    public static <T, K, V> Map<K, V> toMap(List<T> list, Function<T, K> keyFunc, Function<T, V> valueFunc) {
+        if (CollectionUtils.isEmpty(list)) {
+            return Collections.emptyMap();
+        }
+        return list.stream().collect(Collectors.toMap(keyFunc::apply, valueFunc::apply));
+    }
+}

+ 30 - 0
recommend-feature-service/src/main/java/com/tzld/piaoquan/recommend/feature/util/DateUtils.java

@@ -0,0 +1,30 @@
+package com.tzld.piaoquan.recommend.feature.util;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+
+/**
+ * @author dyp
+ */
+public final class DateUtils {
+    public static String getCurrentDateStr(String format) {
+        SimpleDateFormat dateFormat = new SimpleDateFormat(format);
+        Date currentDate = new Date();
+        return dateFormat.format(currentDate);
+    }
+
+    public static int getCurrentHour() {
+        Calendar calendar = Calendar.getInstance();
+        return calendar.get(Calendar.HOUR_OF_DAY);
+    }
+
+    public static String getBeforeDaysDateStr(String format, int d) {
+        SimpleDateFormat dateFormat = new SimpleDateFormat(format);
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(new Date());
+        calendar.add(Calendar.DAY_OF_MONTH, -d);
+        Date previousDate = calendar.getTime();
+        return dateFormat.format(previousDate);
+    }
+}

+ 96 - 0
recommend-feature-service/src/main/java/com/tzld/piaoquan/recommend/feature/util/HttpClientFactory.java

@@ -0,0 +1,96 @@
+package com.tzld.piaoquan.recommend.feature.util;
+
+import com.google.common.collect.Lists;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.concurrent.BasicThreadFactory;
+import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.config.Registry;
+import org.apache.http.config.RegistryBuilder;
+import org.apache.http.conn.HttpClientConnectionManager;
+import org.apache.http.conn.socket.ConnectionSocketFactory;
+import org.apache.http.conn.socket.PlainConnectionSocketFactory;
+import org.apache.http.conn.ssl.NoopHostnameVerifier;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+import org.apache.http.ssl.SSLContexts;
+import org.slf4j.MDC;
+
+import javax.net.ssl.SSLContext;
+import java.security.KeyManagementException;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.util.List;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+@Slf4j
+public class HttpClientFactory {
+
+    private static final ScheduledExecutorService SCHEDULED_CLOSED_EXECUTOR = new ScheduledThreadPoolExecutor(1,
+            new BasicThreadFactory.Builder().namingPattern("http conn-closed-thread-%s").priority(Thread.NORM_PRIORITY).daemon(false).build(), (r, e) -> log.error(" monitor push reject task error={}", e.toString()));
+
+    private static final List<HttpClientConnectionManager> HTTP_CLIENT_CONNECTION_MANAGERS = Lists.newArrayList();
+
+    static {
+        SCHEDULED_CLOSED_EXECUTOR.schedule(() -> HTTP_CLIENT_CONNECTION_MANAGERS.forEach(HttpClientConnectionManager::closeExpiredConnections), 5, TimeUnit.SECONDS);
+    }
+
+    private static HttpRequestInterceptor getInterceptor() {
+        HttpRequestInterceptor requestInterceptor = (request, context) -> {
+            try {
+                String missSpanId = MDC.get("missSpanId");
+                String missTraceId = MDC.get("request-id");
+                if (missTraceId != null && !"".equals(missTraceId.trim())) {
+                    request.setHeader("request-id", missTraceId);
+                }
+                if (missSpanId != null && !"".equals(missSpanId.trim())) {
+                    request.setHeader("missSpanId", missSpanId);
+                }
+            } catch (Exception e) {
+                log.error(e.getMessage(), e);
+            }
+        };
+        return requestInterceptor;
+    }
+
+    /**
+     * @param connectTimeout 连接超时时间 ms
+     * @param socketTimeout  读超时时间(等待数据超时时间)ms
+     * @param maxPerRoute    每个路由的最大连接数
+     * @param maxTotal       最大连接数
+     * @param retryCount     重试次数
+     * @return httpclient instance
+     */
+    public static CloseableHttpClient create(int connectTimeout, int socketTimeout, int maxPerRoute, int maxTotal,
+                                             int retryCount, int connectionWaitTimeout) {
+        try {
+            RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(connectTimeout).setSocketTimeout(socketTimeout).setConnectionRequestTimeout(connectionWaitTimeout).build();
+            CloseableHttpClient client = HttpClientBuilder.create()
+                    .setDefaultRequestConfig(requestConfig)
+                    .setConnectionManager(createConnectionManager(maxPerRoute, maxTotal))
+                    .setRetryHandler(new DefaultHttpRequestRetryHandler(retryCount, false)).addInterceptorFirst(getInterceptor()).build();
+            return client;
+        } catch (Throwable e) {
+            log.error("create HttpPoolClient exception", e);
+            throw new RuntimeException("create HttpPoolClient exception");
+        }
+    }
+
+    private static PoolingHttpClientConnectionManager createConnectionManager(int maxPerRoute, int maxTotal) throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
+        SSLContext sslContext = SSLContexts.custom().loadTrustMaterial((chain, authType) -> true).build();
+        Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
+                .register("http", PlainConnectionSocketFactory.getSocketFactory())
+                .register("https", new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE)).build();
+        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry);
+        cm.setDefaultMaxPerRoute(maxPerRoute);
+        cm.setMaxTotal(maxTotal);
+        HTTP_CLIENT_CONNECTION_MANAGERS.add(cm);
+        return cm;
+    }
+
+}

+ 38 - 0
recommend-feature-service/src/main/java/com/tzld/piaoquan/recommend/feature/util/JSONUtils.java

@@ -0,0 +1,38 @@
+package com.tzld.piaoquan.recommend.feature.util;
+
+import com.alibaba.fastjson.JSONObject;
+import com.google.common.reflect.TypeToken;
+import com.google.gson.Gson;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+
+@Slf4j
+public class JSONUtils {
+
+
+    public static String toJson(Object obj) {
+        if (obj == null) {
+            return "";
+        }
+        try {
+            return new Gson().toJson(obj);
+        } catch (Exception e) {
+            log.error("toJson exception", e);
+            return "";
+        }
+    }
+
+    public static <T> T fromJson(String value, TypeToken<T> typeToken, T defaultValue) {
+
+        if (StringUtils.isBlank(value)) {
+            return defaultValue;
+        }
+        try {
+            return JSONObject.parseObject(value, typeToken.getType());
+        } catch (Exception e) {
+            log.error("parseObject error! value=[{}]", value, e);
+        }
+        return defaultValue;
+    }
+
+}

+ 48 - 0
recommend-feature-service/src/main/java/com/tzld/piaoquan/recommend/feature/util/TraceUtils.java

@@ -0,0 +1,48 @@
+package com.tzld.piaoquan.recommend.feature.util;
+
+import org.apache.commons.lang.RandomStringUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.MDC;
+
+import java.net.InetAddress;
+
+/**
+ * @author dyp
+ */
+public class TraceUtils {
+    public static final String TRACE_ID_KEY = "traceId";
+    public static final String DEFAULT_TRACE_ID = "default_trace_id";
+
+    public static void setMDC() {
+        MDC.put(TraceUtils.TRACE_ID_KEY, genTraceId());
+    }
+
+    public static void removeMDC() {
+        MDC.remove(TraceUtils.TRACE_ID_KEY);
+    }
+
+    public static String genTraceId() {
+        StringBuilder traceIdBuilder = new StringBuilder();
+        InetAddress address = InetAddress.getLoopbackAddress();
+        if (address == null) {
+            return DEFAULT_TRACE_ID;
+        }
+        String addressStr = address.getHostAddress();
+        if (StringUtils.isBlank(addressStr)) {
+            return DEFAULT_TRACE_ID;
+        }
+        String[] addressArr = addressStr.split("\\.");
+        if (addressArr.length != 4) {
+            return DEFAULT_TRACE_ID;
+        }
+
+        traceIdBuilder.append(StringUtils.leftPad(Integer.toHexString(Integer.valueOf(addressArr[0])), 2, '0'))
+                .append(StringUtils.leftPad(Integer.toHexString(Integer.valueOf(addressArr[1])), 2, '0'))
+                .append(StringUtils.leftPad(Integer.toHexString(Integer.valueOf(addressArr[2])), 2, '0'))
+                .append(StringUtils.leftPad(Integer.toHexString(Integer.valueOf(addressArr[3])), 2, '0'))
+                .append(System.currentTimeMillis())
+                .append(RandomStringUtils.randomAlphabetic(4))
+                .append(Thread.currentThread().getId());
+        return traceIdBuilder.toString();
+    }
+}

+ 42 - 0
recommend-feature-service/src/main/java/com/tzld/piaoquan/recommend/feature/web/ControllerAspect.java

@@ -0,0 +1,42 @@
+package com.tzld.piaoquan.recommend.feature.web;
+
+import com.google.common.base.Stopwatch;
+import com.tzld.piaoquan.recommend.feature.util.JSONUtils;
+import com.tzld.piaoquan.recommend.feature.util.TraceUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.springframework.stereotype.Component;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author dyp
+ */
+@Aspect
+@Component
+@Slf4j
+public class ControllerAspect {
+
+    @Around("execution(* com.tzld.piaoquan.recommend.server.web.*Controller.*(..))")
+    public Object around(ProceedingJoinPoint pjp) throws Throwable {
+        TraceUtils.setMDC();
+
+        String className = pjp.getTarget().getClass().getSimpleName();
+        MethodSignature signature = (MethodSignature) pjp.getSignature();
+        Stopwatch stopwatch = Stopwatch.createStarted();
+        log.info("request className=[{}], method=[{}], param=[{}]", className, signature.getName(),
+                JSONUtils.toJson(pjp.getArgs()));
+        Object result = pjp.proceed();
+        if (result != null && result instanceof String) {
+            log.info("response result=[{}] cost=[{}]", result, stopwatch.stop().elapsed(TimeUnit.MILLISECONDS));
+        } else {
+            log.info("response result=[{}] cost=[{}]", JSONUtils.toJson(result), stopwatch.stop().elapsed(TimeUnit.MILLISECONDS));
+        }
+
+        TraceUtils.removeMDC();
+        return result;
+    }
+}

+ 94 - 0
recommend-feature-service/src/main/java/com/tzld/piaoquan/recommend/feature/web/RecommendController.java

@@ -0,0 +1,94 @@
+package com.tzld.piaoquan.recommend.feature.web;
+
+import com.google.common.base.Strings;
+import com.google.protobuf.InvalidProtocolBufferException;
+import com.tzld.piaoquan.recommend.feature.service.RecommendService;
+import com.tzld.piaoquan.recommend.server.client.ProtobufUtils;
+import com.tzld.piaoquan.recommend.server.client.RecommendHttpRequest;
+import com.tzld.piaoquan.recommend.server.gen.recommend.RecommendRequest;
+import com.tzld.piaoquan.recommend.server.gen.recommend.RecommendResponse;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.MapUtils;
+import org.slf4j.MDC;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author dyp
+ */
+@RestController
+@Slf4j
+public class RecommendController {
+    @Autowired
+    private RecommendService recommendService;
+
+    @Bean
+    public ProtobufHttpMessageConverter protobufHttpMessageConverter() {
+        return new ProtobufHttpMessageConverter();
+    }
+
+    @RequestMapping("/homepage/recommend")
+    public String homepageRecommend(@RequestBody RecommendHttpRequest httpRequest) {
+        MDC.put("appType", String.valueOf(httpRequest.getAppType()));
+        RecommendResponse response = recommendService.homepageRecommend(generate(httpRequest));
+        String result = "";
+        try {
+            result = ProtobufUtils.toJson(response);
+        } catch (InvalidProtocolBufferException e) {
+            log.error("homepageRecommend ProtobufUtils.toJson", e);
+        }
+        return result;
+    }
+
+    @RequestMapping("/relevant/recommend")
+    public String relevantRecommend(@RequestBody RecommendHttpRequest httpRequest) {
+        MDC.put("appType", String.valueOf(httpRequest.getAppType()));
+
+        RecommendResponse response = recommendService.relevantRecommend(generate(httpRequest));
+        String result = "";
+        try {
+            result = ProtobufUtils.toJson(response);
+        } catch (InvalidProtocolBufferException e) {
+            log.error("relevantRecommend ProtobufUtils.toJson", e);
+        }
+        return result;
+    }
+
+    private RecommendRequest generate(RecommendHttpRequest httpRequest) {
+        if (httpRequest == null) {
+            return null;
+        }
+        RecommendRequest.Builder builder = RecommendRequest.newBuilder();
+
+        builder.setRequestId(Strings.nullToEmpty(httpRequest.getRequestId()))
+                .setMid(Strings.nullToEmpty(httpRequest.getMid()))
+                .setUid(Strings.nullToEmpty(httpRequest.getUid()))
+                .setSize(httpRequest.getSize())
+                .setAppType(httpRequest.getAppType())
+                .setCityCode(Strings.nullToEmpty(httpRequest.getCityCode()))
+                .setProvinceCode(Strings.nullToEmpty(httpRequest.getProvinceCode()))
+                .setVersionAuditStatus(httpRequest.getVersionAuditStatus())
+                .setRecommendTraceId(Strings.nullToEmpty(httpRequest.getRecommendTraceId()))
+                .setVideoId(httpRequest.getVideoId());
+
+        if (CollectionUtils.isNotEmpty(httpRequest.getAbExpCodes())) {
+            builder.addAllAbExpCode(httpRequest.getAbExpCodes());
+        }
+        if (MapUtils.isNotEmpty(httpRequest.getEventIdMap())) {
+            builder.putAllEventId(httpRequest.getEventIdMap());
+        }
+
+        return builder.build();
+    }
+
+    @GetMapping("/ok")
+    public String ok() {
+        return "ok";
+    }
+}

+ 43 - 0
recommend-feature-service/src/main/resources/application-dev.yml

@@ -0,0 +1,43 @@
+server:
+  port: 8080
+grpc:
+  server:
+    port: 6666
+    reflection-service-enabled: true
+
+eureka:
+  instance:
+    prefer-ip-address: true #是否优先使用IP地址作为主机名的标识,默认false
+    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port} #注册到eureka上的唯一实例ID
+    lease-renewal-interval-in-seconds: 10 #表示eureka client发送心跳给server端的频率,默认30
+    lease-expiration-duration-in-seconds: 30 #表示eureka server至上一次收到client的心跳之后,等待下一次心跳的超时时间,在这个时间内若没收到下一次心跳,则将移除该instance,默认90
+  client:
+    registry-fetch-interval-seconds: 5  #定时从Eureka Server拉取服务注册信息的间隔时间
+    serviceUrl:
+      defaultZone: http://deveureka-internal.piaoquantv.com/eureka/
+
+  redis:
+    hostName: r-bp1ps6my7lzg8rdhwx682.redis.rds.aliyuncs.com
+    port: 6379
+    password: Wqsd@2019
+    timeout: 1000
+    lettuce:
+      pool:
+        max-active: 8
+        max-wait: -1
+        max-idle: 8
+        min-idle: 0
+
+apollo:
+  meta: http://devapolloconfig-internal.piaoquantv.com
+
+aliyun:
+  log:
+    endpoint: cn-hangzhou.log.aliyuncs.com
+    accessKeyId: LTAIP6x1l3DXfSxm
+    accessKeySecret: KbTaM9ars4OX3PMS6Xm7rtxGr1FLon
+    project: recommend-feature-test
+
+logging:
+  file:
+    path: /Users/dingyunpeng/Desktop/code/pq/recommend-feature/logs/${spring.application.name}/

+ 35 - 0
recommend-feature-service/src/main/resources/application-pre.yml

@@ -0,0 +1,35 @@
+server:
+  port: 8080
+
+eureka:
+  instance:
+    prefer-ip-address: true #是否优先使用IP地址作为主机名的标识,默认false
+    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port} #注册到eureka上的唯一实例ID
+    lease-renewal-interval-in-seconds: 10 #表示eureka client发送心跳给server端的频率,默认30
+    lease-expiration-duration-in-seconds: 30 #表示eureka server至上一次收到client的心跳之后,等待下一次心跳的超时时间,在这个时间内若没收到下一次心跳,则将移除该instance,默认90
+  client:
+    registry-fetch-interval-seconds: 5  #定时从Eureka Server拉取服务注册信息的间隔时间
+    serviceUrl:
+      defaultZone: http://preeureka-internal.piaoquantv.com/eureka/
+
+  redis:
+    hostName: r-bp1fogs2mflr1ybfot.redis.rds.aliyuncs.com
+    port: 6379
+    password: Wqsd@2019
+    timeout: 1000
+    lettuce:
+      pool:
+        max-active: 8
+        max-wait: -1
+        max-idle: 8
+        min-idle: 0
+
+apollo:
+  meta: http://preapolloconfig-internal.piaoquantv.com
+
+aliyun:
+  log:
+    endpoint: cn-hangzhou-intranet.log.aliyuncs.com
+    accessKeyId: LTAIP6x1l3DXfSxm
+    accessKeySecret: KbTaM9ars4OX3PMS6Xm7rtxGr1FLon
+    project: recommend-feature

+ 35 - 0
recommend-feature-service/src/main/resources/application-prod.yml

@@ -0,0 +1,35 @@
+server:
+  port: 8080
+
+eureka:
+  instance:
+    prefer-ip-address: true #是否优先使用IP地址作为主机名的标识,默认false
+    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port} #注册到eureka上的唯一实例ID
+    lease-renewal-interval-in-seconds: 10 #表示eureka client发送心跳给server端的频率,默认30
+    lease-expiration-duration-in-seconds: 30 #表示eureka server至上一次收到client的心跳之后,等待下一次心跳的超时时间,在这个时间内若没收到下一次心跳,则将移除该instance,默认90
+  client:
+    registry-fetch-interval-seconds: 5  #定时从Eureka Server拉取服务注册信息的间隔时间
+    serviceUrl:
+      defaultZone: http://eureka-internal.piaoquantv.com/eureka/
+
+  redis:
+    hostName: r-bp1fogs2mflr1ybfot.redis.rds.aliyuncs.com
+    port: 6379
+    password: Wqsd@2019
+    timeout: 1000
+    lettuce:
+      pool:
+        max-active: 8
+        max-wait: -1
+        max-idle: 8
+        min-idle: 0
+
+apollo:
+  meta: http://apolloconfig-internal.piaoquantv.com
+
+aliyun:
+  log:
+    endpoint: cn-hangzhou-intranet.log.aliyuncs.com
+    accessKeyId: LTAIP6x1l3DXfSxm
+    accessKeySecret: KbTaM9ars4OX3PMS6Xm7rtxGr1FLon
+    project: recommend-feature

+ 35 - 0
recommend-feature-service/src/main/resources/application-test.yml

@@ -0,0 +1,35 @@
+server:
+  port: 8080
+
+eureka:
+  instance:
+    prefer-ip-address: true #是否优先使用IP地址作为主机名的标识,默认false
+    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port} #注册到eureka上的唯一实例ID
+    lease-renewal-interval-in-seconds: 10 #表示eureka client发送心跳给server端的频率,默认30
+    lease-expiration-duration-in-seconds: 30 #表示eureka server至上一次收到client的心跳之后,等待下一次心跳的超时时间,在这个时间内若没收到下一次心跳,则将移除该instance,默认90
+  client:
+    registry-fetch-interval-seconds: 5  #定时从Eureka Server拉取服务注册信息的间隔时间
+    serviceUrl:
+      defaultZone: http://testeureka-internal.piaoquantv.com/eureka/
+
+  redis:
+    hostName: r-bp1ps6my7lzg8rdhwx682.redis.rds.aliyuncs.com
+    port: 6379
+    password: Wqsd@2019
+    timeout: 1000
+    lettuce:
+      pool:
+        max-active: 8
+        max-wait: -1
+        max-idle: 8
+        min-idle: 0
+
+apollo:
+  meta: http://testapolloconfig-internal.piaoquantv.com
+
+aliyun:
+  log:
+    endpoint: cn-hangzhou-intranet.log.aliyuncs.com
+    accessKeyId: LTAIP6x1l3DXfSxm
+    accessKeySecret: KbTaM9ars4OX3PMS6Xm7rtxGr1FLon
+    project: recommend-feature-test

+ 16 - 0
recommend-feature-service/src/main/resources/application.yml

@@ -0,0 +1,16 @@
+spring:
+  profiles:
+    active: dev
+  application:
+    name: recommend-feature
+logging:
+  file:
+    path: /datalog/weblog/${spring.application.name}/
+
+app:
+  id: recommend-feature
+apollo:
+  bootstrap:
+    enabled: true
+    namespaces: application
+  cacheDir: /datalog/apollo-cache-dir

+ 328 - 0
recommend-feature-service/src/main/resources/logback-spring.xml

@@ -0,0 +1,328 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出 -->
+<!-- scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true -->
+<!-- scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。 -->
+<!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
+<configuration scan="true" scanPeriod="10 seconds">
+
+    <!--为了防止进程退出时,内存中的数据丢失,请加上此选项-->
+    <shutdownHook class="ch.qos.logback.core.hook.DelayingShutdownHook"/>
+
+    <!-- 日志上下文名称 -->
+    <contextName>logback</contextName>
+
+    <!-- name的值是变量的名称,value的值时变量定义的值。通过定义的值会被插入到logger上下文中。定义变量后,可以使“${}”来使用变量。 -->
+    <!-- 日志输出格式 -->
+    <property name="LOG_PATTERN"
+              value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}\\(%L\\) - %msg%n"/>
+    <property name="mdcFields" value="traceId,appType"></property>
+
+    <!-- spring property使用方式 -->
+    <springProperty name="LOG_PATH" source="logging.file.path"/>
+    <springProperty name="aliyun_log_endpoint" source="aliyun.log.endpoint"/>
+    <springProperty name="aliyun_log_accessKeyId" source="aliyun.log.accessKeyId"/>
+    <springProperty name="aliyun_log_accessKeySecret" source="aliyun.log.accessKeySecret"/>
+    <springProperty name="aliyun_log_project" source="aliyun.log.project"/>
+
+    <!-- 彩色日志 -->
+    <!-- 彩色日志依赖的渲染类 -->
+    <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
+    <conversionRule conversionWord="wex"
+                    converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
+    <conversionRule conversionWord="wEx"
+                    converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
+    <!-- 彩色日志格式 -->
+    <property name="CONSOLE_LOG_PATTERN"
+              value="${CONSOLE_LOG_PATTERN:-%clr{traceId} %clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}\\(%L\\)){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
+
+    <!--输出到控制台-->
+    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+        <!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息-->
+        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+            <level>info</level>
+        </filter>
+        <encoder>
+            <Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
+            <!-- 设置字符集 -->
+            <charset>UTF-8</charset>
+        </encoder>
+    </appender>
+
+    <!--输出到文件-->
+    <!-- 时间滚动输出 level为 DEBUG 日志 -->
+    <appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <!-- 正在记录的日志文件的路径及文件名 -->
+        <file>${LOG_PATH}/debug.log</file>
+        <!--日志文件输出格式-->
+        <encoder>
+            <pattern>${LOG_PATTERN}</pattern>
+            <charset>UTF-8</charset> <!-- 设置字符集 -->
+        </encoder>
+        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 日志归档 -->
+            <fileNamePattern>${LOG_PATH}/debug/log-debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+                <maxFileSize>100MB</maxFileSize>
+            </timeBasedFileNamingAndTriggeringPolicy>
+            <!--日志文件保留天数-->
+            <maxHistory>15</maxHistory>
+        </rollingPolicy>
+        <!-- 此日志文件只记录debug级别的 -->
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>debug</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+    <!-- 时间滚动输出 level为 INFO 日志 -->
+    <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <!-- 正在记录的日志文件的路径及文件名 -->
+        <file>${LOG_PATH}/info.log</file>
+        <!--日志文件输出格式-->
+        <encoder>
+            <pattern>${LOG_PATTERN}</pattern>
+            <charset>UTF-8</charset>
+        </encoder>
+        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 每天日志归档路径以及格式 -->
+            <fileNamePattern>${LOG_PATH}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+                <maxFileSize>100MB</maxFileSize>
+            </timeBasedFileNamingAndTriggeringPolicy>
+            <!--日志文件保留天数-->
+            <maxHistory>15</maxHistory>
+        </rollingPolicy>
+        <!-- 此日志文件只记录info级别的 -->
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>info</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+    <!-- 时间滚动输出 level为 WARN 日志 -->
+    <appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <!-- 正在记录的日志文件的路径及文件名 -->
+        <file>${LOG_PATH}/warn.log</file>
+        <!--日志文件输出格式-->
+        <encoder>
+            <pattern>${LOG_PATTERN}</pattern>
+            <charset>UTF-8</charset> <!-- 此处设置字符集 -->
+        </encoder>
+        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>${LOG_PATH}/warn/log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+                <maxFileSize>100MB</maxFileSize>
+            </timeBasedFileNamingAndTriggeringPolicy>
+            <!--日志文件保留天数-->
+            <maxHistory>15</maxHistory>
+        </rollingPolicy>
+        <!-- 此日志文件只记录warn级别的 -->
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>warn</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+
+    <!-- 时间滚动输出 level为 ERROR 日志 -->
+    <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <!-- 正在记录的日志文件的路径及文件名 -->
+        <file>${LOG_PATH}/error.log</file>
+        <!--日志文件输出格式-->
+        <encoder>
+            <pattern>${LOG_PATTERN}</pattern>
+            <charset>UTF-8</charset> <!-- 此处设置字符集 -->
+        </encoder>
+        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>${LOG_PATH}/error/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+                <maxFileSize>100MB</maxFileSize>
+            </timeBasedFileNamingAndTriggeringPolicy>
+            <!--日志文件保留天数-->
+            <maxHistory>15</maxHistory>
+        </rollingPolicy>
+        <!-- 此日志文件只记录ERROR级别的 -->
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>ERROR</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+    <appender name="loghubAppenderInfo" class="com.tzld.piaoquan.recommend.feature.common.RecommendLoghubAppender">
+        <!--必选项-->
+        <!-- 账号及网络配置 -->
+        <endpoint>${aliyun_log_endpoint}</endpoint>
+        <accessKeyId>${aliyun_log_accessKeyId}</accessKeyId>
+        <accessKeySecret>${aliyun_log_accessKeySecret}</accessKeySecret>
+        <!-- sls 项目配置 -->
+        <project>${aliyun_log_project}</project>
+        <logStore>info-log</logStore>
+        <!--必选项 (end)-->
+        <!-- 可选项 -->
+        <topic></topic>
+        <source></source>
+        <!-- 可选项 详见 '参数说明'-->
+        <totalSizeInBytes>104857600</totalSizeInBytes>
+        <maxBlockMs>0</maxBlockMs>
+        <ioThreadCount>16</ioThreadCount>
+        <batchSizeThresholdInBytes>524288</batchSizeThresholdInBytes>
+        <batchCountThreshold>4096</batchCountThreshold>
+        <lingerMs>2000</lingerMs>
+        <retries>10</retries>
+        <baseRetryBackoffMs>100</baseRetryBackoffMs>
+        <maxRetryBackoffMs>50000</maxRetryBackoffMs>
+        <!-- 可选项 通过配置 encoder 的 pattern 自定义 log 的格式 -->
+        <!--日志文件输出格式-->
+        <encoder>
+            <pattern>${LOG_PATTERN}</pattern>
+            <charset>UTF-8</charset> <!-- 此处设置字符集 -->
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>INFO</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+        <mdcFields>
+            ${mdcFields}
+        </mdcFields>
+    </appender>
+
+    <appender name="loghubAppenderWarn" class="com.tzld.piaoquan.recommend.feature.common.RecommendLoghubAppender">
+        <!--必选项-->
+        <!-- 账号及网络配置 -->
+        <endpoint>${aliyun_log_endpoint}</endpoint>
+        <accessKeyId>${aliyun_log_accessKeyId}</accessKeyId>
+        <accessKeySecret>${aliyun_log_accessKeySecret}</accessKeySecret>
+        <!-- sls 项目配置 -->
+        <project>${aliyun_log_project}</project>
+        <logStore>warn-log</logStore>
+        <!--必选项 (end)-->
+        <!-- 可选项 -->
+        <topic></topic>
+        <source></source>
+        <!-- 可选项 详见 '参数说明'-->
+        <totalSizeInBytes>104857600</totalSizeInBytes>
+        <maxBlockMs>0</maxBlockMs>
+        <ioThreadCount>16</ioThreadCount>
+        <batchSizeThresholdInBytes>524288</batchSizeThresholdInBytes>
+        <batchCountThreshold>4096</batchCountThreshold>
+        <lingerMs>2000</lingerMs>
+        <retries>10</retries>
+        <baseRetryBackoffMs>100</baseRetryBackoffMs>
+        <maxRetryBackoffMs>50000</maxRetryBackoffMs>
+        <!-- 可选项 通过配置 encoder 的 pattern 自定义 log 的格式 -->
+        <!--日志文件输出格式-->
+        <encoder>
+            <pattern>${LOG_PATTERN}</pattern>
+            <charset>UTF-8</charset> <!-- 此处设置字符集 -->
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>WARN</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+        <mdcFields>
+            ${mdcFields}
+        </mdcFields>
+    </appender>
+
+    <appender name="loghubAppenderError" class="com.tzld.piaoquan.recommend.feature.common.RecommendLoghubAppender">
+        <!--必选项-->
+        <!-- 账号及网络配置 -->
+        <endpoint>${aliyun_log_endpoint}</endpoint>
+        <accessKeyId>${aliyun_log_accessKeyId}</accessKeyId>
+        <accessKeySecret>${aliyun_log_accessKeySecret}</accessKeySecret>
+        <!-- sls 项目配置 -->
+        <project>${aliyun_log_project}</project>
+        <logStore>error-log</logStore>
+        <!--必选项 (end)-->
+        <!-- 可选项 -->
+        <topic></topic>
+        <source></source>
+        <!-- 可选项 详见 '参数说明'-->
+        <totalSizeInBytes>104857600</totalSizeInBytes>
+        <maxBlockMs>0</maxBlockMs>
+        <ioThreadCount>16</ioThreadCount>
+        <batchSizeThresholdInBytes>524288</batchSizeThresholdInBytes>
+        <batchCountThreshold>4096</batchCountThreshold>
+        <lingerMs>2000</lingerMs>
+        <retries>10</retries>
+        <baseRetryBackoffMs>100</baseRetryBackoffMs>
+        <maxRetryBackoffMs>50000</maxRetryBackoffMs>
+        <!-- 可选项 通过配置 encoder 的 pattern 自定义 log 的格式 -->
+        <!--日志文件输出格式-->
+        <encoder>
+            <pattern>${LOG_PATTERN}</pattern>
+            <charset>UTF-8</charset> <!-- 此处设置字符集 -->
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>ERROR</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+        <mdcFields>
+            ${mdcFields}
+        </mdcFields>
+    </appender>
+
+    <!--
+        <logger>用来设置某一个包或者具体的某一个类的日志打印级别、
+        以及指定<appender>。<logger>仅有一个name属性,
+        一个可选的level和一个可选的addtivity属性。
+        name:用来指定受此logger约束的某一个包或者具体的某一个类。
+        level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,
+              还有一个特俗值INHERITED或者同义词NULL,代表强制执行上级的级别。
+              如果未设置此属性,那么当前logger将会继承上级的级别。
+        addtivity:是否向上级logger传递打印信息。默认是true。
+    -->
+    <!--<logger name="org.springframework.web" level="info"/>-->
+    <!--<logger name="org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor" level="INFO"/>-->
+    <!--
+        使用mybatis的时候,sql语句是debug下才会打印,而这里我们只配置了info,所以想要查看sql语句的话,有以下两种操作:
+        第一种把<root level="info">改成<root level="DEBUG">这样就会打印sql,不过这样日志那边会出现很多其他消息
+        第二种就是单独给dao下目录配置debug模式,代码如下,这样配置sql语句会打印,其他还是正常info级别:
+     -->
+
+    <!--
+        root节点是必选节点,用来指定最基础的日志输出级别,只有一个level属性
+        level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,
+        不能设置为INHERITED或者同义词NULL。默认是DEBUG
+        可以包含零个或多个元素,标识这个appender将会添加到这个logger。
+    -->
+
+    <springProfile name="dev">
+        <logger name="com.tzld.piaoquan.recommend.server" level="info"/>
+    </springProfile>
+    <springProfile name="test">
+        <logger name="com.tzld.piaoquan.recommend.server" level="info"/>
+    </springProfile>
+    <springProfile name="pre">
+        <logger name="com.tzld.piaoquan.recommend.server" level="info"/>
+    </springProfile>
+    <springProfile name="stress">
+        <logger name="com.tzld.piaoquan.recommend.server" level="info"/>
+    </springProfile>
+    <springProfile name="prod">
+        <logger name="com.tzld.piaoquan.recommend.server" level="info"/>
+    </springProfile>
+
+    <root level="info">
+        <appender-ref ref="CONSOLE"/>
+        <appender-ref ref="DEBUG_FILE"/>
+        <appender-ref ref="INFO_FILE"/>
+        <appender-ref ref="WARN_FILE"/>
+        <appender-ref ref="ERROR_FILE"/>
+        <appender-ref ref="loghubAppenderInfo"/>
+        <appender-ref ref="loghubAppenderWarn"/>
+        <appender-ref ref="loghubAppenderError"/>
+    </root>
+
+</configuration>

+ 54 - 0
recommend-feature-service/src/main/resources/mybatis-generator-config.xml

@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE generatorConfiguration
+        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
+        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
+<!-- 配置生成器 -->
+<generatorConfiguration>
+    <context id="mysql" defaultModelType="flat">
+        <property name="autoDelimitKeywords" value="true"/>
+        <!-- 生成的Java文件的编码 -->
+        <property name="javaFileEncoding" value="UTF-8"/>
+        <!-- 格式化java代码 -->
+        <property name="javaFormatter" value="org.mybatis.generator.api.dom.DefaultJavaFormatter"/>
+        <!-- 格式化XML代码 -->
+        <property name="xmlFormatter" value="org.mybatis.generator.api.dom.DefaultXmlFormatter"/>
+        <!-- beginningDelimiter和endingDelimiter:指明数据库的用于标记数据库对象名的符号,比如ORACLE就是双引号,MYSQL默认是`反引号; -->
+        <property name="beginningDelimiter" value="`"/>
+        <property name="endingDelimiter" value="`"/>
+
+        <plugin type="org.mybatis.generator.plugins.ToStringPlugin"></plugin>
+        <plugin type="org.mybatis.generator.plugins.UnmergeableXmlMappersPlugin" />
+
+        <commentGenerator>
+            <property name="addRemarkComments" value="true"/>
+        </commentGenerator>
+
+        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
+                        connectionURL="jdbc:mysql://rm-bp1k5853td1r25g3n690.mysql.rds.aliyuncs.com:3306/longvideo?useUnicode=true&amp;characterEncoding=utf-8&amp;zeroDateTimeBehavior=convertToNull&amp;useSSL=false"
+                        userId="wx2016_longvideo" password="wx2016_longvideoP@assword1234">
+        </jdbcConnection>
+
+        <javaTypeResolver type="org.mybatis.generator.internal.types.JavaTypeResolverDefaultImpl">
+            <property name="forceBigDecimals" value="false"/>
+        </javaTypeResolver>
+
+
+
+        <javaModelGenerator targetPackage="com.tzld.piaoquan.recommend.server.model.po" targetProject="src/main/java">
+            <property name="constructorBased" value="false"/>
+            <property name="enableSubPackages" value="true"/>
+            <property name="immutable" value="false"/>
+        </javaModelGenerator>
+
+        <sqlMapGenerator targetPackage="mapper" targetProject="src/main/resources">
+            <property name="enableSubPackages" value="true"/>
+        </sqlMapGenerator>
+
+        <javaClientGenerator targetPackage="com.tzld.piaoquan.recommend.server.dao.mapper" type="XMLMAPPER" targetProject="src/main/java">
+            <property name="enableSubPackages" value="true"/>
+        </javaClientGenerator>
+
+        <table tableName="wx_ab_laboratory_white" domainObjectName="WxAbLaboratoryWhite" alias=""/>
+    </context>
+
+</generatorConfiguration>