day17_订单(结算,提交订单,支付页,立即购买,我的订单)

news2024/11/14 20:30:17

文章目录

  • 订单模块
  • 1 结算
    • 1.1 需求说明
    • 1.2 获取用户地址
      • 1.2.1 UserAddress
      • 1.2.2 UserAddressController
      • 1.2.3 UserAddressService
      • 1.2.4 UserAddressMapper
      • 1.2.5 UserAddressMapper.xml
    • 1.3 获取购物项数据
      • 1.3.1 CartController
      • 1.3.2 CartService
      • 1.3.3 openFeign接口定义
    • 1.4 环境搭建(service-order)
    • 1.5 后端结算业务接口开发
      • 1.5.1 TradeVo
      • 1.5.2 OrderItem
      • 1.5.3 OrderInfoController
      • 1.5.4 OrderInfoService
      • 1.5.5 服务网关
    • 1.6 openFeign拦截器使用
      • 1.6.1 问题说明
      • 1.6.2 问题解决
  • 2 提交订单
    • 2.1 需求说明
    • 2.2 后端接口
      • 2.2.1 查询用户地址
        • 业务接口开发
          • UserAddressController
          • UserAddressService
          • UserAddressMapper
          • UserAddressMapper.xml
        • openFeign接口开发
      • 2.2.2 业务接口开发
        • OrderInfoDto
        • OrderLog
        • OrderInfoController
        • OrderInfoService
        • Mapper接口
          • OrderInfoMapper
          • OrderItemMapper
          • OrderLogMapper
        • XML映射文件
          • OrderInfoMapper.xml
          • OrderItemMapper.xml
          • OrderLogMapper.xml
      • 2.2.3 清空购物车
        • service-cart
          • CartController
          • CartService
        • openFeign接口
        • service-order
  • 3 支付页
    • 3.1 需求说明
    • 3.2 后端接口
      • 3.2.1 OrderInfoController
      • 3.2.2 OrderInfoService
      • 3.2.3 OrderInfoMapper
      • 3.2.4 OrderInfoMapper.xml
  • 4 立即购买
    • 4.1 需求说明
    • 4.2 后端接口
      • 4.2.1 OrderInfoController
      • 4.2.2 OrderInfoService
  • 5 我的订单
    • 5.1 需求说明
    • 5.2 后端接口
      • 5.2.1 OrderInfoController
      • 5.2.2 OrderInfoService
      • 5.2.3 OrderInfoMapper
      • 5.2.4 OrderInfoMapper.xml
      • 5.2.5 OrderInfo
      • 5.2.6 OrderItemMapper.xml
      • 5.2.7 OrderItemMapper
      • 5.2.4 OrderInfoMapper.xml
      • 5.2.5 OrderInfo
      • 5.2.6 OrderItemMapper.xml
      • 5.2.7 OrderItemMapper

订单模块

1 结算

1.1 需求说明

入口:购物车点击去结算按钮 ,进入结算页面(订单确认页面),如图所示:

在这里插入图片描述

分析页面需要的数据:

1、 用户地址信息列表,结算页选中默认地址

2、 购物车中选择的商品列表,及商品的总金额

查看接口文档:

获取用户地址信息列表接口地址及返回结果:

get /api/user/userAddress/auth/findUserAddressList
返回结果:
{
    "code": 200,
    "message": "操作成功",
    "data": [
        {
            "id": 1,
            "createTime": "2023-05-12 17:50:41",
            "updateTime": "2023-06-02 19:15:17",
            "isDeleted": 0,
            "userId": 1,
            "name": "晴天",
            "phone": "15014526352",
            "tagName": "家",
            "provinceCode": "110000",
            "cityCode": "110100",
            "districtCode": "110114",
            "address": "天通苑大街1号",
            "fullAddress": "北京市北京市昌平区天通苑大街1号",
            "isDefault": 0
        },
        ...
    ]
}

结算接口地址及返回结果:

get api/order/orderInfo/auth/trade
返回结果:
{
    "code": 200,
    "message": "操作成功",
    "data": {
        "totalAmount": 6997.00,
        "orderItemList": [
            {
                "skuId": 1,
                "skuName": "小米 红米Note10 5G手机 颜色:白色 内存:8G",
                "thumbImg": "http://139.198.127.41:9000/spzx/20230525/665832167-5_u_1 (1).jpg",
                "skuPrice": 1999.00,
                "skuNum": 2
            },
            ...
        ]
    }
} 

获取用户地址列表调用流程如下所示:

在这里插入图片描述

获取购物车中选中的商品列表调用流程如下所示:

在这里插入图片描述

1.2 获取用户地址

在service-user模块中开发一个获取地址列表接口,供service-order微服务进行调用。

1.2.1 UserAddress

针对当前要操作的数据定义一个与之对应的实体类:

//com.atguigu.spzx.model.entity.user;
@Data
@Schema(description = "用户地址实体类")
public class UserAddress extends BaseEntity {

   private static final long serialVersionUID = 1L;

   @Schema(description = "用户ID")
   private Long userId;

   @Schema(description = "name")
   private String name;

   @Schema(description = "电话")
   private String phone;

   @Schema(description = "标签名称")
   private String tagName;

   @Schema(description = "provinceCode")
   private String provinceCode;

   @Schema(description = "cityCode")
   private String cityCode;

   @Schema(description = "districtCode")
   private String districtCode;

   @Schema(description = "详细地址")
   private String address;

   @Schema(description = "完整地址")
   private String fullAddress;

   @Schema(description = "是否默认地址(0:否 1:是)")
   private Integer isDefault;

}

1.2.2 UserAddressController

表现层代码:

// com.atguigu.spzx.user.controller;
@Tag(name = "用户地址接口")
@RestController
@RequestMapping(value="/api/user/userAddress")
@SuppressWarnings({"unchecked", "rawtypes"})
public class UserAddressController {
   
   @Autowired
   private UserAddressService userAddressService;

   @Operation(summary = "获取用户地址列表")
   @GetMapping("auth/findUserAddressList")
   public Result<List<UserAddress>> findUserAddressList() {
      List<UserAddress> list = userAddressService.findUserAddressList();
      return Result.build(list , ResultCodeEnum.SUCCESS) ;
   }
}

1.2.3 UserAddressService

业务层代码实现:

//业务接口
public interface UserAddressService {

    List<UserAddress> findUserAddressList();
}

//业务接口实现
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class UserAddressServiceImpl implements UserAddressService {

   @Autowired
   private UserAddressMapper userAddressMapper;

   @Override
   public List<UserAddress> findUserAddressList() {
      Long userId = AuthContextUtil.getUserInfo().getId();
      return userAddressMapper.findByUserId(userId);
   }
}

1.2.4 UserAddressMapper

持久层代码实现:

@Mapper
public interface UserAddressMapper {

    List<UserAddress> findByUserId(Long userId);
}

1.2.5 UserAddressMapper.xml

在映射文件中定义对应的sql语句:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">


<mapper namespace="com.atguigu.spzx.user.mapper.UserAddressMapper">

   <resultMap id="userAddressMap" type="com.atguigu.spzx.model.entity.user.UserAddress" autoMapping="true">
   </resultMap>
   
   <!-- 用于select查询公用抽取的列 -->
   <sql id="columns">
      id,user_id,name,phone,tag_name,province_code,city_code,district_code,address,full_address,is_default,create_time,update_time,is_deleted
   </sql>

   <select id="findByUserId" resultMap="userAddressMap">
      select <include refid="columns" />
      from user_address
      where
      user_id = #{userId}
   </select>
</mapper>

启动服务进行测试。

1.3 获取购物项数据

在service-cart微服务中开发一个接口,获取当前登录用户选中的购物项列表数据,如下所示:

1.3.1 CartController

表现层接口:

@Operation(summary="选中的购物车")
@GetMapping(value = "/auth/getAllCkecked")
public Result<List<CartInfo>> getAllCkecked() {
    List<CartInfo> cartInfoList = cartService.getAllCkecked() ;
    return Result.build(cartInfoList , ResultCodeEnum.SUCCESS) ;
}

1.3.2 CartService

业务层接口:

// 业务接口
List<CartInfo> getAllCkecked();

// 业务接口实现类
@Override
public List<CartInfo> getAllCkecked() {
    Long userId = AuthContextUtil.getUserInfo().getId();
    String cartKey = getCartKey(userId);
    List<Object> objectList = redisTemplate.opsForHash().values(cartKey);       // 获取所有的购物项数据
    if(!CollectionUtils.isEmpty(objectList)) {
        List<CartInfo> cartInfoList = objectList.stream().map(cartInfoJSON -> JSON.parseObject(cartInfoJSON.toString(), CartInfo.class))
            .filter(cartInfo -> cartInfo.getIsChecked() == 1)
            .collect(Collectors.toList());
        return cartInfoList ;
    }
    return new ArrayList<>() ;
}

1.3.3 openFeign接口定义

步骤:

1、在spzx-service-client模块下创建一个service-cart-client的子模块

2、在service-cart-client模块下定义远程openFeign接口

// com.atguigu.spzx.feign.cart;
@FeignClient(value = "service-cart" , fallback = CartFeignClientFallback.class)
public interface CartFeignClient {

    @GetMapping(value = "/api/order/cart/auth/getAllCkecked")
    public abstract Result<List<CartInfo>> getAllCkecked() ;

}


// com.atguigu.spzx.feign.cart.fallback;
@Slf4j
public class CartFeignClientFallback implements CartFeignClient {

    @Override
    public Result<List<CartInfo>> getAllCkecked() {
        log.info("CartFeignClientFallback...getAllCkecked的方法执行了");
        return Result.build(null , ResultCodeEnum.SUCCESS) ;
    }
}

3、降级类自动化配置

在resources目录下创建一个MATE-INF/spring文件夹,在该文件夹下创建一个

org.springframework.boot.autoconfigure.AutoConfiguration.imports文件,文件的中的内容如下所示:

com.atguigu.spzx.feign.cart.fallback.CartFeignClientFallback

1.4 环境搭建(service-order)

步骤:

1、在spzx-service模块下创建一个service-order微服务,并加入如下依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

2、准备application.yml、application-dev.yml、logback-spring.xml、mybatis-config.xml文件。文件内容如下所示:

server:
  port: 8514

spring:
  application:
    name: service-order
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.136.142:8848
    sentinel:
      transport:
        dashboard: localhost:8080
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://192.168.136.142:3306/db_spzx?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=true
    username: root
    password: 1234
  data:
    redis:
      host: 192.168.136.142
      port: 6379
      password: 1234

mybatis:
  config-location: classpath:mybatis-config.xml
  mapper-locations: classpath:mapper/*/*.xml
feign:
  sentinel:
    enabled: true

logback-spring.xml修改输出路径:

<property name="log.path" value="D://work//service-order//logs" />

mybatis-config.xml:从之前的模块中进行复制

3、创建启动类

//  com.atguigu.spzx.order;
@SpringBootApplication
@EnableFeignClients(basePackages = {
        "com.atguigu.spzx.feign.cart"
})
public class OrderApplication {

    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class , args) ;
    }

}

1.5 后端结算业务接口开发

操作模块:service-order

1.5.1 TradeVo

定义一个实体类用来封装结算的参数(根据接口文档进行定义),具体定义如下所示

// com.atguigu.spzx.model.vo.h5
@Data
@Schema(description = "结算实体类")
public class TradeVo {

    @Schema(description = "结算总金额")
    private BigDecimal totalAmount;

    @Schema(description = "结算商品列表")
    private List<OrderItem> orderItemList;

}

1.5.2 OrderItem

定义一个与数据库表相对应的实体类,封装订单明细数据:

 // com.atguigu.spzx.model.entity.order
@Data
@Schema(description = "订单项实体类")
public class OrderItem extends BaseEntity {

   private static final long serialVersionUID = 1L;

   @Schema(description = "订单id")
   private Long orderId;

   @Schema(description = "商品sku编号")
   private Long skuId;

   @Schema(description = "商品sku名字")
   private String skuName;

   @Schema(description = "商品sku图片")
   private String thumbImg;

   @Schema(description = "商品sku价格")
   private BigDecimal skuPrice;

   @Schema(description = "商品购买的数量")
   private Integer skuNum;

}

1.5.3 OrderInfoController

业务层代码实现:

@Tag(name = "订单管理")
@RestController
@RequestMapping(value="/api/order/orderInfo")
@SuppressWarnings({"unchecked", "rawtypes"})
public class OrderInfoController {
   
   @Autowired
   private OrderInfoService orderInfoService;

   @Operation(summary = "确认下单")
   @GetMapping("auth/trade")
   public Result<TradeVo> trade() {
      TradeVo tradeVo = orderInfoService.getTrade();
      return Result.build(tradeVo, ResultCodeEnum.SUCCESS);
   }

}

1.5.4 OrderInfoService

业务层代码实现:

//业务接口
public interface OrderInfoService {
    TradeVo getTrade();
}

//业务接口实现
// com.atguigu.spzx.order.service.impl;
@Service
public class OrderInfoServiceImpl implements OrderInfoService {

    @Autowired
    private CartFeignClient cartFeignClient ;

    @Override
    public TradeVo getTrade() {

        // 获取当前登录的用户的id
        Long userId = AuthContextUtil.getUserInfo().getId();

        // 获取选中的购物项列表数据
        List<CartInfo> cartInfoList = cartFeignClient.getAllCkecked().getData() ;
        List<OrderItem> orderItemList = new ArrayList<>();
        for (CartInfo cartInfo : cartInfoList) {        // 将购物项数据转换成功订单明细数据
            OrderItem orderItem = new OrderItem();
            orderItem.setSkuId(cartInfo.getSkuId());
            orderItem.setSkuName(cartInfo.getSkuName());
            orderItem.setSkuNum(cartInfo.getSkuNum());
            orderItem.setSkuPrice(cartInfo.getCartPrice());
            orderItem.setThumbImg(cartInfo.getImgUrl());
            orderItemList.add(orderItem);
        }

        // 计算总金额
        BigDecimal totalAmount = new BigDecimal(0);
        for(OrderItem orderItem : orderItemList) {
            totalAmount = totalAmount.add(orderItem.getSkuPrice().multiply(new BigDecimal(orderItem.getSkuNum())));
        }
        TradeVo tradeVo = new TradeVo();
        tradeVo.setTotalAmount(totalAmount);
        tradeVo.setOrderItemList(orderItemList);
        return tradeVo;
        
    }

}

1.5.5 服务网关

服务网关中配置service-order微服务的路由规则:

spring:
  cloud:
    gateway:
      routes:
        - id: service-order
          uri: lb://service-order
          predicates:
            - Path=/api/order/orderInfo/**

启动服务进行测试。

1.6 openFeign拦截器使用

1.6.1 问题说明

在测试的时候,那么service-cart微服务会报错,如下所示:

java.lang.NullPointerException: Cannot invoke "com.atguigu.spzx.model.entity.user.UserInfo.getId()" because the return value of "com.atguigu.spzx.common.util.AuthContextUtil.getUserInfo()" is null
	at com.atguigu.spzx.cart.service.impl.CartServiceImpl.getAllCkecked(CartServiceImpl.java:147)

针对service-cart微服务是获取不到当前登录用户的信息。

原因:service-order微服务调用service-cart微服务的时候,是通过openFeign进行调用,openFeign在调用的时候会丢失请求头

1.6.2 问题解决

解决方案:使用feign拦截器拦截请求,获取token,重新传递token

在这里插入图片描述

注意:为了让feign拦截器更加通用,那么此时可以将拦截器定义到common-service模块中

feign拦截器开发:

1、在common-service模块中加入openFeign的依赖

<!-- openfeign依赖 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
    <scope>provided</scope>
</dependency>

2、定义feign拦截器

// com.atguigu.spzx.common.feign;
public class UserTokenFeignInterceptor implements RequestInterceptor {

    @Override
    public void apply(RequestTemplate requestTemplate) {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        String token = request.getHeader("token");
        requestTemplate.header("token" , token) ;
    }

}

3、定义使用该拦截器的注解

// com.atguigu.spzx.common.anno;
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = ElementType.TYPE)
@Import(value = UserTokenFeignInterceptor.class)
public @interface EnableUserTokenFeignInterceptor {
    
}

4、service-order微服务启动类上使用**@EnableUserTokenFeignInterceptor**注解

2 提交订单

2.1 需求说明

需求说明:用户在结算页面点击提交订单按钮,那么此时就需要保存订单信息(order_info)订单项信息(order_item)记录订单日志(order_log),成功后还需要清空订单对应的购物项,下单成功重定向到订单支付页面

查看接口文档:

下单接口地址及返回结果:

post /api/order/orderInfo/auth/submitOrder
参数:
{
    "orderItemList": [
        {
            "skuId": 6,
            "skuName": "小米 红米Note10 5G手机 颜色:黑色 内存:18G",
            "thumbImg": "http://139.198.127.41:9000/spzx/20230525/665832167-1_u_1.jpg",
            "skuPrice": 2999,
            "skuNum": 1
        },
        ...
    ],
    "userAddressId": 2,
    "feightFee": 0,
    "remark": "赶快发货"
}
返回结果(订单id){
    "code": 200,
    "message": "操作成功",
    "data": 1
}

调用流程如下所示:

在这里插入图片描述

2.2 后端接口

2.2.1 查询用户地址

业务接口开发

操作模块:service-user

UserAddressController

表现层代码:

@Operation(summary = "获取地址信息")
@GetMapping("getUserAddress/{id}")
public UserAddress getUserAddress(@PathVariable Long id) {
   return userAddressService.getById(id);
}
UserAddressService

业务层代码实现:

//业务接口
UserAddress getById(Long id);

//业务接口实现
@Override
public UserAddress getById(Long id) {
   return userAddressMapper.getById(id);
}
UserAddressMapper

持久层代码实现:

UserAddress getById(Long id);
UserAddressMapper.xml

在映射文件中定义对应的sql语句:

<select id="getById" resultMap="userAddressMap">
   select <include refid="columns" />
   from user_address
   where
   id = #{id}
</select>

启动程序进行测试。

openFeign接口开发

步骤如下:

1、在spzx-service-client模块下创建一个service-user-client的子模块

2、在service-user-client模块下定义远程openFeign接口

// com.atguigu.spzx.feign.user;
@FeignClient(value = "service-user" , fallback = UserFeignClientFallback.class)
public interface UserFeignClient {

    @GetMapping("/api/user/userAddress/getUserAddress/{id}")
    public abstract Result<UserAddress> getUserAddress(@PathVariable Long id) ;

}


// com.atguigu.spzx.feign.user.fallback;
@Slf4j
public class UserFeignClientFallback implements UserFeignClient {

    @Override
    public Result<UserAddress> getUserAddress(Long id) {
        log.info("UserFeignClientFallback...getUserAddress的方法执行了");
        return Result.build(null , ResultCodeEnum.SUCCESS) ;
    }

}

3、降级类自动化配置

在resources目录下创建一个MATE-INF/spring文件夹,在该文件夹下创建一个

org.springframework.boot.autoconfigure.AutoConfiguration.imports文件,文件的中的内容如下所示:

com.atguigu.spzx.feign.user.fallback.UserFeignClientFallback

4、修改service-order微服务

pom.xml文件中加入依赖:

<dependency>
    <groupId>com.atguigu.spzx</groupId>
    <artifactId>service-user-client</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

启动类修改:

@EnableFeignClients(basePackages = {
        "com.atguigu.spzx.feign.cart",
        "com.atguigu.spzx.feign.user"
})

2.2.2 业务接口开发

操作模块:service-order

OrderInfoDto

定义一个实体类用来封装下单的参数,具体定义如下所示

@Data
public class OrderInfoDto {

    //送货地址id
    private Long userAddressId;

    //运费
    private BigDecimal feightFee;
    //备注
    private String remark;
    //订单明细
    private List<OrderItem> orderItemList;
}
OrderLog

订单日志体类定义:

@Data
@Schema(description = "订单日志实体对象")
public class OrderLog extends BaseEntity {

   private static final long serialVersionUID = 1L;

   @Schema(description = "订单id")
   private Long orderId;

   @Schema(description = "操作人:用户;系统;后台管理员")
   private String operateUser;

   @Schema(description = "订单状态")
   private Integer processStatus;

   @Schema(description = "备注")
   private String note;

}
OrderInfoController

表现层代码:

@Operation(summary = "提交订单")
@PostMapping("auth/submitOrder")
public Result<Long> submitOrder(@Parameter(name = "orderInfoDto", description = "请求参数实体类", required = true) @RequestBody OrderInfoDto orderInfoDto) {
   Long orderId = orderInfoService.submitOrder(orderInfoDto);
   return Result.build(orderId, ResultCodeEnum.SUCCESS);
}
OrderInfoService

业务层代码实现:

//业务接口
Long submitOrder(OrderInfoDto orderInfoDto);

//业务接口实现
@Transactional
@Override
public Long submitOrder(OrderInfoDto orderInfoDto) {

    // 数据校验
    List<OrderItem> orderItemList = orderInfoDto.getOrderItemList();
    if (CollectionUtils.isEmpty(orderItemList)) {
        throw new GuiguException(ResultCodeEnum.DATA_ERROR);
    }
    for (OrderItem orderItem : orderItemList) {
        ProductSku productSku = productFeignClient.getBySkuId(orderItem.getSkuId()).getData();
        if(null == productSku) {
            throw new GuiguException(ResultCodeEnum.DATA_ERROR);
        }
        if(orderItem.getSkuNum().intValue() > productSku.getStockNum().intValue()) {
            throw new GuiguException(ResultCodeEnum.STOCK_LESS);
        }
    }

    // 构建订单数据,保存订单
    UserInfo userInfo = AuthContextUtil.getUserInfo();
    OrderInfo orderInfo = new OrderInfo();
    String orderNo = IdUtil.createSnowflake(1L, 1L).nextIdStr();
    orderInfo.setOrderNo(orderNo);
    orderInfo.setUserId(userInfo.getId());
    orderInfo.setNickName(userInfo.getUsername());

    UserAddress userAddress = userFeignClient.getUserAddress(orderInfoDto.getUserAddressId()).getData();
    orderInfo.setReceiverName(userAddress.getName());
    orderInfo.setReceiverPhone(userAddress.getPhone());
    orderInfo.setReceiverTagName(userAddress.getTagName());
    orderInfo.setReceiverProvince(userAddress.getProvinceCode());
    orderInfo.setReceiverCity(userAddress.getCityCode());
    orderInfo.setReceiverDistrict(userAddress.getDistrictCode());
    orderInfo.setReceiverAddress(userAddress.getFullAddress());

    BigDecimal totalAmount = new BigDecimal(0);
    for (OrderItem orderItem : orderItemList) {
        totalAmount = totalAmount.add(orderItem.getSkuPrice().multiply(new BigDecimal(orderItem.getSkuNum())));
    }
    orderInfo.setTotalAmount(totalAmount);
    orderInfo.setCouponAmount(new BigDecimal(0));
    orderInfo.setOriginalTotalAmount(totalAmount);
    orderInfo.setFeightFee(orderInfoDto.getFeightFee());
    orderInfo.setPayType(2);
    orderInfo.setOrderStatus(0);
    orderInfoMapper.save(orderInfo);

    //保存订单明细
    for (OrderItem orderItem : orderItemList) {
        orderItem.setOrderId(orderInfo.getId());
        orderItemMapper.save(orderItem);
    }

    //记录日志
    OrderLog orderLog = new OrderLog();
    orderLog.setOrderId(orderInfo.getId());
    orderLog.setProcessStatus(0);
    orderLog.setNote("提交订单");
    orderLogMapper.save(orderLog);
    
    // TODO 远程调用service-cart微服务接口清空购物车数据
    return orderInfo.getId();
}

在ResultCodeEnum类添加枚举

STOCK_LESS( 219, "库存不足"),
Mapper接口

持久层代码实现:

OrderInfoMapper
@Mapper
public interface OrderInfoMapper {

    void save(OrderInfo orderInfo);
}
OrderItemMapper
@Mapper
public interface OrderItemMapper {
    void save(OrderItem orderItem);
}
OrderLogMapper
@Mapper
public interface OrderLogMapper {
    void save(OrderLog orderLog);
}
XML映射文件

在映射文件中定义对应的sql语句

OrderInfoMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.atguigu.spzx.order.mapper.OrderInfoMapper">

   <resultMap id="orderInfoMap" type="com.atguigu.spzx.model.entity.order.OrderInfo" autoMapping="true">
   </resultMap>
   
   <!-- 用于select查询公用抽取的列 -->
   <sql id="columns">
      id,user_id,nick_name,order_no,coupon_id,total_amount,coupon_amount,original_total_amount,feight_fee,pay_type,order_status,receiver_name,receiver_phone,receiver_tag_name,receiver_province,receiver_city,receiver_district,receiver_address,payment_time,delivery_time,receive_time,remark,cancel_time,cancel_reason,create_time,update_time,is_deleted
   </sql>

   <insert id="save" useGeneratedKeys="true" keyProperty="id">
      insert into order_info (
         id,
         user_id,
         nick_name,
         order_no,
         coupon_id,
         total_amount,
         coupon_amount,
         original_total_amount,
         feight_fee,
         pay_type,
         order_status,
         receiver_name,
         receiver_phone,
         receiver_tag_name,
         receiver_province,
         receiver_city,
         receiver_district,
         receiver_address,
         payment_time,
         delivery_time,
         receive_time,
         remark,
         cancel_time,
         cancel_reason
      ) values (
         #{id},
         #{userId},
         #{nickName},
         #{orderNo},
         #{couponId},
         #{totalAmount},
         #{couponAmount},
         #{originalTotalAmount},
         #{feightFee},
         #{payType},
         #{orderStatus},
         #{receiverName},
         #{receiverPhone},
         #{receiverTagName},
         #{receiverProvince},
         #{receiverCity},
         #{receiverDistrict},
         #{receiverAddress},
         #{paymentTime},
         #{deliveryTime},
         #{receiveTime},
         #{remark},
         #{cancelTime},
         #{cancelReason}
      )
   </insert>
</mapper>
OrderItemMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.atguigu.spzx.order.mapper.OrderItemMapper">

   <resultMap id="orderItemMap" type="com.atguigu.spzx.model.entity.order.OrderItem" autoMapping="true">
   </resultMap>
   
   <!-- 用于select查询公用抽取的列 -->
   <sql id="columns">
      id,order_id,sku_id,sku_name,thumb_img,sku_price,sku_num,create_time,update_time,is_deleted
   </sql>

   <insert id="save" useGeneratedKeys="true" keyProperty="id">
      insert into order_item (
         id,
         order_id,
         sku_id,
         sku_name,
         thumb_img,
         sku_price,
         sku_num
      ) values (
         #{id},
         #{orderId},
         #{skuId},
         #{skuName},
         #{thumbImg},
         #{skuPrice},
         #{skuNum}
      )
   </insert>
</mapper>
OrderLogMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.atguigu.spzx.order.mapper.OrderLogMapper">

   <resultMap id="orderLogMap" type="com.atguigu.spzx.model.entity.order.OrderLog" autoMapping="true">
   </resultMap>
   
   <!-- 用于select查询公用抽取的列 -->
   <sql id="columns">
      id,order_id,operate_user,process_status,note,create_time,update_time,is_deleted
   </sql>

   <insert id="save" useGeneratedKeys="true" keyProperty="id">
      insert into order_log (
         id,
         order_id,
         operate_user,
         process_status,
         note
      ) values (
         #{id},
         #{orderId},
         #{operateUser},
         #{processStatus},
         #{note}
      )
   </insert>

</mapper>

2.2.3 清空购物车

需求说明:订单生成成功以后,需要调用service-cart微服务的接口清空选中的购物车数据。

service-cart

在service-cart微服务中开发一个清空购物车的接口供service-order微服务进行调用:

CartController
// com.atguigu.spzx.cart.controller.CartController
@GetMapping(value = "/auth/deleteChecked")
public Result deleteChecked() {
    cartService.deleteChecked() ;
    return Result.build(null , ResultCodeEnum.SUCCESS) ;
}
CartService
// com.atguigu.spzx.cart.service.impl.CartServiceImpl
@Override
public void deleteChecked() {
    Long userId = AuthContextUtil.getUserInfo().getId();
    String cartKey = getCartKey(userId);
    List<Object> objectList = redisTemplate.opsForHash().values(cartKey);       // 删除选中的购物项数据
    if(!CollectionUtils.isEmpty(objectList)) {
        objectList.stream().map(cartInfoJSON -> JSON.parseObject(cartInfoJSON.toString(), CartInfo.class))
            .filter(cartInfo -> cartInfo.getIsChecked() == 1)
            .forEach(cartInfo -> redisTemplate.opsForHash().delete(cartKey , String.valueOf(cartInfo.getSkuId())));
    }
}
openFeign接口

在service-cart-client模块CartFeignClient中定义删除选中的购物车数据的远程接口方法:

// com.atguigu.spzx.feign.cart
@FeignClient(value = "service-cart" , fallback = CartFeignClientFallback.class)
public interface CartFeignClient {
    
    @GetMapping(value = "/api/order/cart/auth/deleteChecked")
    public abstract Result deleteChecked() ;

}
service-order

改造service-order微服务的下单接口方法,添加清空选中购物车数据的远程调用代码:

// com.atguigu.spzx.order.service.impl.OrderInfoServiceImpl
@Transactional
@Override
public Long submitOrder(OrderInfoDto orderInfoDto) {

    // 1、数据校验
    // 2、构建订单数据,保存订单
    // 3、保存订单明细
    // 4、记录日志
    // 5、清空购物车数据
    cartFeignClient.deleteChecked() ;
    
    return orderInfo.getId();
}

3 支付页

3.1 需求说明

提交订单成功,跳转到支付页面,根据订单id获取订单详细信息,展示订单支付信息

查看接口文档:

根据订单id获取订单信息接口地址及返回结果:

get /api/order/orderInfo/auth/{orderId}
返回结果(订单id){
    "code": 200,
    "message": "成功",
    "data": {
        "id": 194,
        "createTime": "2023-06-14 19:29:31",
        "userId": 1,
        "nickName": "test",
        "orderNo": "1686713371363",
        "couponId": null,
        "totalAmount": 2999,
        "couponAmount": 0,
        "originalTotalAmount": 2999,
        "feightFee": 0,
        "payType": 2,
        "orderStatus": 0,
        "receiverName": "张三",
        "receiverPhone": "15012563333",
        "receiverTagName": "公司",
        "receiverProvince": "130000",
        "receiverCity": "130700",
        "receiverDistrict": "130724",
        "receiverAddress": "河北省张家口市沽源县快乐家园1号",
        "paymentTime": null,
        "deliveryTime": null,
        "receiveTime": null,
        "remark": null,
        "cancelTime": null,
        "cancelReason": null
    }
}

3.2 后端接口

3.2.1 OrderInfoController

表现层代码:

@Operation(summary = "获取订单信息")
@GetMapping("auth/{orderId}")
public Result<OrderInfo> getOrderInfo(@Parameter(name = "orderId", description = "订单id", required = true) @PathVariable Long orderId) {
   OrderInfo orderInfo = orderInfoService.getOrderInfo(orderId);
   return Result.build(orderInfo, ResultCodeEnum.SUCCESS);
}

3.2.2 OrderInfoService

业务层代码实现:

//业务接口
OrderInfo getOrderInfo(Long orderId);

//业务接口实现
@Override
public OrderInfo getOrderInfo(Long orderId) {
    return orderInfoMapper.getById(orderId);
}

3.2.3 OrderInfoMapper

持久层代码实现:

OrderInfo getById(Long orderId);

3.2.4 OrderInfoMapper.xml

在映射文件中定义对应的sql语句:

<select id="getById" resultMap="orderInfoMap">
   select <include refid="columns" />
   from order_info
   where
   id = #{id}
</select>

4 立即购买

4.1 需求说明

入口:商品详情页,点击“立即购买”按钮

立即购买直接进入结算页,不经过购物车,结算页返回数据与正常下单结算数据一致,提交订单接口不变,如图所示:

在这里插入图片描述

查看接口文档:

立即购买接口地址及返回结果:

get /api/order/orderInfo/auth/buy/{skuId}
返回结果:
{
    "code": 200,
    "message": "操作成功",
    "data": {
        "totalAmount": 6997.00,
        "orderItemList": [
            {
                "skuId": 1,
                "skuName": "小米 红米Note10 5G手机 颜色:白色 内存:8G",
                "thumbImg": "http://139.198.127.41:9000/spzx/20230525/665832167-5_u_1 (1).jpg",
                "skuPrice": 1999.00,
                "skuNum": 2
            },
            ...
        ]
    }
}

4.2 后端接口

4.2.1 OrderInfoController

表现层代码:

@Operation(summary = "立即购买")
@GetMapping("auth/buy/{skuId}")
public Result<TradeVo> buy(@Parameter(name = "skuId", description = "商品skuId", required = true) @PathVariable Long skuId) {
   TradeVo tradeVo = orderInfoService.buy(skuId);
   return Result.build(tradeVo, ResultCodeEnum.SUCCESS);
}

4.2.2 OrderInfoService

业务层代码实现:

//业务接口
TradeVo buy(Long skuId);

//业务接口实现
@Override
public TradeVo buy(Long skuId) {

    // 查询商品
    ProductSku productSku = productFeignClient.getBySkuId(skuId).getData();
    List<OrderItem> orderItemList = new ArrayList<>();
    OrderItem orderItem = new OrderItem();
    orderItem.setSkuId(skuId);
    orderItem.setSkuName(productSku.getSkuName());
    orderItem.setSkuNum(1);
    orderItem.setSkuPrice(productSku.getSalePrice());
    orderItem.setThumbImg(productSku.getThumbImg());
    orderItemList.add(orderItem);

    // 计算总金额
    BigDecimal totalAmount = productSku.getSalePrice();
    TradeVo tradeVo = new TradeVo();
    tradeVo.setTotalAmount(totalAmount);
    tradeVo.setOrderItemList(orderItemList);

    // 返回
    return tradeVo;
}

5 我的订单

5.1 需求说明

我的订单根据订单状态展示列表,如图所示:

在这里插入图片描述

查看接口文档:

我的订单接口地址及返回结果:

get /api/order/orderInfo/auth/{page}/{limit}?orderStatus={orderStatus}
返回结果:
{
    "code": 200,
    "message": "成功",
    "data": {
        "total": 63,
        "list": [
            {
                "id": 194,
                "createTime": "2023-06-14 19:29:31",
                "userId": 1,
                "nickName": "test",
                "orderNo": "1686713371363",
                "couponId": null,
                "totalAmount": 2999.00,
                "couponAmount": 0.00,
                "originalTotalAmount": 2999.00,
                "feightFee": 0.00,
                "payType": 2,
                "orderStatus": 0,
                "receiverName": "张三",
                "receiverPhone": "15012563333",
                "receiverTagName": "公司",
                "receiverProvince": "130000",
                "receiverCity": "130700",
                "receiverDistrict": "130724",
                "receiverAddress": "河北省张家口市沽源县快乐家园1号",
                "paymentTime": null,
                "deliveryTime": null,
                "receiveTime": null,
                "remark": null,
                "cancelTime": null,
                "cancelReason": null,
                "orderItemList": [
                    {
                        "id": 428,
                        "createTime": "2023-06-14 19:29:31",
                        "orderId": 194,
                        "skuId": 6,
                        "skuName": "小米 红米Note10 5G手机 颜色:黑色 内存:18G",
                        "thumbImg": "http://139.198.127.41:9000/spzx/20230525/665832167-1_u_1.jpg",
                        "skuPrice": 2999.00,
                        "skuNum": 1
                    }
                ]
            },
            ...
        ],
        "pageNum": 1,
        "pageSize": 10,
        "size": 6,
        "startRow": 1,
        "endRow": 6,
        "pages": 7,
        "prePage": 0,
        "nextPage": 2,
        "isFirstPage": true,
        "isLastPage": false,
        "hasPreviousPage": false,
        "hasNextPage": true,
        "navigatePages": 10,
        "navigatepageNums": [
            1,
            2,
            3,
            4,
            5,
            6,
            7
        ],
        "navigateFirstPage": 1,
        "navigateLastPage": 7
    }
}

5.2 后端接口

5.2.1 OrderInfoController

表现层代码:

@Operation(summary = "获取订单分页列表")
@GetMapping("auth/{page}/{limit}")
public Result<PageInfo<OrderInfo>> list(
      @Parameter(name = "page", description = "当前页码", required = true)
      @PathVariable Integer page,

      @Parameter(name = "limit", description = "每页记录数", required = true)
      @PathVariable Integer limit,

      @Parameter(name = "orderStatus", description = "订单状态", required = false)
      @RequestParam(required = false, defaultValue = "") Integer orderStatus) {
   PageInfo<OrderInfo> pageInfo = orderInfoService.findUserPage(page, limit, orderStatus);
   return Result.build(pageInfo, ResultCodeEnum.SUCCESS);
}

5.2.2 OrderInfoService

业务层代码实现:

//业务接口
PageInfo<OrderInfo> findUserPage(Integer page, Integer limit, Integer orderStatus);

//业务接口实现
@Override
public PageInfo<OrderInfo> findUserPage(Integer page, Integer limit, Integer orderStatus) {
    PageHelper.startPage(page, limit);
    Long userId = AuthContextUtil.getUserInfo().getId();
    List<OrderInfo> orderInfoList = orderInfoMapper.findUserPage(userId, orderStatus);
    return new PageInfo<>(orderInfoList, limit);
}

5.2.3 OrderInfoMapper

持久层代码实现:

List<OrderInfo> findUserPage(Long userId, Integer orderStatus);

5.2.4 OrderInfoMapper.xml

在映射文件中定义对应的sql语句:

<resultMap id="orderInfoMap" type="com.atguigu.spzx.model.entity.order.OrderInfo" autoMapping="true">
   <!--id:表示主键 property:表示实体类的属性名 column:表示通过sql 执行以后查询出来的字段名-->
   <id property="id" column="id"></id>

   <!-- 一对多 -->
   <collection property="orderItemList" ofType="com.atguigu.spzx.model.entity.order.OrderItem" autoMapping="true"
            column="{orderId = id}"
            select="com.atguigu.spzx.order.mapper.OrderItemMapper.findByOrderId">
   </collection>
</resultMap>
<select id="findUserPage" resultMap="orderInfoMap">
   select <include refid="columns" />
   from order_info
   <where>
      <if test="userId != null">
         and user_id = #{userId}
      </if>
      <if test="orderStatus != null">
         and order_status = #{orderStatus}
      </if>
      and is_deleted = 0
   </where>
   order by id desc
</select>

5.2.5 OrderInfo

订单实体添加属性:

@Schema(description = "订单项列表")
private List<OrderItem> orderItemList;

5.2.6 OrderItemMapper.xml

在映射文件中定义对应的sql语句:

<select id="findByOrderId" resultMap="orderItemMap">
   select <include refid="columns" />
   from order_item
   where
      order_id = #{orderId}
      and is_deleted = 0
   order by id desc
</select>

5.2.7 OrderItemMapper

持久层代码实现:

List<OrderItem> findByOrderId(Long orderId);

Mapper

持久层代码实现:

List<OrderInfo> findUserPage(Long userId, Integer orderStatus);

5.2.4 OrderInfoMapper.xml

在映射文件中定义对应的sql语句:

<resultMap id="orderInfoMap" type="com.atguigu.spzx.model.entity.order.OrderInfo" autoMapping="true">
   <!--id:表示主键 property:表示实体类的属性名 column:表示通过sql 执行以后查询出来的字段名-->
   <id property="id" column="id"></id>

   <!-- 一对多 -->
   <collection property="orderItemList" ofType="com.atguigu.spzx.model.entity.order.OrderItem" autoMapping="true"
            column="{orderId = id}"
            select="com.atguigu.spzx.order.mapper.OrderItemMapper.findByOrderId">
   </collection>
</resultMap>
<select id="findUserPage" resultMap="orderInfoMap">
   select <include refid="columns" />
   from order_info
   <where>
      <if test="userId != null">
         and user_id = #{userId}
      </if>
      <if test="orderStatus != null">
         and order_status = #{orderStatus}
      </if>
      and is_deleted = 0
   </where>
   order by id desc
</select>

5.2.5 OrderInfo

订单实体添加属性:

@Schema(description = "订单项列表")
private List<OrderItem> orderItemList;

5.2.6 OrderItemMapper.xml

在映射文件中定义对应的sql语句:

<select id="findByOrderId" resultMap="orderItemMap">
   select <include refid="columns" />
   from order_item
   where
      order_id = #{orderId}
      and is_deleted = 0
   order by id desc
</select>

5.2.7 OrderItemMapper

持久层代码实现:

List<OrderItem> findByOrderId(Long orderId);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1502769.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

VUE_nuxt启动只能通过localhost访问,ip访问不到:问题解决

修改项目根目录下的 package.json "config": {"nuxt": {"host": "0.0.0.0","port": "3000"} } 这样项目启动后就可以通过ip进行访问了

ViewModel

1.ViewModel的诞生与作用 Activity package com.tiger.viewmodel;import androidx.appcompat.app.AppCompatActivity; import androidx.lifecycle.ViewModelProvider;import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Te…

Codeforces Round 929 (Div. 3)- ABCDEFG

A:Turtle Puzzle: Rearrange and Negate 思路&#xff1a; 将负的元素全部排到一起&#xff0c;然后对它们符号取反&#xff0c;然后求所有元素的和&#xff0c;此时就是最大值了。 代码&#xff1a; #include<iostream> using namespace std;void solve() {int n;cin&…

电脑资料管理软件(5个高效批量管理电脑资料的方法)

企业电脑资料管理是企业一大难题&#xff0c;为什么这样说&#xff1f; 首先&#xff0c;企业电脑资料的数量庞大且种类繁多。 其次&#xff0c;电脑资料的安全性和保密性要求高。 再者&#xff0c;电脑资料的管理涉及到多个部门和员工的协作。 ...... 针对此类情况很多企业…

Vue中的组件:构建现代Web应用的基石

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

基于redis实现互斥锁

利用setnx命令实现类似获取锁和释放锁。 获取锁&#xff0c;setnx lock 1&#xff0c;返回值为1视为获取成功&#xff0c;为0视为获取失败 释放锁&#xff0c;del lock 特殊情况&#xff1a; 如果获取锁之后&#xff0c;锁来还来不及释放&#xff0c;redis宕机了&#xff0c;这…

MIT6.5840(6.824)Lab2总结(Raft)

MIT6.5840&#xff08;原MIT6.824&#xff09;Lab2总结&#xff08;Raft&#xff09; 资源分享&#xff1a; 官网地址&#xff1a;http://nil.csail.mit.edu/6.5840/2023/ Raft论文地址&#xff1a;http://nil.csail.mit.edu/6.5840/2023/papers/raft-extended.pdf 官方学生…

gumbel-softmax如何实现离散分布可微+torch代码+原理+证明

文章目录 背景方法通俗理解什么是重参数化gumbel-softmax为什么是gumbeltorch实现思考 背景 这里举一个简单的情况&#xff0c;当前我们有p1, p2, p3三个概率&#xff0c;我们需要得到最优的一个即max(p1, p2, p3)&#xff0c;例如当前p3 max(p1, p2, p3)&#xff0c;那么理想…

SSRF漏洞基础原理(浅层面解释 + 靶场演示)

一、SSRF漏洞的基本概念&#xff1a; SSRF--全名&#xff1a;Server-Side Request Forgery&#xff0c;汉译&#xff1a;服务端请求伪造&#xff0c;漏洞别名“借刀杀人”。 想象以下&#xff0c;现存在一个 Web应用&#xff0c;这个Web应用可以帮助我们能爬取互联网上的其他…

js 添加、删除DOM元素

1. js添加、删除DOM元素 1.1. 添加DOM元素 1.1.1. appendChild()方法 该方法添加的元素位于父元素的末尾&#xff0c;使用方法&#xff1a; parentNode.appenChild(NewNode) // parentNode是需要添加元素的容器&#xff0c;NewNode是新添加的元素   创建一个li元素并添加到…

数据结构与算法-哈希表

引言 在计算机科学中&#xff0c;数据结构与算法是构建高效软件系统的关键基石。其中&#xff0c;哈希表作为一种非常实用的数据结构&#xff0c;以其快速查找、插入和删除等特性&#xff0c;在诸多领域发挥着无可替代的作用。本文将深入探讨哈希表的工作原理、实现细节以及其在…

Qt Creator常见问题解决方法

Qt Creator源文件重命名的正确方法 光改文件名是不够的&#xff0c;还要在.pro文件中的SOURCES中把名字改成之后的。 中文乱码&#xff08;字符集设置&#xff09; 菜单栏-工具-选项-设置为utf-8

Reset Verification IP

Reset Verification IP IP 参数及接口 IP 例化界面 相关函数 assert_reset //置位复位信号 < hierarchy_path>.assert_reset();deassert_reset //取消置位复位信号 < hierarchy_path>.deassert_reset();set_master_mode //设置 RST_VIP 模式为 Master < hi…

STM32基本定时功能

1、定时器就是计数器。 2、怎么计数&#xff1f; 3、我们需要有一恒定频率的方波信号&#xff0c;再加上一个寄存器。 4、比如每来一个上升沿信号&#xff0c;寄存器值加1&#xff0c;就可以完成计数。 5、假设方波频率是100Hz&#xff0c;也就是1秒100个脉冲。…

严刑拷打_微服务

文章详情 &#xff1a;&#x1f60a; 作者&#xff1a;Lion J &#x1f496; 主页&#xff1a; https://blog.csdn.net/weixin_69252724 &#x1f389; 主题&#xff1a; 微服务相关知识 ⏱️ 创作时间&#xff1a;2024年03月8日 ———————————————— 文章目…

[数据结构]队列

1.队列的概念及结构 队列&#xff1a;只允许在一端进行插入数据操作&#xff0c;在另一端进行删除数据操作的特殊线性表&#xff0c;队列具有先进先出 FIFO(First In First Out) 入队列&#xff1a;进行插入操作的一端称为队尾 出队列&#xff1a;进行删除操作的一端称为队头 2…

Dynamo3.0.3——六年来最大的更新

Hello大家好&#xff01;我是九哥~ 前几天&#xff0c;Dynamo Core 3.0.0版本发布&#xff0c;迎来了Dynamo六年来最大的一次更新。最大的改变&#xff0c;是更新到了.net8&#xff0c;这回对Dynamo节点包产生不小影响。接下来我们详细看一下都有哪些变化。 首先&#xff0…

【vue.js】文档解读【day 3】 | 列表渲染

如果阅读有疑问的话&#xff0c;欢迎评论或私信&#xff01;&#xff01; 文章目录 列表渲染v-forv-for 与对象在 v-for 里使用范围值template 上的 v-forv-for与v-if通过key管理状态组件上使用v-for数组变化侦测 列表渲染 v-for 在我们想要渲染出一个数组中的元素时&#xf…

node-day3-es6模块化+webpack

模块化 一、模块化分类 回顾node.js模块化&#xff1a; node.js遵循了CommonJS的模块化规范【见下文】&#xff0c;其中&#xff1a; 1.导入其它模块使用require()方法 2.模块对外共享成员使用module.exports对象 模块化的好处&#xff1a; 大家都遵守同样的模块化规范写代…

Only fullscreen opaque activities can request orientation

关于作者&#xff1a;CSDN内容合伙人、技术专家&#xff0c; 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 &#xff0c;擅长java后端、移动开发、商业变现、人工智能等&#xff0c;希望大家多多支持。 未经允许不得转载 目录 一、导读二、概览三、分析四、 推荐阅…