From 141639ab8491be448e374b30704f83cd7fa742a2 Mon Sep 17 00:00:00 2001 From: panliang <1163816051@qq.com> Date: Fri, 17 Apr 2026 21:32:23 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BA=BF=E4=B8=8B=E8=AE=A2=E5=8D=95=20?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=A4=87=E6=B3=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/OfflineOrderController.php | 4 + app/Admin/Controllers/PointLogController.php | 8 +- .../Controllers/Store/OrderController.php | 4 +- app/Admin/Controllers/UserVipController.php | 6 +- app/Admin/Forms/PointChange.php | 5 +- .../Auth/MiniprogramController.php | 63 ++++++---------- .../Controllers/OfflineOrderController.php | 1 + .../OfflineOrderPreviewController.php | 71 ++++++++++-------- .../Api/Http/Controllers/VipController.php | 2 +- app/Endpoint/Api/routes.php | 2 +- app/Models/OfflineOrder.php | 2 + app/Models/OfflineOrderPreview.php | 2 +- app/Models/PointLog.php | 7 ++ app/Models/Store/Store.php | 3 + app/Models/UserVip.php | 8 +- app/Services/AfterSaleService.php | 5 +- app/Services/OfflineOrderService.php | 17 ++++- app/Services/OrderService.php | 11 ++- app/Services/PointService.php | 17 ++++- app/Services/VipService.php | 17 ++--- .../2021_12_03_135905_create_vips_table.php | 1 + ...3_10_12_103127_create_point_logs_table.php | 1 + public/images/avatar.jpeg | Bin 0 -> 10221 bytes resources/lang/zh_CN/offline-order.php | 2 + resources/lang/zh_CN/point-log.php | 1 + resources/lang/zh_CN/user-vip.php | 3 + 26 files changed, 164 insertions(+), 99 deletions(-) create mode 100644 public/images/avatar.jpeg diff --git a/app/Admin/Controllers/OfflineOrderController.php b/app/Admin/Controllers/OfflineOrderController.php index 0962f345..45a42905 100644 --- a/app/Admin/Controllers/OfflineOrderController.php +++ b/app/Admin/Controllers/OfflineOrderController.php @@ -89,6 +89,8 @@ class OfflineOrderController extends AdminController $grid->column('status')->display(fn($v) => $v->dot()); $grid->column('payment_method')->display(fn($v) => $v?->dot()); $grid->column('payment_time'); + $grid->column('user_remark'); + $grid->column('staff_remark'); $grid->column('created_at'); $grid->filter(function (Grid\Filter $filter) { @@ -207,6 +209,8 @@ class OfflineOrderController extends AdminController $show->field('out_trade_no'); $show->field('created_at'); $show->field('revoked_at'); + $show->field('user_remark'); + $show->field('staff_remark'); }); $show->panel()->tools(function (Show\Tools $tools) use ($show) { $tools->disableEdit(); diff --git a/app/Admin/Controllers/PointLogController.php b/app/Admin/Controllers/PointLogController.php index 540ee2ae..999f04b1 100644 --- a/app/Admin/Controllers/PointLogController.php +++ b/app/Admin/Controllers/PointLogController.php @@ -10,6 +10,7 @@ use App\Models\OrderProduct; use App\Models\PointLog; use App\Models\UserInfo; use App\Models\UserVip; +use App\Models\Store\Store; use Dcat\Admin\Admin; use Dcat\Admin\Grid; use Dcat\Admin\Http\Controllers\AdminController; @@ -33,12 +34,13 @@ class PointLogController extends AdminController CSS ); - $builder = PointLogRepository::with(['user', 'administrator']); + $builder = PointLogRepository::with(['user', 'administrator', 'store']); return Grid::make($builder, function (Grid $grid) { $grid->export()->titles([ 'id' => 'ID', + 'store_id' => __('point-log.fields.store_id'), 'username' => '用户昵称', 'user_id' => __('point-log.fields.user.phone'), 'first_rechare_time' => '首次充值时间', @@ -65,6 +67,7 @@ class PointLogController extends AdminController $vipCount = $userVips->count(); $row['user_id'] = data_get($row, 'user.phone'); + $row['store_id'] = data_get($row, 'store.title'); $row['username'] = data_get($userInfo, 'nickname'); $row['first_rechare_time'] = $firstVip ? $firstVip->success_time->format('Y-m-d H:i:s') : ''; @@ -108,6 +111,7 @@ class PointLogController extends AdminController $grid->model()->orderBy('id', 'desc'); $grid->column('id')->sortable(); + $grid->column('store.title', __('point-log.fields.store_id')); $grid->column('user.phone')->copyable(); $grid->column('action')->display(fn ($action) => $action->label())->label(); $grid->column('change_points')->display(function ($value) { @@ -144,7 +148,9 @@ class PointLogController extends AdminController }); $grid->filter(function (Grid\Filter $filter) { + $stores = Store::pluck('title', 'id')->all(); $filter->panel(false); + $filter->equal('store_id', __('point-log.fields.store_id'))->select($stores)->width(3); $filter->equal('user.phone')->width(3); $filter->like('administrator.name', '操作人')->width(3); $filter->in('action')->multipleSelect(PointLogAction::options())->width(3); diff --git a/app/Admin/Controllers/Store/OrderController.php b/app/Admin/Controllers/Store/OrderController.php index 82c2d143..7e5e1392 100644 --- a/app/Admin/Controllers/Store/OrderController.php +++ b/app/Admin/Controllers/Store/OrderController.php @@ -49,7 +49,7 @@ class OrderController extends AdminController $grid->column('sn')->copyable(); $grid->column('user_id')->display(function () { $nickname = $this->userInfo?->nickname ?? '---'; - $avatar = $this->userInfo?->avatar ?? 'https://via.placeholder.com/45x45.png'; + $avatar = $this->userInfo?->avatar ?? asset('images/avatar.jpeg'); $phone = $this->user?->phone; return << @@ -59,7 +59,7 @@ class OrderController extends AdminController }); $grid->column('inviter_id')->display(function () { $nickname = $this->inviterInfo?->nickname ?? '---'; - $avatar = $this->inviterInfo?->avatar ?? 'https://via.placeholder.com/45x45.png'; + $avatar = $this->inviterInfo?->avatar ?? asset('images/avatar.jpeg'); $phone = $this->inviter?->phone; return << diff --git a/app/Admin/Controllers/UserVipController.php b/app/Admin/Controllers/UserVipController.php index b401e16a..3cf37b63 100644 --- a/app/Admin/Controllers/UserVipController.php +++ b/app/Admin/Controllers/UserVipController.php @@ -3,6 +3,7 @@ namespace App\Admin\Controllers; use App\Models\{UserVip, Vip}; +use App\Models\Store\Store; use Dcat\Admin\Form; use Dcat\Admin\Grid; use Dcat\Admin\Http\Controllers\AdminController; @@ -17,11 +18,12 @@ class UserVipController extends AdminController */ protected function grid() { - return Grid::make(UserVip::with(['user']), function (Grid $grid) { + return Grid::make(UserVip::with(['user', 'store']), function (Grid $grid) { $grid->model()->where('status', UserVip::STATUS_SUCCESS)->latest('created_at'); $grid->column('user.phone'); + $grid->column('store.title'); $grid->column('name'); $grid->column('times')->display(function ($v) { return data_get($v, 'text'); @@ -34,7 +36,9 @@ class UserVipController extends AdminController $grid->disableViewButton(false); $grid->filter(function (Grid\Filter $filter) { + $stores = Store::pluck('title', 'id')->all(); $filter->panel(false); + $filter->equal('store_id', __('user-vip.fields.store.title'))->select($stores)->width(3); $filter->like('user.phone')->width(3); $filter->like('name')->width(3); }); diff --git a/app/Admin/Forms/PointChange.php b/app/Admin/Forms/PointChange.php index b3ed3fba..0c0a8e34 100644 --- a/app/Admin/Forms/PointChange.php +++ b/app/Admin/Forms/PointChange.php @@ -40,7 +40,10 @@ class PointChange extends Form implements LazyRenderable try { DB::beginTransaction(); - (new PointService())->change($user, $changePoints, $action, $input['remark'], null, Admin::user()); + (new PointService())->change($user, $changePoints, $action, [ + 'remark' => $input['remark'], + 'administrator' => Admin::user() + ]); DB::commit(); } catch (Throwable $th) { diff --git a/app/Endpoint/Api/Http/Controllers/Auth/MiniprogramController.php b/app/Endpoint/Api/Http/Controllers/Auth/MiniprogramController.php index 77ea96a5..b8b225f0 100644 --- a/app/Endpoint/Api/Http/Controllers/Auth/MiniprogramController.php +++ b/app/Endpoint/Api/Http/Controllers/Auth/MiniprogramController.php @@ -38,44 +38,21 @@ class MiniprogramController extends Controller $time = now(); $ip = $request->realIp(); - $inviter = null; - if ($request->filled('invite_code')) { - $inviter = $this->findUserByCode($request->input('invite_code', '')); - if (!$inviter) { - throw new BizException('邀请人不存在'); - } - } - try { DB::beginTransaction(); - $user_social = SocialiteUser::where(['socialite_id' => $openid, 'socialite_type' => $type])->first(); - - if (!$user_social) { - $attributes = [ - 'register_ip' => $ip, - 'last_login_at' => $time, - 'last_login_ip' => $ip, - ]; - $user = User::create($attributes, $inviter); - $user->socialites()->create([ - 'socialite_id' => $openid, - 'socialite_type' => $type, - ]); - } else { - $user = $user_social->user; + $user_social = SocialiteUser::firstOrCreate(['socialite_id' => $openid, 'socialite_type' => $type]); + $user = $user_social->user; + $token = ''; + if ($user) { + $token = $user->createToken($type)->plainTextToken; } - if (!$user) { - throw new BizException($openid); - } - - $token = $user->createToken($type); DB::commit(); return response()->json([ 'openid' => $openid, - 'token' => $token->plainTextToken + 'token' => $token ]); } catch (Throwable $e) { DB::rollBack(); @@ -89,7 +66,6 @@ class MiniprogramController extends Controller public function bindPhone(Request $request) { - $user = $request->user(); $request->validate([ 'code' => 'required' ], [ @@ -105,19 +81,26 @@ class MiniprogramController extends Controller } $phone = data_get($result, 'phone_info.purePhoneNumber'); + $openid = $request->input('openid'); try { DB::beginTransaction(); - // 检测手机号是否已经注册 - $old_user = User::where('phone', $phone)->where('status', User::STATUS_ACTIVE)->where('id', '!=', $user->id)->first(); - if ($old_user) { - // 禁用新用户 - throw new BizException('手机号已经注册'); - } else { - $user->update([ - 'phone' => $phone, - 'phone_verified_at' => now(), - ]); + $user = User::where('phone', $phone)->first(); + if (!$user) { + $time = now(); + $ip = $request->realIp(); + $inviter = $this->findUserByCode($request->input('invite_code', '')); + $user = User::create([ + 'register_ip' => $ip, + 'last_login_at' => $time, + 'last_login_ip' => $ip, + ], $inviter); + } + if ($openid) { + SocialiteUser::updateOrCreate( + ['socialite_id' => $openid, 'socialite_type' => SocialiteType::WechatMiniProgram->value], + ['user_id' => $user->id] + ); } $token = $user->createToken(SocialiteType::WechatMiniProgram->value); diff --git a/app/Endpoint/Api/Http/Controllers/OfflineOrderController.php b/app/Endpoint/Api/Http/Controllers/OfflineOrderController.php index 06fd21f8..c85b89c4 100644 --- a/app/Endpoint/Api/Http/Controllers/OfflineOrderController.php +++ b/app/Endpoint/Api/Http/Controllers/OfflineOrderController.php @@ -46,6 +46,7 @@ class OfflineOrderController extends Controller $request->user(), $preview, bcmul($request->input('points', 0), 100), + ['user_remark' => $request->input('user_remark')] ); DB::commit(); diff --git a/app/Endpoint/Api/Http/Controllers/OfflineOrderPreviewController.php b/app/Endpoint/Api/Http/Controllers/OfflineOrderPreviewController.php index 36cc5ec9..4a6ad9d3 100644 --- a/app/Endpoint/Api/Http/Controllers/OfflineOrderPreviewController.php +++ b/app/Endpoint/Api/Http/Controllers/OfflineOrderPreviewController.php @@ -35,43 +35,52 @@ class OfflineOrderPreviewController extends Controller 'store_id' => $store->id, 'staff_id' => $user->id, 'payload' => ['items' => $request->input('items')], + 'remark' => $request->input('remark') ]); - $scene = http_build_query([ - 'oo' => $preview->id, - 'i' => $user->userInfo->code, - ]); - - // 生成小程序码 - $app = Factory::miniProgram(config('wechat.mini_program.default')); - - $response = $app->app_code->getUnlimit($scene, [ - 'page' => 'pages/welcome/index', - 'check_path' => false, - 'env_version' => app()->isProduction() ? 'release' : $request->input('env_version', 'trial'), - 'width' => $request->input('width', 200), - ]); - - // 保存小程序码 - if ($response instanceof StreamResponse) { - $directory = 'offline-order-preview'; - $filename = "{$preview->id}.png"; - - $disk = Storage::disk('public'); - - $response->save($disk->path($directory), $filename); - - $preview->update(['qrcode' => $disk->url("{$directory}/{$filename}")]); - - return response()->json([ - 'id' => $preview->id, - 'qrcode' => $preview->qrcode, + if (config('app.env') != "local") { + $scene = http_build_query([ + 'oo' => $preview->id, + 'i' => $user->userInfo->code, ]); + + // 生成小程序码 + $app = Factory::miniProgram(config('wechat.mini_program.default')); + + $response = $app->app_code->getUnlimit($scene, [ + 'page' => 'pages/welcome/index', + 'check_path' => false, + 'env_version' => app()->isProduction() ? 'release' : $request->input('env_version', 'trial'), + 'width' => $request->input('width', 200), + ]); + + // 保存小程序码 + if ($response instanceof StreamResponse) { + $directory = 'offline-order-preview'; + $filename = "{$preview->id}.png"; + + $disk = Storage::disk('public'); + + $response->save($disk->path($directory), $filename); + + $preview->update(['qrcode' => $disk->url("{$directory}/{$filename}")]); + + return response()->json([ + 'id' => $preview->id, + 'qrcode' => $preview->qrcode, + ]); + } + + logger('offline_order_preview 小程序码生成失败', $response); + + throw new BizException('生成失败, 请重试'); } - logger('offline_order_preview 小程序码生成失败', $response); + return response()->json([ + 'id' => $preview->id, + 'qrcode' => asset("images/logo.png") + ]); - throw new BizException('生成失败, 请重试'); } public function show($id) diff --git a/app/Endpoint/Api/Http/Controllers/VipController.php b/app/Endpoint/Api/Http/Controllers/VipController.php index a31655eb..ca846eca 100644 --- a/app/Endpoint/Api/Http/Controllers/VipController.php +++ b/app/Endpoint/Api/Http/Controllers/VipController.php @@ -27,7 +27,7 @@ class VipController extends Controller $service = new VipService(); try { DB::beginTransaction(); - $user_vip = $service->buy($request->user(), $vip); + $user_vip = $service->buy($request->user(), $vip, $request->only(['store_id'])); $result = $service->pay($user_vip, $request->input('pay_way')); DB::commit(); diff --git a/app/Endpoint/Api/routes.php b/app/Endpoint/Api/routes.php index c6132f15..3426f3cf 100644 --- a/app/Endpoint/Api/routes.php +++ b/app/Endpoint/Api/routes.php @@ -232,7 +232,7 @@ Route::group([ // 微信小程序 Route::group(['prefix' => 'wechat-mini'], function () { Route::post('login', [MiniprogramController::class, 'login']); - Route::group(['middleware' => ['auth:api', \App\Endpoint\Api\Http\Middleware\CheckUserStatus::class]], function () { + Route::group(['middleware' => [\App\Endpoint\Api\Http\Middleware\CheckUserStatus::class]], function () { Route::post('bind-phone', [MiniprogramController::class, 'bindPhone']); }); }); diff --git a/app/Models/OfflineOrder.php b/app/Models/OfflineOrder.php index 239db4fa..80993870 100644 --- a/app/Models/OfflineOrder.php +++ b/app/Models/OfflineOrder.php @@ -43,6 +43,8 @@ class OfflineOrder extends Model 'revoked_at', 'orderable_type', 'orderable_id', + 'user_remark', + 'staff_remark', ]; public function store() diff --git a/app/Models/OfflineOrderPreview.php b/app/Models/OfflineOrderPreview.php index 151709c9..765be689 100644 --- a/app/Models/OfflineOrderPreview.php +++ b/app/Models/OfflineOrderPreview.php @@ -15,7 +15,7 @@ class OfflineOrderPreview extends Model ]; protected $fillable = [ - 'store_id', 'staff_id', 'payload', 'qrcode', + 'store_id', 'staff_id', 'payload', 'qrcode', 'remark' ]; public function store() diff --git a/app/Models/PointLog.php b/app/Models/PointLog.php index 38e0acce..ae42e40f 100644 --- a/app/Models/PointLog.php +++ b/app/Models/PointLog.php @@ -7,6 +7,7 @@ use App\Models\Admin\Administrator; use Dcat\Admin\Traits\HasDateTimeFormatter; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use App\Models\Store\Store; class PointLog extends Model { @@ -22,6 +23,7 @@ class PointLog extends Model 'after_points', 'remark', 'administrator_id', + 'store_id' ]; protected $casts = [ @@ -33,6 +35,11 @@ class PointLog extends Model return $this->belongsTo(User::class); } + public function store() + { + return $this->belongsTo(Store::class, 'store_id'); + } + public function administrator() { return $this->belongsTo(Administrator::class); diff --git a/app/Models/Store/Store.php b/app/Models/Store/Store.php index 7ebc02a2..365ce2b9 100644 --- a/app/Models/Store/Store.php +++ b/app/Models/Store/Store.php @@ -6,6 +6,9 @@ use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Dcat\Admin\Traits\HasDateTimeFormatter; +/** + * 门店 + */ class Store extends Model { use HasFactory, HasDateTimeFormatter; diff --git a/app/Models/UserVip.php b/app/Models/UserVip.php index c7230721..4c7e93f7 100644 --- a/app/Models/UserVip.php +++ b/app/Models/UserVip.php @@ -2,6 +2,7 @@ namespace App\Models; +use App\Models\Store\Store; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Dcat\Admin\Traits\HasDateTimeFormatter; @@ -14,7 +15,7 @@ class UserVip extends Model const STATUS_SUCCESS = 1; const STATUS_PROCESSING = 2; - protected $fillable = ['user_id', 'vip_id', 'name', 'price', 'times', 'gift', 'status', 'expired', 'success_time']; + protected $fillable = ['user_id', 'vip_id', 'name', 'price', 'times', 'gift', 'status', 'expired', 'success_time', 'store_id']; protected $casts = [ 'times' => 'json', @@ -34,6 +35,11 @@ class UserVip extends Model return $this->belongsTo(User::class, 'user_id'); } + public function store() + { + return $this->belongsTo(Store::class, 'store_id'); + } + public function vip() { return $this->belongsTo(Vip::class, 'vip_id'); diff --git a/app/Services/AfterSaleService.php b/app/Services/AfterSaleService.php index 2418da9a..ae3ac0ef 100644 --- a/app/Services/AfterSaleService.php +++ b/app/Services/AfterSaleService.php @@ -398,7 +398,10 @@ class AfterSaleService } if ($afterSale->points > 0) { - (new PointService())->change($order->user, $afterSale->points, PointLogAction::Refund, "售后单{$afterSale->sn}退还积分", $afterSale); + (new PointService())->change($order->user, $afterSale->points, PointLogAction::Refund, [ + 'remark' => "售后单{$afterSale->sn}退还积分", + 'loggable' => $afterSale, + ]); } //执行实际退款操作; diff --git a/app/Services/OfflineOrderService.php b/app/Services/OfflineOrderService.php index cba51305..0e5d65e1 100644 --- a/app/Services/OfflineOrderService.php +++ b/app/Services/OfflineOrderService.php @@ -16,10 +16,11 @@ use App\Models\SocialiteUser; use App\Models\User; use App\Services\Payment\WxpayService; +use function EasyWeChat\Kernel\data_get; class OfflineOrderService { - public function create(User $user, OfflineOrderPreview $orderPreview, int $points = 0): OfflineOrder + public function create(User $user, OfflineOrderPreview $orderPreview, int $points = 0, $params = []): OfflineOrder { $items = $this->mapItems($orderPreview->payload['items']); @@ -54,13 +55,19 @@ class OfflineOrderService 'status' => OfflineOrderStatus::Pending, 'orderable_type' => $orderPreview->getMorphClass(), 'orderable_id' => $orderPreview->id, + 'user_remark' => data_get($params, 'user_remark'), + 'staff_remark' => $orderPreview->remark, ]); $this->insertOrderItems($order, $items); // 扣除积分 if ($points > 0) { - (new PointService)->change($user, -$points, PointLogAction::Consumption, "线下订单{$order->sn}使用积分", $order); + (new PointService)->change($user, -$points, PointLogAction::Consumption, [ + 'remark' => "线下订单{$order->sn}使用积分", + 'loggable' => $order, + 'store_id' => $orderPreview->store_id, + ]); } if ($order->payment_amount === 0) { @@ -178,7 +185,11 @@ class OfflineOrderService } if ($order->points_deduction_amount > 0) { - (new PointService())->change($order->user, $order->points_deduction_amount, PointLogAction::Refund, "线下订单{$order->sn}退还积分", $order); + (new PointService())->change($order->user, $order->points_deduction_amount, PointLogAction::Refund, [ + 'remark' => "线下订单{$order->sn}退还积分", + 'loggable' => $order, + 'store_id' => $order->store_id, + ]); } $order->update([ diff --git a/app/Services/OrderService.php b/app/Services/OrderService.php index 172dbd4e..59b85972 100644 --- a/app/Services/OrderService.php +++ b/app/Services/OrderService.php @@ -427,7 +427,10 @@ class OrderService // 扣除积分 if ($points > 0) { - (new PointService)->change($user, -$points, PointLogAction::Consumption, "订单{$order->sn}使用积分", $order); + (new PointService)->change($user, -$points, PointLogAction::Consumption, [ + 'remark' => "订单{$order->sn}使用积分", + 'loggable' => $order + ]); } return $order; @@ -1418,7 +1421,11 @@ class OrderService } if ($order->point_discount_amount > 0) { - (new PointService())->change($order->user, $order->point_discount_amount, PointLogAction::Refund, "订单{$order->sn}退还积分", $order); + (new PointService())->change($order->user, $order->point_discount_amount, PointLogAction::Refund, [ + 'remark' => "订单{$order->sn}退还积分", + 'loggable' => $order, + 'store_id' => $order->store_id + ]); } $order->update([ diff --git a/app/Services/PointService.php b/app/Services/PointService.php index 55ab6fb1..3b4a7863 100644 --- a/app/Services/PointService.php +++ b/app/Services/PointService.php @@ -14,16 +14,21 @@ class PointService { /** * 变更积分 - * + * + * @param User $user 用户 + * @param int $points 积分 + * @param PointLogAction $action 操作 + * @param $options {remark: 备注, loggable: 来源, administrator: 操作人, store_id: 所属门店} * @throws \App\Exceptions\BizException */ public function change( User $user, int $points, PointLogAction $action, - ?string $remark = null, - ?Model $loggable = null, - ?Administrator $administrator = null, + // ?string $remark = null, + // ?Model $loggable = null, + // ?Administrator $administrator = null, + $options = [] ) { if ($points === 0) { return; @@ -56,6 +61,9 @@ class PointService $userinfo->update([ 'points' => $after, ]); + $remark = data_get($options, 'remark'); + $loggable = data_get($options, 'loggable'); + $administrator = data_get($options, 'administrator'); PointLog::create([ 'loggable_id' => $loggable?->id, @@ -67,6 +75,7 @@ class PointService 'after_points' => $after, 'remark' => $remark, 'administrator_id' => $administrator?->id, + 'store_id' => data_get($options, 'store_id'), ]); } } diff --git a/app/Services/VipService.php b/app/Services/VipService.php index 4a075b86..1cc8af1f 100644 --- a/app/Services/VipService.php +++ b/app/Services/VipService.php @@ -11,7 +11,7 @@ use Carbon\Carbon; class VipService { - public function buy(User $user, Vip $vip) + public function buy(User $user, Vip $vip, $params = []) { if (!$vip->status) { throw new BizException('该会员卡已关闭'); @@ -22,6 +22,7 @@ class VipService 'price' => $vip->price, 'times' => $vip->times, 'gift' => $vip->gift, + 'store_id' => data_get($params, 'store_id') ]); return $user_vip; @@ -79,7 +80,7 @@ class VipService * @param UserVip $userVip 购买记录 * @param array $params {'pay_at':'购买时间'} */ - public function success(UserVip $userVip, array $params = null) + public function success(UserVip $userVip, array $params = []) { if ($userVip->status == UserVip::STATUS_SUCCESS) { return false; @@ -133,13 +134,11 @@ class VipService } if (isset($gift['points']) && bccomp($gift['points'], 0, 2) === 1) { - (new PointService())->change( - $user, - bcmul($gift['points'], 100), - PointLogAction::Recharge, - "购买【{$userVip->name}】赠送积分", - $userVip, - ); + (new PointService())->change($user, bcmul($gift['points'], 100), PointLogAction::Recharge, [ + 'remarks' => "购买【{$userVip->name}】赠送积分", + 'loggable' => $userVip, + 'store_id' => $userVip->store_id, + ]); } } diff --git a/database/migrations/2021_12_03_135905_create_vips_table.php b/database/migrations/2021_12_03_135905_create_vips_table.php index aa15e11c..4d397e4d 100644 --- a/database/migrations/2021_12_03_135905_create_vips_table.php +++ b/database/migrations/2021_12_03_135905_create_vips_table.php @@ -28,6 +28,7 @@ class CreateVipsTable extends Migration $table->id(); $table->unsignedBigInteger('user_id')->comment('用户ID'); $table->unsignedBigInteger('vip_id')->comment('会员ID'); + $table->unsignedBigInteger('store_id')->nullable()->comment('开通门店'); $table->string('name')->comment('名称'); $table->decimal('price', 12, 2)->default(0)->comment('价格'); $table->text('times')->comment('时效{num,unit,text}'); diff --git a/database/migrations/2023_10_12_103127_create_point_logs_table.php b/database/migrations/2023_10_12_103127_create_point_logs_table.php index 5c6afb21..dd3d8b33 100644 --- a/database/migrations/2023_10_12_103127_create_point_logs_table.php +++ b/database/migrations/2023_10_12_103127_create_point_logs_table.php @@ -22,6 +22,7 @@ class CreatePointLogsTable extends Migration $table->unsignedBigInteger('before_points')->comment('变更前的积分'); $table->unsignedBigInteger('after_points')->comment('变更后的积分'); $table->string('remark')->nullable()->comment('备注'); + $table->unsignedBigInteger('store_id')->nullable()->comment('所属门店'); $table->unsignedBigInteger('administrator_id')->nullable()->comment('管理员ID'); $table->timestamps(); diff --git a/public/images/avatar.jpeg b/public/images/avatar.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..e05555c8179d9e4a65e5a3ca32d95466e7c5bf37 GIT binary patch literal 10221 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 literal 0 HcmV?d00001 diff --git a/resources/lang/zh_CN/offline-order.php b/resources/lang/zh_CN/offline-order.php index 261b8e88..f23de3c7 100644 --- a/resources/lang/zh_CN/offline-order.php +++ b/resources/lang/zh_CN/offline-order.php @@ -23,6 +23,8 @@ return [ 'status' => '状态', 'revoked_at' => '取消时间', 'items' => '品类', + 'user_remark' => '用户备注', + 'staff_remark' => '员工备注', ], 'options' => [ ], diff --git a/resources/lang/zh_CN/point-log.php b/resources/lang/zh_CN/point-log.php index c3cbd1b1..7fcd440b 100644 --- a/resources/lang/zh_CN/point-log.php +++ b/resources/lang/zh_CN/point-log.php @@ -6,6 +6,7 @@ return [ 'point-logs' => '积分流水', ], 'fields' => [ + 'store_id' => '所属门店', 'user_id' => '用户ID', 'user'=>[ 'phone' => '手机号', diff --git a/resources/lang/zh_CN/user-vip.php b/resources/lang/zh_CN/user-vip.php index e6061ec4..7b8641af 100644 --- a/resources/lang/zh_CN/user-vip.php +++ b/resources/lang/zh_CN/user-vip.php @@ -18,6 +18,9 @@ return [ 'points' => '赠送积分', 'expired' => '会员有效期', 'price' => '价格', + 'store' => [ + 'title' => '开通门店' + ], ], 'options' => [ ],