欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2024-01-26 12:22:34
...

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-京淘订单业务实现

2.1.2 选择jar包

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

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-京淘订单业务实现

2.1.5 导入订单POJO对象

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

2.1.6 导入订单项目

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

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-京淘订单业务实现
2).页面展现信息说明
京淘实训Day19-京淘订单业务实现

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-京淘订单业务实现

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-京淘订单业务实现
一般条件下:如果参数中有重名属性,则通过 对象引用的形式实现数据传递.

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="<fmt:formatNumber groupingUsed="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-京淘订单业务实现

2.4 订单新增业务

2.4.1页面分析

1).url地址
京淘实训Day19-京淘订单业务实现

2).参数提交
京淘实训Day19-京淘订单业务实现
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-京淘订单业务实现

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-京淘订单业务实现

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-京淘订单业务实现
组件说明:
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