9.Flink实时项目之订单宽表

article/2025/10/12 9:10:14

1.需求分析

订单是统计分析的重要的对象,围绕订单有很多的维度统计需求,比如用户、地区、商品、品类、品牌等等。为了之后统计计算更加方便,减少大表之间的关联,所以在实时计算过程中将围绕订单的相关数据整合成为一张订单的宽表。那究竟哪些数据需要和订单整合在一起?

如上图,由于在之前的操作(BaseDbTask)我们已经把数据分拆成了事实数据和维度数据,事实数据(绿色)进入 kafka 数据流(DWD 层)中,维度数据(蓝色)进入 hbase 中长期保存。那么我们在 DWM 层中要把实时和维度数据进行整合关联在一起,形成宽表。那么这里就要处理有两种关联,事实数据和事实数据关联、事实数据和维度数据关联。

  • 事实数据和事实数据关联,其实就是流与流之间的关联。
  • 事实数据与维度数据关联,其实就是流计算中查询外部数据源。

数据流程走向

2. 创建实体类

import java.math.BigDecimal;
/*** @author zhangbao* @date 2021/10/25 19:55* @desc 订单*/
@Data
public class OrderInfo {Long id;Long province_id;String order_status;Long user_id;BigDecimal total_amount;BigDecimal activity_reduce_amount;BigDecimal coupon_reduce_amount;BigDecimal original_total_amount;BigDecimal feight_fee;String expire_time;String create_time;String operate_time;String create_date; // 把其他字段处理得到String create_hour;Long create_ts;
}
import java.math.BigDecimal;
/*** @author zhangbao* @date 2021/10/25 19:55* @desc 订单明细*/
@Data
public class OrderDetail {Long id;Long order_id ;Long sku_id;BigDecimal order_price ;Long sku_num ;String sku_name;String create_time;BigDecimal split_total_amount;BigDecimal split_activity_amount;BigDecimal split_coupon_amount;Long create_ts;
}

3. 消费kafka事实数据

在dwm包下创建任务OrderWideApp.java,对订单及明细数据做格式转换,在这个阶段可以做一些ETL操作。

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONObject;
import com.zhangbao.gmall.realtime.bean.OrderDetail;
import com.zhangbao.gmall.realtime.bean.OrderInfo;
import com.zhangbao.gmall.realtime.utils.MyKafkaUtil;
import org.apache.flink.api.common.functions.RichMapFunction;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.runtime.state.filesystem.FsStateBackend;
import org.apache.flink.streaming.api.CheckpointingMode;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.connectors.kafka.FlinkKafkaConsumer;/*** @author zhangbao* @date 2021/10/25 19:58* @desc*/
public class OrderWideApp {public static void main(String[] args) {//webui模式,需要添加pom依赖StreamExecutionEnvironment env = StreamExecutionEnvironment.createLocalEnvironmentWithWebUI(new Configuration());
//        StreamExecutionEnvironment env1 = StreamExecutionEnvironment.createLocalEnvironment();//设置并行度env.setParallelism(4);//设置检查点
//        env.enableCheckpointing(5000, CheckpointingMode.EXACTLY_ONCE);
//        env.getCheckpointConfig().setCheckpointTimeout(60000);
//        env.setStateBackend(new FsStateBackend("hdfs://hadoop101:9000/gmall/flink/checkpoint/uniqueVisit"));
//        //指定哪个用户读取hdfs文件
//        System.setProperty("HADOOP_USER_NAME","zhangbao");//从kafka的dwd主题获取订单和订单详情String orderInfoTopic = "dwd_order_info";String orderDetailTopic = "dwd_order_detail";String orderWideTopic = "dwm_order_wide";String orderWideGroup = "order_wide_group";//订单数据FlinkKafkaConsumer<String> orderInfoSource = MyKafkaUtil.getKafkaSource(orderInfoTopic, orderWideGroup);DataStreamSource<String> orderInfoDs = env.addSource(orderInfoSource);//订单详情数据FlinkKafkaConsumer<String> orderDetailSource = MyKafkaUtil.getKafkaSource(orderDetailTopic, orderWideGroup);DataStreamSource<String> orderDetailDs = env.addSource(orderDetailSource);//对订单数据进行转换SingleOutputStreamOperator<OrderInfo> orderInfoObjDs = orderInfoDs.map(new RichMapFunction<String, OrderInfo>() {@Overridepublic OrderInfo map(String jsonStr) throws Exception {System.out.println("order info str >>> "+jsonStr);OrderInfo orderInfo = JSONObject.parseObject(jsonStr, OrderInfo.class);DateTime createTime = DateUtil.parse(orderInfo.getCreate_time(), "yyyy-MM-dd HH:mm:ss");orderInfo.setCreate_ts(createTime.getTime());return orderInfo;}});//对订单明细数据进行转换SingleOutputStreamOperator<OrderDetail> orderDetailObjDs = orderDetailDs.map(new RichMapFunction<String, OrderDetail>() {@Overridepublic OrderDetail map(String jsonStr) throws Exception {System.out.println("order detail str >>> "+jsonStr);OrderDetail orderDetail = JSONObject.parseObject(jsonStr, OrderDetail.class);DateTime createTime = DateUtil.parse(orderDetail.getCreate_time(), "yyyy-MM-dd HH:mm:ss");orderDetail.setCreate_ts(createTime.getTime());return orderDetail;}});orderInfoDs.print("order info >>>");orderDetailDs.print("order detail >>>");try {env.execute("order wide task");} catch (Exception e) {e.printStackTrace();}}
}

4. 双流join准备

在 flink 中的流 join 大体分为两种,一种是基于时间窗口的 join(Time Windowed Join),比如 join、coGroup 等。另一种是基于状态缓存的 join(Temporal Table Join),比如 intervalJoin。这里选用 intervalJoin,因为相比较窗口 join,intervalJoin 使用更简单,而且避免了应匹配的数据处于不同窗口的问题。

intervalJoin 目前只有一个问题,就是还不支持 left join。但是我们这里是订单主表与订单从表之间的关联不需要 left join,所以 intervalJoin 是较好的选择。

官方文档:interval-join

先设置时间水位线,然后在分组

//指定事件时间字段
//订单事件时间字段
SingleOutputStreamOperator<OrderInfo> orderInfoWithTsDs = orderInfoObjDs.assignTimestampsAndWatermarks(WatermarkStrategy.<OrderInfo>forBoundedOutOfOrderness(Duration.ofSeconds(3)).withTimestampAssigner(new SerializableTimestampAssigner<OrderInfo>() {@Overridepublic long extractTimestamp(OrderInfo orderInfo, long l) {return orderInfo.getCreate_ts();}})
);
//订单明细指定事件事件字段
SingleOutputStreamOperator<OrderDetail> orderDetailWithTsDs = orderDetailObjDs.assignTimestampsAndWatermarks(WatermarkStrategy.<OrderDetail>forBoundedOutOfOrderness(Duration.ofSeconds(3)).withTimestampAssigner(new SerializableTimestampAssigner<OrderDetail>() {@Overridepublic long extractTimestamp(OrderDetail orderDetail, long l) {return orderDetail.getCreate_ts();}})
);//分组
KeyedStream<OrderInfo, Long> orderInfoKeysDs = orderInfoWithTsDs.keyBy(OrderInfo::getId);
KeyedStream<OrderDetail, Long> orderDetailKeysDs = orderDetailWithTsDs.keyBy(OrderDetail::getId);

5. 建立订单宽表

import lombok.AllArgsConstructor;
import lombok.Data;
import org.apache.commons.lang3.ObjectUtils;import java.math.BigDecimal;/*** @author zhangbaohpu* @date  2021/11/13 11:10* @desc 订单宽表*/
@Data
@AllArgsConstructor
public class OrderWide {Long detail_id;Long order_id ;Long sku_id;BigDecimal order_price ;Long sku_num ;String sku_name;Long province_id;String order_status;Long user_id;BigDecimal total_amount;BigDecimal activity_reduce_amount;BigDecimal coupon_reduce_amount;BigDecimal original_total_amount;BigDecimal feight_fee;BigDecimal split_feight_fee;BigDecimal split_activity_amount;BigDecimal split_coupon_amount;BigDecimal split_total_amount;String expire_time;String create_time;String operate_time;String create_date; // 把其他字段处理得到String create_hour;String province_name;//查询维表得到String province_area_code;String province_iso_code;String province_3166_2_code;Integer user_age ;String user_gender;Long spu_id; //作为维度数据 要关联进来Long tm_id;Long category3_id;String spu_name;String tm_name;String category3_name;public OrderWide(OrderInfo orderInfo, OrderDetail orderDetail){mergeOrderInfo(orderInfo);mergeOrderDetail(orderDetail);}public void mergeOrderInfo(OrderInfo orderInfo ) {if (orderInfo != null) {this.order_id = orderInfo.id;this.order_status = orderInfo.order_status;this.create_time = orderInfo.create_time;this.create_date = orderInfo.create_date;this.activity_reduce_amount = orderInfo.activity_reduce_amount;this.coupon_reduce_amount = orderInfo.coupon_reduce_amount;this.original_total_amount = orderInfo.original_total_amount;this.feight_fee = orderInfo.feight_fee;this.total_amount = orderInfo.total_amount;this.province_id = orderInfo.province_id;this.user_id = orderInfo.user_id;}}public void mergeOrderDetail(OrderDetail orderDetail ) {if (orderDetail != null) {this.detail_id = orderDetail.id;this.sku_id = orderDetail.sku_id;this.sku_name = orderDetail.sku_name;this.order_price = orderDetail.order_price;this.sku_num = orderDetail.sku_num;this.split_activity_amount=orderDetail.split_activity_amount;this.split_coupon_amount=orderDetail.split_coupon_amount;this.split_total_amount=orderDetail.split_total_amount;}}public void mergeOtherOrderWide(OrderWide otherOrderWide){this.order_status =ObjectUtils.firstNonNull( this.order_status ,otherOrderWide.order_status);this.create_time =ObjectUtils.firstNonNull(this.create_time,otherOrderWide.create_time);this.create_date =ObjectUtils.firstNonNull(this.create_date,otherOrderWide.create_date);this.coupon_reduce_amount =ObjectUtils.firstNonNull(this.coupon_reduce_amount,otherOrderWide.coupon_reduce_amount);this.activity_reduce_amount =ObjectUtils.firstNonNull(this.activity_reduce_amount,otherOrderWide.activity_reduce_amount);this.original_total_amount =ObjectUtils.firstNonNull(this.original_total_amount,otherOrderWide.original_total_amount);this.feight_fee = ObjectUtils.firstNonNull( this.feight_fee,otherOrderWide.feight_fee);this.total_amount =ObjectUtils.firstNonNull( this.total_amount,otherOrderWide.total_amount);this.user_id = ObjectUtils.<Long>firstNonNull(this.user_id,otherOrderWide.user_id);this.sku_id = ObjectUtils.firstNonNull( this.sku_id,otherOrderWide.sku_id);this.sku_name = ObjectUtils.firstNonNull(this.sku_name,otherOrderWide.sku_name);this.order_price =ObjectUtils.firstNonNull(this.order_price,otherOrderWide.order_price);this.sku_num = ObjectUtils.firstNonNull( this.sku_num,otherOrderWide.sku_num);this.split_activity_amount=ObjectUtils.firstNonNull(this.split_activity_amount);this.split_coupon_amount=ObjectUtils.firstNonNull(this.split_coupon_amount);this.split_total_amount=ObjectUtils.firstNonNull(this.split_total_amount);} }

6. 双流join

在做好数据封装,并标记时间水位线,我们可以做订单和订单明细表的双流join操作了。

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONObject;
import com.zhangbao.gmall.realtime.bean.OrderDetail;
import com.zhangbao.gmall.realtime.bean.OrderInfo;
import com.zhangbao.gmall.realtime.bean.OrderWide;
import com.zhangbao.gmall.realtime.utils.MyKafkaUtil;
import org.apache.flink.api.common.eventtime.SerializableTimestampAssigner;
import org.apache.flink.api.common.eventtime.WatermarkStrategy;
import org.apache.flink.api.common.functions.RichMapFunction;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.datastream.KeyedStream;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.co.ProcessJoinFunction;
import org.apache.flink.streaming.api.windowing.time.Time;
import org.apache.flink.streaming.connectors.kafka.FlinkKafkaConsumer;
import org.apache.flink.util.Collector;import java.time.Duration;/*** @author zhangbao* @date 2021/10/25 19:58* @desc* 启动服务*      zk > kf > maxwell > hdfs > hbase > baseDbTask > OrderWideApp > mysql配置表* 业务流程*      模拟生成数据*      maxwell监控mysql数据*      kafka接收maxwell发送的数据,放入ODS层(ods_base_db_m)*      baseDbTask消费kafka的主题数据并进行分流*          从mysql读取配置表*          将配置缓存到map集合中*          检查phoenix(hbase的皮肤)是否存在表*          对数据表进行分流发送到不同dwd层主题*/
public class OrderWideApp {public static void main(String[] args) {//webui模式,需要添加pom依赖StreamExecutionEnvironment env = StreamExecutionEnvironment.createLocalEnvironmentWithWebUI(new Configuration());
//        StreamExecutionEnvironment env1 = StreamExecutionEnvironment.createLocalEnvironment();//设置并行度env.setParallelism(4);//设置检查点
//        env.enableCheckpointing(5000, CheckpointingMode.EXACTLY_ONCE);
//        env.getCheckpointConfig().setCheckpointTimeout(60000);
//        env.setStateBackend(new FsStateBackend("hdfs://hadoop101:9000/gmall/flink/checkpoint/uniqueVisit"));
//        //指定哪个用户读取hdfs文件
//        System.setProperty("HADOOP_USER_NAME","zhangbao");//从kafka的dwd主题获取订单和订单详情String orderInfoTopic = "dwd_order_info";String orderDetailTopic = "dwd_order_detail";String orderWideTopic = "dwm_order_wide";String orderWideGroup = "order_wide_group";//订单数据FlinkKafkaConsumer<String> orderInfoSource = MyKafkaUtil.getKafkaSource(orderInfoTopic, orderWideGroup);DataStreamSource<String> orderInfoDs = env.addSource(orderInfoSource);//订单详情数据FlinkKafkaConsumer<String> orderDetailSource = MyKafkaUtil.getKafkaSource(orderDetailTopic, orderWideGroup);DataStreamSource<String> orderDetailDs = env.addSource(orderDetailSource);//对订单数据进行转换SingleOutputStreamOperator<OrderInfo> orderInfoObjDs = orderInfoDs.map(new RichMapFunction<String, OrderInfo>() {@Overridepublic OrderInfo map(String jsonStr) throws Exception {System.out.println("order info str >>> "+jsonStr);OrderInfo orderInfo = JSONObject.parseObject(jsonStr, OrderInfo.class);DateTime createTime = DateUtil.parse(orderInfo.getCreate_time(), "yyyy-MM-dd HH:mm:ss");orderInfo.setCreate_ts(createTime.getTime());return orderInfo;}});//对订单明细数据进行转换SingleOutputStreamOperator<OrderDetail> orderDetailObjDs = orderDetailDs.map(new RichMapFunction<String, OrderDetail>() {@Overridepublic OrderDetail map(String jsonStr) throws Exception {System.out.println("order detail str >>> "+jsonStr);OrderDetail orderDetail = JSONObject.parseObject(jsonStr, OrderDetail.class);DateTime createTime = DateUtil.parse(orderDetail.getCreate_time(), "yyyy-MM-dd HH:mm:ss");orderDetail.setCreate_ts(createTime.getTime());return orderDetail;}});orderInfoObjDs.print("order info >>>");orderDetailObjDs.print("order detail >>>");//指定事件时间字段//订单事件时间字段SingleOutputStreamOperator<OrderInfo> orderInfoWithTsDs = orderInfoObjDs.assignTimestampsAndWatermarks(WatermarkStrategy.<OrderInfo>forBoundedOutOfOrderness(Duration.ofSeconds(3)).withTimestampAssigner(new SerializableTimestampAssigner<OrderInfo>() {@Overridepublic long extractTimestamp(OrderInfo orderInfo, long l) {return orderInfo.getCreate_ts();}}));//订单明细指定事件事件字段SingleOutputStreamOperator<OrderDetail> orderDetailWithTsDs = orderDetailObjDs.assignTimestampsAndWatermarks(WatermarkStrategy.<OrderDetail>forBoundedOutOfOrderness(Duration.ofSeconds(3)).withTimestampAssigner(new SerializableTimestampAssigner<OrderDetail>() {@Overridepublic long extractTimestamp(OrderDetail orderDetail, long l) {return orderDetail.getCreate_ts();}}));//分组KeyedStream<OrderInfo, Long> orderInfoKeysDs = orderInfoWithTsDs.keyBy(OrderInfo::getId);KeyedStream<OrderDetail, Long> orderDetailKeysDs = orderDetailWithTsDs.keyBy(OrderDetail::getOrder_id);/*** interval-join* https://nightlies.apache.org/flink/flink-docs-release-1.14/docs/dev/datastream/operators/joining/#interval-join*/SingleOutputStreamOperator<OrderWide> orderWideDs = orderInfoKeysDs.intervalJoin(orderDetailKeysDs).between(Time.milliseconds(-5), Time.milliseconds(5)).process(new ProcessJoinFunction<OrderInfo, OrderDetail, OrderWide>() {@Overridepublic void processElement(OrderInfo orderInfo, OrderDetail orderDetail, ProcessJoinFunction<OrderInfo, OrderDetail, OrderWide>.Context context, Collector<OrderWide> out) throws Exception {out.collect(new OrderWide(orderInfo, orderDetail));}});orderWideDs.print("order wide ds >>>");try {env.execute("order wide task");} catch (Exception e) {e.printStackTrace();}}
}

到这里我们就把订单的部分宽表数据给做出来了,下一节,我们再把一些维度数据给关联进来,就形成了一个完整订单宽表。

更多请在某公号平台搜索:选手一号位,本文编号:1009,回复即可获取。


http://chatgpt.dhexx.cn/article/q4e7Olu5.shtml

相关文章

数仓建模—宽表的设计

宽表的设计 高内聚低耦合 宽表是数仓里面非常重要的一块&#xff0c;数仓是分层的&#xff0c;这是技术进步和时代变化相结合的产物&#xff0c;数仓的分层式为了更好地管理数仓以及更加高效地进行数据开发。 宽表主要出现在dwd 层和报表层&#xff0c;当然有的人说dws 层也有…

基于宽表的数据建模应用

一、业务背景 1.1 数据建模现状 互联网企业往往存在多个产品线&#xff0c;每天源源不断产出大量数据&#xff0c;这些数据服务于数据分析师、业务上的产品经理、运营、数据开发人员等各角色。为了满足这些角色的各种需求&#xff0c;业界传统数仓常采用的是经典分层模型的数…

数据仓库宽表

1. 构建宽表的目的 讲宽表我想从为什么需要宽表入手&#xff0c;而不是一上来就抠概念。因为我觉得一门知识叫什么名字并不是最核心的&#xff0c;关键是搞清楚它的诞生背景以及如何在特定场景用好它。 构建宽表的目的很简单,就是为了"一站式"尽可能多的展示我们需要…

宽表, 窄表, 维度表, 事实表的区别

在数据开发里, 会涉及到一些概念: 宽表, 窄表, 维度表, 事实表 宽表: 把多个维度的字段都放在一张表存储, 增加数据冗余是为了减少关联, 便于查询. 查询一张表就可以查出不同维度的多个字段窄表: 和我们 mysql 普通表三范式相同, 把相同维度的字段组成一张表, 表和表之间关联查…

[转]科普 | 什么是宽表?

科普 | 什么是宽表&#xff1f;一文带你了解 数据仓库宽表_数据宽表_吕归尘0的博客-CSDN博客 一、什么是“宽表”&#xff1f; “宽表”从字面上的意思就是字段&#xff08;列&#xff09;比较多的数据库表&#xff0c;是通过关联字段将多个业务主题相关的数据表进行挂接组装…

数仓建模,宽表是什么?如何设计?

数仓建模&#xff0c;宽表是什么&#xff1f;如何设计&#xff1f; 宽表的设计为什么要建设宽表宽表的好处和不足如何设计宽表总结 宽表的设计 其实宽表是数仓里面非常重要的一块&#xff0c;宽表主要出现在dwd 层和报表层&#xff0c;当然有的人说dws 层也有宽表&#xff0c;…

线性代数笔记22——特征值和特征向量

特征向量 函数通常作用在数字上&#xff0c;比如函数f作用在x上&#xff0c;结果得到了f(x)。在线性代数中&#xff0c;我们将x扩展到多维&#xff0c;对于Ax来说&#xff0c;矩阵A的作用就像一个函数&#xff0c;输入一个向量x&#xff0c;通过A的作用&#xff0c;得到向量Ax。…

特征值和特征向量意义

本文转载自https://blog.csdn.net/fuming2021118535/article/details/51339881&#xff0c;版权问题请联系博主删除 在刚开始学的特征值和特征向量的时候只是知道了定义和式子&#xff0c;并没有理解其内在的含义和应用&#xff0c;这段时间整理了相关的内容&#xff0c;跟大家…

特征值和特征向量的几何含义理解

在刚开始学的特征值和特征向量的时候只是知道了定义和式子&#xff0c;并没有理解其内在的含义和应用&#xff0c;这段时间整理了相关的内容&#xff0c;跟大家分享一下&#xff1b; 首先我们先把特征值和特征向量的定义复习一下&#xff1a; 定义&#xff1a; 设A是n阶矩阵&am…

线性代数之——特征值和特征向量

线性方程 A x b Axb Axb 是稳定状态的问题&#xff0c;特征值在动态问题中有着巨大的重要性。 d u / d t A u du/dtAu du/dtAu 的解随着时间增长、衰减或者震荡&#xff0c;是不能通过消元来求解的。接下来&#xff0c;我们进入线性代数一个新的部分&#xff0c;基于 A x …

特征值和特征向量概述-面试必问3(含特征值、向量意义)

特征值和特征向量&#xff08;Eigenvalues and eigenvectors&#xff09; 在线性代数中&#xff0c;一个线性变换的特征向量&#xff08;eigenvector 或者 characteristic vector&#xff09;是一个非零向量。将线性变换应用在它上面&#xff0c;它最多以一个标量因子进行伸缩…

java 如何实现深拷贝

1、什么叫Java浅拷贝&#xff1f;  浅拷贝是按位拷贝对象&#xff0c;它会创建一个新对象&#xff0c;这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型&#xff0c;拷贝的就是基本类型的值&#xff1b;如果属性是内存地址&#xff08;引用类型&#xff09;&…

单例模式之枚举实现

如果你没有学过单例模式&#xff0c;请点击&#xff1a;确保对象的唯一性——单例模式。 有很多网友留言说我漏掉了一种非常重要的Java语言的单例模式实现方式——枚举。^_^ 这篇姗姗来迟的博文将弥补这个“巨大的”缺陷。^_^~~~~~~~~~~~ 在Java语言中&#xff0c;如果综合考虑…

vue实现购物车功能

随着时代发展&#xff0c;网购成了人们必不可少的一部分&#xff0c;所以我们常常遇到要实现购物车功能&#xff0c;如下图&#xff0c;我们来分析一下 下图所示页面: 首先&#xff0c;我们通过ElementUI中的<el-table>标签来实现页面的呈现。 其次&#xff0c;我们可以看…

css实现轮播图

轮播图&#xff1a;就是多张图片按照一定的时间和顺序依次从某个窗口来向用户展示图片 轮播图的实现代码&#xff1a; 1&#xff09;创建一个容器来进行轮播图的展示 这里的容器就是最外部的盒子 注意最外部盒子设置宽高时要与我们进行展示的图片的宽高保持一致&#xff0c…

RabbitMQ实现延迟队列的方式

1.背景 最近在做类似拍卖系统的上架功能&#xff0c;卖家上架物品以后&#xff0c;例如到期时间24小时或者48小时&#xff0c;如果无竞拍者或者购买者&#xff0c;则物品自动下架到用户的邮件中。诸如电商用户下单&#xff0c;30分钟未支付&#xff0c;则自动取消订单&#xff…

接口的实现详解

接口 接口就是定义的规则&#xff0c;规范。 声明类时需要使用的关键字时class&#xff0c;声明接口的关键字时interface&#xff1b; 接口本身就是抽象的&#xff0c;需要一个实现类去实现接口中定义的内容。 接口当中不能定义方法&#xff1a; 接口本身就是抽象的 所在我们…

Qt实现简易计算器

目录 写在前面 一、设计思路 效果展示&#xff1a; 二、功能实现 三、设计代码 1.mainwindow.cpp 2.calculate.cpp 写在前面 上篇文章写了C中如何实现简单的计算器&#xff0c;先用C写看来我的选择是正确的&#xff0c;明白了其中的原理再用Qt实现是水到渠成的事&#…

vue实现复制功能

目录 一、vue实现复制功能 1.功能实现 2.模板结构 3.js行为 4.样式 二.延伸扩展 一、vue实现复制功能 1.功能实现 点击复制弹出复制成功信息&#xff0c;粘贴即可获得复制数据。 2.模板结构 在自己想要复制的内容所在标签上添加一个类名和一个点击事件方法&#xff0c;…

多态的实现

多态实现条件 在Java中要实现多态&#xff0c;那么必须要满足以下几个条件&#xff0c;缺一不可&#xff1a; 必须在继承体系下 子类必须要对父类中的方法进行重写 通过父类的引用调用重写的方法 多态体现&#xff1a;在代码运行时&#xff0c;当传递不同类对象时&#xff0c;…