Browse Source

web api stash and fix charge

huodongdong 7 years ago
parent
commit
0a7d232e36
67 changed files with 1227 additions and 227 deletions
  1. 6 0
      rankin-api-web/pom.xml
  2. 0 2
      rankin-api-web/src/main/java/cn/rankin/apiweb/ApiWebApplication.java
  3. 14 0
      rankin-api-web/src/main/java/cn/rankin/apiweb/assist/resolver/NeedUser.java
  4. 36 0
      rankin-api-web/src/main/java/cn/rankin/apiweb/assist/resolver/NeedUserResolver.java
  5. 14 0
      rankin-api-web/src/main/java/cn/rankin/apiweb/assist/resolver/RequestHeader.java
  6. 37 0
      rankin-api-web/src/main/java/cn/rankin/apiweb/assist/resolver/RequestHeaderResolver.java
  7. 16 1
      rankin-api-web/src/main/java/cn/rankin/apiweb/code/ApiWebCode.java
  8. 8 13
      rankin-api-web/src/main/java/cn/rankin/apiweb/configuration/FrontConfiguration.java
  9. 10 7
      rankin-api-web/src/main/java/cn/rankin/apiweb/configuration/RedisCacheConfig.java
  10. 4 0
      rankin-api-web/src/main/java/cn/rankin/apiweb/controller/CourseController.java
  11. 4 0
      rankin-api-web/src/main/java/cn/rankin/apiweb/controller/LessonController.java
  12. 44 0
      rankin-api-web/src/main/java/cn/rankin/apiweb/controller/LoginController.java
  13. 32 0
      rankin-api-web/src/main/java/cn/rankin/apiweb/controller/RecommendController.java
  14. 4 0
      rankin-api-web/src/main/java/cn/rankin/apiweb/controller/ShopCartController.java
  15. 4 0
      rankin-api-web/src/main/java/cn/rankin/apiweb/controller/SupportController.java
  16. 4 0
      rankin-api-web/src/main/java/cn/rankin/apiweb/controller/TagController.java
  17. 0 33
      rankin-api-web/src/main/java/cn/rankin/apiweb/controller/TestFeignClient.java
  18. 10 0
      rankin-api-web/src/main/java/cn/rankin/apiweb/controller/UserController.java
  19. 19 0
      rankin-api-web/src/main/java/cn/rankin/apiweb/entity/GlobalHeader.java
  20. 47 47
      rankin-api-web/src/main/java/cn/rankin/apiweb/intercepter/LoginInterceptor.java
  21. 0 11
      rankin-api-web/src/main/java/cn/rankin/apiweb/service/ProductService.java
  22. 16 0
      rankin-api-web/src/main/java/cn/rankin/apiweb/service/auth/AuthClient.java
  23. 21 7
      rankin-api-web/src/main/java/cn/rankin/apiweb/service/auth/AuthService.java
  24. 0 30
      rankin-api-web/src/main/java/cn/rankin/apiweb/service/auth/SimpleAuthService.java
  25. 17 0
      rankin-api-web/src/main/java/cn/rankin/apiweb/service/product/ProductClient.java
  26. 35 0
      rankin-api-web/src/main/java/cn/rankin/apiweb/service/product/ProductService.java
  27. 60 1
      rankin-api-web/src/main/java/cn/rankin/apiweb/service/user/UserClient.java
  28. 207 0
      rankin-api-web/src/main/java/cn/rankin/apiweb/service/user/UserService.java
  29. 17 0
      rankin-api-web/src/main/java/cn/rankin/apiweb/utils/DTOConverter.java
  30. 19 0
      rankin-api-web/src/main/java/cn/rankin/apiweb/utils/RequestHeaderManager.java
  31. 46 0
      rankin-api-web/src/main/java/cn/rankin/apiweb/utils/SecurityManager.java
  32. 1 1
      rankin-cms-web/src/main/java/cn/rankin/cmsweb/controller/trade/MoneyController.java
  33. 3 2
      rankin-cms-web/src/main/java/cn/rankin/cmsweb/controller/trade/OrderController.java
  34. 2 5
      rankin-cms-web/src/main/java/cn/rankin/cmsweb/service/trade/order/OrderClient.java
  35. 3 6
      rankin-cms-web/src/main/java/cn/rankin/cmsweb/service/trade/order/OrderService.java
  36. 2 2
      rankin-common-utils/src/main/java/cn/rankin/common/utils/constant/Constant.java
  37. 2 0
      rankin-common-utils/src/main/java/cn/rankin/common/utils/constant/RedisKey.java
  38. 4 0
      rankin-common-utils/src/main/java/cn/rankin/common/utils/service/impl/RedisServiceImpl.java
  39. 13 0
      rankin-data-api/src/main/java/cn/rankin/data/api/app/dto/DeviceLoginDTO.java
  40. 19 0
      rankin-data-api/src/main/java/cn/rankin/data/api/app/dto/LoginInfoDTO.java
  41. 11 12
      rankin-api-web/src/main/java/cn/rankin/apiweb/vo/DeviceUserVo.java
  42. 21 0
      rankin-data-api/src/main/java/cn/rankin/data/api/app/vo/RecommendVo.java
  43. 25 0
      rankin-data-api/src/main/java/cn/rankin/data/api/app/vo/UserInfoVo.java
  44. 1 2
      rankin-data-api/src/main/java/cn/rankin/data/api/product/dto/CourseDTO.java
  45. 2 2
      rankin-data-api/src/main/java/cn/rankin/data/api/product/entity/Course.java
  46. 9 0
      rankin-data-api/src/main/java/cn/rankin/data/api/product/entity/Recommend.java
  47. 1 1
      rankin-data-api/src/main/java/cn/rankin/data/api/product/vo/CourseVo.java
  48. 13 0
      rankin-data-api/src/main/java/cn/rankin/data/api/trade/dto/OrderSendDTO.java
  49. 28 0
      rankin-data-api/src/main/java/cn/rankin/data/api/user/dto/TerminalDeviceDTO.java
  50. 5 6
      rankin-data-api/src/main/java/cn/rankin/data/api/user/entity/TerminalDevice.java
  51. 48 0
      rankin-data-api/src/main/java/cn/rankin/data/api/user/entity/TerminalDeviceBindLog.java
  52. 6 1
      rankin-data-api/src/main/java/cn/rankin/data/api/user/entity/TerminalUser.java
  53. 4 0
      rankin-data-api/src/main/java/cn/rankin/data/api/user/vo/TerminalUserVo.java
  54. 11 0
      rankin-product-service/src/main/java/cn/rankin/productservice/controller/RecommendController.java
  55. 56 9
      rankin-product-service/src/main/java/cn/rankin/productservice/service/RecommendService.java
  56. 3 6
      rankin-trade-service/src/main/java/cn/rankin/tradeservice/controller/OrderController.java
  57. 2 2
      rankin-trade-service/src/main/java/cn/rankin/tradeservice/repository/OrderDetailRepository.java
  58. 10 8
      rankin-trade-service/src/main/java/cn/rankin/tradeservice/service/OrderDetailService.java
  59. 7 0
      rankin-user-service/src/main/java/cn/rankin/userservice/code/UserServiceAPICode.java
  60. 38 0
      rankin-user-service/src/main/java/cn/rankin/userservice/controller/TerminalDeviceController.java
  61. 17 0
      rankin-user-service/src/main/java/cn/rankin/userservice/controller/TerminalUserController.java
  62. 7 0
      rankin-user-service/src/main/java/cn/rankin/userservice/repository/TerminalDeviceBindLogRepository.java
  63. 6 10
      rankin-user-service/src/main/java/cn/rankin/userservice/repository/TerminalDeviceRepository.java
  64. 2 0
      rankin-user-service/src/main/java/cn/rankin/userservice/repository/TerminalUserRepository.java
  65. 26 0
      rankin-user-service/src/main/java/cn/rankin/userservice/service/TerminalDeviceBindLogService.java
  66. 64 0
      rankin-user-service/src/main/java/cn/rankin/userservice/service/TerminalDeviceService.java
  67. 4 0
      rankin-user-service/src/main/java/cn/rankin/userservice/service/TerminalUserService.java

+ 6 - 0
rankin-api-web/pom.xml

@@ -59,6 +59,12 @@
             <artifactId>rankin-common-utils</artifactId>
             <version>0.0.1-SNAPSHOT</version>
         </dependency>
+
+        <dependency>
+            <groupId>cn.rankin</groupId>
+            <artifactId>rankin-data-api</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+        </dependency>
 	</dependencies>
 
 	<dependencyManagement>

+ 0 - 2
rankin-api-web/src/main/java/cn/rankin/apiweb/ApiWebApplication.java

@@ -6,11 +6,9 @@ import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
 import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
 import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
 import org.springframework.cloud.netflix.feign.EnableFeignClients;
-import org.springframework.context.annotation.ComponentScan;
 
 @EnableFeignClients
 @EnableDiscoveryClient
-@ComponentScan(basePackages = {"cn.rankin.apiweb", "cn.rankin.common.utils"})
 @SpringBootApplication(exclude={DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class})
 public class ApiWebApplication {
 

+ 14 - 0
rankin-api-web/src/main/java/cn/rankin/apiweb/assist/resolver/NeedUser.java

@@ -0,0 +1,14 @@
+package cn.rankin.apiweb.assist.resolver;
+
+import java.lang.annotation.*;
+
+/**
+ * @author tomas
+ * @date 2017-04-17
+ * @what
+ */
+@Target({ElementType.PARAMETER})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface NeedUser {
+}

+ 36 - 0
rankin-api-web/src/main/java/cn/rankin/apiweb/assist/resolver/NeedUserResolver.java

@@ -0,0 +1,36 @@
+package cn.rankin.apiweb.assist.resolver;
+
+import cn.rankin.data.api.app.vo.DeviceUserVo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.core.MethodParameter;
+import org.springframework.stereotype.Component;
+import org.springframework.web.bind.support.WebDataBinderFactory;
+import org.springframework.web.context.request.NativeWebRequest;
+import org.springframework.web.context.request.WebRequest;
+import org.springframework.web.method.support.HandlerMethodArgumentResolver;
+import org.springframework.web.method.support.ModelAndViewContainer;
+
+/**
+ * @author Tomas
+ * @date 2016-07-17
+ * @what
+ */
+@Component
+public class NeedUserResolver implements HandlerMethodArgumentResolver {
+
+    private static final Logger logger = LoggerFactory.getLogger(NeedUserResolver.class);
+
+    public boolean supportsParameter(MethodParameter methodParameter) {
+        return  methodParameter.hasParameterAnnotation(NeedUser.class);
+    }
+
+    public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
+        logger.info(String.format("Resolve argument %s.", methodParameter.getParameterName()));
+        if(methodParameter.hasParameterAnnotation(NeedUser.class)){
+            DeviceUserVo deviceUserVo = (DeviceUserVo) nativeWebRequest.getAttribute("RANKIN_DEVICE_USER_INFO", WebRequest.SCOPE_REQUEST);
+            return deviceUserVo;
+        }
+        return null;
+    }
+}

+ 14 - 0
rankin-api-web/src/main/java/cn/rankin/apiweb/assist/resolver/RequestHeader.java

@@ -0,0 +1,14 @@
+package cn.rankin.apiweb.assist.resolver;
+
+import java.lang.annotation.*;
+
+/**
+ * @author tomas
+ * @date 2017-04-17
+ * @what
+ */
+@Target({ElementType.PARAMETER})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface RequestHeader {
+}

+ 37 - 0
rankin-api-web/src/main/java/cn/rankin/apiweb/assist/resolver/RequestHeaderResolver.java

@@ -0,0 +1,37 @@
+package cn.rankin.apiweb.assist.resolver;
+
+import cn.rankin.apiweb.entity.GlobalHeader;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.core.MethodParameter;
+import org.springframework.stereotype.Component;
+import org.springframework.web.bind.support.WebDataBinderFactory;
+import org.springframework.web.context.request.NativeWebRequest;
+import org.springframework.web.method.support.HandlerMethodArgumentResolver;
+import org.springframework.web.method.support.ModelAndViewContainer;
+
+/**
+ * @author Tomas
+ * @date 2016-07-17
+ * @what
+ */
+@Component
+public class RequestHeaderResolver implements HandlerMethodArgumentResolver {
+    private static final Logger logger = LoggerFactory.getLogger(RequestHeaderResolver.class);
+    public boolean supportsParameter(MethodParameter methodParameter) {
+        return  methodParameter.hasParameterAnnotation(RequestHeader.class);
+    }
+
+    public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
+        logger.info(String.format("Resolve argument %s.", methodParameter.getParameterName()));
+        if(methodParameter.hasParameterAnnotation(RequestHeader.class)){
+            GlobalHeader globalHeader = new GlobalHeader();
+            globalHeader.setUid(nativeWebRequest.getHeader("uid"));
+            globalHeader.setEid(nativeWebRequest.getHeader("eid"));
+            globalHeader.setSign(nativeWebRequest.getHeader("sign"));
+            globalHeader.setRequestId(nativeWebRequest.getHeader("requestId"));
+            return globalHeader;
+        }
+        return null;
+    }
+}

+ 16 - 1
rankin-api-web/src/main/java/cn/rankin/apiweb/code/ApiWebCode.java

@@ -6,5 +6,20 @@ import cn.rankin.common.utils.api.model.BaseCode;
 public class ApiWebCode extends APICode {
 
     public static final int _DEVICE_BOUND_ERROR = 30001;
-    public static final BaseCode DEVICE_BOUND_ERROR = new BaseCode(_DEVICE_BOUND_ERROR, "终端与账号不匹配");
+    public static final BaseCode DEVICE_BOUND_ERROR = new BaseCode(_DEVICE_BOUND_ERROR, "绑定终端失败");
+
+    public static final int _C_LOGIN_ERROR = 31001;
+    public static final BaseCode LOGIN_ERROR = new BaseCode(_C_LOGIN_ERROR, "登录失败");
+
+    public static final int _C_PASSWORD_ERROR = 31002;
+    public static final BaseCode PASSWORD_ERROR = new BaseCode(_C_PASSWORD_ERROR, "密码错误");
+
+    public static final int _C_TOKEN_EXPIRE = 31003;
+    public static final BaseCode TOKEN_EXPIRE = new BaseCode(_C_TOKEN_EXPIRE, "token过期");
+
+    public static final int _C_LOGOUT_ERROR = 31004;
+    public static final BaseCode LOGOUT_ERROR = new BaseCode(_C_LOGOUT_ERROR, "退出失败");
+
+    public static final int _C_HEADER_ERROR = 32001;
+    public static final BaseCode HEADER_ERROR = new BaseCode(_C_HEADER_ERROR, "请求头错误");
 }

+ 8 - 13
rankin-api-web/src/main/java/cn/rankin/apiweb/configuration/FrontConfiguration.java

@@ -1,17 +1,14 @@
 package cn.rankin.apiweb.configuration;
 
-import cn.rankin.apiweb.intercepter.LoginInterceptor;
-import cn.rankin.common.utils.configuration.RedisConfiguration;
+import cn.rankin.apiweb.assist.resolver.NeedUserResolver;
+import cn.rankin.apiweb.assist.resolver.RequestHeaderResolver;
+import cn.rankin.apiweb.intercepter.RequestSignatureInterceptor;
 import cn.rankin.common.utils.web.intercepter.HeaderProcessIntercepter;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 import org.springframework.context.EnvironmentAware;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.ComponentScan;
 import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.Import;
 import org.springframework.core.env.Environment;
 import org.springframework.web.method.support.HandlerMethodArgumentResolver;
 import org.springframework.web.servlet.config.annotation.EnableWebMvc;
@@ -36,22 +33,20 @@ public class FrontConfiguration extends WebMvcConfigurerAdapter implements Envir
 	private static final Logger logger = LoggerFactory.getLogger(FrontConfiguration.class);
 
 	@Autowired
-	private LoginInterceptor loginInterceptor;
-
-	@Autowired
-	private HeaderProcessIntercepter headerProcessIntercepter;
+	private RequestSignatureInterceptor requestSignatureInterceptor;
 
 	// 当前的环境对象
 	protected Environment environment;
 
 
 	public void addInterceptors(InterceptorRegistry registry) {
-		registry.addInterceptor(headerProcessIntercepter);
-//		registry.addInterceptor(loginInterceptor).excludePathPatterns("*/favicon.ico");
+		registry.addInterceptor(new HeaderProcessIntercepter());
+		registry.addInterceptor(requestSignatureInterceptor).excludePathPatterns("*/favicon.ico");
 	}
 
 	public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
-//		argumentResolvers.add(needUserResolver);
+		argumentResolvers.add(new RequestHeaderResolver());
+		argumentResolvers.add(new NeedUserResolver());
 		super.addArgumentResolvers(argumentResolvers);
 	}
 

+ 10 - 7
rankin-api-web/src/main/java/cn/rankin/apiweb/configuration/RedisCacheConfig.java

@@ -1,5 +1,7 @@
 package cn.rankin.apiweb.configuration;
 
+import cn.rankin.common.utils.service.RedisService;
+import cn.rankin.common.utils.service.impl.RedisServiceImpl;
 import com.fasterxml.jackson.annotation.JsonAutoDetect;
 import com.fasterxml.jackson.annotation.PropertyAccessor;
 import com.fasterxml.jackson.databind.ObjectMapper;
@@ -30,9 +32,6 @@ public class RedisCacheConfig extends CachingConfigurerSupport {
     @Value("${spring.redis.timeout}")
     private int timeout;
 
-    @Value("${spring.redis.database}")
-    private Integer database;
-
     @Value("${spring.redis.password}")
     private String password;
 
@@ -49,9 +48,6 @@ public class RedisCacheConfig extends CachingConfigurerSupport {
         factory.setPort(port);
         factory.setTimeout(timeout);
         factory.setPassword(password);
-        if (database != null) {
-            factory.setDatabase(database);
-        }
         return factory;
     }
 
@@ -64,7 +60,7 @@ public class RedisCacheConfig extends CachingConfigurerSupport {
      *
      * @return
      */
-    @Bean
+    @Bean(name = "redisTemplate")
     public RedisTemplate<Object, Object> redisTemplate() {
         //StringRedisTemplate的构造方法中默认设置了stringSerializer
         RedisTemplate<Object, Object> template = new RedisTemplate<>();
@@ -103,6 +99,13 @@ public class RedisCacheConfig extends CachingConfigurerSupport {
         return redisCacheManager;
     }
 
+    @Bean
+    public RedisService redisService() {
+        RedisServiceImpl redisService = new RedisServiceImpl();
+        redisService.setRedisTemplate(this.redisTemplate());
+        return redisService;
+    }
+
     /**
      * 自定义生成redis-key
      *

+ 4 - 0
rankin-api-web/src/main/java/cn/rankin/apiweb/controller/CourseController.java

@@ -0,0 +1,4 @@
+package cn.rankin.apiweb.controller;
+
+public class CourseController {
+}

+ 4 - 0
rankin-api-web/src/main/java/cn/rankin/apiweb/controller/LessonController.java

@@ -0,0 +1,4 @@
+package cn.rankin.apiweb.controller;
+
+public class LessonController {
+}

+ 44 - 0
rankin-api-web/src/main/java/cn/rankin/apiweb/controller/LoginController.java

@@ -0,0 +1,44 @@
+package cn.rankin.apiweb.controller;
+
+import cn.rankin.apiweb.assist.resolver.RequestHeader;
+import cn.rankin.apiweb.entity.GlobalHeader;
+import cn.rankin.apiweb.service.user.UserService;
+import cn.rankin.common.utils.api.model.APIResult;
+import cn.rankin.common.utils.util.HttpUtil;
+import cn.rankin.data.api.app.dto.DeviceLoginDTO;
+import cn.rankin.data.api.app.dto.LoginInfoDTO;
+import cn.rankin.data.api.app.vo.UserInfoVo;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
+
+@RestController
+@RequestMapping(value = "/token")
+public class LoginController {
+
+    @Autowired
+    private UserService userService;
+
+    @RequestMapping(method = RequestMethod.POST)
+    public APIResult<UserInfoVo> login(HttpServletRequest request, @RequestBody LoginInfoDTO loginInfoDTO) {
+        String ip = HttpUtil.getClientIp(request);
+        loginInfoDTO.setIp(ip);
+        return userService.login(loginInfoDTO);
+    }
+
+    @RequestMapping(method = RequestMethod.PUT)
+    public APIResult<UserInfoVo> refresh(@RequestBody DeviceLoginDTO deviceLoginDTO) {
+        String deviceCode = deviceLoginDTO.getDeviceCode();
+        return userService.refresh(deviceCode, Boolean.TRUE);
+    }
+
+    @RequestMapping(method = RequestMethod.DELETE)
+    public APIResult<Boolean> logout(@RequestHeader GlobalHeader globalHeader) {
+        String userId = globalHeader.getUid();
+        return userService.logout(userId);
+    }
+}

+ 32 - 0
rankin-api-web/src/main/java/cn/rankin/apiweb/controller/RecommendController.java

@@ -0,0 +1,32 @@
+package cn.rankin.apiweb.controller;
+
+import cn.rankin.apiweb.assist.resolver.NeedUser;
+import cn.rankin.apiweb.code.ApiWebCode;
+import cn.rankin.apiweb.service.product.ProductService;
+import cn.rankin.common.utils.api.model.APIResult;
+import cn.rankin.data.api.app.vo.DeviceUserVo;
+import cn.rankin.data.api.app.vo.RecommendVo;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+@RestController
+@RequestMapping(value = "/recommend")
+public class RecommendController {
+
+    @Autowired
+    private ProductService productService;
+
+    @RequestMapping(value = "/courses", method = RequestMethod.GET)
+    public APIResult<List<RecommendVo>> getRecommend(@NeedUser DeviceUserVo user) {
+        if (user == null) {
+            return APIResult.error(ApiWebCode.NOT_EXISTS);
+        }
+
+        String merchantId = user.getMerchantId();
+        return productService.getRecommendCourses(merchantId);
+    }
+}

+ 4 - 0
rankin-api-web/src/main/java/cn/rankin/apiweb/controller/ShopCartController.java

@@ -0,0 +1,4 @@
+package cn.rankin.apiweb.controller;
+
+public class ShopCartController {
+}

+ 4 - 0
rankin-api-web/src/main/java/cn/rankin/apiweb/controller/SupportController.java

@@ -0,0 +1,4 @@
+package cn.rankin.apiweb.controller;
+
+public class SupportController {
+}

+ 4 - 0
rankin-api-web/src/main/java/cn/rankin/apiweb/controller/TagController.java

@@ -0,0 +1,4 @@
+package cn.rankin.apiweb.controller;
+
+public class TagController {
+}

+ 0 - 33
rankin-api-web/src/main/java/cn/rankin/apiweb/controller/TestFeignClient.java

@@ -1,33 +0,0 @@
-package cn.rankin.apiweb.controller;
-
-import cn.rankin.apiweb.service.ProductService;
-import cn.rankin.common.utils.service.RedisService;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.RestController;
-
-@RestController
-public class TestFeignClient {
-
-    @Value("${config.current.name}")
-    private String name;
-
-    @Autowired
-    ProductService productClient;
-
-    @Autowired
-    private RedisService redisService;
-
-    @RequestMapping(value = "/hello", method = RequestMethod.GET)
-    public String test() {
-        return "current profile: " + name;
-    }
-
-    @RequestMapping(value = "/redis", method = RequestMethod.GET)
-    public Object redis() {
-        redisService.set("test", 1);
-        return redisService.get("test");
-    }
-}

+ 10 - 0
rankin-api-web/src/main/java/cn/rankin/apiweb/controller/UserController.java

@@ -0,0 +1,10 @@
+package cn.rankin.apiweb.controller;
+
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping(value = "/user")
+public class UserController {
+
+}

+ 19 - 0
rankin-api-web/src/main/java/cn/rankin/apiweb/entity/GlobalHeader.java

@@ -0,0 +1,19 @@
+package cn.rankin.apiweb.entity;
+
+import lombok.Data;
+import lombok.ToString;
+
+import java.io.Serializable;
+
+@Data
+@ToString
+public class GlobalHeader implements Serializable {
+
+    private String uid;
+
+    private String eid;
+
+    private String sign;
+
+    private String requestId;
+}

+ 47 - 47
rankin-api-web/src/main/java/cn/rankin/apiweb/intercepter/LoginInterceptor.java

@@ -1,16 +1,17 @@
 package cn.rankin.apiweb.intercepter;
 
 import cn.rankin.apiweb.code.ApiWebCode;
-import cn.rankin.apiweb.vo.DeviceUserVo;
-import cn.rankin.common.utils.api.model.BaseCode;
+import cn.rankin.apiweb.entity.GlobalHeader;
+import cn.rankin.apiweb.service.user.UserService;
+import cn.rankin.apiweb.utils.RequestHeaderManager;
+import cn.rankin.apiweb.utils.SecurityManager;
 import cn.rankin.common.utils.constant.RedisKey;
-import cn.rankin.common.utils.service.RedisService;
 import cn.rankin.common.utils.util.HttpUtil;
+import cn.rankin.data.api.app.vo.DeviceUserVo;
 import com.alibaba.fastjson.JSON;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
 import org.springframework.core.NamedThreadLocal;
 import org.springframework.http.HttpMethod;
 import org.springframework.stereotype.Component;
@@ -21,12 +22,13 @@ import org.springframework.web.servlet.ModelAndView;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.util.ArrayList;
+import java.util.Date;
 import java.util.List;
 
 @Component
-public class LoginInterceptor implements HandlerInterceptor {
+public class RequestSignatureInterceptor implements HandlerInterceptor {
 
-    private static final Logger logger = LoggerFactory.getLogger(LoginInterceptor.class);
+    private static final Logger logger = LoggerFactory.getLogger(RequestSignatureInterceptor.class);
 
     private static final String LOGIN_TOKEN_FORMAT_KEY = RedisKey.LOGIN_TOKEN_FORMAT_KEY;
 
@@ -34,23 +36,26 @@ public class LoginInterceptor implements HandlerInterceptor {
 
     private NamedThreadLocal<Long> startTimeThreadLocal = new NamedThreadLocal<Long>("StopWatch-StartTime");
 
-    @Autowired
-    private RedisService<String, Object> redisService;
-
     // 忽略options请求,默认为true
     private boolean ignoreOptions = true;
 
-    @Value("${'${request.header.ignore_path}'.split(',')}")
-    private List<String> ignorePaths = new ArrayList<>();
+//    @Value("${'${request.header.ignore_path}'.split(',')}")
+    private static List<String> ignorePaths = new ArrayList<>();
+
+    static {
+        ignorePaths.add("/token");
+    }
+
+    @Autowired
+    private UserService userService;
 
     @Override
     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
-
         //判断 OPTIONS 请求
         if (ignoreOptions && HttpMethod.OPTIONS.matches(request.getMethod())) {
             logger.info("OPTIONS 请求 忽略 返回200");
             request.setAttribute("NO_LOGIN_SIGN", "OPTIONS");
-            responseOutWithJson(request, response);
+            writeResponse(request, response, ApiWebCode.OK);
             return false;
         }
 
@@ -60,31 +65,44 @@ public class LoginInterceptor implements HandlerInterceptor {
         //登录请求不拦截
         String path = request.getServletPath();
         if (ignorePaths.contains(path)) {
-            logger.info("url: {} not intercepted!");
+            logger.info("url: {} do not intercepted!");
             return true;
         }
 
-        String uid = request.getHeader("uid");
-        String eid = request.getHeader("eid");
-        String sign = request.getHeader("sign");
-        String requestId = request.getHeader("requestId");
-        logger.info("request start, requestId={}, path={}, uid={}, eid={}, sign={}", requestId, path, uid, eid, sign);
+        GlobalHeader headers = RequestHeaderManager.parseHeader(request);
+        logger.info("request start, path={}, headers={}", path, JSON.toJSONString(headers));
 
-        if (StringUtils.isEmpty(uid) || StringUtils.isEmpty(sign)) {
-            return false;
-        }
+        String uid = headers.getUid();
+        String sign = headers.getSign();
+//        if (StringUtils.isEmpty(uid) || StringUtils.isEmpty(sign)) {
+//            writeResponse(request, response, ApiWebCode.HEADER_ERROR);
+//            return false;
+//        }
 
         //因为缓存了用户id和设备id
-        DeviceUserVo du = (DeviceUserVo) redisService.get(String.format(LOGIN_TOKEN_FORMAT_KEY, uid));
+        DeviceUserVo du = userService.load(uid);
         if (null == du) {
-            logger.error("check header failed, not exists!");
-            request.setAttribute(ERROR_LOGIN_HEADER, "ERROR_TOKEN");
-            responseOutWithJson(request, response);
+            writeResponse(request, response, ApiWebCode.NOT_EXISTS);
             return false;
         }
 
-        logger.info("token check success: {}", JSON.toJSONString(du));
+        request.setAttribute("RANKIN_DEVICE_USER_INFO", du);
+        if (true) {
+            return true;
+        }
+
+        if (du.getExpireAt().before(new Date())) {
+            writeResponse(request, response, ApiWebCode.TOKEN_EXPIRE);
+            return false;
+        }
 
+        String token = du.getToken();
+        if (!SecurityManager.validate(headers, path, token)) {
+            writeResponse(request, response, ApiWebCode.INVALID_TOKEN);
+            return false;
+        }
+
+        logger.info("token check success: {}", JSON.toJSONString(du));
         return true;
     }
 
@@ -104,26 +122,8 @@ public class LoginInterceptor implements HandlerInterceptor {
         }
     }
 
-    protected void responseOutWithJson(HttpServletRequest request, HttpServletResponse response) {
-        BaseCode code = ApiWebCode.SERVER_ERROR;
-        logger.error("请求参数详情 {}", JSON.toJSONString(request.getParameterMap()));
-        if (!StringUtils.isEmpty(request.getAttribute(ERROR_LOGIN_HEADER))) {
-            String sign = (String) request.getAttribute(ERROR_LOGIN_HEADER);
-            switch (sign) {
-                case "ERROR_REQUEST":
-                    code = ApiWebCode.UNAUTHORIZED;
-                    break;
-                case "ERROR_TOKEN":
-                    code = ApiWebCode.INVALID_TOKEN;
-                    break;
-                case "OPTIONS":
-                    code = ApiWebCode.OK;
-                    break;
-                case "NOT_MATCH":
-                    code = ApiWebCode.DEVICE_BOUND_ERROR;
-            }
-        }
-        HttpUtil.responseOutWithJson(request, response, code);
+    public void writeResponse(HttpServletRequest request, HttpServletResponse response, Object object) {
+        HttpUtil.responseOutWithJson(request, response, object);
     }
 
     public void setIgnoreOptions(boolean ignoreOptions) {

+ 0 - 11
rankin-api-web/src/main/java/cn/rankin/apiweb/service/ProductService.java

@@ -1,11 +0,0 @@
-package cn.rankin.apiweb.service;
-
-import org.springframework.cloud.netflix.feign.FeignClient;
-import org.springframework.web.bind.annotation.RequestMapping;
-
-@FeignClient(name = "${service.product.name}")
-public interface ProductService {
-
-    @RequestMapping(value = "/hello")
-    String hello();
-}

+ 16 - 0
rankin-api-web/src/main/java/cn/rankin/apiweb/service/auth/AuthClient.java

@@ -0,0 +1,16 @@
+package cn.rankin.apiweb.service.auth;
+
+import cn.rankin.apiweb.entity.AuthResult;
+
+public interface AuthClient {
+
+    // 鉴权
+    AuthResult auth(String userId, String itemId);
+
+    // 增加时长
+    AuthResult add(String userId, String itemId, Integer day);
+
+    // 缩减时长
+    AuthResult reduce(String userId, String itemId, Integer day);
+
+}

+ 21 - 7
rankin-api-web/src/main/java/cn/rankin/apiweb/service/auth/AuthService.java

@@ -1,16 +1,30 @@
 package cn.rankin.apiweb.service.auth;
 
 import cn.rankin.apiweb.entity.AuthResult;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
 
-public interface AuthService {
+@Service
+public class AuthService implements AuthClient {
 
-    // 鉴权
-    AuthResult auth(String userId, String itemId);
+    private final static org.slf4j.Logger log = LoggerFactory.getLogger(AuthService.class);
 
-    // 增加时长
-    AuthResult add(String userId, String itemId, Integer day);
+    @Override
+    public AuthResult auth(String userId, String itemId) {
+        log.info("auth vo: {}, item: {}", userId, itemId);
+        AuthResult auth = new AuthResult(itemId, userId, 0, 0L, null);
+        return auth;
+    }
 
-    // 缩减时长
-    AuthResult reduce(String userId, String itemId, Integer day);
+    @Override
+    public AuthResult add(String userId, String itemId, Integer day) {
+        log.info("add vo: {} item: {} day: {}", userId, itemId, day);
+        return new AuthResult(itemId, userId, 0, 0L, null);
+    }
 
+    @Override
+    public AuthResult reduce(String userId, String itemId, Integer day) {
+        log.info("reduce vo: {} item: {} day: {}", userId, itemId, day);
+        return new AuthResult(itemId, userId, 0, 0L, null);
+    }
 }

+ 0 - 30
rankin-api-web/src/main/java/cn/rankin/apiweb/service/auth/SimpleAuthService.java

@@ -1,30 +0,0 @@
-package cn.rankin.apiweb.service.auth;
-
-import cn.rankin.apiweb.entity.AuthResult;
-import org.slf4j.LoggerFactory;
-import org.springframework.stereotype.Service;
-
-@Service
-public class SimpleAuthService implements AuthService {
-
-    private final static org.slf4j.Logger log = LoggerFactory.getLogger(SimpleAuthService.class);
-
-    @Override
-    public AuthResult auth(String userId, String itemId) {
-        log.info("auth vo: {}, item: {}", userId, itemId);
-        AuthResult auth = new AuthResult(itemId, userId, 0, 0L, null);
-        return auth;
-    }
-
-    @Override
-    public AuthResult add(String userId, String itemId, Integer day) {
-        log.info("add vo: {} item: {} day: {}", userId, itemId, day);
-        return new AuthResult(itemId, userId, 0, 0L, null);
-    }
-
-    @Override
-    public AuthResult reduce(String userId, String itemId, Integer day) {
-        log.info("reduce vo: {} item: {} day: {}", userId, itemId, day);
-        return new AuthResult(itemId, userId, 0, 0L, null);
-    }
-}

+ 17 - 0
rankin-api-web/src/main/java/cn/rankin/apiweb/service/product/ProductClient.java

@@ -0,0 +1,17 @@
+package cn.rankin.apiweb.service.product;
+
+import cn.rankin.common.utils.api.model.APIResult;
+import cn.rankin.data.api.product.entity.Recommend;
+import org.springframework.cloud.netflix.feign.FeignClient;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+
+import java.util.List;
+
+@FeignClient(name = "${service.product.name}")
+public interface ProductClient {
+
+    @RequestMapping(value = "/recommend/courses", method = RequestMethod.GET)
+    APIResult<List<Recommend>> getRecommendCourses(@RequestParam("merchantId") String merchantId);
+}

+ 35 - 0
rankin-api-web/src/main/java/cn/rankin/apiweb/service/product/ProductService.java

@@ -0,0 +1,35 @@
+package cn.rankin.apiweb.service.product;
+
+import cn.rankin.common.utils.api.model.APIResult;
+import cn.rankin.common.utils.api.model.BaseCode;
+import cn.rankin.data.api.app.vo.RecommendVo;
+import cn.rankin.data.api.product.entity.Recommend;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static cn.rankin.apiweb.utils.DTOConverter.convert;
+
+@Service
+public class ProductService {
+
+    @Autowired
+    private ProductClient productClient;
+
+    public APIResult<List<RecommendVo>> getRecommendCourses(String merchantId) {
+        APIResult<List<Recommend>> apiResult = productClient.getRecommendCourses(merchantId);
+        if (!apiResult.getSuccess()) {
+            return APIResult.error(new BaseCode(apiResult.getCode(), apiResult.getMessage()));
+        }
+
+        List<Recommend> recommendList = apiResult.getData();
+        List<RecommendVo> recommendVoList = new ArrayList<>();
+        for (Recommend recommend : recommendList) {
+            RecommendVo recommendVo = convert(recommend);
+            recommendVoList.add(recommendVo);
+        }
+        return APIResult.ok(recommendVoList);
+    }
+}

+ 60 - 1
rankin-api-web/src/main/java/cn/rankin/apiweb/service/user/UserClient.java

@@ -1,7 +1,66 @@
 package cn.rankin.apiweb.service.user;
 
+import cn.rankin.apiweb.code.ApiWebCode;
+import cn.rankin.common.utils.api.model.APIResult;
+import cn.rankin.data.api.user.dto.TerminalDeviceDTO;
+import cn.rankin.data.api.user.vo.TerminalDeviceVo;
+import cn.rankin.data.api.user.vo.TerminalUserVo;
 import org.springframework.cloud.netflix.feign.FeignClient;
+import org.springframework.stereotype.Component;
+import org.springframework.web.bind.annotation.*;
 
-@FeignClient
+@FeignClient(name = "${service.user.name}", fallback = UserClient.UserServiceHystrix.class)
 public interface UserClient {
+
+    @RequestMapping(value = "/user/code/{code}", method = RequestMethod.GET)
+    APIResult<TerminalUserVo> loadUserByEid(@PathVariable("code") String code);
+
+    @RequestMapping(value = "/user/{id}", method = RequestMethod.GET)
+    APIResult<TerminalUserVo> findUserById(@PathVariable("id") String id);
+
+    @RequestMapping(value = "/device/bind", method = RequestMethod.POST)
+    APIResult<TerminalDeviceVo> deviceBind(@RequestBody TerminalDeviceDTO terminalDeviceDTO);
+
+    @RequestMapping(value = "/device/unbind", method = RequestMethod.DELETE)
+    APIResult<Boolean> deviceUnbind(@RequestParam("userId") String userId);
+
+    @RequestMapping(value = "/device/uid/{uid}", method = RequestMethod.GET)
+    APIResult<TerminalDeviceVo> findDeviceByUid(@PathVariable("userId") String uid);
+
+    @RequestMapping(value = "/device/deviceCode/{deviceCode}", method = RequestMethod.GET)
+    APIResult<TerminalDeviceVo> findByDeviceCode(@PathVariable("deviceCode") String deviceCode);
+
+    @Component
+    class UserServiceHystrix implements UserClient {
+
+        @Override
+        public APIResult loadUserByEid(String code) {
+            return APIResult.error(ApiWebCode.SERVER_ERROR);
+        }
+
+        @Override
+        public APIResult deviceBind(TerminalDeviceDTO terminalDeviceDTO) {
+            return APIResult.error(ApiWebCode.SERVER_ERROR);
+        }
+
+        @Override
+        public APIResult deviceUnbind(String userId) {
+            return APIResult.error(ApiWebCode.SERVER_ERROR);
+        }
+
+        @Override
+        public APIResult findDeviceByUid(String uid) {
+            return APIResult.error(ApiWebCode.SERVER_ERROR);
+        }
+
+        @Override
+        public APIResult findUserById(String id) {
+            return APIResult.error(ApiWebCode.SERVER_ERROR);
+        }
+
+        @Override
+        public APIResult findByDeviceCode(String deviceCode) {
+            return APIResult.error(ApiWebCode.SERVER_ERROR);
+        }
+    }
 }

+ 207 - 0
rankin-api-web/src/main/java/cn/rankin/apiweb/service/user/UserService.java

@@ -1,7 +1,214 @@
 package cn.rankin.apiweb.service.user;
 
+import cn.rankin.apiweb.code.ApiWebCode;
+import cn.rankin.apiweb.utils.SecurityManager;
+import cn.rankin.common.utils.api.model.APIResult;
+import cn.rankin.common.utils.constant.RedisKey;
+import cn.rankin.common.utils.service.RedisService;
+import cn.rankin.data.api.app.dto.LoginInfoDTO;
+import cn.rankin.data.api.app.vo.DeviceUserVo;
+import cn.rankin.data.api.app.vo.UserInfoVo;
+import cn.rankin.data.api.user.dto.TerminalDeviceDTO;
+import cn.rankin.data.api.user.vo.TerminalDeviceVo;
+import cn.rankin.data.api.user.vo.TerminalUserVo;
+import com.alibaba.fastjson.JSON;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.time.DateUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 
+import java.util.Date;
+
+
 @Service
+@Slf4j
 public class UserService {
+
+    public final static String USER_FORMAT_KEY = RedisKey.USER_FORMAT_KEY;
+
+    @Value(value = "${token.expiration:604800}")
+    private int expiration;
+
+    @Autowired
+    private UserClient userClient;
+
+    @Autowired
+    private RedisService redisService;
+
+    public APIResult<UserInfoVo> login(LoginInfoDTO loginInfoDTO) {
+        String deviceCode = loginInfoDTO.getDeviceCode();
+        String eid = loginInfoDTO.getEid();
+        String password = loginInfoDTO.getPassword();
+        log.info("user login start, user={}", JSON.toJSONString(loginInfoDTO));
+
+        APIResult<TerminalUserVo> userVoAPIResult = userClient.loadUserByEid(eid);
+        TerminalUserVo userVo = userVoAPIResult.getData();
+        if (!userVoAPIResult.getSuccess()) {
+            log.error("load user api error");
+            return APIResult.error(ApiWebCode.LOGIN_ERROR);
+        }
+
+        String key = userVo.getPassword();
+        if (!SecurityManager.validate(password, key)) {
+            log.error("密码校验错误, password={}, key={}", password, key);
+            return APIResult.error(ApiWebCode.PASSWORD_ERROR);
+        }
+
+        String userId = userVo.getId();
+        String ip = loginInfoDTO.getIp();
+        TerminalDeviceVo deviceVo = this.bind(userId, deviceCode, ip);
+        if (deviceVo == null) {
+            return APIResult.error(ApiWebCode.DEVICE_BOUND_ERROR);
+        }
+
+        // assembly device and use info
+        DeviceUserVo deviceUserVo = toDeviceUserVo(userVo, deviceVo);
+        // 保存至缓存
+        this.save(deviceUserVo);
+
+        UserInfoVo userInfoVo = toUserInfoVo(deviceUserVo);
+        log.info("user={} login success, userInfo={}", eid, JSON.toJSONString(userInfoVo));
+
+        return APIResult.ok(userInfoVo);
+    }
+
+    public APIResult<Boolean> logout(String userId) {
+        APIResult<Boolean> unbindResult = userClient.deviceUnbind(userId);
+        if (!unbindResult.getSuccess()) {
+            return APIResult.error(ApiWebCode.LOGOUT_ERROR);
+        }
+
+        String key = getUserFormatKey(userId);
+        redisService.delete(key);
+
+        return APIResult.ok();
+    }
+
+    public TerminalDeviceVo bind(String userId, String deviceCode, String ip) {
+        TerminalDeviceDTO deviceDTO = new TerminalDeviceDTO();
+        deviceDTO.setUserId(userId);
+        deviceDTO.setDeviceCode(deviceCode);
+        deviceDTO.setIp(ip);
+        APIResult<TerminalDeviceVo> apiResult = userClient.deviceBind(deviceDTO);
+        if (!apiResult.getSuccess()) {
+            log.error("bind user api error");
+            return null;
+        }
+        return apiResult.getData();
+    }
+
+    // load user and device info
+    public DeviceUserVo load(String uid) {
+        String key = getUserFormatKey(uid);
+        DeviceUserVo deviceUserVo = (DeviceUserVo) redisService.get(key);
+        if (deviceUserVo == null) {
+            deviceUserVo = getDeviceUserVo(uid);
+            this.save(deviceUserVo);
+        }
+        return deviceUserVo;
+    }
+
+    public DeviceUserVo getDeviceUserVo(String uid) {
+        APIResult<TerminalDeviceVo> deviceAPIResult = userClient.findDeviceByUid(uid);
+        if (!deviceAPIResult.getSuccess()) {
+            log.error("load device api error, {}", deviceAPIResult.getMessage());
+            return null;
+        }
+        TerminalDeviceVo deviceVo = deviceAPIResult.getData();
+        // load user info
+        APIResult<TerminalUserVo> userAPIResult = userClient.findUserById(uid);
+        if (!userAPIResult.getSuccess()) {
+            log.error("load user api error, {}", userAPIResult.getMessage());
+            return null;
+        }
+        TerminalUserVo userVo = userAPIResult.getData();
+
+        return toDeviceUserVo(userVo, deviceVo);
+    }
+
+    public APIResult<UserInfoVo> refresh(String deviceCode, Boolean force) {
+        APIResult<TerminalDeviceVo> deviceAPIResult = userClient.findByDeviceCode(deviceCode);
+        if (!deviceAPIResult.getSuccess()) {
+            log.error("load device api error, {}", deviceAPIResult.getMessage());
+            return APIResult.error(ApiWebCode.NOT_FOUND);
+        }
+
+        TerminalDeviceVo deviceVo = deviceAPIResult.getData();
+        if (deviceVo == null) {
+            log.error("device code not bind any eid! deviceCode={}", deviceCode);
+            return APIResult.error(ApiWebCode.NOT_EXISTS);
+        }
+
+        String uid = deviceVo.getUserId();
+
+        DeviceUserVo deviceUserVo;
+        if (force) {
+            APIResult<TerminalUserVo> userAPIResult = userClient.findUserById(uid);
+            if (!userAPIResult.getSuccess()) {
+                log.error("load user api error, {}", userAPIResult.getMessage());
+                return APIResult.error(ApiWebCode.NOT_EXISTS);
+            }
+            TerminalUserVo userVo = userAPIResult.getData();
+            deviceUserVo = toDeviceUserVo(userVo, deviceVo);
+        }else {
+            String key = getUserFormatKey(uid);
+            deviceUserVo = (DeviceUserVo) redisService.get(key);
+        }
+
+        // 无论怎样都能成功刷新
+        if (deviceUserVo == null) {
+            deviceUserVo = getDeviceUserVo(uid);
+        }
+
+        refreshToken(deviceUserVo);
+        save(deviceUserVo);
+
+        UserInfoVo userInfoVo = toUserInfoVo(deviceUserVo);
+        return APIResult.ok(userInfoVo);
+    }
+
+    // cache user and device info
+    public void save(DeviceUserVo deviceUserVo) {
+        String key = getUserFormatKey(deviceUserVo.getUid());
+        redisService.set(key, deviceUserVo);
+    }
+
+    public static String getUserFormatKey(String uid) {
+        return String.format(USER_FORMAT_KEY, uid);
+    }
+
+    public static UserInfoVo toUserInfoVo(DeviceUserVo deviceUserVo) {
+        UserInfoVo userInfoVo = new UserInfoVo();
+        userInfoVo.setUid(deviceUserVo.getUid());
+        userInfoVo.setEid(deviceUserVo.getEid());
+        userInfoVo.setName(deviceUserVo.getName());
+        userInfoVo.setMerchantName(deviceUserVo.getMerchantName());
+        userInfoVo.setMerchantContactName(deviceUserVo.getMerchantContactName());
+        userInfoVo.setMerchantContactMobile(deviceUserVo.getMerchantContactMobile());
+        userInfoVo.setToken(deviceUserVo.getToken());
+        return userInfoVo;
+    }
+
+    public DeviceUserVo toDeviceUserVo(TerminalUserVo userVo, TerminalDeviceVo deviceVo) {
+        DeviceUserVo deviceUserVo = new DeviceUserVo();
+        deviceUserVo.setUid(userVo.getId());
+        deviceUserVo.setEid(userVo.getCode());
+        deviceUserVo.setPassword(userVo.getPassword());
+        deviceUserVo.setName(userVo.getName());
+        deviceUserVo.setDeviceCode(deviceVo.getCode());
+        deviceUserVo.setMerchantId(userVo.getMerchantId());
+        deviceUserVo.setMerchantName(userVo.getMerchantName());
+        deviceUserVo.setMerchantContactName(userVo.getMerchantContactName());
+        deviceUserVo.setMerchantContactMobile(userVo.getMerchantContactMobile());
+        refreshToken(deviceUserVo);
+        return deviceUserVo;
+    }
+
+    public void refreshToken(DeviceUserVo deviceUserVo) {
+        String token = SecurityManager.generateToken(deviceUserVo.getPassword());
+        Date expireAt = DateUtils.addSeconds(new Date(), this.expiration);
+        deviceUserVo.setToken(token);
+        deviceUserVo.setExpireAt(expireAt);
+    }
 }

+ 17 - 0
rankin-api-web/src/main/java/cn/rankin/apiweb/utils/DTOConverter.java

@@ -0,0 +1,17 @@
+package cn.rankin.apiweb.utils;
+
+import cn.rankin.data.api.app.vo.RecommendVo;
+import cn.rankin.data.api.product.entity.Recommend;
+
+public class DTOConverter {
+
+    public static RecommendVo convert(Recommend recommend) {
+        RecommendVo recommendVo = new RecommendVo();
+        recommendVo.setId(recommend.getPid());
+        recommend.setCode(recommend.getCode());
+        recommend.setTitle(recommend.getTitle());
+        recommend.setSubTitle(recommend.getSubTitle());
+        recommend.setBreadCrumb(recommend.getBreadCrumb());
+        return recommendVo;
+    }
+}

+ 19 - 0
rankin-api-web/src/main/java/cn/rankin/apiweb/utils/RequestHeaderManager.java

@@ -0,0 +1,19 @@
+package cn.rankin.apiweb.utils;
+
+import cn.rankin.apiweb.entity.GlobalHeader;
+import javax.servlet.http.HttpServletRequest;
+
+public class RequestHeaderManager {
+
+    public static GlobalHeader parseHeader(HttpServletRequest request) {
+        GlobalHeader globalHeader = new GlobalHeader();
+        globalHeader.setUid(request.getHeader("uid"));
+        globalHeader.setEid(request.getHeader("eid"));
+        globalHeader.setSign(request.getHeader("sign"));
+        globalHeader.setRequestId(request.getHeader("requestId"));
+        return globalHeader;
+    }
+
+
+
+}

+ 46 - 0
rankin-api-web/src/main/java/cn/rankin/apiweb/utils/SecurityManager.java

@@ -0,0 +1,46 @@
+package cn.rankin.apiweb.utils;
+
+import cn.rankin.apiweb.entity.GlobalHeader;
+import cn.rankin.common.utils.util.SecurityUtil;
+import cn.rankin.common.utils.util.UUIDUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.StringUtils;
+
+public class SecurityManager {
+
+    private static final Logger log = LoggerFactory.getLogger(SecurityManager.class);
+
+    // 校验密码是否正确
+    public static boolean validate(String password, String key) {
+        if (StringUtils.isEmpty(password)) {
+            log.error("password is empty");
+            return false;
+        }
+
+        String pwdMd5 = SecurityUtil.MD5(password);
+        if (pwdMd5.equals(key)) {
+            return true;
+        }
+
+        return false;
+    }
+
+    // 生成token
+    public static String generateToken(String str) {
+        String uuid = UUIDUtil.getUUID();
+        return SecurityUtil.MD5(uuid + str);
+    }
+
+    public static Boolean validate(GlobalHeader globalHeader, String path, String token) {
+        String sign = globalHeader.getSign();
+
+        StringBuilder sb = new StringBuilder();
+        sb.append("path=" + path + "&");
+        sb.append("uid=" + globalHeader.getUid() + "&");
+        sb.append("eid=" + globalHeader.getEid() + "&");
+        String computeSign = SecurityUtil.MD5(sb.toString(), token);
+
+        return computeSign.equals(sign);
+    }
+}

+ 1 - 1
rankin-cms-web/src/main/java/cn/rankin/cmsweb/controller/trade/MoneyController.java

@@ -22,7 +22,7 @@ public class MoneyController {
 
     @RequestMapping(value = "/charge", method = RequestMethod.POST)
     public APIResult<Boolean> charge(@NeedUser UserDetails user, @RequestBody ChargeDTO chargeDTO) {
-        if (user.getId().equals(Constant.ROOT_ID)) {
+        if (!user.getId().equals(Constant.ROOT_ID)) {
             return APIResult.error(CmsWebAPICode.ACCESS_DENIED);
         }
         return payingService.charge(chargeDTO);

+ 3 - 2
rankin-cms-web/src/main/java/cn/rankin/cmsweb/controller/trade/OrderController.java

@@ -10,6 +10,7 @@ import cn.rankin.data.api.cms.OrderDetailWebSearchDTO;
 import cn.rankin.data.api.cms.OrderSearchWebDTO;
 import cn.rankin.data.api.cms.OrderSnapshotWebSearchDTO;
 import cn.rankin.data.api.trade.dto.OrderDTO;
+import cn.rankin.data.api.trade.dto.OrderSendDTO;
 import cn.rankin.data.api.trade.entity.OrderProductSnapshot;
 import cn.rankin.data.api.trade.vo.OrderDetailVo;
 import cn.rankin.data.api.trade.vo.OrderVo;
@@ -87,9 +88,9 @@ public class OrderController {
     }
 
     @RequestMapping(value = "/send/{detailId}", method = RequestMethod.POST)
-    public APIResult<Boolean> send(@NeedUser UserDetails user, @PathVariable("detailId") String detailId) {
+    public APIResult<Boolean> send(@NeedUser UserDetails user, @PathVariable("detailId") String detailId, @RequestBody OrderSendDTO sendDTO) {
         if (user.isCp() || user.isPlatForm()) {
-            return orderService.send(detailId);
+            return orderService.send(detailId, sendDTO);
         }
         return APIResult.error(CmsWebAPICode.ACCESS_DENIED);
     }

+ 2 - 5
rankin-cms-web/src/main/java/cn/rankin/cmsweb/service/trade/order/OrderClient.java

@@ -2,10 +2,7 @@ package cn.rankin.cmsweb.service.trade.order;
 
 import cn.rankin.common.utils.api.model.APIResult;
 import cn.rankin.common.utils.api.page.Page;
-import cn.rankin.data.api.trade.dto.OrderDTO;
-import cn.rankin.data.api.trade.dto.OrderDetailSearchDTO;
-import cn.rankin.data.api.trade.dto.OrderSearchDTO;
-import cn.rankin.data.api.trade.dto.OrderSnapshotSearchDTO;
+import cn.rankin.data.api.trade.dto.*;
 import cn.rankin.data.api.trade.entity.Order;
 import cn.rankin.data.api.trade.entity.OrderDetail;
 import cn.rankin.data.api.trade.entity.OrderProductSnapshot;
@@ -44,7 +41,7 @@ public interface OrderClient {
     APIResult<OrderDetail> getDetailOrder(@PathVariable("detailId") String detailId);
 
     @RequestMapping(value = "/order/send/{detailId}", method = RequestMethod.POST)
-    APIResult<Boolean> send(@PathVariable("detailId") String detailId);
+    APIResult<Boolean> send(@PathVariable("detailId") String detailId, @RequestBody OrderSendDTO sendDTO);
 
     @RequestMapping(value = "/receive/{detailId}", method = RequestMethod.POST)
     APIResult<Boolean> receive(@PathVariable("detailId") String detailId);

+ 3 - 6
rankin-cms-web/src/main/java/cn/rankin/cmsweb/service/trade/order/OrderService.java

@@ -18,10 +18,7 @@ import cn.rankin.data.api.cms.OrderSnapshotWebSearchDTO;
 import cn.rankin.data.api.product.entity.Goods;
 import cn.rankin.data.api.product.vo.GoodsVo;
 import cn.rankin.data.api.product.vo.ProductVo;
-import cn.rankin.data.api.trade.dto.OrderDTO;
-import cn.rankin.data.api.trade.dto.OrderDetailSearchDTO;
-import cn.rankin.data.api.trade.dto.OrderGoodsDTO;
-import cn.rankin.data.api.trade.dto.OrderSearchDTO;
+import cn.rankin.data.api.trade.dto.*;
 import cn.rankin.data.api.trade.entity.OrderDetail;
 import cn.rankin.data.api.trade.entity.OrderGoods;
 import cn.rankin.data.api.trade.entity.Order;
@@ -121,8 +118,8 @@ public class OrderService {
     }
 
     // 发货
-    public APIResult<Boolean> send(String detailId) {
-        return orderClient.send(detailId);
+    public APIResult<Boolean> send(String detailId, OrderSendDTO sendDTO) {
+        return orderClient.send(detailId, sendDTO);
     }
 
     // 收货

+ 2 - 2
rankin-common-utils/src/main/java/cn/rankin/common/utils/constant/Constant.java

@@ -6,6 +6,8 @@ public class Constant {
 
     public final static String APP_CODE = "1502";
 
+    public final static String BIZ_CODE = "0129";
+
     public final static String MRegEx = "^\\d{4}$";
 
     public final static String PLAT_MERCHANT_ID = "1";
@@ -19,8 +21,6 @@ public class Constant {
         public final static int WECHAT = 2;
     }
 
-    public final static String BIZ_CODE = "0129";
-
     public final static class ProductType {
 
         public final static int VIP = 0;

+ 2 - 0
rankin-common-utils/src/main/java/cn/rankin/common/utils/constant/RedisKey.java

@@ -3,4 +3,6 @@ package cn.rankin.common.utils.constant;
 public class RedisKey {
 
     public static final String LOGIN_TOKEN_FORMAT_KEY = "api:web:token:%s";
+
+    public static final String USER_FORMAT_KEY = "api:web:user:%s";
 }

+ 4 - 0
rankin-common-utils/src/main/java/cn/rankin/common/utils/service/impl/RedisServiceImpl.java

@@ -22,6 +22,10 @@ public class RedisServiceImpl<K,V> implements RedisService<K,V> {
 	@Qualifier(value = "redisTemplate")
 	RedisTemplate<K,V> redisTemplate;
 
+	public void setRedisTemplate(RedisTemplate redisTemplate) {
+	    this.redisTemplate = redisTemplate;
+    }
+
 	/**
 	 * 获取
 	 * @param key

+ 13 - 0
rankin-data-api/src/main/java/cn/rankin/data/api/app/dto/DeviceLoginDTO.java

@@ -0,0 +1,13 @@
+package cn.rankin.data.api.app.dto;
+
+import lombok.Data;
+import lombok.ToString;
+
+import java.io.Serializable;
+
+@Data
+@ToString
+public class DeviceLoginDTO implements Serializable{
+
+    private String deviceCode;
+}

+ 19 - 0
rankin-data-api/src/main/java/cn/rankin/data/api/app/dto/LoginInfoDTO.java

@@ -0,0 +1,19 @@
+package cn.rankin.data.api.app.dto;
+
+import lombok.Data;
+import lombok.ToString;
+
+import java.io.Serializable;
+
+@Data
+@ToString
+public class LoginInfoDTO implements Serializable {
+
+    private String deviceCode;
+
+    private String eid;
+
+    private String password;
+
+    private String ip;
+}

+ 11 - 12
rankin-api-web/src/main/java/cn/rankin/apiweb/vo/DeviceUserVo.java

@@ -1,6 +1,7 @@
-package cn.rankin.apiweb.vo;
+package cn.rankin.data.api.app.vo;
 
 import cn.rankin.common.utils.enums.GenderEnum;
+import cn.rankin.common.utils.util.SecurityUtil;
 import lombok.Data;
 import lombok.ToString;
 import java.io.Serializable;
@@ -12,31 +13,29 @@ import java.util.Date;
 @Data
 @ToString
 public class DeviceUserVo implements Serializable {
-    private static final long serialVersionUID = 1L;
 
     //设备信息
-    private String deviceId;
+    private String deviceCode;
 
     //用户信息
     private String eid;
 
     private String uid;
 
-    private String avatar;
+    private String name;
 
-    private Date birthday;
+    private String password;
 
-    private Integer domain;
+    private String merchantId;
 
-    private GenderEnum gender;
+    private String merchantName;
 
-    private String merchantId;     //集团 id
+    private String merchantContactName;
 
-    private String merchantName; //集团 name
+    private String merchantContactMobile;
 
-    private String campusId;
+    private String token;
 
-    private String mobile;
+    private Date expireAt;
 
-    private String name;
 }

+ 21 - 0
rankin-data-api/src/main/java/cn/rankin/data/api/app/vo/RecommendVo.java

@@ -0,0 +1,21 @@
+package cn.rankin.data.api.app.vo;
+
+import lombok.Data;
+import lombok.ToString;
+
+import java.io.Serializable;
+
+@Data
+@ToString
+public class RecommendVo implements Serializable{
+
+    private String id;
+
+    private String code;
+
+    private String title;
+
+    private String subTitle;
+
+    private String breadCrumb;
+}

+ 25 - 0
rankin-data-api/src/main/java/cn/rankin/data/api/app/vo/UserInfoVo.java

@@ -0,0 +1,25 @@
+package cn.rankin.data.api.app.vo;
+
+import lombok.Data;
+import lombok.ToString;
+
+import java.io.Serializable;
+
+@Data
+@ToString
+public class UserInfoVo implements Serializable {
+
+    private String uid;
+
+    private String eid;
+
+    private String name;
+
+    private String token;
+
+    private String merchantName;
+
+    private String merchantContactName;
+
+    private String merchantContactMobile;
+}

+ 1 - 2
rankin-data-api/src/main/java/cn/rankin/data/api/product/dto/CourseDTO.java

@@ -3,7 +3,6 @@ package cn.rankin.data.api.product.dto;
 import cn.rankin.common.utils.enums.BaseStatusEnum;
 import lombok.Data;
 
-import javax.persistence.Column;
 import java.io.Serializable;
 import java.util.*;
 
@@ -20,7 +19,7 @@ public class CourseDTO implements Serializable {
 
     private String subTitle;
 
-    private String breadCrumbs;
+    private String breadCrumb;
 
     private String digest;
 

+ 2 - 2
rankin-data-api/src/main/java/cn/rankin/data/api/product/entity/Course.java

@@ -36,8 +36,8 @@ public class Course implements Serializable{
     @Column(name = "sub_title")
     private String subTitle;
 
-    @Column(name = "bread_crumbs")
-    private String breadCrumbs;
+    @Column(name = "breadcrumb")
+    private String breadCrumb;
 
     @Column
     private String digest;

+ 9 - 0
rankin-data-api/src/main/java/cn/rankin/data/api/product/entity/Recommend.java

@@ -55,4 +55,13 @@ public class Recommend implements Serializable {
     @Transient
     private String code;
 
+    @Transient
+    private String title;
+
+    @Transient
+    private String subTitle;
+
+    @Transient
+    private String breadCrumb;
+
 }

+ 1 - 1
rankin-data-api/src/main/java/cn/rankin/data/api/product/vo/CourseVo.java

@@ -19,7 +19,7 @@ public class CourseVo implements Serializable {
 
     private String subTitle;
 
-    private String breadCrumbs;
+    private String breadCrumb;
 
     private String digest;
 

+ 13 - 0
rankin-data-api/src/main/java/cn/rankin/data/api/trade/dto/OrderSendDTO.java

@@ -0,0 +1,13 @@
+package cn.rankin.data.api.trade.dto;
+
+import lombok.Data;
+import lombok.ToString;
+
+import java.io.Serializable;
+
+@Data
+@ToString
+public class OrderSendDTO implements Serializable{
+
+    private String trackNo;
+}

+ 28 - 0
rankin-data-api/src/main/java/cn/rankin/data/api/user/dto/TerminalDeviceDTO.java

@@ -0,0 +1,28 @@
+package cn.rankin.data.api.user.dto;
+
+import cn.rankin.common.utils.enums.BaseStatusEnum;
+import lombok.Data;
+import lombok.ToString;
+
+import javax.validation.constraints.NotNull;
+
+@Data
+@ToString
+public class TerminalDeviceDTO {
+
+    private String id;
+
+    @NotNull
+    private String deviceCode;
+
+    @NotNull
+    private String userId;
+
+    private String brand;
+
+    private String ip;
+
+    private String modelNo;
+
+    private BaseStatusEnum status = BaseStatusEnum.NORMAL;
+}

+ 5 - 6
rankin-data-api/src/main/java/cn/rankin/data/api/user/entity/TerminalDevice.java

@@ -5,6 +5,7 @@ import lombok.Data;
 import lombok.ToString;
 import org.hibernate.annotations.DynamicInsert;
 import org.hibernate.annotations.DynamicUpdate;
+import org.springframework.context.annotation.ComponentScan;
 
 import javax.persistence.*;
 import java.io.Serializable;
@@ -19,7 +20,7 @@ import java.util.Date;
 @Data
 @ToString
 @Entity
-@Table(name="u_device")
+@Table(name="u_device", uniqueConstraints = {@UniqueConstraint(columnNames = {"user_id", "device_code"})})
 @DynamicInsert
 @DynamicUpdate
 public class TerminalDevice implements Serializable {
@@ -28,7 +29,8 @@ public class TerminalDevice implements Serializable {
 	@Id
 	private String id;
 
-	private String code;
+	@Column(name = "device_code")
+	private String deviceCode;
 
 	@Column(name="user_id")
 	private String userId;
@@ -40,11 +42,8 @@ public class TerminalDevice implements Serializable {
 	@Column(name="model_no")
 	private String modelNo;
 
-	@Column(name="bind")
-	private Boolean bind;
-
 	@Enumerated(EnumType.ORDINAL)
-	private BaseStatusEnum status;
+	private BaseStatusEnum status = BaseStatusEnum.NORMAL;
 
     @Column(name = "gmt_created", updatable = false, insertable = false, columnDefinition = "timestamp NULL DEFAULT CURRENT_TIMESTAMP")
     @Temporal(TemporalType.TIMESTAMP)

+ 48 - 0
rankin-data-api/src/main/java/cn/rankin/data/api/user/entity/TerminalDeviceBindLog.java

@@ -0,0 +1,48 @@
+package cn.rankin.data.api.user.entity;
+
+import cn.rankin.common.utils.enums.BaseStatusEnum;
+import lombok.Data;
+import lombok.ToString;
+import org.hibernate.annotations.DynamicInsert;
+import org.hibernate.annotations.DynamicUpdate;
+
+import javax.persistence.*;
+import java.io.Serializable;
+import java.util.Date;
+
+@Data
+@ToString
+@Entity
+@Table(name="u_device_bind_log")
+@DynamicInsert
+@DynamicUpdate
+public class TerminalDeviceBindLog implements Serializable {
+
+    @Id
+    private String id;
+
+    @Column(name = "device_code")
+    private String deviceCode;
+
+    @Column(name="user_id")
+    private String userId;
+
+    private String brand;
+
+    private String ip;
+
+    @Column(name="model_no")
+    private String modelNo;
+
+    @Enumerated(EnumType.ORDINAL)
+    private BaseStatusEnum status;
+
+    @Column(name = "gmt_created", updatable = false, insertable = false, columnDefinition = "timestamp NULL DEFAULT CURRENT_TIMESTAMP")
+    @Temporal(TemporalType.TIMESTAMP)
+    private Date gmtCreated;
+
+    @Column(name = "gmt_modified", updatable = false, insertable = false, columnDefinition = "timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")
+    @Temporal(TemporalType.TIMESTAMP)
+    private Date gmtModified;
+
+}

+ 6 - 1
rankin-data-api/src/main/java/cn/rankin/data/api/user/entity/TerminalUser.java

@@ -30,7 +30,6 @@ public class TerminalUser implements Serializable {
     @Column(unique = true, nullable = false, updatable = false)
     private String code;
 
-    @JsonIgnore
     @Column(nullable = false)
     private String password;
 
@@ -58,6 +57,12 @@ public class TerminalUser implements Serializable {
     private String merchantName;
 
     @Transient
+    private String merchantContactName;
+
+    @Transient
+    private String merchantContactMobile;
+
+    @Transient
     private String contactName;
 
     @Transient

+ 4 - 0
rankin-data-api/src/main/java/cn/rankin/data/api/user/vo/TerminalUserVo.java

@@ -35,6 +35,10 @@ public class TerminalUserVo implements Serializable {
 
     private String merchantName;
 
+    private String merchantContactName;
+
+    private String merchantContactMobile;
+
     private String contactName;
 
     private String mobile;

+ 11 - 0
rankin-product-service/src/main/java/cn/rankin/productservice/controller/RecommendController.java

@@ -1,6 +1,7 @@
 package cn.rankin.productservice.controller;
 
 import cn.rankin.common.utils.api.model.APIResult;
+import cn.rankin.common.utils.api.page.Page;
 import cn.rankin.productservice.code.ProductServiceAPICode;
 import cn.rankin.data.api.product.entity.Recommend;
 import cn.rankin.productservice.service.RecommendService;
@@ -37,4 +38,14 @@ public class RecommendController {
         List<Recommend> recommendList = recommendService.put(merchantId, itemIdList);
         return APIResult.ok(recommendList);
     }
+
+    @RequestMapping(value = "/courses", method = RequestMethod.GET)
+    public APIResult<List<Recommend>> getRecommendCourses(@RequestParam("merchantId") String merchantId) {
+        if (StringUtils.isEmpty(merchantId)) {
+            return APIResult.error(ProductServiceAPICode.PARAMETER_ERROR);
+        }
+
+        List<Recommend> recommendList = recommendService.getRecommendCourses(merchantId);
+        return APIResult.ok(recommendList);
+    }
 }

+ 56 - 9
rankin-product-service/src/main/java/cn/rankin/productservice/service/RecommendService.java

@@ -2,9 +2,13 @@ package cn.rankin.productservice.service;
 
 import cn.rankin.common.utils.enums.BaseStatusEnum;
 import cn.rankin.common.utils.util.ListUtil;
+import cn.rankin.data.api.product.entity.Course;
 import cn.rankin.data.api.product.entity.MerchantProduct;
+import cn.rankin.data.api.product.entity.Product;
 import cn.rankin.data.api.product.entity.Recommend;
+import cn.rankin.productservice.repository.CourseRepository;
 import cn.rankin.productservice.repository.MerchantProductRepository;
+import cn.rankin.productservice.repository.ProductRepository;
 import cn.rankin.productservice.repository.RecommendRepository;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
@@ -21,6 +25,12 @@ public class RecommendService {
     private MerchantProductRepository merchantProductRepository;
 
     @Autowired
+    private ProductRepository productRepository;
+
+    @Autowired
+    private CourseRepository courseRepository;
+
+    @Autowired
     private RecommendRepository recommendRepository;
 
     public List<Recommend> get(String merchantId) {
@@ -29,27 +39,55 @@ public class RecommendService {
             return new ArrayList<>();
         }
 
-        return setRecommendInfo(recommendList);
+        return setProductInfo(recommendList);
     }
 
-    public List<Recommend> setRecommendInfo(List<Recommend> recommendList) {
+    public List<Recommend> setProductInfo(List<Recommend> recommendList) {
         if (CollectionUtils.isEmpty(recommendList)) {
             return new ArrayList<>();
         }
 
         List<String> productIdList = new ArrayList<>();
         recommendList.forEach(recommend -> productIdList.add(recommend.getPid()));
-        List<MerchantProduct> merchantProductList = merchantProductRepository.findByPidIn(productIdList);
-        Map<String, MerchantProduct> merchantProductMap = ListUtil.convert(merchantProductList, "pid", MerchantProduct.class);
+//        List<MerchantProduct> merchantProductList = merchantProductRepository.findByPidIn(productIdList);
+//        Map<String, MerchantProduct> merchantProductMap = ListUtil.convert(merchantProductList, "pid", MerchantProduct.class);
+        List<Product> productList = productRepository.findByPids(productIdList);
+        Map<String, Product> productMap = ListUtil.convert(productList, "pid", Product.class);
 
         List<Recommend> result = new ArrayList<>();
         for (Recommend recommend : recommendList) {
-            MerchantProduct merchantProduct = merchantProductMap.get(recommend.getPid());
-            if (merchantProduct == null) {
+            Product product = productMap.get(recommend.getPid());
+            if (product == null) {
                 continue;
             }
-            recommend.setName(merchantProduct.getName());
-            recommend.setCode(merchantProduct.getCode());
+            recommend.setName(product.getName());
+            recommend.setCode(product.getCode());
+            result.add(recommend);
+        }
+        return result;
+    }
+
+    public List<Recommend> setCourseInfo(List<Recommend> recommendList) {
+        if (CollectionUtils.isEmpty(recommendList)) {
+            return new ArrayList<>();
+        }
+
+        List<String> productIdList = new ArrayList<>();
+        recommendList.forEach(recommend -> productIdList.add(recommend.getPid()));
+        List<Course> courseList = courseRepository.findByIds(productIdList);
+        Map<String, Course> courseMap = ListUtil.convert(courseList, "id", Course.class);
+
+        List<Recommend> result = new ArrayList<>();
+        for (Recommend recommend : recommendList) {
+            Course course = courseMap.get(recommend.getPid());
+            if (course == null) {
+                continue;
+            }
+            recommend.setName(course.getName());
+            recommend.setCode(course.getCode());
+            recommend.setBreadCrumb(course.getBreadCrumb());
+            recommend.setTitle(course.getTitle());
+            recommend.setSubTitle(course.getSubTitle());
             result.add(recommend);
         }
         return result;
@@ -102,6 +140,15 @@ public class RecommendService {
 
         recommendRepository.save(recommendList);
         List<Recommend> result = recommendRepository.findByMerchantId(merchantId);
-        return setRecommendInfo(result);
+        return setProductInfo(result);
+    }
+
+    public List<Recommend> getRecommendCourses(String merchantId) {
+        List<Recommend> recommendList = recommendRepository.findByStatus(merchantId, BaseStatusEnum.NORMAL);
+        if (CollectionUtils.isEmpty(recommendList)) {
+            return new ArrayList<>();
+        }
+
+        return setCourseInfo(recommendList);
     }
 }

+ 3 - 6
rankin-trade-service/src/main/java/cn/rankin/tradeservice/controller/OrderController.java

@@ -2,10 +2,7 @@ package cn.rankin.tradeservice.controller;
 
 import cn.rankin.common.utils.api.model.APIResult;
 import cn.rankin.common.utils.api.page.Page;
-import cn.rankin.data.api.trade.dto.OrderDetailSearchDTO;
-import cn.rankin.data.api.trade.dto.OrderSearchDTO;
-import cn.rankin.data.api.trade.dto.OrderDTO;
-import cn.rankin.data.api.trade.dto.OrderSnapshotSearchDTO;
+import cn.rankin.data.api.trade.dto.*;
 import cn.rankin.data.api.trade.entity.Order;
 import cn.rankin.data.api.trade.entity.OrderDetail;
 import cn.rankin.data.api.trade.entity.OrderProductSnapshot;
@@ -85,8 +82,8 @@ public class OrderController {
     }
 
     @RequestMapping(value = "/send/{detailId}", method = RequestMethod.POST)
-    public APIResult<Boolean> send(@PathVariable("detailId") String detailId) {
-        return orderDetailService.send(detailId);
+    public APIResult<Boolean> send(@PathVariable("detailId") String detailId, @RequestBody OrderSendDTO sendDTO) {
+        return orderDetailService.send(detailId, sendDTO);
     }
 
     @RequestMapping(value = "/receive/{detailId}", method = RequestMethod.POST)

+ 2 - 2
rankin-trade-service/src/main/java/cn/rankin/tradeservice/repository/OrderDetailRepository.java

@@ -10,8 +10,8 @@ import java.util.List;
 public interface OrderDetailRepository extends BasicJpaRepository<OrderDetail, String> {
 
     @Modifying
-    @Query(value = "update OrderDetail o set o.orderStatus = 6 where o.orderStatus = 5 and o.status = 0 and o.id = ?1")
-    Integer send(String detailId);
+    @Query(value = "update OrderDetail o set o.orderStatus = 6, o.trackNo = ?2 where o.orderStatus = 5 and o.status = 0 and o.id = ?1")
+    Integer send(String detailId, String trackNo);
 
     @Modifying
     @Query(value = "update OrderDetail o set o.orderStatus = 7 where o.orderStatus = 6 and o.status = 0 and o.id = ?1")

+ 10 - 8
rankin-trade-service/src/main/java/cn/rankin/tradeservice/service/OrderDetailService.java

@@ -6,6 +6,7 @@ import cn.rankin.common.utils.enums.BaseOrderEnum;
 import cn.rankin.common.utils.enums.OrderStatusEnum;
 import cn.rankin.common.utils.util.JpaSortUtil;
 import cn.rankin.data.api.trade.dto.OrderDetailSearchDTO;
+import cn.rankin.data.api.trade.dto.OrderSendDTO;
 import cn.rankin.data.api.trade.entity.OrderDetail;
 import cn.rankin.data.api.trade.entity.OrderGoods;
 import cn.rankin.tradeservice.code.TradeServiceAPICode;
@@ -43,24 +44,25 @@ public class OrderDetailService {
     private OrderRepository orderRepository;
 
     @Transactional
-    public APIResult<Boolean> send(String detailId) {
+    public APIResult<Boolean> send(String detailId, OrderSendDTO sendDTO) {
+        String trackNo = sendDTO.getTrackNo();
         OrderDetail orderDetail = orderDetailRepository.find(detailId);
         if (orderDetail == null) {
             return APIResult.error(TradeServiceAPICode.NOT_EXISTS);
         }
 
-        Integer count = orderDetailRepository.send(detailId);
+        Integer count = orderDetailRepository.send(detailId, trackNo);
         if (count == 0) {
             log.error("update detail order send status error");
             return APIResult.error(TradeServiceAPICode.UPDATE_ERROR);
         }
 
-        String orderId = orderDetail.getOrderId();
-        Integer another = orderRepository.send(orderId);
-        if (another == 0) {
-            log.error("update order send status error");
-            return APIResult.error(TradeServiceAPICode.UPDATE_ERROR);
-        }
+//        String orderId = orderDetail.getOrderId();
+//        Integer another = orderRepository.send(orderId);
+//        if (another == 0) {
+//            log.error("update order send status error");
+//            return APIResult.error(TradeServiceAPICode.UPDATE_ERROR);
+//        }
 
         return APIResult.ok();
     }

+ 7 - 0
rankin-user-service/src/main/java/cn/rankin/userservice/code/UserServiceAPICode.java

@@ -2,6 +2,7 @@ package cn.rankin.userservice.code;
 
 import cn.rankin.common.utils.api.model.APICode;
 import cn.rankin.common.utils.api.model.BaseCode;
+import com.fasterxml.jackson.databind.ser.Serializers;
 
 public class UserServiceAPICode extends APICode {
 
@@ -19,4 +20,10 @@ public class UserServiceAPICode extends APICode {
 
     public final static int _C_MONEY_NOT_ENOUGH = 22002;
     public final static BaseCode MONEY_NOT_ENOUGH = new BaseCode(_C_MONEY_NOT_ENOUGH, "余额不足");
+
+    public final static int _C_DEVICE_IS_BOUND = 23001;
+    public final static BaseCode DEVICE_IS_BOUND = new BaseCode(_C_DEVICE_IS_BOUND, "设备已经绑定其他用户");
+
+    public final static int _C_USER_IS_BOUND = 23002;
+    public final static BaseCode USER_IS_BOUND = new BaseCode(_C_USER_IS_BOUND, "用户已经绑定其他设备");
 }

+ 38 - 0
rankin-user-service/src/main/java/cn/rankin/userservice/controller/TerminalDeviceController.java

@@ -0,0 +1,38 @@
+package cn.rankin.userservice.controller;
+
+import cn.rankin.common.utils.api.model.APIResult;
+import cn.rankin.data.api.user.dto.TerminalDeviceDTO;
+import cn.rankin.data.api.user.entity.TerminalDevice;
+import cn.rankin.userservice.service.TerminalDeviceService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+
+@RestController
+@RequestMapping(value = "/device")
+public class TerminalDeviceController {
+
+    @Autowired
+    private TerminalDeviceService terminalDeviceService;
+
+    @RequestMapping(value = "/bind", method = RequestMethod.POST)
+    public APIResult<TerminalDevice> bind(@Valid @RequestBody TerminalDeviceDTO terminalDeviceDTO) {
+        return terminalDeviceService.bind(terminalDeviceDTO);
+    }
+
+    @RequestMapping(value = "/unbind", method = RequestMethod.DELETE)
+    public APIResult<Boolean> unbind(@RequestParam("userId") String userId) {
+        return terminalDeviceService.unbind(userId);
+    }
+
+    @RequestMapping(value = "/uid/{userId}", method = RequestMethod.GET)
+    public APIResult<TerminalDevice> findByUserId(@PathVariable("userId") String userId) {
+        return terminalDeviceService.findByUserId(userId);
+    }
+
+    @RequestMapping(value = "/deviceCode/{deviceCode}", method = RequestMethod.GET)
+    public APIResult<TerminalDevice> findByDeviceCode(@PathVariable("deviceCode") String deviceCode) {
+        return terminalDeviceService.findByDeviceCode(deviceCode);
+    }
+}

+ 17 - 0
rankin-user-service/src/main/java/cn/rankin/userservice/controller/TerminalUserController.java

@@ -201,6 +201,17 @@ public class TerminalUserController {
         return APIResult.ok(terminalUser);
     }
 
+    @RequestMapping(value = "/code/{code}", method = RequestMethod.GET)
+    public APIResult<TerminalUser> loadUser(@PathVariable("code") String code) {
+        TerminalUser terminalUser = terminalUserService.loadUserByCode(code);
+        if (terminalUser == null) {
+            return APIResult.error(UserServiceAPICode.NOT_EXISTS);
+        }
+
+        setUserInfo(terminalUser);
+        return APIResult.ok(terminalUser);
+    }
+
     @RequestMapping(value = "/campus/ids", method = RequestMethod.GET)
     public APIResult<List<TerminalUser>> findByCampusIds(@RequestParam("id") List<String> campusIds) {
         List<TerminalUser> terminalUserList = terminalUserService.findByCampusIds(campusIds);
@@ -234,6 +245,8 @@ public class TerminalUserController {
 
         if (merchant != null) {
             terminalUser.setMerchantName(merchant.getName());
+            terminalUser.setMerchantContactMobile(merchant.getMobile());
+            terminalUser.setMerchantContactName(merchant.getContactName());
         }
     }
 
@@ -252,6 +265,8 @@ public class TerminalUserController {
         Merchant merchant = merchantService.findOne(campus.getMerchantId());
         if (merchant != null) {
             terminalUser.setMerchantName(merchant.getName());
+            terminalUser.setMerchantContactMobile(merchant.getMobile());
+            terminalUser.setMerchantContactName(merchant.getContactName());
         }
     }
 
@@ -283,6 +298,8 @@ public class TerminalUserController {
             Merchant merchant = merchantMap.get(terminalUser.getMerchantId());
             if (merchant != null) {
                 terminalUser.setMerchantName(merchant.getName());
+                terminalUser.setMerchantContactMobile(merchant.getMobile());
+                terminalUser.setMerchantContactName(merchant.getContactName());
             }
         }
     }

+ 7 - 0
rankin-user-service/src/main/java/cn/rankin/userservice/repository/TerminalDeviceBindLogRepository.java

@@ -0,0 +1,7 @@
+package cn.rankin.userservice.repository;
+
+import cn.rankin.common.utils.jpa.BasicJpaRepository;
+import cn.rankin.data.api.user.entity.TerminalDeviceBindLog;
+
+public interface TerminalDeviceBindLogRepository extends BasicJpaRepository<TerminalDeviceBindLog, String> {
+}

+ 6 - 10
rankin-user-service/src/main/java/cn/rankin/userservice/repository/TerminalDeviceRepository.java

@@ -1,25 +1,21 @@
 package cn.rankin.userservice.repository;
 
-import cn.rankin.common.utils.enums.BaseStatusEnum;
 import cn.rankin.common.utils.jpa.BasicJpaRepository;
 import cn.rankin.data.api.user.entity.TerminalDevice;
 import org.springframework.data.jpa.repository.Modifying;
-import org.springframework.data.jpa.repository.Query;
-import org.springframework.data.repository.query.Param;
 import org.springframework.stereotype.Repository;
 
-import java.util.List;
-
 
 @Repository
 public interface TerminalDeviceRepository extends BasicJpaRepository<TerminalDevice, String> {
 
-    @Modifying
-    @Query("update TerminalDevice a set a.status = :status where a.id in (:ids)")
-    int updateStatusByIds(@Param("ids") List<Long> ids, @Param("status") BaseStatusEnum status);
+    TerminalDevice findByDeviceCode(String code);
+
+    TerminalDevice findByUserId(String userId);
+
+    TerminalDevice findByDeviceCodeOrUserId(String code, String userId);
 
     @Modifying
-    @Query(value = "update TerminalDevice t set t.bind = 0 where t.userId = :userId")
-    int unbind(@Param("userId") String userId);
+    Integer deleteByUserId(String userId);
 
 }

+ 2 - 0
rankin-user-service/src/main/java/cn/rankin/userservice/repository/TerminalUserRepository.java

@@ -16,6 +16,8 @@ public interface TerminalUserRepository extends BasicJpaRepository<TerminalUser,
 
     List<TerminalUser> findByCampusIdIn(List<String> campusIds);
 
+    TerminalUser findByCode(String code);
+
     @Modifying
     @Query(value = "update TerminalUser t set t.status = :status where t.id = :id")
     Integer updateStatusById(@Param("status") BaseStatusEnum status, @Param("id") String id);

+ 26 - 0
rankin-user-service/src/main/java/cn/rankin/userservice/service/TerminalDeviceBindLogService.java

@@ -0,0 +1,26 @@
+package cn.rankin.userservice.service;
+
+import cn.rankin.data.api.user.entity.TerminalDeviceBindLog;
+import cn.rankin.userservice.repository.TerminalDeviceBindLogRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import javax.transaction.Transactional;
+
+@Service
+public class TerminalDeviceBindLogService {
+
+    @Autowired
+    private TerminalDeviceBindLogRepository terminalDeviceBindLogRepository;
+
+    @Transactional
+    public TerminalDeviceBindLog insert(String deviceCode, String userId, String brand, String ip, String modelNo) {
+        TerminalDeviceBindLog deviceBindLog = new TerminalDeviceBindLog();
+        deviceBindLog.setDeviceCode(deviceCode);
+        deviceBindLog.setUserId(userId);
+        deviceBindLog.setBrand(brand);
+        deviceBindLog.setId(ip);
+        deviceBindLog.setModelNo(modelNo);
+        return terminalDeviceBindLogRepository.save(deviceBindLog);
+    }
+}

+ 64 - 0
rankin-user-service/src/main/java/cn/rankin/userservice/service/TerminalDeviceService.java

@@ -1,13 +1,77 @@
 package cn.rankin.userservice.service;
 
+import cn.rankin.common.utils.api.model.APIResult;
+import cn.rankin.data.api.user.dto.TerminalDeviceDTO;
+import cn.rankin.data.api.user.entity.TerminalDevice;
+import cn.rankin.userservice.code.UserServiceAPICode;
 import cn.rankin.userservice.repository.TerminalDeviceRepository;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import javax.transaction.Transactional;
+
 @Service
 public class TerminalDeviceService {
 
     @Autowired
+    private TerminalDeviceBindLogService terminalDeviceBindLogService;
+
+    @Autowired
     private TerminalDeviceRepository terminalDeviceRepository;
 
+    @Transactional
+    public APIResult<TerminalDevice> bind(TerminalDeviceDTO terminalDeviceDTO) {
+        String deviceCode = terminalDeviceDTO.getDeviceCode();
+        String userId = terminalDeviceDTO.getUserId();
+        String brand = terminalDeviceDTO.getBrand();
+        String ip = terminalDeviceDTO.getIp();
+        String modelNo = terminalDeviceDTO.getModelNo();
+
+        TerminalDevice terminalDevice = terminalDeviceRepository.findByDeviceCodeOrUserId(deviceCode, userId);
+        if (terminalDevice != null) {
+            if (userId.equals(terminalDevice.getUserId())) {
+                return APIResult.error(UserServiceAPICode.USER_IS_BOUND);
+            }else {
+                return APIResult.error(UserServiceAPICode.DEVICE_IS_BOUND);
+            }
+        }
+
+        terminalDevice = new TerminalDevice();
+        terminalDevice.setUserId(userId);
+        terminalDevice.setDeviceCode(deviceCode);
+        terminalDevice.setBrand(brand);
+        terminalDevice.setModelNo(modelNo);
+        terminalDevice.setIp(ip);
+
+        TerminalDevice deviceBind = terminalDeviceRepository.save(terminalDevice);
+        terminalDeviceBindLogService.insert(deviceCode, userId, brand, ip, modelNo);
+
+        return APIResult.ok(deviceBind);
+    }
+
+    @Transactional
+    public APIResult<Boolean> unbind(String userId) {
+        TerminalDevice terminalDevice = terminalDeviceRepository.findByUserId(userId);
+        if (terminalDevice == null) {
+            return APIResult.error(UserServiceAPICode.NOT_EXISTS);
+        }
+        terminalDeviceRepository.deleteByUserId(userId);
+        return APIResult.ok(Boolean.TRUE);
+    }
+
+    public APIResult<TerminalDevice> findByUserId(String userId) {
+        TerminalDevice terminalDevice = terminalDeviceRepository.findByUserId(userId);
+        if (terminalDevice == null) {
+            return APIResult.error(UserServiceAPICode.NOT_EXISTS);
+        }
+        return APIResult.ok(terminalDevice);
+    }
+
+    public APIResult<TerminalDevice> findByDeviceCode(String deviceCode) {
+        TerminalDevice terminalDevice = terminalDeviceRepository.findByDeviceCode(deviceCode);
+        if (terminalDevice == null) {
+            return APIResult.error(UserServiceAPICode.NOT_EXISTS);
+        }
+        return APIResult.ok(terminalDevice);
+    }
 }

+ 4 - 0
rankin-user-service/src/main/java/cn/rankin/userservice/service/TerminalUserService.java

@@ -40,6 +40,10 @@ public class TerminalUserService {
         return terminalUserRepository.find(id);
     }
 
+    public TerminalUser loadUserByCode(String code) {
+        return terminalUserRepository.findByCode(code);
+    }
+
     public List<TerminalUser> find(TerminalUser terminalUser, LinkedHashMap<String, BaseOrderEnum> sort) {
         List<TerminalUser> terminalUserList = terminalUserRepository.find(terminalUser, JpaSortUtil.sort(sort));
         return terminalUserList;