private plot

应无所住 | 而生其心

问题描述

列表查询是很常用的功能,通常有多个输入框来输入条件查询列表数据。输入框是’AND’的关系。虽然实现通常来讲很简单,但是也有一些复杂场景实现好起来是很复杂的,下面就是一个示例:

有个需求是关于设计师设计任务列表的查询功能,其中有两个搜索条件,一个是处理人(登记人、设计人、摄影人、建模人,是一个下拉框,选中一个类型再输入昵称),一个是处理时间(处理时间没有下拉框,就一个日期输入窗口),输入后查询有以下几个场景:

  1. 不输入处理人只输入处理时间,设计时间和摄影时间及建模时间是或的关系,也就是只要有一个对的上就算是匹配了该条数据
  2. 输入处理人不输入处理时间,匹配对应处理人(登记人、设计人、摄影人、建模人)的数据
  3. 问题在于,当既输入处理人又输入处理时间的时候:
    • 如果选的是登记人,只匹配登记人,时间不产生作用
    • 如果选的是设计人,既匹配设计人,又要匹配设计人的处理时间
    • 如果选的是摄影人,既匹配摄影人,又要匹配摄影人的处理时间
    • 如果选的是建模人,既匹配建模人,又要匹配建模人的处理时间

所有的情况穷举完了。按照需求,后端实现是很复杂的,sql执行前要根据参数类型和是否有值做大量的判断,然后根据判断结果拼接不同的查询逻辑。当然实现肯定是没问题的,但是后端同时都不认同这么做。


新的方案

后面一起讨论后的解决方案是:细化处理时间的输入条件,也就是处理时间也弄成下拉框,分别是设计师处理时间,摄影师处理时间,建模师处理时间,登记人没有处理时间。用户选则的是哪个时间输入窗口,就对应匹配哪个时间,时间是区分类型细化到不同人的。这样查询就变得很直观,所查即所得。


新的问题

这样会不会存在新的问题,把复杂性转移给了用户。当用户选择了设计人,并且选择了摄影人处理时间,是查不出结果的,类似其他的场景也是一样。我的理解是,如果抛开实现方式不谈,需求是没有问题的,站在用户的角度来讲,输入不同的角色,然后再输入时间,最好就能得到对应的数据,我哪管你时间跟觉得对应关系呢?


思考

这里存在一个复杂性谁来消耗的问题?

最开始,复杂性是后端来处理的,这真的有什么问题吗,处理实现起来相对麻烦点,在xml中有大量的逻辑判断(我们当前的实现方式,习惯在xml的test标签中来处理参数),后期也不好维护,从后端开发者的角度来讲肯定是要pass掉这种方案

问了下chat-gpt,它的建议是前端动态绑定参数来实现,也就是还是细化时间参数的方案,只不过在页面不体现出来,通过选择不同的角色来绑定不同的时间参数。我觉得这个很好了,不过如果站在前端角度来讲,这等于说是把复杂性转移给了前端,chat-gpt的建议如下:

问题分析

当前问题的复杂性来源

  1. 条件的组合多样性
    • 处理人和处理时间两类条件之间的逻辑关系较复杂,尤其在既输入处理人又输入处理时间的情况下,不同处理人的匹配逻辑不一致。
    • 用户选择设计人处理时间时,其查询逻辑与摄影人、建模人等有所区别。
  2. 查询逻辑的隐性不一致
    • 当用户选择设计人,却选了摄影人处理时间,出现“查不到数据”的结果。从技术上来说符合逻辑,但从用户体验的角度来看,可能引起困惑。
  3. 需求变化后的逻辑清晰度提升
    • 通过将处理时间细化为多个输入条件(例如设计人处理时间、摄影人处理时间等),逻辑变得更直观,但这种设计也将部分输入责任转移给了用户(例如处理人和处理时间的匹配需要用户自己意识到)。

现有方案的优点

  1. 查询直观
    • 通过细化处理时间的选择,查询条件明确,所查即所得,减少了逻辑复杂度。
    • 用户对条件的输入更明确,查询过程符合“用户输入->匹配结果”的直觉。
  2. 逻辑清晰
    • 后端查询逻辑简化,不需要再处理复杂的“处理人+处理时间”的组合匹配问题。
  3. 结果符合逻辑
    • 如果条件冲突(如选择设计人+摄影人处理时间),查询不到结果是合理的,符合设计意图。

潜在问题

  1. 用户体验问题
    • 对普通用户来说,条件细化后的逻辑可能不够直观,例如他们可能没有意识到选择“设计人+摄影人处理时间”会导致查询无结果。
    • 如果用户输入了一个“矛盾”的条件组合,界面未明确提示,可能会让用户产生困惑。
  2. 复杂性转移
    • 细化输入条件虽然简化了代码实现,但将复杂性转移到了用户端,用户需要对条件组合关系有较清楚的理解。
    • 某些情况下,用户可能需要重复尝试不同组合才能找到他们需要的结果。

改进建议

1. 增强用户体验的引导

(1)条件依赖动态变化

  • 当用户选择某个处理人(例如设计人)时,只显示与之匹配的处理时间输入窗口(即仅展示“设计人处理时间”)。

  • 如果选择“登记人”,隐藏所有时间输入框,因为登记人没有处理时间的概念。

  • 实现方式

    • 前端动态绑定条件显示逻辑。
    • 用户选择处理人后,后台返回与之相关的时间条件。

(2)条件冲突提示

  • 如果用户选择了互斥条件(如设计人+摄影人处理时间),在查询前即时提示:“处理时间与处理人不匹配,请调整条件”。
  • 提示内容可以用高亮文字显示在页面上,或以弹窗形式提醒。

2. 提供默认的“宽松查询”模式

为避免用户因条件不匹配而查不到结果,可以提供一种宽松查询模式,调整查询逻辑:

  • 规则
    • 如果用户输入设计人,但选择了摄影人处理时间,则放宽匹配条件:
      • 匹配设计人。
      • 或匹配摄影人处理时间。
    • 提示用户这是宽松查询结果(例如在结果列表顶部显示“部分条件未完全匹配”)。
  • 实现
    • 在 SQL 或查询逻辑中,增加“或”逻辑,默认情况下允许匹配部分条件。

3. 增加筛选条件的清晰度

通过界面设计增加筛选条件的可理解性,减少用户困惑:

  • 改进处理时间选择框的标签

    • 将“摄影人处理时间”改为更明确的描述,例如“仅限摄影人数据的处理时间”。
    • 增加说明文字:提示“处理人和处理时间需保持一致”。
  • 添加智能搜索提示

    • 当用户输入不合理组合时,弹出可能的修正建议。例如“您选择了设计人,但输入了摄影人处理时间,是否改为选择摄影人?”

4. 优化代码逻辑(查询逻辑)

优化查询逻辑,使其支持上述功能:

  1. 动态条件拼接

    • 使用 SQL 动态拼接条件,确保处理人和时间条件相互匹配。
  2. 条件优先级处理

    • 按条件的重要性排列优先级。例如:
      • 优先匹配处理人。
      • 如果处理人为空,则匹配处理时间的“或”条件。

总结

你的方案通过细化条件设计,大大简化了查询逻辑,这是非常好的思路。但可以进一步增强用户体验,引导用户输入正确条件,减少困惑。结合动态条件显示和冲突提示等方法,既能保持代码简洁,又能提升用户满意度。

此外,在代码层面,确保查询逻辑的灵活性和条件的动态组合处理,可以更好地满足未来可能的扩展需求。

再问问chat-gpt,这样就把复杂性转移给了前端,它是这样回答的:

前端复杂性转移的利与弊

优点

  1. 用户输入直观清晰
    • 用户通过可视化界面理解查询条件,不需要推测后端的处理逻辑。
    • 动态交互(如根据处理人显示对应的处理时间)减少了用户的错误输入。
  2. 后端代码逻辑简化
    • 将条件的动态处理前置到前端,后端只需处理明确的输入,避免了复杂的条件组合查询逻辑。
  3. 提高响应速度
    • 动态的前端逻辑减少了无效查询(如用户输入不合理组合条件),避免后端资源浪费。

缺点

  1. 前端逻辑复杂化
    • 前端需要动态管理条件的显示和冲突检测,可能增加代码复杂性。
    • 如果后续查询条件需要调整,前端也需要同步更新,增加了维护成本。
  2. 用户体验依赖前端实现
    • 如果前端设计不够直观或提示不到位,用户可能仍会输入错误组合,导致不符合预期的查询结果。
  3. 责任边界不清
    • 一些逻辑本应由后端处理(如条件优先级判断),却放到前端,这可能导致系统维护复杂化。

优化平衡前后端职责的建议

1. 将复杂性适当分担

  • 动态条件显示仍由前端负责

    • 这是用户体验的核心功能,处理人和处理时间的动态联动是前端擅长的领域。
    • 前端负责基础的输入验证和条件关系的动态更新。
  • 条件冲突提示由后端提供

    • 前端只负责收集用户输入的条件,后端在查询逻辑中判断条件是否冲突,并返回提示信息。
    • 例如,用户提交查询后,后端返回“处理人和处理时间不匹配”的提示,前端只需显示这条信息。

2. 使用统一的查询规则

  • 查询逻辑集中在后端

    • 后端维护一套核心的条件处理逻辑,前端只负责将用户输入的内容格式化发送给后端。
  • 规则更新统一管理

    • 如果后续业务逻辑变化(如新增处理人类型或时间条件),只需要更新后端逻辑,而前端无需复杂调整。

3. 智能化查询功能

  • 在用户提交查询前,前端通过调用后端接口,预先检查条件是否合法:
    • 如果条件冲突,直接提示用户调整。
    • 如果条件组合不完整,提供默认或宽松查询模式。

4. 降低前端复杂性

  • 使用现有框架或工具简化动态交互逻辑:
    • 前端可使用状态管理库(如 Vuex、Redux)或条件绑定工具(如 Vue 的 v-if 和 React 的条件渲染)来动态管理条件展示。
    • 通过清晰的数据结构,将条件关系解耦,减少前端代码混乱。

总结

复杂问题通过具体细化后,是能变得很简单很清晰的,这样就不是什么问题

当然具体细化这个动作,是有一定复杂性的,这个转移给回来处理,值得商榷

当前我们还是选择了将复杂性转移给了用户,毕竟这是内部的工具,使用体验也没有那么差吧

问题描述:

有个场景是,订单需要最多支持两百条批量设置出库相关信息(开发阶段没有这个需求,业务使用过程中体现的需要,所以没有测试这种大批量的场景),实际使用时批量选择超过100条就会出现超时(客户端超时时间设置的10s),经测试发现选择200条时实际耗时18s左右。

优化过程记录:
  1. 初步想到的是优化索引
  • 有一定效果,但是效果不明显,还是会超时
  1. 优化代码,使用线程池异步处理
  • 因为是异步处理,所以接口能立马详情结果(70ms),但是实际批量设置过程还没有处理完,前端会在批量设置的请求响应后执行列表查询
  • 这个时候是查不出来最新设置的全部结果的,需要多次查询才能更新完成
  1. 在使用线程池的基础上,继续找原因优化代码
  • 业务场景比较复杂,不是简单的设置发货信息,然后更新数据,实际是在更新前有大量的其他业务数据需要查询。也就是在原来批量修改的场景里,forEach循环嵌套有大量的查询,这个比较耗时
  • 切换到当前多线程场景里,每个线程任务里也一样执行大量的查询任务。所以想到在任务处理前,把所有需要的业务数据批量查询好,然后转换成Map<key,value>形式的业务数据,再多线程执行修改
  • 除了查询外,设置前还有个配对的服务需要调用,该服务里也有大量的查询和修改。这个也改到最外层统一处理
  • 从结果来看,执行完后列表查询还是不能立马展示全部的最新信息,也要分多次
  1. 从线程池自身角度再优化
  • 200条数据更新任务通过线程池处理,sit环境CPU核心数是4,设置的核心线程数是3,从结果查询的效果来看,实际耗时还有优化空间
  • 考虑到服务器核心数较少,200条任务全部丢到线程池中,在IO密集型的场景下,多线程上下文切换的耗时还有优化的空间,所以想到将任务分批处理
  • offset设置为70,这样200条就是3个任务,100条就是2个任务,能充分利用CUP核心
  • 分批后再放到线程池中处理,减轻线程池压力和多线程上下文切换的耗时
  • 结果立竿见影,平均耗时直接感到了3s左右
  1. 考虑到3s左右直接响应结果,与再去点击按钮刷新页面相比,体验更好
  • 改成CompletableFuture.supplyAsync来执行异步任务,等全部执行完成后再响应请求
  • 页面效果是,批量设置后,页面不可操作,等待2-3s,页面自动请求展示所有最新数据
  1. 线程池优化完了,从功能整体上看,订单预处理可以拆分处理
  • 批量设置中取消配对服务调用,改成从订单创建时触发(通过spring event),另外通过定时器处理全部
  • 从结果来看不明显,但是实际解耦了这两个功能,默认批量设置前订单信息是完善的
总结:
  1. 总整体上看,大的功能解耦,只做好功能模块自己的事,默认上一步的数据是完备的。至于上一步数据完整的实现交由上一模块自己去实现
  2. 循环内不要嵌套查询
  3. 循环内嵌套查询,通常是在功能升级改造时出现的,也就是单个改成批量。这个没法避免,也不建议做单个功能时都默认做成批量的,没必要吧,毕竟不是每个功能都会升级为批量操作。能做到的,就是尽量结构化代码,让功能重构的时候尽量容易一些
  4. 使用线程池有很多讲究,这次的优化也充分体现了将大问题拆分成若干个小问题的重要性,类似分治思想。200个直接扔到线程池里的效率,是远远赶不上分成小批次再多线程处理的效率的。纯粹递归处理问题效率也远远低于分治法与动态规划
  5. 通常数据库是最后的性能瓶颈,好的业务模型划分、表结构设计和合理的索引也很重要

问题:

多次迭代的功能,在开发过程中存在一些障碍、坑。虽然经过看历史代码、沟通、修复bug等,需求能上线,但是不高效,上线后还容易出新的问题。

以一键代发批量发货功能举例-task178
  1. 需求不明确,体现不了细节,没有一处文档来指导功能实现
    • 需求:截图(截图体现了要校验发货信息),文字描述(如果选了5个订单进行批量发货,自动过滤不符合提交到oms的订单,只提交符合的订单。)
    • 看代码和沟通后梳理出来的细节:
      • 第一版:校验基础信息,校验库存,失败均报错;校验通过才生成出库单;
      • 第二版:校验基础信息失败报错,校验库存失败修改订单状态为缺货,要保存异常信息,要调用出库单取消接口;不关心校验结果,都生成出库单;
      • 第三版(当前需求版本):校验基础信息失败忽略,校验库存失败修改订单状态为缺货,要保存异常信息,不用调用出库单取消接口;默认设置好了基础信息,有了出库单(批量设置)
  2. 需求迭代之外,临时bug改动也会穿插到原有逻辑中
    • 示例:提交功能里面临时穿插了调用lx取消出库单接口,应该是异常页面重复提交时用到。但是这个在批量发货中是用不到的,需要注意排除掉
  3. 新功能与原功能场景不一样,存在环节缺失
    • 示例:原批量设置功能设置的是发货信息,SKU配对是在详情页面中完成的。而新的批量发货是在批量设置后去操作,这就会导致批量发货时缺少SKU。这就会导致推送出库单失败-bug3537
  4. 上线后引发新的问题
    • 示例:批量设置功能增加自动配对服务,使得问题3闭环,但是导致了线上批量设置接口在大批量的场景会超时-task191
反思与总结:
  1. 不能轻视迭代的需求,不是明显很小改动的,也要进行评审
  2. 对于反复迭代的功能,要加强Code Review,防止过于混乱后面难以维护
  3. 其实核心问题,是没有需求文档,需求描述不明确。当前项目只有基于Axhub的在线原型管理,没有详情的需求文档来规范功能细节
  4. 不过第三点是对于产品角色的要求。从开发角度来说,我们需要做好1、2两点。

  1. 增加activiti模块,增加相关基础的maven依赖

  2. 容易忽略掉的是,admin模块的dependencies中,需要引入activiti,因为启动类在admin模块中

  3. 需要初始化一个act_ge_property的表单,其他activiti相关的表单不用初始化

  4. activiti相关配置不要漏掉了
    activiti: check-process-definitions: false database-schema-update: true history-level: full db-history-used: true

  5. 此时启动项目正常会成功并且生成其他相关的表单,但是大概率会报错,如下:
    Caused by: java.sql.SQLSyntaxErrorException: Table '····.act_ru_job' doesn't exist

  • 问题分析:

    • 从mysql-connector-java 5.x 到6.x,nullCatalogMeansCurrent属性由原来的默认true改为了false。
    • true 使用指定的数据库进行查询。优先取当前传入的数据库名,其次取当前链接的数据库名。
    • false 代表遍历当前链接下的所有数据库进行查询,就是会遍历当前链接下的所有数据库(information_schema, mysql 和 performance_schema 这三个系统DB,虽然在最终结果里进行了排除,但是依然进行了查询),这样其他库对当前库就会造成干扰。我们需要的是使用链接中指定的数据库进行操作。
  • 解决方法:

    • 将mysql版本降为5.x
    • 在url后面加上nullCatalogMeansCurrent=true
    • 我们采用方案2
  1. 启动项目,会发现启动成功,同时生成activiti相应的表单

计算机基础

  • 编码
  • Unix 编程艺术
  • 深入理解计算机系统
  • SICP
  • A Philosophy of Software Design
  • TCP/IP 详解 1、2
  • CLRS
  • 操作系统导论

软件工程 & 敏捷开发

  • 代码大全
  • 代码整洁之道
  • 程序员修炼之道
  • 重构
  • 敏捷软件开发
  • Practical Unit Testing with JUnit and Mockito
  • scrum 精髓
  • Software Engineering at Google
  • Google软件工程(上面的中文版)

性能优化 & 高级特性

  • Effective Java
  • Java并发编程实战
  • Java 性能权威指南
  • Java 性能优化权威指南
  • 高性能 MySQL
  • 深入分析Java Web技术内幕
  • 深入理解Java虚拟机(第2版)
  • Redis 设计与实现

OOP & DDD

  • 设计模式
  • 深入浅出设计模式
  • 冒号学堂
  • 领域驱动设计
  • 实现领域驱动设计
  • Patterns, Principles, and Practices of Domain-Driven Design
  • 面向对象分析与设计

DevOps

  • 发布!软件的设计与部署
  • SRE Google运维解密
  • 可观测性

大数据

  • 大数据日知录
  • 大数据系统构建/Big Data: Principles and best practices of scalable realtime data systems
  • Streaming Systems
  • 华为数据之道

管理

  • 格鲁夫给经理人的第一课
  • 门后的秘密
  • 人件
  • 人月神话
  • 项目:计划、进度与控制
  • 成为技术领导者
  • 第五项修炼
  • 质量软件管理 1:系统思维
  • 技术为径/The Manager’s Path
  • An Elegant Puzzle

系统架构

  • 企业应用架构模式
  • 大型网站技术架构
  • 大型网站系统与Java中间件开发实践
  • 微服务架构设计模式
  • 架构整洁之道
  • 架构即未来
  • 企业IT架构转型之道:阿里巴巴中台战略思想与架构实战
  • 数据密集型应用系统设计/Design Data Intensive Applications
  • 系统架构
  • 微服务架构设计模式
  • Fundamentals of Software Architecture
  • 华为数字化转型之道
  • 面向模式的软件架构 第1卷

综合素养

  • 高效能人士的七个习惯
  • 穷查理宝典
  • 思考:快与慢
  • 卓有成效的管理者
  • 哈佛商业评论:大师十论
  • 刻意练习
  • 软件随想录
  • 程序员思维修炼
  • 黑客与画家
  • 别逗了,费曼先生
  • 浪潮之巅
  • 结构思考力
  • 原则
  • 软件开发者路线图
  • Staff Engineer
  • 你的灯亮着吗?
  • 软件随想录 1 & 2

最近发生了不少大事情。

前几天备受关注的美国大选结果出炉了,78岁高龄的特朗普再次以高票当选美国第47任总统,恭喜这个老头。
对中国有什么影响,犹未可知,但是就跟台湾大选一样,把它当一节目看也是很能消磨蹲马桶时的无聊时光的。

另外有一个很有意思的事情。郑州的四名大学生,突发奇想夜骑单车到郑州吃小笼包,这个事情不知道为什么在社交媒体爆火(这个时代爆火总是很容易也很突然),然后引发大家纷纷效仿。其规模完全超过了各职能部门的预料,交通堵塞,餐馆堵塞,当然马桶也随后堵塞了。最后以封路封校收场,很有意思。年轻真好,我若也在当下,必定也会跟上这场百里奔袭。

暴戾的事情也有。珠海航展正进行的如火如荼,结果体育馆发生了汽车袭击跑步团的人间惨案,曝光的视频网上有在流传,我一直没敢看,惨!

昨晚T1和BLG打满了5局是我没想到的。
看完第一局,以为最多3比1BLG就能终结比赛,但是没想到反过来被T1给终结了。

准确来说,是被faker给终结的。第四局看到这个男人TP绕后高移速偷大招开团一气呵成,就感觉到不妙了。算是见识到了绝境李相赫。

本来现在lol热度越来越低,今晚之后估计关注的人会更少。
在我这里,昨晚就是lpl的终章。

今天周六,晚上10点有英雄联盟总决赛,BLG打T1,BLG加油!
预测个3:1吧,BLG是3,晚上见分晓。

hexo是什么?

hexo是一个基于node.js的前端框架,可以根据模板(主题)和指定目录的文本内容,生成一系列完备的静态文件,包含index.html和js及css等文件,也就是想要的可访问静态文件博客了。

光生成静态文件还不行,生成的静态文件得有可访问的服务器用来部署文件,这样才算联网了,可以到处访问;另外得有一个地方来托管原始文件及hexo基于它生成的静态文件,这样文件能存储且保持同步;然后最好还有CI/CD功能,能够本地上传后自动触发部署。

有一个完美的并且免费的服务能够满足上述需求,那就是github Pages了。

github能够存放原始文件和hexo基于原始文件生成的博客文件,注意区分这是两个不同的项目仓库。github免费提供的Pages服务,可以用来部署静态网页,并且提供类似 https://xxxx.github.io/xxxx.github.io的通用域名供用户访问,这里xxxx指的是你的github用户名,注意xxxx.github.io这个仓库名是要求固定的格式,该仓库对应存放hexo生成的博客静态文件。另外,github有actions服务,配置好后,当xxxx.github.io有文件变更提交后,会自动触发流水线,在Pages中部署好最新的内容,简直完美。

说下具体的操作

首先我们down下来我们的原始博客项目,这里暂且给它一个项目名叫blog。然后在使用npm install安装,blog项目是基于hexo生成的,有hexo相关的依赖,安装完成后就可以使用hexo的命令来操作了。接下来我们就可以在文件目录中编写自己博客或者文章了。然后依次执行以下命令生成文件,并启动项目:

hexo clean
hexo g
hexo s

这时我们可以本地看一下效果,没问题后推送新生成的文件到xxxx.github.io中:

hexo d

github Actions服务会自动帮你部署好,在https://xxxx.github.io/xxxx.github.io 中就可以看到你刚发布的新内容了。

这个时候,有的人有疑问了。明明是两个项目,一个是blog,一个是xxxx.github.io,新增文章文件是在blog里面,使用hexo d命令后,怎么会推送文件到xxxx.github.io呢.这是因为blog是基于hexo生成的,里面有一系列的配置,其中根目录的_config.yml中,需要配置xxxx.github.io仓库的详细信息,这样两个项目就关联起来了。

总结几点细节

  1. hexo怎么生成基础的blog项目,怎么配置,怎么更换主题,看hexo使用手册,介绍很详细
  2. hexo是基于node.js的,本地需要安装node.js
  3. 存在blog和xxxx.github.io两个代码仓库,blog和xxxx是示例名称
  4. blog项目存放原始文件,是基于hexo innit初始化得到的,博客及文章的源文件是存放在这个项目中
  5. xxxx.github.io是使用hexo g基于blog项目的文件生成的,最终用来部署和访问的
  6. _config.yml配置xxxx.github.io的目录时需要注意,目录不对,会导致js和css文件加载失败,页面丢失样式,而且很奇快的是,配置好使得https://xxxx.github.io/xxxx.github.io能正常显示时,在其他地方可能访问不正常,例如cloudflare Pages,需要根据实际情况调整
  7. 使用github Pages服务部署网站,xxxx.github.io仓库不能是private
  8. hexo g生成静态文件后,需要hexo d将文件推送到xxxx.github.io仓库中,否则xxxx.github.io仓库中不会显示任何文件

另外一套方案

还有另一套可以完美可以替换github Pages的服务,就是cloudflare Pages,它甚至更好。毕竟免费,有cloudflare的域名,有CDN加速,有DNS解析,而且有免费SSL。
下一篇来介绍hexo + github + cloudflare Pages。

这是个不好的消息。
昨天,也就是2024年10月27日晚上,在法国巴黎举行的英雄联盟全球总决赛的半决赛中,T1又一次战胜了GENG进入了决赛,据现场解说的报道,这是T1连续第三年进入总决赛。值得注意的是,上一次连续三年进入总决赛的队伍,也是T1!
在历届英雄联盟全球总决赛的BO5中,中国队没有赢过一次T1。跟一众国内网友一样,我当然支持BLG夺冠。但是假如Faker赢得第五个冠军,连续两年获得冠军,想想是不是感觉也很值得期待呢?
不管是续写传奇,还是新王登基,今年的全球总决赛可以说是近四五年来最好看的了,也有很多值得说的地方,待续。。。

0%