京淘实训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 创建项目
2.1.2 选择jar包
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 订单表设计
2.1.5 导入订单POJO对象
说明:将POJO对象导入到common中.
2.1.6 导入订单项目
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分析
2).页面展现信息说明
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页面效果
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) {
}
一般条件下:如果参数中有重名属性,则通过 对象引用的形式实现数据传递.
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对象的定义
2.4 订单新增业务
2.4.1页面分析
1).url地址
2).参数提交
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
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 查询效果展现
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。
组件说明:
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 "删除成功!!!";
}
}
上一篇: 前端剑指offer算法题整理
下一篇: 新站优化应该注意什么 新站优化的注意事项