items) {
this.total = total;
this.items = items;
}
}
================================================
FILE: ly-common/src/main/java/com/leyou/common/utils/CookieUtils.java
================================================
package com.leyou.common.utils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
/**
* @Author: TianCi.Xiong
* @Description: Cookie工具类
* @Date: Created in 2019-11-14 11:01
*/
public class CookieUtils {
static final Logger logger = LoggerFactory.getLogger(CookieUtils.class);
/**
* 得到Cookie的值, 不编码
*
* @param request
* @param cookieName
* @return
*/
public static String getCookieValue(HttpServletRequest request, String cookieName) {
return getCookieValue(request, cookieName, false);
}
/**
* 得到Cookie的值,
*
* @param request
* @param cookieName
* @return
*/
public static String getCookieValue(HttpServletRequest request, String cookieName, boolean isDecoder) {
Cookie[] cookieList = request.getCookies();
if (cookieList == null || cookieName == null) {
return null;
}
String retValue = null;
try {
for (int i = 0; i < cookieList.length; i++) {
if (cookieList[i].getName().equals(cookieName)) {
if (isDecoder) {
retValue = URLDecoder.decode(cookieList[i].getValue(), "UTF-8");
} else {
retValue = cookieList[i].getValue();
}
break;
}
}
} catch (UnsupportedEncodingException e) {
logger.error("Cookie Decode Error.", e);
}
return retValue;
}
/**
* 得到Cookie的值,
*
* @param request
* @param cookieName
* @return
*/
public static String getCookieValue(HttpServletRequest request, String cookieName, String encodeString) {
Cookie[] cookieList = request.getCookies();
if (cookieList == null || cookieName == null) {
return null;
}
String retValue = null;
try {
for (int i = 0; i < cookieList.length; i++) {
if (cookieList[i].getName().equals(cookieName)) {
retValue = URLDecoder.decode(cookieList[i].getValue(), encodeString);
break;
}
}
} catch (UnsupportedEncodingException e) {
logger.error("Cookie Decode Error.", e);
}
return retValue;
}
/**
* 生成cookie,并指定编码
*
* @param request 请求
* @param response 响应
* @param cookieName name
* @param cookieValue value
* @param encodeString 编码
*/
public static final void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, String encodeString) {
setCookie(request, response, cookieName, cookieValue, null, encodeString, null);
}
/**
* 生成cookie,并指定生存时间
*
* @param request 请求
* @param response 响应
* @param cookieName name
* @param cookieValue value
* @param cookieMaxAge 生存时间
*/
public static final void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, Integer cookieMaxAge) {
setCookie(request, response, cookieName, cookieValue, cookieMaxAge, null, null);
}
/**
* 设置cookie,不指定httpOnly属性
*/
public static final void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, Integer cookieMaxAge, String encodeString) {
setCookie(request, response, cookieName, cookieValue, cookieMaxAge, encodeString, null);
}
/**
* 设置Cookie的值,并使其在指定时间内生效
*
* @param cookieMaxAge cookie生效的最大秒数
*/
public static final void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, Integer cookieMaxAge, String encodeString, Boolean httpOnly) {
try {
if (StringUtils.isBlank(encodeString)) {
encodeString = "utf-8";
}
if (cookieValue == null) {
cookieValue = "";
} else {
cookieValue = URLEncoder.encode(cookieValue, encodeString);
}
Cookie cookie = new Cookie(cookieName, cookieValue);
if (cookieMaxAge != null && cookieMaxAge > 0)
cookie.setMaxAge(cookieMaxAge);
if (null != request)// 设置域名的cookie
cookie.setDomain(getDomainName(request));
cookie.setPath("/");
if (httpOnly != null) {
cookie.setHttpOnly(httpOnly);
}
response.addCookie(cookie);
} catch (Exception e) {
logger.error("Cookie Encode Error.", e);
}
}
/**
* 得到cookie的域名
*/
private static final String getDomainName(HttpServletRequest request) {
String domainName = null;
String serverName = request.getRequestURL().toString();
if (serverName == null || serverName.equals("")) {
domainName = "";
} else {
serverName = serverName.toLowerCase();
serverName = serverName.substring(7);
final int end = serverName.indexOf("/");
serverName = serverName.substring(0, end);
final String[] domains = serverName.split("\\.");
int len = domains.length;
if (len > 3) {
// www.xxx.com.cn
domainName = domains[len - 3] + "." + domains[len - 2] + "." + domains[len - 1];
} else if (len <= 3 && len > 1) {
// xxx.com or xxx.cn
domainName = domains[len - 2] + "." + domains[len - 1];
} else {
domainName = serverName;
}
}
if (domainName != null && domainName.indexOf(":") > 0) {
String[] ary = domainName.split("\\:");
domainName = ary[0];
}
return domainName;
}
}
================================================
FILE: ly-common/src/main/java/com/leyou/common/utils/IdWorker.java
================================================
package com.leyou.common.utils;
import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.NetworkInterface;
/**
* 名称:IdWorker.java
* 描述:分布式自增长ID
*
* Twitter的 Snowflake JAVA实现方案
*
* 核心代码为其IdWorker这个类实现,其原理结构如下,我分别用一个0表示一位,用—分割开部分的作用:
* 1||0---0000000000 0000000000 0000000000 0000000000 0 --- 00000 ---00000 ---000000000000
* 在上面的字符串中,第一位为未使用(实际上也可作为long的符号位),接下来的41位为毫秒级时间,
* 然后5位datacenter标识位,5位机器ID(并不算标识符,实际是为线程标识),
* 然后12位该毫秒内的当前毫秒内的计数,加起来刚好64位,为一个Long型。
* 这样的好处是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由datacenter和机器ID作区分),
* 并且效率较高,经测试,snowflake每秒能够产生26万ID左右,完全满足需要。
*
* 64位ID (42(毫秒)+5(机器ID)+5(业务编码)+12(重复累加))
*
* @Author: TianCi.Xiong
* @Description:
* @Date: Created in 2019-11-15 9:57
*/
public class IdWorker {
// 时间起始标记点,作为基准,一般取系统的最近时间(一旦确定不能变动)
private final static long twepoch = 1288834974657L;
// 机器标识位数
private final static long workerIdBits = 5L;
// 数据中心标识位数
private final static long datacenterIdBits = 5L;
// 机器ID最大值
private final static long maxWorkerId = -1L ^ (-1L << workerIdBits);
// 数据中心ID最大值
private final static long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
// 毫秒内自增位
private final static long sequenceBits = 12L;
// 机器ID偏左移12位
private final static long workerIdShift = sequenceBits;
// 数据中心ID左移17位
private final static long datacenterIdShift = sequenceBits + workerIdBits;
// 时间毫秒左移22位
private final static long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
private final static long sequenceMask = -1L ^ (-1L << sequenceBits);
/* 上次生产id时间戳 */
private static long lastTimestamp = -1L;
// 0,并发控制
private long sequence = 0L;
private final long workerId;
// 数据标识id部分
private final long datacenterId;
public IdWorker() {
this.datacenterId = getDatacenterId(maxDatacenterId);
this.workerId = getMaxWorkerId(datacenterId, maxWorkerId);
}
/**
* @param workerId 工作机器ID
* @param datacenterId 序列号
*/
public IdWorker(long workerId, long datacenterId) {
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
}
if (datacenterId > maxDatacenterId || datacenterId < 0) {
throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
}
this.workerId = workerId;
this.datacenterId = datacenterId;
}
/**
* 获取下一个ID
*
* @return
*/
public synchronized long nextId() {
long timestamp = timeGen();
if (timestamp < lastTimestamp) {
throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
}
if (lastTimestamp == timestamp) {
// 当前毫秒内,则+1
sequence = (sequence + 1) & sequenceMask;
if (sequence == 0) {
// 当前毫秒内计数满了,则等待下一秒
timestamp = tilNextMillis(lastTimestamp);
}
} else {
sequence = 0L;
}
lastTimestamp = timestamp;
// ID偏移组合生成最终的ID,并返回ID
long nextId = ((timestamp - twepoch) << timestampLeftShift)
| (datacenterId << datacenterIdShift)
| (workerId << workerIdShift) | sequence;
return nextId;
}
private long tilNextMillis(final long lastTimestamp) {
long timestamp = this.timeGen();
while (timestamp <= lastTimestamp) {
timestamp = this.timeGen();
}
return timestamp;
}
private long timeGen() {
return System.currentTimeMillis();
}
/**
*
* 获取 maxWorkerId
*
*/
protected static long getMaxWorkerId(long datacenterId, long maxWorkerId) {
StringBuffer mpid = new StringBuffer();
mpid.append(datacenterId);
String name = ManagementFactory.getRuntimeMXBean().getName();
if (!name.isEmpty()) {
/*
* GET jvmPid
*/
mpid.append(name.split("@")[0]);
}
/*
* MAC + PID 的 hashcode 获取16个低位
*/
return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);
}
/**
*
* 数据标识id部分
*
*/
protected static long getDatacenterId(long maxDatacenterId) {
long id = 0L;
try {
InetAddress ip = InetAddress.getLocalHost();
NetworkInterface network = NetworkInterface.getByInetAddress(ip);
if (network == null) {
id = 1L;
} else {
byte[] mac = network.getHardwareAddress();
id = ((0x000000FF & (long) mac[mac.length - 1])
| (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6;
id = id % (maxDatacenterId + 1);
}
} catch (Exception e) {
System.out.println(" getDatacenterId: " + e.getMessage());
}
return id;
}
}
================================================
FILE: ly-common/src/main/java/com/leyou/common/utils/JsonUtils.java
================================================
package com.leyou.common.utils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sun.istack.internal.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.List;
import java.util.Map;
/**
* @Author: TianCi.Xiong
* @Description:
* @Date: Created in 2019-11-14 17:43
*/
public class JsonUtils {
public static final ObjectMapper mapper = new ObjectMapper();
private static final Logger logger = LoggerFactory.getLogger(JsonUtils.class);
@Nullable
public static String serialize(Object obj) {
if (obj == null) {
return null;
}
if (obj.getClass() == String.class) {
return (String) obj;
}
try {
return mapper.writeValueAsString(obj);
} catch (JsonProcessingException e) {
logger.error("json序列化出错:" + obj, e);
return null;
}
}
@Nullable
public static T parse(String json, Class tClass) {
try {
return mapper.readValue(json, tClass);
} catch (IOException e) {
logger.error("json解析出错:" + json, e);
return null;
}
}
@Nullable
public static List parseList(String json, Class eClass) {
try {
return mapper.readValue(json, mapper.getTypeFactory().constructCollectionType(List.class, eClass));
} catch (IOException e) {
logger.error("json解析出错:" + json, e);
return null;
}
}
@Nullable
public static Map parseMap(String json, Class kClass, Class vClass) {
try {
return mapper.readValue(json, mapper.getTypeFactory().constructMapType(Map.class, kClass, vClass));
} catch (IOException e) {
logger.error("json解析出错:" + json, e);
return null;
}
}
@Nullable
public static T nativeRead(String json, TypeReference type) {
try {
return mapper.readValue(json, type);
} catch (IOException e) {
logger.error("json解析出错:" + json, e);
return null;
}
}
}
================================================
FILE: ly-common/src/main/java/com/leyou/common/utils/NumberUtils.java
================================================
package com.leyou.common.utils;
import java.util.Random;
/**
* @Author: TianCi.Xiong
* @Description:
* @Date: Created in 2019-11-13 17:29
*/
public class NumberUtils {
/**
* 生成指定位数的随机数字
*
* @param len 随机数的位数
* @return 生成的随机数
*/
public static String generateCode(int len) {
len = Math.min(len, 8);
int min = Double.valueOf(Math.pow(10, len - 1)).intValue();
int num = new Random().nextInt(
Double.valueOf(Math.pow(10, len + 1)).intValue() - 1) + min;
return String.valueOf(num).substring(0, len);
}
}
================================================
FILE: ly-goods-web/pom.xml
================================================
leyou
com.leyou.parent
1.0.0-SNAPSHOT
4.0.0
com.leyou.goods
ly-goods-web
1.0.0-SNAPSHOT
商品详情页服务模块
org.springframework.boot
spring-boot-starter-web
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
org.springframework.cloud
spring-cloud-starter-openfeign
org.springframework.boot
spring-boot-starter-thymeleaf
org.springframework.boot
spring-boot-starter-amqp
com.leyou.service
ly-item-interface
1.0.0-SNAPSHOT
================================================
FILE: ly-goods-web/src/main/java/com/leyou/LyGoodsWebApplication.java
================================================
package com.leyou;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
/**
* @Author: TianCi.Xiong
* @Description:
* @Date: Created in 2019-11-11 13:33
*/
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class LyGoodsWebApplication {
public static void main(String[] args) {
SpringApplication.run(LyGoodsWebApplication.class, args);
}
}
================================================
FILE: ly-goods-web/src/main/java/com/leyou/client/BrandClient.java
================================================
package com.leyou.client;
import com.leyou.item.api.BrandApi;
import org.springframework.cloud.openfeign.FeignClient;
@FeignClient(value = "item-service")
public interface BrandClient extends BrandApi {
}
================================================
FILE: ly-goods-web/src/main/java/com/leyou/client/CategoryClient.java
================================================
package com.leyou.client;
import com.leyou.item.api.CategoryApi;
import org.springframework.cloud.openfeign.FeignClient;
@FeignClient(value = "item-service")
public interface CategoryClient extends CategoryApi {
}
================================================
FILE: ly-goods-web/src/main/java/com/leyou/client/GoodsClient.java
================================================
package com.leyou.client;
import com.leyou.item.api.GoodsApi;
import org.springframework.cloud.openfeign.FeignClient;
@FeignClient(value = "item-service")
public interface GoodsClient extends GoodsApi {
}
================================================
FILE: ly-goods-web/src/main/java/com/leyou/client/SpecificationClient.java
================================================
package com.leyou.client;
import com.leyou.item.api.SpecificationApi;
import org.springframework.cloud.openfeign.FeignClient;
@FeignClient(value = "item-service")
public interface SpecificationClient extends SpecificationApi {
}
================================================
FILE: ly-goods-web/src/main/java/com/leyou/controller/GoodsController.java
================================================
package com.leyou.controller;
import com.leyou.service.GoodsService;
import com.leyou.service.GoodsHtmlService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.Map;
/**
* @Author: TianCi.Xiong
* @Description:
* @Date: Created in 2019-11-11 13:56
*/
@Controller
@RequestMapping("/item")
public class GoodsController {
@Autowired
private GoodsService goodsService;
@Autowired
private GoodsHtmlService goodsHtmlService;
/**
* 跳转到商品详情页
*
* @param model
* @param id
* @return
*/
@GetMapping("/{id}.html")
public String toItemPage(Model model, @PathVariable("id") Long id) {
// 加载所需的数据
Map modelMap = this.goodsService.loadModel(id);
// 放入模型
model.addAllAttributes(modelMap);
// 页面静态化
this.goodsHtmlService.asyncExcute(id);
return "/item";
}
}
================================================
FILE: ly-goods-web/src/main/java/com/leyou/listener/GoodsListener.java
================================================
package com.leyou.listener;
import com.leyou.service.GoodsHtmlService;
import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @Author: TianCi.Xiong
* @Description: mq监听器
* @Date: Created in 2019-11-13 11:41
*/
@Component
public class GoodsListener {
@Autowired
private GoodsHtmlService goodsHtmlService;
/**
* 处理insert和update的消息
*
* @param id
* @throws Exception
*/
@RabbitListener(bindings = @QueueBinding(
value = @Queue(value = "leyou.create.web.queue", durable = "true"),
exchange = @Exchange(
value = "leyou.item.exchange",
ignoreDeclarationExceptions = "true",
type = ExchangeTypes.TOPIC),
key = {"item.insert", "item.update"}))
public void listenCreate(Long id) throws Exception {
if (id == null) {
return;
}
// 创建页面
goodsHtmlService.createHtml(id);
}
/**
* 处理delete的消息
*
* @param id
*/
@RabbitListener(bindings = @QueueBinding(
value = @Queue(value = "leyou.delete.web.queue", durable = "true"),
exchange = @Exchange(
value = "leyou.item.exchange",
ignoreDeclarationExceptions = "true",
type = ExchangeTypes.TOPIC),
key = "item.delete"))
public void listenDelete(Long id) {
if (id == null) {
return;
}
// 删除页面
goodsHtmlService.deleteHtml(id);
}
}
================================================
FILE: ly-goods-web/src/main/java/com/leyou/service/GoodsHtmlService.java
================================================
package com.leyou.service;
import com.leyou.utils.ThreadUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import java.io.File;
import java.io.PrintWriter;
import java.util.Map;
/**
* @Author: TianCi.Xiong
* @Description: 整合Thymeleaf静态化商品详情页
* @Date: Created in 2019-11-12 13:46
*/
@Service
public class GoodsHtmlService {
@Autowired
private GoodsService goodsService;
@Autowired
private TemplateEngine templateEngine;
private static final Logger logger = LoggerFactory.getLogger(GoodsHtmlService.class);
/**
* 创建html页面
*
* @param spuId
*/
public void createHtml(Long spuId) {
PrintWriter writer = null;
try {
// 获取页面数据
Map spuMap = this.goodsService.loadModel(spuId);
// 创建thymeleaf上下文对象
Context context = new Context();
// 把数据放入上下文对象
context.setVariables(spuMap);
// 创建输出流
File file = new File("D:\\JAVA\\nginx-1.12.2\\html\\leyou\\" + spuId + ".html");
writer = new PrintWriter(file);
// 执行页面静态化方法
templateEngine.process("item", context, writer);
} catch (Exception e) {
logger.error("页面静态化出错:{}," + e, spuId);
} finally {
if (writer != null) {
writer.close();
}
}
}
/**
* 新建线程处理页面静态化
*
* @param spuId
*/
public void asyncExcute(Long spuId) {
ThreadUtils.execute(() -> createHtml(spuId));
/*ThreadUtils.execute(new Runnable() {
@Override
public void run() {
createHtml(spuId);
}
});*/
}
/**
* 删除页面
*
* @param id
*/
public void deleteHtml(Long id) {
File file = new File("D:\\JAVA\\nginx-1.12.2\\html\\leyou\\", id + ".html");
file.deleteOnExit();
}
}
================================================
FILE: ly-goods-web/src/main/java/com/leyou/service/GoodsService.java
================================================
package com.leyou.service;
import com.leyou.client.BrandClient;
import com.leyou.client.CategoryClient;
import com.leyou.client.GoodsClient;
import com.leyou.client.SpecificationClient;
import com.leyou.item.pojo.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.*;
/**
* @Author: TianCi.Xiong
* @Description:
* @Date: Created in 2019-11-11 14:21
*/
@Service
public class GoodsService {
@Autowired
private BrandClient brandClient;
@Autowired
private CategoryClient categoryClient;
@Autowired
private GoodsClient goodsClient;
@Autowired
private SpecificationClient specificationClient;
/**
* 封装数据模型
*
* @param spuId
* @return
*/
public Map loadModel(Long spuId) {
Map map = new HashMap<>();
// 根据id查询spu对象
Spu spu = this.goodsClient.querySpuById(spuId);
// 查询spudetail
SpuDetail spuDetail = this.goodsClient.querySpuDetailBySpuId(spuId);
// 查询sku集合
List skus = this.goodsClient.querySkusBySpuId(spuId);
// 查询分类
List cids = Arrays.asList(spu.getCid1(), spu.getCid2(), spu.getCid3());
List names = this.categoryClient.queryNameByIds(cids);
List