企业级开发项目《苍穹外卖》(二)

设置店铺营业状态

先去我之前写的博客“Redis数据库学习笔记”学习Redis的基本操作。

在config层下创建Redis的配置类,里面编写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Configuration
@Slf4j
public class RedisConfiguration {

@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){
log.info("开始创建redis对象:{}",redisConnectionFactory);
RedisTemplate redisTemplate = new RedisTemplate();
//设置redis工厂对象
redisTemplate.setConnectionFactory(redisConnectionFactory);
//设置redis序列化器
redisTemplate.setKeySerializer(new StringRedisSerializer());
return redisTemplate;
}
}

然后再在controller层里面创建shop店铺管理的类,里面编写设置店铺状态和查询店铺状态的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@RestController("adminShopController")
@Slf4j
@Api(tags = "店铺管理")
@RequestMapping("/admin/shop")
public class ShopController {

@Autowired
private RedisTemplate redisTemplate;

@PutMapping("/{status}")
@ApiOperation("设置店铺营业状态")
public Result setStaus(@PathVariable Integer status){
log.info("设置店铺营业状态:{}",status ==1 ?"营业中": "打烊中");
redisTemplate.opsForValue().set("SHOP_STATUS",status);
return Result.success();
}

@GetMapping("/status")
@ApiOperation("获取店铺营业状态")
public Result<Integer> getStatus(){
Integer shopStatus = (Integer) redisTemplate.opsForValue().get("SHOP_STATUS");
log.info("获取店铺营业状态:{}",shopStatus ==1 ?"营业中": "打烊中");
return Result.success(shopStatus);
}
}

记得在yml和的-dev yml里面配置redis的用户连接信息:

1
2
3
4
5
redis:
host: localhost
port: 6379
password: 123456
database: 1

接口管理分开管理端和用户端

·  在WebMvcConfiguration里面复制一个bean注解下的代码,并在new Docket下编写.groupName(“管理端接口/用户端接口”),具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/**
* 通过knife4j生成接口文档
* @return
*/
@Bean
public Docket docket1() {
log.info("准备生成接口文档...");
ApiInfo apiInfo = new ApiInfoBuilder()
.title("苍穹外卖项目接口文档")
.version("2.0")
.description("苍穹外卖项目接口文档")
.build();
Docket docket = new Docket(DocumentationType.SWAGGER_2)
.groupName("管理端接口")
.apiInfo(apiInfo)
.select()
.apis(RequestHandlerSelectors.basePackage("com.sky.controller.admin"))
.paths(PathSelectors.any())
.build();
return docket;
}

@Bean
public Docket docket2() {
log.info("准备生成接口文档...");
ApiInfo apiInfo = new ApiInfoBuilder()
.title("苍穹外卖项目接口文档")
.version("2.0")
.description("苍穹外卖项目接口文档")
.build();
Docket docket = new Docket(DocumentationType.SWAGGER_2)
.groupName("用户端接口")
.apiInfo(apiInfo)
.select()
.apis(RequestHandlerSelectors.basePackage("com.sky.controller.user"))
.paths(PathSelectors.any())
.build();
return docket;
}

这样在项目接口文档里面就可以区分开这两个端了。

微信小程序开发

下载微信开发者工具,然后将资料里面的前端文件导入进来,输入自己的openid(openid和小程序appid和secret自己去网上搜),微信小程序开发我博客之前也写过。

将资料里面的用户端文件导入进对应server文件夹里

Spring Cache

maven坐标:

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>

常用注解:

img

在苍穹外卖项目中的应用:用于缓存套餐数据,只需在启动类上加入@EnableCaching注解,在admin的SetmealController里面的删改和启动禁用上加上@CacheEvict(cacheNames = “setmealCache”,allEntries = true)注解,增加套餐上添加@CacheEvict(cacheNames = “setmealCache”,key=”#setmealDTO.categoryId”)注解。在user的SetmealController的根据id查询套餐上添加@Cacheable(cacheNames =”setmealCache”,key=”#categoryId”)

购物车管理

后面的购物车管理就跟之前的差不多了,直接上impl的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
@Autowired
private ShoppingCartMapper shoppingCartMapper;
@Autowired
private SetmealMapper setmealMapper;
@Autowired
private DishMapper dishMapper;

/**
* 添加物品
* @param shoppingCartDTO
*/
public void addShoppingCart(ShoppingCartDTO shoppingCartDTO) {
//判断当前加入购物车的商品是否存在
ShoppingCart shoppingCart=new ShoppingCart();
BeanUtils.copyProperties(shoppingCartDTO,shoppingCart);
Long userId= BaseContext.getCurrentId();
shoppingCart.setUserId(userId);

List<ShoppingCart> list =shoppingCartMapper.list(shoppingCart);
//如果已经存在了,只需要将数量增加
if (list !=null&& list.size()>0){
ShoppingCart cart=list.get(0);
cart.setNumber(cart.getNumber()+1);
shoppingCartMapper.updateById(cart);
}else {
//判断是为菜品还是套餐
Long dishId = shoppingCart.getDishId();
if (dishId !=null){
Dish dish=dishMapper.getById(dishId);
shoppingCart.setName(dish.getName());
shoppingCart.setImage(dish.getImage());
shoppingCart.setAmount(dish.getPrice());

shoppingCart.setNumber(1);
shoppingCart.setCreateTime(LocalDateTime.now());

}else {
//为套餐
Long setmealId=shoppingCart.getSetmealId();
Setmeal setmeal=setmealMapper.pageById(setmealId);
shoppingCart.setName(setmeal.getName());
shoppingCart.setImage(setmeal.getImage());
shoppingCart.setAmount(setmeal.getPrice());

shoppingCart.setNumber(1);
shoppingCart.setCreateTime(LocalDateTime.now());

}
shoppingCartMapper.insert(shoppingCart);
}

}

导入收货地址接口(AdressBook)和用户下单接口代码(Order),代码的话就自己去下载看看吧,没啥新技术。

除了用户下单的 ServiceImpl的代码有点难:其他的还将就

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/**
* 用户下单
*
* @param ordersSubmitDTO
* @return
*/
@Transactional
public OrderSubmitVO submitOrder(OrdersSubmitDTO ordersSubmitDTO) {
//异常情况的处理(收货地址为空、购物车为空)
AddressBook addressBook = addressBookMapper.getById(ordersSubmitDTO.getAddressBookId());
if (addressBook == null) {
throw new AddressBookBusinessException(MessageConstant.ADDRESS_BOOK_IS_NULL);
}

Long userId = BaseContext.getCurrentId();
ShoppingCart shoppingCart = new ShoppingCart();
shoppingCart.setUserId(userId);

//查询当前用户的购物车数据
List<ShoppingCart> shoppingCartList = shoppingCartMapper.list(shoppingCart);
if (shoppingCartList == null || shoppingCartList.size() == 0) {
throw new ShoppingCartBusinessException(MessageConstant.SHOPPING_CART_IS_NULL);
}

//构造订单数据
Orders order = new Orders();
BeanUtils.copyProperties(ordersSubmitDTO,order);
order.setPhone(addressBook.getPhone());
order.setAddress(addressBook.getDetail());
order.setConsignee(addressBook.getConsignee());
order.setNumber(String.valueOf(System.currentTimeMillis()));
order.setUserId(userId);
order.setStatus(Orders.PENDING_PAYMENT);
order.setPayStatus(Orders.UN_PAID);
order.setOrderTime(LocalDateTime.now());

//向订单表插入1条数据
orderMapper.insert(order);

//订单明细数据
List<OrderDetail> orderDetailList = new ArrayList<>();
for (ShoppingCart cart : shoppingCartList) {
OrderDetail orderDetail = new OrderDetail();
BeanUtils.copyProperties(cart, orderDetail);
orderDetail.setOrderId(order.getId());
orderDetailList.add(orderDetail);
}

//向明细表插入n条数据
orderDetailMapper.insertBatch(orderDetailList);

//清理购物车中的数据
shoppingCartMapper.deleteByUserId(userId);

//封装返回结果
OrderSubmitVO orderSubmitVO = OrderSubmitVO.builder()
.id(order.getId())
.orderNumber(order.getNumber())
.orderAmount(order.getAmount())
.orderTime(order.getOrderTime())
.build();

return orderSubmitVO;
}

这里就顺便补充下@Transactional注解:

事务处理translational
概念:指一组原子性的操作序列,这些操作要么全部执行,要么全部不执行

特性:原子性、一致性、隔离性和持久性

应用场景:当一组操作需要同时执行时。–金融行业、电商平台、物流行业

开发流程:在需要事务管理的方法上加注解:@Transactional()

rollbackFor属性:回滚指定类型的异常:@Transactional(rollbackFor =
Exception.class)

Propagation属性 :

事务传播:当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行事务控制。

REQUIRED – 【默认值】需要事务,有则加入,无则创建新事务

REQUIRES_NEW –总是创建新事务

内网穿透

封面图就是内网穿透的原理图:

img

进入临时域名的使用网站:https://dashboard.cpolar.com/,注册一个账户登进去选择免费的临时域名,下载cpolar的安装exe,按照步骤安装好就行。然后回到cpolar网站选择验证,复制你的Authtoken,然后运行刚刚安装好的cpolar.exe,输入cpolar.exe authtoken 【刚刚复制的】 ,再输入cpolar.exe http 8080然后回车,这时您的公网域名就创建好了,别人的电脑和手机就可以访问你的苍穹外卖了。可以输入https://xxxx/doc.html访问你的接口文档。

其他的接口就参考day9的资料吧:

img

微信小程序备案上线

img

img

剩下的催单接口和表格输出就自己去看黑马的吧~

苍穹外卖,完结!