文件
yjyf/server/application/api/controller/Auth.php
YuanWen 361dffc7d8 feat: 新增师傅端功能及服务增项管理模块
- 新增师傅端路由和页面配置,切换登录后默认跳转到师傅大厅
- 新增服务分类图标上传功能和增项服务管理模块
- 新增订单、申诉、结算等模型关联关系及后台控制器
- 新增师傅端TabBar组件和消息页面跳转逻辑
- 新增文件上传接口和服务计算逻辑
- 新增各类SVG图标和空状态提示
2026-11-03 15:45:21 +08:00

340 行
13 KiB
PHP

此文件含有模棱两可的 Unicode 字符

此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。

<?php
namespace app\api\controller;
use fast\Random;
use think\Db;
use think\Validate;
/**
* 登录 / 角色 / 个人信息
*/
class Auth extends Base
{
protected $noNeedLogin = ['login', 'wxlogin', 'wxPhoneLogin'];
/**
* 手机号 + 验证码登录(兜底通道)
* 验证码来源由 .env 的 [sms] 段控制:mock=1 时使用 mock_code(联调用),
* 接入真实短信后置 mock=0 并在此处改为 Sms::check($mobile, $code, 'mobilelogin')
* @ApiMethod (POST)
*/
public function login()
{
$mobile = trim((string)$this->request->post('mobile', $this->request->post('account', '')));
$mobile = preg_replace('/[^\d]/', '', $mobile);
$password = (string)$this->request->post('password', '');
$code = trim((string)$this->request->post('code', ''));
// 1. 手机号 + 密码登录(直连真实数据库)
if ($password !== '' || ($mobile !== '' && $code === '')) {
if ($mobile === '') {
$this->error('请填写手机号');
}
if (!preg_match('/^1[3-9]\d{9}$/', $mobile)) {
$this->error('手机号格式不正确');
}
if ($password === '') {
$this->error('请填写登录密码');
}
// 直接查询真实数据库 fa_user 表
$user = Db::name('user')->where('mobile', $mobile)->find();
if (!$user) {
// 若数据库中无此手机号:直连数据库插入注册新会员
$ret = $this->auth->register($mobile, $password, '', $mobile, []);
if (!$ret) {
$this->error($this->auth->getError() ?: '注册登录失败');
}
} else {
if ($user['status'] !== 'normal') {
$this->error('账号已被禁用');
}
// 若微信手机号快捷登录初始化的用户未设密码,直接落库更新密码
if (empty($user['password'])) {
$salt = Random::alnum();
$encryptPassword = $this->auth->getEncryptPassword($password, $salt);
Db::name('user')->where('id', $user['id'])->update([
'password' => $encryptPassword,
'salt' => $salt,
'updatetime' => time(),
]);
$this->auth->direct($user['id']);
} else {
$encryptPassword = $this->auth->getEncryptPassword($password, $user['salt']);
if ($user['password'] !== $encryptPassword) {
$this->error('手机号或密码不正确');
}
$this->auth->direct($user['id']);
}
}
// 直查真实数据库返回用户完整信息
$this->success('登录成功', [
'token' => $this->auth->getToken(),
'expires' => 2592000,
'userinfo' => $this->profileData(),
]);
}
// 2. 手机号 + 验证码登录(向下兼容)
if (!$mobile || !$code) {
$this->error('请填写手机号和验证码');
}
if (!Validate::regex($mobile, "^1\d{10}$")) {
$this->error('手机号格式不正确');
}
$mockOn = (int)\think\Env::get('sms.mock', 1);
$mockCode = (string)\think\Env::get('sms.mock_code', '123456');
if (!($mockOn === 1 && $mockCode !== '' && (string)$code === $mockCode)) {
$this->error('短信通道尚未接入,请联系管理员开通', ['sms_ready' => 0], 2010);
}
$user = \app\common\model\User::getByMobile($mobile);
if ($user) {
if ($user['status'] !== 'normal') {
$this->error('账号已被禁用');
}
$this->auth->direct($user['id']);
} else {
$ret = $this->auth->register($mobile, Random::alnum(), '', $mobile, []);
if (!$ret) {
$this->error($this->auth->getError() ?: '注册失败');
}
}
$this->success('登录成功', [
'token' => $this->auth->getToken(),
'expires' => 2592000,
'userinfo' => $this->profileData(),
]);
}
/**
* 我的信息(含角色与师傅状态)
*/
public function profile()
{
$this->success('', $this->profileData());
}
/**
* 组装个人信息
*/
protected function profileData()
{
$uid = $this->uid();
$user = Db::name('user')->field('id,nickname,mobile,avatar,money,score,createtime')->where('id', $uid)->find();
$master = Db::name('master')->where('user_id', $uid)->find();
$couponCount = Db::name('user_coupon')->where('user_id', $uid)->where('status', 'unused')->count();
$unread = Db::name('message')->where('receive_type', 'user')->where('receive_id', $uid)->where('is_read', 0)->count();
$masterInfo = null;
if ($master) {
$masterInfo = [
'id' => (int)$master['id'],
'realname' => $master['realname'],
'verify_status' => $master['verify_status'],
'audit_msg' => $master['audit_msg'],
'work_status' => $master['work_status'],
'level' => (int)$master['level'],
'score' => (float)$master['score'],
'order_count' => (int)$master['order_count'],
'money' => (float)$master['money'],
'can_take' => $master['verify_status'] === 'pass',
];
}
$merchantRel = $this->merchantOf($uid);
$merchant = $merchantRel ? $merchantRel['merchant'] : null;
$merchantRole = $merchantRel ? $merchantRel['role'] : null;
$merchantInfo = null;
if ($merchant) {
$merchantInfo = [
'id' => (int)$merchant['id'],
'name' => $merchant['name'],
'verify_status' => $merchant['verify_status'],
'audit_msg' => $merchant['audit_msg'],
'settle_type' => $merchant['settle_type'],
'role' => $merchantRole,
'is_owner' => $merchantRole === 'owner',
'can_order' => $merchant['verify_status'] === 'pass',
];
}
return [
'id' => (int)$user['id'],
'nickname' => $user['nickname'] ?: ('用户' . substr((string)$user['mobile'], -4)),
'mobile' => $user['mobile'],
'avatar' => $user['avatar'] ?: '',
'balance' => (float)$user['money'],
'coupon_count' => (int)$couponCount,
'unread_count' => (int)$unread,
'is_master' => $master ? true : false,
'master' => $masterInfo,
'is_merchant' => $merchant ? true : false,
'merchant' => $merchantInfo,
];
}
/**
* 微信授权登录(小程序 uni.login 拿到的 code)
* @ApiMethod (POST)
*/
public function wxlogin()
{
$code = trim((string)$this->request->post('code'));
$nickname = trim((string)$this->request->post('nickname'));
$avatar = trim((string)$this->request->post('avatar'));
if ($code === '') {
$this->error('缺少微信登录凭证 code');
}
$wx = \app\common\library\WxLogin::jscode2session($code);
if (isset($wx['error'])) {
$this->error($wx['error'], ['wx_errcode' => isset($wx['errcode']) ? (int)$wx['errcode'] : 0]);
}
$ret = \app\common\library\WxLogin::provision(
$wx['openid'],
isset($wx['unionid']) ? $wx['unionid'] : '',
isset($wx['session_key']) ? $wx['session_key'] : '',
$nickname,
$avatar
);
$this->auth->direct($ret['user_id']);
$this->success('登录成功', [
'token' => $this->auth->getToken(),
'expires' => 2592000,
'userinfo' => $this->profileData(),
'is_new' => $ret['is_new'],
]);
}
/**
* 切换角色(用户 / 师傅)
* @ApiMethod (POST)
*/
/**
* 微信手机号授权快捷登录
* @ApiMethod (POST)
*/
public function wxPhoneLogin()
{
$phoneCode = trim((string)$this->request->post('phone_code'));
$loginCode = trim((string)$this->request->post('login_code'));
if ($phoneCode === '') {
$this->error('缺少微信手机号授权凭证');
}
// 1. 换取手机号
$phoneInfo = \app\common\library\WxLogin::getPhoneNumber($phoneCode);
if (isset($phoneInfo['error'])) {
$this->error('获取微信手机号失败:' . $phoneInfo['error'], ['wx_errcode' => isset($phoneInfo['errcode']) ? (int)$phoneInfo['errcode'] : 0]);
}
$mobile = isset($phoneInfo['purePhoneNumber']) ? (string)$phoneInfo['purePhoneNumber'] : (string)$phoneInfo['phoneNumber'];
if (!$mobile) {
$this->error('解析手机号失败');
}
// 2. 如果传了 login_code,换取 openid
$openid = '';
$sessionKey = '';
$unionid = '';
if ($loginCode !== '') {
$wx = \app\common\library\WxLogin::jscode2session($loginCode);
if (!isset($wx['error']) && !empty($wx['openid'])) {
$openid = $wx['openid'];
$sessionKey = isset($wx['session_key']) ? $wx['session_key'] : '';
$unionid = isset($wx['unionid']) ? $wx['unionid'] : '';
}
}
// 3. 落库与关联登录
$ret = \app\common\library\WxLogin::provisionWithMobile(
$mobile,
$openid,
$unionid,
$sessionKey
);
$this->auth->direct($ret['user_id']);
$this->success('登录成功', [
'token' => $this->auth->getToken(),
'expires' => 2592000,
'userinfo' => $this->profileData(),
'is_new' => $ret['is_new'],
'mobile' => $mobile,
]);
}
public function switchRole()
{
$role = $this->request->post('role', 'user');
if (!in_array($role, ['user', 'master', 'merchant'], true)) {
$this->error('角色参数错误');
}
if ($role === 'master') {
$master = $this->masterInfo();
if (!$master) {
$this->error('请先完成师傅入驻', ['need_apply' => 1], 2001);
}
if ($master['verify_status'] === 'pending') {
$this->error('师傅资质审核中,请耐心等待', ['verify_status' => 'pending'], 2002);
}
if ($master['verify_status'] === 'reject') {
$this->error('资质未通过:' . $master['audit_msg'], ['verify_status' => 'reject'], 2003);
}
}
if ($role === 'merchant') {
$merchantRel = $this->merchantOf($this->uid());
$merchant = $merchantRel ? $merchantRel['merchant'] : null;
if (!$merchant) {
$this->error('请先完成商家入驻', ['need_apply' => 1], 2001);
}
if ($merchant['verify_status'] === 'pending') {
$this->error('商家资质审核中,请耐心等待', ['verify_status' => 'pending'], 2002);
}
if ($merchant['verify_status'] === 'reject') {
$this->error('资质未通过:' . $merchant['audit_msg'], ['verify_status' => 'reject'], 2003);
}
}
$redirect = '/pages/index/index';
if ($role === 'master') {
$redirect = '/pages/master/hall';
} elseif ($role === 'merchant') {
$redirect = '/pages/merchant/workbench';
}
$this->success('切换成功', ['role' => $role, 'redirect' => $redirect]);
}
/**
* 当前用户的商家归属:主账号 / 关联账号
* 返回 ['merchant' => 商家行, 'role' => owner|member]
*/
protected function merchantOf($uid)
{
$merchant = Db::name('merchant')->where('user_id', $uid)->find();
if ($merchant) {
return ['merchant' => $merchant, 'role' => 'owner'];
}
$bind = Db::name('merchant_member')->where('user_id', $uid)->find();
if ($bind) {
$merchant = Db::name('merchant')->where('id', (int)$bind['merchant_id'])->find();
if ($merchant) {
return ['merchant' => $merchant, 'role' => 'member'];
}
}
return null;
}
}