feat: 新增修改邮箱功能

This commit is contained in:
韦荣超 2022-07-12 18:23:04 +08:00
parent 1daba51286
commit d80943ab6b
8 changed files with 316 additions and 20 deletions

View File

@ -753,4 +753,24 @@ class SystemController extends AbstractController
}
return $array;
}
/**
* @api {get} api/system/get/regverify 18. 获取是否开启邮箱验证
*
* @apiDescription 获取邮箱配置是否开启注册邮箱验证
* @apiVersion 1.0.0
* @apiGroup system
* @apiName get__regverify
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
*/
public function get__regverify()
{
User::auth();
$isRegVerify = Base::settingFind('emailSetting', 'reg_verify') === 'open';
return Base::retSuccess('success', $isRegVerify ? 1 : 0);
}
}

View File

@ -983,4 +983,100 @@ class UsersController extends AbstractController
$data['msgs'] = $msgs;
return Base::retSuccess('发送邀请成功', $data);
}
/**
* @api {get} api/users/send/email 18. 发送邮箱验证码
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
* @apiGroup users
* @apiName send__email
*
* @apiParam {Number} type 邮件类型
* @apiParam {String} email 邮箱地址
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
*/
public function send__email()
{
$type = Request::input('type', 2);
$email = Request::input('email');
$user = User::auth();
if (!$email) {
return Base::retError('请输入新邮箱地址');
}
if (!Base::isEmail($email)) {
return Base::retError('邮箱地址错误');
}
if ($user->email == $email) {
return Base::retError('不能与旧邮箱一致');
}
if (User::where('userid', '<>', $user->userid)->whereEmail($email)->exists()) {
return Base::retError('邮箱地址已存在');
}
UserEmailVerification::userEmailSend($user, $type, $email);
return Base::retSuccess('发送成功');
}
/**
* @api {get} api/users/editemail 19. 修改邮箱
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
* @apiGroup users
* @apiName editemail
*
* @apiParam {String} newEmail 新邮箱地址
* @apiParam {String} code 邮箱验证码
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
*/
public function editemail()
{
$user = User::auth();
$user->checkSystem();
//
$newEmail = trim(Request::input('newEmail'));
$code = trim(Request::input('code'));
if (!$newEmail) {
return Base::retError('请输入新邮箱地址');
}
if (!Base::isEmail($newEmail)) {
return Base::retError('邮箱地址错误');
}
$isRegVerify = Base::settingFind('emailSetting', 'reg_verify') === 'open';
if ($isRegVerify) {
if (!$code) {
return Base::retError('请输入验证码');
}
$res = UserEmailVerification::whereEmail($newEmail)->whereCode($code)->whereType(2)->orderByDesc('id')->first();
if (empty($res)) {
return Base::retError('验证码错误');
}
$oldTime = Carbon::parse($res->created_at)->timestamp;
$time = Base::Time();
// 30分钟失效
if (abs($time - $oldTime) > 1800) {
return Base::retError("验证码已失效");
}
UserEmailVerification::whereUserid($user->userid)->whereCode($code)->whereType(2)->update([
'status' => 1
]);
}
$user->email = $newEmail;
$user->save();
User::token($user);
return Base::retSuccess('修改成功', $user);
}
}

View File

@ -36,39 +36,46 @@ class UserEmailVerification extends AbstractModel
/**
* 发验证邮箱
* @param User $user
* @param int $type
* @param null $newEmail
*/
public static function userEmailSend(User $user)
public static function userEmailSend(User $user, $type = 1, $newEmail = null)
{
$res = self::whereUserid($user->userid)->where('created_at', '>', Carbon::now()->subMinutes(30))->first();
$email = $type == 2 ? $newEmail : $user->email;
$res = self::whereEmail($email)->where('created_at', '>', Carbon::now()->subMinutes(30))->whereType($type)->first();
if ($res) return;
//删除
self::whereUserid($user->userid)->delete();
self::whereUserid($email)->delete();
$code = $type == 2 ? rand(100000, 999999) : Base::generatePassword(64);
$userEmailVerification = self::createInstance([
'userid' => $user->userid,
'email' => $user->email,
'code' => Base::generatePassword(64),
'email' => $email,
'code' => $code,
'status' => 0,
'type' => $type
]);
$userEmailVerification->save();
$setting = Base::setting('emailSetting');
$url = Base::fillUrl('single/valid/email') . '?code=' . $userEmailVerification->code;
try {
if (!Base::isEmail($user->email)) {
throw new \Exception("User email '{$user->email}' address error");
if (!Base::isEmail($email)) {
throw new \Exception("User email '{$email}' address error");
}
$subject = env('APP_NAME') . " 绑定邮箱验证";
$content = "<p>{$user->nickname} 您好,您正在绑定 " . env('APP_NAME') . " 的邮箱请于30分钟之内点击以下链接完成验证 :</p><p style='display: flex; justify-content: center;'><a href='{$url}' target='_blank'>{$url}</a></p>";
Setting::validateAddr($user->email, function ($to) use ($content, $subject, $setting) {
Factory::mailer()
->setDsn("smtp://{$setting['account']}:{$setting['password']}@{$setting['smtp_server']}:{$setting['port']}?verify_peer=0")
->setMessage(EmailMessage::create()
->from(env('APP_NAME', 'Task') . " <{$setting['account']}>")
->to($to)
->subject($subject)
->html($content))
->send();
});
if($type ==2){
$subject = env('APP_NAME') . "修改邮箱验证";
$content = "<p>{$user->nickname} 您好,您正在修改 " . env('APP_NAME') . " 的邮箱验证码如下。请在30分钟内输入验证码</p><p style='color: #0000DD; margin-left: 10%;'>$code</p><p>如果不是本人操作,您的账号可能存在风险,请及时修改密码!</p>";
}else{
$subject = env('APP_NAME') . "绑定邮箱验证";
$content = "<p>{$user->nickname} 您好,您正在绑定 " . env('APP_NAME') . " 的邮箱请于30分钟之内点击以下链接完成验证 :</p><p style='display: flex; justify-content: center;'><a href='{$url}' target='_blank'>{$url}</a></p>";
}
Factory::mailer()
->setDsn("smtp://{$setting['account']}:{$setting['password']}@{$setting['smtp_server']}:{$setting['port']}?verify_peer=0")
->setMessage(EmailMessage::create()
->from(env('APP_NAME', 'Task') . " <{$setting['account']}>")
->to($email)
->subject($subject)
->html($content))
->send();
} catch (\Throwable $e) {
if (str_contains($e->getMessage(), "Timed Out")) {
throw new ApiException("language.TimedOut");

View File

@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddTypeToUserEmailVerificationsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('user_email_verifications', function (Blueprint $table) {
if (!Schema::hasColumn('user_email_verifications', 'type')) {
$table->tinyInteger('type')->nullable()->default(1)->after('status')->comment('邮件类型1-邮箱认证2-修改邮箱');
}
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('user_email_verifications', function (Blueprint $table) {
$table->dropColumn("type");
});
}
}

View File

@ -500,5 +500,18 @@
{"CN": "邀请码:注册时需填写下方邀请码。","EN": "Invitation code: You need to fill in the following invitation code when registering.","TC": "邀請碼:註冊時需填寫下方邀請碼。","KM": "លេខកូដលិខិតអញ្ជើញ: អ្នកត្រូវបំពេញលេខកូដលិខិតអញ្ជើញខាងក្រោមនៅពេលចុះឈ្មោះ។","TH": "รหัสเชิญ: คุณต้องกรอกรหัสคำเชิญต่อไปนี้เมื่อลงทะเบียน","KO": "초대 코드 : 등록 할 때 다음 초대 코드를 입력해야합니다.","JA": "招待コード:登録時に次の招待コードを入力する必要があります。"},
{"CN": "添加模板","EN": "Add template","TC": "添加模板","KM": "បន្ថែមគំរូ","TH": "เพิ่มเทมเพลต","KO": "템플릿을 추가하십시오","JA": "テンプレートを追加します"},
{"CN": "版本","EN": "Version","TC": "版本","KM": "កមបុរក្នា","TH": "รุ่น","KO": "버전","JA": "バージョン"},
{"CN": "请输入新邮箱地址","EN": "Please enter the new mailbox address","TC": "請輸入新郵箱地址","KM": "សូមបញ្ចូលអាសយដ្ឋានប្រអប់សំបុត្រថ្មី","TH": "โปรดป้อนที่อยู่กล่องจดหมายใหม่","KO": "새 사서함 주소를 입력하십시오","JA": "新しいメールボックスアドレスを入力してください"},
{"CN": "邮箱地址错误","EN": "Email address error","TC": "郵箱地址錯誤","KM": "កំហុសអាសយដ្ឋានអ៊ីមែល","TH": "ข้อผิดพลาดที่อยู่อีเมล","KO": "이메일 주소 오류","JA": "メールアドレスエラー"},
{"CN": "不能与旧邮箱一致","EN": "Can't be consistent with the old mailbox","TC": "不能與舊郵箱一致","KM": "មិនអាចត្រូវបានស្របជាមួយនឹងប្រអប់សំបុត្រចាស់","TH": "ไม่สามารถสอดคล้องกับกล่องจดหมายเก่า","KO": "이전 사서함과 일치 할 수 없습니다","JA": "古いメールボックスと一致することはできません"},
{"CN": "验证码错误","EN": "Verification code error","TC": "驗證碼錯誤","KM": "កំហុសក្នុងការផ្ទៀងផ្ទាត់លេខកូដ","TH": "ข้อผิดพลาดของรหัสการตรวจสอบ","KO": "Verifin 코드 오류","JA": "検証コードエラー"},
{"CN": "验证码已失效","EN": "The verification code has failed","TC": "驗證碼已失效","KM": "លេខកូដផ្ទៀងផ្ទាត់បានបរាជ័យ","TH": "รหัสการยืนยันล้มเหลว","KO": "검증 코드가 실패했습니다","JA": "検証コードは失敗しました"},
{"CN": "发送验证码","EN": "Send the verification code","TC": "發送驗證碼","KM": "ផ្ញើកូដផ្ទៀងផ្ទាត់","TH": "ส่งรหัสการยืนยัน","KO": "확인 코드를 보냅니다","JA": "確認コードを送信します"},
{"CN": "秒","EN": "Second","TC": "秒","KM": "ដេលរេបីរយចហើយ","TH": "ที่สอง","KO": "섹스","JA": "2番目"},
{"CN": "输入邮箱验证码","EN": "Enter the mailbox verification code","TC": "輸入郵箱驗證碼","KM": "បញ្ចូលលេខកូដផ្ទៀងផ្ទាត់ប្រអប់សំបុត្រ","TH": "ป้อนรหัสการยืนยันกล่องจดหมาย","KO": "사서함 확인 코드를 입력하십시오","JA": "メールボックス検証コードを入力します"},
{"CN": "新邮箱地址","EN": "New mailbox address","TC": "新郵箱地址","KM": "អាសយដ្ឋានប្រអប់សំបុត្រថ្មី","TH": "ที่อยู่กล่องจดหมายใหม่","KO": "새로운 사서함 주소","JA": "新しいメールボックスアドレス"},
{"CN": "请输入新邮箱地址!","EN": "Please enter the new mailbox address!","TC": "請輸入新郵箱地址!","KM": "សូមបញ្ចូលអាសយដ្ឋានប្រអប់សំបុត្រថ្មី!","TH": "กรุณากรอกที่อยู่กล่องจดหมายใหม่!","KO": "새 사서함 주소를 입력하십시오!","JA": "新しいメールボックスアドレスを入力してください!"},
{"CN": "请输入正确的邮箱地址!","EN": "Please input the correct email address!","TC": "請輸入正確的郵箱地址!","KM": "សូមបញ្ចូលអាសយដ្ឋានអ៊ីមែលត្រឹមត្រូវ!","TH": "กรุณาป้อนที่อยู่อีเมลที่ถูกต้อง!","KO": "thecorct 이메일 주소를 입력하십시오!","JA": "正しいメールアドレスを入力してください!"},
{"CN": "验证码","EN": "Verification code","TC": "驗證碼","KM": "កូដ​ផ្ទៀង​ផ្ទាត់","TH": "รหัสการตรวจสอบ","KO": "Verifin 코드","JA": "検証コード"},
{"CN": "修改邮箱","EN": "Modify the mailbox","TC": "修改郵箱","KM": "កែប្រែប្រអប់សំបុត្រ","TH": "แก้ไขกล่องจดหมาย","KO": "사서함을 수정하십시오","JA": "メールボックスを変更します"},
]
})(window)

View File

@ -0,0 +1,120 @@
<template>
<div class="setting-item submit">
<Form ref="formDatum" :model="formDatum" :rules="ruleDatum" label-width="auto" @submit.native.prevent>
<FormItem :label="$L('新邮箱地址')" prop="newEmail">
<Row>
<Col span="6">
<Input v-model="formDatum.newEmail"></Input>
</Col>
<Col span="6" v-if="isRegVerify == 1">
<Button v-if="isUpdateShow" @click="sendEmailCode" type="primary">{{ $L('发送验证码') }}</Button>
<Button v-if="!isUpdateShow" disabled><span>{{ count }}</span>{{ $L('秒') }}</Button>
</Col>
</Row>
</FormItem>
<FormItem :label="$L('验证码')" prop="code" v-if="isRegVerify == 1">
<Input v-model="formDatum.code" :placeholder="$L('输入邮箱验证码')"/>
</FormItem>
</Form>
<div class="setting-footer">
<Button :loading="loadIng > 0" type="primary" @click="submitForm">{{ $L('提交') }}</Button>
<Button :loading="loadIng > 0" @click="resetForm" style="margin-left: 8px">{{ $L('重置') }}</Button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
loadIng: 0,
formDatum: {
newEmail: '',
code: '',
},
ruleDatum: {},
count: 0,
isUpdateShow: true,
codeShow: false,
isRegVerify: 0,
}
},
mounted() {
this.getRegVerify();
},
methods: {
initLanguage() {
this.ruleDatum = {
newEmail: [
{
validator: (rule, value, callback) => {
if (value === '') {
callback(new Error(this.$L('请输入新邮箱地址!')));
} else if (!$A.isEmail(value)) {
callback(new Error(this.$L('请输入正确的邮箱地址!')));
} else {
callback();
}
},
required: true,
trigger: 'change'
},
],
};
},
sendEmailCode() {
this.$store.dispatch("call", {
url: 'users/send/email',
data: {type: 2, email: this.formDatum.newEmail}
}).then(({}) => {
this.isUpdateShow = false;
this.count = 120; //120
let times = setInterval(() => {
this.count--; //
if (this.count <= 0) {
this.isUpdateShow = true;
clearInterval(times);
}
}, 1000); //1000
}).catch(({msg}) => {
$A.messageError(msg);
})
},
submitForm() {
this.$refs.formDatum.validate((valid) => {
if (valid) {
this.loadIng++;
this.$store.dispatch("call", {
url: 'users/editemail',
data: this.formDatum,
}).then(({data}) => {
$A.messageSuccess('修改成功');
this.$store.dispatch("saveUserInfo", data);
this.$refs.formDatum.resetFields();
this.isUpdateShow = true;
}).catch(({msg}) => {
$A.modalError(msg);
}).finally(_ => {
this.loadIng--;
});
}
})
},
resetForm() {
this.$refs.formDatum.resetFields();
},
getRegVerify() {
this.$store.dispatch("call", {
url: 'system/get/regverify',
}).then(({data}) => {
this.isRegVerify = data;
}).catch(() => {
})
},
},
}
</script>

View File

@ -73,6 +73,7 @@ export default {
{path: 'personal', name: '个人设置'},
{path: 'language', name: '语言设置'},
{path: 'password', name: '密码设置'},
{path: 'email', name: '修改邮箱'},
]
if ([
'127.0.0.1:2222',

View File

@ -49,6 +49,11 @@ export default [
path: 'password',
component: () => import('./pages/manage/setting/password.vue'),
},
{
name: 'manage-setting-email',
path: 'email',
component: () => import('./pages/manage/setting/email.vue'),
},
{
name: 'manage-setting-system',
path: 'system',