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', '')); if ($password === '' && $code !== '') { $this->error('短信验证码通道已移除,请使用手机号密码或微信一键登录', null, 2010); } // 手机号 + 密码登录(直连真实数据库) 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'])) { $this->error('该账号尚未设置登录密码,请使用微信一键登录后设置密码'); } $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(), ]); } /** * 我的信息(含角色与师傅状态) */ public function profile() { $this->success('', $this->profileData()); } /** * 组装个人信息 */ protected function profileData() { $uid = $this->uid(); $user = Db::name('user')->field('id,nickname,mobile,email,avatar,money,score,user_type,contact_address,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) { $memberCount = (int)Db::name('merchant_member')->where('merchant_id', (int)$merchant['id'])->count(); $merchantInfo = [ 'id' => (int)$merchant['id'], 'name' => $merchant['name'], 'legal_person' => isset($merchant['legal_person']) ? (string)$merchant['legal_person'] : '', 'legal_mobile' => isset($merchant['legal_mobile']) ? (string)$merchant['legal_mobile'] : '', 'tech_mobile' => isset($merchant['tech_mobile']) ? (string)$merchant['tech_mobile'] : '', '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', 'member_count' => $memberCount, ]; } // 统计发单端各状态订单数量(与 Order::index 共享同一套团队归属口径) $team = $this->currentMerchantTeam(); $teamUids = $team ? $team['user_ids'] : [$uid]; $statusGroups = Db::name('order') ->field('status, count(*) as cnt') ->where('user_id', 'in', $teamUids) ->where('user_deleted', 0) ->group('status') ->select(); $rawCounts = []; foreach ($statusGroups as $sg) { $rawCounts[(int)$sg['status']] = (int)$sg['cnt']; } $getCnt = function ($st) use ($rawCounts) { return isset($rawCounts[$st]) ? $rawCounts[$st] : 0; }; $orderCounts = [ 'pending_pay' => $getCnt(0), 'pending_accept' => $getCnt(1), 'quoting' => $getCnt(2), 'scheduled' => $getCnt(3), 'serving' => $getCnt(4) + $getCnt(5) + $getCnt(6), 'pending_verify' => $getCnt(7), ]; if ($merchantRole === 'member' && $merchant && $merchant['verify_status'] === 'pass') { $userType = 'merchant'; } elseif (!empty($user['user_type']) && in_array($user['user_type'], ['personal', 'merchant'], true)) { $userType = $user['user_type']; } else { $userType = ($merchant && $merchant['verify_status'] === 'pass') ? 'merchant' : 'personal'; } return [ 'id' => (int)$user['id'], 'nickname' => $user['nickname'] ?: ('用户' . substr((string)$user['mobile'], -4)), 'mobile' => $user['mobile'], 'email' => !empty($user['email']) ? (string)$user['email'] : ($merchant && !empty($merchant['email']) ? (string)$merchant['email'] : ''), 'contact_address' => !empty($user['contact_address']) ? (string)$user['contact_address'] : ($merchant ? (string)($merchant['province'] . $merchant['city'] . $merchant['district'] . $merchant['address']) : ''), 'user_type' => $userType, 'company_name' => $merchant ? (string)$merchant['name'] : '', 'legal_person' => $merchant && isset($merchant['legal_person']) ? (string)$merchant['legal_person'] : '', 'legal_mobile' => $merchant && isset($merchant['legal_mobile']) ? (string)$merchant['legal_mobile'] : '', 'tech_mobile' => $merchant && isset($merchant['tech_mobile']) ? (string)$merchant['tech_mobile'] : '', 'avatar' => $user['avatar'] ?: '', 'balance' => (float)$user['money'], 'coupon_count' => (int)$couponCount, 'unread_count' => (int)$unread, 'order_counts' => $orderCounts, 'is_master' => $master ? true : false, 'master' => $masterInfo, 'is_merchant' => $merchant ? true : false, 'merchant' => $merchantInfo, ]; } /** * 保存账号信息(对标奇兵到家「账号信息」页:支持 家庭/个人 与 商家 两种用户类型) * @ApiMethod (POST) */ public function saveAccount() { $uid = $this->uid(); $userType = trim((string)$this->request->post('user_type', 'personal')); if (!in_array($userType, ['personal', 'merchant'], true)) { $userType = 'personal'; } $nickname = trim((string)$this->request->post('nickname', '')); $mobile = trim((string)$this->request->post('mobile', '')); $contactAddress = trim((string)$this->request->post('contact_address', '')); $email = trim((string)$this->request->post('email', '')); $companyName = trim((string)$this->request->post('company_name', '')); $legalPerson = trim((string)$this->request->post('legal_person', '')); $legalMobile = trim((string)$this->request->post('legal_mobile', '')); $techMobile = trim((string)$this->request->post('tech_mobile', '')); if ($nickname === '') { $this->error('请输入用户昵称'); } if ($legalPerson !== '' && preg_match('/\d/', $legalPerson)) { $this->error('法人姓名不支持数字输入'); } if ($legalMobile !== '' && !preg_match('/^1\d{10}$/', $legalMobile)) { $this->error('法人手机号格式不正确'); } if ($techMobile !== '' && !preg_match('/^1\d{10}$/', $techMobile)) { $this->error('技术手机号格式不正确'); } if ($email !== '' && !filter_var($email, FILTER_VALIDATE_EMAIL)) { $this->error('电子邮箱格式不正确'); } $merchantRel = $this->merchantOf($uid); if ($userType === 'merchant') { if (!$merchantRel || ($merchantRel['merchant']['verify_status'] ?? '') !== 'pass') { $this->error('请先提交企业入驻申请并通过审核后再启用商家账号'); } } $userUpdate = [ 'nickname' => $nickname, 'user_type' => $userType, 'contact_address' => $contactAddress, 'email' => $email, 'updatetime' => time(), ]; if ($mobile !== '' && preg_match('/^1\d{10}$/', $mobile)) { $conflict = Db::name('user')->where('mobile', $mobile)->where('id', '<>', $uid)->find(); if ($conflict) { $this->error('该联系号码已被其他账号绑定'); } $userUpdate['mobile'] = $mobile; } Db::name('user')->where('id', $uid)->update($userUpdate); if ($userType === 'merchant' && $merchantRel && $merchantRel['role'] === 'owner') { $existMerchant = $merchantRel['merchant']; $curUser = Db::name('user')->where('id', $uid)->find(); $merchantSave = [ 'name' => $companyName !== '' ? $companyName : (!empty($existMerchant['name']) ? $existMerchant['name'] : $nickname), 'legal_person' => $legalPerson, 'legal_mobile' => $legalMobile, 'tech_mobile' => $techMobile, 'contact' => $nickname, 'mobile' => $curUser ? (string)$curUser['mobile'] : '', 'address' => $contactAddress, 'email' => $email, 'updatetime' => time(), ]; Db::name('merchant')->where('id', (int)$existMerchant['id'])->update($merchantSave); } $this->success('保存成功', ['userinfo' => $this->profileData()]); } /** * 微信授权登录(小程序 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 $openid = ''; $sessionKey = ''; $unionid = ''; if ($loginCode !== '') { $wx = \app\common\library\WxLogin::jscode2session($loginCode); if (isset($wx['error']) || empty($wx['openid'])) { $errMsg = isset($wx['error']) && $wx['error'] !== '' ? (string)$wx['error'] : '微信登录凭证换取 OpenID 失败,请重进小程序再试'; \think\Log::error('[Auth::wxPhoneLogin] jscode2session 失败: ' . $errMsg); $this->error($errMsg, ['wx_errcode' => isset($wx['errcode']) ? (int)$wx['errcode'] : 0]); } $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/mine/mine'; } $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; } }