6
0
Fork 0

添加砍价接口

release
vine_liutk 2022-04-08 15:50:24 +08:00
parent a76160ce0d
commit 942edeabe8
17 changed files with 553 additions and 29 deletions

View File

@ -101,6 +101,7 @@ class BargainActivityController extends AdminController
return array_column($value, 'name');
})->label();
$show->field('images')->image()->width(10, 1);
$show->field('description')->unescape()->width(10, 1);
$show->width(6)->field('created_at');
@ -148,6 +149,13 @@ class BargainActivityController extends AdminController
// dd($value, explode(',', $value));
return json_encode(explode(',', $value));
});
$form->text('share_title', '分享文案')->placeholder('默认使用活动名称');
$form->image('share_image', '分享图')
->move('bargain/share/'.Carbon::now()->toDateString())
->saveFullUrl()
->autoUpload()
->retainable()
->removable(false)->required();
$form->showFooter();
});
$form->block(6, function (Form\BlockForm $form) {
@ -161,8 +169,8 @@ class BargainActivityController extends AdminController
$form->saving(function ($form) {
if ($form->is_enable) {
//查询是否有除了自己以外开启的活动
if ($form->id) {
if (BargainActivityModel::where('id', '<>', $form->id)->isEnable()->exists()) {
if ($form->model()->id) {
if (BargainActivityModel::where('id', '<>', $form->model()->id)->isEnable()->exists()) {
return $form->response()->error('当前已有开启的活动');
}
} else {

View File

@ -1,23 +0,0 @@
<?php
namespace App\Endpoint\Api\Http\Controllers;
use App\Models\BargainActivity;
use Illuminate\Http\Request;
class BargainActivityController extends Controller
{
/**
*
*
* @param \Illuminate\Http\Request $request
* @return void
*/
public function detail($id, Request $request)
{
$activity = BargainActivity::findOrFail($id);
return $request->response()->json([
// 'name'
]);
}
}

View File

@ -0,0 +1,243 @@
<?php
namespace App\Endpoint\Api\Http\Controllers;
use App\Endpoint\Api\Http\Resources\BargainActivityResource;
use App\Endpoint\Api\Http\Resources\BargainOrderResource;
use App\Endpoint\Api\Http\Resources\OrderResource;
use App\Events\OrderPaid;
use App\Exceptions\BizException;
use App\Models\BargainActivity;
use App\Models\BargainOrder;
use App\Models\BargainSku;
use App\Models\ProductSku;
use App\Services\BargainService;
use App\Services\OrderService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Throwable;
class BargainController extends Controller
{
/**
*
*
* @param \Illuminate\Http\Request $request
* @return void
*/
public function activityDetail($id)
{
$activity = BargainActivity::with('skus')->findOrFail($id);
if (!$activity->is_enable) {
throw new BizException('砍价活动暂未开始');
}
if ($activity->start_at > now() || $activity->end_at < now()) {
throw new BizException('不在活动时间范围');
}
return BargainActivityResource::make($activity);
}
/**
* 通过砍价商品查看当前砍价记录
*
* @param [type] $id
* @param Request $request
* @return void
*/
public function bargainSku($id)
{
//判断商品是否参与砍价
$bargainSku = BargainSku::with(['activity'=>function ($query) {
return $query->where('is_enable', true)->where('start_at', '<=', now())->where('end_at', '>=', now());
}, 'sku'])->where('sku_id', $id)->first();
if (!($bargainSku && $bargainSku->activity)) {
throw new BizException('该商品未参与砍价活动,或者活动已结束');
}
return response()->json([
'sku' => [
'name' => (string) $bargainSku->sku->name,
'subtitle' => (string) $bargainSku->sku->subtitle,
'cover' => (string) $bargainSku->sku->cover,
'sell_price' => (string) $bargainSku->sku->sell_price_format,
'vip_price' => (string) $bargainSku->sku->vip_price_format,
],
'max_bargain_price'=> array_sum(json_decode($bargainSku->activity->rules)),
]);
}
/**
* 通过商品获取当前砍价进度(看自己的)
*
* @param Request $request
* @return void
*/
public function barginaOrderBySku($id, Request $request)
{
// $order = [];
$user = $request->user();
$userId = $user?->id ?? 0;
$order = BargainOrder::with(['logs', 'logs.userInfo'])
->where([
'sku_id' => $id,
'user_id'=> $userId,
])->where('status', '>', 0)
->where('expire_at', '>', now())
->orderBy('created_at', 'desc')->first();
if ($order) {
return BargainOrderResource::make($order);
}
return response()->noContent();
}
/**
* 通过订单ID获取当前砍价进度看别人的
*
* @param Request $request
* @return void
*/
public function bargainOrderById($id, Request $request)
{
$order = BargainOrder::with('logs')
->where([
'id' => $id,
])->where('status', '>', 0)
->where('expire_at', '>', now())
->orderBy('created_at', 'desc')->first();
if ($order) {
return BargainOrderResource::make($order);
}
return response()->noContent();
}
/**
* 发起砍价
*
* @param [type] $id
* @param Request $request
* @return void
*/
public function createBargainOrder(ProductSku $sku, Request $request)
{
if (!$sku->isBargaing()) {
throw new BizException('活动已结束');
}
//判断是否已经有正在进行的砍价单, 直接返回
$order = BargainOrder::where([
'sku_id' => $sku->id,
'user_id'=> $request->user()->id,
])->where('status', '>', 0)
->where('expire_at', '>', now())
->orderBy('created_at', 'desc')->first();
if (!$order) {
//创建新的砍价单
$bargainService = new BargainService();
try {
DB::beginTransaction();
$order = $bargainService->createBargainOrder($request->user(), $sku);
DB::commit();
} catch (Throwable $th) {
DB::rollBack();
report($th);
throw new BizException($th->getMessage());
}
}
$order->load(['logs', 'logs.userInfo']);
return BargainOrderResource::make($order);
}
/**
* 帮人砍价
*
* @param BargainOrder $order
* @param Request $request
* @return void
*/
public function bargain(BargainOrder $order, Request $request)
{
$bargainService = new BargainService();
try {
DB::beginTransaction();
$bargainService->bargain($request->user(), $order);
DB::commit();
} catch (Throwable $th) {
DB::rollBack();
report($th);
throw new BizException($th->getMessage());
}
$order->load(['logs', 'logs.userInfo']);
return BargainOrderResource::make($order);
}
/**
* 通过砍价订单创建待支付商城订单
*
* @param BargainOrder $bargainOrder
* @param Request $request
* @return void
*/
public function createMallOrderByBargainOrder(BargainOrder $bargainOrder, Request $request)
{
$rules = [
'shipping_address_id' => ['bail', 'required', 'int'],
'note' => ['bail', 'nullable', 'string', 'max:255'],
];
$input = $request->validate($rules, [], [
'shipping_address_id' => '收货地址',
'note' => '订单备注',
]);
$user = $request->user();
if ($user->id != $bargainOrder->user_id) {
throw new BizException('您不能替别人下单');
}
$orderService = new OrderService();
try {
DB::beginTransaction();
$bargainOrder->lockForUpdate();//下单的时候不允许砍价了
//已下单
if ($bargainOrder->order_id > 0) {
throw new BizException('您已下单,无需重复下单');
}
//砍价超时
if ($bargainOrder->expire_at > now()) {
throw new BizException('当前砍价已结束,无法下单');
}
//如果砍价活动结束了,也不能砍了
$activity = $bargainOrder->sku->isBargaing();
if (!$activity) {
throw new BizException('当前砍价已结束,无法下单');
}
$order = $orderService->createQuickOrder(
$user,
$bargainOrder->sku_id,
1,
$input['shipping_address_id'],
null,
$input['note'] ?? null,
$bargainOrder
);
$bargainOrder->update([
'order_id' => $order->id,
]);
DB::commit();
} catch (Throwable $th) {
DB::rollBack();
report($th);
throw new BizException($th->getMessage());
}
OrderPaid::dispatchIf($order->isPaid(), $order);
return OrderResource::make($order);
}
}

View File

@ -0,0 +1,26 @@
<?php
namespace App\Endpoint\Api\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class BargainActivityResource 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,
'images' => $this->images,
'description' => $this->description,
'skus' => ProductSkuSimpleResource::collection($this->whenLoaded('skus')),
'share_image'=> $this->share_image ?? '',
'share_title'=> $this->share_title ?? '',
];
}
}

View File

@ -0,0 +1,27 @@
<?php
namespace App\Endpoint\Api\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class BargainOrderLogResource 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 [
'nickname'=> (string) $this->whenLoaded('userInfo', function () {
return $this->userInfo->nickname;
}, ''),
'avatar'=> (string) $this->whenLoaded('userInfo', function () {
return $this->userInfo->avatar;
}, ''),
'bargain_amount' => $this->bargain_amount_format,
];
}
}

View File

@ -0,0 +1,27 @@
<?php
namespace App\Endpoint\Api\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class BargainOrderResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
*/
public function toArray($request)
{
$user = $request->user();
$userId = $user?->id ?? 0;
return [
'id' => $this->id,
'logs' => BargainOrderLogResource::collection($this->whenLoaded('logs')),
'expire_at'=> $this->expire_at->format('Y-m-d H:i:s'),
'status' => $this->status,
'is_owner' => $this->user_id == $userId ? true : false,
];
}
}

View File

@ -35,6 +35,7 @@ class ProduckSkuResource extends JsonResource
}),
'growth_value' => (int) $this->growth_value,
'sales_value' => (int) $this->sales_value,
'is_bargaing' => $this->isBargaing() ? true : false,
];
}
}

View File

@ -89,6 +89,13 @@ Route::group([
Route::get('configs', [SettingController::class, 'index']);
Route::get('configs-custom', [SettingController::class, 'custom']);
//砍价
Route::get('bargains/{activity}', [\App\Endpoint\Api\Http\Controllers\BargainController::class, 'activityDetail']);
Route::get('bargain-sku/{sku}', [\App\Endpoint\Api\Http\Controllers\BargainController::class, 'bargainSku']);
Route::get('bargain-order/sku/{sku}', [\App\Endpoint\Api\Http\Controllers\BargainController::class, 'barginaOrderBySku']);
Route::get('bargain-order/order/{order}', [\App\Endpoint\Api\Http\Controllers\BargainController::class, 'bargainOrderById']);
//三方登录聚合
Route::group([
'prefix' =>'socialite',
@ -196,7 +203,10 @@ Route::group([
Route::get('users/{phone}', [\App\Endpoint\Api\Http\Controllers\UserController::class, 'show']);
Route::get('bargains/{bargain}', [\App\Endpoint\Api\Http\Controllers\BargainActivityController::class, 'detail']);
//砍价
Route::post('bargains/create-order/{sku}', [\App\Endpoint\Api\Http\Controllers\BargainController::class, 'createBargainOrder']);
Route::post('bargains/bargain/{order}', [\App\Endpoint\Api\Http\Controllers\BargainController::class, 'bargain']);
Route::post('bargains/create-mall-order/{bargainOrder}', [\App\Endpoint\Api\Http\Controllers\BargainController::class, 'createMallOrderByBargainOrder']);
});
Route::group([

View File

@ -2,6 +2,7 @@
namespace App\Models;
use App\Casts\JsonArray;
use Dcat\Admin\Traits\HasDateTimeFormatter;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
@ -11,6 +12,10 @@ class BargainActivity extends Model
use HasFactory;
use HasDateTimeFormatter;
protected $casts = [
'images' => JsonArray::class,
];
public static $enabledText = [
0=>'禁用',
1=>'启用',

View File

@ -2,10 +2,39 @@
namespace App\Models;
use Dcat\Admin\Traits\HasDateTimeFormatter;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class BargainOrder extends Model
{
use HasFactory;
use HasDateTimeFormatter;
protected $casts = [
'expire_at'=> 'datetime',
];
protected $fillable = [
'order_id', 'remark',
];
public const ORDER_STATUS_WAIT = 0;
public const ORDER_STATUS_START = 1;
public const ORDER_STATUS_FINISHED = 2;
public function logs()
{
return $this->hasMany(BargainOrderLog::class, 'order_id');
}
public function sku()
{
return $this->hasOne(ProductSku::class, 'id', 'sku_id');
}
public function isFinished()
{
return $this->status == static::ORDER_STATUS_FINISHED;
}
}

View File

@ -2,10 +2,26 @@
namespace App\Models;
use Dcat\Admin\Traits\HasDateTimeFormatter;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class BargainOrderLog extends Model
{
use HasFactory;
use HasDateTimeFormatter;
protected $fillable = [
'user_id', 'bargain_amount',
];
public function userInfo()
{
return $this->hasOne(UserInfo::class, 'user_id', 'user_id');
}
public function getBargainAmountFormatAttribute()
{
return bcdiv($this->attributes['bargain_amount'], 100, 2);
}
}

View File

@ -10,4 +10,14 @@ class BargainSku extends Model
use HasFactory;
protected $timestamp = false;
public function activity()
{
return $this->belongsTo(BargainActivity::class, 'activity_id');
}
public function sku()
{
return $this->belongsTo(ProductSku::class, 'sku_id');
}
}

View File

@ -93,6 +93,7 @@ class Order extends Model
'is_settle',
'sales_value',
'is_settlable',
'bargain_amount',
];
/**
@ -427,4 +428,14 @@ class Order extends Model
{
return OrderStatus::$statusTexts[$this->order_status] ?? OrderStatus::$statusTexts[OrderStatus::UNKNOWN];
}
/**
* 获取订单砍价优惠
*
* @return string
*/
public function getBargainAmountFormatAttribute()
{
return trim_trailing_zeros(bcdiv($this->attributes['bargain_amount'], 100, 2));
}
}

View File

@ -195,4 +195,21 @@ class ProductSku extends Model
return bcdiv($price, 100, 2);
}
/**
* 是否正在进行砍价
*
* @return boolean
*/
public function isBargaing()
{
$bargainSku = BargainSku::with(['activity'=>function ($query) {
return $query->where('is_enable', true)->where('start_at', '<=', now())->where('end_at', '>=', now());
}])->where('sku_id', $this->id)->first();
if ($bargainSku && $bargainSku->activity) {
return $bargainSku->activity;
}
return null;
}
}

View File

@ -0,0 +1,106 @@
<?php
namespace App\Services;
use App\Exceptions\BizException;
use App\Models\BargainOrder;
use App\Models\BargainOrderLog;
use App\Models\BargainSku;
use App\Models\ProductSku;
use App\Models\User;
use Illuminate\Support\Arr;
class BargainService
{
/**
* 创建砍价单-todo
*
* @param User $user
* @param ProductSku $productSku
* @return void
*/
public function createBargainOrder(User $user, ProductSku $productSku)
{
$bargainSku = BargainSku::with(['activity'=>function ($query) {
return $query->where('is_enable', true)->where('start_at', '<=', now())->where('end_at', '>=', now());
}])->where('sku_id', $productSku->id)->first();
if (!($bargainSku && $bargainSku->activity)) {
throw new BizException('砍价商品活动已结束');
}
$order = new BargainOrder();
$order->activity_id = $bargainSku->activity_id;
$order->user_id = $user->id;
$order->sku_id = $productSku->id;
$order->sku_price = $productSku->getRealPrice($user);
$order->status = BargainOrder::ORDER_STATUS_START;
$order->expire_at = now()->addHours($bargainSku->activity->expire_hours);
$order->save();
return $order;
}
/**
* 用户砍价
*
* @param User $user
* @param BargainOrder $order
* @return void
*/
public function bargain(User $user, BargainOrder $order)
{
$order->lockForUpdate();
$order->load('sku');
//不能给自己砍
if ($user->id == $order->user_id) {
throw new BizException('不能帮自己砍价');
}
//已砍完
if ($order->isFinished()) {
throw new BizException('当前砍价单已完成,无法再砍');
}
//已下单
if ($order->order_id > 0) {
throw new BizException('当前砍价单下单,无法再砍');
}
//砍价超时
if ($order->expire_at > now()) {
throw new BizException('当前砍价已结束,无法再砍');
}
//如果砍价活动结束了,也不能砍了
$activity = $order->sku->isBargaing();
if (!$activity) {
throw new BizException('当前砍价已结束,无法再砍');
}
//执行砍价动作;
//计算第几次砍价,并获得当前砍价金额
$nowBargainCount = $order->logs->count();
$bargainRules = json_decode($activity->rules, true);
$log = new BargainOrderLog([
'user_id'=>$user->id,
'bargain_amount'=> bcmul(Arr::get($bargainRules, $nowBargainCount, 0), 100),
]);
$order->logs()->save($log);
$order->bargain_price += $log->bargain_amount;
if ($activity->times > 0 && $activity->times <= ($nowBargainCount ++)) {
$order->status = BargainOrder::ORDER_STATUS_FINISHED;
}
$order->save();
return $order;
}
public function createMallOrder(BargainOrder $order)
{
}
}

View File

@ -11,6 +11,7 @@ use App\Enums\WxpayTradeType;
use App\Exceptions\BizException;
use App\Exceptions\ShippingNotSupportedException;
use App\Models\ActivityProductPart;
use App\Models\BargainOrder;
use App\Models\DistributionPreIncomeJob;
use App\Models\Order;
use App\Models\OrderActivity;
@ -38,6 +39,7 @@ class OrderService
* @param int $shippingAddressId
* @param int|null $couponId
* @param string|null $note
* @param BargainOrder|null $bargainOrder
* @return \App\Models\Order
*/
public function createQuickOrder(
@ -46,7 +48,8 @@ class OrderService
int $quantity,
int $shippingAddressId,
?int $couponId = null,
?string $note = null
?string $note = null,
?BargainOrder $bargainOrder = null,
): Order {
$sku = ProductSku::online()->findOrFail($skuId);
@ -55,7 +58,7 @@ class OrderService
'quantity' => $quantity,
];
return $this->createOrder($user, [$product], $shippingAddressId, $couponId, $note);
return $this->createOrder($user, [$product], $shippingAddressId, $couponId, $note, $bargainOrder);
}
/**
@ -97,6 +100,7 @@ class OrderService
* @param int $shippingAddressId
* @param int|null $couponId
* @param string|null $note
* @param BargainOrder|null $bargainOrder
* @return \App\Models\Order
*/
protected function createOrder(
@ -105,6 +109,7 @@ class OrderService
int $shippingAddressId,
?int $couponId = null,
?string $note = null,
?BargainOrder $bargainOrder = null,
): Order {
foreach ($products as $product) {
$sku = $product['sku'];
@ -144,7 +149,8 @@ class OrderService
$shippingFee,
$salesValue,
$note,
$coupon
$coupon,
$bargainOrder,//添加砍价订单逻辑
);
$this->storeOrderProducts($order, $mapProducts);
@ -185,6 +191,7 @@ class OrderService
$salesValue,
?string $note = null,
?UserCoupon $coupon = null,
?BargainOrder $bargainOrder = null,
): Order {
// 订单支付金额=商品总额-券折扣金额-会员折扣金额+邮费
$totalAmount = $productsTotalAmount - $couponDiscountAmount - $vipDiscountAmount;
@ -213,6 +220,8 @@ class OrderService
'consignee_telephone' => $shippingAddress->telephone,
'consignee_zone' => $shippingAddress->zone,
'consignee_address' => $shippingAddress->address,
//砍价订单金额
'bargain_amount'=>$bargainOrder?->bargain_price,
];
return $user->orders()->create($attrs);

View File

@ -17,6 +17,8 @@ class CreateBargainActivitiesTable extends Migration
$table->id();
$table->string('name')->comment('活动名称');
$table->text('images')->nullable()->comment('活动图');
$table->text('share_image')->nullable()->comment('分享图');
$table->text('share_title')->nullable()->comment('分享文案');
$table->text('description')->nullable()->comment('活动描述');
$table->boolean('is_enable')->nullable()->comment('是否开启');
$table->text('rules')->nullable()->comment('砍价规则');