|
@@ -3,11 +3,16 @@
|
|
|
namespace App\Services\Client;
|
|
|
|
|
|
use App\Enums\UserStatus;
|
|
|
+use App\Exceptions\BusinessException;
|
|
|
+use App\Models\CoachUser;
|
|
|
+use App\Models\MarketDistTeam;
|
|
|
use App\Models\MemberSocialAccount;
|
|
|
use App\Models\MemberUser;
|
|
|
use App\Services\SmsService;
|
|
|
use Illuminate\Support\Facades\Auth;
|
|
|
use Illuminate\Support\Facades\Cache;
|
|
|
+use Illuminate\Support\Facades\DB;
|
|
|
+use Illuminate\Support\Facades\Log;
|
|
|
|
|
|
class AccountService
|
|
|
{
|
|
@@ -20,6 +25,11 @@ class AccountService
|
|
|
|
|
|
/**
|
|
|
* 发送验证码
|
|
|
+ * 业务逻辑:
|
|
|
+ * 1. 生成6位随机数字验证码
|
|
|
+ * 2. 将验证码保存到缓存中,有效期5分钟
|
|
|
+ * 3. 调用短信服务发送验证码
|
|
|
+ * 4. 返回发送成功消息和验证码
|
|
|
*/
|
|
|
public function sendVerifyCode(string $mobile)
|
|
|
{
|
|
@@ -37,10 +47,15 @@ class AccountService
|
|
|
|
|
|
/**
|
|
|
* 用户登录
|
|
|
+ * 业务逻辑:
|
|
|
+ * 1. 验证用户输入的验证码是否与缓存中的一致
|
|
|
+ * 2. 根据手机号查找用户,不存在则创建新用户
|
|
|
+ * 3. 新用户默认状态为开启,记录注册区域
|
|
|
+ * 4. 生成用户认证token
|
|
|
+ * 5. 返回token和用户信息
|
|
|
*/
|
|
|
public function login(string $mobile, string $code)
|
|
|
{
|
|
|
-
|
|
|
// 验证验证码
|
|
|
$cacheCode = Cache::get("verify_code:{$mobile}");
|
|
|
|
|
@@ -68,37 +83,137 @@ class AccountService
|
|
|
|
|
|
/**
|
|
|
* 微信登录
|
|
|
+ * 业务逻辑:
|
|
|
+ * 1. 根据openid查找或创建社交账号记录
|
|
|
+ * 2. 检查社交账号是否已关联用户
|
|
|
+ * 3. 未关联则创建新用户并建立关联
|
|
|
+ * 4. 更新用户微信相关信息
|
|
|
+ * 5. 生成用户认证token
|
|
|
+ * 6. 返回token和用户信息
|
|
|
+ *
|
|
|
+ * @throws BusinessException 业务异常
|
|
|
*/
|
|
|
- public function wxLogin(string $openid)
|
|
|
+ public function wxLogin(string $openid, array $userInfo): array
|
|
|
{
|
|
|
- // 查找或创建微信用户
|
|
|
- $socialAccount = MemberSocialAccount::firstOrCreate(
|
|
|
- [
|
|
|
- 'platform' => 'WECHAT',
|
|
|
- 'social_id' => $openid,
|
|
|
- ]
|
|
|
- );
|
|
|
+ return DB::transaction(function () use ($openid, $userInfo) {
|
|
|
+ // 查找或创建社交账号
|
|
|
+ $socialAccount = MemberSocialAccount::firstOrCreate(
|
|
|
+ [
|
|
|
+ 'platform' => 'WECHAT',
|
|
|
+ 'social_id' => $openid,
|
|
|
+ ]
|
|
|
+ );
|
|
|
+
|
|
|
+ $user = $socialAccount->user;
|
|
|
+ $isNewUser = false;
|
|
|
+
|
|
|
+ if (! $user) {
|
|
|
+ // 创建新用户
|
|
|
+ $user = MemberUser::create([
|
|
|
+ 'state' => UserStatus::OPEN->value,
|
|
|
+ 'register_area' => request()->header('area_code'),
|
|
|
+ 'nickname' => $userInfo['nickname'] ?? null,
|
|
|
+ 'avatar' => $userInfo['avatar'] ?? null,
|
|
|
+ 'gender' => $userInfo['gender'] ?? null,
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $socialAccount->update(['user_id' => $user->id]);
|
|
|
+ $isNewUser = true;
|
|
|
+ } else {
|
|
|
+ // 更新现有用户信息
|
|
|
+ $user->update([
|
|
|
+ 'nickname' => $userInfo['nickname'] ?? $user->nickname,
|
|
|
+ 'avatar' => $userInfo['avatar'] ?? $user->avatar,
|
|
|
+ 'gender' => $userInfo['gender'] ?? $user->gender,
|
|
|
+ ]);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理邀请关系
|
|
|
+ if (isset($userInfo['invite_code']) && $isNewUser) {
|
|
|
+ $this->handleInviteRelation($user, $userInfo['invite_code']);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 生成token
|
|
|
+ $token = $user->createToken('auth-token')->plainTextToken;
|
|
|
+
|
|
|
+ return [
|
|
|
+ 'token' => $token,
|
|
|
+ 'user' => $user->fresh(),
|
|
|
+ ];
|
|
|
+ });
|
|
|
+ }
|
|
|
|
|
|
- $user = $socialAccount->user;
|
|
|
- if (! $user) {
|
|
|
- $user = MemberUser::create([
|
|
|
- 'state' => 'enable',
|
|
|
- 'register_area' => request()->header('area_code'),
|
|
|
+ /**
|
|
|
+ * 处理邀请关系
|
|
|
+ *
|
|
|
+ * @param MemberUser $user 新用户
|
|
|
+ * @param string $inviteCode 邀请码 (格式: type_id, 如 user_1, coach_1)
|
|
|
+ */
|
|
|
+ protected function handleInviteRelation(MemberUser $user, string $inviteCode): void
|
|
|
+ {
|
|
|
+ try {
|
|
|
+ // 解析邀请码
|
|
|
+ $parts = explode('_', $inviteCode);
|
|
|
+ if (count($parts) !== 2) {
|
|
|
+ Log::warning('Invalid invite code format', ['invite_code' => $inviteCode]);
|
|
|
+
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ [$type, $id] = $parts;
|
|
|
+
|
|
|
+ // 根据类型查找邀请人
|
|
|
+ $inviter = match ($type) {
|
|
|
+ 'user' => MemberUser::find($id),
|
|
|
+ 'coach' => CoachUser::find($id),
|
|
|
+ default => null
|
|
|
+ };
|
|
|
+
|
|
|
+ if (! $inviter) {
|
|
|
+ Log::warning('Inviter not found', [
|
|
|
+ 'type' => $type,
|
|
|
+ 'id' => $id,
|
|
|
+ 'invite_code' => $inviteCode,
|
|
|
+ ]);
|
|
|
+
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查用户是否已在营销团队中
|
|
|
+ $existingTeam = MarketDistTeam::where('user_id', $user->id)->exists();
|
|
|
+ if ($existingTeam) {
|
|
|
+ Log::info('User already in marketing team', ['user_id' => $user->id]);
|
|
|
+
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建团队关系
|
|
|
+ DB::transaction(function () use ($user, $inviter) {
|
|
|
+ MarketDistTeam::create([
|
|
|
+ 'user_id' => $user->id,
|
|
|
+ 'owner_id' => $inviter->id,
|
|
|
+ 'owner_type' => $inviter::class,
|
|
|
+ 'level' => 1,
|
|
|
+ 'status' => 1,
|
|
|
+ ]);
|
|
|
+ });
|
|
|
+
|
|
|
+ } catch (\Exception $e) {
|
|
|
+ Log::error('Failed to handle invite relation', [
|
|
|
+ 'user_id' => $user->id,
|
|
|
+ 'invite_code' => $inviteCode,
|
|
|
+ 'error' => $e->getMessage(),
|
|
|
]);
|
|
|
- $socialAccount->update(['user_id' => $user->id]);
|
|
|
}
|
|
|
-
|
|
|
- // 生成token
|
|
|
- $token = $user->createToken('auth-token')->plainTextToken;
|
|
|
-
|
|
|
- return [
|
|
|
- 'token' => $token,
|
|
|
- 'user' => $user,
|
|
|
- ];
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 用户退出
|
|
|
+ * 业务逻辑:
|
|
|
+ * 1. 根据用户ID查找用户
|
|
|
+ * 2. 验证用户是否存在
|
|
|
+ * 3. 删除用户所有token
|
|
|
+ * 4. 返回退出成功消息
|
|
|
*/
|
|
|
public function logout(int $userId)
|
|
|
{
|
|
@@ -112,6 +227,13 @@ class AccountService
|
|
|
|
|
|
/**
|
|
|
* 用户注销
|
|
|
+ * 业务逻辑:
|
|
|
+ * 1. 获取当前认证用户
|
|
|
+ * 2. 验证用户存在且状态为启用
|
|
|
+ * 3. 更新用户状态为禁用
|
|
|
+ * 4. 软删除用户记录
|
|
|
+ * 5. 删除用户所有token
|
|
|
+ * 6. 返回注销成功消息
|
|
|
*/
|
|
|
public function deleteAccount()
|
|
|
{
|