想买个 WatchS11,怎样才能抢到国补券
每天闹钟 9 点,到 8 点 59 分 59 秒准时点下去,就提示活动火爆~
然后到 9 点 1 分的时候,提示今日券已领完.....
xiaohack博客专注前沿科技动态与实用技术干货分享,涵盖 AI 代理、大模型应用、编程工具、文档解析、SEO 实战、自动化部署等内容,提供开源项目教程、科技资讯日报、工具使用指南,助力开发者、AI 爱好者获取前沿技术与实战经验。
想象一下,你是一个小型 PHP 网站的主开发。过去几个月,网站运行一直很平稳——流量不高,负载也可预测。然后某一天,一切突然改变:你的网站流量在一夜之间增长了 10 倍。 接下来会发生什么?你的网站开始变慢,用户感受到明显延迟,甚至更糟,网站直接崩掉。你遇到的是大多数开发者迟早都会碰到的问题:在突发压力下扩展系统。本文会带你看清 PHP 文件加载机制背后到底发生了什么,以及在扩展 Web 应用时,如何避开那些会导致性能下降和 Bug 的常见陷阱。 开发 PHP 网站时,你会频繁使用 底层主要发生了以下事情: 为了更直观,可以把 下一节我们看开发者在 PHP 文件包含中最常见的错误。 表现:在应用不同位置反复对同一文件使用 原因:没有意识到重复包含同一文件会导致性能问题,甚至触发函数或类重复声明的致命错误。 影响:可能出现函数重复定义、类重复声明,或因不必要的重复解析导致变慢。 表现:使用相对路径,如 原因:误以为 PHP 总会在你预期的目录下解析路径。 影响:文件包含失败,或在不同环境行为不一致。 表现:所有文件都用 原因:想确保文件不被重复包含,但忽略了这两个语句比 影响:在高流量应用中,过度使用 表现:使用相对路径(例如 原因:很多开发者只看本地可运行,忽略了不同环境路径解析细节。 影响:生产环境无法包含文件,出现难以定位的错误。 表现:根据用户输入动态包含文件(如 原因:为了让页面更动态,直接把用户可控输入用于文件路径。 影响:应用会暴露给本地文件包含(LFI)或远程文件包含(RFI)攻击。 表现:在一个请求里多次包含重量级文件(如配置文件或库文件)。 原因:没有对包含行为做缓存或优化,尤其是那些很少变更的共享资源。 影响:每次包含都需要再次解析并执行文件,即便文件未变化,也会造成额外负载和性能下降。 现在我们看正确做法。 只有在你确实需要确保“同一请求只包含一次”时,才使用 始终从项目根目录定义绝对路径,确保包含的是正确文件。 对于不常变化的文件(如配置文件),使用 OPcache 等 opcode 缓存机制。 如果必须动态包含文件,务必净化输入,避免安全漏洞。 随着网站规模扩大,文件包含在生产环境中的影响会越来越关键。这里有几个生产实践要点: 要理解应用在不同环境(如 Docker、Serverless)中的运行差异。部署架构不同,文件路径行为也不同,因此包含路径要尽量环境无关。 通过模块化降低对大文件手动包含的依赖。现代 PHP 应用通常通过 Composer 加载依赖,这会减少手写 include 的需求。 如果你的应用因为文件包含开始报错,可按以下清单排查: 如果你最近已经遇到性能问题,建议尽快回顾你的文件包含实践: 这些调整通常改动不大,但对线上稳定性的提升很直接。当你的 PHP 网站一天内流量增长 10 倍时,会发生什么?
引言:意料之外的流量激增
实际发生了什么?
include、require、include_once 和 require_once 把外部文件引入脚本。这些能力对构建模块化、可维护的 PHP 应用非常关键。但当流量上来后,这些看似普通的操作很快就可能变成瓶颈。include_once 或 require_once,PHP 会先检查该文件在当前请求里是否已加载。这能防止重复包含,但也会带来额外性能成本,在高流量下更明显。include 想成“去图书馆借书”——每借一次都要走一遍借阅流程。单次开销不大,但并发高了,这些固定动作就会被放大。常见错误
没有正确使用
require_oncerequire 或 include。路径解析错误
include 'config.php';,却没有考虑脚本是从哪里执行的。过度使用
include_once / require_onceinclude_once 或 require_once,认为这是最稳妥的方案。include / require 更重,因为 PHP 需要先检查文件是否已加载。_once 会拖慢性能,检查机制本身可能成为瓶颈,显著影响响应时间。不使用绝对路径
include 'includes/functions.php';),却没有考虑应用上线后目录结构可能变化。用户输入带来的安全风险
include $_GET['page'] . '.php';)。没有缓存包含结果
如何正确处理
在必要时使用
require_oncerequire_once 或 include_once。其他场景优先用 include 或 require 以获得更好性能。// Bad example
include 'config.php'; // Included multiple times across scripts
// Good example
require_once 'config.php'; // Ensures the file is only loaded once使用绝对路径
// Bad example
include 'includes/functions.php'; // Might break in production
// Good example
include $_SERVER['DOCUMENT_ROOT'] . '/includes/functions.php'; // Resolves paths correctly缓存包含
动态包含时净化输入
// Bad example
include $_GET['page'] . '.php'; // Potential security risk
// Good example
$page = basename($_GET['page']);
$allowed_pages = ['home', 'about', 'contact'];
if (in_array($page, $allowed_pages)) {
include $page . '.php';
} else {
echo "Page not found";
}生产环境注意事项
安全影响
allow_url_include = Off),避免被恶意利用。扩展与性能
*_once 的性能开销:在关键性能路径避免过度使用 require_once 或 include_once。可观测性
部署差异
API 密集型应用
排障检查清单
include()、require()、require_once() 相关错误。// Debugging snippet
error_log("File included: " . $file_path);结论:关键要点
require_once 要节制:它对防止重复包含很重要,但有性能成本。下一步
*_once 的依赖。
在企业级应用开发中,数据脱敏是合规性的刚需。然而,开发者往往在“功能完整性”与“系统性能”之间面临权衡:基于 AOP 的方案难以穿透复杂的嵌套对象(如 List<Map>),而基于 Jackson 序列化的方案又难以应用于非 Web 输出场景(如 Excel 导出、日志记录)。 开源项目 easy-desensitize 提供了一个值得关注的解法。该框架通过精简的反射缓存架构,在保证深度递归处理的同时,将损耗压制在了纳秒级别。 该框架不依赖特定的 Web 容器,其底层设计避开了高开销的正则表达式,转而采用基于字符索引的高效切分算法。它最显著的特征是能够自动探测对象图(Object Graph),在处理包含集合、Map、数组以及多层嵌套 Bean 的复杂数据模型时,表现出了极高的鲁棒性。 该框架遵循“声明式定义,编程式调用”的设计原则,接入成本极低。 在 Maven 项目中引入核心库 定义脱敏处理器 定义实体类 执行脱敏 在实际业务中,数据往往以 Result<T> 或嵌套列表的形式存在。 项目仓库: easy-desensitize-core 通过 在字段上使用 @MaskingField 定义脱敏策略。 在 方法或类 上添加 @ResponseMasking 注解。 响应结果 项目仓库: easy-desensitize-spring-boot-starter 从实测结果看,easy-desensitize 不仅仅是一个简单的注解工具,它更像是一个经过精细调优的性能引擎。对于追求高吞吐量、且需处理复杂对象模型的 Java 应用,它提供了一个高性能且低侵入的标准化方案。核心特性
1. 概述
easy-desensitize-core是一个独立于业务逻辑的Java脱敏引擎。其核心价值在于提供了一套结构自适应的脱敏机制。2. 快速开始
2.1 依赖引入
<dependency>
<groupId>io.github.zhengyuelaii</groupId>
<artifactId>easy-desensitize-core</artifactId>
<version>${latest.version}</version> <!-- 使用变量替换最新版本 -->
</dependency>2.2 规则标注
/**
* 手机号脱敏处理器示例
*/
public class MobileMaskingHandler implements MaskingHandler {
@Override
public String getMaskingValue(String value) {
// 使用内置 Masker 工具类隐藏中间 4 位
return Masker.hide(value, 3, 7);
}
}public class User {
@MaskingField(typeHandler = KeepFirstAndLastHandler.class)
private String name;
@MaskingField(typeHandler = MobileMaskingHandler.class)
private String mobile;
@MaskingField(typeHandler = FixedMaskHandler.class)
private String password;
// getter、setter 略
}通过在实体类字段上添加 @MaskingField 注解,开发者可以精确定义脱敏策略。
public class QuickStart {
public static void main(String[] args) {
User user = new User();
user.setName("张小凡");
user.setMobile("13700001234");
user.setPassword("123456");
EasyDesensitize.mask(user);
System.out.println(user);
// 输出:User [name=张*凡, mobile=137****1234, password=******]
}
}3. 复杂对象支持
easy-desensitize在处理这类复杂结构时表现出了关键技术优势。public class ListTypeDesensitize {
public static void main(String[] args) {
User user = new User();
user.setName("李小鹏");
user.setMobile("13700001234");
user.setPassword("123456");
// List脱敏
List<User> list = Collections.singletonList(user);
EasyDesensitize.mask(list);
System.out.println(list);
// 输出:[User [name=李*鹏, mobile=137****1234, password=******]]
}
}public class MapTypeDesensitize {
public static void main(String[] args) {
User user1 = new User("李小鹏", "13700001234", "123456");
User user2 = new User("张三", "13888880000", "456789");
// Map脱敏
Map<String, Object> map = new HashMap<>();
map.put("code", 200);
map.put("data", user1);
map.put("list", Collections.singleton(user2));
EasyDesensitize.mask(map);
System.out.println(map);
// 输出:{code=200, data=User [name=李*鹏, mobile=137****1234, password=******], list=[User [name=张*, mobile=138****0000, password=******]]}
}
}public class TreeVO {
private String id;
@MaskingField(typeHandler = KeepFirstAndLastHandler.class)
private String name;
private List<TreeVO> children;
// ...
}
public class TreeTypeDesensitize {
public static void main(String[] args) {
TreeVO root = new TreeVO();
root.setId("1");
root.setName("XXX有限公司");
TreeVO t1 = new TreeVO();
t1.setId("1001");
t1.setName("行政部");
TreeVO t2 = new TreeVO();
t2.setId("1001");
t2.setName("研发部");
root.setChildren(Arrays.asList(t1, t2));
EasyDesensitize.mask(root);
System.out.println(root);
// 输出:TreeVO [id=1, name=X*****司, children=[TreeVO [id=1001, name=行*部, children=null], TreeVO [id=1001, name=研*部, children=null]]]
}
}// 统一包装
public class Result<T> {
private Integer code;
private String message;
private T data;
// ...
public static <T> Result<T> success(T data) {
return new Result<>(200, "success", data);
}
// ...
}
// 测试脱敏
public class ResultDesensitize {
public static void main(String[] args) {
Result<User> result = Result.success(new User("张小凡", "13700001234", "123456"));
EasyDesensitize.mask(result);
System.out.println(result);
// 输出:Result{code=200, message='success', data=User [name=张*凡, mobile=137****1234, password=******]}
}
}4. 与 Spring 集成
easy-desensitize-spring-boot-starter,框架实现了自动装配逻辑。它利用 Spring MVC 的 ResponseBodyAdvice 切面,在 HTTP 响应序列化前的最后一步自动触发脱敏。这意味着开发者无需在 Service 层编写任何脱敏代码,即可实现全局的安全加固。4.1 引入依赖
<dependency>
<groupId>io.github.zhengyuelaii</groupId>
<artifactId>easy-desensitize-spring-boot-starter</artifactId>
<version>${latest.version}</version> <!-- 使用变量替换最新版本 -->
</dependency>4.2 创建实体类
import io.github.zhengyuelaii.desensitize.core.annotation.MaskingField;
import io.github.zhengyuelaii.desensitize.core.handler.FixedMaskHandler;
import io.github.zhengyuelaii.desensitize.core.handler.KeepFirstAndLastHandler;
public class User {
@MaskingField(typeHandler = KeepFirstAndLastHandler.class)
private String name; // 李小龙 -> 李*龙
@MaskingField(typeHandler = FixedMaskHandler.class)
private String password; // 123456 -> ******
private String address;
// getter/setter
}4.3 在Controller启用脱敏
@RestController
@RequestMapping("/user")
public class UserController {
@GetMapping("/get")
@ResponseMasking
public User get() {
return new User( "李小龙", "12345678", "上海");
}
}{
"username": "李*龙",
"password": "******",
"address": "上海"
}总结
在物联网(IoT)领域,设备的功耗管理是确保其长期稳定运行的关键。对于依赖电池供电的 IoT 设备,如数据传输单元(DTU),精确评估和优化功耗至关重要。这不仅有助于合理设计电池容量,更能显著延长设备的使用寿命。 本文将深入探讨基于 EdgeBus 平台的 DTU 设备功耗计算方法,涵盖不同运行模式下的功耗组成部分,并提供详细的计算逻辑和优化建议。如需查阅详细的计算公式和示例,请访问我们的官方指南:DTU 功耗计算指南。 DTU 的总功耗主要由以下几个部分组成: 所有 ManThink DTU 产品的基本静态功耗通常为 3 µA。这是设备在最低活动状态下的持续功耗。 SW 模式通过周期性地监听前导码来降低功耗。其功耗受 周期 (Period)、扩频因子 (SF) 和 带宽 (BW) 三个参数影响。SF 和 BW 共同决定符号持续时间,DTU 会持续监听两个符号持续时间以检测唤醒前导码。 详细的 SW 模式功耗计算涉及符号时间的获取和 SW 静态电流的计算。您可以通过 DTU 功耗计算指南 了解具体的计算步骤和示例。 LoRaWAN 传输功耗与数据速率和通信距离密切相关。较低的数据速率和较长的距离会导致更长的传输时间,从而增加功耗。LoRaWAN 传输功耗可以通过 Semtech 在线计算器进行估算。 关键参数包括: 通过在线工具提交参数后,可以获得单次传输的能量消耗。静态电流等效值可通过将平均传输功耗除以传输周期来计算。更多详情和示例请参考 DTU 功耗计算指南。 有线通信功耗包含两部分:外部设备功耗和 DTU 有线接口功耗。 当 DTU 通过升压电路(通常效率为 70%)为外部设备提供 15.1V 电源时,外部设备电流需要转换为电池侧等效电流。这确保了功耗计算的准确性。 外部设备功耗不仅取决于电流,还与通信工作时间有关,包括预热时间、指令传输时间、设备响应时间以及超时重试时间。串行通信时间可以通过传输和接收字节数、每字节位数和波特率计算得出。 对于详细的电流转换公式、通信工作时间计算以及单次读取能量计算,请查阅 DTU 功耗计算指南。 例如,RS-485 接口的工作电流通常小于 12 mA。由于 RS-485 不需要升压转换,电池侧等效电流可直接按此值计算。其他接口类型如 4-20mA 和 0-10V 也有其典型的电流消耗。 总通信功耗包括在完整有线通信过程中所有组件的功耗,如外部设备电池侧等效电流、RS-485 接口电流和 MCU 工作电流。这些功耗的叠加构成了设备在通信时的瞬时功耗。 日均功耗是评估电池寿命的关键指标,它必须综合考虑设备在不同模式下的运行时间及其相应的功耗。日均功耗是基础功耗、SW 模式功耗、LoRaWAN 功耗和有线通信功耗在 24 小时内的累积。 重要提示:如果 DTU 无需为外部设备供电,则应忽略外部设备功耗的计算,此时有线通信功耗仅包括 DTU 自身的有线接口功耗和 MCU 工作功耗。 在评估电池寿命时,建议考虑工程裕量,如正常运行、重试场景和安全裕量系数。电池寿命估算公式将电池容量与日均功耗及安全裕量相结合,以提供一个实际可行的设备寿命预测。 所有这些计算的详细公式和具体示例,都可以在 DTU 功耗计算指南 中找到。 为准确评估和优化 DTU 设备功耗,以下建议可供实际应用参考: 参考资料: [1] [DTU Power Consumption Calculation Guide] https://think-link.net/tkl-docs/en/AN/AN-26022301%20DTU%20Power%20Consumption%20Calculation%20Guide TKL+EB 组合方案重新定义了 LoRaWAN 在存量市场改造中的角色:它不仅解决了传感器无线化接入的难题,更通过 EB 的灵活性与远程可控性,实现了硬件先行、软件后置、快速迭代的新型部署范式。 无论您是传感器厂商、系统集成商,还是智慧城市解决方案提供商,只要您有 将传统设备接入 LoRaWAN 网络的需求,我们都诚挚邀请您与门思科技合作。 ThinkLink 体验地址 :https://thinklink.manthink.cn ThinkLink 官网 :https://think-link.netDTU 功耗构成
功耗计算方法概述
1. 基础功耗
2. SW 模式功耗
3. LoRaWAN 功耗
4. 有线通信功耗
外部设备功耗
DTU 有线接口功耗
整体功耗计算与电池寿命评估
1. 总通信功耗
2. 日均功耗
3. 电池寿命估算
工程建议
要放客厅不能太大,机器多了也不好管理,成本也高
所以采用了 ITX 机箱,到处找结果咸鱼找到了霜秋凛然的 3D 打印机箱
显卡外置,4 盘位,可以满足所有需求




鸿蒙生态正在高速发展,越来越多的开发者投入到 HarmonyOS 应用开发中。但在实际项目中,我们常常面临这些痛点: HCompass 正是为解决这些问题而生。它不是一个 UI 组件库,而是一套完整的应用开发框架 -- 帮你把地基打好,让你专注于业务本身。 HCompass 的核心思想很简单:把应用拆成一个个独立的"功能包",像搭积木一样组合它们。 每个功能包都是一个自包含的业务模块,拥有自己的页面、ViewModel、服务和数据模型。需要登录功能?放入 HCompass 采用清晰的四层架构设计: 这种分层设计带来的好处是:Core 层可以直接复用到你的下一个项目,Packages 层的功能包可以按需组合,Shared 层确保模块之间的通信有据可依。 轻量级 DI 容器,让功能包之间彻底解耦: 不再需要硬编码依赖关系,功能包只依赖契约,不依赖具体实现。 统一的路由管理,支持路由守卫: 支持登录守卫、权限守卫、条件守卫,灵活组合,按优先级执行。 基于 Axios 封装,告别重复的请求模板代码: 内置拦截器链、统一错误处理、分页逻辑,你只需关注业务数据。 统一的设计令牌,确保 UI 一致性: 三个 ViewModel 基类覆盖 90% 的页面场景: 光说架构不够直观,我们直接看官方实现的 View / ViewModel / Service / Navigation 各司其职,结构一目了然。 功能包对外暴露的能力,全部通过 Shared 层的接口契约来声明: 其他功能包只需要依赖这些接口,不需要知道登录页长什么样、用了哪个 SDK。 三个方法,把服务注入、路由注册、登录守卫全部声明完毕。当用户访问受保护的页面时, View 层不包含任何业务逻辑,只负责绑定 ViewModel 的状态和方法。 注意跳转短信登录页时,通过 DI 容器解析 login 功能包还沉淀了一组可复用的认证 UI 组件: 这就是 HCompass 功能包的开发范式。每个功能包都遵循同样的模式,新成员看一个包就能上手所有包。 HCompass 提供了基于 VitePress 构建的完整文档站点,涵盖: 文档地址:https://hcompass.codelably.com 环境要求: HCompass 基于 MIT 协议开源,你可以自由使用、修改和分发。 欢迎通过 Issue 和 Pull Request 参与项目建设,一起让鸿蒙开发更简单。
模块化、可复用、开源的 HarmonyOS 快速开发框架
为什么需要 HCompass?
核心理念:功能包即积木
auth 功能包。需要用户中心?放入 user 功能包。功能包之间通过契约层解耦,互不依赖,随时可以拆卸和替换。你的应用 = Entry(入口) + 若干功能包(积木)四层架构,职责分明

层级 职责 关键特征 Entry 应用入口 初始化框架、注册功能包、配置路由 Packages 业务功能包 独立开发、独立测试、可跨项目复用 Shared 共享契约 定义服务接口和类型,功能包之间的"协议" Core 框架核心 与业务无关的通用能力,可直接迁移到任何项目 开箱即用的核心能力
依赖注入(DI)
// 在 Shared 层定义契约
interface IUserRepository {
getUserInfo(id: string): Promise<User>;
}
// 在功能包中实现并注册
register<IUserRepository>(ServiceKeys.USER_REPO, new UserRepositoryImpl());
// 在任何地方解析使用
const userRepo = resolve<IUserRepository>(ServiceKeys.USER_REPO);导航系统
// 路由跳转
NavigationService.push(UserRoutes.PROFILE, { userId: "123" });
// 路由守卫 -- 未登录自动跳转登录页
GuardManager.addGuard(new LoginGuard());网络请求
// 继承 BaseNetWorkViewModel,自动管理 loading/error/success 状态
@ObservedV2
class UserProfileViewModel extends BaseNetWorkViewModel<User> {
async fetchData(): Promise<void> {
await this.request(() => this.userRepo.getUserInfo(this.userId));
}
}设计系统
// 百分比常量,告别魔法数字
Row() { ... }.width(P100).height(P50)
// 间距组件,统一视觉节奏
Column() {
Text("标题")
SpaceVerticalMedium() // 12vp 间距
Text("内容")
}基础父类
基类 适用场景 BaseViewModel通用页面,提供生命周期管理 BaseNetWorkViewModel网络请求页面,自动处理 loading/error/success BaseNetWorkListViewModel分页列表页面,内置下拉刷新和上拉加载 实战案例:聚合登录功能包
login 功能包 -- 一个支持华为账号一键登录、微信、支付宝、短信验证码的聚合登录模块,完整展示了功能包从契约定义到页面渲染的全流程。
功能包目录结构
packages/login/
├── LoginModule.ets # 模块生命周期(DI注册 + 路由 + 守卫)
├── view/
│ ├── LoginPage.ets # 主登录页(华为一键登录 + 三方登录)
│ └── SmsLoginPage.ets # 短信验证码登录页
├── viewmodel/
│ ├── LoginViewModel.ets # 登录业务逻辑
│ └── SmsLoginViewModel.ets # 短信登录逻辑
├── components/
│ ├── AnimatedAuthPage.ets # 认证页面基础布局
│ ├── PhoneInputField.ets # 手机号输入组件
│ ├── VerificationCodeField.ets # 验证码输入组件
│ ├── UserAgreement.ets # 用户协议组件
│ └── ...
├── navigation/
│ ├── LoginNav.ets # 登录页导航构建器
│ └── SmsLoginNav.ets # 短信登录页导航构建器
├── services/
│ └── AuthNavSvcImpl.ets # 导航服务实现
└── models/
└── Constant.ets # 三方 APP_ID 配置第一步:在 Shared 层定义契约
// shared/contracts -- 导航服务契约
export const AUTH_NAV_SVC_KEY: string = "authNavService";
export interface IAuthNavSvc {
toLogin(): void; // 跳转登录页
toSmsLogin(): void; // 跳转短信登录页
}
// shared/contracts -- 路由常量
export class AuthRoutes {
static Login = "auth/login";
static SmsLogin = "auth/sms-login";
}第二步:注册模块 -- 一个类搞定 DI、路由、守卫
LoginModule 实现 FeatureModule 接口,框架会在启动时自动调用:export class LoginModule implements FeatureModule {
readonly moduleId: string = 'auth';
readonly moduleName: string = '认证模块';
readonly version: string = '1.0.0';
readonly dependencies: string[] = [];
// 注册服务到 DI 容器
registerServices(container: Container): void {
container.register<IAuthNavSvc>(AUTH_NAV_SVC_KEY, () => new AuthNavSvcImpl());
}
// 注册页面路由
registerRoutes(registry: RouteRegistry): void {
registry.register(AuthRoutes.Login, loginNavBuilderWrapper);
registry.register(AuthRoutes.SmsLogin, smsLoginNavBuilderWrapper);
}
// 注册路由守卫 -- 未登录自动拦截
registerGuards(navigationService: NavigationService): void {
navigationService.registerGuard(new AuthGuard());
}
}AuthGuard 会自动检查登录状态,未登录则跳转到登录页:class AuthGuard implements RouteGuard {
readonly name: string = 'AuthGuard';
readonly priority: number = 100; // 优先级最高
canActivate(context: RouteContext): boolean {
return getUserState().isLoggedIn(); // 已登录放行,未登录拦截
}
onReject(context: RouteContext): void {
// 拦截后自动跳转登录页
navigation?.navigateTo(AuthRoutes.Login, context.params);
}
}第三步:ViewModel 封装业务逻辑
LoginViewModel 继承 BaseViewModel,集中处理多种登录方式:@ObservedV2
export default class LoginViewModel extends BaseViewModel {
@Trace anonymousPhone: string = ""; // 华为账号匿名手机号
// 华为账号一键登录控制器
huaweiLoginController: LoginWithHuaweiIDButtonController =
new LoginWithHuaweiIDButtonController()
.setAgreementStatus(AgreementStatus.ACCEPTED)
.onClickLoginWithHuaweiIDButton((error, response) => {
this.handleLoginWithHuaweiIDButton(error, response);
});
// 微信 OAuth 登录
async onWechatLoginClick(): Promise<void> {
const share: ShareWxSdk = new ShareWxSdk(APP_ID_WX);
const res = await share.wechatOAuth("snsapi_userinfo", state, () => {});
if (res instanceof SendAuthResp) {
const userInfo = await share.getUserInfo(APP_SECRET_WX, res);
// 处理登录结果...
}
}
// 支付宝授权登录
onAlipayLoginClick(): void {
AFServiceCenter.call(AFService.AFServiceAuth, params);
}
}第四步:View 层 -- 声明式 UI 渲染
@ComponentV2
export struct LoginPage {
@Local private vm: LoginViewModel = new LoginViewModel();
build(): void {
AppNavDestination({ viewModel: this.vm }) {
// Logo
LogoIcon()
// 匿名手机号展示
Text(this.vm.anonymousPhone)
// 华为账号一键登录按钮
LoginWithHuaweiIDButton({
params: { style: Style.BUTTON_CUSTOM, loginType: LoginType.QUICK_LOGIN },
controller: this.vm.huaweiLoginController,
})
// 短信验证码登录
IBestButton({
text: $r("app.string.sms_login"),
onBtnClick: () => {
getContainer().tryResolve<IAuthNavSvc>(AUTH_NAV_SVC_KEY)?.toSmsLogin();
}
})
// 第三方登录(微信 / 支付宝 / QQ)
this.buildThirdPartyLogin()
// 用户协议
UserAgreement()
}
}
}IAuthNavSvc 服务来导航,而不是直接引用目标页面 -- 这就是契约解耦的威力。第五步:可复用组件沉淀
// AnimatedAuthPage -- 认证页面通用布局,接收自定义内容
@ComponentV2
export struct AnimatedAuthPage {
@Param title: ResourceStr = "";
@BuilderParam content: CustomBuilder;
build(): void {
LargePaddingVerticalScroll({ fillMaxSize: true }) {
Text(this.title).fontSize(28).fontWeight(FontWeight.Medium)
SpaceVerticalXXLarge()
if (this.content) { this.content(); }
}
}
}
// 短信登录页直接复用这个布局
AnimatedAuthPage({ title: $r("app.string.welcome_login") }) {
PhoneInputField({ ... })
VerificationCodeField({ ... })
UserAgreement()
IBestButton({ text: $r("app.string.login"), ... })
}PhoneInputField、VerificationCodeField、UserAgreement 这些组件,在你开发注册页、找回密码页时可以直接复用。小结:一个功能包的完整生命周期
Shared 层定义契约(接口 + 路由 + 类型)
|
LoginModule 注册服务、路由、守卫
|
ViewModel 封装业务逻辑(华为/微信/支付宝/短信)
|
View 层声明式渲染(绑定 ViewModel,零业务代码)
|
可复用组件沉淀(AnimatedAuthPage / PhoneInputField / ...)技术栈
技术 用途 HarmonyOS NEXT 开发平台 ArkTS / ArkUI 开发语言与 UI 框架 @ohos/axios HTTP 请求 @ibestservices/ibest-ui-v2 UI 组件库 @ibestservices/ibest-orm 数据库 ORM 谁适合使用 HCompass?
完善的文档体系
快速开始
# 克隆项目
git clone https://github.com/codelably/HCompass.git
# 使用 DevEco Studio 打开项目
# 等待依赖同步完成
# 运行 entry 模块即可体验开源协议
这不只是教程,是一份完整实录。(实在不会,丢给CC让他帮你安装) 从【以为很简单】到【真的跑通】,每一个报错、每一个坑、每一次修正,我都记下来了。 最终结果:从素材到公众号草稿箱,全链路自动化可用。 一句话:给素材 → 自动写文 → 自动排版 → 自动进公众号草稿箱。 我只做三件事: 其余流程由系统自动完成。 这套系统的核心价值在于:把重复劳动交给机器,把创意决策留给人。 整个系统由 5 个核心组件构成: 1. Claude Code CLI — 写作引擎 根据 2. AGENTS.md — 写作规范 这是稳定输出的关键。AGENTS.md 是一个开放标准,用于指导 AI 编码代理的行为规范。 在内容创作场景中,它定义了写作风格、禁用词汇、排版规则等约束条件,确保 AI 生成的内容始终符合你的品牌调性。 3. KIE API — 图像生成 用于生成文章封面图。KIE.ai 提供了多种 AI 图像生成模型的统一接口,包括 GPT-Image-1、Midjourney、Flux 等,价格比官方更实惠,响应速度快。 4. ImageMagick — 图片处理 负责封面图的裁剪和尺寸处理。微信公众号封面图有特定尺寸要求:大图 900×383px (2.35:1),小图 383×383px (1:1)。 ImageMagick 可以通过命令行快速完成批量裁剪。 5. @wenyan-md/cli — 发布工具 将 Markdown 转换为微信公众号格式并推送到草稿箱。这个工具支持自动上传图片、应用主题样式、处理代码高亮和公式渲染。 完整流程: 素材(task.md + materials.md) ↓ Claude Code CLI 读取 AGENTS.md 规范 ↓ 生成 article.md ↓ (可选)KIE API 生成封面图 ↓ ImageMagick 裁剪为 900×383px ↓ wenyan publish 推送 ↓ 公众号草稿箱 ✅ 当时已完成: 待完成: 一开始装错包名: npm install -g wenyan-cli 报错 404(包不存在)。 正确包名是: npm install -g @wenyan-md/cli 安装成功(1.0.11)。 **💡 经验:**包名容易搞混,遇到 404 先去 npm 官网确认正确的 package name。 一开始以为可以这样配: wenyan config set appId xxx wenyan config set appSecret xxx 结果报错:too many arguments。 原因:这个版本的 wenyan 不支持这套 config 子命令。 正确方式:用环境变量(示例): echo 'export WECHAT\_APP\_ID="你的AppID"' >> ~/.zshrc echo 'export WECHAT\_APP\_SECRET="你的AppSecret"' >> ~/.zshrc source ~/.zshrc KIE 也是同理: echo 'export KIE\_API\_KEY="你的KIE\_API\_KEY"' >> ~/.zshrc source ~/.zshrc **⚠️ 安全提醒:**AppSecret 只展示一次,若泄露请立即重置并更新本地环境变量。 这是我踩得最深的一个坑。 报错: 40164: invalid ip xxx.xxx.xxx.xxx, not in whitelist **现象:**每次请求显示的出口 IP 都可能变化。 **根因:**VPN/代理自动切换节点导致出口 IP 不固定。 我最终的解决方案: ① 查询当前出口 IP(通过本地代理): curl -s --proxy http://127.0.0.1:7890 https://api.ipify.org ② 把返回 IPv4 加到公众号后台「API IP 白名单」 ③ 发布时固定走同一代理出口: HTTPS\_PROXY=http://127.0.0.1:7890 HTTP\_PROXY=http://127.0.0.1:7890 \ wenyan publish -f "./article.md" 然后成功拿到 **💡 经验:**如果你在国内服务器运行,直接用 可以用渲染命令做最小验收: echo "# 测试" | wenyan render | head -20 能正常输出 HTML 结构说明本地渲染链路没问题。 再用一次 wenyan publish -f "./test-article.md" 经过多次踩坑,我总结出这套可直接复用的标准流程: Step 1:安装工具 npm i -g @wenyan-md/cli brew install imagemagick # macOS Step 2:配置环境变量 export WECHAT\_APP\_ID="你的AppID" export WECHAT\_APP\_SECRET="你的AppSecret" export KIE\_API\_KEY="你的KIE\_API\_KEY" # 可选 Step 3:公众号后台配置 Step 4:本地渲染测试 echo "# 测试" | wenyan render | head -20 Step 5:发布测试(必要时带代理) HTTPS\_PROXY=http://127.0.0.1:7890 \ wenyan publish -f "./article.md" Step 6:到草稿箱确认文章与封面 坑 现象 根因 解决方案 包名错误 npm 404 包名写错 用 配置命令错误 too many arguments 版本不支持 config set 改用环境变量 IP 白名单反复失败 40164 invalid ip 代理出口 IP 漂移 固定节点+固定代理端口 封面报错 找不到 cover frontmatter 缺字段/路径无效 补 cover 且路径有效 doctor 命令不存在 not valid command 该版本无此子命令 用 render + publish 验证 图片尺寸不对 封面显示不全 未按 2.35:1 裁剪 用 ImageMagick 裁剪为 900×383px 很多人以为 AI 写作就是“丢个 prompt 就行”。 错了。 真正决定质量的,是你的 AGENTS.md。 AGENTS.md 是一个由 Linux 基金会下属的 Agentic AI Foundation 管理的开放标准。它最初是为编码代理设计的,但现在被广泛应用于各种 AI 自动化场景。 它的作用是什么? 把你的部落知识(tribal knowledge)写成机器可读的规范。 就像资深工程师脑子里的那些“潜规则”: 在内容创作场景中,AGENTS.md 定义的是: 一个好的 AGENTS.md 应该包含: 💡 最佳实践: 我的 AGENTS.md 里写了什么? 规范写得越细,AI 输出越稳定、越像你自己。 wenyan 是一个开源工具,专门解决“Markdown → 微信公众号”这个痛点。 它做了这些事: 关键 API 调用链路: 1. 获取 access\_token POST https://api.weixin.qq.com/cgi-bin/token 2. 上传图片素材 POST https://api.weixin.qq.com/cgi-bin/material/add\_material 3. 创建草稿 POST https://api.weixin.qq.com/cgi-bin/draft/add citation wenyan 把这些复杂的 API 调用封装成一个简单的命令: wenyan publish -f "./article.md" 这就是工程化的力量。 之前生成封面图,我要在不同平台之间切换: 现在有了 KIE API,一个接口调用所有主流模型。 支持的模型包括: 价格优势: KIE 的定价比官方便宜 20-30%,而且按需付费,不需要订阅。 集成方式: \# 设置 API Key export KIE\_API\_KEY="your\_api\_key" \# 生成图片(伪代码示例) curl -X POST https://api.kie.ai/v1/images/generate \ -H "Authorization: Bearer $KIE\_API\_KEY" \ -d '{"prompt": "...", "model": "gpt-image-1"}' 生成的图片可以直接用 ImageMagick 裁剪: magick input.jpg -crop 900x383+0+0 cover.jpg ImageMagick 是一个开源的图像处理工具,支持超过 100 种图片格式。 核心命令: 1. 裁剪图片 \# 从左上角裁剪 900×383 的区域 magick input.jpg -crop 900x383+0+0 output.jpg \# 居中裁剪 magick input.jpg -gravity center -crop 900x383+0+0 output.jpg 2. 调整尺寸 \# 缩放到 900×383(可能变形) magick input.jpg -resize 900x383 output.jpg \# 按比例缩放,宽度固定 900px magick input.jpg -resize 900x output.jpg 3. 批量处理 \# 批量裁剪当前目录所有 jpg for img in *.jpg; do magick "$img" -crop 900x383+0+0 "cropped\_$img" done 💡 实战技巧: 微信公众号封面有大图(2.35:1)和小图(1:1)两种显示形式。如果想同时兼顾,可以做一张 1283×383px 的图:左边 383×383 是小图区域,右边 900×383 是完整大图。 现在把所有组件串起来,看看一篇文章是怎么从 0 到发布的。 Step 1:准备素材 创建 \# 任务 将 YouTube 视频转换成公众号文章 \# 要求 - 字数 1500-2000 - 风格:信息猎手+卡兹克 - 必须包含数据和 SOP 创建 \# 素材 - YouTube 视频链接:https://youtube.com/watch?v=xxx - 视频文字稿:(粘贴完整转录文本) Step 2: Claude 自动写作 claude code --agents ./AGENTS.md \ "读取 task.md 和 materials.md,生成 article.md" Claude 会: Step 3:生成封面图(可选) \# 调用 KIE API 生成封面 \# (这里可以用脚本或手动) \# 裁剪为公众号尺寸 magick cover-raw.jpg -crop 900x383+0+0 cover.jpg Step 4:发布到草稿箱 wenyan publish -f "./article.md" Step 5:人工审核 登录公众号后台,在草稿箱中: 整个流程,除了审核,全部自动化。 从素材到草稿箱,不超过 5 分钟。 系统跑通后,我又做了一些优化: wenyan 不仅支持微信,还支持知乎、掘金、CSDN 等平台。 可以写一个脚本,一次生成,多平台分发: \# 发布到微信 wenyan publish -f "./article.md" --platform wechat \# 发布到知乎 wenyan publish -f "./article.md" --platform zhihu 结合 cron 或 GitHub Actions,可以实现定时自动发布: \# .github/workflows/publish.yml name: Auto Publish on: schedule: - cron: '0 9 * * *' # 每天早上9点 jobs: publish: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - run: npm i -g @wenyan-md/cli - run: wenyan publish -f "./article.md" env: WECHAT\_APP\_ID: ${{ secrets.WECHAT\_APP\_ID }} WECHAT\_APP\_SECRET: ${{ secrets.WECHAT\_APP\_SECRET }} 可以加一个 AI 审核层,在发布前自动检查: 如果不通过,自动打回重写。 发布后,可以用 n8n 或 Zapier 监控文章数据: 然后自动生成数据报告,反馈到下一次的 task.md 中,形成闭环优化。 如果你是: 投入产出比: 但工具只是放大器。 真正决定内容质量的,永远是你的 AGENTS.md。 规范写得越细,AI 输出越稳定、越像你自己。 如果你的 AGENTS.md 只有 3 行,那 AI 生成的内容就是 3 行的质量。 如果你的 AGENTS.md 有 150 行细节约束,那 AI 就能生成 150 行质量的内容。 这是一个“垃圾进,垃圾出”(Garbage In, Garbage Out)的系统。 你投入多少,就能收获多少。 这套系统从想法到跑通,我花了整整半天。 踩了很多坑。 但当我第一次看到文章自动出现在草稿箱时,那种成就感是无法替代的。 这不仅仅是一个自动化工具。 它改变了我对内容生产的理解: 从手工作坊 → 流水线工厂。 以前写一篇文章,我要: 总计:3 小时 40 分钟。 现在: 总计:32 分钟。 效率提升了 7 倍。 更重要的是,我的精力被解放了。 我不再纠结【这个词用得对不对】、【这个段落要不要换行】。 我只需要关注:这个选题值不值得写?这个观点够不够深刻? 工具处理执行,人类负责决策。 这才是 AI 时代内容创作的正确姿势。 如果你也想搭建这样一套系统,我的建议是: 别怕踩坑。 每一个报错,都是你对系统理解更深一层的机会。 每一次调试,都是你把“隐性知识”变成“显性规范”的过程。 当你把所有坑都踩完,你就拥有了一套真正属于自己的内容生产流水线。 而这套流水线,会成为你最大的竞争壁垒。 P. S. 如果你在搭建过程中遇到问题,欢迎留言交流。我会持续更新这套系统的优化方案。 此即未来。🤖 这个系统在做什么?

task.md)materials.md)🧩 系统组成(最终可用架构)
task.md 和 materials.md 生成符合要求的 article.md。这是整个系统的大脑,负责理解需求、提炼素材、生成文章。
🛠 从零到跑通:完整过程
1)先盘点依赖
2)安装 wenyan(第一个坑)
3)公众号配置(第二个坑:命令用错)
4)IP 白名单(最大的坑)


media_id,文章进入草稿箱。curl -s https://ipinfo.io/ip 查询出口 IP 即可,不需要代理。5)验收命令(替代 doctor)
wenyan doctor 在该版本不可用。wenyan publish 验证微信接口链路:
✅ 最终可复用 SOP(精简版)

🕳️ 坑点总表(实战版)
@wenyan-md/cli
🎯 关键技术深度解析
AGENTS.md:稳定输出的灵魂
> 格式
wenyan-md:从 Markdown 到公众号的最后一公里
KIE API:多模型图像生成的统一入口

ImageMagick:命令行图片处理的瑞士军刀
🔄 完整自动化工作流实战
场景:我要写一篇“独立开发者搞钱案例”
task.md:materials.md:
💡 进阶优化方向
1)多平台发布
2)定时发布
3)内容质量监控
4)数据分析闭环
🤔 这套系统值不值得搭?
🎬 写在最后


最近经常逛公园,对遇到的植物感兴趣但是浏览器自带的拍照识物不好用,V 友有推荐的工具吗?最好是可以接入 API 的,自己也可以做一些小玩意~
给定一个 host.html
<!doctype html>
<meta charset="utf-8" />
<title>host</title>
<iframe
id="outer"
sandbox="allow-scripts"
src="https://another-site/sandbox.html"
></iframe>
其中 sandbox.html
<!doctype html>
<meta charset="utf-8" />
<title>sandbox</title>
<script>
const inner = document.createElement('iframe');
inner.src = 'https://another-another-site/inner.html';
document.body.appendChild(inner);
</script>
其中 inner.html
<!doctype html>
<meta charset="utf-8" />
<title>inner</title>
<script>
const v = localStorage.getItem('no_such_key');
console.log('value:', v);
</script>
问:试分析localStorage.getItem('no_such_key')的行为
我的回答:这大概率会抛出一个 SecurityError ,我的映像里因为 sandbox 的缘故 iframe 里面的网页浏览器是不会指定 origin 的。没有 origin 的话大概率 localstorage, session storage, index db 这一类都不可用
Follow up
大平衡者应该也是 0.06,但是有几位佬再全力冲击,马上就不是最稀有了
幸运儿这个勋章虽然是 T2,但是想要获得真实要靠脸啊(其实是靠坚持不懈的水
在软件分发领域,代码签名证书是保障代码完整性、验证开发者身份的核心工具。随着用户安全意识提升和操作系统对未签名软件的拦截机制强化,开发者需在EV(扩展验证)与OV(组织验证)两类证书中做出选择。本文从技术原理、验证流程、应用场景及成本效益四大维度展开分析,为开发者提供决策参考。 EV证书采用FIPS 140-2认证的USB Key存储私钥,通过物理隔离防止私钥泄露。其技术核心在于: OV证书通过企业组织信息验证构建信任链,技术特点包括: EV证书的验证流程包含以下关键环节: 案例:某金融科技公司申请EV证书时,因注册地址与办公地址不一致,需补充提供租赁合同及水电费账单,导致审核周期延长至9个工作日。 OV证书的验证流程相对简化: 数据:根据JoySSL统计,OV证书平均签发时间为1.8个工作日,92%的申请可在24小时内完成。 案例:某安全软件厂商使用EV证书后,软件安装转化率提升37%,用户投诉率下降62%。 数据:OV证书年均成本约为EV证书的65%,但可覆盖80%的常规软件分发场景。 表格 注:JoySSL等厂商提供OV证书首年优惠,实际成本可低至3,000元/年。 EV证书: OV证书: 以年分发量10万次的软件为例: EV证书: OV证书: 结论:高风险、高价值软件优先选择EV证书;标准化企业应用OV证书更具经济性。 作为国内领先的数字证书服务商,JoySSL提供以下创新服务: 案例:某制造业企业通过JoySSL OV内网IP证书,在3天内完成200台工业设备的HTTPS改造,年节省加密设备采购成本超50万元。 评估安全等级: 计算成本阈值: 测试兼容性: 在软件供应链安全日益严峻的今天,代码签名证书已从“可选配置”升级为“必选项”。开发者需根据自身安全需求、成本预算及业务发展阶段,在EV与OV证书间做出理性选择。对于大多数企业而言, “OV证书打基础+EV证书保核心” 的混合策略,或许是平衡安全与成本的最优解。
代码签名证书申请入口,填写注册码230959兑换一、技术原理:信任链构建的底层逻辑
1. EV代码签名证书:基于硬件加密的强信任体系
2. OV标准证书:平衡安全与效率的解决方案
二、验证流程:从申请到签发的全链路对比
1. EV证书:5-7个工作日的深度审核
2. OV证书:1-3个工作日的标准化流程
三、应用场景:安全需求与成本敏感度的博弈
1. EV证书的强制适用场景
2. OV证书的性价比优势场景
四、成本效益分析:长期投资回报率计算
1. 直接成本对比
证书类型 年均费用(元) 硬件成本 审核周期 续费成本 EV证书 5,300 含USB Key 5-7天 与首年同 OV证书 3,800 无 1-3天 降价5% 2. 隐性成本考量
3. 长期ROI模型
五、JoySSL的差异化解决方案
六、决策框架:三步选择法
很多人会问:为什么不用 DOM?为什么不用 SVG? 答案其实很残酷。 浏览器为了渲染 DOM 和 SVG 节点,底层维护了一套极其庞大的对象模型和布局引擎。每一次你在触控板上轻轻一划,哪怕只是让相机的世界坐标移动几个像素,都可能被迫引发大量节点的重排(Reflow)与重绘(Repaint)。 当节点树突破临界点,主线程被压垮,原本丝滑的缩放和平移就会掉进帧率泥潭。 无限画布的核心诉求只有一个: 无论同屏多少元素,都要稳定 60fps。 想达成这一点,你必须脱离浏览器的排版流,进入纯粹的"像素缓冲区"世界。 在当前的 Web 技术栈中,能承载这个目标的只有两条路:Canvas 2D 和 WebGL。 它们不是同一个问题的两种解法,而是两种截然不同的工程解法。 Canvas 2D 是绝大多数画布项目的起点——在线白板、流程图工具、轻量设计工具。 原因很简单:它太好用了。 画一个带边框的矩形: 想画曲线就调用 Canvas 2D 给你的不是 GPU 访问权,而是一套图形语义 API——你在表达"我要画什么",而不是"我要如何让 GPU 算出这些像素"。底层可能是 Skia、CoreGraphics 或 Direct2D,你不需要知道。 这就是它的价值。 但这背后有个隐藏代价:你把控制权交给了浏览器。 Canvas 2D 的指令调用是单向且串行的,绝大多数场景依赖 CPU 处理绘图指令。当你同屏几万个图形,每一帧都要重新执行所有 你没法告诉浏览器: Canvas 不给你这个能力。 所以当大量使用图层合成、全局阴影时,每帧的绘制时间很容易突破 16.6ms 的底线。 对于中等规模应用,Canvas 2D 性价比极高。但当你开始追求"Figma 级别"的体验时,它会成为瓶颈。 跨入 WebGL 的那一刻,你会遇到一个残酷现实: 这个世界里没有矩形。只有三角形。 我们来做一个直观对比。 在 Canvas 2D 里画一个红色矩形: 2 行代码。 在 WebGL 里画同一个矩形: 如果你想画一个"带圆角的 2 像素红框矩形"呢? 在 WebGL 中,连 甚至连文字排版都是个难题。WebGL 本身不懂文字——你要么把文字先用 Canvas 渲染成图片,再贴到 WebGL 里作纹理(Texture),要么引入复杂的 SDF(有向距离场)算法让 GPU 渲染出不失真的文字轮廓。 复杂度是指数级增长的。 选择 WebGL,你不只是在换一个 API。你是在决定成为一个"引擎开发者"。 我后来想明白了一件事: 大多数人不是死在 Canvas 性能不够,而是死在过早复杂化。 如果你的同屏元素不超过 5000,Canvas 2D 够用。如果你连空间索引、脏矩形优化都没做,谈 WebGL 就是在逃避真正的问题。 但如果你的目标是做重型设计工具级产品——WebGL 是绕不过的坎。 区别在于:你是否愿意为"控制权"买单。 关于 Canvas 还是 WebGL,没有统一的正确答案。取决于你的产品对同屏元素量级的刚需,以及团队对底层图形基建的掌控力。 本系列不会从零手写 WebGL 引擎。 我们选择基于 Canvas 2D,但不会裸写绘制指令。 我们会引入 Konva.js 作为底层支撑。Konva 是一个基于 Canvas 2D 的场景图引擎,自带图元管理、分层渲染和像素级事件系统。我们将在此基础上,剥离前端框架(React/Vue)的边界,用纯 TypeScript 构建一个接近 Excalidraw 架构的轻量级无限画布。 这不是性能最极限的方案。 但它是当前阶段最诚实的解法。 无论 Canvas 还是 WebGL,它们本质上都只是一块"死板"的像素画布。 当你脱离 DOM,你就失去了节点树。 浏览器的 但不会告诉你:这是第 7823 个图元。 悬停、拖拽、框选、事件冒泡——全部失效。 当画布变成一个没有结构的像素世界,点击本质上变成了一道几何题。 下一篇,我们用真实代码来解这道题:在没有 DOM 的"野生像素图"上,从零实现空间检测与事件分发系统。DOM 和 SVG 为什么第一个被淘汰?
Canvas 2D:浏览器替你扛住复杂度
ctx.fillStyle = "rgba(255, 0, 0, 0.5)";
ctx.strokeStyle = "#000000";
ctx.lineWidth = 2;
ctx.beginPath();
ctx.roundRect(10, 10, 100, 100, [5]);
ctx.fill();
ctx.stroke();quadraticCurveTo,想写字就 fillText,想加阴影就配置 shadowBlur。draw 指令。哪怕只是平移一个像素,也要把全部图形循环重绘一遍。"把这批图形放到显存里,下次只改变换矩阵就行。"
WebGL:你需要接管整个物理世界
ctx.fillStyle = "red";
ctx.fillRect(10, 10, 100, 100);// 1. 定义矩形四个顶点的坐标(拆分成两个三角形)
const vertices = new Float32Array([
-0.5,
0.5, // 左上
-0.5,
-0.5, // 左下
0.5,
0.5, // 右上
0.5,
-0.5, // 右下
]);
// 2. 将数据送入显存 Buffer 中
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
// 3. 编写 Vertex Shader 算坐标,编写 Fragment Shader 涂成红色
// ...(此处省略几十行 Shader 编译与变量绑定代码)...
// 4. 通知 GPU 绘制这两个三角形(Triangle Strip)
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);roundRect 都没有。你需要用数学算法算出圆弧上的离散点,将边框转换成一个个细小的三角形,然后还要自己在着色器里处理抗锯齿(Anti-Aliasing)。真正的问题不是性能
我们的选择:现实、克制
真正的深水区:交互
click 事件只能告诉你:用户点在了 { x: 500, y: 400 }。
1.项目成功上线
老板:AI 果然强大,可以把人裁了,换些实习生来,节约成本
2.项目不符合预期
老板:这些个员工 AI 都用不明白,不思进取,裁了,换一批
在如今的数字化时代,软件分发面临着严峻的安全挑战。当用户下载未签名的软件时,系统往往会弹出“未知发布者”的安全警告,这极大地影响了软件的口碑与下载转化率。代码签名证书正是解决这一信任难题的基础设施。 代码签名证书是一种基于公钥基础设施(PKI)技术的数字证书,专门用于软件开发者对其开发的代码(如.exe、.dll、.msi、.zip等文件)进行数字签名。 它的核心功能就像软件的“数字身份证+防伪封条”: 在主流操作系统(Windows、macOS)和安全软件严格拦截未知名软件的今天,部署代码签名证书已成为刚需: 申请代码签名证书通常需要经历选择类型、提交验证、获取证书三个步骤。目前,国产自主数字证书品牌JoySSL因其支持国内验证流程、兼容国际标准且提供自动化管理工具,成为众多开发者的新选择。 首先,访问JoySSL官网,根据你的身份和需求选择证书: 审核通过后,下载证书文件(通常为PFX格式,包含私钥)。使用签名工具(如Windows SDK的一、什么是代码签名证书?

二、为什么要部署代码签名证书?
三、怎么申请代码签名证书?(以JoySSL为例)
以下是具体的申请与部署流程:代码签名证书申请入口
1. 选择证书类型
注:注册JoySSL账户时,通常可填写推荐码 230970 以获取技术指导或优惠套餐。2. 提交CSR与验证材料
3. 下载证书并签名
signtool.exe)对软件进行签名并添加时间戳。
上班下大雨就算了,骰子还扔了个 1,第一次鉴定大失败
效果咋样,普通 Linux 服务器跑起来咋样
先给结论: 在 scrapy-redis 爬虫中,如果不重写 是否触发去重,取决于 关键就在 逻辑链路: 自己重写示例: 不影响: RedisSpider 作为分布式爬虫入口,URL 是从 Redis 队列 所以默认 核心结论: 不是没用 Redis Scheduler,而是
dont_filter=True 跳过了去重器,所以 Redis 里不会创建 dupefilter 这个 Key。问题背景
start_requests(),而是使用父类默认方法,会发现 Redis 里没有 xxx:dupefilter 这个 Set。调度流程
Spider → Engine → Scheduler → DupeFilter(去重器)request.dont_filter 的值。父类 start_requests() 做了什么?
scrapy_redis.spiders.RedisSpider 默认实现大致如下:yield Request(url, dont_filter=True)dont_filter=True。调度器内部逻辑
Scheduler.enqueue_request() 中有如下判断:if not request.dont_filter:
if self.df.request_seen(request):
return Falsedont_filter=True → 跳过去重逻辑request_seen()SADD top250:dupefilter fingerprint对比验证
场景 方式 Redis dupefilter 使用父类默认 start_requests()dont_filter=True❌ 不创建 自己重写,使用默认 Request dont_filter=False(默认)✅ 创建 def start_requests(self):
yield scrapy.Request(url) # dont_filter 默认为 False,会触发去重dont_filter=True 的影响范围
dont_filter=True 只影响:为什么 scrapy-redis 这样设计?
lpush 进来的,设计假设是:dont_filter=True,跳过去重。总结
问题 答案 是否使用 Redis Scheduler? 是 为什么没有 dupefilter? 因为 dont_filter=True,跳过了去重是不是没用 Redis? 不是 去重什么时候发生? 当 dont_filter=False 时本质理解:
dont_filter 控制的是单个 Request 是否走去重流程,与使用哪个 Scheduler 无关。
新春快乐,祝 V2 的老哥、靓女们,新春大吉,身体健康,马年鸿运当头,万事顺遂!
开工大吉,宅家上网、打游戏、炒股、交易赚钱、亲戚串门连 Wi-Fi ,没个稳如老狗的网络怎么行?
趁着新春佳节,继续分享现阶段我老婆最常受理的广州电信套餐,给各位准备升级宽带、或者给家里长辈拉网的老哥参考。
如有需要在广州报装宽带或相关咨询,可联系贱内 Base64: MTgwMTE4ODA2Njk=
🧨 第一种,费用 149/月(新春强烈推荐)
适合个人、小家庭、多人合租、公寓、商铺等使用。这个套餐最香的一点是: 如果家庭成员有其它运营商的,可以不换号直接绑定到此套餐,这样就直接把家人其他卡的月租给免了!全家共享,绝对的每月话费节省利器。
🧨 第二种,费用 199/月
适合对网络要求高的大家庭或商务办公使用。过年亲朋好友来串门,十几台设备同时连 Wi-Fi 抢红包、打王者、看 4K 电影都不带卡顿的,越用越省心。