diff --git a/application/admin/controller/Third.php b/application/admin/controller/Third.php new file mode 100644 index 0000000..ac7dc77 --- /dev/null +++ b/application/admin/controller/Third.php @@ -0,0 +1,66 @@ +model = new \app\admin\model\Third; + } + + /** + * 查看 + */ + public function index() + { + $this->relationSearch = true; + //设置过滤方法 + $this->request->filter(['strip_tags']); + if ($this->request->isAjax()) { + //如果发送的来源是Selectpage,则转发到Selectpage + if ($this->request->request('keyField')) { + return $this->selectpage(); + } + list($where, $sort, $order, $offset, $limit) = $this->buildparams(); + $total = $this->model + ->with(['user']) + ->where($where) + ->order($sort, $order) + ->count(); + + $list = $this->model + ->with(['user']) + ->where($where) + ->order($sort, $order) + ->limit($offset, $limit) + ->select(); + foreach ($list as $index => $item) { + if ($item->user) { + $item->user->visible(['nickname']); + } + } + $list = collection($list)->toArray(); + $result = array("total" => $total, "rows" => $list); + + return json($result); + } + return $this->view->fetch(); + } + +} diff --git a/application/admin/lang/zh-cn/third.php b/application/admin/lang/zh-cn/third.php new file mode 100644 index 0000000..a3aa09c --- /dev/null +++ b/application/admin/lang/zh-cn/third.php @@ -0,0 +1,17 @@ + 'ID', + 'User_id' => '会员ID', + 'Platform' => '平台', + 'Apptype' => '类型', + 'Unionid' => 'UnionID', + 'Openid' => 'OpenID', + 'Openname' => '第三方会员昵称', + 'Access_token' => 'AccessToken', + 'Expires_in' => '有效期', + 'Createtime' => '创建时间', + 'Updatetime' => '更新时间', + 'Logintime' => '登录时间', + 'Expiretime' => '过期时间' +]; diff --git a/application/admin/model/Third.php b/application/admin/model/Third.php new file mode 100644 index 0000000..66d3be0 --- /dev/null +++ b/application/admin/model/Third.php @@ -0,0 +1,56 @@ +belongsTo("User", 'user_id', 'id')->setEagerlyType(0); + } +} diff --git a/application/admin/validate/Third.php b/application/admin/validate/Third.php new file mode 100644 index 0000000..f9766b4 --- /dev/null +++ b/application/admin/validate/Third.php @@ -0,0 +1,26 @@ + [], + 'edit' => [], + ]; +} diff --git a/application/admin/view/third/index.html b/application/admin/view/third/index.html new file mode 100644 index 0000000..6fc9d97 --- /dev/null +++ b/application/admin/view/third/index.html @@ -0,0 +1,23 @@ +
+ {:build_heading()} + +
+
+
+
+ + +
+
+
+ +
+
+
diff --git a/application/api/controller/Index.php b/application/api/controller/Index.php index 24c615e..3fac732 100644 --- a/application/api/controller/Index.php +++ b/application/api/controller/Index.php @@ -20,4 +20,25 @@ class Index extends Api { $this->success('请求成功'); } + + + + + /** + * @ApiTitle(init接口,小程序初始化参数接口) + * @ApiSummary(小程序初始化参数接口) + * @ApiRoute(/api/index/init) + * @ApiMethod(GET) + * @ApiReturn({ + "code" => 1, + "msg" => "获取成功", + "data" => {} + *}) + */ + public function init() { + + $this->success('',[]); + } + + } diff --git a/application/api/controller/User.php b/application/api/controller/User.php index a46a8fb..d379570 100644 --- a/application/api/controller/User.php +++ b/application/api/controller/User.php @@ -7,7 +7,10 @@ use app\common\controller\Api; use app\common\library\Ems; use app\common\library\Sms; use fast\Random; +use think\Cache; use think\Config; +use think\Db; +use think\Log; use think\Validate; use app\admin\library\Wechat; @@ -16,7 +19,7 @@ use app\admin\library\Wechat; */ class User extends Api { - protected $noNeedLogin = ['login', 'mobilelogin', 'register', 'resetpwd', 'changeemail', 'changemobile', 'third']; + protected $noNeedLogin = ["registerLogin",'getOpenid','decodeData','login', 'mobilelogin', 'register', 'resetpwd', 'changeemail', 'changemobile', 'third']; protected $noNeedRight = '*'; protected $miniConfig; @@ -25,6 +28,9 @@ class User extends Api { $this->miniConfig = (new Wechat)->getMiniConfig(); parent::_initialize(); + if (!Config::get('fastadmin.usercenter')) { + $this->error(__('User center already closed')); + } } @@ -33,7 +39,7 @@ class User extends Api /** * @ApiTitle(获取小程序openid信息) * @ApiSummary(根据前端code换取openid信息) - * @ApiRoute(/getOpenid) + * @ApiRoute(/api/user/getOpenid) * @ApiMethod(GET) * @ApiParams(name="code",type="string",required=true,description="前端code值") * @ApiHeaders(name = "brand-key", type = "string",require = true, description = "应用key") @@ -51,8 +57,6 @@ class User extends Api public function getOpenid(){ $code = $this->request->param('code/s'); - - try { $result = LoginService::getInstance(['mini_config' => $this->miniConfig])->getOpenid($code); } catch (\WeChat\Exceptions\LocalCacheException $e){ @@ -60,13 +64,14 @@ class User extends Api } catch (\Exception $e){ $this->error($e->getMessage()); } + Cache::set('wechat_mini_code'.$code.$result['openid'],"1",60); $this->success('',$result); } /** * @ApiTitle(加密信息解密) * @ApiSummary(解密微信信息) - * @ApiRoute(/decodeData) + * @ApiRoute(/api/user/decodeData) * @ApiMethod(POST) * @ApiParams(name = "iv", type = "string",required=true) * @ApiParams(name = "encryptedData", type = "string",required=true) @@ -92,24 +97,71 @@ class User extends Api - - - public function _initialize() - { - parent::_initialize(); - - if (!Config::get('fastadmin.usercenter')) { - $this->error(__('User center already closed')); + /** + * @ApiTitle(微信小程序授权登录注册通用[支持静默登录]) + * @ApiSummary(登录注册通用-支持静默登录) + * @ApiRoute(/api/user/registerLogin) + * @ApiMethod(POST) + * @ApiParams(name = "mobile", type = "string",required=false,description = "mobile 静默非必传,手机授权必传") + * @ApiParams(name = "unionid", type = "string",required=false, description = "unionid 非必传") + * @ApiParams(name = "apptype", type = "string",required=false, description = "应用类型默认miniapp") + * @ApiParams(name = "platform", type = "string",require=false, description = "平台标识默认wechat") + * @ApiParams(name = "openid", type = "string", require=true, description = "授权的openid") + * @ApiParams(name = "keeptime", type = "string", require=false, description = "token保持时间,默认0永久") + * @ApiParams(name = "code", type = "string", require=true, description = "授权的code") + * @ApiReturn({ + "code" => 1, + "msg" => "获取成功", + "data" => {‘token’:xxxxx,'errcode':0(如果为30001则是需要重新授权)} + *}) + */ + public function registerLogin(){ + $extend= $params = []; + $extend['mobile'] = $this->request->param('mobile/s',''); + $params['unionid'] = $this->request->param('unionid/s',''); + $params['apptype'] = $this->request->param('apptype/s','miniapp'); + $params['openid'] = $this->request->param('openid/s',''); + $code = $this->request->param('code/s',''); + if(empty($code)){ + $this->error(__('缺少code')); } + $wechat_mini_code = Cache::get('wechat_mini_code'.$code.$params['openid']); + if(!$wechat_mini_code)$this->error("授权code已过期或已使用,请重新发起授权!",['errcode'=>30002]); + + $platform = $this->request->param('platform/s','wechat'); + //推荐人:逻辑未实现 + $rec_user_id = $this->request->param('rec_user_id',0,'xilufitness_get_id_value'); + $keeptime = $this->request->param('keeptime/d',0); + //access_token + + try { + \addons\third\library\Service::loginAndRegisterByMobile($platform, $params, $extend, $keeptime); + } catch (\Exception $e){ + Log::log($e->getMessage()); + $this->error($e->getMessage(),['errcode'=>$e->getCode()]); + } + Cache::rm('wechat_mini_code'.$code.$params['openid']); + $this->success('获取成功', ['token' => $this->auth->getToken()]); } + + + + + + /** * 会员中心 */ public function index() { - $this->success('', ['welcome' => $this->auth->nickname]); + $data = [ + 'welcome' => $this->auth->nickname, + 'user_info'=>$this->auth->getUserinfo() + ]; + $data['user_info']["avatar"] = $data['user_info']["avatar"]? cdnurl($data['user_info']["avatar"],true):$data['user_info']["avatar"]; + $this->success('调用成功',$data); } /** @@ -230,34 +282,56 @@ class User extends Api * 修改会员个人信息 * * @ApiMethod (POST) + * @ApiParams (name="update_fields", type="json", required=true, description="本次需要更新的用户字段json格式:更新谁传谁的字段名,比如:只更新头像和昵称 则 json=['avatar','nickname']") * @ApiParams (name="avatar", type="string", required=true, description="头像地址") * @ApiParams (name="username", type="string", required=true, description="用户名") * @ApiParams (name="nickname", type="string", required=true, description="昵称") + * @ApiParams (name="realname", type="string", required=true, description="姓名") + * @ApiParams (name="gender", type="string", required=true, description="性别:0女 1男") + * @ApiParams (name="birthday", type="string", required=true, description="出生日期:日期字符串 比如”1995-9-20") + * @ApiParams (name="work", type="string", required=true, description="职业") * @ApiParams (name="bio", type="string", required=true, description="个人简介") */ public function profile() { $user = $this->auth->getUser(); - $username = $this->request->post('username'); - $nickname = $this->request->post('nickname'); - $bio = $this->request->post('bio'); - $avatar = $this->request->post('avatar', '', 'trim,strip_tags,htmlspecialchars'); - if ($username) { + $update_fields = $this->request->post('update_fields/a',[]); +// var_dump($update_fields);die; + + if(!$update_fields)$this->error(__('请指定要更新的字段!')); + $username = $this->request->post('username/s'); + $nickname = $this->request->post('nickname/s'); + $realname = $this->request->post('realname/s'); + $gender = $this->request->post('gender/d'); + $birthday = $this->request->post('birthday/s'); + $work = $this->request->post('work/s'); + $bio = $this->request->post('bio/s',null); + $avatar = $this->request->post('avatar', null, 'trim,strip_tags,htmlspecialchars'); + + + if ($username && in_array('username', $update_fields)) { $exists = \app\common\model\User::where('username', $username)->where('id', '<>', $this->auth->id)->find(); if ($exists) { $this->error(__('Username already exists')); } $user->username = $username; } - if ($nickname) { + if ($nickname && in_array('nickname', $update_fields)) { $exists = \app\common\model\User::where('nickname', $nickname)->where('id', '<>', $this->auth->id)->find(); if ($exists) { $this->error(__('Nickname already exists')); } $user->nickname = $nickname; } - $user->bio = $bio; - $user->avatar = $avatar; + if(in_array('bio', $update_fields))$user->bio = $bio; + if($avatar!==null && in_array('avatar', $update_fields))$user->avatar = $avatar; + if(in_array('realname', $update_fields))$user->realname = $realname; + if(in_array('gender', $update_fields)){ + if(!in_array($gender, [1,2]))$this->error(__('请输入正确的性别!')); + $user->gender = $gender; + } + if(in_array('birthday', $update_fields))$user->birthday = $birthday; + if(in_array('work', $update_fields))$user->work = $work; $user->save(); $this->success(); } diff --git a/application/common/library/Auth.php b/application/common/library/Auth.php index f3ed90f..cea4d32 100644 --- a/application/common/library/Auth.php +++ b/application/common/library/Auth.php @@ -26,7 +26,7 @@ class Auth //默认配置 protected $config = []; protected $options = []; - protected $allowFields = ['id', 'username', 'nickname', 'mobile', 'avatar', 'score']; + protected $allowFields = ['id', 'username', 'nickname', 'mobile', 'avatar', 'score', 'realname', 'work', 'gender', 'birthday', 'bio']; public function __construct($options = []) { diff --git a/application/index/controller/Third.php b/application/index/controller/Third.php new file mode 100644 index 0000000..2dd4c7e --- /dev/null +++ b/application/index/controller/Third.php @@ -0,0 +1,200 @@ +app = new Application($config); + + $auth = $this->auth; + //监听注册登录事件 + Hook::add('user_login_successed', function ($user) use ($auth) { + $expire = input('post.keeplogin') ? 30 * 86400 : 0; + Cookie::set('uid', $user->id, $expire); + Cookie::set('token', $auth->getToken(), $expire); + }); + Hook::add('user_register_successed', function ($user) use ($auth) { + Cookie::set('uid', $user->id); + Cookie::set('token', $auth->getToken()); + }); + } + + /** + * 准备绑定 + */ + public function prepare() + { + $platform = $this->request->request('platform', ''); + if (!in_array($platform, ['wechat', 'weibo', 'qq'])) { + $this->error("未找到指定平台"); + } + $url = $this->request->get('url', '/', 'trim,xss_clean'); + + // 授权成功后的回调 + $thirdinfo = Session::get("third-{$platform}"); + if (!$thirdinfo) { + $this->error("操作失败,请返回重试"); + } + + $apptype = Service::getApptype(); + $openid = $thirdinfo['openid'] ?? ''; + $unionid = $thirdinfo['unionid'] ?? ''; + + // 如果是登录状态,直接跳到绑定 + if ($this->auth->id) { + $isBind = Service::isBindThird($platform, $openid, '', $unionid); + if ($isBind) { + $this->error("已经绑定其它账号,无法进行绑定"); + } + $this->redirect(url("index/third/bind") . "?" . http_build_query(['platform' => $platform, 'url' => $url])); + } + + if ($this->request->isPost()) { + $mobile = $this->request->post("mobile"); + $platform = $this->request->post("platform"); + $captcha = $this->request->post("captcha"); + $nickname = $thirdinfo['userinfo']['nickname'] ?? ''; + + if (!Sms::check($mobile, $captcha, 'bind')) { + $this->error(__('验证码不正确')); + } + + $user = \app\common\model\User::where('mobile', $mobile)->find(); + if ($user) { + $isBind = \addons\third\model\Third::where('platform', $platform)->where('user_id', $user['id'])->find(); + if ($isBind) { + $this->error('该手机号已经占用'); + } + + $result = $this->auth->direct($user->id); + } else { + $extend = array_filter(['nickname' => $nickname]); + $result = $this->auth->register($mobile, Random::alnum(), '', $mobile, $extend); + } + // 账号创建成功则添加绑定第三方绑定 + if ($result) { + \addons\third\model\Third::create(['user_id' => $this->auth->id, 'platform' => $platform, 'apptype' => $apptype, 'openid' => $openid, 'unionid' => $unionid, 'openname' => $nickname], true); + } + + //删除临时Session + Session::delete("third-{$platform}"); + + //绑定成功,跳转到之前页面 + $this->success("绑定成功", $url); + } + + $this->view->assign('userinfo', $thirdinfo['userinfo']); + $this->view->assign('platform', $platform); + $this->view->assign('url', $url); + $this->view->assign('bindurl', url("index/third/bind") . '?' . http_build_query(['platform' => $platform, 'url' => $url])); + $this->view->assign('captchaType', config('fastadmin.user_register_captcha')); + $this->view->assign('title', "账号绑定"); + + return $this->view->fetch(); + } + + /** + * 绑定账号 + */ + public function bind() + { + $platform = $this->request->request('platform', ''); + if (!in_array($platform, ['wechat', 'weibo', 'qq'])) { + $this->error("未找到指定平台"); + } + $url = $this->request->get('url', $this->request->server('HTTP_REFERER', '', 'trim'), 'trim'); + if (!$platform) { + $this->error("参数不正确"); + } + + $apptype = $platform == 'wechat' ? Service::getApptype() : ''; + + // 授权成功后的回调 + $thirdinfo = Session::get("third-{$platform}"); + if (!$thirdinfo) { + $this->redirect(addon_url('third/index/connect', [':platform' => $platform]) . '?url=' . urlencode($url)); + } + $third = \addons\third\model\Third::where('user_id', $this->auth->id)->where('platform', $platform)->find(); + if ($third) { + $this->error("已绑定账号,请勿重复绑定"); + } + $time = time(); + $values = [ + 'platform' => $platform, + 'apptype' => $apptype, + 'user_id' => $this->auth->id, + 'unionid' => $thirdinfo['unionid'] ?? '', + 'openid' => $thirdinfo['openid'], + 'openname' => $thirdinfo['userinfo']['nickname'] ?? '', + 'access_token' => $thirdinfo['access_token'], + 'refresh_token' => $thirdinfo['refresh_token'], + 'expires_in' => $thirdinfo['expires_in'], + 'logintime' => $time, + 'expiretime' => $time + $thirdinfo['expires_in'], + ]; + $isBind = Service::isBindThird($platform, $values['openid'], '', $values['unionid']); + if ($isBind) { + $this->error("已经绑定其它账号,无法进行绑定"); + } + $third = \addons\third\model\Third::create($values); + if ($third) { + + //删除临时Session + Session::delete("third-{$platform}"); + + $this->success("账号绑定成功", $url); + } else { + $this->error("账号绑定失败,请重试", $url); + } + } + + /** + * 解绑账号 + */ + public function unbind() + { + $platform = $this->request->request('platform', ''); + if (!in_array($platform, ['wechat', 'weibo', 'qq'])) { + $this->error("未找到指定平台"); + } + $apptype = $platform == 'wechat' ? Service::getApptype() : ''; + + $third = \addons\third\model\Third::where('user_id', $this->auth->id) + ->where('platform', $platform) + ->where(function ($query) use ($platform, $apptype) { + if ($platform == 'wechat') { + $query->where('apptype', $apptype); + } + }) + ->find(); + if (!$third) { + $this->error("未找到指定的账号绑定信息"); + } + Session::delete("third-{$platform}"); + $third->delete(); + $this->success("账号解绑成功"); + } +} diff --git a/application/index/view/third/prepare.html b/application/index/view/third/prepare.html new file mode 100644 index 0000000..e9fe868 --- /dev/null +++ b/application/index/view/third/prepare.html @@ -0,0 +1,76 @@ + +
+ {if isset($userinfo['avatar'])} +
+ +
{$userinfo.nickname|default=''|htmlentities}
+
+ {/if} +
+ +
+
+ + diff --git a/public/assets/js/backend/third.js b/public/assets/js/backend/third.js new file mode 100644 index 0000000..a3c2d70 --- /dev/null +++ b/public/assets/js/backend/third.js @@ -0,0 +1,61 @@ +define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefined, Backend, Table, Form) { + + var Controller = { + index: function () { + // 初始化表格参数配置 + Table.api.init({ + extend: { + index_url: 'third/index' + location.search, + add_url: 'third/add', + edit_url: 'third/edit', + del_url: 'third/del', + multi_url: 'third/multi', + table: 'third', + } + }); + + var table = $("#table"); + + // 初始化表格 + table.bootstrapTable({ + url: $.fn.bootstrapTable.defaults.extend.index_url, + pk: 'id', + sortName: 'id', + fixedColumns: true, + fixedRightNumber: 1, + columns: [ + [ + {checkbox: true}, + {field: 'id', title: __('Id')}, + {field: 'user_id', title: __('User_id'), formatter: Table.api.formatter.search}, + {field: 'user.nickname', title: __('Nickname')}, + {field: 'platform', title: __('Platform'), formatter: Table.api.formatter.flag, searchList: {'wechat': '微信', 'qq': 'QQ', 'weibo': '微博'}, custom: {'wechat': 'success', 'qq': 'info', 'weibo': 'danger'}}, + {field: 'apptype', title: __('Apptype'), formatter: Table.api.formatter.normal, searchList: {'mp': '公众号', 'wxapp': '小程序', 'web': 'PC端'}}, + {field: 'openid', title: __('Openid')}, + {field: 'unionid', title: __('Unionid')}, + {field: 'openname', title: __('Openname'), formatter: Table.api.formatter.search}, + {field: 'createtime', title: __('Createtime'), sortable: true, operate: 'RANGE', addclass: 'datetimerange', formatter: Table.api.formatter.datetime}, + {field: 'updatetime', title: __('Updatetime'), visible: false, sortable: true, operate: 'RANGE', addclass: 'datetimerange', formatter: Table.api.formatter.datetime}, + {field: 'logintime', title: __('Logintime'), operate: 'RANGE', addclass: 'datetimerange', formatter: Table.api.formatter.datetime}, + {field: 'operate', title: __('Operate'), table: table, events: Table.api.events.operate, formatter: Table.api.formatter.operate} + ] + ] + }); + + // 为表格绑定事件 + Table.api.bindevent(table); + }, + add: function () { + Controller.api.bindevent(); + }, + edit: function () { + Controller.api.bindevent(); + }, + api: { + bindevent: function () { + Form.api.bindevent($("form[role=form]")); + } + } + }; + return Controller; +});