From e29a718b2fa5562f059c463de07dee7c518ce83d Mon Sep 17 00:00:00 2001 From: zheng020 Date: Fri, 22 May 2026 18:43:29 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BF=AE=E6=94=B9=E4=B8=BA4/3=E6=AF=94?= =?UTF-8?q?=E4=BE=8B=E7=9A=84=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../specs/2026-05-22-sms-register-design.md | 621 +++++++++++++++++- .../pages/square/components/WaterfallGrid.vue | 134 ++-- 2 files changed, 666 insertions(+), 89 deletions(-) diff --git a/docs/superpowers/specs/2026-05-22-sms-register-design.md b/docs/superpowers/specs/2026-05-22-sms-register-design.md index 70bad43..7176c3f 100644 --- a/docs/superpowers/specs/2026-05-22-sms-register-design.md +++ b/docs/superpowers/specs/2026-05-22-sms-register-design.md @@ -126,14 +126,18 @@ register.vue setNickname.vue ``` **优点:** -- 阿里云 credentials 配置原本就在 gateway(OSS 用的是同一套),直接复用 +- 网关已有阿里云 AccessKey 配置,可直接用于 SMS(短信使用直接 AccessKey,非 STS) - 前端请求在网关层就能触发短信,不用穿透到后端服务 - 网关是单点,处理短信很方便 **缺点:** - 短信发送成功后的后续注册流程还在 userService,逻辑割裂 -- 网关承担了越来越多的职责(路由、认证、OSS上传、现在又加短信),越来越重 -- 如果 SMS 认证配置要从 gateway 复用,需要确保 userService 能访问到同一份配置,增加了部署耦合 +- 网关承担了越来越多的职责(路由、认证、OSS上传签名、现在又加短信),越来越重 +- 网关当前 OSS 相关接口: + - `GET /oss/signature` - 获取 OSS 上传签名 + - `GET /oss/presigned-url` - 获取 OSS 预签名URL(读取用) + - `GET /oss/batch-presigned-urls` - 批量获取 OSS 预签名URL +- SMS 和 OSS 认证方式不同(SMS 用直接 AccessKey,OSS 用 STS Role ARN),实际上并不能完全复用同一套配置 **适用场景**:适合短信作为独立操作、不属于用户注册主流程的场景。 @@ -155,9 +159,9 @@ register.vue setNickname.vue | 项目 | 选择 | |------|------| -| 短信 SDK | `github.com/alibabacloud-go/dysmsapi-20180501/v2`(阿里官方 V2 Go SDK) | +| 短信 SDK | `github.com/alibabacloud-go/dysmsapi-20180501/v2/client`(阿里官方 V2 Go SDK) | | 环境要求 | Go 1.10.x 或更高 | -| 安装方式 | `go get github.com/alibabacloud-go/dysmsapi-20180501/v2` | +| 安装方式 | `go get github.com/alibabacloud-go/dysmsapi-20180501/v2/client` | | 依赖包 | 还需 `github.com/alibabacloud-go/darabonba-openapi/v2/client` | | 认证方式 | 阿里云默认凭据链(AccessKey 等)自动查找 | | API Endpoint | `dysmsapi.aliyuncs.com` | @@ -216,13 +220,70 @@ Value (Hash): TTL: 60 秒 ``` -### 7.3 防暴力破解策略 +### 7.3 防暴力破解与限流策略 -- **失败计数**:每次验证失败递增 attempts 字段,≥ 3 次后删除 Key,要求用户重新获取 -- **发送频率限制**:每小时最多发送 10 次,超限返回 429 - - 使用独立 Key `sms:limit:register:{mobile}` 计数(String 类型,TTL=3600 秒) -- **IP 维度限流**(可选):每 IP 每小时最多请求 30 次 - - 使用独立 Key `sms:limit:ip:{ip}` 计数(String 类型,TTL=3600 秒) +#### 限制规则总览 + +| 限制维度 | 限制规则 | TTL | 触发动作 | +|---------|---------|-----|---------| +| 同一手机号(发送) | 60 秒内不能重复发送 | 60s | 返回 429 | +| 同一手机号(发送) | 每小时最多 10 次 | 3600s | 返回 429 | +| 同一手机号(验证) | 最多失败 3 次 | 60s | 删除验证码,要求重新获取 | +| 同一 IP(发送) | 每小时最多 30 次 | 3600s | 返回 429 | +| 同一 IP(验证) | 每分钟最多 10 次 | 60s | 返回 429 | +| 验证码有效期 | 60 秒 | 60s | 过期自动失效 | + +#### Redis Key 设计 + +``` +# 验证码存储(Hash) +sms:register:{mobile} + code: "123456" + created_at: "1700000000" + attempts: "0" +TTL: 60秒 + +# 手机号发送频率(String,每小时清一次) +sms:limit:mobile:register:{mobile} + value: "1" (计数器,每次发送 INCR) +TTL: 3600秒 + +# IP 发送频率(String,每小时清一次) +sms:limit:ip:send:{ip} + value: "1" +TTL: 3600秒 + +# IP 验证频率(String,每分钟清一次) +sms:limit:ip:verify:{ip} + value: "1" +TTL: 60秒 +``` + +#### 防御措施详情 + +**1. 发送频率限制** +- 60 秒内同一手机号只能发送 1 次(防止轰炸) +- 每小时同一手机号最多发送 10 次(结合套餐包成本控制) +- 每小时同一 IP 最多发送 30 次(防止 IP 轮询攻击) + +**2. 验证失败限制** +- 验证码错误后递增 attempts 计数器 +- 失败 3 次后强制删除验证码,要求用户重新获取 +- 60 秒内同一 IP 最多发起 10 次验证请求 + +**3. IP 黑名单机制** +- 触发限流时记录来源 IP +- 1 小时内触发 3 次限流的 IP,临时拉黑 30 分钟 +- 黑名单 Key: `sms:blacklist:ip:{ip}`,TTL=1800 秒 + +**4. 验证码安全** +- 验证码为 6 位纯数字,共 100 万种组合 +- 有效期 60 秒,暴力破解窗口极小 +- 验证成功后立即删除,防止重放 + +**5. 异常检测** +- 同一手机号在 5 分钟内连续失败 5 次,触发告警(可考虑临时冻结) +- 同一 IP 在 5 分钟内请求超过 50 次,触发告警 ### 7.4 验证后处理 @@ -243,10 +304,13 @@ POST /api/v1/auth/send-code Content-Type: application/json { - "mobile": "13800138000" + "mobile": "13800138000", + "scene": "register" } ``` +`scene` 可选值:`register`(注册)、`password`(找回密码)。 + **响应** ```json { @@ -267,7 +331,8 @@ Content-Type: application/json { "mobile": "13800138000", - "code": "123456" + "code": "123456", + "scene": "register" } ``` @@ -325,7 +390,9 @@ Content-Type: application/json | 验证码已使用 | 400 | 提示"验证码已使用,请重新获取" | | 发送频率超限(60秒内重复发送) | 429 | 提示"发送过于频繁,请稍后再试" | | 每小时发送次数超限(>10次) | 429 | 提示"当前手机号发送次数超限,请稍后再试" | -| IP 请求频率超限 | 429 | 提示"请求过于频繁,请稍后再试" | +| IP 发送频率超限(>30次/小时) | 429 | 提示"请求过于频繁,请稍后再试" | +| IP 验证频率超限(>10次/分钟) | 429 | 提示"请求过于频繁,请稍后再试" | +| IP 被临时拉黑 | 403 | 提示"暂时无法操作,请稍后再试" | --- @@ -353,6 +420,8 @@ SMS_REGION=cn-hangzhou 2. **verify_token 安全**:token 有效期 5 分钟,只能使用一次,验证后立即删除 3. **防暴力破解**:验证失败 3 次后强制删除验证码,要求用户重新获取 4. **限流保护**:从手机号和 IP 两个维度限制请求频率 +5. **IP 黑名单**:1 小时内触发 3 次限流的 IP,临时拉黑 30 分钟 +6. **异常告警**:同一手机号 5 分钟内连续失败 5 次,或同一 IP 5 分钟内请求超 50 次,触发告警 --- @@ -371,24 +440,202 @@ SMS_REGION=cn-hangzhou > 💡 **推荐**:5,000 条或 15,000 条套餐性价比最高,适合中等规模业务使用。 -### 12.2 短信模板类型 +### 12.2 短信模板规范 + +#### 模板类型 | 模板类型 | 说明 | 审核时间 | |----------|------|----------| -| 验证码模板 | 仅含变量(如 ${code}),用于发送随机验证码 | 通常 2 小时内 | +| **验证码模板** | 仅含变量(如 ${code}),用于发送随机验证码 | 通常 2 小时内 | | 通知模板 | 含固定文本 + 少量变量 | 通常 2 小时内 | | 推广模板 | 营销类内容,审核更严格 | 通常 4 小时以上 | -**验证码模板示例:** -``` -您的注册验证码是${code},5分钟内有效。如非本人操作,请忽略此短信。 -``` +#### 内容规范 -### 12.3 签名 +**必须包含:** +- 必须包含"**验证码、注册码、校验码、动态码**"之一 +- 必须体现"**使用平台、用途、失效时间**"之一 -- 签名是短信发送者的标识,位于短信内容开头 -- 一个账号可创建多个签名,需审核通过后才能使用 -- 签名名称示例:`TopFans`、`TOPFANS官方` +**禁止包含:** +- 不能含有通知、营销、广告词语、退订方式 +- 结尾不能包含"拒收请回复R" +- **不能含任何联系方式**:手机/固话/QQ/微信/抖音/钉钉/旺旺/闲鱼/小红书/邮箱等 +- 用途为"他用"的签名不支持申请金融相关模板 + +#### 格式规范 + +| 项目 | 规范 | +|------|------| +| **模板长度** | 1~500 个字符(含变量) | +| **支持字符** | 中文、英文、数字、符号 | +| **不支持** | 繁体字、特殊符号(如 `#`、`『』、『「」、「」、〖〗`、`m²`、`•`、`①`、`★`、`※`、`→` 等) | +| **禁止** | 错别字、变体字、异体字、各类干扰符号 | +| **模板内容中禁用** | `【】`符号(任意位置),首尾禁用`[ ]` | + +#### 变量规范 + +| 变量类型 | 变量名示例 | 变量规范 | +|---------|-----------|---------| +| **单变量(仅数字)** | `${code}`、`${time}` | 长度限制 4~6 位 | +| **单变量(数字+字母)** | `${code}` | 长度限制 4~6 位 | +| **双变量** | `${code}`、`${time}` | 仅支持通过控制台"常用模板推荐"申请,不支持自定义 | + +**变量格式要求:** +- 变量格式为 `${变量名}`,其中 `$` 符号在 `{ }` 外面 +- 变量名首字母必须为英文字母 +- 变量名只能由字母、数字和下划线组成 +- **变量名不能为**:email、mobile、id、nick、site、ip 等 + +#### 验证码模板示例 + +| 应用场景 | 模板内容 | +|---------|---------| +| 登录 | 您的验证码`${code}`,该验证码5分钟内有效,请勿泄露给他人! | +| 登录 | 验证码为:`${code}`,您正在登录,若非本人操作,请勿泄露。 | +| 注册 | 您正在申请手机注册,验证码为:`${code}`,5分钟内有效! | +| 注册 | 尊敬的用户,您的注册会员动态密码为:`${code}`,请勿泄露于他人! | +| 注册 | 您的注册码:`${code}`,如非本人操作,请忽略本短信! | +| 重置密码 | 您的动态码为:`${code}`,您正在进行密码重置操作,如非本人操作,请忽略本短信! | + +#### 审核时长 + +- 预计 **2 个小时** 内审核完成 +- 工作时间:周一至周日 9:00~21:00(法定节假日顺延) + +#### 常见审核失败原因 + +| 类别 | 驳回原因 | 处理建议 | +|------|---------|---------| +| 内容模糊 | 模板没有体现业务内容,含义不明 | 使用实际业务内容 | +| 场景不详 | 模板使用场景不详 | 在场景说明中填写业务场景或线上链接 | +| 含退订方式 | 验证码模板包含退订方式 | 删除退订方式后重新提交 | +| 多变量 | 验证码模板包含多变量 | 去掉多余变量,仅支持单变量或双变量 | +| 关键词不全 | 缺少"验证码/注册码/校验码/动态码"之一 | 添加验证码关键词之一 | +| 变量格式错误 | 变量格式不符合规范 | 变量格式为 `${code}`,首字母必须为英文字母 | +| 模板类型错误 | 模板类型选择与实际需求不匹配 | 根据场景选择验证码/通知/推广模板 | + +##### 模板审核详细失败原因 + +| 类别 | 驳回原因 | 处理建议 | +|------|---------|---------| +| 内容模糊 | 模板没有体现业务内容,含义不明 | 使用实际业务内容进行测试 | +| 内容模糊 | 模板使用场景不详 | 填写业务场景或线上链接,提供测试账号密码 | +| 内容模糊 | 应用市场中未核实到App/公众号/小程序 | 填写正确链接,若产品未上线暂不支持申请 | +| 内容模糊 | 业务内容不明确(如"加微信送礼") | 不支持加群内容,删除后重新提交 | +| 格式问题 | 推广短信模板包含变量 | 将变量内容体现在模板文案中 | +| 格式问题 | 推广短信没包含退订方式 | 加上 `拒收请回复R` | +| 格式问题 | 验证码/通知模板包含退订方式 | 删除退订方式后重新提交 | +| 格式问题 | 验证码模板包含多变量 | 去掉多余变量,仅支持单变量或双变量 | +| 格式问题 | 验证码模板包含无关内容 | 删除与验证码无关的内容 | +| 格式问题 | 验证码关键词不全 | 必须含验证码/注册码/校验码/动态码之一 | +| 格式问题 | 通知模板包含推广内容 | 删除推广部分或申请推广模板 | +| 格式问题 | 含错别字/繁体字/特殊符号/中括号 | 修改为正确内容,【】符号不能在模板内容中使用 | +| 内容问题 | 内容涉及金融/交友/宗教/游戏 | 交友/游戏仅支持验证码模板;他用签名不支持金融相关 | +| 内容问题 | 内容涉及第三方 | 需提供第三方企业证件和授权委托书 | +| 内容问题 | 内容涉及营销信息 | 提供隐私协议截图、会员管理系统截图,备注投诉对接人 | +| 变量问题 | 变量内传入链接/IP地址/App等 | 变量外使用链接,固定链接用一级域名+变量格式 | +| 变量问题 | 变量内容模糊 | 修改表达以清晰判断参数内容 | +| 变量问题 | 变量格式错误 | 变量格式`${code}`,首字母必须为英文字母,不能为email/mobile/id/nick/site/ip | +| 变量问题 | 变量属性选择错误 | 根据业务场景选择合适的变量属性 | +| 链接问题 | 链接无ICP备案 | 提供已ICP备案的网址链接 | +| 链接问题 | 短链+变量格式不符合规范 | 改为一级的域名或官网链接+变量组合 | +| 链接问题 | 链接存在跳转 | 将原链接压缩成短链 | +| 链接问题 | 链接无法访问 | 提供公网可访问的链接 | +| SEO推广 | 涉及数据排名/关键字搜索/精准拓客引流 | 修改模板内容 | + +### 12.3 签名规范 + +#### 什么是短信签名 + +短信签名是短信发送方属性的一种标识,一条完整的短信由**短信签名**和**短信内容**组成。短信签名位于短信内容前的`【】`中,用于标识企业、产品或业务。 + +**示例**:`【阿里云】您的验证码是123456` - 签名`阿里云`会自动补全`【】` + +#### 签名来源要求 + +| 签名来源 | 说明 | +|---------|------| +| **企事业单位名**(推荐) | 企业名称的全称或简称,极大提升签名报备成功率 | +| 已注册商标名 | 需在国家知识产权局商标局可查且注册主体一致 | + +> ⚠️ **不支持**:公众号、小程序、电商平台店铺名、已上线APP、测试/学习用途作为签名来源 + +#### 签名内容规范 + +| 项目 | 规范 | +|------|------| +| **签名内容** | 需能明确辨别发送方公司信息、产品或业务 | +| **支持** | 企事业单位名、已注册商标名 | +| **不支持** | 含义模糊的中性签名(如"客服通知"、"客户您好"、"温馨提示") | +| **不支持** | 个人姓名 | +| **不支持** | 含"测试"、"test"等字样的签名 | +| **不支持** | 全英文签名、全数字签名、英文+数字组合 | +| **不支持** | 特殊符号(含空格)、繁体字 | +| **格式** | 申请时直接填写签名名称,无需添加【】等符号,系统会自动添加 | + +#### 签名长度限制 + +- 长度:**2~12个字符** +- 中文、英文、数字按1个字符计算 + +#### 签名用途 + +| 用途 | 说明 | +|------|------| +| **自用** | 资质对应的企业/个人信息与阿里云账号信息一致 | +| **他用** | 需上传委托授权书,第三方仅支持企业及事业单位,不得为个人 | + +> ⚠️ **个人认证用户**:自用签名**无法通过**签名实名制报备,请申请他用资质或升级为企业认证账号 + +#### 签名可申请次数 + +| 账户类型 | 可申请次数 | +|---------|-----------| +| 个人认证用户 | 同阿里云账号一个自然日内支持申请 **1个** 签名 | +| 企业认证用户 | **无数量限制** | + +#### 签名审核时长 + +- 预计 **2个小时内** 审核完成 +- 工作时间:周一至周日 9:00~21:00(法定节假日顺延) +- 运营商实名报备:**5-10个工作日**(可能更长) + +#### 签名状态异常原因 + +| 状态 | 异常原因 | +|------|---------| +| 可用-异常 | 签名来源不合规、未关联资质、资质信息不全、实名制报备异常、长期未使用等 | +| 不可用 | 审批未通过、被禁用、被冻结 | + +##### 签名审核详细失败原因 + +| 类别 | 驳回原因 | 处理建议 | +|------|---------|---------| +| 内容模糊 | App/业务内容较少,无法核实业务场景 | 完善线上业务信息后再提交,不支持未上线产品 | +| 内容模糊 | 签名过于宽泛,如"客服通知"、"客户您好" | 申请与已上线应用名称或企事业单位名称作为签名 | +| 内容模糊 | 已提供信息,因无关联性被驳回 | 提供关联后台认证截图,备注关联性 | +| 内容模糊 | 未核实到商标信息 | 在场景说明中提供商标注册号及完整商标名 | +| 链接问题 | 链接无法访问 | 提供正确或公网可访问的链接 | +| 链接问题 | 链接与签名无关联 | 申请企业名称作为签名 | +| 链接问题 | 下载/内测链接无法核实业务主体 | 提供应用商城链接以便核实 | +| 链接问题 | IP地址无法核实企业所属性 | 提供App或其他链接以便核实 | +| 授权书 | 授权书未签字 | 在授权书右下角落款处让法定代表人或负责人签字 | +| 授权书 | 授权书盖章错误 | 盖授权方(签名归属方)的企事业单位公章或合同章 | +| 授权书 | 授权书无日期/有效期短/过期 | 填写完整有效期限,建议1~3年 | +| 授权书 | 授权书内容变更 | 委托授权书经法务评估后出具,不支持变更内容 | +| 授权书 | 被授权方/授权方/风险承担方填写错误 | 被授权方为账号认证主体名称,授权方和风险承担方为短信内容所属方 | +| 资质材料 | 涉及政企业务但材料未提供完全 | 需提供授权委托书和组织机构代码证,场景说明备注固话 | +| 资质材料 | 营业执照水印非平台使用 | 去除水印或改为"仅供云通信备案使用" | +| 资质材料 | 证件无法查看 | 支持JPG、PNG、GIF、JPEG格式,每张不大于2MB | +| 格式问题 | 签名名称包含"测试"字样 | 删除测试字样后重新提交 | +| 格式问题 | 签名字数不符合要求 | 限制在2~12个字符内 | +| 格式问题 | 签名带符号/繁体字/首字母拼写 | 国内短信不支持全英文、全数字、繁体字、特殊符号签名 | +| 格式问题 | 签名是个人姓名 | 签名不能为个人姓名 | +| 格式问题 | 签名是小程序或公众号 | 不支持申请 | + +#### 签名申请受限内容 + +禁止包含:违法违规、色情、赌博、毒品、党政、博彩、彩票、暴力、恐吓、走私、成人用品、虚拟货币、烟草酒类、代开发票、代办证件、刷单、贷款催款、法律维权、股票私募、整容医美、宗教迷信、投资理财、房地产推广、游戏推广、交友推广、金融推广(含银行/保险/借贷)、招商加盟等内容的短信。 ### 12.4 开通流程 @@ -412,7 +659,7 @@ SMS_REGION=cn-hangzhou #### 依赖安装 ```bash -go get github.com/alibabacloud-go/dysmsapi-20170525/v4/client +go get github.com/alibabacloud-go/dysmsapi-20180501/v2/client go get github.com/alibabacloud-go/darabonba-openapi/v2/client go get github.com/alibabacloud-go/tea/tea go get github.com/alibabacloud-go/tea-utils/v2/service @@ -446,7 +693,7 @@ setx ALIBABA_CLOUD_ACCESS_KEY_SECRET yourAccessKeySecret /M import ( "os" openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client" - dysmsapi20170525 "github.com/alibabacloud-go/dysmsapi-20170525/v4/client" + dysmsapi20170525 "github.com/alibabacloud-go/dysmsapi-20180501/v2/client" "github.com/alibabacloud-go/tea/tea" ) @@ -465,30 +712,32 @@ func CreateClient() (_result *dysmsapi20170525.Client, _err error) { #### 发送短信(注册验证码场景) +使用 `SendMessageWithTemplate` API 发送中国内地短信: + ```go import ( - dysmsapi20170525 "github.com/alibabacloud-go/dysmsapi-20170525/v4/client" + dysmsapi20170525 "github.com/alibabacloud-go/dysmsapi-20180501/v2/client" util "github.com/alibabacloud-go/tea-utils/v2/service" "github.com/alibabacloud-go/tea/tea" ) -func SendVerificationCode() (_result *dysmsapi20170525.SendSmsResponse, _err error) { +func SendVerificationCode() (_result *dysmsapi20170525.SendMessageWithTemplateResponse, _err error) { client, _err := CreateClient() if _err != nil { return nil, _err } // 构造请求 - sendSmsRequest := &dysmsapi20170525.SendSmsRequest{ - PhoneNumbers: tea.String("13800138000"), // 手机号 - SignName: tea.String("TopFans"), // 签名 - TemplateCode: tea.String("SMS_xxxxxxx"), // 模板CODE - TemplateParam: tea.String(`{"code":"123456"}`), // 模板变量 + request := &dysmsapi20170525.SendMessageWithTemplateRequest{ + ToNumber: tea.String("861503871XXXXX"), // 接收手机号,格式:国际区号+号码 + FromNumber: tea.String("TopFans"), // 发送方标识(中国内地填签名) + TemplateCode: tea.String("SMS_xxxxxxx"), // 短信模板CODE + TemplateParam: tea.String(`{"code":"123456"}`), // 模板变量(JSON格式) } // 发送 runtime := &util.RuntimeOptions{} - response, _err := client.SendSmsWithOptions(sendSmsRequest, runtime) + response, _err := client.SendMessageWithTemplateWithOptions(request, runtime) if _err != nil { return nil, _err } @@ -496,6 +745,33 @@ func SendVerificationCode() (_result *dysmsapi20170525.SendSmsResponse, _err err } ``` +**请求参数说明** + +| 参数 | 类型 | 必填 | 说明 | +|------|------|------|------| +| ToNumber | string | 是 | 接收手机号,格式:`86` + 国内手机号,如 `861503871XXXXX` | +| FromNumber | string | 是 | 发送方标识,中国内地填短信签名 | +| TemplateCode | string | 是 | 短信模板 CODE | +| TemplateParam | string | 否 | 模板变量 JSON,如 `{"code":"123456"}` | +| SmsUpExtendCode | string | 否 | 上行短信扩展码 | + +**返回示例** + +```json +{ + "MessageId": "10080303003****", + "NumberDetail": { + "Carrier": "China Mobile", + "Country": "China", + "Region": "Nanjing, Jiangsu" + }, + "ResponseCode": "OK", + "ResponseDescription": "The SMS Send Request was accepted", + "Segments": "1", + "To": "861503871XXXXX" +} +``` + #### 返回码说明 | Code | 说明 | @@ -537,13 +813,285 @@ fmt.Println(tea.StringValue(response.Body.Code)) | "Specified access key is not found" | AccessKey ID 错误或已删除 | 确认环境变量是否正确设置 | | "dial tcp: lookup xxx: no such host" | Endpoint 配置错误 | 确认 Endpoint 为 `dysmsapi.aliyuncs.com` | -### 12.6 相关文档 +### 12.6 开通步骤与资质要求 + +#### 重要提示 + +> ⚠️ **个人账号限制**:在当前的短信签名实名制要求下,个人账号的自用资质无法通过签名实名制报备。个人用户请使用短信认证产品或**升级为企业认证账号**。 + +#### 开通步骤 + +| 步骤 | 操作 | 说明 | +|------|------|------| +| ① 准备工作 | 注册阿里云账号 + 完成实名认证 + 开通短信服务 + 创建 AccessKey | API 调用必需 | +| ② 申请资质 | 提交资质(企业/个人证明)→ 等待审核(预计 2 个工作日) | 国内短信必需 | +| ③ 申请签名 | 提交签名 → 等待审核(预计 2 小时)→ 等待运营商报备(7-10 工作日) | 签名实名制报备 | +| ④ 申请模板 | 提交模板 → 等待审核(预计 2 小时) | 验证码模板 | +| ⑤ 发送短信 | 使用已审核通过的签名和模板发送 | 建议先少量测试 | +| ⑥ 查询详情 | 查询发送状态、获取回执 | 可选 | +| ⑦ 设置预警 | 配置联系人、验证码防盗刷、套餐包余量预警等 | 建议配置 | + +#### 资质要求 + +##### 基本概念 + +| 类型 | 说明 | +|------|------| +| **个人认证** | 阿里云账号认证类型为个人。**个人认证自用资质无法通过签名实名制报备**,请申请"他用资质"或升级为企业认证账号 | +| **企业认证** | 阿里云账号认证类型为企业 | +| **自用资质** | 资质企业/个人信息与阿里云账号已认证信息完全一致 | +| **他用资质** | 资质企业/个人信息与阿里云账号已认证信息不一致,需提供委托授权书 | + +##### 资质材料清单 + +| 材料类型 | 具体材料 | 说明 | 适用对象 | +|---------|---------|------|---------| +| **企业信息** | 加载统一社会信用代码的证照 | 社会信用代码证书、营业执照、事业单位法人证书等(选择一种) | 企业认证(自用/他用)、个人认证(他用) | +| **法定代表人信息** | 姓名 + 身份证件 | 若证件中无法定代表人信息,需提供负责人或首席代表的身份证件 | 企业认证(自用/他用)、个人认证(他用) | +| **管理员信息** | 管理员姓名 + 身份证件 + 手机号 | 管理员是管理短信业务的运营人员,可与企业法定代表人为同一人。**一人一企**:同一管理员只能关联一个企业资质,否则报备失败 | 企业认证(自用/他用)、个人认证(他用) | +| **个人信息** | 个人身份证明 | 个人认证自用资质**无法通过**签名实名制报备 | 个人认证(自用) | + +##### 证件要求 + +- 彩色原件无需盖章 +- 复印件/黑白照片需加盖**企业红章**并拍照上传 +- 证件标记建议修改为"仅供短信业务使用"或"仅供运营商报备使用" + +##### 资质审核时长 + +- 预计 **2个工作日** 内完成 +- 工作时间:周一至周日 9:00~21:00(法定节假日顺延) + +##### 常见审核失败原因 + +| 类别 | 问题 | 建议 | +|------|------|------| +| 企业信息 | 企业经营状态异常 | 向市场监管部门移除经营异常名录后再提交 | +| 企业信息 | 证件标记非平台使用 | 修改为"仅供短信业务使用" | +| 法定代表人 | 非中国国籍 | 护照、港澳居民来往内地通行证视为有效证件 | +| 管理员 | 非中国国籍或港澳居民 | **仅支持身份证**,否则无法报备成功 | + +##### 资质审核详细失败原因 + +| 类别 | 问题 | 原因/处理建议 | +|------|------|---------------| +| 企业信息 | 企业经营状态异常 | 企业已被列入经营异常名单。建议向市场监管部门移除经营异常名录 | +| 企业信息 | 证件标记非平台使用 | 修改为"仅供短信业务使用"或"仅供运营商报备使用" | +| 法定代表人 | 非中国国籍人士或港澳居民 | 护照、港澳居民来往内地通行证视为有效证件 | +| 法定代表人 | 工商个体户没有公章 | 可提供法定代表人签名 | +| 管理员 | 非中国国籍或港澳居民(无身份证) | **仅支持身份证**,否则无法报备成功 | + +##### 资质复用 + +- **跨产品复用**:企业账号同时使用语音服务、号码隐私保护等产品时,可复用已审核通过的资质 +- **短信服务内复用**:可重复使用同一账号下已审核通过的资质 + +#### 签名要求 + +- 签名需要能明确辨别发送方 +- 建议使用企事业单位名称作为签名 +- 个人账号自用资质无法通过签名实名制报备 +- **未报备的签名会被运营商拦截发送**,返回 `PORT_NOT_REGISTERED` 错误 + +#### 运营商报备时长 + +- 平均 **5-7 个工作日** +- 部分运营商可能需要 **7-10 个工作日** +- 运营商未承诺此时效,实际可能更长 +- **建议**:提前申请资质和签名,预留足够时间完成实名报备后再正式使用 + +#### 审核时间 + +| 审核项 | 审核时间 | 工作时间 | +|--------|---------|---------| +| 资质审核 | 预计 2 个工作日 | 周一至周日 9:00~21:00,法定节假日顺延 | +| 签名审核 | 预计 2 小时内 | 周一至周日 9:00~21:00 | +| 模板审核 | 预计 2 小时内 | 周一至周日 9:00~21:00 | + +#### 建议配置项 + +| 配置项 | 说明 | +|--------|------| +| 联系人设置 | 设置预警联系人,接收通知 | +| 验证码防盗刷预警 | 建议开启,防止验证码被大量消耗 | +| 套餐包余量预警 | 余额不足时通知 | +| 发送频率预警 | 异常发送时通知 | + +--- + +### 12.7 相关文档 - [阿里云短信服务帮助文档](https://help.aliyun.com/zh/sms) - [快速入门(Go SDK)](https://help.aliyun.com/zh/sms/getting-started/get-started-with-sms) - [SDK 示例](https://help.aliyun.com/zh/sms/sdk-demo/go) - [SendSms API 文档](https://help.aliyun.com/zh/sms/developer-reference/sendsms) - [Go SDK 源码仓库](https://github.com/alibabacloud-go/dysmsapi-20180501/) +- [资质申请指南](https://help.aliyun.com/zh/sms/user-guide/apply-qualification) + +--- + +### 12.8 短信使用记录表 + +为方便后续资源核算,需记录每次短信发送情况。 + +#### 方案一:PostgreSQL 表记录 + +```sql +CREATE TABLE sms_send_log ( + id BIGSERIAL PRIMARY KEY, + mobile VARCHAR(20) NOT NULL COMMENT '手机号(脱敏存储)', + scene VARCHAR(20) NOT NULL DEFAULT 'register' COMMENT '使用场景:register/password', + template_code VARCHAR(50) NOT NULL COMMENT '短信模板CODE', + sign_name VARCHAR(50) NOT NULL COMMENT '短信签名', + message_id VARCHAR(64) DEFAULT '' COMMENT '阿里云返回的MessageId', + response_code VARCHAR(20) DEFAULT '' COMMENT '阿里云返回状态码', + response_description VARCHAR(255) DEFAULT '' COMMENT '阿里云返回描述', + status SMALLINT NOT NULL DEFAULT 1 COMMENT '发送状态:0=失败,1=成功', + send_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '发送时间', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX idx_sms_send_log_mobile ON sms_send_log(mobile); +CREATE INDEX idx_sms_send_log_scene ON sms_send_log(scene); +CREATE INDEX idx_sms_send_log_send_time ON sms_send_log(send_time); +``` + +#### 方案二:Redis 记录(轻量级) + +使用 Redis 哈希记录发送统计,按月汇总: + +``` +Key: sms:stat:{year}:{month} +Type: Hash +Fields: + - total_count: 总发送条数 + - success_count: 成功条数 + - fail_count: 失败条数 + - register_count: 注册场景条数 + - password_count: 密码找回场景条数(未来扩展) +``` + +**推荐方案一(PostgreSQL)**,便于查询和导出做成本分析。 + +#### 资源核算查询示例 + +```sql +-- 按月统计各场景短信发送量 +SELECT + TO_CHAR(send_time, 'YYYY-MM') AS month, + scene, + COUNT(*) AS total_count, + SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) AS success_count, + SUM(CASE WHEN status = 0 THEN 1 ELSE 0 END) AS fail_count +FROM sms_send_log +GROUP BY TO_CHAR(send_time, 'YYYY-MM'), scene +ORDER BY month DESC; + +-- 统计剩余套餐条数(结合阿里云控制台数据) +-- 当前已消耗 = 注册验证成功 + 失败重试 等 +``` + +--- + +### 12.9 忘记密码/找回密码 + +#### 功能概述 + +用户可通过手机号验证码方式重置密码,与注册流程类似但更简洁: + +1. 用户输入手机号 → 发送验证码 +2. 输入验证码 → 验证通过 +3. 输入新密码 → 完成修改 + +#### 前端页面变化 + +新建 `frontend/pages/password/forget.vue`(找回密码页面): + +``` +forget.vue + │ + ├── 输入手机号 + ├── 点击"发送验证码" ──→ 发送验证码 + ├── 输入验证码 + ├── 点击"验证验证码" ──→ 验证通过 + │ 返回 verify_token + ├── 输入新密码 + └── 点击"确认修改" ──→ 修改密码 API + (mobile, verify_token, new_password) +``` + +#### 后端 API 变化 + +| 接口 | 方法 | 说明 | +|------|------|------| +| `/api/v1/auth/send-code` | POST | 复用注册逻辑,scene=password | +| `/api/v1/auth/verify-code` | POST | 复用注册逻辑,scene=password | +| `/api/v1/auth/reset-password` | POST | 新接口,重置密码 | + +**重置密码接口:** + +``` +POST /api/v1/auth/reset-password +Content-Type: application/json + +{ + "mobile": "13800138000", + "verify_token": "vtf_abc123xyz789...", + "new_password": "xxx" +} +``` + +**响应:** +```json +{ + "code": 200, + "message": "密码修改成功", + "data": null +} +``` + +**后端逻辑:** +1. 根据 mobile 从 Redis 获取 `verify:password:{mobile}` 的值 +2. 与请求中的 verify_token 比对,不一致则拒绝 +3. 验证通过后删除该记录 +4. 使用新密码更新用户数据(需加密存储) +5. 建议要求用户重新登录 + +#### Redis Key 复用 + +| 场景 | Key 格式 | 说明 | +|------|----------|------| +| 找回密码验证码 | `sms:password:{mobile}` | 验证码存储(TTL 60秒) | +| 找回密码验证通过 | `verify:password:{mobile}` | 验证 token 存储(TTL 300秒) | + +限流规则与注册场景完全一致,共享 `sms:limit:*` 规则。 + +#### 错误码 + +| 场景 | 错误码 | 提示 | +|------|--------|------| +| 密码修改成功 | 200 | "密码修改成功" | +| verify_token 无效/已过期 | 400 | "验证码已失效,请重新获取" | +| 验证码错误 | 400 | "验证码错误" | +| 同一手机号找回密码频率超限 | 429 | "操作过于频繁,请稍后再试" | + +#### 前端登录页入口 + +在 `frontend/pages/login/login.vue` 添加"忘记密码"入口: + +```vue + + 忘记密码 + +``` + +跳转到找回密码页面: +```js +uni.navigateTo({ + url: '/pages/password/forget' +}); +``` --- @@ -552,4 +1100,5 @@ fmt.Println(tea.StringValue(response.Body.Code)) - [ ] 阿里云短信签名和模板CODE(需在阿里云控制台创建) - [ ] 验证码有效期(默认 60 秒是否合适) - [ ] 每小时同一手机号发送次数上限(默认 10 次) -- [ ] 验证失败次数上限(默认 3 次后强制重新获取) \ No newline at end of file +- [ ] 验证失败次数上限(默认 3 次后强制重新获取) +- [ ] 找回密码页面是否需要单独创建,还是复用现有页面 \ No newline at end of file diff --git a/frontend/pages/square/components/WaterfallGrid.vue b/frontend/pages/square/components/WaterfallGrid.vue index c5aee91..8340e6f 100644 --- a/frontend/pages/square/components/WaterfallGrid.vue +++ b/frontend/pages/square/components/WaterfallGrid.vue @@ -1,52 +1,24 @@