huodongdong vor 7 Jahren
Ursprung
Commit
f15b6dbe00
38 geänderte Dateien mit 1115 neuen und 163 gelöschten Zeilen
  1. 11 0
      rankin-api-web/pom.xml
  2. 8 1
      rankin-api-web/src/main/java/cn/rankin/apiweb/ApiWebApplication.java
  3. 10 0
      rankin-api-web/src/main/java/cn/rankin/apiweb/code/ApiWebCode.java
  4. 75 0
      rankin-api-web/src/main/java/cn/rankin/apiweb/configuration/FrontConfiguration.java
  5. 11 2
      rankin-api-web/src/main/java/cn/rankin/apiweb/controller/TestFeignClient.java
  6. 141 0
      rankin-api-web/src/main/java/cn/rankin/apiweb/intercepter/LoginInterceptor.java
  7. 23 0
      rankin-api-web/src/main/java/cn/rankin/apiweb/security/JwsToken.java
  8. 55 0
      rankin-api-web/src/main/java/cn/rankin/apiweb/security/JwsTokenService.java
  9. 42 0
      rankin-api-web/src/main/java/cn/rankin/apiweb/vo/DeviceUserVo.java
  10. 34 2
      rankin-cms-web/src/main/java/cn/rankin/cmsweb/controller/user/MerchantController.java
  11. 21 0
      rankin-cms-web/src/main/java/cn/rankin/cmsweb/service/product/RecommendService.java
  12. 20 1
      rankin-common-utils/pom.xml
  13. 9 0
      rankin-common-utils/src/main/java/cn/rankin/common/utils/api/model/APICode.java
  14. 113 0
      rankin-common-utils/src/main/java/cn/rankin/common/utils/configuration/RedisConfiguration.java
  15. 6 0
      rankin-common-utils/src/main/java/cn/rankin/common/utils/constant/RedisKey.java
  16. 3 2
      rankin-common-utils/src/main/java/cn/rankin/common/utils/dto/user/MerchantDTO.java
  17. 1 1
      rankin-common-utils/src/main/java/cn/rankin/common/utils/enums/ItemTypeEnum.java
  18. 113 0
      rankin-common-utils/src/main/java/cn/rankin/common/utils/service/RedisService.java
  19. 180 0
      rankin-common-utils/src/main/java/cn/rankin/common/utils/service/impl/RedisServiceImpl.java
  20. 30 0
      rankin-common-utils/src/main/java/cn/rankin/common/utils/vo/product/RecommendVo.java
  21. 3 0
      rankin-common-utils/src/main/java/cn/rankin/common/utils/vo/user/MerchantVo.java
  22. 33 0
      rankin-common-utils/src/main/java/cn/rankin/common/utils/vo/user/TerminalDeviceVo.java
  23. 6 0
      rankin-product-service/src/main/java/cn/rankin/productservice/code/ProductServiceAPICode.java
  24. 1 3
      rankin-product-service/src/main/java/cn/rankin/productservice/controller/ItemController.java
  25. 33 2
      rankin-product-service/src/main/java/cn/rankin/productservice/controller/RecommendController.java
  26. 0 1
      rankin-product-service/src/main/java/cn/rankin/productservice/controller/TagController.java
  27. 0 74
      rankin-product-service/src/main/java/cn/rankin/productservice/entity/Item.java
  28. 0 65
      rankin-product-service/src/main/java/cn/rankin/productservice/entity/ItemPrice.java
  29. 10 4
      rankin-product-service/src/main/java/cn/rankin/productservice/entity/Recommend.java
  30. 2 0
      rankin-product-service/src/main/java/cn/rankin/productservice/proxy/ItemServiceProxy.java
  31. 0 1
      rankin-product-service/src/main/java/cn/rankin/productservice/repository/ItemPriceRepository.java
  32. 0 1
      rankin-product-service/src/main/java/cn/rankin/productservice/repository/ItemRepository.java
  33. 10 0
      rankin-product-service/src/main/java/cn/rankin/productservice/repository/RecommendRepository.java
  34. 0 1
      rankin-product-service/src/main/java/cn/rankin/productservice/service/ItemPriceService.java
  35. 0 1
      rankin-product-service/src/main/java/cn/rankin/productservice/service/ItemService.java
  36. 18 0
      rankin-product-service/src/main/java/cn/rankin/productservice/service/PackageItemRelationService.java
  37. 92 0
      rankin-product-service/src/main/java/cn/rankin/productservice/service/RecommendService.java
  38. 1 1
      rankin-user-service/src/main/java/cn/rankin/userservice/entity/TerminalDevice.java

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

@@ -42,12 +42,23 @@
 			<groupId>org.springframework.boot</groupId>
 			<artifactId>spring-boot-starter-web</artifactId>
 		</dependency>
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt</artifactId>
+            <version>0.7.0</version>
+        </dependency>
 
 		<dependency>
 			<groupId>org.springframework.boot</groupId>
 			<artifactId>spring-boot-starter-test</artifactId>
 			<scope>test</scope>
 		</dependency>
+
+        <dependency>
+            <groupId>cn.rankin</groupId>
+            <artifactId>rankin-common-utils</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+        </dependency>
 	</dependencies>
 
 	<dependencyManagement>

+ 8 - 1
rankin-api-web/src/main/java/cn/rankin/apiweb/ApiWebApplication.java

@@ -1,13 +1,20 @@
 package cn.rankin.apiweb;
 
+import cn.rankin.apiweb.configuration.FrontConfiguration;
+import cn.rankin.common.utils.configuration.RedisConfiguration;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
+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;
+import org.springframework.context.annotation.Import;
 
 @EnableFeignClients
 @EnableDiscoveryClient
-@SpringBootApplication
+@ComponentScan(basePackages = {"cn.rankin.apiweb", "cn.rankin.common.utils"})
+@SpringBootApplication(exclude={DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class})
 public class ApiWebApplication {
 
 	public static void main(String[] args) {

+ 10 - 0
rankin-api-web/src/main/java/cn/rankin/apiweb/code/ApiWebCode.java

@@ -0,0 +1,10 @@
+package cn.rankin.apiweb.code;
+
+import cn.rankin.common.utils.api.model.APICode;
+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, "终端与账号不匹配");
+}

+ 75 - 0
rankin-api-web/src/main/java/cn/rankin/apiweb/configuration/FrontConfiguration.java

@@ -0,0 +1,75 @@
+package cn.rankin.apiweb.configuration;
+
+import cn.rankin.apiweb.intercepter.LoginInterceptor;
+import cn.rankin.common.utils.configuration.RedisConfiguration;
+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;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
+
+import java.util.List;
+
+/**
+ * 类名:WebConfiguration  <br />
+ *
+ * 功能:Web相关配置
+ *
+ * @author tomas <br />
+ * 创建时间:2016年7月27日 下午3:57:19  <br />
+ * @version 2016年7月27日*/
+@Configuration
+@EnableWebMvc
+public class FrontConfiguration extends WebMvcConfigurerAdapter implements EnvironmentAware {
+	// 日志记录器
+	private static final Logger logger = LoggerFactory.getLogger(FrontConfiguration.class);
+
+	@Autowired
+	private LoginInterceptor loginInterceptor;
+
+	@Autowired
+	private HeaderProcessIntercepter headerProcessIntercepter;
+
+	// 当前的环境对象
+	protected Environment environment;
+
+
+	public void addInterceptors(InterceptorRegistry registry) {
+		registry.addInterceptor(headerProcessIntercepter);
+		registry.addInterceptor(loginInterceptor).excludePathPatterns("*/favicon.ico");
+	}
+
+	public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
+//		argumentResolvers.add(needUserResolver);
+		super.addArgumentResolvers(argumentResolvers);
+	}
+
+	public void addResourceHandlers(ResourceHandlerRegistry registry) {
+		registry.addResourceHandler("swagger-ui.html")
+				.addResourceLocations("classpath:/META-INF/resources/");
+		registry.addResourceHandler("favicon.ico")
+				.addResourceLocations("classpath:/static/favicon.ico");
+		registry.addResourceHandler("/webjars/**")
+				.addResourceLocations("classpath:/META-INF/resources/webjars/");
+	}
+	/**
+	 * Set the {@code Environment} that this object runs in.
+	 *
+	 * @param environment
+	 */
+	@Override
+	public void setEnvironment(Environment environment) {
+		this.environment = environment;
+	}
+}

+ 11 - 2
rankin-api-web/src/main/java/cn/rankin/apiweb/controller/TestFeignClient.java

@@ -1,6 +1,7 @@
 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.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestMethod;
@@ -12,9 +13,17 @@ public class TestFeignClient {
     @Autowired
     ProductService productClient;
 
-    @RequestMapping(value = "/test", method = RequestMethod.GET)
+    @Autowired
+    private RedisService redisService;
+
+    @RequestMapping(value = "/hello", method = RequestMethod.GET)
     public String test() {
-        return productClient.hello();
+        return "hello";
+    }
 
+    @RequestMapping(value = "/redis", method = RequestMethod.GET)
+    public Object redis() {
+        redisService.set("test", 1);
+        return redisService.get("test");
     }
 }

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

@@ -0,0 +1,141 @@
+package cn.rankin.apiweb.intercepter;
+
+import cn.rankin.apiweb.code.ApiWebCode;
+import cn.rankin.apiweb.security.JwsToken;
+import cn.rankin.apiweb.security.JwsTokenService;
+import cn.rankin.apiweb.vo.DeviceUserVo;
+import cn.rankin.common.utils.api.model.BaseCode;
+import cn.rankin.common.utils.constant.RedisKey;
+import cn.rankin.common.utils.service.RedisService;
+import cn.rankin.common.utils.util.HttpUtil;
+import com.alibaba.fastjson.JSON;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.NamedThreadLocal;
+import org.springframework.http.HttpMethod;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+import org.springframework.web.servlet.HandlerInterceptor;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+@Component
+public class LoginInterceptor implements HandlerInterceptor {
+
+    private static final Logger logger = LoggerFactory.getLogger(LoginInterceptor.class);
+
+    private static final String LOGIN_TOKEN_FORMAT_KEY = RedisKey.LOGIN_TOKEN_FORMAT_KEY;
+
+    private static final String ERROR_LOGIN_HEADER = "NO_LOGIN_SIGN";
+
+    private NamedThreadLocal<Long> startTimeThreadLocal = new NamedThreadLocal<Long>("StopWatch-StartTime");
+
+    private static final long REFRESH_INTERVAL = 2 * 60 * 60 * 1000;
+
+    @Autowired
+    RedisService<String, Object> redisService;
+
+    @Autowired
+    private JwsTokenService jwsTokenService;
+
+    // 忽略options请求,默认为true
+    private boolean ignoreOptions = true;
+
+    @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);
+            return false;
+        }
+
+        //记录开始时间
+        startTimeThreadLocal.set(System.currentTimeMillis());//线程绑定变量(该数据只有当前请求的线程可见)
+
+        //登录请求不拦截
+        String url = request.getServletPath();
+        if (url.equals("/login") || url.equals("/login/")) {
+            return true;
+        }
+
+        String token = request.getHeader("Authentication");
+        logger.info("请求开始  url={}  token={}", url, token);
+        if (StringUtils.isEmpty(token)) {
+            return false;
+        }
+
+        JwsToken jwsToken = jwsTokenService.parse(token);
+        if (jwsToken == null) {
+            return false;
+        }
+
+        String userName = jwsToken.getUserName();
+
+        //因为缓存了用户id和设备id
+        DeviceUserVo du = (DeviceUserVo) redisService.get(String.format(LOGIN_TOKEN_FORMAT_KEY, userName));
+        if (null == du) {
+            logger.error("验证 token 异常:  token={}", JSON.toJSONString(jwsToken));
+            request.setAttribute(ERROR_LOGIN_HEADER, "ERROR_TOKEN");
+            responseOutWithJson(request, response);
+            return false;
+        }else if (!jwsToken.getDeviceId().equals(du.getDevice())) {
+            logger.error("device not match, token={}", JSON.toJSONString(jwsToken));
+            request.setAttribute(ERROR_LOGIN_HEADER, "NOT_MATCH");
+            responseOutWithJson(request, response);
+            return false;
+        }
+        //查询到结果 如果存在 token 对应的 user
+        // 1.判断 如果有效期小于 2小时
+        logger.info("token check success: {}", JSON.toJSONString(du));
+
+        return true;
+    }
+
+    @Override
+    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
+
+    }
+
+    @Override
+    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
+
+        logger.info("请求结束 url={}", request.getRequestURI());
+        long beginTime = startTimeThreadLocal.get();//得到线程绑定的局部变量(开始时间)
+        long consumeTime = System.currentTimeMillis() - beginTime;//消耗的时间
+        if (consumeTime > 500) {//此处认为处理时间超过500毫秒的请求为慢请求
+            logger.warn(" 请求: {} consume {} millis", request.getRequestURI(), consumeTime);
+        }
+    }
+
+    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 setIgnoreOptions(boolean ignoreOptions) {
+        this.ignoreOptions = ignoreOptions;
+    }
+}

+ 23 - 0
rankin-api-web/src/main/java/cn/rankin/apiweb/security/JwsToken.java

@@ -0,0 +1,23 @@
+package cn.rankin.apiweb.security;
+
+import lombok.Data;
+import org.apache.commons.lang.StringUtils;
+
+import java.io.Serializable;
+
+@Data
+public class JwsToken implements Serializable {
+
+    private String userName;
+
+    private String deviceId;
+
+    private String sign;
+
+    public boolean isRight() {
+        if (StringUtils.isEmpty(userName) || StringUtils.isEmpty(deviceId)) {
+            return false;
+        }
+        return true;
+    }
+}

+ 55 - 0
rankin-api-web/src/main/java/cn/rankin/apiweb/security/JwsTokenService.java

@@ -0,0 +1,55 @@
+package cn.rankin.apiweb.security;
+
+import cn.rankin.common.utils.util.ListUtil;
+import com.alibaba.fastjson.JSON;
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jwts;
+import org.apache.commons.lang.StringUtils;
+import org.springframework.security.crypto.util.EncodingUtils;
+import org.springframework.stereotype.Component;
+import org.springframework.util.CollectionUtils;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Base64;
+import java.util.List;
+
+/**
+ * @author Jonsy
+ *
+ */
+@Component
+public class JwsTokenService implements Serializable {
+
+    private static final String SEPARATER = "\\.";
+
+    private static final String ENCODING = "UTF-8";
+
+    public final Base64.Decoder decoder = Base64.getDecoder();
+
+    public JwsToken parse(String token) {
+        String[] parts = token.split(SEPARATER);
+        if (parts.length != 2) {
+            return null;
+        }
+
+        String payload = new String(decoder.decode(parts[0]));
+        JwsToken jwsToken = JSON.parseObject(payload, JwsToken.class);
+        if (!jwsToken.isRight()) {
+            return null;
+        }
+
+        String sign = new String(decoder.decode(parts[1]));
+        if (StringUtils.isEmpty(sign)) {
+            return null;
+        }
+
+        jwsToken.setSign(sign);
+
+        return jwsToken;
+    }
+
+    public static void main(String[] args) {
+        JwsToken jwsToken = new JwsTokenService().parse("eyJ1c2VyTmFtZSI6Imh1b2RvbmciLCJkZXZpY2VJZCI6ImpsanNsZGppZXgwMDIzamMifQ==.OTB1M2prYzd1amU=");
+    }
+}

+ 42 - 0
rankin-api-web/src/main/java/cn/rankin/apiweb/vo/DeviceUserVo.java

@@ -0,0 +1,42 @@
+package cn.rankin.apiweb.vo;
+
+import cn.rankin.common.utils.enums.GenderEnum;
+import lombok.Data;
+import lombok.ToString;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * Created by tomas on 2017/5/22.
+ */
+@Data
+@ToString
+public class DeviceUserVo implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    //设备信息
+    private String device;
+
+    //用户信息
+    private String code;
+
+    private String userId;
+
+    private String avatar;
+
+    private Date birthday;
+
+    private Integer domain;
+
+    private GenderEnum gender;
+
+    private String merchantId;     //集团 id
+
+    private String merchantName; //集团 name
+
+    private String campusId;
+
+    private String mobile;
+
+    private String name;
+}

+ 34 - 2
rankin-cms-web/src/main/java/cn/rankin/cmsweb/controller/user/MerchantController.java

@@ -1,16 +1,21 @@
 package cn.rankin.cmsweb.controller.user;
 
+import cn.rankin.cmsweb.code.CmsWebAPICode;
+import cn.rankin.cmsweb.service.product.RecommendService;
 import cn.rankin.cmsweb.service.user.MerchantService;
 import cn.rankin.common.utils.api.model.APIResult;
 import cn.rankin.common.utils.api.page.Page;
 import cn.rankin.common.utils.dto.user.MerchantDTO;
 import cn.rankin.common.utils.dto.user.MerchantSearchDTO;
 import cn.rankin.common.utils.util.BeanUtil;
+import cn.rankin.common.utils.vo.product.RecommendVo;
 import cn.rankin.common.utils.vo.user.MerchantVo;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.CollectionUtils;
 import org.springframework.web.bind.annotation.*;
 
 import javax.validation.Valid;
+import java.util.List;
 
 @RestController
 @RequestMapping(value = "/merchant")
@@ -19,6 +24,9 @@ public class MerchantController {
     @Autowired
     private MerchantService merchantService;
 
+    @Autowired
+    private RecommendService recommendService;
+
     @RequestMapping(value = {"", "/list"}, method = RequestMethod.GET)
     public APIResult<Page<MerchantVo>> getPage(MerchantSearchDTO searchDTO) {
         return merchantService.search(BeanUtil.convertToMap(searchDTO));
@@ -31,7 +39,23 @@ public class MerchantController {
 
     @RequestMapping(method = RequestMethod.PUT)
     public APIResult<MerchantVo> update(@RequestBody @Valid MerchantDTO merchantDTO) {
-        return merchantService.update(merchantDTO);
+        APIResult<MerchantVo> result = merchantService.update(merchantDTO);
+        if (!result.getSuccess()) {
+            return result;
+        }
+
+        MerchantVo merchantVo = result.getData();
+        List<String> recommendIdList = merchantDTO.getRecommendIdList();
+
+        if (!CollectionUtils.isEmpty(recommendIdList)) {
+            APIResult<List<RecommendVo>> recommendAPIResult = recommendService.put(merchantDTO.getId(), recommendIdList);
+            if (!recommendAPIResult.getSuccess()) {
+                return APIResult.error(CmsWebAPICode.error("更新推荐位失败"));
+            }
+            merchantVo.setRecommendVoList(recommendAPIResult.getData());
+        }
+
+        return result;
     }
 
     @RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
@@ -41,7 +65,15 @@ public class MerchantController {
 
     @RequestMapping(value = "/{id}", method = RequestMethod.GET)
     public APIResult<MerchantVo> getMerchant(@PathVariable String id) {
-        return merchantService.getMerchant(id);
+        APIResult<MerchantVo> result = merchantService.getMerchant(id);
+        if (result.getSuccess()) {
+            MerchantVo merchantVo = result.getData();
+            APIResult<List<RecommendVo>> recommendAPIResult = recommendService.get(id);
+            if (recommendAPIResult.getSuccess()) {
+                merchantVo.setRecommendVoList(recommendAPIResult.getData());
+            }
+        }
+        return result;
     }
 
 }

+ 21 - 0
rankin-cms-web/src/main/java/cn/rankin/cmsweb/service/product/RecommendService.java

@@ -0,0 +1,21 @@
+package cn.rankin.cmsweb.service.product;
+
+import cn.rankin.common.utils.api.model.APIResult;
+import cn.rankin.common.utils.vo.product.RecommendVo;
+import org.springframework.cloud.netflix.feign.FeignClient;
+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.RequestParam;
+
+import java.util.List;
+
+@FeignClient(name = "${service.product.name}")
+public interface RecommendService {
+
+    @RequestMapping(value = "/recommend", method = RequestMethod.GET)
+    APIResult<List<RecommendVo>> get(@RequestParam("merchantId") String merchantId);
+
+    @RequestMapping(value = "/recommend", method = RequestMethod.PUT)
+    APIResult<List<RecommendVo>> put(@RequestParam("merchantId") String merchantId, @RequestBody List<String> itemIdList);
+}

+ 20 - 1
rankin-common-utils/pom.xml

@@ -34,7 +34,21 @@
             <groupId>javax.validation</groupId>
             <artifactId>validation-api</artifactId>
         </dependency>
-
+        <!-- aop 切面 -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-aop</artifactId>
+        </dependency>
+        <!-- redis 组件 -->
+        <dependency>
+            <groupId>org.springframework.data</groupId>
+            <artifactId>spring-data-redis</artifactId>
+            <version>1.8.3.RELEASE</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis</artifactId>
+        </dependency>
         <dependency>
             <groupId>com.alibaba</groupId>
             <artifactId>fastjson</artifactId>
@@ -67,6 +81,11 @@
             <artifactId>spring-security-web</artifactId>
             <version>4.2.2.RELEASE</version>
         </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+            <version>2.8.10</version>
+        </dependency>
 
     </dependencies>
 

+ 9 - 0
rankin-common-utils/src/main/java/cn/rankin/common/utils/api/model/APICode.java

@@ -17,6 +17,12 @@ public class APICode {
     public final static int _C_PARAMETER_ERROR = 400;
     public final static BaseCode PARAMETER_ERROR = new BaseCode(_C_PARAMETER_ERROR, "参数错误");
 
+    public final static int _C_UNAUTHORIZED = 401;
+    public final static BaseCode UNAUTHORIZED = new BaseCode(_C_UNAUTHORIZED, "未认证");
+
+    public final static int _C_INVALID_TOKEN = 402;
+    public final static BaseCode INVALID_TOKEN = new BaseCode(_C_INVALID_TOKEN, "无效的Token");
+
     public final static int _C_REMOTE_SERVER_ERROR = 600;
     public final static BaseCode REMOTE_SERVER_ERROR = new BaseCode(_C_REMOTE_SERVER_ERROR, "远程服务器错误");
 
@@ -38,6 +44,9 @@ public class APICode {
     // user flag code
     public final static int _USER_SERVICE_FLAG = 20000;
 
+    // api flag code
+    public final static int _API_WEB_FLAG = 30000;
+
     public static BaseCode ok(String message) {
         return new BaseCode(_C_OK, message);
     }

+ 113 - 0
rankin-common-utils/src/main/java/cn/rankin/common/utils/configuration/RedisConfiguration.java

@@ -0,0 +1,113 @@
+/**
+ * 文件名:@RedisConfiguration.java <br/>
+ * 包名: configuration <br/>
+ * @author tomas <br/>
+*/
+
+
+package cn.rankin.common.utils.configuration;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.springframework.cache.CacheManager;
+import org.springframework.cache.annotation.CachingConfigurerSupport;
+import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.cache.interceptor.KeyGenerator;
+import org.springframework.context.EnvironmentAware;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.env.Environment;
+import org.springframework.data.redis.cache.DefaultRedisCachePrefix;
+import org.springframework.data.redis.cache.RedisCacheManager;
+import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+
+import java.lang.reflect.Method;
+
+/**
+ * 类名:RedisConfiguration  <br />
+ *
+ * 功能:redis相关配置
+ *
+ * @author tomas <br />
+ * 创建时间:2016年8月16日 上午10:00:53  <br />
+ * @version 2016年8月16日
+*/
+@Configuration
+@EnableCaching
+public class RedisConfiguration extends CachingConfigurerSupport implements EnvironmentAware {
+
+	// 应用环境信息
+	protected Environment environment;
+
+	@Bean
+	public KeyGenerator keyGenerator() {
+		return new KeyGenerator() {
+			@Override
+			public Object generate(Object target, Method method, Object... params) {
+				StringBuilder sb = new StringBuilder();
+				sb.append(target.getClass().getName());
+				sb.append(method.getName());
+				for (Object obj : params) {
+					sb.append(obj.toString());
+				}
+				return sb.toString();
+			}
+		};
+	}
+
+	@Bean
+	public CacheManager cacheManager(RedisTemplate redisTemplate) {
+		RedisCacheManager rcm = new RedisCacheManager(redisTemplate);
+		rcm.setCachePrefix(new DefaultRedisCachePrefix());
+		//设置缓存过期时间 30天
+		rcm.setDefaultExpiration(2592000);//秒
+		return rcm;
+	}
+
+	@Bean(name="redisTemplate")
+	public RedisTemplate<?, ?> redisTemplate(JedisConnectionFactory factory){
+		RedisTemplate<?, ?> template = new RedisTemplate<>();
+		template.setConnectionFactory(factory);
+		factory.setDatabase(2);
+
+		//初始化序列化框架(StringRedisSerializer)
+		/*StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
+		JdkSerializationRedisSerializer redisSerializer = new JdkSerializationRedisSerializer();
+
+		template.setDefaultSerializer(redisSerializer);
+		//设置key的序列化方式
+		template.setKeySerializer(stringRedisSerializer);
+		//设置value的序列化方式
+		template.setValueSerializer(redisSerializer);
+		//设置hash key的序列化方式
+		template.setHashKeySerializer(stringRedisSerializer);
+		//设置hash value的序列化方式
+		template.setHashValueSerializer(redisSerializer);
+
+		template.afterPropertiesSet();
+		return template;*/
+
+		Jackson2JsonRedisSerializer jackson2JsonRedisSerializer=new Jackson2JsonRedisSerializer(Object.class);
+		ObjectMapper om=new ObjectMapper();
+		om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
+		om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
+		jackson2JsonRedisSerializer.setObjectMapper(om);
+		template.setKeySerializer(new StringRedisSerializer());
+		template.setValueSerializer(jackson2JsonRedisSerializer);
+		template.setDefaultSerializer(jackson2JsonRedisSerializer);
+		template.setHashValueSerializer(jackson2JsonRedisSerializer);
+		template.setHashKeySerializer(new StringRedisSerializer());
+		template.afterPropertiesSet();
+		return template;
+
+	}
+	@Override
+	public void setEnvironment(Environment environment) {
+		this.environment = environment;
+	}
+
+}

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

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

+ 3 - 2
rankin-common-utils/src/main/java/cn/rankin/common/utils/dto/user/MerchantDTO.java

@@ -2,12 +2,11 @@ package cn.rankin.common.utils.dto.user;
 
 import cn.rankin.common.utils.enums.BaseStatusEnum;
 import cn.rankin.common.utils.enums.ReceiptTypeEnum;
-import cn.rankin.common.utils.validator.Domain;
 import lombok.Data;
 
 import javax.validation.constraints.NotNull;
-import javax.validation.constraints.Pattern;
 import java.io.Serializable;
+import java.util.List;
 
 @Data
 public class MerchantDTO implements Serializable{
@@ -35,4 +34,6 @@ public class MerchantDTO implements Serializable{
 
     @NotNull
     private BaseStatusEnum status;
+
+    private List<String> recommendIdList;
 }

+ 1 - 1
rankin-common-utils/src/main/java/cn/rankin/common/utils/enums/ItemTypeEnum.java

@@ -2,7 +2,7 @@ package cn.rankin.common.utils.enums;
 
 public enum ItemTypeEnum {
 
-    COURSE("课程"), SUPPORT("周边");
+    COURSE("课程"), SUPPORT("周边"), PACKAGE("课程包");
 
     private String desc;
 

+ 113 - 0
rankin-common-utils/src/main/java/cn/rankin/common/utils/service/RedisService.java

@@ -0,0 +1,113 @@
+package cn.rankin.common.utils.service;
+
+import org.springframework.data.redis.core.*;
+
+import java.util.Collection;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 封装 redisTemplate 接口
+ * Created by tomas on 2017/6/13.
+ */
+public interface RedisService<K,V> {
+    /**
+     * 获取
+     * @param key
+     * @return Object
+     */
+    V get(K key);
+
+    /**
+     * 默认设置 30天有效 避免长期 暂用 redis
+     * @param key
+     * @param value
+     */
+     void set(K key, V value);
+
+    /**
+     * 存值并且设置 redis key 过期时间
+     * @param key
+     * @param value
+     * @param timeout 秒
+     */
+     void set(K key, V value, long timeout);
+
+    /**
+     * 存值并且设置 redis key 过期时间
+     * @param key
+     * @param value
+     * @param timeout
+     */
+    void set(K key, V value, long timeout, TimeUnit unit);
+
+    /**
+     * 删除 key
+     * @param key
+     * @return Object
+     */
+    void delete(K key);
+    /**
+     * 删除 key
+     * @param keys
+     * @return Object
+     */
+    void delete(Collection<K> keys);
+
+    /**
+     * 剩余生存时间 (秒单位)
+     * @param key
+     * @return
+     */
+     Long getExpire(K key);
+
+    /**
+     * 更新 key 的过期时间
+     * @param key
+     * @param timeout
+     * @param unit
+     * @return
+     */
+     boolean setExpire(K key, long timeout, TimeUnit unit);
+    /**
+     * 判断 key 是否存在
+     * @param key
+     * @return
+     */
+     boolean hasKey(K key);
+
+    /**
+     * 返回 redisTemplate for Hash的原生 操作对象
+     * @return HashOperations
+     */
+     HashOperations redisHash();
+
+    /**
+     * 返回 redisTemplate for List的原生 操作对象
+     * @return ListOperations
+     */
+     ListOperations redisList();
+    /**
+     * 返回 redisTemplate for Set的原生 操作对象
+     * @return SetOperations
+     */
+     SetOperations redisSet();
+
+    /**
+     * 返回 redisTemplate for ZSet的原生 操作对象
+     * @return ZSetOperations
+     */
+     ZSetOperations redisZSet() ;
+
+    /**
+     * 返回 redisTemplate for Value的原生 操作对象
+     * @return ValueOperations
+     */
+     ValueOperations redisValue();
+
+    /**
+     * /**
+     * 返回 redisTemplat 原生操作对象
+     * return RedisTemplate
+     */
+     RedisTemplate redisTemplate();
+}

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

@@ -0,0 +1,180 @@
+package cn.rankin.common.utils.service.impl;
+
+import cn.rankin.common.utils.configuration.RedisConfiguration;
+import cn.rankin.common.utils.service.RedisService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.context.annotation.Import;
+import org.springframework.data.redis.core.*;
+import org.springframework.stereotype.Component;
+
+import java.util.Collection;
+import java.util.concurrent.TimeUnit;
+
+@Slf4j
+@Component
+@Import(RedisConfiguration.class)
+public class RedisServiceImpl<K,V> implements RedisService<K,V> {
+
+
+	@Autowired
+	@Qualifier(value = "redisTemplate")
+	RedisTemplate<K,V> redisTemplate;
+
+	/**
+	 * 获取
+	 * @param key
+	 * @return Object
+	 */
+	@Override
+	public V get(K key) {
+		return redisTemplate.opsForValue().get(key);
+	}
+
+	/**
+	 * 默认设置 30天有效 避免长期 暂用 redis
+	 * @param key
+	 * @param value
+	 */
+	@Override
+	public void set(K key, V value) {
+		redisTemplate.opsForValue().set(key, value,3600*24*30, TimeUnit.SECONDS);
+	}
+
+	/**
+	 * 存值并且设置 redis key 过期时间
+	 * @param key
+	 * @param value
+	 * @param timeout
+	 */
+	@Override
+	public void set(K key, V value, long timeout) {
+		redisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);
+	}
+
+	/**
+	 * 存值并且设置 redis key 过期时间
+	 * @param key
+	 * @param value
+	 * @param timeout
+	 */
+	@Override
+	public void set(K key, V value, long timeout,TimeUnit unit) {
+		redisTemplate.opsForValue().set(key, value, timeout, unit);
+	}
+
+	/**
+	 * 删除 key
+	 * @param key
+	 * @return Object
+	 */
+	@Override
+	public void delete(K key) {
+		if (hasKey(key)) {
+			redisTemplate.delete(key);
+		}
+	}
+
+	/**
+	 * 删除 key
+	 *
+	 * @param keys
+	 * @return Object
+	 */
+	@Override
+	public void delete(Collection<K> keys) {
+		redisTemplate.delete(keys);
+	}
+
+	/**
+	 * 剩余生存时间 (秒单位)
+	 * @param key
+	 * @return
+	 */
+	@Override
+	public Long getExpire(K key) {
+		if (!hasKey(key)) {
+			return 0L;
+		}
+		return redisTemplate.getExpire(key);
+	}
+
+	/**
+	 * 更新 key 的过期时间
+	 * @param key
+	 * @param timeout
+	 * @param unit
+	 * @return
+	 */
+	@Override
+	public boolean setExpire(K key, long timeout, TimeUnit unit) {
+		return redisTemplate.expire(key, timeout, unit);
+	}
+
+	/**
+	 * 判断 key 是否存在
+	 * @param key
+	 * @return
+	 */
+	@Override
+	public boolean hasKey(K key) {
+		return redisTemplate.hasKey(key);
+	}
+
+	/**
+	 * 返回 redisTemplate for Hash的原生 操作对象
+	 * @return HashOperations
+	 */
+	@Override
+	public HashOperations redisHash() {
+		return redisTemplate.opsForHash();
+	}
+
+	/**
+	 * 返回 redisTemplate for List的原生 操作对象
+	 * @return ListOperations
+	 */
+	@Override
+	public ListOperations redisList() {
+		return redisTemplate.opsForList();
+	}
+	/**
+	 * 返回 redisTemplate for Set的原生 操作对象
+	 * @return SetOperations
+	 */
+	@Override
+	public SetOperations redisSet()  {
+		return redisTemplate.opsForSet();
+	}
+
+	/**
+	 * 返回 redisTemplate for ZSet的原生 操作对象
+	 * @return ZSetOperations
+	 */
+	@Override
+	public ZSetOperations redisZSet()  {
+		return redisTemplate.opsForZSet();
+	}
+	/**
+	 * 返回 redisTemplate for Value的原生 操作对象
+	 * @return ValueOperations
+	 */
+
+	@Override
+	public ValueOperations redisValue()  {
+		return redisTemplate.opsForValue();
+	}
+
+	/**
+	 * /**
+	 * 返回 redisTemplat 原生操作对象
+	 * return redisTemplate
+	 */
+	@Override
+	public RedisTemplate redisTemplate()  {
+		return redisTemplate;
+	}
+
+
+}

+ 30 - 0
rankin-common-utils/src/main/java/cn/rankin/common/utils/vo/product/RecommendVo.java

@@ -0,0 +1,30 @@
+package cn.rankin.common.utils.vo.product;
+
+import cn.rankin.common.utils.enums.BaseStatusEnum;
+import cn.rankin.common.utils.enums.ItemTypeEnum;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+@Data
+public class RecommendVo implements Serializable {
+
+    private String id;
+
+    private String merchantId;
+
+    private String itemId;
+
+    private BaseStatusEnum status;
+
+    private Integer sort;
+
+    private Date gmtCreated;
+
+    private Date gmtModified;
+
+    private ItemTypeEnum type;
+
+    private String name;
+}

+ 3 - 0
rankin-common-utils/src/main/java/cn/rankin/common/utils/vo/user/MerchantVo.java

@@ -2,6 +2,7 @@ package cn.rankin.common.utils.vo.user;
 
 import cn.rankin.common.utils.enums.BaseStatusEnum;
 import cn.rankin.common.utils.enums.ReceiptTypeEnum;
+import cn.rankin.common.utils.vo.product.RecommendVo;
 import lombok.Data;
 import lombok.ToString;
 
@@ -42,4 +43,6 @@ public class MerchantVo implements Serializable {
     private Date gmtModified;
 
     private List<CmsUserVo> adminList;
+
+    private List<RecommendVo> recommendVoList;
 }

+ 33 - 0
rankin-common-utils/src/main/java/cn/rankin/common/utils/vo/user/TerminalDeviceVo.java

@@ -0,0 +1,33 @@
+package cn.rankin.common.utils.vo.user;
+
+import cn.rankin.common.utils.enums.BaseStatusEnum;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+@Data
+public class TerminalDeviceVo implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    private String id;
+
+    private String code;
+
+    private String userId;
+
+    private String brand;
+
+    private String ip;
+
+    private String modelNo;
+
+    private Boolean bind;
+
+    private BaseStatusEnum status;
+
+    private Date gmtCreated;
+
+    private Date gmtModified;
+
+}

+ 6 - 0
rankin-product-service/src/main/java/cn/rankin/productservice/code/ProductServiceAPICode.java

@@ -0,0 +1,6 @@
+package cn.rankin.productservice.code;
+
+import cn.rankin.common.utils.api.model.APICode;
+
+public class ProductServiceAPICode extends APICode {
+}

+ 1 - 3
rankin-product-service/src/main/java/cn/rankin/productservice/controller/ItemController.java

@@ -8,8 +8,6 @@ import cn.rankin.common.utils.dto.product.ItemDTO;
 import cn.rankin.common.utils.dto.product.ItemPriceDTO;
 import cn.rankin.common.utils.dto.search.ItemSearchDTO;
 import cn.rankin.common.utils.enums.BaseOrderEnum;
-import cn.rankin.productservice.entity.Item;
-import cn.rankin.productservice.entity.ItemPrice;
 import cn.rankin.productservice.entity.Tag;
 import cn.rankin.productservice.proxy.ItemServiceProxy;
 import cn.rankin.productservice.service.ItemService;
@@ -80,7 +78,7 @@ public class ItemController {
         if (tagIdList != null) {
             APIResult<List<Tag>> tagResult = itemServiceProxy.updateTagRelation(itemId, tagIdList);
             if (!tagResult.getSuccess()) {
-                // 显回滚
+                // 显回滚
                 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                 return APIResult.error(new BaseCode(tagResult.getCode(), tagResult.getMessage()));
             }

+ 33 - 2
rankin-product-service/src/main/java/cn/rankin/productservice/controller/RecommendController.java

@@ -1,9 +1,40 @@
 package cn.rankin.productservice.controller;
 
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import cn.rankin.common.utils.api.model.APIResult;
+import cn.rankin.productservice.code.ProductServiceAPICode;
+import cn.rankin.productservice.entity.Recommend;
+import cn.rankin.productservice.service.RecommendService;
+import org.apache.commons.lang.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.CollectionUtils;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
 
 @RestController
 @RequestMapping(value = "/recommend")
 public class RecommendController {
+
+    @Autowired
+    private RecommendService recommendService;
+
+    @RequestMapping(method = RequestMethod.GET)
+    public APIResult<List<Recommend>> get(@RequestParam("merchantId") String merchantId) {
+        if (StringUtils.isEmpty(merchantId)) {
+            return APIResult.error(ProductServiceAPICode.PARAMETER_ERROR);
+        }
+
+        List<Recommend> recommendList = recommendService.get(merchantId);
+        return APIResult.ok(recommendList);
+    }
+
+    @RequestMapping(method = RequestMethod.PUT)
+    public APIResult<List<Recommend>> put(@RequestParam("merchantId") String merchantId, @RequestBody List<String> itemIdList) {
+        if (StringUtils.isEmpty(merchantId) || CollectionUtils.isEmpty(itemIdList)) {
+            return APIResult.error(ProductServiceAPICode.PARAMETER_ERROR);
+        }
+
+        List<Recommend> recommendList = recommendService.put(merchantId, itemIdList);
+        return APIResult.ok(recommendList);
+    }
 }

+ 0 - 1
rankin-product-service/src/main/java/cn/rankin/productservice/controller/TagController.java

@@ -7,7 +7,6 @@ import cn.rankin.common.utils.api.page.Page;
 import cn.rankin.common.utils.dto.product.TagDTO;
 import cn.rankin.common.utils.dto.search.TagSearchDTO;
 import cn.rankin.common.utils.enums.*;
-import cn.rankin.productservice.entity.Item;
 import cn.rankin.productservice.entity.ItemTagRelation;
 import cn.rankin.productservice.entity.Tag;
 import cn.rankin.productservice.service.ItemService;

+ 0 - 74
rankin-product-service/src/main/java/cn/rankin/productservice/entity/Item.java

@@ -1,74 +0,0 @@
-package cn.rankin.productservice.entity;
-
-import cn.rankin.common.utils.enums.ItemStatusEnum;
-import cn.rankin.common.utils.enums.ItemTypeEnum;
-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;
-import java.util.List;
-import java.util.Set;
-
-@Data
-@ToString
-@Entity
-@Table(name = "lj_item", uniqueConstraints = {@UniqueConstraint(columnNames= {"sub_id", "merchant_id"})},
-        indexes = {@Index(columnList = "code"), @Index(columnList = "name"), @Index(columnList = "sub_id")})
-@DynamicInsert
-@DynamicUpdate
-public class Item implements Serializable {
-
-    @Id
-    private String id;
-
-    @Column(name = "sub_id", updatable = false)
-    private String subId;
-
-    @Column
-    private String code;
-
-    @Column
-    private String name;
-
-    @Column
-    @Enumerated(EnumType.ORDINAL)
-    private ItemTypeEnum type;
-
-    @Column(name = "merchant_id")
-    private String merchantId;
-
-    @Column
-    private Integer sort;
-
-    @Enumerated(EnumType.ORDINAL)
-    private ItemStatusEnum 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;
-
-//    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "itemId")
-//    @OrderBy("duration asc")
-    @Transient
-    private List<ItemPrice> priceList;
-
-//    @ManyToMany(fetch = FetchType.LAZY)
-//    @JoinTable(name = "lj_item_tag_relation", joinColumns = {@JoinColumn(name="item_id", referencedColumnName="id")},
-//            inverseJoinColumns = {@JoinColumn(name = "tag_id", referencedColumnName = "id", unique = false)})
-    @Transient
-    private List<Tag> tagList;
-
-    @Transient
-    private Course course;
-
-    @Transient
-    private Support support;
-}

+ 0 - 65
rankin-product-service/src/main/java/cn/rankin/productservice/entity/ItemPrice.java

@@ -1,65 +0,0 @@
-package cn.rankin.productservice.entity;
-
-import cn.rankin.common.utils.enums.BaseStatusEnum;
-import cn.rankin.common.utils.enums.PriceTypeEnum;
-import lombok.Data;
-import lombok.ToString;
-import org.hibernate.annotations.DynamicInsert;
-import org.hibernate.annotations.DynamicUpdate;
-import org.hibernate.annotations.GenericGenerator;
-
-import javax.persistence.*;
-import java.io.Serializable;
-import java.util.Date;
-
-@Data
-@ToString
-@Entity
-@Table(name = "lj_item_price",  uniqueConstraints = {@UniqueConstraint(columnNames = {"item_id", "type", "duration"})},
-        indexes = {@Index(columnList = "item_id")})
-@DynamicInsert
-@DynamicUpdate
-public class ItemPrice implements Serializable {
-
-    @Id
-    private String id;
-
-    @Column
-    private String name;
-
-    @Column(name = "item_id", updatable = false)
-    private String itemId;
-
-    @Column
-    @Enumerated(EnumType.ORDINAL)
-    private PriceTypeEnum type = PriceTypeEnum.NORMAL;
-
-    @Column
-    private Integer duration;
-
-    @Column(name = "cp_price")
-    private Double cpPrice;
-
-    @Column(name = "merchant_price")
-    private Double merchantPrice;
-
-    @Column(name = "terminal_price")
-    private Double terminalPrice;
-
-    private Integer sort;
-
-    @Column
-    @Enumerated(EnumType.ORDINAL)
-    private BaseStatusEnum status;
-
-    @Column(name = "gmt_created", updatable = false, insertable = false)
-    @Temporal(TemporalType.TIMESTAMP)
-    private Date gmtCreated;
-
-    @Column(name = "gmt_modified")
-    @Temporal(TemporalType.TIMESTAMP)
-    private Date gmtModified;
-
-    @Column(name = "package_id")
-    private String packageId;
-}

+ 10 - 4
rankin-product-service/src/main/java/cn/rankin/productservice/entity/Recommend.java

@@ -1,6 +1,7 @@
 package cn.rankin.productservice.entity;
 
 import cn.rankin.common.utils.enums.BaseStatusEnum;
+import cn.rankin.common.utils.enums.ItemTypeEnum;
 import lombok.Data;
 import lombok.ToString;
 import org.hibernate.annotations.DynamicInsert;
@@ -32,18 +33,23 @@ public class Recommend implements Serializable {
     private String itemId;
 
     @Enumerated(EnumType.ORDINAL)
-    @Column(name="type")
-    private BaseStatusEnum type;
+    private BaseStatusEnum status;
 
     @Column(name="sort")
     private Integer sort;
 
-    @Column(name = "gmt_created")
+    @Enumerated(EnumType.ORDINAL)
+    private ItemTypeEnum type;
+
+    @Column(name = "gmt_created", updatable = false, insertable = false, columnDefinition = "timestamp NULL DEFAULT CURRENT_TIMESTAMP")
     @Temporal(TemporalType.TIMESTAMP)
     private Date gmtCreated;
 
-    @Column(name = "gmt_modified")
+    @Column(name = "gmt_modified", updatable = false, insertable = false, columnDefinition = "timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")
     @Temporal(TemporalType.TIMESTAMP)
     private Date gmtModified;
 
+    @Transient
+    private String name;
+
 }

+ 2 - 0
rankin-product-service/src/main/java/cn/rankin/productservice/proxy/ItemServiceProxy.java

@@ -60,6 +60,8 @@ public class ItemServiceProxy {
         }else if (ItemTypeEnum.SUPPORT.equals(itemType)) {
             Support support = supportServiceProxy.getSupport(subId);
             item.setSupport(support);
+        }else if (ItemTypeEnum.PACKAGE.equals(itemType)) {
+
         }
 
         List<Tag> tagList = getTagList(itemId);

+ 0 - 1
rankin-product-service/src/main/java/cn/rankin/productservice/repository/ItemPriceRepository.java

@@ -3,7 +3,6 @@ package cn.rankin.productservice.repository;
 import cn.rankin.common.utils.enums.BaseStatusEnum;
 import cn.rankin.common.utils.enums.PriceTypeEnum;
 import cn.rankin.common.utils.jpa.BasicJpaRepository;
-import cn.rankin.productservice.entity.ItemPrice;
 import org.springframework.data.jpa.repository.Modifying;
 import org.springframework.data.jpa.repository.Query;
 import org.springframework.data.repository.query.Param;

+ 0 - 1
rankin-product-service/src/main/java/cn/rankin/productservice/repository/ItemRepository.java

@@ -2,7 +2,6 @@ package cn.rankin.productservice.repository;
 
 import cn.rankin.common.utils.enums.ItemStatusEnum;
 import cn.rankin.common.utils.jpa.BasicJpaRepository;
-import cn.rankin.productservice.entity.Item;
 import org.springframework.data.jpa.repository.Modifying;
 import org.springframework.data.jpa.repository.Query;
 import org.springframework.data.repository.query.Param;

+ 10 - 0
rankin-product-service/src/main/java/cn/rankin/productservice/repository/RecommendRepository.java

@@ -1,7 +1,17 @@
 package cn.rankin.productservice.repository;
 
+import cn.rankin.common.utils.enums.BaseStatusEnum;
 import cn.rankin.common.utils.jpa.BasicJpaRepository;
 import cn.rankin.productservice.entity.Recommend;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+
+import java.util.List;
 
 public interface RecommendRepository extends BasicJpaRepository<Recommend, String> {
+
+    @Query(value = "select r from Recommend r where r.merchantId = :merchantId and r.status = :status order by r.sort")
+    List<Recommend> findByStatus(@Param("merchantId") String merchantId, @Param("status")BaseStatusEnum status);
+
+    List<Recommend> findByMerchantId(String merchantId);
 }

+ 0 - 1
rankin-product-service/src/main/java/cn/rankin/productservice/service/ItemPriceService.java

@@ -4,7 +4,6 @@ import cn.rankin.common.utils.api.model.APIResult;
 import cn.rankin.common.utils.dto.product.ItemPriceDTO;
 import cn.rankin.common.utils.enums.BaseStatusEnum;
 import cn.rankin.common.utils.enums.PriceTypeEnum;
-import cn.rankin.productservice.entity.ItemPrice;
 import cn.rankin.productservice.repository.ItemPriceRepository;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;

+ 0 - 1
rankin-product-service/src/main/java/cn/rankin/productservice/service/ItemService.java

@@ -7,7 +7,6 @@ import cn.rankin.common.utils.dto.product.ItemDTO;
 import cn.rankin.common.utils.enums.BaseOrderEnum;
 import cn.rankin.common.utils.enums.ItemStatusEnum;
 import cn.rankin.common.utils.util.JpaSortUtil;
-import cn.rankin.productservice.entity.Item;
 import cn.rankin.productservice.repository.*;
 import cn.rankin.productservice.utils.DTOConverter;
 import lombok.extern.slf4j.Slf4j;

+ 18 - 0
rankin-product-service/src/main/java/cn/rankin/productservice/service/PackageItemRelationService.java

@@ -1,13 +1,31 @@
 package cn.rankin.productservice.service;
 
+import cn.rankin.common.utils.enums.BaseOrderEnum;
+import cn.rankin.common.utils.enums.BaseStatusEnum;
+import cn.rankin.common.utils.util.JpaSortUtil;
+import cn.rankin.productservice.entity.PackageItemRelation;
 import cn.rankin.productservice.repository.PackageItemRelationRepository;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import java.util.LinkedHashMap;
+import java.util.List;
+
 @Service
 public class PackageItemRelationService {
 
     @Autowired
     private PackageItemRelationRepository relationRepository;
 
+    public List<PackageItemRelation> load(String packageId) {
+        PackageItemRelation relation = new PackageItemRelation();
+        relation.setPackageId(packageId);
+        relation.setStatus(BaseStatusEnum.NORMAL);
+
+        // 排序
+        LinkedHashMap<String, BaseOrderEnum> sort = new LinkedHashMap<>();
+        sort.put("sort", BaseOrderEnum.ASC);
+
+        return relationRepository.find(relation, JpaSortUtil.sort(sort));
+    }
 }

+ 92 - 0
rankin-product-service/src/main/java/cn/rankin/productservice/service/RecommendService.java

@@ -1,13 +1,105 @@
 package cn.rankin.productservice.service;
 
+import cn.rankin.common.utils.enums.BaseStatusEnum;
+import cn.rankin.common.utils.util.ListUtil;
+import cn.rankin.productservice.entity.Recommend;
+import cn.rankin.productservice.repository.ItemRepository;
 import cn.rankin.productservice.repository.RecommendRepository;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
 
 @Service
 public class RecommendService {
 
     @Autowired
+    private ItemRepository itemRepository;
+
+    @Autowired
     private RecommendRepository recommendRepository;
 
+    public List<Recommend> get(String merchantId) {
+        List<Recommend> recommendList = recommendRepository.findByStatus(merchantId, BaseStatusEnum.NORMAL);
+        if (CollectionUtils.isEmpty(recommendList)) {
+            return new ArrayList<>();
+        }
+
+        return setRecommendInfo(recommendList);
+    }
+
+    public List<Recommend> setRecommendInfo(List<Recommend> recommendList) {
+        if (CollectionUtils.isEmpty(recommendList)) {
+            return new ArrayList<>();
+        }
+
+        List<String> itemIdList = new ArrayList<>();
+        recommendList.forEach(recommend -> itemIdList.add(recommend.getItemId()));
+        List<Item> itemList = itemRepository.findByIds(itemIdList);
+        Map<String, Item> itemMap = ListUtil.convert(itemList, "id", Item.class);
+
+        List<Recommend> result = new ArrayList<>();
+        for (Recommend recommend : recommendList) {
+            Item item = itemMap.get(recommend.getItemId());
+            if (item == null) {
+                continue;
+            }
+            recommend.setName(item.getName());
+            result.add(recommend);
+        }
+        return result;
+    }
+
+    public List<Recommend> put(String merchantId, List<String> itemIdList) {
+        if (CollectionUtils.isEmpty(itemIdList)) {
+            return null;
+        }
+
+        List<Recommend> recommendList = recommendRepository.findByMerchantId(merchantId);
+        List<String> existRecommendIdList = new ArrayList<>();
+
+        // 更新现存的
+        recommendList.forEach(recommend -> {
+            String itemId = recommend.getItemId();
+            if (itemIdList.contains(itemId)) {
+                recommend.setStatus(BaseStatusEnum.NORMAL);
+            }else {
+                recommend.setStatus(BaseStatusEnum.DEL);
+            }
+            existRecommendIdList.add(itemId);
+        });
+
+        // 更新新加的
+        List<String> notExistsRecommendIdList = ListUtil.subtract(itemIdList, existRecommendIdList);
+        if (notExistsRecommendIdList != null && notExistsRecommendIdList.size() > 0) {
+            List<Item> itemList = itemRepository.findByIds(notExistsRecommendIdList);
+            Map<String, Item> itemMap = ListUtil.convert(itemList, "id", Item.class);
+            for (String itemId : notExistsRecommendIdList) {
+                Item item = itemMap.get(itemId);
+                if (item == null || !item.getMerchantId().equals(merchantId)) {
+                    continue;
+                }
+                Recommend recommend = new Recommend();
+                recommend.setItemId(itemId);
+                recommend.setStatus(BaseStatusEnum.NORMAL);
+                recommend.setMerchantId(merchantId);
+                recommend.setType(item.getType());
+                recommendList.add(recommend);
+            }
+        }
+
+        // 排序
+        recommendList.forEach( recommend -> {
+            String itemId = recommend.getItemId();
+            Integer sort = itemIdList.indexOf(itemId);
+            recommend.setSort(sort);
+        });
+
+        recommendRepository.save(recommendList);
+        List<Recommend> result = recommendRepository.findByMerchantId(merchantId);
+        return setRecommendInfo(result);
+    }
 }

+ 1 - 1
rankin-user-service/src/main/java/cn/rankin/userservice/entity/TerminalDevice.java

@@ -31,7 +31,7 @@ public class TerminalDevice implements Serializable {
 	private String code;
 
 	@Column(name="user_id")
-	private Long userId;
+	private String userId;
 
 	private String brand;