Browse Source

feat:生成用户二维码、钱包明细

刘学玺 4 months ago
parent
commit
7fb6c41c7d

+ 2 - 5
app/Admin/Controllers/CoachUserController.php

@@ -20,7 +20,7 @@ class CoachUserController extends AdminController
             ->filterTogglable(false)
             ->headerToolbar([
                 $this->createButton('dialog'),
-                ...$this->baseHeaderToolBar()
+                ...$this->baseHeaderToolBar(),
             ])
             ->columns([
                 amis()->TableColumn('id', 'ID')->sortable(),
@@ -37,12 +37,9 @@ class CoachUserController extends AdminController
                 amis()->TableColumn('state', '状态'),
                 amis()->TableColumn('created_at', admin_trans('admin.created_at'))->type('datetime')->sortable(),
                 amis()->TableColumn('updated_at', admin_trans('admin.updated_at'))->type('datetime')->sortable(),
-                $this->rowActions('dialog')
+                $this->rowActions('dialog'),
             ]);
 
-        throw new \Exception('测试');
-        $xx = 1 / 0;
-
         return $this->baseList($crud);
     }
 

+ 6 - 2
app/Exceptions/ApiException.php

@@ -1,18 +1,22 @@
 <?php
 /**
  * @Name
+ *
  * @Description
+ *
  * @Author 刘学玺
+ *
  * @Date 2024/3/22 17:24
  */
+
 namespace App\Exceptions;
 
 use Exception;
 
 class ApiException extends Exception
 {
-    public function __construct(array $apiErrorConst, \Throwable $previous = null)
+    public function __construct(array $apiErrorConst, ?\Throwable $previous = null)
     {
-        parent::__construct($apiErrorConst['message'], $apiErrorConst['code'], $previous);
+        // parent::__construct($apiErrorConst['message'], $apiErrorConst['code'], $previous);
     }
 }

+ 16 - 15
app/Http/Controllers/Client/OrderController.php

@@ -22,13 +22,13 @@ class OrderController extends Controller
     }
 
     /**
-     * 订单初始化
+     * [订单]订单初始化
      *
      * 初始化订单
      *
      * @authenticated
      *
-     * @bodyParam coach_id int required 技师ID. Example: 1
+     * @bodyParam coach_id int required 技师ID. Example: 6
      * @bodyParam area_code string required 区划代码. Example: 370602
      * @bodyParam project_id int required 项目ID. Example: 1
      *
@@ -45,7 +45,7 @@ class OrderController extends Controller
     }
 
     /**
-     * 创建订单
+     * [订单]创建订单
      *
      * 创建订单
      *
@@ -57,6 +57,7 @@ class OrderController extends Controller
      * @bodyParam use_balance boolean 使用余额. Example: false
      * @bodyParam service_time datetime required 服务时间. Example: 2024-01-01 10:00:00
      * @bodyParam order_id int 订单ID. Example: null
+     * @bodyParam payment_type string required 支付类型. Example: balance
      *
      * @response {
      *   "status": "success",
@@ -65,13 +66,13 @@ class OrderController extends Controller
      */
     public function create(Request $request)
     {
-        $data = $request->only(['project_id', 'address_id', 'coach_id', 'use_balance', 'order_id', 'service_time']);
+        $data = $request->only(['project_id', 'address_id', 'coach_id', 'use_balance', 'order_id', 'service_time', 'payment_type']);
 
         return $this->service->createOrder(Auth::user()->id, $data);
     }
 
     /**
-     * 结束订单
+     * [订单]结束订单
      *
      * 结束订单
      *
@@ -93,7 +94,7 @@ class OrderController extends Controller
     }
 
     /**
-     * 确认技师离开
+     * [订单]确认技师离开
      *
      * 确认技师离开
      *
@@ -115,7 +116,7 @@ class OrderController extends Controller
     }
 
     /**
-     * 取消订单
+     * [订单]取消订单
      *
      * 取消订单
      *
@@ -137,7 +138,7 @@ class OrderController extends Controller
     }
 
     /**
-     * 获取订单列表
+     * [订单]获取订单列表
      *
      * 获取订单列表
      *
@@ -154,7 +155,7 @@ class OrderController extends Controller
     }
 
     /**
-     * 获取订单详情
+     * [订单]获取订单详情
      *
      * 获取订单详情
      *
@@ -173,7 +174,7 @@ class OrderController extends Controller
     }
 
     /**
-     * 订单退款
+     * [订单]订单退款
      *
      * 订单退款
      *
@@ -192,7 +193,7 @@ class OrderController extends Controller
     }
 
     /**
-     * 获取代理商配置
+     * [订单]获取代理商配置
      *
      * 获取代理商配置
      *
@@ -214,7 +215,7 @@ class OrderController extends Controller
     }
 
     /**
-     * 获取技师配置
+     * [订单]获取技师配置
      *
      * 获取技师配置
      *
@@ -235,7 +236,7 @@ class OrderController extends Controller
     }
 
     /**
-     * 计算订单金额
+     * [订单]计算订单金额
      *
      * 计算订单金额
      *
@@ -272,7 +273,7 @@ class OrderController extends Controller
     }
 
     /**
-     * 加钟
+     * [订单]加钟
      *
      * 加钟
      *
@@ -295,7 +296,7 @@ class OrderController extends Controller
     }
 
     /**
-     * 指定技师
+     * [订单]指定技师
      *
      * @authenticated
      *

+ 2 - 1
app/Http/Controllers/Client/TeamController.php

@@ -4,6 +4,7 @@ namespace App\Http\Controllers\Client;
 
 use App\Http\Controllers\Controller;
 use App\Services\Client\TeamService;
+use Auth;
 
 /**
  * @group 用户端
@@ -24,6 +25,6 @@ class TeamController extends Controller
      */
     public function list()
     {
-        return $this->service->getTeamList();
+        return $this->service->getTeamList(Auth::user()->id);
     }
 }

+ 6 - 5
app/Http/Controllers/Client/UserAddressController.php

@@ -22,7 +22,8 @@ class UserAddressController extends Controller
     }
 
     /**
-     * 获取默认地址2
+     * [地址管理]获取默认地址
+     *
      *
      * @description 根据用户编号查询用户数据,用户状态为正常,查询用户地址列表,返回用户默认地址
      *
@@ -50,7 +51,7 @@ class UserAddressController extends Controller
     }
 
     /**
-     * 添加地址
+     * [地址管理]添加地址
      *
      * @description 根据用户编号查询用户数据,用户状态为正常,保存用户地址,如果用户只有一个地址则设置为默认地址
      *
@@ -75,7 +76,7 @@ class UserAddressController extends Controller
     }
 
     /**
-     * 修改地址
+     * [地址管理]修改地址
      *
      * @description 根据用户编号查询用户数据,用户状态为正常,根据地址编号查询地址数据,修改地址信息
      *
@@ -102,7 +103,7 @@ class UserAddressController extends Controller
     }
 
     /**
-     * 删除地址
+     * [地址管理]删除地址
      *
      * @description 根据用户编号查询用户数据,用户状态为正常,根据地址编号查询地址数据,删除地址
      *
@@ -116,7 +117,7 @@ class UserAddressController extends Controller
     }
 
     /**
-     * 设置默认地址
+     * [地址管理]设置默认地址
      *
      * @description 根据用户编号查询用户数据,用户状态为正常,根据地址编号查询地址数据,设置为默认地址
      *

+ 32 - 53
app/Http/Controllers/Client/UserController.php

@@ -21,7 +21,7 @@ class UserController extends Controller
     }
 
     /**
-     * 获取用户信息
+     * [用户]获取用户信息
      *
      * 获取当前用户的信息
      *
@@ -43,7 +43,7 @@ class UserController extends Controller
     }
 
     /**
-     * 修改用户信息
+     * [用户]修改用户信息
      *
      * 修改当前用户的信息
      *
@@ -62,58 +62,13 @@ class UserController extends Controller
     {
         $data = $request->all();
 
-        return $this->service->updateUserInfo($data);
-    }
-
-    /**
-     * 获取用户钱包
-     *
-     * 获取当前用户的钱包信息
-     *
-     * @authenticated
-     *
-     * @response {
-     *   "code": 200,
-     *   "message": "获取成功",
-     *   "data": {
-     *     "balance": 100.00,
-     *     "freeze": 0.00
-     *   }
-     * }
-     */
-    public function wallet()
-    {
-        return $this->service->getUserWallet();
-    }
-
-    /**
-     * 用户提现
-     *
-     * 提现用户的余额
-     *
-     * @authenticated
-     *
-     * @bodyParam amount decimal 提现金额. Example: 100.00
-     * @bodyParam type string 提现方式. Example: wechat
-     * @bodyParam area_code string 行政区划代码. Example: 330100
-     *
-     * @response {
-     *   "code": 200,
-     *   "message": "提现成功",
-     *   "data": null
-     * }
-     */
-    public function withdraw(Request $request)
-    {
-        $amount = $request->input('amount');
-        $type = $request->input('type', 'wechat');
-        $area_code = $request->input('area_code', '');
+        $result = $this->service->updateUserInfo($data);
 
-        return $this->service->withdraw($amount, $type, $area_code);
+        return $result;
     }
 
     /**
-     * 用户反馈
+     * [用户]用户反馈
      *
      * 提交用户的反馈信息
      *
@@ -131,11 +86,13 @@ class UserController extends Controller
     {
         $content = $request->input('content');
 
-        return $this->service->feedback($content);
+        $result = $this->service->feedback($content);
+
+        return $result;
     }
 
     /**
-     * 申请成为技师
+     * [用户]申请成为技师
      *
      * 申请成为技师
      *
@@ -159,6 +116,28 @@ class UserController extends Controller
         $work_years = $request->input('work_years');
         $intention_city = $request->input('intention_city');
 
-        return $this->service->applyCoach($mobile, $gender, $work_years, $intention_city);
+        $result = $this->service->applyCoach($mobile, $gender, $work_years, $intention_city);
+
+        return $result;
+    }
+
+    /**
+     * [用户]生成二维码
+     *
+     * @description 生成当前用户的邀请码和对应的二维码
+     *
+     * @response {
+     *   "code": 200,
+     *   "message": "生成成功",
+     *   "data": {
+     *     "invite_code": "ABC123",
+     *     "qr_code": "data:image/png;base64,..."
+     *   }
+     * }
+     */
+    public function generateInviteCode()
+    {
+        // 调用服务层生成邀请码
+        return $this->service->generateInviteCode();
     }
 }

+ 24 - 2
app/Http/Controllers/Client/WalletController.php

@@ -4,6 +4,7 @@ namespace App\Http\Controllers\Client;
 
 use App\Http\Controllers\Controller;
 use App\Services\Client\WalletService;
+use Auth;
 
 /**
  * @group 用户端
@@ -20,7 +21,7 @@ class WalletController extends Controller
     }
 
     /**
-     * 获取钱包明细
+     * [钱包管理]获取钱包明细
      *
      * 获取钱包明细
      *
@@ -36,6 +37,27 @@ class WalletController extends Controller
      */
     public function records()
     {
-        return $this->service->getWalletRecords();
+        return $this->service->getWalletRecords(Auth::user()->id, request('per_page', 10));
+    }
+
+    /**
+     * [钱包管理]获取用户钱包
+     *
+     * 获取当前用户的钱包信息
+     *
+     * @authenticated
+     *
+     * @response {
+     *   "code": 200,
+     *   "message": "获取成功",
+     *   "data": {
+     *     "balance": 100.00,
+     *     "freeze": 0.00
+     *   }
+     * }
+     */
+    public function wallet()
+    {
+        return $this->service->getUserWallet(Auth::user()->id);
     }
 }

+ 1 - 1
app/Services/Client/CoachService.php

@@ -12,7 +12,7 @@ class CoachService
     /**
      * 获取技师列表
      */
-    public function getCoachList($latitude, $longitude)
+    public function getNearCoachList($latitude, $longitude)
     {
         $page = request()->get('page', 1);
         $perPage = request()->get('per_page', 15);

+ 4 - 4
app/Services/Client/OrderService.php

@@ -150,13 +150,13 @@ class OrderService
 
             // 6. 验证金额和余额
             abort_if($amounts['total_amount'] <= 0, 400, '订单金额异常');
-            if ($amounts['payment_type'] == 'balance') {
+            if ($data['payment_type'] == 'balance') {
                 $wallet = $user->wallet;
                 abort_if($wallet->available_balance < $amounts['balance_amount'], 400, '可用余额不足');
             }
 
             // 7. 创建订单记录
-            $order = $this->createOrderRecord($userId, $data, $orderType, $address, (object) $amounts);
+            $order = $this->createOrderRecord($userId, $data, $orderType, $address, $data['payment_type'], (object) $amounts);
 
             // 8. 余额支付处理
             if ($order->payment_type == 'balance') {
@@ -212,7 +212,7 @@ class OrderService
     }
 
     // 提取方法:创建订单记录
-    private function createOrderRecord($userId, $data, $orderType, $address, object $amounts): Order
+    private function createOrderRecord($userId, $data, $orderType, $address, $payment_type, object $amounts): Order
     {
         $order = new Order;
         $order->user_id = $userId;
@@ -226,7 +226,7 @@ class OrderService
         $order->pay_amount = $amounts->pay_amount;
         $order->project_amount = $amounts->project_amount;
         $order->traffic_amount = $orderType == 'add_time' ? 0 : $amounts->delivery_fee;
-        $order->payment_type = ($data['use_balance'] && $amounts->pay_amount == 0) ? 'balance' : null;
+        $order->payment_type = ($data['use_balance'] && $amounts->pay_amount == 0) ? 'balance' : $payment_type;
         $order->service_time = $data['service_time'];
         $order->address_id = $data['address_id'];
         $order->longitude = $address->longitude;

+ 6 - 13
app/Services/Client/TeamService.php

@@ -2,35 +2,28 @@
 
 namespace App\Services\Client;
 
-use App\Models\CoachUser;
 use App\Models\MemberUser;
-use Illuminate\Support\Facades\Auth;
-use Illuminate\Support\Facades\DB;
 
 class TeamService
 {
-
     /**
      * 获取团队列表
      */
-    public function getTeamList()
+    public function getTeamList($userId)
     {
         // 获取当前用户
-        $userId = Auth::id();
         $user = MemberUser::findOrFail($userId);
-        
+
         // 检查用户状态
-        if ($user->state !== 'enable') {
-            throw new \Exception('用户状态异常');
-        }
-        
+        abort_if($user->state !== 'enable', 403, '用户状态异常');
+
         // 获取下级用户列表
         $teamList = MemberUser::where('parent_id', $userId)
             ->where('state', 'enable')
             ->with(['coachUser'])  // 关联技师信息
             ->orderBy('created_at', 'desc')
             ->paginate(10);
-            
+
         return $teamList;
     }
-} 
+}

+ 8 - 0
app/Services/Client/UserAddressService.php

@@ -46,6 +46,8 @@ class UserAddressService
             // 根据用户编号查询用户数据
             $user = MemberUser::findOrFail($userId);
             if ($user->state != 'enable') {
+                DB::rollBack();
+
                 return ['code' => 400, 'message' => '用户状态异常'];
             }
 
@@ -85,6 +87,8 @@ class UserAddressService
             // 根据用户编号查询用户数据
             $user = MemberUser::findOrFail($userId);
             if ($user->state != 'enable') {
+                DB::rollBack();
+
                 return ['code' => 400, 'message' => '用户状态异常'];
             }
 
@@ -130,6 +134,8 @@ class UserAddressService
             // 根据用户编号查询用户数据
             $user = MemberUser::findOrFail($userId);
             if ($user->state != 'enable') {
+                DB::rollBack();
+
                 return ['code' => 400, 'message' => '用户状态异常'];
             }
 
@@ -166,6 +172,8 @@ class UserAddressService
             // 根据用户编号查询用户数据
             $user = MemberUser::findOrFail($userId);
             if ($user->state != 'enable') {
+                DB::rollBack();
+
                 return ['code' => 400, 'message' => '用户状态异常'];
             }
 

+ 120 - 138
app/Services/Client/UserService.php

@@ -2,185 +2,167 @@
 
 namespace App\Services\Client;
 
-use App\Models\CoachUser;
-use App\Models\MemberUser;
-use App\Models\SysConfig;
-use App\Models\Wallet;
-use App\Models\WalletWithdrawRecord;
+use App\Models\CoachApplication;
+use App\Models\Feedback;
 use Illuminate\Support\Facades\Auth;
-use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Log;
+use SimpleSoftwareIO\QrCode\Facades\QrCode;
 
 class UserService
 {
     /**
-     * 获取用户信息
+     * 获取当前用户信息
+     *
+     * @return \Illuminate\Http\JsonResponse
      */
     public function getUserInfo()
     {
-        $user = Auth::user();
-        $wallet = Wallet::where('owner_id', $user->id)
-            ->where('owner_type', 'USER')
-            ->first();
-
-        return [
-            'user' => $user,
-            'wallet' => $wallet ? [
-                'id' => $wallet->id,
-                'balance' => $wallet->available_balance,
-            ] : null,
-        ];
+        try {
+            // 获取当前登录用户
+            $user = Auth::user();
+
+            return response()->json([
+                'code' => 200,
+                'message' => '获取成功',
+                'data' => $user,
+            ]);
+        } catch (\Exception $e) {
+            Log::error('获取用户信息失败: '.$e->getMessage());
+            throw $e;
+        }
     }
 
     /**
-     * 修改用户信息
+     * 更新当前用户信息
+     *
+     * @return \Illuminate\Http\JsonResponse
      */
     public function updateUserInfo(array $data)
     {
-        $user = Auth::user();
-        $user->update($data);
-
-        return ['message' => '修改成功'];
+        try {
+            // 更新用户信息
+            $user = Auth::user();
+            $user->update($data);
+
+            return response()->json([
+                'code' => 200,
+                'message' => '修改成功',
+                'data' => null,
+            ]);
+        } catch (\Exception $e) {
+            Log::error('更新用户信息失败: '.$e->getMessage());
+            throw $e;
+        }
     }
 
     /**
-     * 获取用户钱包
+     * 提交用户反馈
+     *
+     * @return \Illuminate\Http\JsonResponse
      */
-    public function getUserWallet()
+    public function feedback(string $content)
     {
-        $user = Auth::user();
-        $wallet = $user->wallet;
-        if (! $wallet) {
-            throw new \Exception('钱包不存在');
+        try {
+            // 保存用户反馈
+            // Feedback::create([
+            //     'user_id' => Auth::id(),
+            //     'content' => $content,
+            // ]);
+
+            // return response()->json([
+            //     'code' => 200,
+            //     'message' => '提交成功',
+            //     'data' => null,
+            // ]);
+        } catch (\Exception $e) {
+            Log::error('提交反馈失败: '.$e->getMessage());
+            throw $e;
         }
-
-        return $wallet;
-
     }
 
     /**
-     * 用户提现
+     * 申请成为技师
+     *
+     * @return \Illuminate\Http\JsonResponse
      */
-    public function withdraw($amount, $type = 'wechat', $area_code = '')
+    public function applyCoach(string $mobile, string $gender, string $work_years, string $intention_city)
     {
-        // 获取当前用户
-        $user = Auth::user();
-        if (! $user || $user->state !== 'enable') {
-            throw new \Exception('用户状态异常');
-        }
-
-        // 获取钱包并检查余额
-        $wallet = $user->wallet;
-        if ($wallet->state !== 'enable') {
-            throw new \Exception('钱包已冻结');
-        }
-
-        if ($wallet->available_balance < $amount) {
-            throw new \Exception('钱包余额不足');
-        }
-
-        $config = SysConfig::where('key', 'withdraw_min_amount')->first();
-        $configValue = json_decode($config?->value, true);
-
-        $minAmount = $configValue['minAmount'] ?? 1;
-        $maxAmount = $configValue['maxAmount'] ?? 10000;
-        $fee = $configValue['fee'] ?? 0.00;
-
-        if ($amount < $minAmount) {
-            throw new \Exception("提现金额不能小于{$minAmount}元");
-        }
-
-        if ($amount > $maxAmount) {
-            throw new \Exception("提现金额不能大于{$maxAmount}元");
-        }
-
-        // 创建提现记录
-        DB::transaction(function () use ($amount, $fee, $wallet, $type, $area_code) {
-            WalletWithdrawRecord::create([
-                'wallet_id' => $wallet->id,
-                'amount' => $amount,    // 提现金额
-                'withdraw_type' => $type,   // 提现方式
-                'fee' => $fee,    // 提现手续费
-                'area_code' => $area_code,    // 行政区划代码
-                'state' => 'processing',    // 状态
+        try {
+            // 创建技师申请记录
+            // CoachApplication::create([
+            //     'user_id' => Auth::id(),
+            //     'mobile' => $mobile,
+            //     'gender' => $gender,
+            //     'work_years' => $work_years,
+            //     'intention_city' => $intention_city,
+            // ]);
+
+            return response()->json([
+                'code' => 200,
+                'message' => '申请成功',
+                'data' => null,
             ]);
-
-            // 扣除钱包余额
-            $wallet->available_balance -= $amount;
-            $wallet->total_balance -= $amount;
-            $wallet->save();
-        });
-
-        return ['message' => '提现申请已提交'];
+        } catch (\Exception $e) {
+            Log::error('申请成为技师失败: '.$e->getMessage());
+            throw $e;
+        }
     }
 
     /**
-     * 用户反馈
+     * 生成用户邀请码
+     *
+     * @return \Illuminate\Http\JsonResponse
      */
-    public function feedback($content)
+    public function generateInviteCode()
     {
-        $user = Auth::user();
-        if (! $user || $user->state !== 'enable') {
-            throw new \Exception('用户状态异常');
-        }
-
-        // UserFeedback::create([
-        //     'owner_id' => $user->id,
-        //     'owner_type' => MemberUser::class,
-        //     'content' => $content
-        // ]);
+        try {
+            // 获取当前用户
+            $user = Auth::user();
 
-        return ['message' => '反馈已提交'];
-    }
+            // 生成邀请码
+            $inviteCode = strtoupper(substr(md5($user->id), 0, 6));
 
-    /**
-     * 申请成为技师
-     */
-    public function applyCoach($mobile = '', $gender = '', $work_years = '', $intention_city = '')
-    {
-        // 获取当前用户
-        $user = Auth::user();
+            // 生成带参数的网页链接
+            $qrContent = config('app.url').'/invite?'.http_build_query([
+                'user_id' => $user->id,
+                'role' => 'user',
+                'code' => $inviteCode,
+            ]);
 
-        // 检查用户状态
-        if ($user->state !== 'enable') {
-            throw new \Exception('用户状态异常');
-        }
+            // 使用QrCode库生成SVG格式的二维码
+            $qrImage = QrCode::format('svg')
+                ->size(200)
+                ->margin(2)
+                ->encoding('UTF-8')
+                ->generate($qrContent);
 
-        // 检查是否已经申请过
-        $coach = $user->coach;
-        if ($coach) {
-            if ($coach->state === 'pending') {
-                throw new \Exception('您已提交申请,请等待审核');
-            }
-            if ($coach->state === 'enable') {
-                throw new \Exception('您已经是技师了');
-            }
-            if ($coach->state === 'disable') {
-                throw new \Exception('您的技师资格已被禁用');
-            }
-        }
+            // 将SVG转为base64
+            $qrBase64 = base64_encode($qrImage);
 
-        // 创建技师申请
-        DB::transaction(function () use ($user, $mobile, $gender, $work_years, $intention_city) {
-            // 创建技师基本信息
-            $coach = CoachUser::create([
+            // 记录生成日志
+            Log::info('用户生成邀请码:', [
                 'user_id' => $user->id,
-                'state' => 'pending',  // 待审核状态
+                'invite_code' => $inviteCode,
+                'invite_url' => $qrContent,  // 记录生成的邀请链接
             ]);
 
-            $coach->infoRecords()->create([
-                'nickname' => $user->nickname ?? '',  // 姓名
-                'mobile' => $mobile,    // 服务电话
-                'avatar' => $user->avatar ?? '',  // 头像
-                'gender' => $gender ?? 'unknown',  // 性别
-                'work_years' => $work_years ?? '',  // 从业年份
-                'intention_city' => $intention_city ?? '',   // 意向城市
-                'state' => 'pending',
+            return response()->json([
+                'code' => 200,
+                'message' => '生成成功',
+                'data' => [
+                    'invite_code' => $inviteCode,
+                    'invite_url' => $qrContent,  // 返回邀请链接
+                    'qr_code' => 'data:image/svg+xml;base64,'.$qrBase64,
+                ],
             ]);
 
-        });
+        } catch (\Exception $e) {
+            Log::error('生成邀请码失败: '.$e->getMessage());
 
-        return [
-            'message' => '申请已提交,请等待审核',
-        ];
+            return response()->json([
+                'code' => 500,
+                'msg' => '生成二维码失败,请稍后再试。',
+            ]);
+        }
     }
 }

+ 55 - 15
app/Services/Client/WalletService.php

@@ -3,30 +3,70 @@
 namespace App\Services\Client;
 
 use App\Models\MemberUser;
-use App\Models\WalletTransRecord;
-use Illuminate\Support\Facades\Auth;
+use Illuminate\Support\Facades\Log;
 
 class WalletService
 {
     /**
-     * 获取钱包明细
+     * [钱包管理]获取钱包明细
+     *
+     * @param  int  $userId  用户ID
+     * @param  int  $perPage  每页记录数
+     * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
+     *
+     * @throws \Exception
      */
-    public function getWalletRecords()
+    public function getWalletRecords($userId, $perPage = 10)
     {
-        // 获取当前用户
-        $user = Auth::user();
+        try {
+            // 获取当前用户
+            $user = MemberUser::find($userId);
 
-        // 检查用户状态
-        if ($user->state !== 'enable') {
-            throw new \Exception('用户状态异常');
+            // 检查用户是否存在
+            abort_if(! $user, 400, '用户不存在');
+            // 检查用户状态
+            abort_if($user->state !== 'enable', 400, '用户状态异常');
+
+            // 获取钱包交易记录
+            $records = $user->wallet->transRecords()
+                ->where('state', 'enable')
+                ->orderBy('created_at', 'desc')
+                ->paginate($perPage);
+
+            return $records;
+
+        } catch (\Exception $e) {
+            // 记录错误日志
+            \Log::error('获取钱包明细失败', [
+                'userId' => $userId,
+                'error' => $e->getMessage(),
+                'trace' => $e->getTraceAsString(),
+            ]);
+            throw $e;
         }
+    }
+
+    /**
+     * 获取用户钱包
+     */
+    public function getUserWallet($userId)
+    {
+        try {
+            $user = MemberUser::find($userId);
 
-        // 获取钱包交易记录
-        $records = WalletTransRecord::where('wallet_id', $user->wallet->id)
-            ->where('state', 'enable')
-            ->orderBy('created_at', 'desc')
-            ->paginate(10);
+            abort_if(! $user, 400, '用户不存在');
+            abort_if($user->state !== 'enable', 400, '用户状态异常');
 
-        return $records;
+            $wallet = $user->wallet;
+            if (! $wallet) {
+                Log::warning('用户钱包不存在', ['user_id' => $user->id]);
+                throw new \Exception('钱包不存在');
+            }
+
+            return $wallet;
+        } catch (\Exception $e) {
+            Log::error('获取用户钱包失败', ['error' => $e->getMessage()]);
+            throw $e;
+        }
     }
 }

+ 1 - 0
composer.json

@@ -13,6 +13,7 @@
         "laravel/tinker": "^2.9",
         "monolog/monolog": "^3.5",
         "rap2hpoutre/laravel-log-viewer": "^2.4",
+        "simplesoftwareio/simple-qrcode": "^4.2",
         "slowlyo/owl-admin": "^4.0",
         "slowlyo/owl-dict": "^1.3",
         "slowlyo/owl-log-viewer": "^1.0",

+ 173 - 1
composer.lock

@@ -4,8 +4,62 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "f745cf2f0362f3f57752e41fd566fc76",
+    "content-hash": "d6497144549bf544f9ccb8210f9c2e22",
     "packages": [
+        {
+            "name": "bacon/bacon-qr-code",
+            "version": "2.0.8",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/Bacon/BaconQrCode.git",
+                "reference": "8674e51bb65af933a5ffaf1c308a660387c35c22"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/Bacon/BaconQrCode/zipball/8674e51bb65af933a5ffaf1c308a660387c35c22",
+                "reference": "8674e51bb65af933a5ffaf1c308a660387c35c22",
+                "shasum": ""
+            },
+            "require": {
+                "dasprid/enum": "^1.0.3",
+                "ext-iconv": "*",
+                "php": "^7.1 || ^8.0"
+            },
+            "require-dev": {
+                "phly/keep-a-changelog": "^2.1",
+                "phpunit/phpunit": "^7 | ^8 | ^9",
+                "spatie/phpunit-snapshot-assertions": "^4.2.9",
+                "squizlabs/php_codesniffer": "^3.4"
+            },
+            "suggest": {
+                "ext-imagick": "to generate QR code images"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "BaconQrCode\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-2-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Ben Scholzen 'DASPRiD'",
+                    "email": "mail@dasprids.de",
+                    "homepage": "https://dasprids.de/",
+                    "role": "Developer"
+                }
+            ],
+            "description": "BaconQrCode is a QR code generator for PHP.",
+            "homepage": "https://github.com/Bacon/BaconQrCode",
+            "support": {
+                "issues": "https://github.com/Bacon/BaconQrCode/issues",
+                "source": "https://github.com/Bacon/BaconQrCode/tree/2.0.8"
+            },
+            "time": "2022-12-07T17:46:57+00:00"
+        },
         {
             "name": "brick/math",
             "version": "0.12.1",
@@ -135,6 +189,56 @@
             ],
             "time": "2024-02-09T16:56:22+00:00"
         },
+        {
+            "name": "dasprid/enum",
+            "version": "1.0.6",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/DASPRiD/Enum.git",
+                "reference": "8dfd07c6d2cf31c8da90c53b83c026c7696dda90"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/DASPRiD/Enum/zipball/8dfd07c6d2cf31c8da90c53b83c026c7696dda90",
+                "reference": "8dfd07c6d2cf31c8da90c53b83c026c7696dda90",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1 <9.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^7 || ^8 || ^9 || ^10 || ^11",
+                "squizlabs/php_codesniffer": "*"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "DASPRiD\\Enum\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-2-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Ben Scholzen 'DASPRiD'",
+                    "email": "mail@dasprids.de",
+                    "homepage": "https://dasprids.de/",
+                    "role": "Developer"
+                }
+            ],
+            "description": "PHP 7.1 enum implementation",
+            "keywords": [
+                "enum",
+                "map"
+            ],
+            "support": {
+                "issues": "https://github.com/DASPRiD/Enum/issues",
+                "source": "https://github.com/DASPRiD/Enum/tree/1.0.6"
+            },
+            "time": "2024-08-09T14:30:48+00:00"
+        },
         {
             "name": "dflydev/dot-access-data",
             "version": "v3.0.3",
@@ -3823,6 +3927,74 @@
             ],
             "time": "2024-02-20T11:51:46+00:00"
         },
+        {
+            "name": "simplesoftwareio/simple-qrcode",
+            "version": "4.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/SimpleSoftwareIO/simple-qrcode.git",
+                "reference": "916db7948ca6772d54bb617259c768c9cdc8d537"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/SimpleSoftwareIO/simple-qrcode/zipball/916db7948ca6772d54bb617259c768c9cdc8d537",
+                "reference": "916db7948ca6772d54bb617259c768c9cdc8d537",
+                "shasum": ""
+            },
+            "require": {
+                "bacon/bacon-qr-code": "^2.0",
+                "ext-gd": "*",
+                "php": ">=7.2|^8.0"
+            },
+            "require-dev": {
+                "mockery/mockery": "~1",
+                "phpunit/phpunit": "~9"
+            },
+            "suggest": {
+                "ext-imagick": "Allows the generation of PNG QrCodes.",
+                "illuminate/support": "Allows for use within Laravel."
+            },
+            "type": "library",
+            "extra": {
+                "laravel": {
+                    "providers": [
+                        "SimpleSoftwareIO\\QrCode\\QrCodeServiceProvider"
+                    ],
+                    "aliases": {
+                        "QrCode": "SimpleSoftwareIO\\QrCode\\Facades\\QrCode"
+                    }
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "SimpleSoftwareIO\\QrCode\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Simple Software LLC",
+                    "email": "support@simplesoftware.io"
+                }
+            ],
+            "description": "Simple QrCode is a QR code generator made for Laravel.",
+            "homepage": "https://www.simplesoftware.io/#/docs/simple-qrcode",
+            "keywords": [
+                "Simple",
+                "generator",
+                "laravel",
+                "qrcode",
+                "wrapper"
+            ],
+            "support": {
+                "issues": "https://github.com/SimpleSoftwareIO/simple-qrcode/issues",
+                "source": "https://github.com/SimpleSoftwareIO/simple-qrcode/tree/4.2.0"
+            },
+            "time": "2021-02-08T20:43:55+00:00"
+        },
         {
             "name": "slowlyo/laravel-support",
             "version": "v0.0.5",

+ 2 - 2
config/scribe.php

@@ -49,11 +49,11 @@ return [
         'in' => 'bearer',
         'name' => 'token',
         'use_value' => env('SCRIBE_AUTH_KEY'),
-        'placeholder' => env('{YOUR_AUTH_KEY}'),
+        'placeholder' => env('YOUR_AUTH_KEY'),
         'extra_info' => 'You can retrieve your token by visiting your dashboard and clicking <b>Generate API token</b>.',
     ],
 
-    'intro_text' => <<<INTRO
+    'intro_text' => <<<'INTRO'
 欢迎使用 Owl Admin API 接口文档。
 
 该文档提供了所有可用的API接口信息。

+ 11 - 4
routes/api.php

@@ -7,6 +7,7 @@ use App\Http\Controllers\Client\OrderController;
 use App\Http\Controllers\Client\ProjectController;
 use App\Http\Controllers\Client\UserAddressController;
 use App\Http\Controllers\Client\UserController;
+use App\Http\Controllers\Client\WalletController;
 use App\Http\Controllers\ScribeController;
 use Illuminate\Support\Facades\Route;
 
@@ -40,15 +41,15 @@ Route::middleware('auth:sanctum')->group(function () {
         // 更新用户信息
         Route::put('/', [UserController::class, 'update']);
 
-        // 获取钱包信息
-        Route::get('wallet', [UserController::class, 'wallet']);
         // 提现
         Route::post('withdraw', [UserController::class, 'withdraw']);
 
         // 用户反馈
-        // Route::post('feedback', [UserController::class, 'feedback']);
+        Route::post('/feedback', [UserController::class, 'feedback']);
         // 申请成为技师
-        Route::post('apply-coach', [UserController::class, 'applyCoach']);
+        Route::post('/apply-coach', [UserController::class, 'applyCoach']);
+        // 生成邀请码
+        Route::get('/invite-code', [UserController::class, 'generateInviteCode']);
     });
 
     Route::prefix('project')->group(function () {
@@ -105,4 +106,10 @@ Route::middleware('auth:sanctum')->group(function () {
         Route::post('/orders/assign-coach', [OrderController::class, 'assignCoach']);
     });
 
+    // 钱包相关
+    Route::prefix('wallet')->group(function () {
+        Route::get('records', [WalletController::class, 'records']);
+        // 获取钱包信息
+        Route::get('wallet', [WalletController::class, 'wallet']);
+    });
 });

+ 10 - 9
script/bin/mylog

@@ -6,43 +6,44 @@ interactive_mode() {
         read -p "请输入命令 (c: 清空日志, p: 打印日志, l: 查看日志, t: 实时查看日志, m: 复制错误信息, q: 退出): " cmd
         case $cmd in
             c)
-                > storage/logs/laravel-$(date +%Y-%m-%d).log
+                > storage/logs/laravel.log
                 echo "日志已清空。"
                 ;;
             p)
-                errors=$(cat storage/logs/laravel-$(date +%Y-%m-%d).log | grep "ERROR")
+                errors=$(cat storage/logs/laravel.log | grep "ERROR")
                 if [ -n "$errors" ]; then
                     echo
-                    cat storage/logs/laravel-$(date +%Y-%m-%d).log | grep "ERROR"
+                    cat storage/logs/laravel.log | grep "ERROR"
                     echo
                 else
                     echo "未发现错误信息。"
                 fi
                 ;;
             l)
-                less storage/logs/laravel-$(date +%Y-%m-%d).log
+                less storage/logs/laravel.log
                 ;;
             t)
-                tail -f -n 1000 storage/logs/laravel-$(date +%Y-%m-%d).log
+                tail -f -n 1000 storage/logs/laravel.log
                 ;;
             q)
                 exit 0
                 ;;
             m)
                 if [ "$(uname)" == "Darwin" ]; then
-                    errors=$(cat storage/logs/laravel-$(date +%Y-%m-%d).log | grep "ERROR")
+                    errors=$(cat storage/logs/laravel.log | grep "ERROR")
                     if [ -n "$errors" ]; then
-                        cat storage/logs/laravel-$(date +%Y-%m-%d).log | grep -m1 "ERROR" | \
+                        cat storage/logs/laravel.log | grep -m1 "ERROR" | \
                         awk '{print $0,"\n\n","上面是错误日志,请帮我修复这个bug,并始终用中文语言回答。"}' | pbcopy
                         echo "已复制错误信息到剪贴板,你可以问AI助手,Ta会给你解决错误。"
                     else
                         echo "未发现错误信息。"
                     fi
                 else
-                    errors=$(cat storage/logs/laravel-$(date +%Y-%m-%d).log | grep "ERROR")
+                    errors=$(cat storage/logs/laravel.log | grep "ERROR")
                     if [ -n "$errors" ]; then
                         echo "正在复制错误信息到剪贴板..."
-                        cat storage/logs/laravel-$(date +%Y-%m-%d).log | grep "ERROR" | pbcopy
+                        cat storage/logs/laravel.log | grep -m8 "ERROR" | \
+                        awk '{print $0,"\n\n","上面是错误日志,请帮我修复这个bug,并始终用中文语言回答。"}' | xclip -selection clipboard
                         echo "已复制错误信息到剪贴板,你可以问AI助手,他会给你解决错误。"
                     else
                         echo "未发现错误信息。"