From 26e42dd2a4223a7129ce9a2a4eb60bcb51797588 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=AB=E5=9C=B0=E5=83=A7?= <357099073@qq.com> Date: Fri, 29 May 2026 22:55:10 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=88=86=E7=BB=84=E5=AF=86?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/GoodsGroupController.php | 13 +++ app/Http/Controllers/Home/HomeController.php | 21 ++++ app/Models/GoodsGroup.php | 8 ++ app/Service/GoodsService.php | 97 ++++++++++++++++++- app/Service/OrderService.php | 2 + public/assets/luna/main.js | 34 ++++++- resources/lang/zh_CN/dujiaoka.php | 10 +- resources/lang/zh_CN/goods-group.php | 3 + resources/lang/zh_TW/dujiaoka.php | 11 ++- resources/lang/zh_TW/goods-group.php | 3 + .../views/hyper/static_pages/home.blade.php | 48 ++++++++- .../views/luna/static_pages/home.blade.php | 14 ++- .../views/unicorn/static_pages/home.blade.php | 55 ++++++++++- routes/common/web.php | 3 +- 14 files changed, 308 insertions(+), 14 deletions(-) diff --git a/app/Admin/Controllers/GoodsGroupController.php b/app/Admin/Controllers/GoodsGroupController.php index 4dd4d8a..b17303d 100644 --- a/app/Admin/Controllers/GoodsGroupController.php +++ b/app/Admin/Controllers/GoodsGroupController.php @@ -26,6 +26,7 @@ class GoodsGroupController extends AdminController $grid->column('id')->sortable(); $grid->column('gp_name')->editable(); $grid->column('is_open')->switch(); + $grid->column('is_open_group_pwd')->switch(); $grid->column('ord')->editable(); $grid->column('created_at'); $grid->column('updated_at')->sortable(); @@ -66,6 +67,16 @@ class GoodsGroupController extends AdminController return admin_trans('dujiaoka.status_close'); } }); + $show->field('is_open_group_pwd')->as(function ($isOpenGroupPwd) { + if ($isOpenGroupPwd == GoodsGroupModel::STATUS_OPEN) { + return admin_trans('dujiaoka.status_open'); + } else { + return admin_trans('dujiaoka.status_close'); + } + }); + $show->field('group_pwd')->as(function ($groupPwd) { + return empty($groupPwd) ? '' : '******'; + }); $show->field('ord'); $show->field('created_at'); $show->field('updated_at'); @@ -83,6 +94,8 @@ class GoodsGroupController extends AdminController $form->display('id'); $form->text('gp_name'); $form->switch('is_open')->default(GoodsGroupModel::STATUS_OPEN); + $form->switch('is_open_group_pwd')->default(GoodsGroupModel::STATUS_CLOSE); + $form->text('group_pwd')->help(admin_trans('goods-group.fields.group_pwd_help')); $form->number('ord')->default(1)->help(admin_trans('dujiaoka.ord')); $form->display('created_at'); $form->display('updated_at'); diff --git a/app/Http/Controllers/Home/HomeController.php b/app/Http/Controllers/Home/HomeController.php index 753acc5..baf8692 100644 --- a/app/Http/Controllers/Home/HomeController.php +++ b/app/Http/Controllers/Home/HomeController.php @@ -65,6 +65,7 @@ class HomeController extends BaseController try { $goods = $this->goodsService->detail($id); $this->goodsService->validatorGoodsStatus($goods); + $this->goodsService->validatorGoodsGroupAccess($goods); // 有没有优惠码可以展示 if (count($goods->coupon)) { $goods->open_coupon = 1; @@ -83,6 +84,26 @@ class HomeController extends BaseController } + /** + * 验证商品分类访问密码 + * + * @param Request $request + * @return \Illuminate\Http\JsonResponse + */ + public function verifyGroupPassword(Request $request) + { + try { + $request->validate([ + 'group_id' => 'required|integer', + 'password' => 'required|string', + ]); + $this->goodsService->verifyGroupPassword((int) $request->input('group_id'), (string) $request->input('password')); + return response()->json(['code' => 200, 'msg' => __('dujiaoka.prompt.goods_group_password_success')]); + } catch (RuleValidationException $ruleValidationException) { + return response()->json(['code' => 400, 'msg' => $ruleValidationException->getMessage()]); + } + } + /** * 极验行为验证 * diff --git a/app/Models/GoodsGroup.php b/app/Models/GoodsGroup.php index 3f13fd2..1a4a831 100644 --- a/app/Models/GoodsGroup.php +++ b/app/Models/GoodsGroup.php @@ -17,6 +17,14 @@ class GoodsGroup extends BaseModel 'deleted' => GoodsGroupDeleted::class ]; + protected $hidden = [ + 'group_pwd', + ]; + + protected $casts = [ + 'is_open_group_pwd' => 'integer', + ]; + /** * 关联商品 * diff --git a/app/Service/GoodsService.php b/app/Service/GoodsService.php index 19df1d6..649cb82 100644 --- a/app/Service/GoodsService.php +++ b/app/Service/GoodsService.php @@ -40,6 +40,7 @@ class GoodsService public function withGroup(): ?array { $goods = GoodsGroup::query() + ->select(['id', 'gp_name', 'is_open', 'is_open_group_pwd', 'group_pwd', 'ord', 'created_at', 'updated_at', 'deleted_at']) ->with(['goods' => function($query) { $query->withCount(['carmis' => function($query) { $query->where('status', Carmis::STATUS_UNSOLD); @@ -48,6 +49,15 @@ class GoodsService ->where('is_open', GoodsGroup::STATUS_OPEN) ->orderBy('ord', 'DESC') ->get(); + + $goods->each(function (GoodsGroup $group) { + $isLocked = $this->isGroupPasswordProtected($group) && !$this->hasGroupAccess($group->id); + $group->setAttribute('is_group_locked', $isLocked ? GoodsGroup::STATUS_OPEN : GoodsGroup::STATUS_CLOSE); + if ($isLocked) { + $group->setRelation('goods', collect()); + } + }); + // 将自动 return $goods ? $goods->toArray() : null; } @@ -65,13 +75,98 @@ class GoodsService public function detail(int $id) { $goods = Goods::query() - ->with(['coupon']) + ->with(['coupon', 'group']) ->withCount(['carmis' => function($query) { $query->where('status', Carmis::STATUS_UNSOLD); }])->where('id', $id)->first(); return $goods; } + /** + * 分类是否开启密码访问 + * + * @param GoodsGroup|null $group + * @return bool + */ + public function isGroupPasswordProtected(?GoodsGroup $group): bool + { + return !empty($group) + && $group->is_open_group_pwd == GoodsGroup::STATUS_OPEN + && !empty($group->group_pwd); + } + + /** + * 当前会话是否已解锁分类 + * + * @param int $groupID 分类id + * @return bool + */ + public function hasGroupAccess(int $groupID): bool + { + $groupIDs = session('dujiaoka_group_pwd_access', []); + return in_array($groupID, $groupIDs); + } + + /** + * 验证分类访问密码 + * + * @param int $groupID 分类id + * @param string $password 访问密码 + * @return bool + */ + public function verifyGroupPassword(int $groupID, string $password): bool + { + $group = GoodsGroup::query() + ->where('is_open', GoodsGroup::STATUS_OPEN) + ->where('id', $groupID) + ->first(); + + if (empty($group)) { + throw new RuleValidationException(__('dujiaoka.prompt.goods_group_does_not_exist')); + } + + if (!$this->isGroupPasswordProtected($group)) { + $this->grantGroupAccess($group->id); + return true; + } + + if (!hash_equals((string) $group->group_pwd, (string) $password)) { + throw new RuleValidationException(__('dujiaoka.prompt.goods_group_password_error')); + } + + $this->grantGroupAccess($group->id); + return true; + } + + /** + * 验证商品所属分类访问权限 + * + * @param Goods $goods 商品模型 + * @return void + */ + public function validatorGoodsGroupAccess(Goods $goods): void + { + $group = $goods->group; + if ($this->isGroupPasswordProtected($group) && !$this->hasGroupAccess($group->id)) { + throw new RuleValidationException(__('dujiaoka.prompt.goods_group_password_required')); + } + } + + /** + * 授权当前会话访问分类 + * + * @param int $groupID 分类id + * @return void + */ + private function grantGroupAccess(int $groupID): void + { + $groupIDs = session('dujiaoka_group_pwd_access', []); + if (!in_array($groupID, $groupIDs)) { + $groupIDs[] = $groupID; + session(['dujiaoka_group_pwd_access' => $groupIDs]); + } + } + /** * 格式化商品信息 * diff --git a/app/Service/OrderService.php b/app/Service/OrderService.php index 85f6936..aa9fa80 100644 --- a/app/Service/OrderService.php +++ b/app/Service/OrderService.php @@ -106,6 +106,8 @@ class OrderService $goods = $this->goodsService->detail($request->input('gid')); // 商品状态验证 $this->goodsService->validatorGoodsStatus($goods); + // 商品所属分类访问权限验证 + $this->goodsService->validatorGoodsGroupAccess($goods); // 如果有限购 if ($goods->buy_limit_num > 0 && $request->input('by_amount') > $goods->buy_limit_num) { throw new RuleValidationException(__('dujiaoka.prompt.purchase_limit_exceeded')); diff --git a/public/assets/luna/main.js b/public/assets/luna/main.js index bc078b6..97ae5ed 100644 --- a/public/assets/luna/main.js +++ b/public/assets/luna/main.js @@ -52,9 +52,37 @@ if (typeof goodsMsg !== 'undefined' && goodsMsg !== '') { let cateTpl = document.getElementById('cateTpl').innerHTML, cateHtml = ''; let goodsTpl = document.getElementById('goodsTpl').innerHTML, goodsHtml; - let changeCate = function (key) { + let showGroupPassword = function (group) { + layer.prompt({ + title : (typeof groupPasswordTitle !== 'undefined' ? groupPasswordTitle : '请输入分类访问密码') + ':' + group.gp_name, + formType: 1 + }, function (value, index) { + $.post(groupPasswordVerifyUrl, { + _token : groupPasswordCsrfToken, + group_id: group.id, + password: value + }, function (res) { + if (res.code === 200) { + layer.close(index); + window.location.reload(); + } else { + layer.msg(res.msg); + } + }); + }); + }; + let changeCate = function (key, silent) { + let group = goodsMsg[key]; + if (group.is_group_locked) { + $('.goods-list').empty(); + $('.cate-box').removeClass('cate-box-select').eq(key).addClass('cate-box-select'); + if (!silent) { + showGroupPassword(group); + } + return; + } goodsHtml = ''; - goodsMsg[key].goods.forEach(function (i) { + group.goods.forEach(function (i) { if (i.wholesale_price_cnf != "" && i.wholesale_price_cnf != null) { i.wholesale_price_arr = i.wholesale_price_cnf.split("\r\n"); i.wholesale_price_arr.forEach(function (ii, k) { @@ -79,7 +107,7 @@ $('.cate').empty().append(cateHtml).on('click', '.cate-box', function () { changeCate($(this).data('key')); }); - changeCate(0); + changeCate(0, true); } diff --git a/resources/lang/zh_CN/dujiaoka.php b/resources/lang/zh_CN/dujiaoka.php index a6eae47..e596fa0 100644 --- a/resources/lang/zh_CN/dujiaoka.php +++ b/resources/lang/zh_CN/dujiaoka.php @@ -124,7 +124,15 @@ return [ 'no_related_order_found_for_cache' => '未找到相关订单缓存!', 'no_related_order_found' => '未找到相关订单!', 'new_order_push' => '新订单通知', - 'loop_carmis_limit' => '此商品最多购买一件!' + 'loop_carmis_limit' => '此商品最多购买一件!', + 'goods_group_does_not_exist' => '商品分类不存在', + 'goods_group_password_error' => '分类访问密码错误', + 'goods_group_password_required' => '请先输入分类访问密码', + 'goods_group_password_required_short' => '密码访问', + 'goods_group_password_title' => '分类密码访问', + 'goods_group_password_placeholder' => '请输入分类访问密码', + 'goods_group_password_submit' => '确认进入', + 'goods_group_password_success' => '验证成功' ], 'equipment' => [ diff --git a/resources/lang/zh_CN/goods-group.php b/resources/lang/zh_CN/goods-group.php index 7f21784..be34629 100644 --- a/resources/lang/zh_CN/goods-group.php +++ b/resources/lang/zh_CN/goods-group.php @@ -8,6 +8,9 @@ return [ 'fields' => [ 'gp_name' => '分类名称', 'is_open' => '是否启用', + 'is_open_group_pwd' => '开启密码访问', + 'group_pwd' => '访问密码', + 'group_pwd_help' => '开启密码访问后,前台需要输入此密码才可查看该分类商品', 'ord' => '排序权重 越大越靠前', ], 'options' => [ diff --git a/resources/lang/zh_TW/dujiaoka.php b/resources/lang/zh_TW/dujiaoka.php index b665810..35f8785 100644 --- a/resources/lang/zh_TW/dujiaoka.php +++ b/resources/lang/zh_TW/dujiaoka.php @@ -123,7 +123,16 @@ return [ 'search_order_browser_tips' => '最多只能查詢最近 5 筆訂單', 'no_related_order_found_for_cache' => '未找到相關訂單快取!', 'no_related_order_found' => '未找到相關訂單!', - 'new_order_push' => '新訂單通知' + 'new_order_push' => '新訂單通知', + 'loop_carmis_limit' => '此商品最多購買一件!', + 'goods_group_does_not_exist' => '商品分類不存在', + 'goods_group_password_error' => '分類訪問密碼錯誤', + 'goods_group_password_required' => '請先輸入分類訪問密碼', + 'goods_group_password_required_short' => '密碼訪問', + 'goods_group_password_title' => '分類密碼訪問', + 'goods_group_password_placeholder' => '請輸入分類訪問密碼', + 'goods_group_password_submit' => '確認進入', + 'goods_group_password_success' => '驗證成功' ], 'equipment' => [ diff --git a/resources/lang/zh_TW/goods-group.php b/resources/lang/zh_TW/goods-group.php index 61cef99..3c5a266 100644 --- a/resources/lang/zh_TW/goods-group.php +++ b/resources/lang/zh_TW/goods-group.php @@ -8,6 +8,9 @@ return [ 'fields' => [ 'gp_name' => '分類名稱', 'is_open' => '是否啟用', + 'is_open_group_pwd' => '開啟密碼訪問', + 'group_pwd' => '訪問密碼', + 'group_pwd_help' => '開啟密碼訪問後,前台需要輸入此密碼才可查看該分類商品', 'ord' => '排序權重 越大越靠前', ], 'options' => [ diff --git a/resources/views/hyper/static_pages/home.blade.php b/resources/views/hyper/static_pages/home.blade.php index 351aa1e..30ca84a 100644 --- a/resources/views/hyper/static_pages/home.blade.php +++ b/resources/views/hyper/static_pages/home.blade.php @@ -32,9 +32,16 @@ @foreach($data as $index => $group) + @if(!empty($group['is_group_locked'])) +