input('code', '')); if (!$code) { $this->fail('缺少微信授权 code'); } $openid = $this->resolveOpenid($code); $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' => '同城用户' . mt_rand(1000, 9999), 'password' => md5(mt_rand()), 'salt' => 'wx', 'email' => substr(md5($openid), 0, 12) . '@wx.local', 'mobile' => '', '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(), ]); $user = Db::name('user')->where('id', $uid)->find(); } if ($user['status'] != 'normal') { $this->fail('账号已被禁用,请联系客服'); } $token = $this->issueToken($user['id']); $this->ok([ 'token' => $token, 'refreshToken' => md5($token . 'refresh'), '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]); } /** * 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]); } /** * A4 个人资料 * 返回 userInfo(含会员状态、余额) */ public function profile() { $user = $this->auth(); $this->ok($this->userInfo($user)); } /** * code -> openid * 已配置微信参数时走 jscode2session;未配置(本地/联调)时用确定性派生,便于前后端联调。 */ protected function resolveOpenid($code) { $appid = (string)$this->cfg('wx.appid', ''); $secret = (string)$this->cfg('wx.secret', ''); if ($appid && $secret) { $url = 'https://api.weixin.qq.com/sns/jscode2session?appid=' . $appid . '&secret=' . $secret . '&js_code=' . urlencode($code) . '&grant_type=authorization_code'; $res = @json_decode(@file_get_contents($url), true); if (!empty($res['openid'])) { return $res['openid']; } } // 未配置微信参数:若 code 本身即为 openid 标识则直接采用,否则派生 if (strpos($code, 'openid_') === 0) { return $code; } return 'openid_' . md5($code); } }