项目准备
- 创建easymall-common-resources工程,里面包含了项目需要使用的javabean和util工具类,在其他工程里导入此依赖就可以复用
- 创建easymall-parent的pom工程
- 继承springboot
- springcloud依赖
- 持久层(jdbc,mysql,mybatis)mybatis依赖
- rediscluster
- ES
Rediscluster
引入了rediscluster集群,实现了redis集群的高可用,后面会依赖这个类
@Configuration
@ConfigurationProperties(prefix="redis.cluster")
public class ClusterConfig {
//四个属性
private List<String> nodes;
private Integer maxIdle;
private Integer maxTotal;
private Integer minIdle;
//初始化读取属性后,创建bean对象JedisCluster
@Bean
public JedisCluster initJedisCluster(){
//收集节点信息
Set<HostAndPort> set=new HashSet<HostAndPort>();
for(String node:nodes){
//每次循环拿到ip:port
String host=node.split(":")[0];
int port=Integer.parseInt(node.split(":")[1]);
set.add(new HostAndPort(host, port));
}
//配置对象
GenericObjectPoolConfig config=new GenericObjectPoolConfig();
config.setMaxTotal(maxTotal);
config.setMaxIdle(maxIdle);
config.setMinIdle(minIdle);
//创建JedisCluster对象
return new JedisCluster(set,config);
}
public List<String> getNodes() {
return nodes;
}
public void setNodes(List<String> nodes) {
this.nodes = nodes;
}
public Integer getMaxIdle() {
return maxIdle;
}
public void setMaxIdle(Integer maxIdle) {
this.maxIdle = maxIdle;
}
public Integer getMaxTotal() {
return maxTotal;
}
public void setMaxTotal(Integer maxTotal) {
this.maxTotal = maxTotal;
}
public Integer getMinIdle() {
return minIdle;
}
public void setMinIdle(Integer minIdle) {
this.minIdle = minIdle;
}
}
产品工程
- springboot
- mybatis
- redis
application.properties
此工程在服务中心注册,服务名称为productservice,zuul转发时要找这个服务名称
server.port=10001
server.contextPath=/
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql:///easydb?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
mybatis.typeAliasesPackage=com.jt.common.pojo
mybatis.mapperLocations=classpath:mapper/*.xml
mybatis.configuration.mapUnderscoreToCamelCase=true
mybatis.configuration.cacheEnabled=false
spring.application.name=productservice
eureka.client.serviceUrl.defaultZone=http://localhost:8888/eureka
启动类
此工程是一个服务
@SpringBootApplication
@MapperScan("cn.tedu.product.mapper")
@EnableEurekaClient
public class StarterProductService {
public static void main(String[] args) {
SpringApplication.run(StarterProductService.class, args);
}
}
分页查询
Request URL:http://www.easymall.com/product-list.html?page=1&rows=5
controller
@RestController
@RequestMapping("/product/manage")
public class ProductController {
@Autowired
private ProductService productService;
//分页查询功能
@RequestMapping("pageManage")
public EasyUIResult queryPageProducts(Integer page,Integer rows){
//page:页数
//rows:每页的条数
//在业务层封装EasyYIResult对象
EasyUIResult result= productService.queryPageProducts(page,rows);
return result;
}
}
service
计算出总页数,从数据库中查出当前页的数据
@Service
public class ProductService {
@Autowired
private ProductMapper productMapper;
public EasyUIResult queryPageProducts(Integer page, Integer rows) {
//准备EasyUIResult
EasyUIResult result=new EasyUIResult();
//total
int total=productMapper.selectProductCount();
result.setTotal(total);
//rows List<Product>对象
int start=(page-1)*rows;
List<Product> pList=productMapper.selectProductList(start,rows);
result.setRows(pList);
return result;
}
}
mapper
在有多个参数时,在参数前添加@Param注解
public interface ProductMapper {
public int selectProductCount();
//在方法中存在多个参数,使用Param注解,实现parameter的翻译导入
public List<Product> selectProductList(@Param("start")int start, @Param("rows")Integer rows);
}
mapper.xml
<mapper namespace="cn.tedu.product.mapper.ProductMapper">
<select id="selectProductCount" resultType="int">
select count(product_id) from t_product;
</select>
<select id="selectProductList" resultType="Product">
select * from t_product limit #{start},#{rows};
</select>
</mapper>
查看商品详情
Request URL:http://www.easymall.com/product-info.html?productId=05e20c1a-0401-4c0a-82ab-6fb0f37db397
新增功能:在商家修改商品时,用户从redis缓存中获取数据,为了避免数据库和redis数据不一致,在商家更新商品时,引入了锁.
controller
@RequestMapping("item/{productId}")
public Product queryProduct(@PathVariable String productId){
return productService.queryProduct(productId);
}
service
@Autowired
private JedisCluster cluster;
public Product queryProduct(String productId) {
//引入缓存判断更新锁的存在
//生成当前逻辑中需要的key
//锁的key product_update_productId+.lock
String updateLock="product_update_"+productId+".lock";
String productKey="product_"+productId;
//判断锁是否存在
try{
if(cluster.exists(updateLock)){
//锁存在,有人更新数据,缓存就不能使用
return productMapper.selectProductById(productId);
}else{
//锁不存在,判断缓存逻辑
if(cluster.exists(productKey)){
//src就是json字符串
//value class对象解析的类反射对象
return MapperUtil.MP.readValue(cluster.get(productKey), Product.class);
}else{
//查持久层
Product product=productMapper.selectProductById(productId);
//加入缓存 维护少量数据的写入缓存
cluster.setex(productKey,60*60*2,MapperUtil.MP.writeValueAsString(product));
return product;
}
}
}catch(Exception e){
e.printStackTrace();
return null;
}
}
mapper
public Product selectProductById(String productId);
<select id="selectProductById" parameterType="String" resultType="Product">
select * from t_product where product_id=#{productId};
</select>
新增商品
Request URL:http://www.easymall.com/products/save
controller
@RequestMapping("save")
public SysResult deployProduct(Product product){
//判断成功失败逻辑
try{
productService.deployProduct(product);
//表示成功200 其他表示失败
return SysResult.ok();
//{"status":200,"msg":"ok","data":null}
}catch(Exception e){
e.printStackTrace();
return SysResult.build(201, e.getMessage(), null);
}
}
service
public void deployProduct(Product product) {
//补齐数据uuid保存productId
product.setProductId(UUID.randomUUID().toString());
productMapper.insertProduct(product);
}
mapper
public void insertProduct(Product product);
<insert id="insertProduct" parameterType="Product">
insert into t_product
(
product_id,
product_name,
product_price,
product_imgurl,
product_num,
product_description,
product_category
)
values
(
#{productId},
#{productName},
#{productPrice},
#{productImgurl},
#{productNum},
#{productDescription},
#{productCategory}
)
</insert>
修改商品
新增功能:用户在取出一个数据时,先看redis中有没有此商品数据
如果商家想要修改产品的价格时,修改了价格,但用户在redis中拿到的是原数据,数据不一致
- 在商家修改商品时,加锁
- 删除缓存productKey
- 更新数据库
- 释放锁
controller
@RequestMapping("update")
public SysResult renewProduct(Product product){
try{
productService.renewProduct(product);
//成功调用返回200 result
return SysResult.ok();
}catch(Exception e){
e.printStackTrace();
return SysResult.build(201, e.getMessage(), null);
}
}
service
public void renewProduct(Product product) {
/*1.加锁(超时时间,10分钟)
*2.删除缓存productKey
*3.更新数据库
*4.释放锁
*/
String updateLock="product_update_"+product.getProductId()+".lock";
String productKey="product_"+product.getProductId();
cluster.setex(updateLock, 60*5,"");
cluster.del(productKey);
productMapper.updateProductById(product);
cluster.del(updateLock);
}
mapper
<update id="updateProductById" parameterType="Product">
update t_product set
product_name = #{productName},
product_price = #{productPrice},
product_imgurl = #{productImgurl},
product_num = #{productNum},
product_description = #{productDescription},
product_category = #{productCategory}
where product_id=#{productId}
</update>
用户工程
application.xml
在zuul的配置中添加如下配置,转发的路径和服务名称是一对
添加注册服务的地址
zuul.routes.user.path=/zuul-user/**
zuul.routes.user.serviceId=userservice
eureka.client.serviceUrl.defaultZone=http://localhost:8888/eureka
- 端口号10003
- 服务名称userservice 与zuul中配置对应
server.port=10003
server.contextPath=/
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql:///easydb?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
mybatis.typeAliasesPackage=com.jt.common.pojo
mybatis.mapperLocations=classpath:mapper/*.xml
mybatis.configuration.mapUnderscoreToCamelCase=true
mybatis.configuration.cacheEnabled=false
spring.application.name=userservice
eureka.client.serviceUrl.defaultZone=http://localhost:8888/eureka
注册校验用户名
Request URL:http://www.easymall.com/user/checkUserName
在用户注册输入完账号后进行校验,应该是用了onblur事件,当鼠标离开时,触发js事件,对controller地址进行了请求
controller
@RequestMapping("checkUserName")
public SysResult checkUserName(String userName){
//控制层判断查询结果可用不可用
int exists=userService.checkUserExists(userName);
// 1或0 1表示不可用:201 0表示可用:200
if(exists==1){
return SysResult.build(201, "不可用", null);
}else{
return SysResult.ok();
}
}
service
public int checkUserExists(String userName) {
//1存在 0不存在
return userMapper.selectUserCountByUserName(userName);
}
mapper
public int selectUserCountByUserName(String userName);
<select id="selectUserCountByUserName" resultType="int" parameterType="String">
select count(user_id) from t_user
where user_name=#{userName}
</select>
用户注册
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-redis</artifactId>
</dependency>
controller
@RequestMapping("save")
public SysResult doRegister(User user){
try{
userService.doRegister(user);
return SysResult.ok();
}catch(Exception e){
e.printStackTrace();
return SysResult.build(201, "注册失败", null);
}
}
service
public void doRegister(User user) {
//补齐一个userId
user.setUserId(UUID.randomUUID().toString());
//对密码进行加密
//对密码加密方式 安全可以使用md5加盐
//user.getUserPassword
user.setUserPassword(MD5Util.md5(user.getUserPassword()));
userMapper.insertUser(user);
}
mapper
public void insertUser(User user);
<insert id="insertUser" parameterType="User">
insert into t_user
(user_id,user_name,user_password,user_nickname,user_email,user_type)
values
(#{userId},#{userName},#{userPassword},#{userNickname},#{userEmail},#{userType})
</insert>
用户登录
Request URL::http://www.easymall.com/user/login?userName=admin&userPassword=admin
登陆成功时,在redis里面存入一对key-value,key是EM_TICKET+currentTime+userId,所以每次都不同,生命为两小时(自定义的)
在cookie中定义一个携带ticket的头的信息EM_TICKET
- name:EM_TICKET value:EM_TICKET15694943098160668c2be-c492-421e-a44c-ab0e990f70ea
新增功能:
- 增加了redis,为了让用户更快的访问一些热点数据,不需要每次都进数据库,引入了redis技术不能多客户端同时登陆
- 每次登陆,都生成key,存入redis,当下一次登陆时检测已经有登陆了,就把redis中的key及相关数据移除
controller
@RequestMapping("login")
public SysResult doLogin(User user,HttpServletRequest req,HttpServletResponse res){
//通过业务层返回的数据ticket是否为空判断
//登陆逻辑是否正常 ""正常值
String ticket=userService.doLogin(user);
if("".equals(ticket)){
//登录失败
return SysResult.build(201, "", null);
}else{
//ticket不为空,说明登陆成功
//返回成功信息之前,要在cookie中定义一个携带ticket的头的信息EM_TICKET
CookieUtils.setCookie(req, res, "EM_TICKET", ticket);
return SysResult.ok();
}
}
service
public String doLogin(User user) {
//判断登陆权限校验 select where username and password
//加密
user.setUserPassword(MD5Util.md5(user.getUserPassword()));
User exits = userMapper.selectUserByUserNameAndPassword(user);
String loginKey="login_"+user.getUserName();
String newTicket="";
//判断loginKey是不是存在
if(jedis.exists(loginKey)){
//曾经有人登陆过
String oldTicket=jedis.get(loginKey);
jedis.del(jedis.get(loginKey));
jedis.del(oldTicket);
}
//正常设置newTicket-userJson
try{
newTicket="EM_TICKET"+System.currentTimeMillis()+exits.getUserId();
String userJson=MapperUtil.MP.writeValueAsString(exits);
jedis.setex(newTicket, 60*60*2, userJson);
//设置有效的ticket使用
jedis.setex(loginKey,60*60*2,newTicket);
}catch(Exception e){
e.printStackTrace();
return "";
}
return newTicket;
}
mapper
public User selectUserByUserNameAndPassword(User user);
<select id="selectUserByUserNameAndPassword" parameterType="User" resultType="User">
select * from t_user where user_name=#{userName} and user_password=#{userPassword};
</select>
登陆状态的保存
Request URL:http://www.easymall.com/user/query/EM_TICKET15694943098160668c2be-c492-421e-a44c-ab0e990f70ea
在登陆时,如果redis中已经存有cookie中的数据,直接在redis中获取用户账号密码信息,因为redis中的数据只能存两小时,所以超过两小时就需要重新登录
新增功能:续约:当生命剩下小于30分钟,就延长两小时
controller
@RequestMapping("query/{ticket}")
public SysResult queryTicket(@PathVariable String ticket){
String userJson=userService.queryTicket(ticket);
if(userJson==null){
//超过两小时
return SysResult.build(201, "用户超时", null);
}else{
//登录状态可用
return SysResult.build(200, "登陆状态可用", userJson);
}
}
service
public String queryTicket(String ticket) {
//判断剩余时间
Long leftTime=jedis.pttl(ticket);
if(leftTime<1000*60*30){
//小于30分钟,续约一小时
jedis.pexpire(ticket, leftTime+1000*60*60);
}
return jedis.get(ticket);
}
购物车工程
application.xml
server.port=10005
server.contextPath=/
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql:///easydb?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
mybatis.typeAliasesPackage=com.jt.common.pojo
mybatis.mapperLocations=classpath:mapper/*.xml
mybatis.configuration.mapUnderscoreToCamelCase=true
mybatis.configuration.cacheEnabled=false
spring.application.name=cartservice
eureka.client.serviceUrl.defaultZone=http://localhost:8888/eureka
启动类
购物车需要调用产品工程,所以需要ribbon
@SpringBootApplication
@MapperScan("cn.tedu.cart.mapper")
@EnableEurekaClient
public class StarterCartService {
public static void main(String[] args) {
SpringApplication.run(StarterCartService.class, args);
}
//ribbon 创建一个可以负载均衡的访问服务对象
@Bean
@LoadBalanced
public RestTemplate initRestTemplate(){
return new RestTemplate();
}
}
查询购物车
URL中userId的获得方式:
查看源码发现,前段通过cookie值比对,从redis中获得该用户的数据,然后从数据中得到userId
Request URL:http://www.easymall.com/cart/query?userId=0668c2be-c492-421e-a44c-ab0e990f70ea
通过user_id,得到该用户购物车的商品id,名字,图片和数量
数据库结构
- user_id
- product_id
- product_image
- product_name
- num
购物车数据结构是反三范式,因为商品id不能确定是谁的购物车,用户id不能确定有哪些商品,所以存在有两个’主键’
controller
// 查询我的购物车
@RequestMapping("query")
public List<Cart> queryMyCarts(String userId) {
return cartService.queryMyCarts(userId);
}
service
public List<Cart> queryMyCarts(String userId) {
return cartMapper.selectCartsByUserId(userId);
}
mapper
public List<Cart> selectCartsByUserId(String userId);
<select id="selectCartsByUserId" parameterType="String" resultType="Cart">
select * from t_cart where user_id=#{userId};
</select>
新增商品到购物车
增加一个商品到购物车中,如果购物车已经存在此商品,就修改购物车的数量
如果购物车没有存在此商品,就获取此商品的信息,放在购物车里(获取商品信息是商品工程中的服务)
controller
@RequestMapping("save")
public SysResult saveMyCart(Cart cart) {
try {
cartService.saveMyCart(cart);
return SysResult.ok();
} catch (Exception e) {
e.printStackTrace();
return SysResult.build(201, "新增购物车失败", null);
}
}
service
@Autowired
private RestTemplate client;
public void saveMyCart(Cart cart) {
//查询已有
Cart exist=cartMapper.selectExistByUserIdAndProductId(cart);
if(exist!=null){
//已存在 更新num的数量
//更新内存对象数据
cart.setNum(cart.getNum()+exist.getNum());
cartMapper.updateCartNumByUserIdAndProductId(cart);
}else{
//没数据 新增购物车
//获取商品服务返回的数据,封装补齐cart对象
Product product=client.getForObject("http://productservice/product/manage/item/"+cart.getProductId(), Product.class);
cart.setProductImage(product.getProductImgurl());
cart.setProductName(product.getProductName());
cart.setProductPrice(product.getProductPrice());
//调用insert语句
cartMapper.insertCart(cart);
}
}
mapper
public List<Cart> selectCartsByUserId(String userId);
public void insertCart(Cart cart);
public void updateCartNumByUserIdAndProductId(Cart cart);
public Cart selectExistByUserIdAndProductId(Cart cart);
<select id="selectExistByUserIdAndProductId" parameterType="Cart" resultType="Cart">
select * from t_cart where user_id=#{userId} and product_id=#{productId};
</select>
<update id="updateCartNumByUserIdAndProductId" parameterType="Cart">
update t_cart set num=#{num} where user_id=#{userId} and product_id=#{productId};
</update>
<insert id="insertCart" parameterType="Cart">
insert into t_cart
(
user_id,
product_id,
product_price,
product_image,
product_name,
num
)
values
(
#{userId},
#{productId},
#{productPrice},
#{productImage},
#{productName},
#{num}
)
</insert>
购物车数量更新
controller
@RequestMapping("update")
public SysResult updateMyCartNum(Cart cart){
try{
cartService.updateMyCartNum(cart);
return SysResult.ok();
}catch(Exception e){
e.printStackTrace();
return SysResult.build(201, "更新失败", null);
}
}
service
public void updateMyCartNum(Cart cart) {
cartMapper.updateCartNumByUserIdAndProductId(cart);
}
mapper
public void updateCartNumByUserIdAndProductId(Cart cart);
<update id="updateCartNumByUserIdAndProductId" parameterType="Cart">
update t_cart set num=#{num} where user_id=#{userId} and product_id=#{productId};
</update>
购物车删除
controller
//购物车的删除
@RequestMapping("delete")
public SysResult deleteMyCart(Cart cart){
try{
cartService.deleteMyCart(cart);
return SysResult.ok();
}catch(Exception e){
e.printStackTrace();
return SysResult.build(201, "删除失败", null);
}
}
service
public void deleteMyCart(Cart cart) {
cartMapper.deleteCartByUserIdAndProductId(cart);
}
mapper
public void deleteCartByUserIdAndProductId(Cart cart);
<delete id="deleteCartByUserIdAndProductId" parameterType="Cart">
delete from t_cart where user_id=#{userId} and product_id=#{productId}
</delete>
订单工程
订单工程的数据用到了mycat,实现存储的分布式 mycat用的是ER分片表,相关的数据会分在一个分片中
application.xml
端口是10006
server.port=10006
server.contextPath=/
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://10.42.141.101:8066/mstest?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
mybatis.typeAliasesPackage=com.jt.common.pojo
mybatis.mapperLocations=classpath:mapper/*.xml
mybatis.configuration.mapUnderscoreToCamelCase=true
mybatis.configuration.cacheEnabled=false
spring.application.name=orderservice
eureka.client.serviceUrl.defaultZone=http://localhost:8888/eureka
查询订单
controller
//查询我的订单
@RequestMapping("query/{userId}")
public List<Order> queryMyOrders(@PathVariable String userId){
return orderService.queryMyOrders(userId);
}
service
public List<Order> queryMyOrders(String userId) {
return orderMapper.selectOrdersByUserId(userId);
}
mapper
List<Order> selectOrdersByUserId(String userId);
mybatis查询两次,没有用关联查询的方式
<resultMap type="Order" id="orderRM">
<!-- order表格字段数据id -->
<id property="orderId" column="order_id" />
<!-- collection封装对多关系 -->
<collection property="orderItems" javaType="ArrayList"
ofType="OrderItem" column="order_id" select="selectOrderItemByOrderId">
</collection>
</resultMap>
<select id="selectOrdersByUserId" parameterType="String"
resultMap="orderRM">
select * from t_order where user_id=#{userId}
</select>
<select id="selectOrderItemByOrderId" parameterType="String"
resultType="OrderItem">
select * from t_order_item where order_id=#{orderId}
</select>
新增订单
controller
//新增订单
@RequestMapping("save")
public SysResult saveOrder(Order order){
try{
orderService.saveOrder(order);
return SysResult.ok();
}catch(Exception e){
e.printStackTrace();
return SysResult.build(201, "", null);
}
}
service
public void saveOrder(Order order) {
//补齐数据orderId orderTime orderPaystate
order.setOrderId(UUID.randomUUID().toString());
order.setOrderTime(new Date());
order.setOrderPaystate(0);
orderMapper.insertOrder(order);
}
mapper
void insertOrder(Order order);
<!-- mybatis 支持数据库mysql的多条insert 简写插入 mycat不支持mysql多条新增简写 -->
<insert id="insertOrder" parameterType="Order">
<!-- 新增主表 -->
insert into t_order (
order_id,user_id,order_money,
order_paystate,order_time,order_receiverinfo)
values (
#{orderId},#{userId},#{orderMoney},
#{orderPaystate},#{orderTime},#{orderReceiverinfo});
<!-- 新增子表 foreach标签循环拼接insert语句 -->
<!-- for(OrderItem item:orderItems) -->
<foreach collection="orderItems" item="item">
insert into t_order_item
(
order_id,product_id,num,
product_name,product_price,product_image)
values (
#{orderId},#{item.productId},#{item.num},
#{item.productName},#{item.productPrice},
#{item.productImage});
</foreach>
</insert>
删除订单
controller
//删除
@RequestMapping("delete/{orderId}")
public SysResult deleteOrder(@PathVariable String orderId){
try{
orderService.deleteOrder(orderId);
return SysResult.ok();
}catch(Exception e){
e.printStackTrace();
return SysResult.build(201, "", null);
}
}
service
public void deleteOrder(String orderId) {
orderMapper.deleteOrderByOrderId(orderId);
}
mapper
void deleteOrderByOrderId(String orderId);
<delete id="deleteOrderByOrderId" parameterType="String">
delete from
t_order where order_id=#{orderId};
delete from t_order_item where
order_id=#{orderId};
</delete>
上传图片
controller
@RestController
public class ImgController {
@Autowired
private ImgService imgService;
//图片上传
@RequestMapping("/pic/upload")
public PicUploadResult picUpload(MultipartFile pic){
//{"url":"http://image.jt.com/upload/**"}
return imgService.picUpload(pic);
}
}
service
@Service
public class ImgService {
@Value("${diskPath}")
private String path;
@Value("${urlPath}")
private String urlPath;
public PicUploadResult picUpload(MultipartFile pic) {
//主要思路:将图片存储在本地C盘 返回url地址
/*
* 1.获取图片名称
* 2.判断图片合法(后缀名)
* 2.1成功:继续逻辑
* 2.2失败:Result error=1 return
* 3.根据图片的原名称生成一个路径的多级地址字符串
* /upload/1/d/3/e/3/d/3/3/ 原名称不变,对应的目录一个
* 4.生成磁盘路径file mkdir
* @Value("${path}")+dir
* 5.重命名文件 uuid.jpg
* 6.输出pic中的流数据到磁盘中形成一个图片文件
* 7.拼接url地址 @Value("${urlPath}")+dir+重命名
* 8.数据赋值返回
*/
//准备一个返回的对象
PicUploadResult result =new PicUploadResult();
try{
//文件名称校验
//拿到原名 **.jpg
String oldName=pic.getOriginalFilename();
//截取后缀名.jpg .png .bmp
String extName=oldName.substring(oldName.lastIndexOf("."));
//判断后缀合法
if(!extName.matches(".(png|jpg|git)$")){
result.setError(1);
return result;
}
//使用工具类生成一个多级路径地址,以upload开始的
String dir="/"+UploadUtil.getUploadPath(oldName, "upload")+"/";
//创建文件夹,文件夹可能存在,也可能不存在
File _dir=new File(path+dir);
if(!_dir.exists()){
//文件不存在
_dir.mkdirs();
}
//重命名文件
String fileName=UUID.randomUUID().toString()+extName;
//xxx.jpg
//将这个名称作为文件存储图片数据
pic.transferTo(new File(path+dir+fileName));
//result封装url地址
String url=urlPath+dir+fileName;
return result;
}catch(Exception e){
e.printStackTrace();
result.setError(1);
return result;
}
}
}
搜索工程
搜索商品
pom.xml
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>5.5.2</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>transport</artifactId>
<version>5.5.2</version>
</dependency>
application.properties
server.port=10007
server.contextPath=/
spring.application.name=searchservice
eureka.client.serviceUrl.defaultZone=http://localhost:8888/eureka
easymall.es.cluster-name=elasticsearch
easymall.es.nodes=10.42.141.101:9300,10.42.145.240:9300,10.42.66.53:9300
ESConfig
从配置文件中读取属性,初始化TransportClient
@Configuration
@ConfigurationProperties(prefix="easymall.es")
public class ESConfig {
//集群名称
private String clusterName;
//节点连接node信息
private List<String> nodes;
//初始化方法对象
@Bean
public TransportClient initTransportClient(){
//setting对象,包装clusterName
Settings set=Settings.builder().put("cluster.name","elasticsearch").build();
TransportClient client=new PreBuiltTransportClient(set);
try {
for(String node:nodes){
String host=node.split(":")[0];
int port=Integer.parseInt(node.split(":")[1]);
InetSocketTransportAddress ista1=new InetSocketTransportAddress(InetAddress.getByName(host), port);
client.addTransportAddress(ista1);
}
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return client;
}
public String getClusterName() {
return clusterName;
}
public void setClusterName(String clusterName) {
this.clusterName = clusterName;
}
public List<String> getNodes() {
return nodes;
}
public void setNodes(List<String> nodes) {
this.nodes = nodes;
}
}
controller
@RestController
public class ESSearchController {
@Autowired
private ESSearchService searchService;
@RequestMapping("search/manage/query")
public List<Product> searchByName(String query,Integer page,Integer rows){
return searchService.searchByName(query,page,rows);
}
}
service
@Service
public class ESSearchService {
@Autowired
private TransportClient client;
public List<Product> searchByName(String text, Integer page, Integer rows) {
//创建query对象,封装查询条件MatchQuery
MatchQueryBuilder query=QueryBuilders.matchQuery("productName", text);
//根据分页条件创建请求request
SearchRequestBuilder request=client.prepareSearch("easymall");
request.setQuery(query).setFrom((page-1)*rows).setSize(rows);
//发送请求,获取查询结果集
SearchResponse response=request.get();
//解析封装了hits的结果集,每个元素中使用source反序列化
SearchHit[] hits=response.getHits().getHits();
//准备一个空list
List<Product> pList=new ArrayList<Product>();
for(SearchHit hit:hits){
String pJson=hit.getSourceAsString();
try{
Product product=MapperUtil.MP.readValue(pJson, Product.class);
pList.add(product);
}catch(Exception e){
e.printStackTrace();
return null;
}
}
return pList;
}
}