新闻详情

新闻详情

首页 / 资讯中心 / 详情

Flink SQL 之 Table API 原理及代码实现:从表达式体系到内部执行流程与实战最佳实践

发布时间:2026/9/27 11:28:19来源:尧图网络
Flink SQL 之 Table API 原理及代码实现:从表达式体系到内部执行流程与实战最佳实践
上一篇讲了 TableEnvironment——Flink SQL 的统一入口和核心上下文。这篇讲 Table API它是 Flink SQL 的另一种表达方式不写 SQL 字符串而是用 Java 方法链式调用来构建查询。很多同学用 Flink SQL 时只写 SQL 字符串对 Table API 了解不多。实际上 Table API 在类型安全、IDE 支持、动态查询构建、复杂逻辑分步表达方面有明显优势。生产环境中Java 开发为主的团队用 Table API 写复杂查询效率和可维护性都比拼 SQL 字符串高很多。这篇从本质定义、与 SQL 对比、八大操作分类、六步内部执行流程、Expression 表达式体系、完整代码实现、六个高级特性、六个常见坑、八条最佳实践把 Table API 一次性讲透。一、Table API 本质定义Table API 的本质一句话概括声明式、链式调用、类型安全的关系型 API与 SQL 共享 Planner 和执行引擎最终都被翻译成 Transformation DAG 执行。下面这张图把 Table API 的本质定义、与 SQL 的对比、八大操作分类放在一起展示。可以把 Table API 理解为用 Java 方法调用写 SQL每个方法调用对应一个 SQL 子句链式调用对应 SQL 的嵌套结构。比如table.select($(a), $(b)).filter($(a).isGreater(10))等价于SELECT a, b FROM t WHERE a 10。1.1 三大核心特点第一声明式链式调用。用户只描述要什么不描述怎么做。每个方法调用返回一个新的 Table 对象链式调用形成完整的查询逻辑。方法名语义清晰select对应投影、filter对应过滤、groupBy对应分组、join对应连接。第二类型安全。列名和类型在编译期检查通过 Expression 表达式体系SQL 字符串的错误要运行时才发现。IDE 提供自动补全、重构、跳转、调试写 Table API 的体验和写普通 Java 代码一样。第三与 SQL 共享 Planner 和执行引擎。Table API 和 SQL 最终走完全相同的执行路径构建 Operation 树 → Planner 翻译为 Calcite RelNode → 优化 → 翻译为 Transformation DAG → 生成 JobGraph 执行。两者的区别仅在于 Operation 树的构建方式方法调用 vs SQL 解析。1.2 Table API 与 SQL 的对比对比维度Table APISQL表达方式Java/Python 方法链式调用SQL 字符串类型安全编译期检查列名和类型运行期检查IDE 支持自动补全/重构/跳转/调试字符串无补全需插件动态查询容易Java 变量/循环/条件构建困难字符串拼接易出错复杂逻辑清晰分步构建变量名可读嵌套子查询可读性差学习成本较高需学 API 方法和表达式低标准 SQL分析师也能写适用场景Java 开发为主、复杂动态查询数据分析师、adhoc 查询、简单固定查询生产环境建议混合使用。Table API 构建复杂逻辑多步转换、动态条件、自定义函数SQL 执行简单查询固定的 DDL/DML。两者可以通过createTemporaryView衔接Table API 结果注册为临时视图然后用 SQL 查询。二、Table API 八大操作分类Table API 的操作可以归纳为八大类每个分类都有关键方法和使用场景2.1 查询与投影select(...)选择列或表达式对应 SQL 的 SELECTas(...)重命名列对应 SQL 的 ASfilter(...)/where(...)过滤行对应 SQL 的 WHEREdistinct()去重对应 SQL 的 DISTINCTlimit(n)限制行数批处理对应 SQL 的 LIMIT2.2 聚合groupBy(...)分组对应 SQL 的 GROUP BYselect(...).aggregate(...)自定义聚合AggregateFunctionselect(...).flatAggregate(...)自定义表聚合TableAggregateFunction如 TopN内置聚合sum/count/avg/min/max2.3 连接join(...)内连接对应 SQL 的 JOINleftOuterJoin(...)左外连接rightOuterJoin(...)右外连接fullOuterJoin(...)全外连接intervalJoin(...)时间区间连接流处理专用2.4 窗口window(...)定义窗口Tumble.over(...)滚动窗口Slide.over(...).every(...)滑动窗口Session.withGap(...)会话窗口Over.partitionBy(...).orderBy(...).preceding(...)Over 窗口窗口属性$(w).start()/$(w).end()/$(w).rowtime()2.5 排序orderBy(...)排序批处理对应 SQL 的 ORDER BYfetch(n)取前 n 行offset(n)跳过 n 行排序方向$(a).desc()/$(a).asc()2.6 集合操作union(...)并集去重unionAll(...)并集不去重intersect(...)交集去重intersectAll(...)交集不去重minus(...)差集去重minusAll(...)差集不去重2.7 行级操作map(...)一行转一行ScalarFunctionflatMap(...)一行转多行TableFunctionaggregate(...)多行转一行AggregateFunctionflatAggregate(...)多行转多行TableAggregateFunction支持自定义函数和 lambda 表达式2.8 输出与执行executeInsert(tableName)写入表并执行execute()执行查询返回结果execute().print()执行并打印explain()查看执行计划toDataStream(table)/toChangelogStream(table)转换为 DataStream三、Table API 内部执行流程理解了本质和操作分类下面深入内部执行流程看 Table API 的方法调用到底是怎么变成执行计划的。下面这张图把六步执行流程、Expression 表达式体系、代码示例放在一起展示。3.1 六步执行流程一条 Table API 链式调用从输入到执行完整的流程分为六步方法调用用户调用 Table 对象上的方法select/filter/groupBy 等构建 Operation每个方法调用构建一个 Operation 对象QueryOperationOperation 树链式调用形成 Operation 树描述完整查询逻辑Planner 翻译Planner 将 Operation 树翻译为 Calcite RelNode优化执行Calcite 优化 RelNode翻译为 Transformation DAG提交执行生成 JobGraph 提交到集群TaskManager 执行关键理解Table API 的方法调用不会立即执行而是构建一个 Operation 树。每个方法调用返回一个新的 Table 对象内部持有一个 Operation 节点链式调用形成 Operation 树。只有调用execute()、executeInsert()或executeSql()时才会触发 Planner 翻译、优化和执行。这也是 Table API 比 SQL 更适合构建复杂动态查询的原因——可以在 Java 代码中用变量、循环、条件判断灵活构建查询最后统一执行。3.2 与 SQL 的执行路径对比SQL 字符串经过 Parser 解析后也生成 Operation 树然后走同样的 Planner 翻译和优化流程。所以 Table API 和 SQL 最终执行路径完全一致区别仅在于 Operation 树的构建方式Table API方法调用 → 直接构建 Operation 节点 → 链式调用形成 Operation 树SQLSQL 字符串 → Parser 解析生成 AST → Validator 校验 → 转换为 Operation 树理解了这一点就理解了为什么 Table API 和 SQL 可以混合使用、可以通过临时视图衔接——它们的中间表示Operation 树是完全相同的。四、Expression 表达式体系Table API 的核心是 Expression 表达式所有的列引用、计算、函数调用都是 Expression。理解了 Expression 体系就掌握了 Table API 的用法。4.1 列引用Field Reference引用表中的列是最基础的 Expression$(colName)通过列名引用推荐类型安全$(0)通过列索引引用从 0 开始col(colName)另一种列引用方式注意$()是 Flink 提供的静态方法需要import static org.apache.flink.table.api.Expressions.*。这是 Table API 最常见的编译错误新手必踩。4.2 字面量Literal常量值表达式lit(10)整数字面量lit(hello)字符串字面量lit(true)布尔字面量lit(new BigDecimal(10.5))Decimal 字面量NULL空值字面量4.3 算术运算Arithmetic数值计算表达式$(a).plus($(b))加法$(a).minus($(b))减法$(a).times($(b))乘法$(a).dividedBy($(b))除法$(a).mod($(b))取模支持与字面量混合$(amount).times(lit(0.8))计算折扣金额。4.4 比较运算Comparison比较表达式返回布尔值$(a).isEqual($(b))等于$(a).isNotEqual($(b))不等于$(a).isGreater($(b))大于$(a).isGreaterOrEqual($(b))大于等于$(a).isLess($(b))小于$(a).between($(b), $(c))在范围内4.5 逻辑运算Logical布尔逻辑表达式$(a).and($(b))与$(a).or($(b))或$(a).not()非$(a).xor($(b))异或$(a).ifNull($(b))如果为 null 则取 b4.6 函数调用Function Call调用内置函数或自定义函数$(name).upperCase()字符串转大写$(amount).sum()聚合求和call(myFunc, $(a), $(b))调用自定义函数$(ts).toDate()类型转换$(arr).elementAt(lit(0))数组元素访问4.7 时间属性Time Attribute事件时间和处理时间属性$(ts).rowtime()声明为事件时间列$(pt).proctime()声明为处理时间列$(ts).minus(lit(5).seconds())Watermark 策略事件时间列用于窗口计算处理时间列用于基于系统时间的操作。4.8 条件表达式Conditional条件判断和分支表达式$(a).isGreater(10).then(lit(big)).otherwise(lit(small))if-else$(a).isNull()判断是否为 null$(a).isNotNull()判断不为 null$(a).in(lit(1), lit(2), lit(3))在列表中$(a).like(lit(A%))SQL LIKE 匹配五、Table API 完整代码实现下面是一个完整的 Table API 使用示例包含创建环境、DDL 建表、Table API 查询、聚合、注册临时视图、输出执行。下面这张图把完整代码实现、高级特性、常见坑、最佳实践放在一起展示。importorg.apache.flink.streaming.api.environment.StreamExecutionEnvironment;importorg.apache.flink.table.api.Table;importorg.apache.flink.table.api.TableResult;importorg.apache.flink.table.api.bridge.java.StreamTableEnvironment;importjava.time.ZoneId;importstaticorg.apache.flink.table.api.Expressions.*;publicclassTableApiExample{publicstaticvoidmain(String[]args)throwsException{// 1. 创建执行环境StreamExecutionEnvironmentenvStreamExecutionEnvironment.getExecutionEnvironment();env.enableCheckpointing(60000);StreamTableEnvironmenttableEnvStreamTableEnvironment.create(env);tableEnv.getConfig().setLocalTimeZone(ZoneId.of(Asia/Shanghai));// 2. DDL创建Kafka源表tableEnv.executeSql( CREATE TABLE orders ( order_id BIGINT, user_id BIGINT, amount DECIMAL(10,2), status STRING, order_time TIMESTAMP(3), WATERMARK FOR order_time AS order_time - INTERVAL 5 SECOND ) WITH ( connector kafka, topic orders, properties.bootstrap.servers localhost:9092, format json ) );// 3. DDL创建MySQL结果表tableEnv.executeSql( CREATE TABLE user_stat ( user_id BIGINT, total_amount DECIMAL(10,2), order_count BIGINT, PRIMARY KEY (user_id) NOT ENFORCED ) WITH ( connector jdbc, url jdbc:mysql://localhost:3306/flink_db, table-name user_stat, username root, password 123456 ) );// 4. Table API过滤 计算 选择TablepaidOrderstableEnv.from(orders).filter($(status).isEqual(lit(PAID))).select($(order_id),$(user_id),$(amount).times(lit(0.8)).as(discount_amount),$(order_time));// 5. Table API分组聚合TableuserStatpaidOrders.groupBy($(user_id)).select($(user_id),$(discount_amount).sum().as(total_amount),$(order_id).count().as(order_count));// 6. 注册临时视图供后续SQL或Table API复用tableEnv.createTemporaryView(paid_orders_view,paidOrders);// 7. 写入结果表并执行TableResultresultuserStat.executeInsert(user_stat);// 8. 等待作业完成result.await();}}代码关键点静态导入 Expressionsimport static org.apache.flink.table.api.Expressions.*这是 Table API 的第一步否则$()、lit()方法找不到。创建 StreamTableEnvironment绑定 StreamExecutionEnvironment支持 DataStream 互转。DDL 建表Kafka 源表带 Watermark 定义MySQL 结果表带主键定义Upsert 模式。Table API 过滤计算filter($(status).isEqual(lit(PAID)))过滤已支付订单select($(amount).times(lit(0.8)).as(discount_amount))计算折扣金额并重命名。分组聚合groupBy($(user_id))按用户分组select($(discount_amount).sum().as(total_amount), $(order_id).count().as(order_count))聚合注意聚合后必须用as()命名结果列。注册临时视图createTemporaryView(paid_orders_view, paidOrders)将中间 Table 注册为视图供后续 SQL 或 Table API 复用。写入执行userStat.executeInsert(user_stat)写入结果表并触发执行返回 TableResult。等待完成result.await()等待作业完成executeInsert 已经触发执行不需要再调用 env.execute()。六、Table API 高级特性掌握了基础用法下面看六个高级特性这些是写出生产级 Table API 代码的关键。6.1 行级操作Map/FlatMapmap一行转一行使用 ScalarFunction 或 lambda。Tableresulttable.map(call(MyScalarFunc,$(a))).as(result);flatMap一行转多行使用 TableFunction。Tableresulttable.flatMap(call(MyTableFunc,$(a))).as(col1,col2);自定义函数需要先注册createTemporarySystemFunction或用call()直接调用类。6.2 自定义聚合Aggregate/FlatAggregateaggregate多行转一行使用 AggregateFunction。Tableresulttable.groupBy($(a)).aggregate(call(MyAggFunc,$(b))).select($(a),$(result));flatAggregate多行转多行使用 TableAggregateFunction如 TopN。Tableresulttable.groupBy($(a)).flatAggregate(call(Top2,$(b))).select($(a),$(b));适用场景自定义统计指标、TopN、中位数等内置聚合不支持的场景。6.3 窗口操作Tumble/Slide/Session/Over// 滚动窗口Tumble.over(lit(5).minutes()).on($(ts)).as(w)// 滑动窗口Slide.over(lit(10).minutes()).every(lit(5).minutes()).on($(ts)).as(w)// 会话窗口Session.withGap(lit(1).minutes()).on($(ts)).as(w)// Over窗口Over.partitionBy($(a)).orderBy($(ts)).preceding(UNBOUNDED_RANGE).as(w)窗口属性$(w).start()窗口开始时间、$(w).end()窗口结束时间、$(w).rowtime()窗口行时间。6.4 时间属性Rowtime/Proctime事件时间在 DDL 中定义 Watermark或在 fromDataStream 时指定// fromDataStream 时声明事件时间tableEnv.fromDataStream(stream,$(ts).rowtime(),$(a),$(b));// 声明处理时间tableEnv.fromDataStream(stream,$(pt).proctime(),$(a),$(b));注意事件时间列必须是 TIMESTAMP(3) 类型且必须定义 Watermark 策略才能用于窗口。6.5 临时视图Temporary View// 创建临时视图tableEnv.createTemporaryView(view_name,table);// Table API 使用TableresulttableEnv.from(view_name).select(...);// SQL 使用tableEnv.executeSql(SELECT * FROM view_name);作用复用中间 Table 对象分步构建复杂查询提高可读性。注意临时视图仅当前会话有效作业重启后丢失需要持久化用 createTable。6.6 执行计划查看Explain// 查看 Table API 的执行计划Stringplantable.explain();System.out.println(plan);// 查看 SQL 的执行计划StringsqlPlantableEnv.explainSql(SELECT ...);计划内容逻辑计划 → 优化后计划 → 物理执行计划Transformation DAG。用途排查 SQL 性能问题确认优化器是否下推谓词、是否使用两阶段聚合、是否有数据倾斜。建议生产环境上线前对复杂查询执行explain()检查执行计划确认优化效果。七、六个常见坑7.1 坑一忘记 import Expressions 静态方法现象代码编译报错$()、lit()方法找不到。根因$()、lit()、UNBOUNDED_RANGE等是 Expressions 类的静态方法需要静态导入。解决方案在类顶部添加import static org.apache.flink.table.api.Expressions.*;。这是 Table API 最常见的编译错误新手必踩。7.2 坑二Table API 是懒执行忘记触发执行现象写了一串 Table API 调用select/filter/groupBy但作业没有执行没有任何输出。根因Table API 的方法调用只构建 Operation 树不会触发执行。需要调用execute()、executeInsert()或executeSql()才会触发。解决方案记住 Table API 是懒执行的。查询用table.executeInsert(tableName)或table.execute()触发写入用tableEnv.executeSql(INSERT INTO ...)。7.3 坑三列名引用错误运行时报 ValidationException现象运行时报错 “Cannot resolve field [xxx]”列名找不到。根因Table API 的列名引用是字符串虽然编译通过但运行时才校验。常见错误列名拼写错误、as()重命名后引用旧列名、join 后列名冲突。解决方案用$(colName)引用列名注意大小写Flink 默认不区分大小写但建议统一。join 后用表前缀引用列$(orders.user_id)。as()重命名后用新列名引用。7.4 坑四事件时间列未定义 Watermark窗口不触发现象Table API 窗口查询不触发没有任何输出。根因事件时间列没有定义 Watermark 策略Flink 无法判断窗口何时触发。或者 fromDataStream 时没有用.rowtime()声明事件时间列。解决方案DDL 中定义 WatermarkWATERMARK FOR order_time AS order_time - INTERVAL 5 SECOND。fromDataStream 时用$(ts).rowtime()声明事件时间列。7.5 坑五聚合后列名丢失后续引用报错现象groupBy select 聚合后后续操作引用列名报错。根因聚合函数sum/count/avg的结果列没有显式命名asFlink 自动生成列名如 EXPR$0后续引用原始列名找不到。解决方案聚合后必须用as()显式命名结果列$(amount).sum().as(total_amount)。groupBy 的列也需要在 select 中显式选择。这是 Table API 的常见坑务必养成as()命名的习惯。7.6 坑六Table API 与 SQL 混用导致执行计划不优现象Table API 和 SQL 混用执行计划不如预期性能差。根因Table API 构建的 Operation 树和 SQL 解析的 Operation 树在混合使用时优化器可能无法跨边界优化如谓词下推、投影裁剪。解决方案尽量统一使用一种 API全 Table API 或全 SQL。如果必须混用用createTemporaryView将 Table API 结果注册为视图然后用 SQL 查询这样优化器可以看到完整的查询树。混用后用explain()检查执行计划确认优化效果。八、八条最佳实践 Checklist上线前逐条检查静态导入 Expressionsimport static org.apache.flink.table.api.Expressions.*;避免编译错误这是 Table API 的第一步。聚合后必须 as() 命名所有聚合函数sum/count/avg/max/min的结果列都要用as()显式命名避免后续引用自动生成的列名。记住懒执行特性Table API 方法调用只构建 Operation 树必须调用execute()/executeInsert()/executeSql()才会触发执行。事件时间必须定义 WatermarkDDL 中定义WATERMARK FOR或 fromDataStream 时用.rowtime()声明否则窗口不触发。复杂查询用临时视图分步构建复杂查询不要一行链式调用到底用createTemporaryView分步构建每步变量名可读提高可维护性。上线前 explain() 检查执行计划对复杂查询执行table.explain()检查执行计划确认谓词下推、两阶段聚合、无数据倾斜。尽量统一使用一种 API全 Table API 或全 SQL避免混用导致优化器无法跨边界优化必须混用时用临时视图衔接。列名引用注意大小写和前缀join 后用表前缀引用列$(orders.user_id)as()重命名后用新列名避免列名冲突。九、总结与下一篇预告Table API 原理及代码实现要点回顾第一本质声明式、链式调用、类型安全的关系型 API与 SQL 共享 Planner 和执行引擎。三大特点声明式链式调用、类型安全编译期检查、与 SQL 共享执行路径。第二与 SQL 对比Table API 类型安全、IDE 支持好、动态查询容易、复杂逻辑清晰但学习成本较高SQL 学习成本低、分析师友好但类型不安全、动态查询困难。生产环境建议混合使用Table API 构建复杂逻辑SQL 执行简单查询。第三八大操作分类查询与投影select/filter/distinct、聚合groupBy/aggregate/flatAggregate、连接join/leftOuterJoin/intervalJoin、窗口Tumble/Slide/Session/Over、排序orderBy/fetch、集合操作union/intersect/minus、行级操作map/flatMap/aggregate/flatAggregate、输出与执行executeInsert/execute/explain。第四六步内部执行流程方法调用 → 构建 Operation → Operation 树 → Planner 翻译 → 优化执行 → 提交执行。关键理解Table API 是懒执行的方法调用只构建 Operation 树execute 时才触发 Planner 翻译执行。第五Expression 表达式体系八大类表达式——列引用$(“colName”)、字面量lit(10)、算术运算plus/times、比较运算isEqual/isGreater、逻辑运算and/or/not、函数调用upperCase/sum/call、时间属性rowtime/proctime、条件表达式then/otherwise/isNull/like。第六完整代码实现创建 StreamEnv → 创建 StreamTableEnv → DDL 建表Kafka 源/MySQL 结果→ Table API 过滤计算filter select as→ 分组聚合groupBy select sum count as→ 注册临时视图 → executeInsert 执行 → result.await() 等待完成。第七六个高级特性行级操作map/flatMap、自定义聚合aggregate/flatAggregate/TopN、窗口操作Tumble/Slide/Session/Over、时间属性rowtime/proctime Watermark、临时视图createTemporaryView 复用中间 Table、执行计划查看explain 排查性能问题。第八六个常见坑忘记 import Expressions 静态方法、懒执行忘记触发、列名引用错误运行时报错、事件时间未定义 Watermark 窗口不触发、聚合后列名丢失必须 as 命名、Table API 与 SQL 混用执行计划不优。第九八条最佳实践静态导入 Expressions、聚合后必须 as 命名、记住懒执行特性、事件时间必须定义 Watermark、复杂查询用临时视图分步构建、上线前 explain 检查执行计划、尽量统一使用一种 API、列名引用注意大小写和前缀。Table API 是 Flink SQL 的另一种表达方式在类型安全、IDE 支持、动态查询构建方面有明显优势。理解了它的本质、表达式体系、内部执行流程和常见坑就能在生产环境中正确使用 Table API写出高质量、可维护的查询代码。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

2026最新一个网站需要多少钱?避开模板坑的实战报价单 2026/9/27 12:14:41

2026最新一个网站需要多少钱?避开模板坑的实战报价单

2026最新一个网站需要多少钱?避开模板坑的实战报价单 很多老板一上来就问:“做个官网大概要多少钱?” 这时候如果你只回一个数字,比如“八千”或者“三万”,其实是在误导自己。 模板网站太丑不够用…

阅读更多 →
模板网站演示站点怎么做避免被坑的高阶最佳实践 2026/9/27 12:14:29

模板网站演示站点怎么做避免被坑的高阶最佳实践

模板网站演示站点怎么做避免被坑的高阶最佳实践 找建站公司怕被坑高价?别急,先看看你的演示站是不是裸奔。很多甲方在验收“模板网站演示站点怎么做”这个环节时,只盯着页面好不好看,忽略了后台安全。一旦演示站上线,黑客脚本就在扫描端口。今天咱们不聊…

阅读更多 →
网站自主制作平台避坑速查手册:告别模板丑站实战 2026/9/27 12:14:10

网站自主制作平台避坑速查手册:告别模板丑站实战

网站自主制作平台避坑速查手册:告别模板丑站实战 做网站最怕什么?不是代码写不出来,而是做出来的东西“丑得没眼看”。 很多独立站长在找 网站自主制作平台…

阅读更多 →
3步搞定wordpress上传下载,避开被黑挂马陷阱 2026/9/27 12:14:09

3步搞定wordpress上传下载,避开被黑挂马陷阱

3步搞定wordpress上传下载,避开被黑挂马陷阱 网站突然打不开,打开全是乱七八糟的弹窗,甚至直接挂了博彩广告?别慌,这大概率不是服务器挂了,而是你的wordpress上传下载配置出了漏洞。很多站长遇到这种情况,第一反应是删库重建,或者…

阅读更多 →
10年实战:dedecms建设慕课网站完整流程与SEO破局指南 2026/9/27 12:13:44

10年实战:dedecms建设慕课网站完整流程与SEO破局指南

10年实战:dedecms建设慕课网站完整流程与SEO破局指南 网站做好了没人访问,这是无数站长和开发者最头疼的噩梦。你花了几周时间,用 DedeCMS…

阅读更多 →
2026最新美业网站建设避坑指南:不会代码也能搞定 2026/9/27 12:13:31

2026最新美业网站建设避坑指南:不会代码也能搞定

2026最新美业网站建设避坑指南:不会代码也能搞定 自己不会代码想做网站?别慌,这是很多美业老板和独立站长的真实写照。过去大家总以为建站必须懂前端后端,其实2026年的技术栈已经彻底降低了门槛。…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉