diff --git a/app/Admin/Forms/DealerProductSaleRule.php b/app/Admin/Forms/DealerProductSaleRule.php index 9ed63a02..5700bf97 100644 --- a/app/Admin/Forms/DealerProductSaleRule.php +++ b/app/Admin/Forms/DealerProductSaleRule.php @@ -96,7 +96,6 @@ class DealerProductSaleRule extends Form implements LazyRenderable $product = DealerProduct::findOrFail($productId); return [ 'saleRules' => $product->saleRules, - // 'email' => 'John.Doe@gmail.com', ]; } } diff --git a/app/Endpoint/Api/Http/Controllers/Dealer/OrderController.php b/app/Endpoint/Api/Http/Controllers/Dealer/OrderController.php index 120ea9ca..eaefedb5 100644 --- a/app/Endpoint/Api/Http/Controllers/Dealer/OrderController.php +++ b/app/Endpoint/Api/Http/Controllers/Dealer/OrderController.php @@ -3,9 +3,13 @@ namespace App\Endpoint\Api\Http\Controllers\Dealer; use App\Endpoint\Api\Http\Controllers\Controller; +use App\Endpoint\Api\Http\Resources\Dealer\OrderResource; +use App\Exceptions\BizException; use App\Models\DealerProduct; use App\Services\Dealer\OrderService; use Illuminate\Http\Request; +use Illuminate\Support\Facades\DB; +use Throwable; class OrderController extends Controller { @@ -17,6 +21,34 @@ class OrderController extends Controller { } + public function store(Request $request, OrderService $orderService) + { + $input = $request->validate([ + 'shipping_address_id' => ['bail', 'required', 'int'], + 'product_id'=>['bail', 'required', 'int', 'min:0'], + 'num'=>['bail', 'required', 'int', 'min:1'], + ], [], [ + 'product_id' => '商品', + 'num' => '数量', + 'shipping_address_id' => '收货地址', + ]); + $product = DealerProduct::online()->findOrFail($input['product_id']); + try { + DB::beginTransaction(); + $order = $orderService->createOrder($request->user(), $product, $input['num'], $input['shipping_address_id']); + DB::commit(); + } catch (BizException $e) { + DB::rollBack(); + throw $e; + } catch (Throwable $th) { + DB::rollBack(); + report($th); + throw new BizException('下单失败,请稍后再试'); + } + + return OrderResource::make($order); + } + public function show($id, Request $request) { } diff --git a/app/Endpoint/Api/Http/Controllers/Dealer/UserController.php b/app/Endpoint/Api/Http/Controllers/Dealer/UserController.php index 9c76866b..e52f6b60 100644 --- a/app/Endpoint/Api/Http/Controllers/Dealer/UserController.php +++ b/app/Endpoint/Api/Http/Controllers/Dealer/UserController.php @@ -3,6 +3,7 @@ namespace App\Endpoint\Api\Http\Controllers\Dealer; use App\Endpoint\Api\Http\Controllers\Controller; +use App\Endpoint\Api\Http\Resources\Dealer\DealerResource; use App\Endpoint\Api\Http\Resources\Dealer\UserInfoResource; use Illuminate\Http\Request; @@ -20,6 +21,7 @@ class UserController extends Controller return response()->json([ 'phone' => $user->phone, + 'dealer'=> $user->dealer ? DealerResource::make($user->dealer) : [], 'user_info' => UserInfoResource::make($user->userInfo), ]); } diff --git a/app/Endpoint/Api/Http/Resources/Dealer/DealerResource.php b/app/Endpoint/Api/Http/Resources/Dealer/DealerResource.php new file mode 100644 index 00000000..6b69a11c --- /dev/null +++ b/app/Endpoint/Api/Http/Resources/Dealer/DealerResource.php @@ -0,0 +1,24 @@ + $this->lvl, + 'lvl_name'=> $this->lvl_text, + 'sale_values'=> '0.00', //todo-当前团队业绩 + 'guanli_values'=> '0.00', //todo-预计管理津贴 + ]; + } +} diff --git a/app/Endpoint/Api/Http/Resources/Dealer/OrderProductResource.php b/app/Endpoint/Api/Http/Resources/Dealer/OrderProductResource.php new file mode 100644 index 00000000..2ede93b9 --- /dev/null +++ b/app/Endpoint/Api/Http/Resources/Dealer/OrderProductResource.php @@ -0,0 +1,25 @@ + $this->name, + 'cover'=> $this->cover, + 'price'=>$this->price, + 'sale_price' =>$this->sale_price, + 'qty' =>$this->qty, + ]; + } +} diff --git a/app/Endpoint/Api/Http/Resources/Dealer/OrderResource.php b/app/Endpoint/Api/Http/Resources/Dealer/OrderResource.php new file mode 100644 index 00000000..77cf7f20 --- /dev/null +++ b/app/Endpoint/Api/Http/Resources/Dealer/OrderResource.php @@ -0,0 +1,27 @@ +$this->sn, + 'product'=>OrderProductResource::collection($this->products), + 'total_amount' => $this->total_amount, + 'created_at' => $this->created_at->toDateTimeString(), + 'status' => $this->status, + 'pay_info' => $this->pay_info??$this->consignor->dealer->pay_info, + 'pay_image'=> $this->pay_image, + ]; + } +} diff --git a/app/Endpoint/Api/Http/Resources/Dealer/OrderSimpleResource.php b/app/Endpoint/Api/Http/Resources/Dealer/OrderSimpleResource.php new file mode 100644 index 00000000..c039bd28 --- /dev/null +++ b/app/Endpoint/Api/Http/Resources/Dealer/OrderSimpleResource.php @@ -0,0 +1,24 @@ +$this->sn, + 'total_amount' => $this->total_amount, + 'created_at' => $this->created_at->toDateTimeString(), + 'status' => $this->status, + ]; + } +} diff --git a/app/Endpoint/Api/Http/Resources/Dealer/ProductResource.php b/app/Endpoint/Api/Http/Resources/Dealer/ProductResource.php index efc1933c..3888ecba 100644 --- a/app/Endpoint/Api/Http/Resources/Dealer/ProductResource.php +++ b/app/Endpoint/Api/Http/Resources/Dealer/ProductResource.php @@ -23,7 +23,7 @@ class ProductResource extends JsonResource 'price' => (string) $this->price, 'is_online' => $this->isOnline(), 'description' => (string) $this->description, - // 'lvl_rules' => $this->whenLoaded('lvlRules', ProductLvlRuleResource::collection($this->lvlRules)), + 'lvl_rules' => $this->whenLoaded('lvlRules', ProductLvlRuleResource::collection($this->lvlRules)), 'sale_rules' => $this->whenLoaded('saleRules', ProductSaleRuleResource::collection($this->saleRules)), ]; } diff --git a/app/Endpoint/Api/Http/Resources/Dealer/ProductSaleRuleResource.php b/app/Endpoint/Api/Http/Resources/Dealer/ProductSaleRuleResource.php index 030daf95..747e6528 100644 --- a/app/Endpoint/Api/Http/Resources/Dealer/ProductSaleRuleResource.php +++ b/app/Endpoint/Api/Http/Resources/Dealer/ProductSaleRuleResource.php @@ -16,6 +16,7 @@ class ProductSaleRuleResource extends JsonResource { return [ 'qty' => $this->qty, + 'price'=>$this->price, ]; } } diff --git a/app/Endpoint/Api/Http/Resources/Dealer/UserInfoResource.php b/app/Endpoint/Api/Http/Resources/Dealer/UserInfoResource.php index 43d5248e..26dbc982 100644 --- a/app/Endpoint/Api/Http/Resources/Dealer/UserInfoResource.php +++ b/app/Endpoint/Api/Http/Resources/Dealer/UserInfoResource.php @@ -20,9 +20,6 @@ class UserInfoResource extends JsonResource 'gender' => (string) $this->gender, 'birthday' => (string) $this->birthday?->toDateString(), 'code' => (string) $this->code, - 'sale_values'=> '0.00', //todo-当前团队业绩 - 'guanli_values'=> '0.00', //todo-预计管理津贴 - 'lvl_name'=> '签约经销商', ]; } } diff --git a/app/Endpoint/Api/routes.php b/app/Endpoint/Api/routes.php index 2394f3bc..2ebd4eaf 100644 --- a/app/Endpoint/Api/routes.php +++ b/app/Endpoint/Api/routes.php @@ -225,5 +225,7 @@ Route::group([ //计算商品下单价格 Route::get('orders/total-amount', [Dealer\OrderController::class, 'totalAmount']); + //下单 + Route::post('orders', [Dealer\OrderController::class, 'store']); }); }); diff --git a/app/Enums/DealerLvl.php b/app/Enums/DealerLvl.php index 4d75cae8..86ae791a 100644 --- a/app/Enums/DealerLvl.php +++ b/app/Enums/DealerLvl.php @@ -10,6 +10,21 @@ enum DealerLvl: int { case Secondary = 5; case Top = 6; + /** + * @return string + */ + public function text() + { + return match ($this) { + static::None => '普通用户', + static::Gold => '金牌经销商', + static::Special => '特邀经销商', + static::Contracted => '签约经销商', + static::Secondary => '二级经销商', + static::Top => '一级经销商', + }; + } + /** * @return string */ diff --git a/app/Models/Dealer.php b/app/Models/Dealer.php index df9cbadd..418c93d0 100644 --- a/app/Models/Dealer.php +++ b/app/Models/Dealer.php @@ -2,6 +2,7 @@ namespace App\Models; +use App\Casts\JsonArray; use App\Enums\DealerLvl; use Illuminate\Database\Eloquent\Model; @@ -17,6 +18,7 @@ class Dealer extends Model 'lvl' => DealerLvl::class, 'is_sale' => 'bool', 'is_manager' => 'bool', + 'pay_info'=>JsonArray::class, ]; protected $fillable = [ @@ -25,4 +27,9 @@ class Dealer extends Model 'is_sale', 'is_manager', ]; + + public function getLvlTextAttribute() + { + return $this->lvl->text(); + } } diff --git a/app/Models/DealerOrder.php b/app/Models/DealerOrder.php index a3d2fd51..e2de5405 100644 --- a/app/Models/DealerOrder.php +++ b/app/Models/DealerOrder.php @@ -2,6 +2,7 @@ namespace App\Models; +use App\Casts\JsonArray; use App\Enums\DealerOrderSettleState; use App\Enums\DealerOrderStatus; use Illuminate\Database\Eloquent\Model; @@ -16,6 +17,7 @@ class DealerOrder extends Model protected $casts = [ 'status' => DealerOrderStatus::class, 'settle_state' => DealerOrderSettleState::class, + 'pay_info'=>JsonArray::class, ]; /** @@ -23,7 +25,7 @@ class DealerOrder extends Model */ public function userInfo() { - return $this->belongsTo(UserInfo::class, 'user_id'); + return $this->belongsTo(UserInfo::class, 'user_id', 'user_id'); } /** @@ -33,4 +35,14 @@ class DealerOrder extends Model { return $this->hasMany(DealerOrderProduct::class, 'order_id'); } + + public function user() + { + return $this->belongsTo(User::class, 'user_id'); + } + + public function consignor() + { + return $this->belongsTo(User::class, 'consignor_id'); + } } diff --git a/app/Models/DealerOrderProduct.php b/app/Models/DealerOrderProduct.php index c5383fa9..2175e63d 100644 --- a/app/Models/DealerOrderProduct.php +++ b/app/Models/DealerOrderProduct.php @@ -6,6 +6,17 @@ use Illuminate\Database\Eloquent\Model; class DealerOrderProduct extends Model { + protected $fillable = [ + 'order_id', + 'product_id', + 'name', + 'subtitle', + 'cover', + 'price', + 'sale_price', + 'qty', + ]; + /** * 商品管理津贴规则 */ diff --git a/app/Models/User.php b/app/Models/User.php index 98ae27b4..763668ac 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -392,6 +392,8 @@ class User extends Model implements AuthorizableContract, AuthenticatableContrac $user->wallet()->create(); //初始化余额 $user->balance()->create(); + //初始化经销商信息 + $user->dealer()->create(); return $user; } diff --git a/app/Services/Dealer/OrderService.php b/app/Services/Dealer/OrderService.php index d8177fed..8e93ea28 100644 --- a/app/Services/Dealer/OrderService.php +++ b/app/Services/Dealer/OrderService.php @@ -2,8 +2,13 @@ namespace App\Services\Dealer; +use App\Exceptions\BizException; +use App\Models\DealerOrder; use App\Models\DealerProduct; +use App\Models\ShippingAddress; use App\Models\User; +use App\Models\UserInfo; +use Illuminate\Database\QueryException; class OrderService { @@ -12,7 +17,7 @@ class OrderService * * @param DealerProduct $product * @param integer $number - * @return void + * @return string */ public function totalAmount(User $user, DealerProduct $product, int $number = 0) { @@ -41,7 +46,101 @@ class OrderService return bcmul($salePrice, $number, 2); } - public function createOrder(User $user, DealerProduct $product, int $number = 0) + public function createOrder(User $user, DealerProduct $product, int $number = 0, int $shippingAddressId) { + //判断是否满足当前等级最低补货价 + $totalAmount = $this->totalAmount($user, $product, $number); + foreach ($product->lvlRules as $rule) { + if ($user->dealer && $rule->lvl == $user->dealer->lvl && $totalAmount < $rule->min_order_amount) { + throw new BizException('当前单次补货价格不能低于'.$rule->min_order_amount.'元'); + } + } + + //找到发货人 + $consignor = $this->getConsignor($user); + + //找到收货地址 + $shippingAddress = $this->getShippingAddress($user, $shippingAddressId); + + //保存订单 + $order = new DealerOrder(); + do { + try { + $order->sn = serial_number(); + $order->user_id = $user->id; + $order->consignor_id = $consignor->user_id; + $order->total_amount = $totalAmount; + $order->consignee_name = $shippingAddress->consignee; + $order->consignee_telephone = $shippingAddress->telephone; + $order->consignee_zone = $shippingAddress->zone; + $order->consignee_address = $shippingAddress->address; + $order->save(); + break; + } catch (QueryException $e) { + if (strpos($e->getMessage(), 'Duplicate entry') === false) { + throw $e; + } + } + } while (true); + + //保存订单商品-----一个订单对应一个商品 + $order->products()->create([ + 'order_id' => $order->id, + 'product_id'=> $product->id, + 'name'=> $product->name, + 'subtitle'=> $product->subtitle, + 'cover'=> $product->cover, + 'price' => $product->price, + 'sale_price'=> bcdiv($totalAmount, $number, 2), + 'qty'=> $number, + ]); + + return $order; + } + + /** + * 更新订单发货人 + * + * @return void + */ + protected function updateOrderConsignor(DealerOrder $order) + { + $consignor = $this->getConsignor($order->user, $order->consignor); + $order->update([ + 'consignor_id' => $consignor->user_id, + ]); + } + + /** + * 获取收货地址 + * + * @param \App\Models\User $user + * @param int|null $shippingAddressId + * @return \App\Models\ShippingAddress|null + * + * @throws \Illuminate\Database\Eloquent\ModelNotFoundException + */ + protected function getShippingAddress(User $user, ?int $shippingAddressId = null): ?ShippingAddress + { + if ($shippingAddressId) { + return $user->shippingAddresses()->findOrFail($shippingAddressId); + } + + return $user->shippingAddresses()->where('is_default', true)->first(); + } + + public function getConsignor(User $user, ?User $lastConsignor = null) + { + $query = UserInfo::with('dealer'); + if ($lastConsignor) { + $query->whereIn('user_id', $lastConsignor->userInfo->parent_ids);//上个发货人的上级 + } else { + $query->whereIn('user_id', $user->userInfo->parent_ids);//自己的上级 + } + $consignor = $query->whereHas('dealer', function ($q) use ($user) { + return $q->where('lvl', '>', $user->dealer->lvl);//经销商身份大于自己的 + })->orderBy('depth', 'desc')->first();//深度逆序第一个 + + return $consignor; } } diff --git a/database/migrations/2022_01_12_165458_create_dealer_orders_table.php b/database/migrations/2022_01_12_165458_create_dealer_orders_table.php index 15339b4d..b718ac83 100644 --- a/database/migrations/2022_01_12_165458_create_dealer_orders_table.php +++ b/database/migrations/2022_01_12_165458_create_dealer_orders_table.php @@ -33,6 +33,7 @@ class CreateDealerOrdersTable extends Migration $table->timestamp('shippinged_time')->nullable()->comment('确认收货时间'); $table->timestamps(); + $table->unique('sn'); $table->index(['user_id', 'status']); $table->index('status'); }); diff --git a/database/migrations/2022_01_13_202326_add_pay_info_to_dealers_table.php b/database/migrations/2022_01_13_202326_add_pay_info_to_dealers_table.php new file mode 100644 index 00000000..cb05f3fd --- /dev/null +++ b/database/migrations/2022_01_13_202326_add_pay_info_to_dealers_table.php @@ -0,0 +1,34 @@ +text('pay_info')->nullable()->comment('用户保留的收款信息'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('dealers', function (Blueprint $table) { + // + $table->dropColumn('pay_info'); + }); + } +}