miracle just wanna be better

easymall项目

2019-09-19
miracle

项目准备

  • 创建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中拿到的是原数据,数据不一致

  1. 在商家修改商品时,加锁
  2. 删除缓存productKey
  3. 更新数据库
  4. 释放锁

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;
	}
}

上一篇 springcloud

下一篇 redis

Comments

Content