京淘实训Day19-京淘订单业务实现

1.关于虚拟机IP地址展现问题

1.1 故障说明

有时重启虚拟机之后,可能会遇到IP地址不能正常展现的现象.
故障原因: CentOS7 系统中 1.netWorkManager 2.network
解决方案:
1.关闭netWorkManager 网卡 systemctl stop NetworkManager
2.禁用netWorkManager 网卡 systemctl disable NetworkManager
3.重启network service network restart

2.京淘订单业务实现

2.1 订单项目创建

2.1.1 创建项目

京淘实训Day19-京淘订单业务实现_第1张图片

2.1.2 选择jar包

京淘实训Day19-京淘订单业务实现_第2张图片

2.1.3 添加继承/依赖/插件

	<parent>
		<groupId>com.jt.huanan</groupId>
		<artifactId>jt</artifactId>
		<version>0.0.1-SNAPSHOT</version>
	</parent>
	
	<!--2.添加依赖 jt-common -->
	<dependencies>
		<dependency>
			<groupId>com.jt.huanan</groupId>
			<artifactId>jt-common</artifactId>
			<version>0.0.1-SNAPSHOT</version>
		</dependency>
	</dependencies>

	<!--3.添加插件 -->
	<!--build是负责项目打包部署 一般将项目开发完成之后,需要进行服务器部署(Linux) -->
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

2.1.4 订单表设计

京淘实训Day19-京淘订单业务实现_第3张图片

2.1.5 导入订单POJO对象

说明:将POJO对象导入到common中.
京淘实训Day19-京淘订单业务实现_第4张图片

2.1.6 导入订单项目

京淘实训Day19-京淘订单业务实现_第5张图片

2.1.7 修改YML配置文件

	server:
  port: 8095
  servlet:
    context-path: /
spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    #如果需要项目发布,则数据库地址必须配置远程数据库
    url: jdbc:mysql://127.0.0.1:3306/jtdb?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
    username: root
    password: root
  
  #配置视图解析器
  mvc:
    view:
      prefix: /WEB-INF/views/
      suffix: .jsp
      
#mybatis-plush配置
mybatis-plus:
  type-aliases-package: com.jt.pojo
  mapper-locations: classpath:/mybatis/mappers/*.xml
  configuration:
    map-underscore-to-camel-case: true

#日志记录 输出数据库的日志信息.
logging:
  config: classpath:logging-config.xml
  level: 
    com.jt.mapper: debug
    
dubbo:
  scan:
    basePackages: com.jt    #指定dubbo的包路径
  application:
    name: provider-order     #指定服务名称(必须指定)
  registry:
    address: zookeeper://192.168.126.129:2181   #?backup=192.168.126.129:2182,192.168.126.129:2183
  protocol:  #指定协议
    name: dubbo  #使用dubbo协议(tcp-ip)  web-controller直接调用sso-Service
    port: 20883 #每个服务都应该有自己特定的端口  

2.2 订单确认页面跳转

2.2.1 页面分析

1).url分析
京淘实训Day19-京淘订单业务实现_第6张图片
2).页面展现信息说明
京淘实训Day19-京淘订单业务实现_第7张图片

2.2.2 编辑OrderController

	package com.jt.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import com.alibaba.dubbo.config.annotation.Reference;
import com.jt.pojo.Cart;
import com.jt.service.DubboCartService;
import com.jt.thread.UserThreadLocal;

@Controller
@RequestMapping("/order")
public class OrderController {
	
	@Reference
	private DubboCartService cartService;
	
	
	/**
	 * 跳转到订单确认页面
	 * 1.url:http://www.jt.com/order/create.html
	 * 2.请求参数: 无
	 * 3.返回值结果: 订单页面逻辑名称
	 * 4.页面取值信息: ${carts}  获取购物车记录
	 */
	@RequestMapping("/create")
	public String create(Model model) {
		
		//1.获取用户id
		Long userId = UserThreadLocal.get().getId();
		List<Cart> carts = cartService.findCartList(userId);
		//2.封装页面数据信息
		model.addAttribute("carts", carts);
		
		return "order-cart";	//跳转指定的页面
	}
	
	
	
	
}

2.2.3页面效果

京淘实训Day19-京淘订单业务实现_第8张图片

2.3 SpringMVC参数传递说明

2.3.1 简单数据类型接参

1).页面信息

	ID: <input  name="id"  type="text"  value="101" />
	年龄: <input  name="age"  type="text"  value="18" />

2).Controller中参数接收说明

	public void aaa(Long id,Integer age) {
		
	}

2.3.2 利用对象接收数据

1).页面信息

	ID: <input  name="id"  type="text"  value="101" />
	年龄: <input  name="age"  type="text"  value="18" />

2).Controller中参数接收说明

	public void aaa(User user) {
		
	}

赋值时name=id 调用对象的setId(…)方法实现为对象赋值.

2.3.3 利用对象的引用传递参数

问题: 有时在进行参数传递时,可能会出现重名数据. 如何解决??? 采用对象的引用

	用户ID: <input  name="id"  type="text"  value="101" />
	用户名称: <input  name="name"  type="text"/>
	宠物名称: <input  name="dog.name"  type="text"/>

2).Controller中参数接收说明

	public void aaa(User user) {
		
	}	

京淘实训Day19-京淘订单业务实现_第9张图片
一般条件下:如果参数中有重名属性,则通过 对象引用的形式实现数据传递.

2.3.4 订单数据传递说明

1).页面描述信息

	<form id="orderForm" class="hide">
		<input type="hidden" name="paymentType" value="1"/>
		<c:forEach items="${carts}" var="cart" varStatus="status">
			<c:set var="totalPrice"  value="${ totalPrice + (cart.itemPrice * cart.num)}"/>
			<input type="hidden" name="orderItems[${status.index}].itemId" value="${cart.itemId}"/>
			<input type="hidden" name="orderItems[${status.index}].num" value="${cart.num }"/>
			<input type="hidden" name="orderItems[${status.index}].price" value="${cart.itemPrice}"/>
			<input type="hidden" name="orderItems[${status.index}].totalFee" value="${cart.itemPrice * cart.num}"/>
			<input type="hidden" name="orderItems[${status.index}].title" value="${cart.itemTitle}"/>
			<input type="hidden" name="orderItems[${status.index}].picPath" value="${cart.itemImage}"/>
		</c:forEach>
		<input type="hidden" name="payment" value="false" maxFractionDigits="2" minFractionDigits="2" value="${totalPrice/100 }"/>"/>
		<input type="hidden" name="orderShipping.receiverName" value="陈晨"/>
		<input type="hidden" name="orderShipping.receiverMobile" value="13800807944"/>
		<input type="hidden" name="orderShipping.receiverState" value="北京"/>
		<input type="hidden" name="orderShipping.receiverCity" value="北京"/>
		<input type="hidden" name="orderShipping.receiverDistrict" value="海淀区"/>
		<input type="hidden" name="orderShipping.receiverAddress" value="清华大学"/>
	</form>

2).Order对象的定义
京淘实训Day19-京淘订单业务实现_第10张图片

2.4 订单新增业务

2.4.1页面分析

1).url地址
京淘实训Day19-京淘订单业务实现_第11张图片

2).参数提交
京淘实训Day19-京淘订单业务实现_第12张图片
3).返回值分析

			jQuery.ajax( {
			type : "POST",
			dataType : "json",
			url : "/order/submit",
			data : $("#orderForm").serialize(),
			cache : false,
			success : function(result) {
				if(result.status == 200){
					location.href = "/order/success.html?id="+result.data;
				}else{
					$("#submit_message").html("订单提交失败,请稍后重试...").show();
				}
			},
			error : function(error) {
				$("#submit_message").html("亲爱的用户请不要频繁点击, 请稍后重试...").show();
			}
		});

2.4.2 编辑OrderController

	@RequestMapping("/submit")
	@ResponseBody
	public SysResult submit(Order order) {
		//1.获取userId信息
		Long userId = UserThreadLocal.get().getId();
		order.setUserId(userId);
		//2.完成订单入库操作
		String orderId = orderService.insertOrder(order);
		//3.返回数据
		return SysResult.success(orderId);
	}

2.4.3 编辑OrderService

	@Service
public class OrderServiceImpl implements DubboOrderService {
	
	@Autowired
	private OrderMapper orderMapper;
	@Autowired
	private OrderShippingMapper orderShippingMapper;
	@Autowired
	private OrderItemMapper orderItemMapper;
	
	
	/**
	 * order:  1.order模块信息    2.orderItem    3.订单物流信息
	 * 应该实现三张表同时入库.
	 */
	@Transactional //控制事务
	@Override
	public String insertOrder(Order order) {
		//1.生成orderID
		String orderId = ""+order.getUserId() + System.currentTimeMillis();
		
		//2.定义入库时间
		Date date = new Date(); //如果项目中有时间获取的工具API,则使用该API,如果没有才使用new date();
		
		//3.实现订单入库
		order.setOrderId(orderId).setStatus(1)
			 .setCreated(date).setUpdated(date);
		orderMapper.insert(order);
		
		//4.完成订单物流入库
		OrderShipping shipping = order.getOrderShipping();
		shipping.setOrderId(orderId);
		shipping.setCreated(date);
		shipping.setUpdated(date);
		orderShippingMapper.insert(shipping);
		
		//5.完成订单商品入库
		List<OrderItem> orderItems = order.getOrderItems();
		for (OrderItem orderItem : orderItems) {
			orderItem.setOrderId(orderId)
					 .setCreated(date)
					 .setUpdated(date);
			orderItemMapper.insert(orderItem);
		}
		
		System.out.println("订单入库成功!!!!!");
		return orderId;
	}
}

2.4 订单查询实现

2.4.1 页面分析

1).url分析: http://www.jt.com/order/success.html?id=71593670116040
京淘实训Day19-京淘订单业务实现_第13张图片

2).跳转页面 success.jsp页面
3).页面取值数据: ${order.orderId}
4).业务逻辑: 通过orderId 查询订单的全部信息.

2.4.2 编辑OrderController

	@RequestMapping("/success")
	public String findOrderById(String id,Model model) {
		
		Order order = orderService.findOrderById(id);
		model.addAttribute("order", order);
		return "success";
	}

2.4.3 编辑OrderService

	//1.order订单信息   2.orderItems   3.orderShipping
	@Override
	public Order findOrderById(String id) {
		
		Order order = orderMapper.selectById(id);
		OrderShipping shipping = orderShippingMapper.selectById(id);
		QueryWrapper<OrderItem> queryWrapper = new QueryWrapper<>();
		queryWrapper.eq("order_id", id);
		List<OrderItem> list = orderItemMapper.selectList(queryWrapper);
		order.setOrderItems(list).setOrderShipping(shipping);
		return order;
	}

2.4.4 查询效果展现

京淘实训Day19-京淘订单业务实现_第14张图片

2.5 订单超时实现状态修改

2.5.1业务说明

说明:当订单入库之后,如果30分钟用户没有完成付款操作,则将订单的状态信息由1未付款改为6交易关闭.
如何实现: 单独开启一个线程,每隔1分钟查询一次是否有超时订单.

2.5.2Quartz框架说明

Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它可以与J2EE与J2SE应用程序相结合也可以单独使用。Quartz可以用来创建简单或为运行十个,百个,甚至是好几万个Jobs这样复杂的程序。Jobs可以做成标准的Java组件或 EJBs。Quartz的最新版本为Quartz 2.3.2。

京淘实训Day19-京淘订单业务实现_第15张图片
组件说明:
1.Job 用户自定义的任务.
2.JobDetail 将用户封装之后的结果.
3.调度器 负责任务的协调服务.
4.触发器 当接收调度器的指令后,开启线程执行任务.

2.5.3 引入jar包文件

	<!--添加Quartz的支持 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-quartz</artifactId>
		</dependency>

2.5.4 关于配置类说明

	@Configuration
public class OrderQuartzConfig {
	
	//定义任务详情
	@Bean
	public JobDetail orderjobDetail() {
		//指定job的名称和持久化保存任务
		return JobBuilder
				.newJob(OrderQuartz.class)	//引入自定义的任务
				.withIdentity("orderQuartz")//指定任务名称
				.storeDurably()
				.build();
	}
	//定义触发器
	@Bean
	public Trigger orderTrigger() {
		/*SimpleScheduleBuilder builder = SimpleScheduleBuilder.simpleSchedule()
				.withIntervalInMinutes(1)	//定义时间周期
				.repeatForever();*/
		//通过调度器,指定程序多久执行一次.
		//0 0/1 * * * ? 时间表达式 规定任务多久执行一次
		CronScheduleBuilder scheduleBuilder 
			= CronScheduleBuilder.cronSchedule("0 0/1 * * * ?");
		return TriggerBuilder
				.newTrigger()
				.forJob(orderjobDetail())
				.withIdentity("orderQuartz")
				.withSchedule(scheduleBuilder).build();
	}
}

2.5.5 执行定时任务

	//准备订单定时任务
@Component
public class OrderQuartz extends QuartzJobBean{
	
	@Autowired
	private OrderMapper orderMapper;
	
	/**
	 * 当规定的执行时间一到,触发器就会开启线程,执行指定的任务.
	 * 业务需求:
	 * 		要求将超时订单关闭. 要求30分钟   status 由1改为6
	 * 如何定义超时:
	 * 		now() - created  > 30分钟  订单超时
	 * 		created < now -30
	 * Sql:
	 * 		update tb_order set status = 6,updated = #{date}
	 * 		where created < (now -30) and status = 1;
	 * 
	 */
	@Override
	@Transactional
	protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
		//对时间进行计算
		Calendar calendar = Calendar.getInstance();	//实例化对象 获取当前时间
		calendar.add(Calendar.MINUTE, -30);
		Date timeOut = calendar.getTime();
		
		Order entity = new Order();
		entity.setStatus(6).setUpdated(new Date());
		UpdateWrapper<Order> updateWrapper = new UpdateWrapper<>();
		updateWrapper.eq("status", 1)
					 .lt("created",timeOut);
		orderMapper.update(entity, updateWrapper);
		System.out.println("定时任务执行成功!!!!");
	}
}

2.6 RedisTemplate

2.6.1RedisTemplate介绍

该工具API是SpringBoot专门专对redis开发的工具API. 主要的目的实现了数据的封装/整合.RedisTemplate是对Jedis工具API的整合.

2.6.2引入jar包

特点:开箱即用, 只要导入相关jar包,则就可以直接链接redis服务器.

	<!--SpringBoot整合redis  -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
		</dependency>

2.6.3 编辑YML配置文件

说明:需要通过yml配置文件的形式配置redis的节点信息.

	server:
  port: 8091
  servlet:
    context-path: /
spring:
  datasource:
    #引入druid数据源
    #type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/jtdb?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
    username: root
    password: root

  mvc:
    view:
      prefix: /WEB-INF/views/
      suffix: .jsp
  
  #SpringBoot整合Redis
  redis:
    cluster:  #配置redis的集群
      nodes: 192.168.126.129:7000,192.168.126.129:7001,192.168.126.129:7002,192.168.126.129:7003,192.168.126.129:7004,192.168.126.129:7005
    jedis:
      pool:   #准备redis链接池
        max-active: 1000  #最大链接数量
        max-idle: 10      #最大的空闲数量
        max-wait: 3       #最大的等待链接数量3
        min-idle: 3       #最小空闲数量    
#mybatis-plush配置
mybatis-plus:
  type-aliases-package: com.jt.pojo
  mapper-locations: classpath:/mybatis/mappers/*.xml
  configuration:
    map-underscore-to-camel-case: true

logging:
  level: 
    com.jt.mapper: debug
        
    
#关于Dubbo配置   
dubbo:
  scan:
    basePackages: com.jt    #指定dubbo的包路径
  application:              #应用名称
    name: provider-item     #一个接口对应一个服务名称
  registry:
    address: zookeeper://192.168.126.129:2181        #?backup=192.168.126.129:2182,192.168.126.129:2183
  protocol:  #指定协议
    name: dubbo  #使用dubbo协议(tcp-ip)  web-controller直接调用sso-Service
    port: 20881  #每一个服务都有自己特定的端口 不能重复. 

2.6.4 添加RedisTemplate的配置类

说明:该配置类可以不写.如果出现了中文乱码/序列化问题.则可以添加.

	package com.jt.config;

import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;

@Configuration
public class RedisTemplateConfig {
	
	@Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(factory);
        // 使用Jackson2JsonRedisSerialize 替换默认序列化
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);

        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);

        // 设置value的序列化规则和 key的序列化规则
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

   @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        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);
        // 配置序列化(解决乱码的问题),过期时间30秒
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
               // .entryTtl(Duration.ofSeconds(1800000))
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
                .disableCachingNullValues();

        RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
                .cacheDefaults(config)
                .build();
        return cacheManager;
    }
	
}

2.6.5 入门案例

	@SpringBootTest
public class TestRedisTemplate {

	//1.专门操作字符串
	@Autowired
	private StringRedisTemplate strTemplate;	//多
	//2.操作任意对象
	@Autowired									
	private RedisTemplate<String,Object> redisTemplate;

	//测试入门案例  
	@Test
	public void test01() {

		strTemplate.opsForValue();	//操作String数据类型
		strTemplate.opsForHash();	//操作hash数据类型
		strTemplate.opsForList();	//操作list数据类型
		strTemplate.opsForSet();	//操作set数据类型
		strTemplate.opsForZSet();	//操作zSet数据类型
	}

	//操作字符串类型 
	@Test
	public void test02() {

		strTemplate.opsForValue().set("aa", "aaa");
		String value = strTemplate.opsForValue().get("aa");
		System.out.println(value);
		//为数据添加超时时间
		strTemplate.opsForValue().set("aaaa","bbbb", Duration.ofDays(1));
		//setNX   如果key不存在时才赋值.
		strTemplate.opsForValue().setIfAbsent("bb", "bbb");
	}


	//操作对象
	@Test
	public void test03() {
		ItemDesc itemDesc = new ItemDesc();
		itemDesc.setItemId(101L).setItemDesc("AAAA")
				.setCreated(new Date())
				.setUpdated(itemDesc.getCreated());
		
		redisTemplate.opsForValue().set("itemDesc", itemDesc);
		ItemDesc itemDesc2 = (ItemDesc) redisTemplate.opsForValue().get("itemDesc");
		System.out.println(itemDesc2);
	}

}

2.7 RedisTemplate提供的缓存注解

2.7.1 @Cacheable

2.7.2 @CachePut

2.7.3 @CacheEvict

	@RestController
public class TestItemDescController {

	@Autowired
	private ItemDescMapper itemDescMapper;

	//ITEMDESC::0
	@RequestMapping("/findItemDesc")
	@Cacheable(cacheNames="ITEMDESC",key="#itemId")  //定义业务名称
	public ItemDesc findItemDescById(Long itemId) {

		System.out.println("查询数据库!!!!");
		return itemDescMapper.selectById(itemId);
	}

	//如果需要实现缓存更新,则必须将更新后的结果进行返回
	@RequestMapping("/update")
	@CachePut(cacheNames="ITEMDESC",key="#itemDesc.getItemId()")
	public ItemDesc update(ItemDesc itemDesc) {

		System.out.println("执行更新操作");
		itemDescMapper.updateById(itemDesc);
		return itemDescMapper.selectById(itemDesc.getItemId());
	}

	//如果需要实现缓存更新,则必须将更新后的结果进行返回
	@RequestMapping("/delete")
	@CacheEvict(cacheNames="ITEMDESC",key="#itemId")
	public String update(Long itemId) {

		System.out.println("删除数据");
		itemDescMapper.deleteById(itemId);
		return "删除成功!!!";
	}


}

你可能感兴趣的:(实训,java)