You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
986 lines
44 KiB
986 lines
44 KiB
<?php
|
|
|
|
namespace app\home\service;
|
|
|
|
use app\home\validate\LoginValidate;
|
|
use app\model\AdminModel;
|
|
use app\model\CountryModel;
|
|
use app\model\UserChatGroupModel;
|
|
use app\model\UserChatLinkModel;
|
|
use app\model\UserModel;
|
|
use app\model\UserVerifyLogModel;
|
|
use app\utility\Jwt;
|
|
use app\utility\UnqId;
|
|
use Psr\SimpleCache\InvalidArgumentException;
|
|
use think\exception\ValidateException;
|
|
use think\facade\Cache;
|
|
use think\facade\Log;
|
|
use think\facade\Queue;
|
|
use GeoIp2\Database\Reader;
|
|
|
|
class LoginService extends BaseHomeService
|
|
{
|
|
|
|
/**
|
|
* 发送邮件
|
|
* @param $param
|
|
* @return array
|
|
* @throws InvalidArgumentException
|
|
*/
|
|
public function sendEmail($param): array
|
|
{
|
|
try {
|
|
// 防止重复操作 单个ip 3秒钟内只能请求一次
|
|
$ip = $this->getClientRealIp();
|
|
|
|
// 参数校验
|
|
validate(LoginValidate::class)->scene('sendEmail')->check($param);
|
|
|
|
$param['email'] = trim($param['email']);
|
|
// 邮箱类型校验 去除国内邮箱
|
|
if ($this->checkForbidEmail($param['email'])) {
|
|
return $this->toData('500', lang('email_not_supported'));
|
|
}
|
|
|
|
// 每天获取code 次数限制
|
|
$sendCodeKey = 'USER:SEND_CODE_NUM:' . $ip;
|
|
if ($this->checkGetNoTradeCodeNum($sendCodeKey)) {
|
|
return $this->toData('500', lang('exceeding_the_maximum_number'));
|
|
}
|
|
|
|
// 获取发送的邮件内容
|
|
$content = $this->getEmailContent();
|
|
$content['email'] = $param['email'];
|
|
|
|
|
|
// 将发送邮件任务添加到异步队列
|
|
$queuename = 'app\home\job\SendEmail';
|
|
Queue::push($queuename, $content, 'sendEmail');
|
|
|
|
// 邮件code存到缓存(未登录操作共用)
|
|
$key = 'DB:USER:UNLOGIN:EMAIL_CODE:' . $param['email'];
|
|
$this->insertCodeToCache($key, $content['code'], 300);
|
|
|
|
// 记录已经发送的code次数
|
|
$this->updateHadGetCodeNumCache($sendCodeKey);
|
|
|
|
// 返回结果
|
|
return $this->toData('0', lang('email_send_successfully'));
|
|
|
|
} catch (ValidateException $validateException) {
|
|
// 参数校验失败 异常类
|
|
$message = $validateException->getError();
|
|
return $this->toData('500', $message);
|
|
} catch (\Exception $exception) {
|
|
// 异常情况
|
|
return $this->toData('500', lang('system_busy'), [$exception->getMessage()]);
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* @desc 邮箱注册用户
|
|
* @param $param
|
|
* @return array
|
|
* @throws InvalidArgumentException
|
|
* @throws \think\db\exception\DataNotFoundException
|
|
* @throws \think\db\exception\DbException
|
|
* @throws \think\db\exception\ModelNotFoundException
|
|
*/
|
|
public function registerEmail($param): array
|
|
{
|
|
try {
|
|
// 获取客户端ip
|
|
$ip = $this->getClientRealIp();
|
|
|
|
$agentCode = $param['agent_code'] ?? ''; // 代理邀请码(该邀请码只限于管理端账号中)
|
|
$inviteCode = $param['invite_code'] ?? ''; // 邀请码 (该邀请码即可以是管理端账号的邀请码,也可以是用户表中的邀请码)
|
|
$chCode = $param['ch_code'] ?? ''; // 注册渠道标识码
|
|
$phone = $param['phone'];
|
|
$email = $param['email'];
|
|
|
|
// 邮件注册参数校验
|
|
validate(LoginValidate::class)->scene('emailRegister')->check($param);
|
|
|
|
// 判断注册数量 ip每天可注册数量
|
|
$ipCanRegisterNumPerIpPerDay = 'USER:REGISTER:' . $ip;
|
|
if ($this->checkRegisterLimit($ipCanRegisterNumPerIpPerDay)) {
|
|
return $this->toData('500', lang('exceeding_the_maximum_number'));
|
|
}
|
|
|
|
// 验证验证码
|
|
$emailKey = 'DB:USER:UNLOGIN:EMAIL_CODE:' . $param['email'];
|
|
if (!$this->checkCode($emailKey, $param['email_code'])) {
|
|
//注册验证码
|
|
$reg_key = "USER:REG:CODE";
|
|
if (!$this->checkCode($reg_key, $param['email_code'])) {
|
|
return $this->toData('500', lang('incorrect_verification_code'));
|
|
} else {
|
|
$code = random_int(1000, 9999);
|
|
$this->insertCodeToCache($reg_key, $code, 300);
|
|
}
|
|
}
|
|
|
|
// 邮箱是否已经存在
|
|
$emailExists = UserModel::checkEmailExists($email);
|
|
if ($emailExists) {
|
|
return $this->toData('500', lang('email_has_already_been_registered'));
|
|
}
|
|
|
|
// 获取代理ID (后续要查询代理下的客服,将用户与其中一个客服绑定)
|
|
$agentId = 0;
|
|
if (!empty($agentCode)) {
|
|
$agentId = AdminModel::getIdByInviteCode($agentCode);
|
|
}
|
|
// 获取邀请码对应的账号ID(可以是管理端的账号,也可以是用户列表中的账号)
|
|
$parentAdminId = 0;
|
|
$parentUserId = 0;
|
|
if (!empty($inviteCode)) {
|
|
$parentAdminId = AdminModel::getIdByInviteCode($inviteCode); // 先从管理端账号中查找是否存在邀请码对应的账号
|
|
if ($parentAdminId) {
|
|
$agentId = $parentAdminId;
|
|
} else {
|
|
$parentUserId = $this->getParentIdByInviteCode($inviteCode); // 再从用户账号中查找是否存在邀请码对应的账号
|
|
}
|
|
}
|
|
// 如果没有传递代理邀请码,也没有根据invite_code找到管理端账号,则设置一个默认的代理
|
|
if (empty($agentId) && empty($parentAdminId)) {
|
|
$agentId = AdminModel::getDefaultAgentId();
|
|
if (empty($agentId)) {
|
|
return $this->toData('500', lang('missing_agent_id'));
|
|
}
|
|
}
|
|
|
|
// 入库
|
|
$userNo = $this->getUniqUserNo();
|
|
$salt = env('ENCRYPT.SALT');
|
|
$password = (new UnqId())->encryptPassword($param['password'], $salt);
|
|
$userInviteCode = $this->getUniqInviteCode();
|
|
// 需要开启事务
|
|
\think\facade\Db::transaction(function () use ($chCode, $phone, $email, $userNo, $userInviteCode, $parentUserId, $password, $ip, $salt, $agentId) {
|
|
// 截取邮箱@前的部分作为用户名称
|
|
$pos = strpos($email, '@');
|
|
if ($pos !== false) {
|
|
$userNickName = substr($email, 0, $pos);
|
|
}
|
|
// 查询父级用户
|
|
$parentIds = '';
|
|
$originUserId = 0;
|
|
if($parentUserId > 0){
|
|
$parentUser = UserModel::where('user_id', $parentUserId)->findOrEmpty();
|
|
if($parentUser){
|
|
$parentIds = $parentUser['parent_ids']? $parentUser['parent_ids'].','.$parentUserId : $parentUserId;
|
|
// 如果祖先id = 0 说明这个父级就是祖先id
|
|
$originUserId = $parentUser['origin_user_id'] == 0 ? $parentUserId : $parentUser['origin_user_id'];
|
|
}
|
|
}
|
|
$regUser = UserModel::create([
|
|
'user_no' => $userNo,
|
|
'nick_name' => $userNickName ?? 'user_' . time(),
|
|
'email' => $email,
|
|
'phone_number' => $phone,
|
|
'invite_code' => $userInviteCode,
|
|
'parent_id' => $parentUserId,
|
|
'agent_id' => $agentId,
|
|
'login_password' => $password,
|
|
'salt' => $salt,
|
|
'reg_ip' => $ip,
|
|
'is_test_user' => UserModel::IS_TEST_USER_NO,
|
|
'origin_user_id' => $originUserId,
|
|
'ch_code' => $chCode,
|
|
'parent_ids' => $parentIds,
|
|
'create_time' => date('Y-m-d H:i:s'),
|
|
'update_time' => date('Y-m-d H:i:s'),
|
|
]);
|
|
$userId = $regUser->user_id;
|
|
|
|
// 生成钱包地址
|
|
(new UserService())->doRegInitUserInfo($userId, $parentUserId);
|
|
// 请求聊天服务,注册聊天账号
|
|
$chatData = [
|
|
'Username' => $userNo,
|
|
'Password' => '123456',
|
|
'Nickname' => $regUser->nick_name,
|
|
'Avatar' => env('USER.DEFAULT_HEAD_IMG_PATH'),
|
|
'Email' => $regUser->email,
|
|
];
|
|
$chatUrl = env('CHAT_SERVER.BASE_URL') . '/api/user/register';
|
|
$chatRes = (new \app\utility\RequestChatServer())->ReqChatServer($chatUrl, $chatData);
|
|
if (!isset($chatRes['data']['uuid'])) {
|
|
return $this->toData('500', lang('account_registration_failed'));
|
|
}
|
|
// 绑定注册账号与聊天账号
|
|
UserChatLinkModel::create([
|
|
'user_id' => $userId,
|
|
'user_type' => 1,
|
|
'chat_uuid' => $chatRes['data']['uuid'],
|
|
'chat_name' => $chatRes['data']['username']
|
|
]);
|
|
// 如果有代理,绑定到代理下一个客服(轮询客服绑定)
|
|
if ($agentId > 0 ) {
|
|
$customerIds = AdminModel::getCustomerIdsByAgentId($agentId); // 获取代理下的所有客服ID
|
|
Log::info("邮箱注册 - 客服列表:".json_encode($customerIds));
|
|
if (empty($customerIds)) {
|
|
return $this->toData('500', lang('account_registration_failed'));
|
|
}
|
|
$counterKey = 'counter_of_bind_customer:'.$agentId;
|
|
$counterIndex = Cache::store('redis')->get($counterKey); //客服绑定计数器索引
|
|
if (empty($counterIndex)) {
|
|
Cache::store('redis')->set($counterKey, 1);
|
|
$tagCustomerId = $customerIds[0];
|
|
} else {
|
|
Cache::store('redis')->inc($counterKey); //更新计数器索引
|
|
$tagIndex = $counterIndex % count($customerIds);
|
|
$tagCustomerId = $customerIds[$tagIndex];
|
|
}
|
|
Log::info("邮箱注册 - 当前绑定客服ID:".$tagCustomerId);
|
|
if ($tagCustomerId > 0) {
|
|
$regUser->customer_id = $tagCustomerId;
|
|
$regUser->save();
|
|
}
|
|
// 将注册账号的chat_id与绑定客服的chat_id加为好友
|
|
$customerChatInfo = UserChatLinkModel::where(['user_id'=>$tagCustomerId, 'user_type'=>UserChatLinkModel::USER_CHAT_LINK_USER_TYPE_ADMIN])->find(); //查询客服的聊天账号uuid
|
|
if (empty($customerChatInfo)) {
|
|
return $this->toData('500', lang('account_registration_failed'));
|
|
}
|
|
$chatFriendsData = [
|
|
'UserUuid' => $chatRes['data']['uuid'],
|
|
'CustomerUuid' => $customerChatInfo->chat_uuid,
|
|
];
|
|
$chatFriendsUrl = env('CHAT_SERVER.BASE_URL') . '/api/eachOtherFriends';
|
|
$chatFriendsRes = (new \app\utility\RequestChatServer())->ReqChatServer($chatFriendsUrl, $chatFriendsData);
|
|
Log::info("邮箱注册 - 用户与客服加好友结果:".json_encode($chatFriendsRes));
|
|
// 将当前用户加入到代理创建的群聊中 【需要查出当前用户的代理创建的群聊信息】
|
|
$agentGroup = UserChatGroupModel::where(['user_id'=>$agentId,'remark'=>UserChatGroupModel::USER_CHAT_GROUP_REMARK_ADMIN_INIT])->find();
|
|
if (empty($agentGroup)) {
|
|
return $this->toData('500', lang('account_registration_failed'));
|
|
}
|
|
$joinChatGroupUrl = env('CHAT_SERVER.BASE_URL') . '/api/group/join/'.$chatRes['data']['uuid'].'/'.$agentGroup->group_uuid;
|
|
$joinChatGroupRes = (new \app\utility\RequestChatServer())->ReqChatServer($joinChatGroupUrl, []);
|
|
Log::info("邮箱注册 - 用户与客服加好友结果:".json_encode($joinChatGroupRes));
|
|
}
|
|
});
|
|
// 删除缓存
|
|
$this->delCache($emailKey);
|
|
// 累加已经注册的个数
|
|
$this->updateHadRegisterNumCache($ipCanRegisterNumPerIpPerDay);
|
|
|
|
return $this->toData('0', lang('account_registration_successful'));
|
|
} catch (ValidateException $validateException) {
|
|
// 参数校验失败 异常类
|
|
$message = $validateException->getError();
|
|
return $this->toData('500', $message);
|
|
} catch (\Exception $exception) {
|
|
trace('【注册错误】提交数据:'.json_encode($param), 'error');
|
|
trace('【注册错误】'.$exception->getMessage(), 'error');
|
|
return $this->toData('500', lang('system_busy'), [$exception->getMessage(), $exception->getTrace()]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @desc 登陆业务处理接口
|
|
* @param array $param
|
|
* @return array
|
|
*/
|
|
public function loginEmail(array $param): array
|
|
{
|
|
try {
|
|
// 参数校验 邮箱 密码
|
|
validate(LoginValidate::class)->scene('emailLogin')->check($param);
|
|
|
|
$param['email'] = trim($param['email']);
|
|
// 查找邮箱
|
|
$userId = UserModel::getUserIdByEmail($param['email']);
|
|
if ($userId <= 0) {
|
|
return $this->toData('500', lang('user_does_not_exist'));
|
|
}
|
|
|
|
// 查找用户信息
|
|
$user = UserModel::getFieldsByUserId('invite_code,user_id,user_no,nick_name,is_real,login_password,salt', $userId);
|
|
if (empty($user)) {
|
|
return $this->toData('500', lang('user_does_not_exist'));
|
|
}
|
|
|
|
// 校验密码
|
|
$checkPasswordBool = (new UnqId())->checkPassword($param['password'], $user['login_password'], $user['salt']);
|
|
if (!$checkPasswordBool) {
|
|
return $this->toData('500', lang('incorrect_account_or_password'));
|
|
}
|
|
|
|
// 生成token
|
|
$token = (new Jwt())->getToken($userId, env('ENCRYPT.SALT'));
|
|
if (empty($token)) {
|
|
return $this->toData('500', lang('system_busy'));
|
|
}
|
|
|
|
// 用户登陆之后需要进行的操作 异步完成
|
|
Queue::push('app\home\job\LoginDone', ['userId' => $userId,
|
|
'fields' => [
|
|
'last_login_time' => date('Y-m-d H:i:s'),
|
|
'ip' => $this->getClientRealIp(),
|
|
'device' => $param['device'],
|
|
|
|
]
|
|
], 'loginDone');
|
|
|
|
// 将token存致缓存 覆盖新的缓存 实现单设备登陆
|
|
$this->setUserTokenCache($token, $userId);
|
|
|
|
// 用户登记关系
|
|
(new UserService())->getUserLevel($userId);
|
|
|
|
// 返回结果以及用户信息
|
|
return $this->toData('0', 'Request successful.', [
|
|
'userId' => $userId,
|
|
'userNo' => $user['user_no'],
|
|
'nickName' => $user['nick_name'],
|
|
'inviteCode' => $user['invite_code'],
|
|
'isReal' => $user['is_real'],
|
|
'logo' => env('USER.DEFAULT_HEAD_IMG_PATH'),
|
|
'token' => $token,
|
|
]);
|
|
} catch (ValidateException $validateException) {
|
|
// 参数校验失败 异常类
|
|
$message = $validateException->getError();
|
|
return $this->toData('500', $message);
|
|
} catch (InvalidArgumentException $invalidArgumentException) {
|
|
return $this->toData('500', lang('system_busy'));
|
|
} catch (\Exception $exception) {
|
|
return $this->toData('500', lang('system_busy'), [$exception->getTrace()]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @desc未登陆获取短信验证码
|
|
* @param array $param
|
|
* @return array
|
|
* @throws InvalidArgumentException
|
|
*/
|
|
public function sendSms(array $param): array
|
|
{
|
|
try {
|
|
// 防止重复操作 单个ip 3秒钟内只能请求一次
|
|
$ip = $this->getClientRealIp();
|
|
|
|
// 参数校验
|
|
validate(LoginValidate::class)->scene('sendSms')->check($param);
|
|
|
|
$param['nation'] = trim($param['nation']);
|
|
$param['phone'] = trim($param['phone']);
|
|
// 去除国内手机号
|
|
if ($this->checkForbidNation($param['nation'])) {
|
|
return $this->toData('500', lang('unsupported_country_or_region'));
|
|
}
|
|
|
|
// 判断国家码是否有效
|
|
$nationExists = CountryModel::checkCodeExists($param['nation']);
|
|
if (!$nationExists) {
|
|
return $this->toData('500', lang('unsupported_country_or_region'));
|
|
}
|
|
|
|
// 发送次数校验 - 号码
|
|
$phoneSendCodeKey = 'USER:PHONE_SEND_CODE_NUM:' . $param['nation'].':'.$param['phone'];
|
|
if ($this->checkGetNoTradeCodeNumPhone($phoneSendCodeKey)) {
|
|
return $this->toData('100300', 'No worries. Please feel free to reach out again tomorrow.', []);
|
|
}
|
|
|
|
// 发送次数校验 - ip
|
|
$ipSendCodeKey = 'USER:IP_SEND_CODE_NUM:' . $ip;
|
|
if ($this->checkGetNoTradeCodeNum($ipSendCodeKey)) {
|
|
return $this->toData('100300', 'No worries. Please feel free to reach out again tomorrow.', []);
|
|
}
|
|
|
|
// 获取发送内容
|
|
$content = $this->getSmsContent();
|
|
$mobile = $param['nation'] . $param['phone'];
|
|
$content['mobile'] = $mobile;
|
|
|
|
// 异步发送
|
|
$queuename = 'app\home\job\SendSms';
|
|
Queue::push($queuename, $content, 'sendSms');
|
|
|
|
// 短信code存到缓存(未登录操作共用)
|
|
$key = 'DB:USER:UNLOGIN:SMS_CODE:' . $mobile;
|
|
$this->insertCodeToCache($key, $content['code'], 300);
|
|
|
|
// 累加已经获取的次数
|
|
$this->updateHadGetCodeNumCache($phoneSendCodeKey);
|
|
$this->updateHadGetCodeNumCache($ipSendCodeKey);
|
|
|
|
// 返回结果
|
|
return $this->toData('0', lang('message_has_been_sent'));
|
|
} catch (ValidateException $validateException) {
|
|
// 参数校验失败 异常类
|
|
$message = $validateException->getError();
|
|
return $this->toData('100400', $message);
|
|
} catch (\Exception $exception) {
|
|
trace('【注册错误】提交数据:'.json_encode($param), 'error');
|
|
trace('【注册错误】'.$exception->getMessage(), 'error');
|
|
return $this->toData('500', lang('system_busy'), [$exception->getMessage()]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @desc 短信注册
|
|
* @param array $param
|
|
* @return array
|
|
* @throws InvalidArgumentException
|
|
*/
|
|
public function registerSms(array $param): array
|
|
{
|
|
try {
|
|
// 防止重复操作
|
|
$ip = $this->getClientRealIp();
|
|
$agentCode = $param['agent_code'] ?? ''; // 代理邀请码(该邀请码只限于管理端账号中)
|
|
$inviteCode = $param['invite_code'] ?? ''; // 邀请码 (该邀请码即可以是管理端账号的邀请码,也可以是用户表中的邀请码)
|
|
$chCode = $param['ch_code'] ?? '';
|
|
|
|
// p2项目调整,手机号注册时候也需要上传邮箱地址
|
|
if (empty($param['email'])) {
|
|
return $this->toData('400', lang('parameter_error'));
|
|
}
|
|
|
|
// 是否上传了实名认证信息,上传了实名认证信息时is_verify=1,否则is_verify=0
|
|
$isVerify = $param['is_verify'] ?? 0;
|
|
if ($isVerify == 1) {
|
|
if (empty($param['verify_name']) || empty($param['verify_surname']) || empty($param['verify_code']) || empty($param['verify_front_img']) || empty($param['verify_country']) || empty($param['verify_birth_day']) || empty($param['verify_gender'])
|
|
|| empty($param['verify_email'])) {
|
|
return $this->toData('400', lang('parameter_error'));
|
|
}
|
|
}
|
|
|
|
// 短信注册参数校验
|
|
validate(LoginValidate::class)->scene('smsRegister')->check($param);
|
|
|
|
// 判断注册数量 ip每天可注册数量
|
|
$ipCanRegisterNumPerIpPerDay = 'USER:REGISTER:' . $ip;
|
|
$this->checkRegisterLimit($ipCanRegisterNumPerIpPerDay);
|
|
|
|
// 校验验证码
|
|
$mobile = $param['nation'] . $param['phone'];
|
|
$smsKey = 'DB:USER:UNLOGIN:SMS_CODE:' . $mobile;
|
|
if ($param['sms_code'] != 8888) { // 方便测试,8888为万能验证码
|
|
if (!$this->checkCode($smsKey, $param['sms_code'])) {
|
|
return $this->toData('500', lang('incorrect_verification_code'));
|
|
//注册验证码
|
|
// $reg_key = "USER:REG:CODE";
|
|
// if (!$this->checkCode($reg_key, $param['sms_code'])) {
|
|
// return $this->toData('100300', 'The verification code is incorrect.', []);
|
|
// }
|
|
}
|
|
}
|
|
|
|
// 手机号是否已经存在
|
|
$phoneExists = UserModel::checkPhoneExists($param['phone']);
|
|
if ($phoneExists) {
|
|
return $this->toData('500', lang('phone_number_has_already_been_registered'));
|
|
}
|
|
|
|
// 获取代理ID (后续要查询代理下的客服,将用户与其中一个客服绑定)
|
|
$agentId = 0;
|
|
if (!empty($agentCode)) {
|
|
$agentId = AdminModel::getIdByInviteCode($agentCode);
|
|
}
|
|
// 获取邀请码对应的账号ID(可以是管理端的账号,也可以是用户列表中的账号)
|
|
$parentAdminId = 0;
|
|
$parentUserId = 0;
|
|
if (!empty($inviteCode)) {
|
|
$parentAdminId = AdminModel::getIdByInviteCode($inviteCode); // 先从管理端账号中查找是否存在邀请码对应的账号
|
|
if ($parentAdminId) {
|
|
$agentId = $parentAdminId;
|
|
} else {
|
|
$parentUserId = $this->getParentIdByInviteCode($inviteCode); // 再从用户账号中查找是否存在邀请码对应的账号
|
|
}
|
|
}
|
|
// 如果没有传递代理邀请码,也没有根据invite_code找到管理端账号,则设置一个默认的代理
|
|
if (empty($agentId) && empty($parentAdminId)) {
|
|
$agentId = AdminModel::getDefaultAgentId();
|
|
if (empty($agentId)) {
|
|
return $this->toData('500', lang('missing_agent_id'));
|
|
}
|
|
}
|
|
|
|
// 入库
|
|
$userNo = $this->getUniqUserNo();
|
|
$salt = env('ENCRYPT.SALT');
|
|
$password = (new UnqId())->encryptPassword($param['password'], $salt);
|
|
$userInviteCode = $this->getUniqInviteCode();
|
|
|
|
// 需要开启事务
|
|
\think\facade\Db::transaction(function () use ($chCode, $param, $userNo, $userInviteCode, $parentUserId, $password, $ip, $salt, $agentId, $isVerify) {
|
|
// 生成用户数据
|
|
$parentIds = '';
|
|
$originUserId = 0;
|
|
if($parentUserId > 0){
|
|
$parentUser = UserModel::where('user_id', $parentUserId)->findOrEmpty();
|
|
if($parentUser){
|
|
$parentIds = $parentUser['parent_ids']? $parentUser['parent_ids'].','.$parentUserId : $parentUserId;
|
|
// 如果祖先id = 0 说明这个父级就是祖先id
|
|
$originUserId = $parentUser['origin_user_id'] == 0 ? $parentUserId : $parentUser['origin_user_id'];
|
|
}
|
|
}
|
|
$regUser = UserModel::create([
|
|
'country_code' => $param['nation'],
|
|
'email' => $param['email'],
|
|
'phone_number' => $param['phone'],
|
|
'user_no' => $userNo,
|
|
'invite_code' => $userInviteCode,
|
|
'parent_id' => $parentUserId,
|
|
'agent_id' => $agentId,
|
|
'real_status' => $isVerify ? 2 : 1,
|
|
'login_password' => $password,
|
|
'salt' => $salt,
|
|
'reg_ip' => $ip,
|
|
'is_test_user' => UserModel::IS_TEST_USER_NO,
|
|
'nick_name' => 'user_'.substr($param['phone'], -4),
|
|
'origin_user_id' => $originUserId,
|
|
'parent_ids' => $parentIds,
|
|
'ch_code' => $chCode,
|
|
'create_time' => date('Y-m-d H:i:s'),
|
|
'update_time' => date('Y-m-d H:i:s'),
|
|
|
|
]);
|
|
$userId = $regUser->user_id;
|
|
|
|
// 如果上传了实名认证信息,则添加实名认证数据
|
|
if ($isVerify == 1) {
|
|
UserVerifyLogModel::create([
|
|
'user_id' => $userId,
|
|
'surname' => $param['verify_surname'],
|
|
'name' => $param['verify_name'],
|
|
'code' => $param['verify_code'],
|
|
'front_img' => $param['verify_front_img'],
|
|
'status' => 1,
|
|
'country' => $param['verify_country'],
|
|
'birth_day' => $param['verify_birth_day'],
|
|
'gender' => $param['verify_gender'],
|
|
// 'addr' => $param['verify_addr'],
|
|
// 'zip_code' => $param['verify_zip_code'],
|
|
'email' => $param['verify_email'],
|
|
'create_time' => date('Y-m-d H:i:s'),
|
|
'update_time' => date('Y-m-d H:i:s'),
|
|
]);
|
|
}
|
|
|
|
// 生成钱包地址
|
|
(new UserService())->doRegInitUserInfo($userId, $parentUserId);
|
|
// 注册聊天账号
|
|
$chatData = [
|
|
'Username' => $userNo,
|
|
'Password' => '123456',
|
|
'Nickname' => $regUser->nick_name,
|
|
'Avatar' => env('USER.DEFAULT_HEAD_IMG_PATH'),
|
|
];
|
|
$chatUrl = env('CHAT_SERVER.BASE_URL') . '/api/user/register';
|
|
$chatRes = (new \app\utility\RequestChatServer())->ReqChatServer($chatUrl, $chatData);
|
|
if (!isset($chatRes['data']['uuid'])) {
|
|
return $this->toData('500', lang('account_registration_failed'));
|
|
}
|
|
// 绑定注册账号与聊天账号
|
|
UserChatLinkModel::create([
|
|
'user_id' => $userId,
|
|
'user_type' => 1,
|
|
'chat_uuid' => $chatRes['data']['uuid'],
|
|
'chat_name' => $chatRes['data']['username']
|
|
]);
|
|
// 如果有代理,绑定到代理下一个客服(轮询客服绑定)
|
|
if ($agentId > 0 ) {
|
|
$customerIds = AdminModel::getCustomerIdsByAgentId($agentId); // 获取代理下的所有客服ID
|
|
if (empty($customerIds)) {
|
|
return $this->toData('500', lang('account_registration_failed'));
|
|
}
|
|
$counterKey = 'counter_of_bind_customer:'.$agentId;
|
|
$counterIndex = Cache::store('redis')->get($counterKey); //客服绑定计数器索引
|
|
if (empty($counterIndex)) {
|
|
Cache::store('redis')->set($counterKey, 1);
|
|
$tagCustomerId = $customerIds[0];
|
|
} else {
|
|
Cache::store('redis')->inc($counterKey); //更新计数器索引
|
|
$tagIndex = $counterIndex % count($customerIds);
|
|
$tagCustomerId = $customerIds[$tagIndex];
|
|
}
|
|
if ($tagCustomerId > 0) {
|
|
$regUser->customer_id = $tagCustomerId;
|
|
$regUser->save();
|
|
}
|
|
// 将注册账号的chat_id与绑定客服的chat_id加为好友
|
|
$customerChatInfo = UserChatLinkModel::where(['user_id'=>$tagCustomerId,'user_type'=>UserChatLinkModel::USER_CHAT_LINK_USER_TYPE_ADMIN])->find(); //查询客服的聊天账号uuid
|
|
if (empty($customerChatInfo)) {
|
|
return $this->toData('100400', 'The customer uuid is error.', []);
|
|
}
|
|
$chatFriendsData = [
|
|
'UserUuid' => $chatRes['data']['uuid'],
|
|
'CustomerUuid' => $customerChatInfo->chat_uuid,
|
|
];
|
|
$chatFriendsUrl = env('CHAT_SERVER.BASE_URL') . '/api/eachOtherFriends';
|
|
$chatFriendsRes = (new \app\utility\RequestChatServer())->ReqChatServer($chatFriendsUrl, $chatFriendsData);
|
|
Log::info("手机号注册 - chat服务加好友结果:".json_encode($chatFriendsRes));
|
|
// 将当前用户加入到代理创建的群聊中 【需要查出当前用户的代理创建的群聊信息】
|
|
$agentGroup = UserChatGroupModel::where(['user_id'=>$agentId,'remark'=>UserChatGroupModel::USER_CHAT_GROUP_REMARK_ADMIN_INIT])->find();
|
|
if (empty($agentGroup)) {
|
|
return $this->toData('500', lang('account_registration_failed'));
|
|
}
|
|
$joinChatGroupUrl = env('CHAT_SERVER.BASE_URL') . '/api/group/join/'.$chatRes['data']['uuid'].'/'.$agentGroup->group_uuid;
|
|
$joinChatGroupRes = (new \app\utility\RequestChatServer())->ReqChatServer($joinChatGroupUrl, []);
|
|
Log::info("手机号注册 - 用户与客服加好友结果:".json_encode($joinChatGroupRes));
|
|
}
|
|
});
|
|
|
|
// 删除缓存
|
|
$this->delCache($smsKey);
|
|
|
|
// 更新注册个数
|
|
$this->updateHadRegisterNumCache($ipCanRegisterNumPerIpPerDay);
|
|
|
|
return $this->toData('0', lang('account_registration_successful'));
|
|
} catch (ValidateException $validateException) {
|
|
// 参数校验失败 异常类
|
|
$message = $validateException->getError();
|
|
return $this->toData('100400', $message);
|
|
} catch (\Exception $exception) {
|
|
return $this->toData('500', lang('system_busy'), [$exception->getMessage(), $exception->getTrace()]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @desc 短信验证码登陆
|
|
* @param array $param
|
|
* @return array
|
|
* @throws InvalidArgumentException
|
|
*/
|
|
public function loginSms(array $param): array
|
|
{
|
|
try {
|
|
// 邮件注册参数校验
|
|
validate(LoginValidate::class)->scene('smsLogin')->check($param);
|
|
|
|
$param['nation'] = trim($param['nation']);
|
|
$param['phone'] = trim($param['phone']);
|
|
|
|
// 验证短信验证码
|
|
$mobile = $param['nation'] . $param['phone'];
|
|
$smsKey = 'DB:USER:UNLOGIN:SMS_CODE:' . $mobile;
|
|
if ($param['sms_code'] != 8888) {
|
|
if (!$this->checkCode($smsKey, $param['sms_code'])) {
|
|
return $this->toData('500', lang('incorrect_verification_code'));
|
|
};
|
|
}
|
|
|
|
// 查找手机号
|
|
$userId = UserModel::getUserIdByNationAndPhone($param['nation'], $param['phone']);
|
|
if ($userId <= 0) {
|
|
return $this->toData('500', lang('user_does_not_exist'));
|
|
}
|
|
|
|
// 查找用户信息
|
|
$user = UserModel::getFieldsByUserId('invite_code,user_id,user_no,nick_name,is_real,login_password,salt', $userId);
|
|
if (empty($user)) {
|
|
return $this->toData('500', lang('user_does_not_exist'));
|
|
}
|
|
|
|
// 生成token
|
|
$token = (new Jwt())->getToken($userId, env('ENCRYPT.SALT'));
|
|
if (empty($token)) {
|
|
return $this->toData('500', lang('system_busy'));
|
|
}
|
|
|
|
// 用户登陆之后需要进行的操作 异步完成
|
|
Queue::push('app\home\job\LoginDone', ['userId' => $userId,
|
|
'fields' => [
|
|
'last_login_time' => date('Y-m-d H:i:s'),
|
|
'ip' => $this->getClientRealIp(),
|
|
'device' => $param['device'],
|
|
]
|
|
], 'loginDone');
|
|
|
|
// 将token存致缓存 覆盖新的缓存 实现单设备登陆
|
|
$this->setUserTokenCache($token, $userId);
|
|
|
|
// 用户登记关系
|
|
(new UserService())->getUserLevel($userId);
|
|
|
|
// 返回结果以及用户信息
|
|
return $this->toData('0', 'Request successful.', [
|
|
'userId' => $userId,
|
|
'userNo' => $user['user_no'],
|
|
'nickName' => $user['nick_name'],
|
|
'isReal' => $user['is_real'],
|
|
'inviteCode' => $user['invite_code'],
|
|
'logo' => env('USER.DEFAULT_HEAD_IMG_PATH'),
|
|
'token' => $token,
|
|
]);
|
|
} catch (ValidateException $validateException) {
|
|
// 参数校验失败 异常类
|
|
$message = $validateException->getError();
|
|
return $this->toData('100400', $message);
|
|
} catch (\Exception $exception) {
|
|
return $this->toData('500', lang('system_busy'), [$exception->getMessage(), $exception->getTrace()]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @desc 忘记密码 根据邮箱设置密码
|
|
* @param array $param
|
|
* @return array
|
|
* @throws InvalidArgumentException
|
|
*/
|
|
public function resetPasswordByEmail(array $param): array
|
|
{
|
|
try {
|
|
// 邮件注册参数校验
|
|
validate(LoginValidate::class)->scene('emailForget')->check($param);
|
|
|
|
// 验证码
|
|
$emailKey = 'DB:USER:UNLOGIN:EMAIL_CODE:' . $param['email'];
|
|
if (!$this->checkCode($emailKey, $param['email_code'])) {
|
|
return $this->toData('500', lang('incorrect_verification_code'));
|
|
}
|
|
|
|
// 查找邮箱
|
|
$userId = UserModel::getUserIdByEmail($param['email']);
|
|
if ($userId <= 0) {
|
|
return $this->toData('500', lang('user_does_not_exist'));
|
|
}
|
|
|
|
// 修改密码
|
|
$salt = env('ENCRYPT.SALT');
|
|
$password = (new UnqId())->encryptPassword($param['password'], $salt);
|
|
UserModel::updatePassword($password, $salt, $userId);
|
|
|
|
// 删除缓存
|
|
$this->delCache($emailKey);
|
|
|
|
return $this->toData('0', 'Modification successful.', []);
|
|
} catch (ValidateException $validateException) {
|
|
// 参数校验失败 异常类
|
|
$message = $validateException->getError();
|
|
return $this->toData('100400', $message);
|
|
} catch (\Exception $exception) {
|
|
return $this->toData('100500', lang('system_busy'), [$exception->getMessage(), $exception->getTrace()]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @desc 忘记密码 根据短信设置密码
|
|
* @param array $param
|
|
* @return array
|
|
* @throws InvalidArgumentException
|
|
*/
|
|
public function resetPasswordBySms(array $param): array
|
|
{
|
|
try {
|
|
// 邮件注册参数校验
|
|
validate(LoginValidate::class)->scene('smsForget')->check($param);
|
|
|
|
// 判断验证码
|
|
$mobile = $param['nation'] . $param['phone'];
|
|
$smsKey = 'DB:USER:UNLOGIN:SMS_CODE:' . $mobile;
|
|
if (!$this->checkCode($smsKey, $param['sms_code'])) {
|
|
return $this->toData('500', lang('incorrect_verification_code'));
|
|
}
|
|
|
|
// 查找用户
|
|
$userId = UserModel::getUserIdByNationAndPhone($param['nation'], $param['phone']);
|
|
if ($userId <= 0) {
|
|
return $this->toData('500', lang('user_does_not_exist'));
|
|
}
|
|
|
|
// 修改密码
|
|
$salt = env('ENCRYPT.SALT');
|
|
$password = (new UnqId())->encryptPassword($param['password'], $salt);
|
|
UserModel::updatePassword($password, $salt, $userId);
|
|
|
|
// 删除缓存
|
|
$this->delCache($smsKey);
|
|
|
|
return $this->toData('0', 'Modification successful.', []);
|
|
} catch (ValidateException $validateException) {
|
|
// 参数校验失败 异常类
|
|
$message = $validateException->getError();
|
|
return $this->toData('100400', $message);
|
|
} catch (\Exception $exception) {
|
|
return $this->toData('500', lang('system_busy'), [$exception->getMessage(), $exception->getTrace()]);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @desc 手机号 密码登陆
|
|
* @param $param
|
|
* @return array
|
|
*/
|
|
public function phoneLogin($param): array
|
|
{
|
|
try {
|
|
// 参数校验
|
|
validate(LoginValidate::class)->scene('smsPasswordLogin')->check($param);
|
|
|
|
// 获取用户
|
|
$userId = UserModel::getUserIdByNationAndPhone($param['nation'], $param['phone']);
|
|
if ($userId <= 0) {
|
|
return $this->toData('500', lang('user_does_not_exist'));
|
|
}
|
|
|
|
$info = UserModel::getFieldsByUserId('invite_code,is_real,nick_name,user_no,user_id,login_password,salt', $userId);
|
|
if (empty($info)) {
|
|
return $this->toData('500', lang('user_does_not_exist'));
|
|
}
|
|
|
|
// 校验密码
|
|
$checkPasswordBool = (new UnqId())->checkPassword($param['password'], $info['login_password'], $info['salt']);
|
|
if (!$checkPasswordBool) {
|
|
return $this->toData('500', lang('incorrect_account_or_password'));
|
|
}
|
|
|
|
// 生成token
|
|
$token = (new Jwt())->getToken($userId, env('ENCRYPT.SALT'));
|
|
if (empty($token)) {
|
|
return $this->toData('500', lang('system_busy'));
|
|
}
|
|
|
|
// 用户登陆之后需要进行的操作 异步完成
|
|
Queue::push('app\home\job\LoginDone', ['userId' => $userId,
|
|
'fields' => [
|
|
'last_login_time' => date('Y-m-d H:i:s'),
|
|
'ip' => $this->getClientRealIp(),
|
|
'device' => $param['device'],
|
|
]
|
|
], 'loginDone');
|
|
|
|
// 将token存致缓存 覆盖新的缓存 实现单设备登陆
|
|
$this->setUserTokenCache($token, $userId);
|
|
|
|
// 用户登记关系
|
|
(new UserService())->getUserLevel($userId);
|
|
|
|
// 返回结果以及用户信息
|
|
return $this->toData('0', 'Request successful.', [
|
|
'userId' => $userId,
|
|
'userNo' => $info['user_no'],
|
|
'nickName' => $info['nick_name'],
|
|
'isReal' => $info['is_real'],
|
|
'inviteCode' => $info['invite_code'],
|
|
'logo' => env('USER.DEFAULT_HEAD_IMG_PATH'),
|
|
'token' => $token,
|
|
]);
|
|
} catch (ValidateException $validateException) {
|
|
// 参数校验失败 异常类
|
|
$message = $validateException->getError();
|
|
return $this->toData('100400', $message);
|
|
} catch (\Exception $exception) {
|
|
return $this->toData('500', lang('system_busy'));
|
|
}
|
|
}
|
|
|
|
public function autoLogin($param): array
|
|
{
|
|
try {
|
|
$login_token=$param['login_token'];
|
|
$tokenUserKey = 'AUTO:TOKEN:'.$login_token;
|
|
$userId = Cache::store('redis')->get($tokenUserKey);
|
|
if(empty($userId) || $userId <= 0){
|
|
return $this->toData('500', 'Incorrect token', []);
|
|
}
|
|
$info = UserModel::getFieldsByUserId('invite_code,is_real,nick_name,user_no,user_id,login_password,salt', $userId);
|
|
if (empty($info)) {
|
|
return $this->toData('500', lang('user_does_not_exist'));
|
|
}
|
|
// 生成token
|
|
$token = (new Jwt())->getToken($userId, env('ENCRYPT.SALT'));
|
|
if (empty($token)) {
|
|
return $this->toData('500', lang('system_busy'), []);
|
|
}
|
|
|
|
// 将token存致缓存 覆盖新的缓存 实现单设备登陆
|
|
$this->setUserTokenCache($token, $userId);
|
|
|
|
// 用户登记关系
|
|
(new UserService())->getUserLevel($userId);
|
|
|
|
// 返回结果以及用户信息
|
|
return $this->toData('0', 'Request successful.', [
|
|
'userId' => $userId,
|
|
'userNo' => $info['user_no'],
|
|
'nickName' => $info['nick_name'],
|
|
'isReal' => $info['is_real'],
|
|
'inviteCode' => $info['invite_code'],
|
|
'logo' => env('USER.DEFAULT_HEAD_IMG_PATH'),
|
|
'token' => $token,
|
|
]);
|
|
} catch (ValidateException $validateException) {
|
|
// 参数校验失败 异常类
|
|
$message = $validateException->getError();
|
|
return $this->toData('100400', $message);
|
|
} catch (\Exception $exception) {
|
|
return $this->toData('500', lang('system_busy'));
|
|
}
|
|
|
|
}
|
|
public function testLogin($param): array
|
|
{
|
|
try {
|
|
$login_token=$param['login_token'];
|
|
$tokenUserKey = 'AUTO:TOKEN:'.$login_token;
|
|
$userId = Cache::store('redis')->get($tokenUserKey);
|
|
if(empty($userId) || $userId <= 0){
|
|
return $this->toData('100300', 'Incorrect token', []);
|
|
}
|
|
$info = UserModel::getFieldsByUserId('invite_code,is_real,nick_name,user_no,user_id,login_password,salt', $userId);
|
|
if (empty($info)) {
|
|
return $this->toData('100300', 'Incorrect account or password.2', []);
|
|
}
|
|
// 生成token
|
|
$token = (new Jwt())->getToken($userId, env('ENCRYPT.SALT'));
|
|
if (empty($token)) {
|
|
return $this->toData('500', lang('system_busy'));
|
|
}
|
|
|
|
// 将token存致缓存 覆盖新的缓存 实现单设备登陆
|
|
$this->setUserTokenCache($token, $userId);
|
|
|
|
// 用户登记关系
|
|
(new UserService())->getUserLevel($userId);
|
|
|
|
// 返回结果以及用户信息
|
|
return $this->toData('0', 'Request successful.', [
|
|
'userId' => $userId,
|
|
'userNo' => $info['user_no'],
|
|
'nickName' => $info['nick_name'],
|
|
'isReal' => $info['is_real'],
|
|
'inviteCode' => $info['invite_code'],
|
|
'logo' => env('USER.DEFAULT_HEAD_IMG_PATH'),
|
|
'token' => $token,
|
|
]);
|
|
} catch (ValidateException $validateException) {
|
|
// 参数校验失败 异常类
|
|
$message = $validateException->getError();
|
|
return $this->toData('100400', $message);
|
|
} catch (\Exception $exception) {
|
|
return $this->toData('500', lang('system_busy'), []);
|
|
}
|
|
|
|
}
|
|
public function getIP(): array
|
|
{
|
|
$ip=$this->getClientRealIp();
|
|
$countryDb = base_path().'GeoLite2-Country.mmdb';
|
|
$cityDb = base_path().'GeoLite2-City.mmdb';
|
|
$countryReader = new Reader($countryDb);
|
|
$cityReader = new Reader($cityDb);
|
|
try{
|
|
$countryNames = $countryReader->country($ip)->country->names;
|
|
$cityNames = $cityReader->city($ip)->city->names;
|
|
return $this->toData('0', 'Request successful.', [
|
|
'country' => $countryNames['zh-CN'],
|
|
'city' => $cityNames['zh-CN'],
|
|
'ip'=>$ip
|
|
]);
|
|
}catch (\Exception $exception){
|
|
return $this->toData('0', 'Request successful.', [
|
|
'country' => '未知',
|
|
'city' =>'未知',
|
|
'ip'=>$ip
|
|
]);
|
|
}
|
|
}
|
|
}
|