// +---------------------------------------------------------------------- declare (strict_types = 1); namespace app\api\controller\v1; use think\facade\Event; use think\exception\ValidateException; use ywxapp\controller\ApiBase; use ywxapp\library\Sms as Smslib; use ywxapp\model\CommonSms as SmsModel; use ywxapp\model\MemberUser as UserModel; use ywxapp\utils\Random; /** * 短信验证码接口(公开,无需登录) * * 路由前缀:/api/v1/sms/ * 业务事件(event)由调用方约定,如 register / resetpwd / changepwd / mobilelogin 等。 * * @package app\api\controller\v1 */ class Sms extends ApiBase { /** * 免登录(公开)接口白名单。 * * @var array */ protected $noNeedLogin = ['*']; /** * 接口存活探测。 * * @return \think\Response JSON 响应,演示用 * * @route GET /api/v1/sms/index */ public function index() { return json(['message' => 'SMS API']); } /** * 发送短信验证码(公开)。 * * 校验手机号与事件后,受发送频率(同号 60 秒、同 IP 每小时 5 条)限制; * 按 event 校验账号是否已注册 / 占用 / 未注册,最后触发 SmsSend 事件下发短信。 * * @param string $mobile 手机号(必填) * @param string $event 事件名称(必填,小写字母,默认 register) * @param string $type 验证类型(可选,auto 自动 / system 系统验证码) * @param string $source_id 来源 ID(可选) * * @return \think\Response JSON 响应,成功提示 * * @throws ValidateException 当参数校验失败时 * * @route POST /api/v1/sms/send */ public function send() { $cfg = config('smsbao'); dump($cfg);die; $mobile = $this->request->post("mobile"); $event = $this->request->param("event", 'register'); $type = $this->request->post("type", 'auto'); $source_id = $this->request->post("source_id", ''); try { validate([ 'mobile' => 'require|max:11', 'event' => 'require|alpha|lower', ])->message([ 'mobile.require' => '手机号必须', 'mobile.max' => '手机号最多不能超过11个字符', 'event.require' => '事件名称必须', 'event.alpha' => '事件名称必须是字母', 'event.lower' => '事件名称必须是小写字母', ])->check([ 'mobile' => $mobile, 'event' => $event, ]); $last = Smslib::get($mobile, $event); if ($last && time() - (int) $last['create_at'] < 60) { $this->apiError('发送频繁'); } $ipSendTotal = SmsModel::where(['ip' => $this->request->ip()])->whereTime('create_at', '-1 hours')->count(); if ($ipSendTotal >= 5) { $this->apiError('发送频繁'); } if ($event) { $userinfo = UserModel::getByMobile($mobile); if ($event == 'register' && $userinfo) { //已被注册 $this->apiError('已被注册'); } elseif (in_array($event, ['changemobile']) && $userinfo) { //被占用 $this->apiError('已被占用'); } elseif (in_array($event, ['changepwd', 'resetpwd']) && ! $userinfo) { //未注册 $this->apiError('未注册'); } } if (! Event::hasListener('SmsSend')) { $this->apiError('请在后台插件管理安装短信验证插件'); } $ret = Smslib::send($mobile, null, $event); if ($ret) { $this->apiSuccess([], '发送成功'); } else { $this->apiError('发送失败,请检查短信配置是否正确'); } } catch (ValidateException $e) { $this->apiError($e->getError()); } } /** * 校验短信验证码(公开)。 * * 校验手机号、事件名称、验证码格式,并依 event 校验账号注册状态, * 最后调用 SmsLib::check 比对验证码(默认有效期 5 分钟)。 * * @param string $mobile 手机号(必填) * @param string $event 事件名称(必填,默认 register) * @param string $captcha 验证码(必填) * * @return \think\Response JSON 响应,成功提示 * * @route POST /api/v1/sms/check */ public function check() { $mobile = $this->request->post("mobile"); $event = $this->request->post("event", 'register'); $captcha = $this->request->post("captcha"); if (! $mobile || ! \think\Validate::regex($mobile, "^1\d{10}$")) { $this->apiError('手机号不正确'); } if (! preg_match("/^[a-z0-9_\-]{3,30}\$/i", $event)) { $this->apiError('事件名称错误'); } if (! preg_match("/^[a-z0-9]{4,6}\$/i", $captcha)) { $this->apiError('验证码格式错误'); } if ($event) { $userinfo = UserModel::getByMobile($mobile); if ($event == 'register' && $userinfo) { //已被注册 $this->apiError('已被注册'); } elseif (in_array($event, ['changemobile']) && $userinfo) { //被占用 $this->apiError('已被占用'); } elseif (in_array($event, ['changepwd', 'resetpwd']) && ! $userinfo) { //未注册 $this->apiError('未注册'); } } $ret = Smslib::check($mobile, $captcha, $event); if ($ret) { $this->apiSuccess([], '成功'); } else { $this->apiError('验证码不正确'); } } }