Live2D 模型渲染崩溃:游戏提取动作文件的 TotalPointCount 元数据错误排查与修复

Live2D 调包侠踩坑记:一个「少算 44 个点」引发的渲染崩溃 故事的开端 某开发者最近在折腾网页 Live2D 看板娘。项目基于 naihe-live2d-widget-v3,它对标的是经典的 live2d-widget,但底层换成了最新的 Cubism SDK for Web v5,专门渲染 .moc3 格式的新版模型。 一切看起来都很顺利——直到把从游戏里提取的一个猫耳角色模型(编号 416)丢进去。 模型加载了,但动作一播放就崩。刷新,又崩。时好时坏,但大部分时间页面一片空白,控制台躺着一行红字: Uncaught (in promise) TypeError: Cannot set properties of undefined (setting 'time') 对调包侠来说,这可能就是那一刻想关电脑的信号。 定位问题:不是模型坏了,是 Meta 骗了 SDK 报错指向 live2d-sdk.js 中 parse 函数的某个 .time 赋值操作。顺着调用栈往上翻: parse → create → loadMotion → preLoadMotionGroup → setupModel → loadAssets ——动作文件解析时崩溃了。 📌 前置知识:Live2D 的 .motion3.json 文件描述了一条条动画曲线。每条曲线包含一串「段」(segments),每个段由若干个「控制点」(points)定义。Meta 段会预先声明总点数和总段数,方便 SDK 预分配内存。 某个开发者的直觉是:会不会是 Meta 里声明的数量跟实际数据对不上? 写了一段简单的验证脚本跑了一下: let calculatedPoints = 0; for (const curve of curves) { const segs = curve.Segments; let pos = 0, first = true; while (pos < segs.length) { if (first) { calculatedPoints++; pos += 2; first = false; } const segType = segs[pos]; switch (segType) { case 0: calculatedPoints++; pos += 3; break; // 线性 case 1: calculatedPoints += 3; pos += 7; break; // 贝塞尔 case 2: calculatedPoints++; pos += 3; break; // 步进 case 3: calculatedPoints++; pos += 3; break; // 反向步进 } } } 结果一看——全都对不上。 ...

二月 26, 2023 · 3 分钟 · 614 字 · yaomingye

JVM类加载与反射:从Class文件到运行时类的全景——Klass模型、类加载器、Metaspace与反射的桥接机制

反射凭什么认识你的类 某开发者写过这样一段代码,当时觉得平平无奇: Method method = obj.getClass().getDeclaredMethod("secretLogic", String.class); method.setAccessible(true); Object result = method.invoke(obj, "hacked!"); 运行完才反应过来——凭什么?一个 Class 对象拿在手里,连 private 方法都能翻出来调用,连参数签名都一清二楚。JVM 到底在背后存了什么东西,让反射可以"看见"一个类的全部内脏? 答案就在 HotSpot 的 Klass 模型 里。 📌 前置知识:本文假设读者知道 .class 文件是 javac 编译产物、JVM 基本内存分区(堆、栈、方法区)的常识。如果不清楚方法区和 Metaspace 的关系,后文有图。 全景先览:JVM 中的类在哪 在深入 Klass 之前,先看一张全局地图——一个类从 .class 文件到进入 JVM 运行时,到底经过了哪些区域。 这张图先有一个印象就行——关键记住一点:一个类被加载后,JVM 在 Metaspace 里存了一份"类模板"(InstanceKlass),在 Heap 里放了一个轻量的"Java 镜像"(Class 对象)。 反射读的元信息来自前者,你代码里 getClass() 拿到的是后者。 类模板:HotSpot 眼中的"类" 你写的每一个 Java 类,在 HotSpot 内部都有一个对应的 C++ 对象来描述它——这就是 Klass 模型。 Klass 继承链 Klass ← 所有类的抽象基类 ├── InstanceKlass ← 普通 Java 类(你写的 99% 的类) │ ├── InstanceMirrorKlass ← Class 对象本身(镜子) │ └── InstanceRefKlass ← 引用类型(软/弱/虚引用) ├── ArrayKlass ← 数组类 │ ├── TypeArrayKlass ← 基本类型数组(int[]) │ └── ObjArrayKlass ← 对象数组(String[]) ⚠️ 新手提示:Klass 不是 Class。Klass 是 C++ 层面的数据结构,存在 Metaspace; java.lang.Class 是 Java 层面的对象,存在 Heap。日常说"类模板"通常指 InstanceKlass。 ...

二月 22, 2023 · 3 分钟 · 590 字 · yaomingye

接口重试的 4 种实现方案:手动重试、Spring Retry、Resilience4j、OpenFeign

接口调失败了?重试之前先看看这四种姿势 为什么需要重试 分布式系统里,接口调用失败是常态,不是意外。网络抖动、服务重启、连接池满、Full GC 停摆——这些故障每天都在发生。 但并不是每次失败都值得重试。有些失败重试一下就好了(瞬时故障),有些失败重试一万次也没用(业务异常、参数错误)。区分这两类失败,是设计重试策略的前提: flowchart TD Call(["发起 RPC 调用"]) --> Result{调用结果} Result -->|"成功"| OK([结束]) Result -->|"失败"| Type{失败类型} Type -->|"网络超时\n连接 refused\n503 Service Unavailable"| Retryable["可重试\n瞬时故障"] Type -->|"400 Bad Request\n403 Forbidden\n业务状态异常"| NoRetry(["直接抛出\n重试无意义"]) Retryable --> Idempotent{接口是否幂等} Idempotent -->|"是"| DoRetry(["执行重试"]) Idempotent -->|"否"| Warn["警告\n需人工介入"] Warn --> Check([手动排查]) classDef startEnd fill:#701a4c,stroke:#e11d48,stroke-width:2.5px,color:#fce7f3,font-weight:bold; classDef condition fill:#2a1147,stroke:#a855f7,stroke-width:2px,color:#ede9fe,font-weight:bold; classDef process fill:#1e1e24,stroke:#6b7280,stroke-width:2px,color:#e5e7eb; classDef reject fill:#450a0a,stroke:#dc2626,stroke-width:2px,color:#fecaca,font-weight:bold; class Call,OK,NoRetry,DoRetry,Check startEnd; class Result,Type,Idempotent condition; class Warn reject; ⚠️ 新手提示:重试只适用于瞬时故障(transient failure)。如果下游返回 400/403/ 业务校验失败,查日志修代码,别重试。 方案一:手动重试——最简单但也最危险 最直接的方式就是用循环自己搞: ...

二月 16, 2023 · 3 分钟 · 568 字 · yaomingye

微服务支付系统全景:从接入第三方到对账结算,一笔钱走完的九九八十一难

一笔钱在微服务里到底怎么走的 为什么支付是微服务里最难啃的骨头 做业务开发,碰到的最常见代码可能就是 CRUD。增删改查写熟了,觉得微服务也不过如此——直到某天被分配了支付模块。 支付和普通业务有本质区别:普通业务操作的是"信息",支付操作的是"钱"。写错一行代码,信息可以修,钱出去了就是真金白银的损失。更麻烦的是,支付不是自己一个服务就能搞定的事——要接微信、要接支付宝、可能还要接银联、接 Stripe。每家渠道的接口风格不同,回调机制不同,对账方式也不同。上游还有订单系统在等支付结果,下游有会计系统等着入账。 flowchart LR %% 半暗底色 + 高亮描边:完美适配博客深色/浅色双主题 %% classDef root fill:#0f172a,stroke:#3b82f6,stroke-width:2.5px,color:#bfdbfe,font-weight:bold; classDef process fill:#1e1e24,stroke:#6b7280,stroke-width:2px,color:#e5e7eb; classDef highlight fill:#431407,stroke:#ea580c,stroke-width:2px,color:#fed7aa,font-weight:bold; classDef reject fill:#450a0a,stroke:#dc2626,stroke-width:2px,color:#fecaca,font-weight:bold; RISK1["[钱出去了\n回不来]"] RISK2["[重复支付\n多扣款]"] RISK3["[回调丢失\n订单卡死]"] RISK4["[对账不平\n财务追杀]"] RISK5["[渠道故障\n全站瘫痪]"] CORE["支付系统\n核心矛盾:\n复杂 × 高风险 × 强一致性"] RISK1 --> CORE RISK2 --> CORE RISK3 --> CORE RISK4 --> CORE RISK5 --> CORE class RISK1,RISK2,RISK3,RISK4,RISK5 reject; class CORE highlight; 把这些复杂度拆开来看,一个支付系统本质上要解决五个问题: 怎么收——对接各种支付渠道,屏蔽渠道差异 怎么记——每笔钱的来龙去脉都要有据可查 怎么验——回调确认钱真的到了,不是"用户说付了就算付了" 怎么对——自己的账和渠道的账对得上 怎么退——钱能收就能退,但不能退多了,也不能重复退 下面逐个拆解。 支付系统的"五脏六腑":模块全景图 在动手写代码之前,先搞清楚一笔钱在系统里要经过哪些模块。 flowchart TD %% 半暗底色 + 高亮描边:完美适配博客深色/浅色双主题 %% classDef process fill:#1e1e24,stroke:#6b7280,stroke-width:2px,color:#e5e7eb; classDef data fill:#052e16,stroke:#16a34a,stroke-width:2px,color:#bbf7d0,font-weight:bold; classDef root fill:#0f172a,stroke:#3b82f6,stroke-width:2.5px,color:#bfdbfe,font-weight:bold; classDef highlight fill:#431407,stroke:#ea580c,stroke-width:2px,color:#fed7aa,font-weight:bold; subgraph FRONT["接入层"] GATE["[支付网关\n路由/验签/限流/协议转换]"] end subgraph CORE_MOD["核心支付域"] ORDER["[支付订单服务\n订单创建/查询/状态流转]"] CHANNEL["[支付渠道服务\n渠道抽象/路由/适配器]"] CALLBACK["[回调处理服务\n异步通知/幂等/重试]"] REFUND["[退款服务\n退款申请/审核/执行]"] end subgraph BILLING["清算对账域"] RECON["[对账服务\nT+1对账/差异处理/长款短款]"] SETTLE["[结算服务\n分账/手续费/入账]"] end subgraph INFRA["基础设施"] MQ["[消息队列\n异步解耦/重试]"] IDEM["[幂等表\n防重支付/防重回调]"] LGR["[流水表\n不可变审计日志]"] end FRONT --> CORE_MOD CORE_MOD --> BILLING INFRA -.-> CORE_MOD INFRA -.-> BILLING class GATE highlight; class ORDER,CHANNEL,CALLBACK,REFUND process; class RECON,SETTLE data; class MQ,IDEM,LGR process; 每个模块解决一类问题: ...

二月 14, 2023 · 6 分钟 · 1167 字 · yaomingye

统一开发团队的流水线哲学

统一开发团队的流水线哲学:从 .gitlab-ci.yml 到 IDP 平台化治理 问题:为什么每个项目的流水线都长得不一样? 团队规模还小的时候,CI/CD 流水线通常是怎么来的?某个开发者把上个项目的 .gitlab-ci.yml 拷过来,改两行,能跑就行。再过两个月新开一个服务,又从那个改过的版本拷过去再改两行。一年下来,十个微服务有十种写法,review 流水线配置的时间比 review 业务代码还长。 这不是某个团队的个例,而是缺少 统一流水线规范 的必然结果。 造成这种混乱的根源有三层: 第一层,认知门槛。GitLab CI 的配置语法看似简单——stages、jobs、script、only/except,但真正写好需要理解 runner 的执行模型、cache 和 artifact 的区别、image 与 service 的作用域。大部分人止步于"能跑就行",不会主动深究。 第二层,缺乏约束。GitLab CI 本身不做 schema 校验,before_script 里写什么都行,Dockerfile 里的 RUN 指令堆多少层也没人管。没有门禁、没有模板、没有 review 机制,流水线质量完全依赖开发者个人习惯。 第三层,业务压力。“先把功能上线"永远排在"把流水线写好"前面。流水线的技术债不像业务代码那样直接影响用户,于是越欠越多,直到有一天构建 40 分钟没人敢动。 📌 前置知识——GitLab CI 基础:建议先理解 .gitlab-ci.yml 的 stages、jobs、script、image、cache、artifacts 六个核心关键字(只需理解各自的职责和生效范围即可)。 结构:一条理想流水线的骨架 先从最核心的问题开始: 一条"完美"的 .gitlab-ci.yml 应该长什么样? 答案不是给你一个 500 行的 YAML 文件,而是说清楚 原则。原则对了,具体写法可以按项目微调。 四阶段流水线 flowchart TD S([📥 代码提交]) --> A1 subgraph Stage1["🔍 验证阶段"] A1[📌 编译检查]:::process A2[📌 代码风格]:::process A3[📌 单元测试]:::process end Stage1 --> Stage2 subgraph Stage2["📦 构建阶段"] B1[📌 构建镜像]:::process B2[📌 推送镜像仓库]:::process B3[📌 导出制品]:::process end Stage2 --> Stage3 subgraph Stage3["🚀 部署阶段"] C1[📌 部署开发环境]:::highlight C2[📌 集成测试]:::process C3{📌 验收通过?}:::condition end C3 -->|✅ 是| Stage4 C3 -->|❌ 否| Rollback[⛔ 回滚通知]:::reject subgraph Stage4["📡 生产发布"] D1[📌 灰度发布]:::highlight D2[📌 全量上线]:::startEnd end classDef startEnd fill:#701a4c,stroke:#e11d48,stroke-width:2px,color:#fce7f3,font-weight:bold; classDef condition fill:#2a1147,stroke:#a855f7,stroke-width:1.5px,color:#ede9fe,font-weight:bold; classDef process fill:#1e1e24,stroke:#6b7280,stroke-width:1.5px,color:#e5e7eb; classDef reject fill:#450a0a,stroke:#dc2626,stroke-width:1.5px,color:#fecaca,font-weight:bold; classDef highlight fill:#450a0a,stroke:#dc2626,stroke-width:1.5px,color:#fecaca,font-weight:bold; 四个阶段各司其职: ...

一月 29, 2023 · 6 分钟 · 1241 字 · yaomingye

SpringCloud微服务测试实战

SpringCloud微服务测试实战:分层策略、完整代码与AI时代的新思路 问题切入 写了一万行业务代码,测试用例只有三行——这种事情在微服务项目里尤其常见。不是开发者不想写测试,而是SpringCloud环境下的测试确实比单体应用复杂得多:服务之间通过Feign/Dubbo调用、配置在Nacos远端、消息通过RocketMQ/Kafka传递、数据库还分库分表。随便写个Service都依赖五六个外部组件,怎么测? 先说结论:微服务测试的核心思路是分层隔离。不同层级关注不同的验证目标,用不同的策略来隔离外部依赖。每一层有明确的边界和颗粒度,而不是不管三七二十一全部启动Spring容器。 flowchart TD subgraph Top[🔺 测试金字塔:越往上越慢、越贵、越少] subgraph L5[⏱️ 端到端测试] E2E[🌐 E2E测试\n全链路验证\n数量:极少] end subgraph L4[🔗 契约/集成测试] CONTRACT[📋 契约测试\nFeign/Dubbo接口契约\n数量:少量] INTEG[🔧 Service集成测试\nSpring容器+真实DB/Redis\n数量:适中] end subgraph L3[🧩 切片测试] WEB[🌐 Web层测试\n@WebMvcTest\n仅Controller上下文] DATA[🗄️ 数据层测试\n@DataJpaTest\n仅JPA上下文] end subgraph L2[⚡ 单元测试] UNIT[📐 纯单元测试\n无Spring容器\nMock所有依赖\n数量:大量] end end L5 --> L4 L4 --> L3 L3 --> L2 classDef layer fill:#1e1e24,stroke:#6b7280,stroke-width:1.5px,color:#e5e7eb; classDef highlight fill:#450a0a,stroke:#dc2626,stroke-width:1.5px,color:#fecaca,font-weight:bold; classDef data fill:#052e16,stroke:#16a34a,stroke-width:1.5px,color:#bbf7d0,font-weight:bold; class E2E,CONTRACT,INTEG,WEB,DATA,UNIT layer class L2 highlight class L2 data 这个金字塔翻译成SpringCloud语境下的操作指南,就是下面这张分层策略表: 测试层级 启动Spring容器? 真实依赖 Mock/Stub 单个耗时 覆盖目标 纯单元测试 否 无 所有外部依赖 毫秒级 业务逻辑分支 Web层切片 是(仅Controller) 无 Service/Mapper 1 ~ 3秒 参数校验/序列化/异常处理 数据层切片 是(仅JPA) 内嵌数据库(H2) 无 1 ~ 3秒 SQL映射/查询方法 Service集成测试 是(完整) H2/内嵌Redis Feign/MQ/外部API 3 ~ 8秒 事务边界/缓存/业务编排 契约测试 是(Consumer端) 无 对Provider的Stub 2 ~ 5秒 Feign接口签名一致性 端到端测试 是(全部服务) 全部 无 分钟级 全链路连通性 ⚠️ 新手提示:这张表建议存下来当速查卡。每次写完代码准备写测试时,先对着表想清楚"这一层该启动什么、该Mock什么",比盲目写省一半时间。 ...

一月 26, 2023 · 9 分钟 · 1870 字 · yaomingye

时序数据库在物联网日志系统中的设计与实践

时序数据库在物联网日志系统中的设计与实践:从数据结构到SpringBoot接入 一、一张表撑不住了 📌 前置知识:了解关系型数据库(RDBMS)基本概念,知道什么是SQL、索引、事务。知道什么是磁盘IO,理解顺序IO和随机IO的大致性能差距(约一个数量级)。 某开发者接手了一个物联网项目,设备每秒上报一次数据,1000台设备跑了一周,MySQL 的 iot_logs 表已经几千万行,查询一条设备最近一小时的数据要跑十几秒。加了索引,写入又慢得让人抓狂。这种场景,写过的都懂——关系型数据库不是不能存时序数据,而是它的每一个设计决策都和时序场景的需求背道而驰。 物联网日志(IoT Logs)具有几个典型特征: 写入密集且顺序:数据按时间顺序持续产生,极少更新或删除 查询模式固定:通常按时间范围 + 设备ID聚合查询,很少跨设备做复杂JOIN 数据冷热分明:最近几小时的数据被频繁访问,一周前的数据偶尔查一次 压缩空间巨大:传感器数据变化缓慢,相邻时间点数据高度相似,而 RDBMS 的通用压缩算法根本不认识这种模式 这就引出了一个核心问题:时序数据库(TSDB,Time Series Database)到底在数据结构层面做了哪些改造,让它和经典关系型数据库(RDBMS)产生了本质差异?以及如何在 Spring Boot 项目中把这些 TSDB 用起来? 二、数据结构层面的根本差异 2.1 行式存储 vs 列式存储 RDBMS 以 行(Row) 为单位组织数据,一行数据的各个字段在磁盘上连续存放。这种设计让单行读写非常高效——适合 OLTP 场景下"查一行、改一行"的套路。但面对时序查询时,问题就暴露了:查询"过去一小时内所有传感器的温度平均值",只需要温度这一个列,行式存储却会把湿度、气压、设备状态等几十个列一并从磁盘读进内存——IO 利用率奇低。 TSDB 采用 列式存储(Columnar Storage),将同一列的数据在磁盘上连续存放。查询温度列时,只读取温度相关的数据块,其他列完全不参与 IO。这和 ClickHouse 等 OLAP 引擎的思路一致,但 TSDB 在列式基础上又叠加了时间维度的特殊优化。 flowchart TD subgraph row["📦 行式存储 RDBMS"] direction TB r1["Row1│ts:1000│temp:25.3│hum:68│loc:WH1"] --> r2["Row2│ts:1001│temp:25.4│hum:67│loc:WH1"] r2 --> r3["Row3│ts:1002│temp:25.3│hum:68│loc:WH1"] end subgraph col["📦 列式存储 TSDB"] direction TB c_ts["Col_ts: 1000, 1001, 1002"] --> c_temp["Col_temp: 25.3, 25.4, 25.3"] c_temp --> c_hum["Col_hum: 68, 67, 68"] c_hum --> c_loc["Col_loc: WH1, WH1, WH1"] end row -.-> col classDef default fill:#1e1e24,stroke:#6b7280,stroke-width:1.5px,color:#e5e7eb; classDef highlight fill:#450a0a,stroke:#dc2626,stroke-width:1.5px,color:#fecaca,font-weight:bold; class r1,r2,r3,c_ts,c_temp,c_hum,c_loc default class r1,c_temp highlight ⚠️ 新手提示:不要混淆"列式存储的数据库"和"时序数据库"。列式存储是 TSDB 的技术手段之一,但不是全部。ClickHouse 是列式存储的 OLAP 数据库,但它不是 TSDB——它没有时间维度的特殊处理(如自动分区、自动过期删除)。 ...

一月 16, 2023 · 7 分钟 · 1406 字 · yaomingye

七牛云 OSS + SMTP 邮件:两个轻量级第三方接入实战

七牛云 OSS + SMTP 邮件接入 第1步:目标说明 — 图片上传和发邮件,每个项目的标配 电商项目两个常见的外部依赖:图片存储和邮件通知。商品图、用户头像得有个地方存,登录异常告警、注册欢迎得有个通道发。 Mall 项目用七牛云 OSS 存图片和文件(CDN 加速),用 SMTP 发邮件(Freemarker 模板渲染 HTML 正文),两个接入都不复杂,加起来不超过 200 行代码。本教程从申请凭证到代码封装,两套接入一次讲完。 第2步:前置条件 条件 七牛云 OSS SMTP 邮件 账号 qiniu.com 注册,实名认证 任意邮箱服务(163、QQ邮箱、企业邮箱) 凭证 AccessKey / SecretKey 邮箱地址 + SMTP 授权码 存储空间 在七牛云控制台创建 Bucket 无需 域名 Bucket 绑定 CDN 加速域名 无需 ⚠️ 新手提示:七牛云 OSS 和阿里云 OSS 是竞品,功能几乎一样。Mall 项目选了七牛云,如果公司已经在用阿里云 OSS,代码结构完全能用,换 SDK 即可。接入模式是通用的。 第3步:环境搭建 — 七牛云 OSS 添加 Maven 依赖 <dependency> <groupId>com.qiniu</groupId> <artifactId>qiniu-java-sdk</artifactId> <version>7.4.0</version> </dependency> 配置属性类 @Component @ConfigurationProperties(prefix = "oss.qiniu") @Data public class QiNiuConfig { private String accessKey; // 七牛云 AK private String secretKey; // 七牛云 SK private String bucketPictureName; // 图片 Bucket 名称 private String domainPicture; // 图片 CDN 域名 private String bucketFileName; // 文件 Bucket 名称 private String domainFile; // 文件 CDN 域名 } 图片和文件分两个 Bucket——图片需要 CDN 加速 + 图片处理(裁剪、加水印),文件的访问频率低但需要支持大文件下载,分开管理方便设置不同的生命周期策略。 ...

一月 15, 2023 · 4 分钟 · 846 字 · yaomingye

支付宝支付接入:沙箱调试 + EasySDK + QR 码支付实战

支付宝支付接入 第1步:目标说明 — 支付接入最怕的是什么 不是代码复杂,而是"没法在本地调"——每次测试都要真的扫码付钱,退款、对账、异常场景根本模拟不了。 支付宝提供了沙箱环境(sandbox),完全模拟生产接口的行为,但用的是虚拟账户和虚拟资金。开发人员在沙箱里可以反复测试支付、退款、异常场景,不花一分钱。 Mall 项目对接的是支付宝"当面付"(FaceToFace),生成二维码让用户扫码支付。本教程覆盖:沙箱环境申请 → RSA2 密钥配置 → EasySDK 集成 → QR 码生成 → MockPay 开发模式 → 生产切换。 第2步:前置条件 条件 要求 获取方式 支付宝开放平台账号 已注册并实名 open.alipay.com 沙箱环境 已开通(免费) 开放平台 → 控制台 → 沙箱环境 沙箱应用 自动创建 沙箱环境会自动生成一个测试应用 RSA2 密钥对 2048 位 支付宝密钥生成工具 或 openssl genrsa ⚠️ 新手提示:沙箱环境和正式环境是两套完全独立的系统——沙箱的 APPID、网关地址、密钥、支付宝公钥都和正式环境不同。Sandbox 网关是 openapi-sandbox.dl.alipaydev.com,生产网关是 openapi.alipay.com。切换环境不是改一两个配置项,而是整套凭证都得换。 第3步:环境搭建 添加 Maven 依赖 <dependency> <groupId>com.alipay.sdk</groupId> <artifactId>alipay-easysdk</artifactId> <version>2.2.0</version> </dependency> EasySDK 是支付宝官方封装的"开箱即用"SDK。老版 SDK alipay-sdk-java 需要手动构造请求参数、手动验签,代码量是 EasySDK 的 3 ~ 5 倍。EasySDK 一个 Factory.Payment.FaceToFace().preCreate() 就完成预下单。 ...

一月 14, 2023 · 4 分钟 · 655 字 · yaomingye

阿里云短信接入:双 Provider + Mock 验证码实战

阿里云短信接入 第1步:目标说明 — 别在生产环境调试短信 发送短信验证码是登录/注册流程的核心环节。但对接阿里云短信服务时有两个现实问题: 2024 年后个人资质基本申请不到官方短信签名和模板,审核周期长还不一定过 开发调试时不可能真发短信,每条几分钱不说,频繁发送会被运营商拦截 Mall 项目从这两个痛点出发,设计了一套"双 Provider + Mock 开关"的短信架构: 生产环境:用 dysmsapi(阿里云官方短信 SDK),需要企业资质 个人测试:用 dypnsapi(阿里云号码验证服务),个人账号可申请 本地开发:Mock 模式跳过真发,固定验证码 123456 目标是把这套架构讲清楚,读者照着做能在 30 分钟内完成短信接入。 第2步:前置条件 条件 要求 验证/获取方式 阿里云账号 已实名认证 aliyun.com 注册 AccessKey 已创建 RAM 用户,获取 AK/SK 阿里云控制台 → RAM 访问控制 → 创建 AccessKey 签名和模板(dysmsapi) 企业资质,审核通过 阿里云短信服务控制台(个人很难申请) 号码验证服务(dypnsapi) 个人账号可开通 阿里云号码验证服务控制台 ⚠️ 新手提示:dysmsapi 和 dypnsapi 是阿里云的两个不同产品。dysmsapi 是传统短信服务,需要申请签名和模板;dypnsapi 是号码验证服务,提供预置的短信模板(验证码、通知等),个人资质就能用。本教程两种都讲,读者根据自己的资质选一种即可。 第3步:环境搭建 添加 Maven 依赖 <!-- 方案1:官方短信 SDK --> <dependency> <groupId>com.aliyun</groupId> <artifactId>alibabacloud-dysmsapi20170525</artifactId> <version>3.0.0</version> </dependency> <!-- 方案2:号码验证服务 SDK(个人可用) --> <dependency> <groupId>com.aliyun</groupId> <artifactId>alibabacloud-dypnsapi20170525</artifactId> <version>1.0.8</version> </dependency> 两个依赖都加也没问题,项目通过 @ConditionalOnProperty 在运行时选一个生效,不会冲突。 ...

一月 13, 2023 · 5 分钟 · 878 字 · yaomingye
Cat Radio