本次修改: 1. 新增分销绑定 API 接口与路由 2. 老用户不再自动绑定推荐人,需手动输入邀请码绑定 3. 新增绑定推荐人 UI 弹层与展示组件 4. 新增防环校验与关系补全逻辑 5. 调整邀请码存储逻辑,仅未登录用户自动存储
575 行
23 KiB
PHP
575 行
23 KiB
PHP
<?php
|
||
|
||
namespace app\api\controller;
|
||
|
||
use think\Db;
|
||
use app\common\library\DistributionService;
|
||
use app\common\library\WxMini;
|
||
|
||
/**
|
||
* 用户端 · 认证与登录(Module-AUTH)
|
||
* 路由:
|
||
* POST /api/v1/auth/wx-login A1 微信授权登录
|
||
* POST /api/v1/auth/bind-phone A2 手机号绑定(手动输入)
|
||
* POST /api/v1/auth/wx-phone A5 微信手机号快捷获取(getPhoneNumber)
|
||
* POST /api/v1/auth/avatar A6 头像上传(用户端)
|
||
* POST /api/v1/auth/switch-type A3 个人/商家身份切换
|
||
* GET|POST /api/v1/auth/profile A4 个人资料(GET 读取 / POST 更新昵称与头像)
|
||
*/
|
||
class Auth extends V1
|
||
{
|
||
/**
|
||
* A1 微信一键授权登录
|
||
* 入参 { code, nickname?, avatar? } → 出参 { token, refreshToken, userInfo }
|
||
* 规则:code 必填;首次登录自动建档并生成邀请码;
|
||
* v1.0.9 起支持携带微信昵称/头像(小程序用 `open-type="chooseAvatar"` + `<input type="nickname">` 采集),
|
||
* 新用户直接落库、老用户填了就更新,保证后台「会员列表」能看到真实微信昵称与头像。
|
||
*/
|
||
public function wxLogin()
|
||
{
|
||
$code = trim((string)$this->input('code', ''));
|
||
if (!$code) {
|
||
$this->fail('缺少微信授权 code');
|
||
}
|
||
|
||
// 频率限制:同一 IP 每分钟最多 30 次授权登录
|
||
$this->rateLimit('wxlogin:' . $this->request->ip(), 30, 60);
|
||
|
||
$nickname = trim((string)$this->input('nickname', ''));
|
||
$avatar = trim((string)$this->input('avatar', ''));
|
||
if (mb_strlen($nickname) > 32) {
|
||
$nickname = mb_substr($nickname, 0, 32);
|
||
}
|
||
|
||
// 解析手机号(支持微信快捷获取的 phoneCode 或手动传入的 phone)
|
||
$phoneCode = trim((string)$this->input('phoneCode', ''));
|
||
$manualPhone = trim((string)$this->input('phone', ''));
|
||
$phone = '';
|
||
if ($phoneCode !== '') {
|
||
try {
|
||
$phone = WxMini::phoneNumber($phoneCode);
|
||
} catch (\Throwable $e) {
|
||
// 手机号属于可选增强:拿不到不阻断登录,前端可回落到手动绑定(A2 bindPhone)
|
||
$phone = '';
|
||
}
|
||
} elseif ($manualPhone !== '' && preg_match('/^1\d{10}$/', $manualPhone)) {
|
||
$phone = $manualPhone;
|
||
}
|
||
|
||
$openid = $this->resolveOpenid($code);
|
||
$inviterId = (int)$this->input('inviterId', $this->input('inviter_id', 0));
|
||
$inviteCode = trim((string)$this->input('inviteCode', $this->input('invite_code', '')));
|
||
|
||
// 同一 openid 串行建号:避免并发首次登录建出两个账号(usage: MySQL GET_LOCK)
|
||
$lockName = 'fc_wxlogin_' . md5($openid);
|
||
Db::query('SELECT GET_LOCK(?, 5)', [$lockName]);
|
||
|
||
try {
|
||
Db::startTrans();
|
||
|
||
$ext = Db::name('fc_user_ext')->where('openid', $openid)->find();
|
||
$user = $ext ? Db::name('user')->where('id', $ext['user_id'])->find() : null;
|
||
|
||
if (!$user) {
|
||
// 首次登录:自动建号(有微信昵称/头像就落库,否则用占位昵称 + 字母头像)
|
||
$uid = Db::name('user')->insertGetId([
|
||
'group_id' => 1,
|
||
'username' => 'wx' . substr(md5($openid), 0, 10),
|
||
'nickname' => $nickname !== '' ? $nickname : '同城用户' . mt_rand(1000, 9999),
|
||
'password' => md5(mt_rand()),
|
||
'salt' => 'wx',
|
||
'email' => substr(md5($openid), 0, 12) . '@wx.local',
|
||
'mobile' => $phone,
|
||
'avatar' => $avatar,
|
||
'level' => 1,
|
||
'money' => 0,
|
||
'score' => 0,
|
||
'status' => 'normal',
|
||
'jointime' => time(),
|
||
'joinip' => $this->request->ip(),
|
||
'createtime' => time(),
|
||
'updatetime' => time(),
|
||
]);
|
||
Db::name('fc_user_ext')->insert([
|
||
'user_id' => $uid, 'openid' => $openid, 'user_type' => 1, 'realname_status' => 0,
|
||
'is_member' => 0, 'member_level' => '', 'member_expiretime' => null,
|
||
'invite_code' => strtoupper(substr(md5('fc' . $uid), 0, 6)), 'inviter_id' => 0,
|
||
'free_view_date' => date('Y-m-d'), 'free_view_used' => 0,
|
||
'createtime' => time(), 'updatetime' => time(),
|
||
]);
|
||
// 首次登录:绑定分销推荐关系
|
||
DistributionService::bindRelation($uid, $inviterId, $inviteCode);
|
||
$user = Db::name('user')->where('id', $uid)->find();
|
||
} else {
|
||
// 老用户:不再自动补绑推荐人(老用户须在分销中心手动填写邀请码补绑)
|
||
// 老用户:仅在本次带上了新值且确实不同时更新(不覆盖运营在后台改过的资料)
|
||
$update = [];
|
||
if ($nickname !== '' && $nickname !== (string)$user['nickname']) {
|
||
$update['nickname'] = $nickname;
|
||
}
|
||
if ($avatar !== '' && $avatar !== (string)$user['avatar']) {
|
||
$update['avatar'] = $avatar;
|
||
}
|
||
if ($phone !== '' && (string)$user['mobile'] === '') {
|
||
$update['mobile'] = $phone;
|
||
}
|
||
if ($update) {
|
||
$update['updatetime'] = time();
|
||
Db::name('user')->where('id', $user['id'])->update($update);
|
||
$user = Db::name('user')->where('id', $user['id'])->find();
|
||
}
|
||
}
|
||
|
||
if ($user['status'] != 'normal') {
|
||
throw new \RuntimeException('账号已被禁用,请联系客服');
|
||
}
|
||
|
||
Db::commit();
|
||
} catch (\Throwable $e) {
|
||
Db::rollback();
|
||
Db::query('SELECT RELEASE_LOCK(?)', [$lockName]);
|
||
$this->fail($e->getMessage() ?: '微信登录失败,请重试');
|
||
}
|
||
|
||
Db::query('SELECT RELEASE_LOCK(?)', [$lockName]);
|
||
|
||
$token = $this->issueToken($user['id']);
|
||
$this->ok([
|
||
'token' => $token,
|
||
'refreshToken' => $this->issueRefreshToken($user['id']),
|
||
'userInfo' => $this->userInfo($user),
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* A2 手机号授权绑定
|
||
* 入参 { phone } → 出参 { phone }
|
||
* 规则 BR-02:必须匹配 ^1\d{10}$
|
||
*/
|
||
public function bindPhone()
|
||
{
|
||
$user = $this->auth();
|
||
$phone = trim((string)$this->input('phone', ''));
|
||
if (!preg_match('/^1\d{10}$/', $phone)) {
|
||
$this->fail('手机号格式不正确');
|
||
}
|
||
Db::name('user')->where('id', $user['id'])->update([
|
||
'mobile' => $phone, 'updatetime' => time(),
|
||
]);
|
||
$this->ok(['phone' => $phone]);
|
||
}
|
||
|
||
/**
|
||
* A5 微信手机号快捷获取
|
||
* 入参 { code }(`<button open-type="getPhoneNumber">` 回调里的 `e.detail.code`)
|
||
* → 出参 { phone }
|
||
* 规则:由 EasyWeChat 的 `getuserphonenumber` 换号并写入 fa_user.mobile(见 \app\common\library\WxMini);
|
||
* 未配置微信参数 / 微信侧失败(快捷验证未开通、code 失效、用户拒绝)时返回业务失败,
|
||
* 前端据此回落到「手动输入手机号」(A2 bindPhone),保证任何情况下都能拿到号码。
|
||
*/
|
||
public function wxPhone()
|
||
{
|
||
$user = $this->auth();
|
||
|
||
$code = trim((string)$this->input('code', ''));
|
||
if ($code === '') {
|
||
$this->fail('缺少手机号授权 code');
|
||
}
|
||
|
||
try {
|
||
$phone = WxMini::phoneNumber($code);
|
||
} catch (\Throwable $e) {
|
||
// 常见原因:手机号快捷验证未开通/额度用尽、code 已失效、用户拒绝授权
|
||
$this->fail($e->getMessage() ?: '微信手机号获取失败,请改用手动输入');
|
||
}
|
||
|
||
Db::name('user')->where('id', $user['id'])->update([
|
||
'mobile' => $phone, 'updatetime' => time(),
|
||
]);
|
||
|
||
$this->ok(['phone' => $phone]);
|
||
}
|
||
|
||
/**
|
||
* A3 用户类型切换
|
||
* 入参 { userType: 1|2, license? } → 出参 { userType, needAudit }
|
||
* 规则 BR-01:切换商家必须提交营业执照,提交后进入待审核
|
||
*/
|
||
public function switchType()
|
||
{
|
||
$user = $this->auth();
|
||
$userType = (int)$this->input('userType', 0);
|
||
if (!in_array($userType, [1, 2], true)) {
|
||
$this->fail('用户类型不合法');
|
||
}
|
||
|
||
if ($userType === 2) {
|
||
$license = trim((string)$this->input('license', ''));
|
||
if (!$license) {
|
||
$this->fail('商家用户需上传营业执照完成资质认证');
|
||
}
|
||
$name = trim((string)$this->input('merchantName', ''));
|
||
$exists = Db::name('fc_merchant')->where('user_id', $user['id'])->find();
|
||
$data = [
|
||
'user_id' => $user['id'],
|
||
'name' => $name !== '' ? $name : (string)$user['nickname'],
|
||
'license' => $license,
|
||
'audit_status' => 0,
|
||
'shop_status' => 'pending',
|
||
'updatetime' => time(),
|
||
];
|
||
if ($exists) {
|
||
Db::name('fc_merchant')->where('id', $exists['id'])->update($data);
|
||
} else {
|
||
$data['createtime'] = time();
|
||
Db::name('fc_merchant')->insert($data);
|
||
}
|
||
}
|
||
|
||
Db::name('fc_user_ext')->where('user_id', $user['id'])->update([
|
||
'user_type' => $userType, 'updatetime' => time(),
|
||
]);
|
||
|
||
$this->ok(['userType' => $userType, 'needAudit' => $userType === 2]);
|
||
}
|
||
|
||
/**
|
||
* A6 头像上传(用户端)
|
||
* POST /api/v1/auth/avatar multipart: file
|
||
* → { url, avatar }
|
||
* 说明:复用 FastAdmin 上传库(与后台上传同一套类型/大小校验与存储);
|
||
* `avatar` 返回相对路径(与 fa_user.avatar 存储一致,后台列表用 cdnurl 拼完整地址),
|
||
* `url` 为完整地址,供小程序立即展示。
|
||
*/
|
||
public function uploadAvatar()
|
||
{
|
||
$user = $this->auth();
|
||
|
||
$file = $this->request->file('file');
|
||
if (!$file) {
|
||
$this->fail('未接收到上传文件');
|
||
}
|
||
try {
|
||
$upload = new \app\common\library\Upload($file);
|
||
$attachment = $upload->upload();
|
||
} catch (\Exception $e) {
|
||
$this->fail($e->getMessage());
|
||
}
|
||
|
||
$path = (string)$attachment->url;
|
||
Db::name('user')->where('id', $user['id'])->update([
|
||
'avatar' => $path,
|
||
'updatetime' => time(),
|
||
]);
|
||
|
||
$this->ok(['url' => cdnurl($path, true), 'avatar' => $path]);
|
||
}
|
||
|
||
/**
|
||
* A4 个人资料
|
||
* GET → userInfo(含会员状态、余额)
|
||
* POST { nickname?, avatar? } → 更新后的 userInfo(微信昵称/头像可在登录后随时补填)
|
||
*/
|
||
public function profile()
|
||
{
|
||
$user = $this->auth();
|
||
|
||
if ($this->request->isPost()) {
|
||
$nickname = trim((string)$this->input('nickname', ''));
|
||
$avatar = trim((string)$this->input('avatar', ''));
|
||
$update = [];
|
||
if ($nickname !== '') {
|
||
if (mb_strlen($nickname) > 32) {
|
||
$nickname = mb_substr($nickname, 0, 32);
|
||
}
|
||
$update['nickname'] = $nickname;
|
||
}
|
||
if ($avatar !== '') {
|
||
$update['avatar'] = $avatar;
|
||
}
|
||
if (!$update) {
|
||
$this->fail('没有需要更新的资料');
|
||
}
|
||
$update['updatetime'] = time();
|
||
Db::name('user')->where('id', $user['id'])->update($update);
|
||
$user = Db::name('user')->where('id', $user['id'])->find();
|
||
}
|
||
|
||
$this->ok($this->userInfo($user));
|
||
}
|
||
|
||
/**
|
||
* code -> openid
|
||
* 由 EasyWeChat SDK 调 jscode2session(\app\common\library\WxMini)。
|
||
* 兼容:本地未配置 AppID/AppSecret 且开启 debug 时,用确定性派生便于前后端联调。
|
||
*/
|
||
protected function resolveOpenid($code)
|
||
{
|
||
// 调试模式下直接支持 mock openid 便于测试与自动化闭环
|
||
if (config('app_debug') && strpos($code, 'openid_') === 0) {
|
||
return $code;
|
||
}
|
||
|
||
try {
|
||
return WxMini::openid($code);
|
||
} catch (\Throwable $e) {
|
||
if (!WxMini::isConfigured() && config('app_debug')) {
|
||
return strpos($code, 'openid_') === 0 ? $code : 'openid_' . md5($code);
|
||
}
|
||
$this->fail($e->getMessage() ?: '微信登录失败,请重试');
|
||
}
|
||
}
|
||
|
||
/**
|
||
* A7 获取当前用户商家认证与商铺资料
|
||
* GET /api/v1/auth/merchant
|
||
* 出参:商家资料详情及审核状态
|
||
*/
|
||
public function merchantInfo()
|
||
{
|
||
$user = $this->auth();
|
||
$merchant = Db::name('fc_merchant')->where('user_id', $user['id'])->find();
|
||
if (!$merchant) {
|
||
$this->ok([
|
||
'hasApplied' => false,
|
||
'auditStatus' => -1,
|
||
'merchant' => null,
|
||
]);
|
||
return;
|
||
}
|
||
|
||
$this->ok([
|
||
'hasApplied' => true,
|
||
'auditStatus' => (int)$merchant['audit_status'],
|
||
'auditReason' => isset($merchant['audit_reason']) ? (string)$merchant['audit_reason'] : '',
|
||
'shopStatus' => (string)$merchant['shop_status'],
|
||
'merchant' => [
|
||
'id' => (int)$merchant['id'],
|
||
'name' => (string)$merchant['name'],
|
||
'logo' => (string)$merchant['logo'],
|
||
'banner' => isset($merchant['banner']) ? (string)$merchant['banner'] : '',
|
||
'notice' => isset($merchant['notice']) ? (string)$merchant['notice'] : '',
|
||
'contact' => (string)$merchant['contact'],
|
||
'address' => isset($merchant['address']) ? (string)$merchant['address'] : (string)$merchant['category'],
|
||
'lat' => isset($merchant['lat']) && $merchant['lat'] !== null ? (float)$merchant['lat'] : null,
|
||
'lng' => isset($merchant['lng']) && $merchant['lng'] !== null ? (float)$merchant['lng'] : null,
|
||
'category' => (string)$merchant['category'],
|
||
'license' => (string)$merchant['license'],
|
||
'auditStatus' => (int)$merchant['audit_status'],
|
||
'auditReason' => isset($merchant['audit_reason']) ? (string)$merchant['audit_reason'] : '',
|
||
'shopStatus' => (string)$merchant['shop_status'],
|
||
'postCount' => (int)$merchant['post_count'],
|
||
],
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* A8 提交商家资质认证 / 更新商铺资料
|
||
* POST /api/v1/auth/merchant-apply
|
||
* 入参:name, license, logo?, contact?, notice?, banner?
|
||
* 出参:{ auditStatus, message }
|
||
*/
|
||
public function merchantApply()
|
||
{
|
||
$user = $this->auth();
|
||
$name = trim((string)$this->input('name', ''));
|
||
$license = trim((string)$this->input('license', ''));
|
||
$logo = trim((string)$this->input('logo', ''));
|
||
$contact = trim((string)$this->input('contact', ''));
|
||
$address = trim((string)$this->input('address', ''));
|
||
$lat = $this->input('lat', null);
|
||
$lng = $this->input('lng', null);
|
||
$notice = trim((string)$this->input('notice', ''));
|
||
$banner = trim((string)$this->input('banner', ''));
|
||
$category = trim((string)$this->input('category', ''));
|
||
if ($address === '' && $category !== '') {
|
||
$address = $category;
|
||
}
|
||
|
||
$merchant = Db::name('fc_merchant')->where('user_id', $user['id'])->find();
|
||
|
||
// 已通过审核:允许直接更新商铺展示资料(公告、Logo、头图、地址等),不重新打入审核
|
||
if ($merchant && (int)$merchant['audit_status'] === 1) {
|
||
$data = [
|
||
'updatetime' => time(),
|
||
];
|
||
if ($name !== '') $data['name'] = $name;
|
||
if ($logo !== '') $data['logo'] = $logo;
|
||
if ($contact !== '') $data['contact'] = $contact;
|
||
if ($notice !== '') $data['notice'] = $notice;
|
||
if ($banner !== '') $data['banner'] = $banner;
|
||
if ($address !== '') $data['address'] = $address;
|
||
if ($lat !== null && $lat !== '') $data['lat'] = (float)$lat;
|
||
if ($lng !== null && $lng !== '') $data['lng'] = (float)$lng;
|
||
if ($category !== '') $data['category'] = $category;
|
||
|
||
// 若更换营业执照则重新审核
|
||
if ($license !== '' && $license !== (string)$merchant['license']) {
|
||
$data['license'] = $license;
|
||
$data['audit_status'] = 0;
|
||
$data['shop_status'] = 'pending';
|
||
Db::name('fc_merchant')->where('id', $merchant['id'])->update($data);
|
||
$this->ok(['auditStatus' => 0, 'needAudit' => true], '营业执照已更新,已提交平台重新审核');
|
||
return;
|
||
}
|
||
|
||
Db::name('fc_merchant')->where('id', $merchant['id'])->update($data);
|
||
$this->ok(['auditStatus' => 1, 'needAudit' => false], '商家资料保存成功');
|
||
return;
|
||
}
|
||
|
||
// 新申请或重新提交审核:必填校验
|
||
if ($name === '') {
|
||
$this->fail('请填写店铺或企业名称');
|
||
}
|
||
if ($license === '') {
|
||
$this->fail('请上传营业执照完成资质认证');
|
||
}
|
||
|
||
$now = time();
|
||
$data = [
|
||
'user_id' => $user['id'],
|
||
'name' => $name,
|
||
'license' => $license,
|
||
'logo' => $logo,
|
||
'contact' => $contact,
|
||
'address' => $address,
|
||
'lat' => ($lat !== null && $lat !== '') ? (float)$lat : null,
|
||
'lng' => ($lng !== null && $lng !== '') ? (float)$lng : null,
|
||
'category' => $category,
|
||
'notice' => $notice,
|
||
'banner' => $banner,
|
||
'audit_status' => 0,
|
||
'audit_reason' => '',
|
||
'shop_status' => 'pending',
|
||
'updatetime' => $now,
|
||
];
|
||
|
||
if ($merchant) {
|
||
Db::name('fc_merchant')->where('id', $merchant['id'])->update($data);
|
||
} else {
|
||
$data['category'] = '';
|
||
$data['violations'] = 0;
|
||
$data['post_count'] = 0;
|
||
$data['createtime'] = $now;
|
||
Db::name('fc_merchant')->insert($data);
|
||
}
|
||
|
||
Db::name('fc_user_ext')->where('user_id', $user['id'])->update([
|
||
'user_type' => 2,
|
||
'updatetime' => $now,
|
||
]);
|
||
|
||
$this->ok(['auditStatus' => 0, 'needAudit' => true], '商家资质已提交,等待平台审核');
|
||
}
|
||
|
||
/**
|
||
* A11 保存收款人真实姓名(提现用)
|
||
* POST /api/v1/auth/realname {realname}
|
||
* 规则:2-20 位中文(含 ·),需与微信实名一致,否则商家转账会校验失败
|
||
*/
|
||
public function realname()
|
||
{
|
||
$user = $this->auth();
|
||
|
||
$realname = trim((string)$this->input('realname', ''));
|
||
if (!preg_match('/^[\x{4e00}-\x{9fa5}·]{2,20}$/u', $realname)) {
|
||
$this->fail('请输入 2-20 位中文真实姓名');
|
||
}
|
||
|
||
$this->ext($user['id']);
|
||
Db::name('fc_user_ext')->where('user_id', $user['id'])->update([
|
||
'realname' => $realname,
|
||
'realname_status' => 1,
|
||
'updatetime' => time(),
|
||
]);
|
||
|
||
$this->ok(['realname' => $realname]);
|
||
}
|
||
|
||
/**
|
||
* A10 刷新访问令牌
|
||
* POST /api/v1/auth/refresh { refreshToken }
|
||
* 规则:refreshToken 必须为 rf_ 前缀且未过期;换新后旧刷新令牌立即作废(轮换)
|
||
*/
|
||
public function refresh()
|
||
{
|
||
$refresh = trim((string)$this->input('refreshToken', $this->input('refresh_token', '')));
|
||
if ($refresh === '') {
|
||
$this->fail('缺少 refreshToken');
|
||
}
|
||
$this->rateLimit('refresh:' . $this->request->ip(), 20, 60);
|
||
|
||
if (strpos($refresh, 'rf_') !== 0) {
|
||
$this->fail('refreshToken 无效');
|
||
}
|
||
|
||
$row = Db::name('user_token')->where('token', $refresh)->find();
|
||
if (!$row || (int)$row['expiretime'] < time()) {
|
||
$this->fail('登录已过期,请重新登录', 401, 401);
|
||
}
|
||
|
||
$user = Db::name('user')->where('id', (int)$row['user_id'])->find();
|
||
if (!$user || $user['status'] !== 'normal') {
|
||
$this->fail('账号状态异常,请重新登录', 401, 401);
|
||
}
|
||
|
||
// 轮换:旧刷新令牌立即失效(fa_user_token 主键是 token,没有 id 列)
|
||
Db::name('user_token')->where('token', $refresh)->delete();
|
||
|
||
$this->ok([
|
||
'token' => $this->issueToken($user['id']),
|
||
'refreshToken' => $this->issueRefreshToken($user['id']),
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* A9 手机号密码登录(支持 H5 / 调试环境)
|
||
* POST /api/v1/auth/password-login
|
||
* 入参:{ phone, password }
|
||
* 出参:{ token, refreshToken, userInfo }
|
||
*/
|
||
public function passwordLogin()
|
||
{
|
||
$phone = trim((string)$this->input('phone', ''));
|
||
$password = (string)$this->input('password', '');
|
||
|
||
if (!preg_match('/^1\d{10}$/', $phone)) {
|
||
$this->fail('手机号格式不正确');
|
||
}
|
||
if ($password === '') {
|
||
$this->fail('请输入登录密码');
|
||
}
|
||
if (strlen($password) < 6) {
|
||
$this->fail('密码长度不能少于 6 位');
|
||
}
|
||
|
||
// 频率限制:同一手机号 + IP,1 分钟最多 5 次
|
||
$this->rateLimit('pwdlogin:' . $phone . ':' . $this->request->ip(), 5, 60);
|
||
|
||
// 查用户:不存在直接失败,不再自动注册(防止批量造号)
|
||
$user = Db::name('user')->where('mobile', $phone)->find();
|
||
if (!$user) {
|
||
$this->fail('手机号未注册,请先使用微信登录');
|
||
}
|
||
|
||
// 校验密码:只认加盐哈希(已移除历史遗留的 123456 万能密码)
|
||
$salt = isset($user['salt']) ? (string)$user['salt'] : '';
|
||
$checkHash = md5(md5($password) . $salt);
|
||
if ((string)$user['password'] !== $checkHash) {
|
||
$this->fail('密码错误,请重新输入');
|
||
}
|
||
|
||
if ($user['status'] !== 'normal') {
|
||
$this->fail('账号已被禁用,请联系客服');
|
||
}
|
||
|
||
$token = $this->issueToken($user['id']);
|
||
$this->ok([
|
||
'token' => $token,
|
||
'refreshToken' => $this->issueRefreshToken($user['id']),
|
||
'userInfo' => $this->userInfo($user),
|
||
]);
|
||
}
|
||
}
|