* @copyright assimon * @link http://utf8.hk/ */ namespace App\Service; use App\Exceptions\RuleValidationException; use App\Models\Carmis; use App\Models\Goods; use App\Models\GoodsGroup; /** * 商品服务层 * * Class GoodsService * @package App\Service * @author: Assimon * @email: Ashang@utf8.hk * @blog: https://utf8.hk * Date: 2021/5/30 */ class GoodsService { /** * 获取所有分类并加载该分类下的商品 * * @return array|null * * @author assimon * @copyright assimon * @link http://utf8.hk/ */ 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); }])->where('is_open', Goods::STATUS_OPEN)->orderBy('ord', 'DESC'); }]) ->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; } /** * 商品详情 * * @param int $id 商品id * @return \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Model|object|null * * @author assimon * @copyright assimon * @link http://utf8.hk/ */ public function detail(int $id) { $goods = Goods::query() ->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 array */ public function verifyGroupPassword(int $groupID, string $password): array { $group = GoodsGroup::query() ->with(['goods' => function($query) { $query->withCount(['carmis' => function($query) { $query->where('status', Carmis::STATUS_UNSOLD); }])->where('is_open', Goods::STATUS_OPEN)->orderBy('ord', 'DESC'); }]) ->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 $this->formatGroupForResponse($group); } if (!hash_equals((string) $group->group_pwd, (string) $password)) { throw new RuleValidationException(__('dujiaoka.prompt.goods_group_password_error')); } $this->grantGroupAccess($group->id); return $this->formatGroupForResponse($group); } /** * 格式化分类接口返回数据 * * @param GoodsGroup $group 分类模型 * @return array */ private function formatGroupForResponse(GoodsGroup $group): array { $data = $group->toArray(); foreach ($data['goods'] as &$goods) { $goods['picture_url'] = picture_ulr($goods['picture']); } unset($goods); return $data; } /** * 验证商品所属分类访问权限 * * @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 */ public function forgetGroupAccess(int $groupID): void { $groupIDs = session('dujiaoka_group_pwd_access', []); $groupIDs = array_values(array_filter($groupIDs, function ($id) use ($groupID) { return (int) $id !== $groupID; })); session(['dujiaoka_group_pwd_access' => $groupIDs]); } /** * 授权当前会话访问分类 * * @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]); } } /** * 格式化商品信息 * * @param Goods $goods 商品模型 * @return Goods * * @author assimon * @copyright assimon * @link http://utf8.hk/ */ public function format(Goods $goods) { // 格式化批发配置以及输入框配置 $goods->wholesale_price_cnf = $goods->wholesale_price_cnf ? format_wholesale_price($goods->wholesale_price_cnf) : null; // 如果存在其他配置输入框且为代充 $goods->other_ipu = $goods->other_ipu_cnf ? format_charge_input($goods->other_ipu_cnf) : null; return $goods; } /** * 验证商品状态 * * @param Goods $goods * @return Goods * @throws RuleValidationException * * @author assimon * @copyright assimon * @link http://utf8.hk/ */ public function validatorGoodsStatus(Goods $goods): Goods { if (empty($goods)) { throw new RuleValidationException(__('dujiaoka.prompt.goods_does_not_exist')); } // 上架判断. if ($goods->is_open != Goods::STATUS_OPEN) { throw new RuleValidationException(__('dujiaoka.prompt.the_goods_is_not_on_the_shelves')); } return $goods; } /** * 库存减去 * * @param int $id 商品id * @param int $number 出库数量 * * @author assimon * @copyright assimon * @link http://utf8.hk/ */ public function inStockDecr(int $id, int $number = 1): bool { return Goods::query()->where('id', $id)->decrement('in_stock', $number); } /** * 商品销量加 * * @param int $id 商品id * @param int $number 数量 * @return bool * * @author assimon * @copyright assimon * @link http://utf8.hk/ */ public function salesVolumeIncr(int $id, int $number = 1): bool { return Goods::query()->where('id', $id)->increment('sales_volume', $number); } }