智慧教务系统
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.
 
 
 
 
 
 

383 lines
13 KiB

<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址:https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace app\service\api\login;
use app\dict\member\MemberLoginTypeDict;
use app\dict\member\MemberRegisterTypeDict;
use app\dict\sys\AppTypeDict;
use app\dict\sys\SmsDict;
use app\model\campus_person_role\CampusPersonRole;
use app\model\member\Member;
use app\model\personnel\Personnel;
use app\model\sys\SysUser;
use app\service\api\captcha\CaptchaService;
use app\service\api\member\MemberConfigService;
use app\service\api\member\MemberService;
use app\service\api\notice\NoticeService;
use core\base\BaseApiService;
use core\exception\ApiException;
use core\exception\AuthException;
use core\util\TokenAuth;
use Exception;
use think\facade\Cache;
use think\facade\Log;
use Throwable;
/**
* 登录服务层
* Class LoginService
* @package app\service\api\login
*/
class LoginService extends BaseApiService
{
public function __construct()
{
parent::__construct();
$this->model = new Member();
}
/**
* 会员注册
* @param $data
*/
public function register($data)
{
//检测设置是否自动注册
//自动注册检测授权信息
//注册登录
}
/**
* 登录操作
* @param Member $member_info
* @param string $login_type
* @return array
*/
public function login(Member $member_info, string $login_type)
{
//绑定第三方授权
$this->bingOpenid($member_info);
if (!$member_info->status) throw new ApiException('MEMBER_LOCK');
$member_info->login_time = time();
$member_info->login_ip = $this->request->ip();
$member_info->login_channel = $this->channel;
$member_info->login_type = $login_type;
$member_info->login_count++;
$member_info->last_visit_time = time();
$member_info->save();
$token_info = $this->createToken($member_info);
event("MemberLogin", $member_info);
return [
'token' => $token_info['token'],
'expires_time' => $token_info['params']['exp'],
'mobile' => $member_info->mobile
];
}
/**
* 账号登录
* @param string $username
* @param string $password
* @return array|false
*/
public function account(string $username, string $password)
{
$member_service = new MemberService();
$member_info = $member_service->findMemberInfo(['username|mobile' => $username]);
if ($member_info->isEmpty()) throw new AuthException('MEMBER_NOT_EXIST');//账号不存在
if (!check_password($password, $member_info->password)) return false;//密码与账号不匹配
return $this->login($member_info, MemberLoginTypeDict::USERNAME);
}
/**
* 手机号登录
* @param array $params
* @return array
*/
public function mobile($params)
{
//校验手机验证码
$this->checkMobileCode($params['mobile']);
//登录注册配置
$config = (new MemberConfigService())->getLoginConfig();
$is_mobile = $config['is_mobile'];
$is_bind_mobile = $config['is_bind_mobile'];
if ($is_mobile != 1 && $is_bind_mobile != 1) throw new AuthException('MOBILE_LOGIN_UNOPENED');
$member_service = new MemberService();
$member_info = $member_service->findMemberInfo(['mobile' => $params['mobile']]);
if ($member_info->isEmpty()) {
//开启强制绑定手机号,登录会自动注册并绑定手机号
if ($is_bind_mobile == 1) {
$data = array(
'mobile' => $params['mobile'],
'nickname' => $params['nickname'],
'headimg' => $params['headimg']
);
return (new RegisterService())->register($params['mobile'], $data, MemberRegisterTypeDict::MOBILE, false);
} else {
throw new AuthException('MEMBER_NOT_EXIST');//账号不存在
}
}
return $this->login($member_info, MemberLoginTypeDict::MOBILE);
}
/**
* 生成token
* @param $member_info
* @return array|null
*/
public function createToken($member_info): ?array
{
$expire_time = env('system.api_token_expire_time') ?? 3600;//todo 不一定和管理端合用这个token时限
return TokenAuth::createToken($member_info->member_id, AppTypeDict::API, ['member_id' => $member_info->member_id, 'username' => $member_info->username], $expire_time);
}
/**
* 登陆退出(当前账户)
*/
public function logout(): ?bool
{
self::clearToken($this->member_id, $this->request->apiToken());
return true;
}
/**
* 清理token
* @param int $member_id
* @param string|null $token
* @return bool|null
*/
public static function clearToken(int $member_id, ?string $token = ''): ?bool
{
TokenAuth::clearToken($member_id, AppTypeDict::API, $token);
return true;
}
/**
* 解析客户端token
* @param string|null $token
* @return array
*/
public function parseToken(?string $token)
{
if (empty($token)) {
//定义专属于授权认证机制的错误响应, 定义专属语言包
throw new AuthException('MUST_LOGIN', 401);
}
try {
$token_info = TokenAuth::parseToken($token, AppTypeDict::API);
dd($token_info,$token,AppTypeDict::PERSONNEL);
} catch (Throwable $e) {
// if(env('app_debug', false)){
// throw new AuthException($e->getMessage(), 401);
// }else{
throw new AuthException('LOGIN_EXPIRE', 401);
// }
}
if (!$token_info) {
throw new AuthException('MUST_LOGIN', 401);
}
//验证有效次数或过期时间
return $token_info;
}
/**
* 解析员工端token
* @param string|null $token
* @return array
*/
public function parsePersonnelToken(?string $token)
{
if (empty($token)) {
//定义专属于授权认证机制的错误响应, 定义专属语言包
throw new AuthException('MUST_LOGIN', 401);
}
try {
$token_info = TokenAuth::parseToken($token, AppTypeDict::PERSONNEL);
} catch (Throwable $e) {
throw new AuthException('LOGIN_EXPIRE', 401);
}
if (!$token_info) {
throw new AuthException('MUST_LOGIN', 401);
}
//验证有效次数或过期时间
return $token_info;
}
/**
* 手机发送验证码
* @param $mobile
* @param string $type 发送短信的业务场景
* @return array
* @throws Exception
*/
public function sendMobileCode($mobile, string $type = '')
{
(new CaptchaService())->check();
if (empty($mobile)) throw new AuthException('MOBILE_NEEDED');
//发送
if (!in_array($type, SmsDict::SCENE_TYPE)) throw new AuthException('MEMBER_MOBILE_CAPTCHA_ERROR');
$code = str_pad(random_int(1, 9999), 4, 0, STR_PAD_LEFT);// 生成4位随机数,左侧补0
(new NoticeService())->send('member_verify_code', ['code' => $code, 'mobile' => $mobile]);
//将验证码存入缓存
$key = md5(uniqid('', true));
$cache_tag_name = "mobile_key" . $mobile . $type;
$this->clearMobileCode($mobile, $type);
Cache::tag($cache_tag_name)->set($key, ['mobile' => $mobile, 'code' => $code, 'type' => $type], 600);
return ['key' => $key];
}
public function getMobileCodeCacheName()
{
}
public function clearMobileCode($mobile, $type)
{
$cache_tag_name = "mobile_key" . $mobile . $type;
Cache::tag($cache_tag_name)->clear();
}
/**
* 校验手机验证码
* @param string $mobile
* @return true
*/
public function checkMobileCode(string $mobile)
{
if (empty($mobile)) throw new AuthException('MOBILE_NEEDED');
$mobile_key = request()->param('mobile_key', '');
$mobile_code = request()->param('mobile_code', '');
if (empty($mobile_key) || empty($mobile_code)) throw new AuthException('MOBILE_CAPTCHA_ERROR');
$cache = Cache::get($mobile_key);
if (empty($cache)) throw new AuthException('MOBILE_CAPTCHA_ERROR');
$temp_mobile = $cache['mobile'];
$temp_code = $cache['code'];
$temp_type = $cache['type'];
if ($temp_mobile != $mobile || $temp_code != $mobile_code) throw new AuthException('MOBILE_CAPTCHA_ERROR');
$this->clearMobileCode($temp_mobile, $temp_type);
return true;
}
/**
* 绑定openid
* @param $member
* @return true
*/
public function bingOpenid($member)
{
$open_id = $this->request->param('openid');
if (!empty($open_id)) {
Log::write('channel_1' . $this->channel);
if (!empty($this->channel)) {
$openid_field = match ($this->channel) {
'wechat' => 'wx_openid',
'weapp' => 'weapp_openid',
default => ''
};
if (!empty($openid_field)) {
if (!$member->isEmpty()) {
if (empty($member->$openid_field)) {
//todo 定义当前第三方授权方没有退出登录功能,故这儿不做openid是否存在账号验证
// $member_service = new MemberService();
// $open_member = $member_service->findMemberInfo([$openid_field => $open_id]);
$member->$openid_field = $open_id;
$member->save();
} else {
if ($member->$openid_field != $open_id) {
throw new AuthException('MEMBER_IS_BIND_AUTH');
}
}
}
}
}
}
return true;
}
/**
* 重置密码
* @param string $mobile
* @param string $password
*/
public function resetPassword(string $mobile, string $password)
{
$member_service = new MemberService();
//校验手机验证码
$this->checkMobileCode($mobile);
$member_info = $member_service->findMemberInfo(['mobile' => $mobile]);
if ($member_info->isEmpty()) throw new AuthException('MOBILE_NOT_EXIST_MEMBER');//账号不存在
//todo 需要考虑一下,新的密码和原密码一样能否通过验证
$password_hash = create_password($password);
$data = array(
'password' => $password_hash,
);
$member_service->editByFind($member_info, $data);
self::clearToken($member_info['member_id'], $this->request->apiToken());
return true;
}
public function loginScanCode()
{
}
public function loginByScanCode()
{
}
public function checkScanCode()
{
}
//员工登陆
public function loginByPersonnel($params)
{
//查询员工信息
$member_info = (new Personnel())->where('phone', $params['phone'])->find();
if (!$member_info) {
throw new ApiException('账号不存在');
}
if ($member_info->status != 2) throw new ApiException('账号状态异常禁止登录');
$user = (new SysUser())->where('username', $params['phone'])->find();
//create_password($params['password'])//创建密码
if (!check_password($params['password'], $user->password)) throw new ApiException('账号或密码错误');
$user->login_time = time();
$user->last_ip = $this->request->ip();
$user->login_type = 'mini';
$user->login_count++;
$user->last_time = time();
$user->save();
$expire_time = env('system.api_token_expire_time') ?? 3600;
//生成token
$token_info = TokenAuth::createToken($member_info->id, AppTypeDict::PERSONNEL, ['id' => $member_info->id, 'member_id' => $member_info->id, 'phone' => $member_info->phone, 'user_type' => $params['login_type']], $expire_time);
//获取用户部门ids(1市场、2教师、3销售)
$depts = (new CampusPersonRole())->where('person_id', $member_info->id)->column('dept_id');
$user_type = in_array($params['login_type'], $depts) ? $params['login_type'] : '';
return [
'token' => $token_info['token'],//token
'expires_time' => $token_info['params']['exp'],//过期时间
'user_type' => $user_type,//用户类型
'userinfo' => $member_info,
];
}
}