HarmonyOS开发:短信服务SMS服务

举报
Jack20 发表于 2026/06/25 20:59:49 2026/06/25
【摘要】 HarmonyOS开发:短信服务SMS服务📌 核心要点:SMS Kit不只是"发个验证码",验证码短信发送、模板管理、送达率优化、防刷策略,一套组合拳确保验证码既安全又稳定。 背景与动机你的App需要手机号验证。发个验证码嘛,多简单的事——调用运营商的短信API,6位数字发出去,用户输入,验证通过。然后呢?验证码被拦截了怎么办?短信发不出去怎么办?用户收不到怎么办?一分钟发了50次验证码...

HarmonyOS开发:短信服务SMS服务

📌 核心要点:SMS Kit不只是"发个验证码",验证码短信发送、模板管理、送达率优化、防刷策略,一套组合拳确保验证码既安全又稳定。

背景与动机

你的App需要手机号验证。发个验证码嘛,多简单的事——调用运营商的短信API,6位数字发出去,用户输入,验证通过。

然后呢?验证码被拦截了怎么办?短信发不出去怎么办?用户收不到怎么办?一分钟发了50次验证码被刷了怎么办?验证码过期了用户还在输入怎么办?

"发个验证码"这件事,背后是一整套短信服务体系。你自己对接运营商API,得处理签名、模板审核、发送频率限制、送达状态回调、费用结算……一套下来,验证码还没上线,短信服务的代码比业务代码还多。

SMS Kit就是来解决这些问题的。你不用对接运营商,不用管模板审核,不用自己搞防刷,SDK一集成,验证码发送和校验全有了。

但SMS Kit不是"调个API发短信"这么简单。送达率优化、防刷策略、模板管理——这些你搞不清楚,验证码发不出去或者被刷爆,用户体验直接崩。

核心原理

SMS Kit的架构:客户端请求验证码 → SMS服务生成验证码 → 通过运营商发送短信 → 用户输入验证码 → 客户端校验。

flowchart TD
    A[用户输入手机号] --> B[客户端请求验证码]
    B --> C[SMS服务端]
    
    C --> D{校验请求}
    D -->|频率超限| E[返回频率限制错误]
    D -->|手机号异常| F[返回号码无效错误]
    D -->|校验通过| G[生成6位验证码]
    
    G --> H[存储验证码到缓存]
    H --> I[调用运营商短信API]
    
    I --> J{发送结果}
    J -->|成功| K[返回发送成功 + VerifyId]
    J -->|失败| L[返回发送失败]
    
    K --> M[运营商下发短信]
    M --> N[用户手机收到短信]
    N --> O[用户输入验证码]
    
    O --> P[客户端提交验证码 + VerifyId]
    P --> Q[SMS服务端校验]
    
    Q --> R{校验结果}
    R -->|验证码正确| S[验证通过]
    R -->|验证码错误| T[返回错误,剩余次数-1]
    R -->|验证码过期| U[返回过期,需重新发送]
    R -->|超过最大尝试次数| V[验证码失效,需重新发送]
    
    classDef client fill:#1565C0,color:#fff,stroke:#0D47A1
    classDef server fill:#E65100,color:#fff,stroke:#BF360C
    classDef carrier fill:#2E7D32,color:#fff,stroke:#1B5E20
    classDef result fill:#6A1B9A,color:#fff,stroke:#4A148C
    classDef error fill:#C62828,color:#fff,stroke:#B71C1C

    class A,B,O,P,client
    class C,D,G,H,Q,server
    class I,J,K,L,M,N,carrier
    class S,result
    class E,F,T,U,V,error

验证码生命周期

一个验证码从生成到失效,经历以下阶段:

  1. 生成:6位数字,有效期通常5分钟
  2. 发送:通过运营商下发到用户手机
  3. 等待输入:用户在有效期内输入
  4. 校验:验证码正确则通过,错误则扣减尝试次数
  5. 失效:超过有效期或尝试次数上限,验证码失效

短信模板

SMS Kit使用模板发送短信,不能随意编辑短信内容。模板需要提前在AGC控制台审核通过才能使用。

模板示例:

【我的应用】您的验证码为${code},${expire}分钟内有效,请勿泄露给他人。

模板中的${code}${expire}是变量,发送时替换为实际值。

送达率优化

短信送达率受很多因素影响:手机号是否真实、运营商是否拦截、用户是否在信号覆盖区。SMS Kit提供送达状态回调,你可以根据回执统计送达率,优化发送策略。

代码实战

基础用法:发送验证码短信

最核心的操作:用户输入手机号,发送验证码,然后校验。

// SmsVerifyService.ets
import { smsService } from '@kit.SmsKit';
import { authentication } from '@kit.AuthenticationKit';

export class SmsVerifyService {
  private static instance: SmsVerifyService;
  private currentVerifyId: string = '';
  private currentPhone: string = '';
  private cooldownEndTime: number = 0;  // 冷却结束时间

  private constructor() {}

  static getInstance(): SmsVerifyService {
    if (!SmsVerifyService.instance) {
      SmsVerifyService.instance = new SmsVerifyService();
    }
    return SmsVerifyService.instance;
  }

  /**
   * 发送验证码
   * @param phoneNumber 手机号(带国际区号,如+8613800138000)
   * @param action 操作类型:登录/注册/绑定手机号
   */
  async sendVerifyCode(
    phoneNumber: string,
    action: authentication.VerifyCodeAction = authentication.VerifyCodeAction.SIGN_IN_OR_SIGN_UP
  ): Promise<{ success: boolean; error?: string; cooldownSeconds?: number }> {
    // 1. 检查冷却时间(60秒内不能重复发送)
    const now = Date.now();
    if (now < this.cooldownEndTime) {
      const remainingSeconds = Math.ceil((this.cooldownEndTime - now) / 1000);
      return {
        success: false,
        error: `${remainingSeconds}秒后再试`,
        cooldownSeconds: remainingSeconds
      };
    }

    // 2. 校验手机号格式
    if (!this.isValidPhoneNumber(phoneNumber)) {
      return { success: false, error: '手机号格式不正确' };
    }

    try {
      // 3. 调用SMS服务发送验证码
      const result = await smsService.sendVerifyCode({
        phoneNumber: phoneNumber,
        action: action,
        locale: 'zh_CN',          // 短信语言
        sendInterval: 60          // 发送间隔(秒)
      });

      // 4. 保存验证ID(校验时需要)
      this.currentVerifyId = result.verifyId;
      this.currentPhone = phoneNumber;

      // 5. 设置冷却时间
      this.cooldownEndTime = now + 60000; // 60秒冷却

      console.info(`[SmsVerify] 验证码已发送: ${phoneNumber}, verifyId=${this.currentVerifyId}`);
      return { success: true, cooldownSeconds: 60 };
    } catch (error) {
      const smsError = error as smsService.SmsError;
      switch (smsError.code) {
        case smsService.SmsErrorCode.RATE_LIMIT:
          return { success: false, error: '发送太频繁,请稍后再试' };
        case smsService.SmsErrorCode.INVALID_PHONE:
          return { success: false, error: '手机号无效' };
        case smsService.SmsErrorCode.TEMPLATE_ERROR:
          return { success: false, error: '短信模板异常,请联系客服' };
        default:
          console.error(`[SmsVerify] 发送失败: ${JSON.stringify(error)}`);
          return { success: false, error: '验证码发送失败,请重试' };
      }
    }
  }

  /**
   * 校验验证码
   */
  async verifyCode(code: string): Promise<{ success: boolean; error?: string }> {
    if (!this.currentVerifyId) {
      return { success: false, error: '请先发送验证码' };
    }

    if (!code || code.length !== 6) {
      return { success: false, error: '请输入6位验证码' };
    }

    try {
      // 使用Authentication Kit的PhoneAuthProvider校验
      const authService = authentication.getService();
      const credential = new authentication.PhoneAuthProvider()
        .credential(this.currentPhone, code, this.currentVerifyId);

      await authService.signIn(credential);

      // 验证成功,清除状态
      this.currentVerifyId = '';
      console.info('[SmsVerify] 验证码校验成功');
      return { success: true };
    } catch (error) {
      const authError = error as authentication.AuthError;
      switch (authError.code) {
        case authentication.AuthErrorCode.VERIFY_CODE_INVALID:
          return { success: false, error: '验证码错误' };
        case authentication.AuthErrorCode.VERIFY_CODE_EXPIRED:
          this.currentVerifyId = '';
          return { success: false, error: '验证码已过期,请重新发送' };
        default:
          console.error(`[SmsVerify] 校验失败: ${JSON.stringify(error)}`);
          return { success: false, error: '验证失败,请重试' };
      }
    }
  }

  /**
   * 获取当前冷却剩余秒数
   */
  getCooldownRemaining(): number {
    const now = Date.now();
    if (now >= this.cooldownEndTime) return 0;
    return Math.ceil((this.cooldownEndTime - now) / 1000);
  }

  /**
   * 校验手机号格式
   */
  private isValidPhoneNumber(phone: string): boolean {
    // 简单校验:+86开头 + 11位数字
    const phoneRegex = /^\+86\d{11}$/;
    return phoneRegex.test(phone);
  }
}

进阶用法:短信模板管理与防刷策略

验证码发送不是无限制的,必须有防刷策略。同时,不同场景用不同的短信模板。

// SmsTemplateManager.ets
import { smsService } from '@kit.SmsKit';

// 短信模板定义
interface SmsTemplate {
  id: string;           // 模板ID
  name: string;         // 模板名称
  scene: string;        // 使用场景
  content: string;      // 模板内容
  variables: string[];  // 变量列表
}

// 预定义的短信模板
const SMS_TEMPLATES: Record<string, SmsTemplate> = {
  // 登录/注册验证码
  'verify_login': {
    id: 'SMS_001',
    name: '登录验证码',
    scene: 'login',
    content: '【我的应用】您的验证码为${code},5分钟内有效,请勿泄露给他人。',
    variables: ['code']
  },
  // 绑定手机号验证码
  'verify_bind_phone': {
    id: 'SMS_002',
    name: '绑定手机号验证码',
    scene: 'bind_phone',
    content: '【我的应用】您正在绑定手机号,验证码为${code},5分钟内有效。如非本人操作,请忽略。',
    variables: ['code']
  },
  // 修改密码验证码
  'verify_reset_password': {
    id: 'SMS_003',
    name: '修改密码验证码',
    scene: 'reset_password',
    content: '【我的应用】您正在修改密码,验证码为${code},5分钟内有效。如非本人操作,请立即修改密码。',
    variables: ['code']
  }
};

// 防刷策略配置
interface AntiSpamConfig {
  maxSendsPerMinute: number;    // 每分钟最大发送次数
  maxSendsPerHour: number;      // 每小时最大发送次数
  maxSendsPerDay: number;       // 每天最大发送次数
  maxVerifyAttempts: number;    // 每个验证码最大校验次数
  codeExpireMinutes: number;    // 验证码有效期(分钟)
  ipBlacklist: string[];        // IP黑名单
  phoneBlacklist: string[];     // 手机号黑名单
}

const DEFAULT_ANTI_SPAM: AntiSpamConfig = {
  maxSendsPerMinute: 1,         // 每分钟1次(即60秒冷却)
  maxSendsPerHour: 5,           // 每小时5次
  maxSendsPerDay: 10,           // 每天10次
  maxVerifyAttempts: 5,         // 最多校验5次
  codeExpireMinutes: 5,         // 5分钟有效
  ipBlacklist: [],
  phoneBlacklist: []
};

export class SmsAntiSpamService {
  private config: AntiSpamConfig;
  // 发送记录:key=手机号, value=时间戳数组
  private sendRecords: Map<string, number[]> = new Map();
  // 校验记录:key=verifyId, value=已尝试次数
  private verifyAttempts: Map<string, number> = new Map();

  constructor(config?: Partial<AntiSpamConfig>) {
    this.config = { ...DEFAULT_ANTI_SPAM, ...config };
  }

  /**
   * 检查是否可以发送验证码
   */
  canSend(phoneNumber: string): { allowed: boolean; reason?: string; waitSeconds?: number } {
    // 1. 黑名单检查
    if (this.config.phoneBlacklist.includes(phoneNumber)) {
      return { allowed: false, reason: '该号码已被限制' };
    }

    const now = Date.now();
    const records = this.sendRecords.get(phoneNumber) || [];

    // 2. 每分钟限制
    const oneMinuteAgo = now - 60000;
    const recentSends = records.filter(t => t > oneMinuteAgo);
    if (recentSends.length >= this.config.maxSendsPerMinute) {
      const waitSeconds = Math.ceil((recentSends[0]! + 60000 - now) / 1000);
      return { allowed: false, reason: '发送太频繁', waitSeconds };
    }

    // 3. 每小时限制
    const oneHourAgo = now - 3600000;
    const hourSends = records.filter(t => t > oneHourAgo);
    if (hourSends.length >= this.config.maxSendsPerHour) {
      return { allowed: false, reason: '每小时发送次数已达上限' };
    }

    // 4. 每天限制
    const oneDayAgo = now - 86400000;
    const daySends = records.filter(t => t > oneDayAgo);
    if (daySends.length >= this.config.maxSendsPerDay) {
      return { allowed: false, reason: '今日发送次数已达上限' };
    }

    return { allowed: true };
  }

  /**
   * 记录一次发送
   */
  recordSend(phoneNumber: string): void {
    const now = Date.now();
    if (!this.sendRecords.has(phoneNumber)) {
      this.sendRecords.set(phoneNumber, []);
    }
    this.sendRecords.get(phoneNumber)!.push(now);

    // 清理过期记录(超过24小时的)
    const oneDayAgo = now - 86400000;
    const cleaned = this.sendRecords.get(phoneNumber)!.filter(t => t > oneDayAgo);
    this.sendRecords.set(phoneNumber, cleaned);
  }

  /**
   * 检查是否可以校验
   */
  canVerify(verifyId: string): { allowed: boolean; remainingAttempts: number } {
    const attempts = this.verifyAttempts.get(verifyId) || 0;
    const remaining = this.config.maxVerifyAttempts - attempts;
    return {
      allowed: remaining > 0,
      remainingAttempts: Math.max(0, remaining)
    };
  }

  /**
   * 记录一次校验尝试
   */
  recordVerifyAttempt(verifyId: string): void {
    const current = this.verifyAttempts.get(verifyId) || 0;
    this.verifyAttempts.set(verifyId, current + 1);
  }

  /**
   * 清除验证记录(验证成功或过期后调用)
   */
  clearVerifyRecord(verifyId: string): void {
    this.verifyAttempts.delete(verifyId);
  }
}

完整示例:生产级验证码登录页面

把发送、校验、防刷、UI倒计时串成完整链路。

// PhoneLoginPage.ets
import { SmsVerifyService } from '../services/SmsVerifyService';
import { SmsAntiSpamService } from '../services/SmsAntiSpamService';
import { authentication } from '@kit.AuthenticationKit';

@Entry
@Component
struct PhoneLoginPage {
  @State phoneNumber: string = '';        // 手机号输入
  @State verifyCode: string = '';         // 验证码输入
  @State countdown: number = 0;           // 倒计时秒数
  @State isLoading: boolean = false;      // 加载状态
  @State errorMessage: string = '';       // 错误提示
  @State step: 'input_phone' | 'input_code' = 'input_phone';  // 当前步骤

  private smsService: SmsVerifyService = SmsVerifyService.getInstance();
  private antiSpam: SmsAntiSpamService = new SmsAntiSpamService();
  private countdownTimer: number = -1;

  aboutToDisappear() {
    // 清理定时器
    if (this.countdownTimer !== -1) {
      clearInterval(this.countdownTimer);
    }
  }

  /**
   * 发送验证码
   */
  async sendCode() {
    this.errorMessage = '';

    // 格式化手机号(添加+86前缀)
    const formattedPhone = this.formatPhoneNumber(this.phoneNumber);

    // 防刷检查
    const checkResult = this.antiSpam.canSend(formattedPhone);
    if (!checkResult.allowed) {
      this.errorMessage = checkResult.reason || '发送受限';
      if (checkResult.waitSeconds) {
        this.countdown = checkResult.waitSeconds;
        this.startCountdown();
      }
      return;
    }

    this.isLoading = true;

    try {
      const result = await this.smsService.sendVerifyCode(
        formattedPhone,
        authentication.VerifyCodeAction.SIGN_IN_OR_SIGN_UP
      );

      if (result.success) {
        // 记录发送
        this.antiSpam.recordSend(formattedPhone);
        // 切换到验证码输入步骤
        this.step = 'input_code';
        // 开始倒计时
        this.countdown = result.cooldownSeconds || 60;
        this.startCountdown();
      } else {
        this.errorMessage = result.error || '发送失败';
      }
    } catch (error) {
      this.errorMessage = '网络异常,请重试';
    } finally {
      this.isLoading = false;
    }
  }

  /**
   * 提交验证码
   */
  async submitCode() {
    this.errorMessage = '';

    if (this.verifyCode.length !== 6) {
      this.errorMessage = '请输入6位验证码';
      return;
    }

    this.isLoading = true;

    try {
      const result = await this.smsService.verifyCode(this.verifyCode);

      if (result.success) {
        // 验证成功,跳转到主页
        // router.replaceUrl({ url: 'pages/MainPage' });
        console.info('[PhoneLogin] 登录成功');
      } else {
        this.errorMessage = result.error || '验证失败';
      }
    } catch (error) {
      this.errorMessage = '网络异常,请重试';
    } finally {
      this.isLoading = false;
    }
  }

  /**
   * 重新发送验证码
   */
  async resendCode() {
    if (this.countdown > 0) return;
    this.verifyCode = '';
    await this.sendCode();
  }

  /**
   * 开始倒计时
   */
  private startCountdown() {
    if (this.countdownTimer !== -1) {
      clearInterval(this.countdownTimer);
    }

    this.countdownTimer = setInterval(() => {
      this.countdown--;
      if (this.countdown <= 0) {
        this.countdown = 0;
        clearInterval(this.countdownTimer);
        this.countdownTimer = -1;
      }
    }, 1000);
  }

  /**
   * 格式化手机号
   */
  private formatPhoneNumber(phone: string): string {
    // 如果没有+86前缀,自动添加
    if (phone.startsWith('+86')) return phone;
    return `+86${phone}`;
  }

  build() {
    Column() {
      // 标题
      Text('手机号登录')
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
        .margin({ bottom: 40 })

      if (this.step === 'input_phone') {
        // 第一步:输入手机号
        TextInput({ placeholder: '请输入手机号', text: this.phoneNumber })
          .type(InputType.PhoneNumber)
          .maxLength(11)
          .onChange((value) => { this.phoneNumber = value })
          .width('100%')
          .height(48)
          .margin({ bottom: 16 })

        Button('获取验证码')
          .width('100%')
          .height(48)
          .enabled(this.phoneNumber.length === 11 && !this.isLoading)
          .onClick(() => this.sendCode())
      } else {
        // 第二步:输入验证码
        Text(`验证码已发送至 ${this.phoneNumber}`)
          .fontSize(14)
          .fontColor(Color.Gray)
          .margin({ bottom: 24 })

        TextInput({ placeholder: '请输入6位验证码', text: this.verifyCode })
          .type(InputType.Number)
          .maxLength(6)
          .onChange((value) => { this.verifyCode = value })
          .width('100%')
          .height(48)
          .margin({ bottom: 16 })

        Button('登录')
          .width('100%')
          .height(48)
          .enabled(this.verifyCode.length === 6 && !this.isLoading)
          .onClick(() => this.submitCode())

        // 重新发送
        Row() {
          if (this.countdown > 0) {
            Text(`${this.countdown}秒后可重新发送`)
              .fontSize(14)
              .fontColor(Color.Gray)
          } else {
            Text('重新发送')
              .fontSize(14)
              .fontColor('#1976D2')
              .onClick(() => this.resendCode())
          }
        }
        .margin({ top: 16 })

        // 返回修改手机号
        Text('更换手机号')
          .fontSize(14)
          .fontColor('#1976D2')
          .margin({ top: 12 })
          .onClick(() => {
            this.step = 'input_phone';
            this.verifyCode = '';
            this.errorMessage = '';
          })
      }

      // 错误提示
      if (this.errorMessage) {
        Text(this.errorMessage)
          .fontSize(14)
          .fontColor(Color.Red)
          .margin({ top: 16 })
      }

      // 加载指示器
      if (this.isLoading) {
        LoadingProgress()
          .width(32)
          .height(32)
          .margin({ top: 16 })
      }
    }
    .padding(24)
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
}

踩坑与注意事项

坑1:手机号必须带国际区号

SMS Kit要求手机号带国际区号,如+8613800138000。如果你只传了13800138000,发送失败。而且错误信息可能不会明确告诉你"缺少区号",而是返回一个通用的"参数错误"。

建议:在UI层就引导用户选择国家/地区,自动添加区号前缀。

坑2:短信模板需要审核

短信模板不是你写什么就发什么。模板必须在AGC控制台提交审核,审核通过后才能使用。审核通常需要1-2个工作日。

建议:提前准备好所有可能用到的模板,一次性提交审核。别等上线了才发现模板还没审核通过。

坑3:验证码不要存客户端

验证码的校验必须在服务端完成。如果你在客户端校验验证码,等于把验证码的逻辑暴露给了攻击者,安全形同虚设。

SMS Kit的验证码校验是通过Authentication Kit的PhoneAuthProvider完成的,校验逻辑在服务端,客户端只传验证码和VerifyId。

坑4:验证码有效期别设太短

5分钟是常见的有效期。如果你设了1分钟,用户在信号不好的地方,短信延迟30秒才到,用户只剩30秒输入——体验很差。

但也别设太长。30分钟的验证码,安全性大打折扣。5分钟是平衡安全性和体验的最佳值。

坑5:短信签名必须和模板一致

短信签名(如【我的应用】)必须和模板中的一致,而且签名本身也需要审核。你不能在模板里写【我的应用】,实际发送时改成【其他应用】。

坑6:海外手机号的送达率

海外手机号的短信送达率明显低于国内。部分国家/地区的运营商对验证码短信有拦截策略。如果你的App有海外用户,建议同时提供邮箱验证作为备选方案。

HarmonyOS 6适配说明

HarmonyOS 6对SMS Kit做了以下更新:

  1. 短信自动填充:HarmonyOS 6新增了验证码自动填充能力。用户收到验证码短信后,系统自动提取验证码并填充到输入框,用户不需要手动输入。
// HarmonyOS 6验证码自动填充
import { smsService } from '@kit.SmsKit';

// 监听验证码自动填充
smsService.onAutoFill((code: string) => {
  this.verifyCode = code;  // 自动填充到输入框
});
  1. 语音验证码:新增语音验证码能力。当短信送达失败时,可以自动切换为语音验证码——系统拨打用户电话,语音播报验证码。送达率从95%提升到99%。

  2. 智能风控:SMS Kit新增了智能风控模块,自动识别异常发送行为(如同一IP大量请求、虚拟号码等),无需你手动配置防刷规则。

  3. 多语言模板:短信模板支持多语言版本。同一模板可以有中文、英文、日文等版本,系统根据用户手机的语言设置自动选择。

  4. 送达回执增强:新增详细的送达状态回执,包括"已提交运营商"、“已送达”、“已读”(部分运营商支持)等状态,送达率统计更精确。

总结

SMS Kit让你不用对接运营商就能发验证码,但"发出去"只是第一步。送达率、防刷、模板管理、用户体验——每个环节都影响最终效果。

核心记住三点:

  • 手机号必须带国际区号,忘了就发送失败
  • 防刷策略不能少,没有防刷,验证码接口分分钟被刷爆
  • 短信模板提前审核,别等上线了才发现模板没过审
评估维度 说明
学习难度 ⭐⭐ 发送验证码简单,防刷策略和送达率优化需要经验
使用频率 ⭐⭐⭐⭐⭐ 手机号验证是几乎所有App的标配
重要程度 ⭐⭐⭐⭐⭐ 没有短信验证,手机号登录就是空谈

SMS Kit不是可选项。你自己对接运营商发短信,成本高、送达率低、防刷难做——何必呢?

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。