6
0
Fork 0

处理冲突

release
vine_liutk 2022-01-13 21:12:14 +08:00 committed by 李静
parent 9a57d884e7
commit 5a94d3c686
19 changed files with 322 additions and 8 deletions

View File

@ -96,7 +96,6 @@ class DealerProductSaleRule extends Form implements LazyRenderable
$product = DealerProduct::findOrFail($productId); $product = DealerProduct::findOrFail($productId);
return [ return [
'saleRules' => $product->saleRules, 'saleRules' => $product->saleRules,
// 'email' => 'John.Doe@gmail.com',
]; ];
} }
} }

View File

@ -3,9 +3,13 @@
namespace App\Endpoint\Api\Http\Controllers\Dealer; namespace App\Endpoint\Api\Http\Controllers\Dealer;
use App\Endpoint\Api\Http\Controllers\Controller; 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\Models\DealerProduct;
use App\Services\Dealer\OrderService; use App\Services\Dealer\OrderService;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Throwable;
class OrderController extends Controller 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) public function show($id, Request $request)
{ {
} }

View File

@ -3,6 +3,7 @@
namespace App\Endpoint\Api\Http\Controllers\Dealer; namespace App\Endpoint\Api\Http\Controllers\Dealer;
use App\Endpoint\Api\Http\Controllers\Controller; use App\Endpoint\Api\Http\Controllers\Controller;
use App\Endpoint\Api\Http\Resources\Dealer\DealerResource;
use App\Endpoint\Api\Http\Resources\Dealer\UserInfoResource; use App\Endpoint\Api\Http\Resources\Dealer\UserInfoResource;
use Illuminate\Http\Request; use Illuminate\Http\Request;
@ -20,6 +21,7 @@ class UserController extends Controller
return response()->json([ return response()->json([
'phone' => $user->phone, 'phone' => $user->phone,
'dealer'=> $user->dealer ? DealerResource::make($user->dealer) : [],
'user_info' => UserInfoResource::make($user->userInfo), 'user_info' => UserInfoResource::make($user->userInfo),
]); ]);
} }

View File

@ -0,0 +1,24 @@
<?php
namespace App\Endpoint\Api\Http\Resources\Dealer;
use Illuminate\Http\Resources\Json\JsonResource;
class DealerResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
*/
public function toArray($request)
{
return [
'lvl' => $this->lvl,
'lvl_name'=> $this->lvl_text,
'sale_values'=> '0.00', //todo-当前团队业绩
'guanli_values'=> '0.00', //todo-预计管理津贴
];
}
}

View File

@ -0,0 +1,25 @@
<?php
namespace App\Endpoint\Api\Http\Resources\Dealer;
use Illuminate\Http\Resources\Json\JsonResource;
class OrderProductResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
*/
public function toArray($request)
{
return [
'name' => $this->name,
'cover'=> $this->cover,
'price'=>$this->price,
'sale_price' =>$this->sale_price,
'qty' =>$this->qty,
];
}
}

View File

@ -0,0 +1,27 @@
<?php
namespace App\Endpoint\Api\Http\Resources\Dealer;
use Illuminate\Http\Resources\Json\JsonResource;
class OrderResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
*/
public function toArray($request)
{
return [
'sn' =>$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,
];
}
}

View File

@ -0,0 +1,24 @@
<?php
namespace App\Endpoint\Api\Http\Resources\Dealer;
use Illuminate\Http\Resources\Json\JsonResource;
class OrderSimpleResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
*/
public function toArray($request)
{
return [
'sn' =>$this->sn,
'total_amount' => $this->total_amount,
'created_at' => $this->created_at->toDateTimeString(),
'status' => $this->status,
];
}
}

View File

@ -23,7 +23,7 @@ class ProductResource extends JsonResource
'price' => (string) $this->price, 'price' => (string) $this->price,
'is_online' => $this->isOnline(), 'is_online' => $this->isOnline(),
'description' => (string) $this->description, '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)), 'sale_rules' => $this->whenLoaded('saleRules', ProductSaleRuleResource::collection($this->saleRules)),
]; ];
} }

View File

@ -16,6 +16,7 @@ class ProductSaleRuleResource extends JsonResource
{ {
return [ return [
'qty' => $this->qty, 'qty' => $this->qty,
'price'=>$this->price,
]; ];
} }
} }

View File

@ -20,9 +20,6 @@ class UserInfoResource extends JsonResource
'gender' => (string) $this->gender, 'gender' => (string) $this->gender,
'birthday' => (string) $this->birthday?->toDateString(), 'birthday' => (string) $this->birthday?->toDateString(),
'code' => (string) $this->code, 'code' => (string) $this->code,
'sale_values'=> '0.00', //todo-当前团队业绩
'guanli_values'=> '0.00', //todo-预计管理津贴
'lvl_name'=> '签约经销商',
]; ];
} }
} }

View File

@ -225,5 +225,7 @@ Route::group([
//计算商品下单价格 //计算商品下单价格
Route::get('orders/total-amount', [Dealer\OrderController::class, 'totalAmount']); Route::get('orders/total-amount', [Dealer\OrderController::class, 'totalAmount']);
//下单
Route::post('orders', [Dealer\OrderController::class, 'store']);
}); });
}); });

View File

@ -10,6 +10,21 @@ enum DealerLvl: int {
case Secondary = 5; case Secondary = 5;
case Top = 6; 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 * @return string
*/ */

View File

@ -2,6 +2,7 @@
namespace App\Models; namespace App\Models;
use App\Casts\JsonArray;
use App\Enums\DealerLvl; use App\Enums\DealerLvl;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
@ -17,6 +18,7 @@ class Dealer extends Model
'lvl' => DealerLvl::class, 'lvl' => DealerLvl::class,
'is_sale' => 'bool', 'is_sale' => 'bool',
'is_manager' => 'bool', 'is_manager' => 'bool',
'pay_info'=>JsonArray::class,
]; ];
protected $fillable = [ protected $fillable = [
@ -25,4 +27,9 @@ class Dealer extends Model
'is_sale', 'is_sale',
'is_manager', 'is_manager',
]; ];
public function getLvlTextAttribute()
{
return $this->lvl->text();
}
} }

View File

@ -2,6 +2,7 @@
namespace App\Models; namespace App\Models;
use App\Casts\JsonArray;
use App\Enums\DealerOrderSettleState; use App\Enums\DealerOrderSettleState;
use App\Enums\DealerOrderStatus; use App\Enums\DealerOrderStatus;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
@ -16,6 +17,7 @@ class DealerOrder extends Model
protected $casts = [ protected $casts = [
'status' => DealerOrderStatus::class, 'status' => DealerOrderStatus::class,
'settle_state' => DealerOrderSettleState::class, 'settle_state' => DealerOrderSettleState::class,
'pay_info'=>JsonArray::class,
]; ];
/** /**
@ -23,7 +25,7 @@ class DealerOrder extends Model
*/ */
public function userInfo() 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'); 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');
}
} }

View File

@ -6,6 +6,17 @@ use Illuminate\Database\Eloquent\Model;
class DealerOrderProduct extends Model class DealerOrderProduct extends Model
{ {
protected $fillable = [
'order_id',
'product_id',
'name',
'subtitle',
'cover',
'price',
'sale_price',
'qty',
];
/** /**
* 商品管理津贴规则 * 商品管理津贴规则
*/ */

View File

@ -392,6 +392,8 @@ class User extends Model implements AuthorizableContract, AuthenticatableContrac
$user->wallet()->create(); $user->wallet()->create();
//初始化余额 //初始化余额
$user->balance()->create(); $user->balance()->create();
//初始化经销商信息
$user->dealer()->create();
return $user; return $user;
} }

View File

@ -2,8 +2,13 @@
namespace App\Services\Dealer; namespace App\Services\Dealer;
use App\Exceptions\BizException;
use App\Models\DealerOrder;
use App\Models\DealerProduct; use App\Models\DealerProduct;
use App\Models\ShippingAddress;
use App\Models\User; use App\Models\User;
use App\Models\UserInfo;
use Illuminate\Database\QueryException;
class OrderService class OrderService
{ {
@ -12,7 +17,7 @@ class OrderService
* *
* @param DealerProduct $product * @param DealerProduct $product
* @param integer $number * @param integer $number
* @return void * @return string
*/ */
public function totalAmount(User $user, DealerProduct $product, int $number = 0) public function totalAmount(User $user, DealerProduct $product, int $number = 0)
{ {
@ -41,7 +46,101 @@ class OrderService
return bcmul($salePrice, $number, 2); 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;
} }
} }

View File

@ -33,6 +33,7 @@ class CreateDealerOrdersTable extends Migration
$table->timestamp('shippinged_time')->nullable()->comment('确认收货时间'); $table->timestamp('shippinged_time')->nullable()->comment('确认收货时间');
$table->timestamps(); $table->timestamps();
$table->unique('sn');
$table->index(['user_id', 'status']); $table->index(['user_id', 'status']);
$table->index('status'); $table->index('status');
}); });

View File

@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddPayInfoToDealersTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('dealers', function (Blueprint $table) {
//
$table->text('pay_info')->nullable()->comment('用户保留的收款信息');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('dealers', function (Blueprint $table) {
//
$table->dropColumn('pay_info');
});
}
}