会员卡
parent
0e395eb986
commit
16b04e024b
|
|
@ -18,6 +18,9 @@ class UserVipController extends AdminController
|
|||
protected function grid()
|
||||
{
|
||||
return Grid::make(UserVip::with(['user', 'vip']), function (Grid $grid) {
|
||||
|
||||
$grid->model()->where('status', 1);
|
||||
|
||||
$grid->column('user.phone');
|
||||
$grid->column('vip.name');
|
||||
$grid->column('times')->display(function ($v) {
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ class VipController extends AdminController
|
|||
$grid->column('sort')->editable();
|
||||
$grid->column('status')->switch();
|
||||
|
||||
$grid->disableViewButton(false);
|
||||
//新增
|
||||
if (Admin::user()->can('dcat.admin.vips.create')) {
|
||||
$grid->disableCreateButton(false);
|
||||
|
|
@ -67,6 +68,7 @@ class VipController extends AdminController
|
|||
$show->filed('price');
|
||||
$show->filed('sort');
|
||||
$show->field('status');
|
||||
$show->field('description');
|
||||
$show->field('created_at');
|
||||
$show->field('updated_at');
|
||||
});
|
||||
|
|
@ -90,6 +92,7 @@ class VipController extends AdminController
|
|||
$form->number('price')->min(0);
|
||||
$form->number('sort')->min(0);
|
||||
$form->switch('status')->default(1);
|
||||
$form->textarea('description');
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
namespace App\Endpoint\Api\Http\Controllers;
|
||||
|
||||
use App\Endpoint\Api\Http\Resources\{VipResource, UserVipResource};
|
||||
use App\Models\Vip;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use App\Exceptions\BizException;
|
||||
use App\Services\VipService;
|
||||
|
||||
class VipController extends Controller
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
$list = Vip::where('status', 1)->get();
|
||||
|
||||
return VipResource::collection($list);
|
||||
}
|
||||
|
||||
public function buy(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'vip_id' => 'required'
|
||||
]);
|
||||
$vip = Vip::findOrFail($request->input('vip_id'));
|
||||
$service = new VipService();
|
||||
try {
|
||||
DB::beginTransaction();
|
||||
$user_vip = $service->buy($request->user(), $vip);
|
||||
|
||||
$result = $service->pay($user_vip, $request->input('pay_way'));
|
||||
DB::commit();
|
||||
return $result;
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
report($e);
|
||||
throw new BizException($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function list(Request $request)
|
||||
{
|
||||
$user = $request->user();
|
||||
|
||||
$query = $user->vips();
|
||||
|
||||
$list = $query->paginate($request->input('per_page'));
|
||||
|
||||
return UserVipResource::collection($list);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
namespace App\Endpoint\Api\Http\Resources;
|
||||
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class UserVipResource extends JsonResource
|
||||
{
|
||||
public function toArray($request)
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'vip_id' => $this->vip_id,
|
||||
'name' => $this->name,
|
||||
'times' => $this->times,
|
||||
'price' => (int)$this->price,
|
||||
'gift' => $this->gift,
|
||||
'status' => $this->status,
|
||||
'success_time' => $this->success_time,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
namespace App\Endpoint\Api\Http\Resources;
|
||||
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class VipResource extends JsonResource
|
||||
{
|
||||
public function toArray($request)
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'name' => $this->name,
|
||||
'times' => $this->times,
|
||||
'price' => (int)$this->price,
|
||||
'gift' => $this->gift,
|
||||
'description' => $this->description,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -209,6 +209,11 @@ Route::group([
|
|||
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::get('vip', [\App\Endpoint\Api\Http\Controllers\VipController::class, 'index']);
|
||||
Route::get('vip/buy', [\App\Endpoint\Api\Http\Controllers\VipController::class, 'list']);
|
||||
Route::post('vip/buy', [\App\Endpoint\Api\Http\Controllers\VipController::class, 'buy']);
|
||||
});
|
||||
|
||||
// 微信小程序
|
||||
|
|
|
|||
|
|
@ -39,6 +39,8 @@ class PayLog extends Model
|
|||
'out_trade_no',
|
||||
'status',
|
||||
'failed_reason',
|
||||
'payable_type',
|
||||
'payable_id',
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -11,6 +11,11 @@ class UserVip extends Model
|
|||
|
||||
protected $fillable = ['user_id', 'vip_id', 'name', 'price', 'times', 'gift', 'status', 'success_time'];
|
||||
|
||||
protected $casts = [
|
||||
'times' => 'json',
|
||||
'gift' => 'json'
|
||||
];
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'user_id');
|
||||
|
|
@ -20,9 +25,4 @@ class UserVip extends Model
|
|||
{
|
||||
return $this->belongsTo(Vip::class, 'vip_id');
|
||||
}
|
||||
|
||||
public function pay()
|
||||
{
|
||||
return $this->morphOne(PayLog::class, 'payable');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ class Vip extends Model
|
|||
const TIME_MONTH = 'month';
|
||||
const TIME_DAY = 'day';
|
||||
|
||||
protected $fillable = ['name', 'times', 'sort', 'status', 'gift', 'price'];
|
||||
protected $fillable = ['name', 'times', 'sort', 'status', 'gift', 'price', 'description'];
|
||||
|
||||
protected $casts = [
|
||||
'times' => 'json',
|
||||
|
|
|
|||
|
|
@ -70,6 +70,8 @@ class PayService
|
|||
'out_trade_no' => $payLog->out_trade_no,
|
||||
'status' => Order::STATUS_PAID,
|
||||
]);
|
||||
} else if ($payable instanceof \App\Models\UserVip) {
|
||||
(new App\Services\VipService())->success($payable, ['pay_at' => $payLog->pay_at]);
|
||||
}
|
||||
|
||||
return $payLog;
|
||||
|
|
|
|||
|
|
@ -2,10 +2,11 @@
|
|||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\{User, Vip, UserVip};
|
||||
use App\Models\{User, Vip, UserVip, PayLog};
|
||||
use App\Exceptions\BizException;
|
||||
use App\Enums\{PayWay, SocialiteType};
|
||||
use App\Enums\{PayWay, SocialiteType, WxpayTradeType};
|
||||
use App\Services\Payment\WxpayService;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class VipService
|
||||
{
|
||||
|
|
@ -25,32 +26,89 @@ class VipService
|
|||
return $user_vip;
|
||||
}
|
||||
|
||||
public function pay(UserVip $userVip, $pay_way)
|
||||
public function pay(UserVip $userVip, $pay_way = '')
|
||||
{
|
||||
if (!$pay_way) {
|
||||
$pay_way = PayWay::WxpayMiniProgram->value;
|
||||
}
|
||||
if ($userVip->status == 2) {
|
||||
throw new BizException('支付处理中,请勿重复操作');
|
||||
}
|
||||
$userVip->update([
|
||||
'status' => 2
|
||||
]);
|
||||
$pay_log = $user_vip->pay()->create([
|
||||
$pay_log = PayLog::create([
|
||||
'payable_type' => UserVip::class,
|
||||
'payable_id' => $userVip->id,
|
||||
'pay_sn' => serial_number(),
|
||||
'pay_way' => $pay_way
|
||||
]);
|
||||
$money = $userVip->price;
|
||||
$debug = config('app.debug');
|
||||
if ($debug) {
|
||||
$money = 0.01;
|
||||
}
|
||||
$user = $userVip->user;
|
||||
// 微信小程序支付
|
||||
if ($pay_log->pay_way === PayWay::WxpayMiniProgram->value) {
|
||||
if ($pay_log->pay_way === PayWay::WxpayMiniProgram) {
|
||||
$openid = $user->socialites()->where('socialite_type', SocialiteType::WechatMiniProgram)->value('socialite_id');
|
||||
if (!$openid && $debug) {
|
||||
$openid = 'oU7xS5UspzVvpPEBqKZuW6N9WXDg';
|
||||
}
|
||||
if (!$openid) {
|
||||
throw new BizException('未找到用户的微信授权信息, 无法使用微信支付');
|
||||
}
|
||||
$params = [
|
||||
'body' => app_settings('app.app_name').'-会员-' . $userVip->name,
|
||||
'out_trade_no' => $pay_log->pay_sn,
|
||||
'total_fee' => $userVip->price,
|
||||
'trade_type' => 'JSAPI',
|
||||
'total_fee' => $money * 100,
|
||||
'trade_type' => WxpayTradeType::JSAPI->value,
|
||||
'openid' => $openid,
|
||||
];
|
||||
return (new WxpayService())->pay($params);
|
||||
}
|
||||
throw new BizException('未知的支付方式');
|
||||
}
|
||||
|
||||
public function success(UserVip $userVip, array $params = null)
|
||||
{
|
||||
$userVip->update([
|
||||
'status' => 1,
|
||||
'success_time' => data_get($params, 'pay_at', now())
|
||||
]);
|
||||
$user = $userVip->user;
|
||||
$vip_expired = $this->convertTimes(data_get($userVip->times, 'num'), data_get($userVip->times, 'unit'), $user->vip_expired);
|
||||
$userVip->user->update([
|
||||
'vip_expired' => $vip_expired
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 时间转换
|
||||
*
|
||||
* @param int $num 数量
|
||||
* @param string $unit 单位
|
||||
* @return Carbon
|
||||
*/
|
||||
public function convertTimes(int $num, string $unit, Carbon $start = null)
|
||||
{
|
||||
if (!$start) {
|
||||
$start = now();
|
||||
}
|
||||
switch ($unit) {
|
||||
case Vip::TIME_YEAR:
|
||||
$start->addYears($num);
|
||||
break;
|
||||
case Vip::TIME_MONTH:
|
||||
$start->addYears($num);
|
||||
break;
|
||||
case TIME_DAY:
|
||||
$start->addDays($num);
|
||||
break;
|
||||
default:
|
||||
throw new BizException('未知的时间单位');
|
||||
}
|
||||
|
||||
return $start;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ class CreateVipsTable extends Migration
|
|||
$table->unsignedInteger('sort')->default(1)->comment('排序(asc)');
|
||||
$table->tinyInteger('status')->default(1)->comment('状态');
|
||||
$table->text('gift')->nullable()->comment('赠品');
|
||||
$table->text('description')->nullable()->comment('描述');
|
||||
$table->timestamps();
|
||||
});
|
||||
Schema::create('user_vips', function (Blueprint $table) {
|
||||
|
|
|
|||
|
|
@ -99,6 +99,12 @@ class AppSettingSeeder extends Seeder
|
|||
],
|
||||
'remarks' => '个推配置',
|
||||
],
|
||||
'custom' => [
|
||||
'key_value' => [
|
||||
'wechat_mini_show_vip_banner' => 'https://zcs-test.oss-cn-chengdu.aliyuncs.com/ac/is_vip.png',
|
||||
'wechat_mini_become_vip_banner' => 'https://zcs-test.oss-cn-chengdu.aliyuncs.com/ac/not_vip.png',
|
||||
]
|
||||
]
|
||||
] as $key => $values) {
|
||||
Setting::firstOrCreate(['key' => $key], $values);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,9 +18,9 @@ class VipsTableSeeder extends Seeder
|
|||
UserVip::truncate();
|
||||
$time = now();
|
||||
$list = [
|
||||
['name' => '年卡', 'times' => json_encode(['text' => '1年', 'num' => 1, 'unit' => 'year']), 'price' => 100, 'created_at' => $time, 'updated_at' => $time],
|
||||
['name' => '季卡', 'times' => json_encode(['text' => '3月', 'num' => 3, 'unit' => 'month']), 'price' => 25, 'created_at' => $time, 'updated_at' => $time],
|
||||
['name' => '月卡', 'times' => json_encode(['text' => '1月', 'num' => 1, 'unit' => 'month']), 'price' => 10, 'created_at' => $time, 'updated_at' => $time],
|
||||
['name' => '年卡', 'times' => json_encode(['text' => '1年', 'num' => 1, 'unit' => 'year']), 'price' => 100, 'description' => '瞰澜书店年卡'.PHP_EOL.'咖啡饮品85折,图书85折,免费咖啡饮品40杯;'.PHP_EOL.'生日礼物惊喜图书一本;'.PHP_EOL.'通用现金券500元(有限期365天)', 'created_at' => $time, 'updated_at' => $time],
|
||||
['name' => '季卡', 'times' => json_encode(['text' => '3月', 'num' => 3, 'unit' => 'month']), 'price' => 25, 'description' => '瞰澜书店季卡'.PHP_EOL.'咖啡饮品85折,图书85折,免费咖啡饮品40杯;'.PHP_EOL.'生日礼物惊喜图书一本;'.PHP_EOL.'通用现金券500元(有限期365天)', 'created_at' => $time, 'updated_at' => $time],
|
||||
['name' => '月卡', 'times' => json_encode(['text' => '1月', 'num' => 1, 'unit' => 'month']), 'price' => 10, 'description' => '瞰澜书店月卡'.PHP_EOL.'咖啡饮品85折,图书85折,免费咖啡饮品40杯;'.PHP_EOL.'生日礼物惊喜图书一本;'.PHP_EOL.'通用现金券500元(有限期365天)', 'created_at' => $time, 'updated_at' => $time],
|
||||
];
|
||||
Vip::insert($list);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,8 @@ return [
|
|||
'times' => '时效',
|
||||
'gift' => '赠品',
|
||||
'price' => '价格',
|
||||
'status' => '状态'
|
||||
'status' => '状态',
|
||||
'description' => '描述',
|
||||
],
|
||||
'options' => [
|
||||
'deny' => '删除失败',
|
||||
|
|
|
|||
Loading…
Reference in New Issue