From 6521b4e7467e539ade6726b2cee2645d4dc2018c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E4=B8=96=E7=95=8C?= <642747453@qq.com> Date: Thu, 28 Jul 2022 13:46:08 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20#20220728=20=E5=95=86=E5=93=81=E5=9F=BA?= =?UTF-8?q?=E7=A1=80=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Filters/Filters.php | 121 ++++++++++++++++++ app/Http/Controllers/Controller.php | 6 + .../Goods/GoodsBrandsController.php | 71 ++++++++-- .../Controllers/Goods/GoodsController.php | 79 ++++++++++++ .../Controllers/Goods/GoodsSkusController.php | 26 ++++ .../Goods/GoodsTypesController.php | 81 +++++++++++- app/Http/Resources/GoodsResource.php | 19 +++ app/Http/Resources/GoodsSkuResource.php | 19 +++ app/Models/Goods.php | 20 +++ app/Models/GoodsBrand.php | 7 +- app/Models/GoodsSku.php | 15 +++ app/Models/GoodsType.php | 7 +- app/Models/Model.php | 12 ++ app/Models/traits/Filter.php | 52 ++++++++ .../2014_10_12_000000_create_users_table.php | 2 +- ..._07_26_061712_create_goods_types_table.php | 2 +- ...07_26_085847_create_goods_brands_table.php | 2 +- ...2_07_26_090150_create_goods_skus_table.php | 2 +- routes/api.php | 16 ++- 19 files changed, 536 insertions(+), 23 deletions(-) create mode 100644 app/Filters/Filters.php create mode 100644 app/Http/Controllers/Goods/GoodsController.php create mode 100644 app/Http/Controllers/Goods/GoodsSkusController.php create mode 100644 app/Http/Resources/GoodsResource.php create mode 100644 app/Http/Resources/GoodsSkuResource.php create mode 100644 app/Models/traits/Filter.php diff --git a/app/Filters/Filters.php b/app/Filters/Filters.php new file mode 100644 index 0000000..c757ff8 --- /dev/null +++ b/app/Filters/Filters.php @@ -0,0 +1,121 @@ +request = Request(); + } + + /** + * Apply the filters. + * + * @param \Illuminate\Database\Eloquent\Builder $builder + * @return \Illuminate\Database\Eloquent\Builder + */ + public function apply($builder) + { + $this->builder = $builder; + $fieldsSearchable = $this->getFieldsSearchable(); + $fields = $this->getFilters($fieldsSearchable); + if (is_array($fields) && count($fields) && is_array($fieldsSearchable) && count($fieldsSearchable)) { + foreach ($fields as $field => $value) { + $method = Str::camel($field); + if (method_exists($this, $method)) { + $this->$method($value); + } elseif (isset($fieldsSearchable[$field])) { + $condition = $fieldsSearchable[$field]; + $value = $condition == "like" ? "%{$value}%" : $value; + $relation = null; + if (stripos($field, '.')) { + $explode = explode('.', $field); + $field = array_pop($explode); + $relation = implode('.', $explode); + } + $modelTableName = $builder->getModel()->getTable(); + $field = Str::snake($field); + if (!is_null($value)) { + if (!is_null($relation)) { + $builder->whereHas($relation, function ($query) use ($field, $condition, $value) { + $query->where($field, $condition, $value); + }); + } else { + $builder->where($modelTableName . '.' . $field, $condition, $value); + } + } + } + } + + return $this->builder; + } + } + + /** + * Fetch all relevant filters from the request. + * + * @return array + */ + public function getFilters($fieldsSearchable) + { + if (!is_array($fieldsSearchable) || count($fieldsSearchable) == 0) { + return []; + } + $keys = array_keys($fieldsSearchable); + //$this->request->only() 会把user.nickname参数里包含"." 转换成数组 + //Arr::dot 方法使用「.」号将将多维数组转化为一维数组: + + return array_filter(Arr::dot($this->request->only($keys)), function ($value) { + return !is_null($value) && $value != ''; + }); + } + + //获取model 可以查询的参数 + protected function getFieldsSearchable() + { + $model = $this->builder->getModel(); + if (!property_exists($model, 'fieldSearchable')) { + return []; + } + $fieldSearchable = $model->fieldSearchable; + $fields = []; + foreach ($fieldSearchable as $field => $condition) { + if (is_numeric($field)) { + $field = $condition; + $condition = "="; + } + $fields[$field] = $condition; + } + + return $fields; + } +} diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index a0a2a8a..81d81af 100644 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -10,4 +10,10 @@ use Illuminate\Routing\Controller as BaseController; class Controller extends BaseController { use AuthorizesRequests, DispatchesJobs, ValidatesRequests; + + protected $res = [ + 'httpCode' => 200, + 'errorCode' => 0, + 'errorMessage' => '', + ]; } diff --git a/app/Http/Controllers/Goods/GoodsBrandsController.php b/app/Http/Controllers/Goods/GoodsBrandsController.php index f147810..539aa32 100644 --- a/app/Http/Controllers/Goods/GoodsBrandsController.php +++ b/app/Http/Controllers/Goods/GoodsBrandsController.php @@ -5,6 +5,9 @@ namespace App\Http\Controllers\Goods; use App\Http\Controllers\Controller; use App\Http\Resources\GoodsBrandResource; use App\Models\GoodsBrand; +use Illuminate\Http\Request; +use Illuminate\Support\Facades\Validator; +use Illuminate\Validation\Rule; class GoodsBrandsController extends Controller { @@ -17,28 +20,78 @@ class GoodsBrandsController extends Controller public function store(Request $request) { - $request->validate([ - 'name' => 'required|string|unique:users|max:255', + $validator = Validator::make($request->all(), [ + 'names' => 'required|array', + 'names.*' => 'required|string|max:255|unique:goods_brands,name', ]); + if ($validator->fails()) { + $this->res = [ + 'httpCode' => 400, + 'errorCode' => 400416, + 'errorMessage' => $validator->getMessageBag()->getMessages(), + ]; + goto end; + } + $goodsBrands = []; + foreach ($request->names as $name) { + $goodsBrands[] = ['name' => $name]; + } + $goodsBrand = new GoodsBrand(); + if (!$goodsBrand->batchInsert($goodsBrands)) { + $this->res = [ + 'httpCode' => 500, + 'errorCode' => 500500, + 'errorMessage' => '批量添加失败', + ]; + } + end: - GoodsBrand::created(); + return response($this->res, $this->res['httpCode']); } public function show($id) { - $columns = ['*']; - $goodsBrand = GoodsBrand::query()->find($id, $columns); - - return GoodsBrandResource::collection($goodsBrand); + return new GoodsBrandResource(GoodsBrand::query()->find($id)); } - public function update() + public function update($id, Request $request) { + $validator = Validator::make($request->all(), [ + 'name' => [ + 'required', + 'string', + 'max:255', + Rule::unique('goods_brands')->ignore($id), + ] + ]); + if ($validator->fails()) { + $this->res = [ + 'httpCode' => 400, + 'errorCode' => 400416, + 'errorMessage' => $validator->getMessageBag()->getMessages(), + ]; + return response($this->res, $this->res['httpCode']); + } + $goodsBrand = GoodsBrand::query()->find($id); + $goodsBrand->name = request('name'); + $goodsBrand->save(); + return new GoodsBrandResource($goodsBrand); } - public function destory() + public function destroy($id) { + $goodsBrand = GoodsBrand::query()->find($id); + try { + $goodsBrand->delete(); + } catch (\Exception $e) { + $this->res = [ + 'httpCode' => 500, + 'errorCode' => 500416, + 'errorMessage' => $e->getMessage(), + ]; + } + return response($this->res, $this->res['httpCode']); } } diff --git a/app/Http/Controllers/Goods/GoodsController.php b/app/Http/Controllers/Goods/GoodsController.php new file mode 100644 index 0000000..b6a26ef --- /dev/null +++ b/app/Http/Controllers/Goods/GoodsController.php @@ -0,0 +1,79 @@ +with(['goods' => function($query) { + + }]) + ->paginate(); + + return GoodsSkuResource::collection($goods); + } + + public function store(Request $request) + { + $validator = Validator::make($request->all(), [ + 'title' => ['required', 'string', 'max:255'], + 'img_url' => ['required', 'string', 'max:255'], + 'type_id' => ['required', 'integer', 'exists:goods_types,id'], + 'brand_id' => ['integer', 'exists:goods_brands,id'], + 'goods_code' => ['required', 'alpha_dash', 'max:32', 'unique:goods,goods_code'], + 'skus' => ['required', 'array'], + 'skus.*.title' => ['required', 'string', 'max:255'], + 'skus.*.sku_code' => ['required', 'distinct', 'alpha_dash', 'max:32'], + 'skus.*.status' => ['required', 'integer', Rule::in([0, 1, 2])], + 'skus.*.num' => ['required', 'integer', 'max:10'], + 'skus.*.cost' => ['required', 'numeric', 'max:10'], + ]); + if ($validator->fails()) { + $this->res = [ + 'httpCode' => 400, + 'errorCode' => 400416, + 'errorMessage' => $validator->getMessageBag()->getMessages(), + ]; + goto end; + } + DB::beginTransaction(); + try { + $goods = new Goods(); + $goods->title = $request->title; + $goods->img_url = $request->img_url; + $goods->type_id = $request->type_id; + $goods->brand_id = $request->brand_id; + $goods->goods_code = $request->goods_code; + $goods->save(); + $goodsSkus = []; + foreach ($request->skus as $item) { + $item['goods_id'] = $goods->id; + $goodsSkus[] = $item; + } + $goods->skus()->createMany($goodsSkus); + DB::commit(); + } catch (\Exception $exception) { + DB::rollBack(); + $this->res = [ + 'httpCode' => 400, + 'errorCode' => 400416, + 'errorMessage' => $exception->getMessage(), + ]; + } + end: + + return response($this->res, $this->res['httpCode']); + } +} diff --git a/app/Http/Controllers/Goods/GoodsSkusController.php b/app/Http/Controllers/Goods/GoodsSkusController.php new file mode 100644 index 0000000..2d24755 --- /dev/null +++ b/app/Http/Controllers/Goods/GoodsSkusController.php @@ -0,0 +1,26 @@ +get(['id', 'title'])->toArray(); + } + + public function show($id) + { + return new GoodsSkuResource(GoodsSku::query()->with(['goods', 'brand'])->find($id)); + } + + public function update($id, Request $request) + { + + } +} diff --git a/app/Http/Controllers/Goods/GoodsTypesController.php b/app/Http/Controllers/Goods/GoodsTypesController.php index 55b0a76..2006b0b 100644 --- a/app/Http/Controllers/Goods/GoodsTypesController.php +++ b/app/Http/Controllers/Goods/GoodsTypesController.php @@ -3,8 +3,87 @@ namespace App\Http\Controllers\Goods; use App\Http\Controllers\Controller; +use App\Http\Resources\GoodsTypeResource; +use App\Models\GoodsType; +use Illuminate\Http\Request; +use Illuminate\Support\Facades\Validator; +use Illuminate\Validation\Rule; -class GoodsTypeController extends Controller +class GoodsTypesController extends Controller { + public function index() + { + $goodsTypes = GoodsType::query()->paginate(); + return GoodsTypeResource::collection($goodsTypes); + } + + public function store(Request $request) + { + $validator = Validator::make($request->all(), [ + 'names' => 'required|array', + 'names.*' => 'required|string|max:255|unique:goods_types,name', + ]); + if ($validator->fails()) { + $this->res = [ + 'httpCode' => 400, + 'errorCode' => 400416, + 'errorMessage' => $validator->getMessageBag()->getMessages(), + ]; + goto end; + } + $goodsTypes = []; + foreach ($request->names as $name) { + $goodsTypes[] = ['name' => $name]; + } + $goodsType = new GoodsType(); + if (!$goodsType->batchInsert($goodsTypes)) { + $this->res = [ + 'httpCode' => 500, + 'errorCode' => 500500, + 'errorMessage' => '批量添加失败', + ]; + } + end: + + return response($this->res, $this->res['httpCode']); + } + + public function show($id) + { + return new GoodsTypeResource(GoodsType::query()->find($id)); + } + + public function update($id, Request $request) + { + $validator = Validator::make($request->all(), [ + 'name' => [ + 'required', + 'string', + 'max:255', + Rule::unique('goods_brands')->ignore($id), + ] + ]); + if ($validator->fails()) { + $this->res = [ + 'httpCode' => 400, + 'errorCode' => 400416, + 'errorMessage' => $validator->getMessageBag()->getMessages(), + ]; + return response($this->res, $this->res['httpCode']); + } + $goodsType = GoodsType::query()->find($id); + $goodsType->name = request('name'); + $goodsType->save(); + + return new GoodsTypeResource($goodsType); + } + + public function destroy($id) + { + $goodsType = GoodsType::query()->find($id); + $goodsType->delete(); + + return response($this->res, $this->res['httpCode']); + } } diff --git a/app/Http/Resources/GoodsResource.php b/app/Http/Resources/GoodsResource.php new file mode 100644 index 0000000..b3b8586 --- /dev/null +++ b/app/Http/Resources/GoodsResource.php @@ -0,0 +1,19 @@ +hasMany(GoodsSku::class, 'goods_id'); + } + + public function brand() + { + return $this->hasOne(GoodsBrand::class, 'id', 'brand_id'); + } + + public function type() + { + return $this->hasOne(GoodsType::class, 'id', 'type_id'); + } } diff --git a/app/Models/GoodsBrand.php b/app/Models/GoodsBrand.php index 4efdf68..1d624ce 100644 --- a/app/Models/GoodsBrand.php +++ b/app/Models/GoodsBrand.php @@ -4,5 +4,10 @@ namespace App\Models; class GoodsBrand extends Model { - + /** + * 数组中的属性会被隐藏。 + * + * @var array + */ + protected $hidden = ['deleted_at']; } diff --git a/app/Models/GoodsSku.php b/app/Models/GoodsSku.php index e72b428..9963d55 100644 --- a/app/Models/GoodsSku.php +++ b/app/Models/GoodsSku.php @@ -4,5 +4,20 @@ namespace App\Models; class GoodsSku extends Model { + /** + * 不可批量赋值的属性。为空则所有熟悉都可以批量赋值 + * + * @var array + */ + protected $guarded = []; + protected $hidden = ['goods_id', 'created_at']; + + /** + * 此规格从属于一个商品 + */ + public function goods() + { + return $this->hasOne(Goods::class, 'id', 'goods_id'); + } } diff --git a/app/Models/GoodsType.php b/app/Models/GoodsType.php index cc0466b..53c496b 100644 --- a/app/Models/GoodsType.php +++ b/app/Models/GoodsType.php @@ -4,5 +4,10 @@ namespace App\Models; class GoodsType extends Model { - + /** + * 数组中的属性会被隐藏。 + * + * @var array + */ + protected $hidden = ['deleted_at']; } diff --git a/app/Models/Model.php b/app/Models/Model.php index 6bf14ce..fd85d4e 100644 --- a/app/Models/Model.php +++ b/app/Models/Model.php @@ -3,6 +3,7 @@ namespace App\Models; use Illuminate\Database\Eloquent\Model as EloquentModel; +use Illuminate\Support\Facades\DB; class Model extends EloquentModel { @@ -20,4 +21,15 @@ class Model extends EloquentModel { return $query->where('user_id', $user->id); } + + public function batchInsert(array $data) + { + $time = date('Y-m-d H:i:s'); + foreach ($data as &$val) { + $val['created_at'] = $val['created_at'] ?? $time; + $val['updated_at'] = $val['updated_at'] ?? $time; + } + + return Db::table($this->getTable())->insert($data); + } } diff --git a/app/Models/traits/Filter.php b/app/Models/traits/Filter.php new file mode 100644 index 0000000..c0f0cf9 --- /dev/null +++ b/app/Models/traits/Filter.php @@ -0,0 +1,52 @@ +getFilterClass($query); + if ($filters) { + + return $filters->apply($query); + } + + return $query; + } + + //自动获取过滤的类 + private function getFilterClass($query) + { + $prefix = 'App\Filters\\'; + $filterClass = $this->getModelFilterClass($query); + if (!$filterClass) { + $currentClass = get_class($this); + $className = substr(strrchr($currentClass, '\\'), 1). 'Filter'; + } + $filterClass = $prefix.$className; + if (class_exists($filterClass)) { + + return new $filterClass(); + } + $filterClass = $prefix.'Filters'; + + return new $filterClass(); + } + + //查看 model是否有 getModelClassName 方法 + private function getModelFilterClass($query) + { + $model = $query->getModel(); + $className = ''; + if (method_exists($model, 'getFilterClassName')) { + $className = $model->getFilterClassName(); + } + + return $className; + } + +} diff --git a/database/migrations/2014_10_12_000000_create_users_table.php b/database/migrations/2014_10_12_000000_create_users_table.php index 5a3b4fa..fc36207 100644 --- a/database/migrations/2014_10_12_000000_create_users_table.php +++ b/database/migrations/2014_10_12_000000_create_users_table.php @@ -19,7 +19,7 @@ class CreateUsersTable extends Migration $table->string('email')->unique(); $table->timestamp('email_verified_at')->nullable(); $table->string('password'); - $table->string('api_token', 80)->unique()->nullable()->default(null); + $table->string('api_token', 80)->unique()->nullable(false); $table->rememberToken(); $table->timestamps(); }); diff --git a/database/migrations/2022_07_26_061712_create_goods_types_table.php b/database/migrations/2022_07_26_061712_create_goods_types_table.php index 7ba17c9..f4bb7d6 100644 --- a/database/migrations/2022_07_26_061712_create_goods_types_table.php +++ b/database/migrations/2022_07_26_061712_create_goods_types_table.php @@ -16,7 +16,7 @@ class CreateGoodsTypesTable extends Migration Schema::create('goods_types', function (Blueprint $table) { $table->bigIncrements('id'); $table->string('name')->nullable(false); - $table->unsignedTinyInteger('is_delete')->default(0); + $table->softDeletes(); $table->timestamps(); // 索引 $table->unique('name'); diff --git a/database/migrations/2022_07_26_085847_create_goods_brands_table.php b/database/migrations/2022_07_26_085847_create_goods_brands_table.php index ed85d2a..b785b7f 100644 --- a/database/migrations/2022_07_26_085847_create_goods_brands_table.php +++ b/database/migrations/2022_07_26_085847_create_goods_brands_table.php @@ -16,7 +16,7 @@ class CreateGoodsBrandsTable extends Migration Schema::create('goods_brands', function (Blueprint $table) { $table->bigIncrements('id'); $table->string('name')->nullable(false); - $table->unsignedTinyInteger('is_delete')->default(0); + $table->softDeletes(); $table->timestamps(); // 索引 $table->unique('name'); diff --git a/database/migrations/2022_07_26_090150_create_goods_skus_table.php b/database/migrations/2022_07_26_090150_create_goods_skus_table.php index af6bd93..522319b 100644 --- a/database/migrations/2022_07_26_090150_create_goods_skus_table.php +++ b/database/migrations/2022_07_26_090150_create_goods_skus_table.php @@ -15,6 +15,7 @@ class CreateGoodsSkusTable extends Migration { Schema::create('goods_skus', function (Blueprint $table) { $table->bigIncrements('id'); + $table->unsignedBigInteger('goods_id')->nullable(false)->comment('商品id'); $table->string('title')->nullable(false)->comment('商品规格'); $table->string('sku_code', 32)->nullable(false)->comment('规格编码'); $table->unsignedTinyInteger('status')->default(0)->comment('规格状态(0-下架,1在售,2预警)'); @@ -27,7 +28,6 @@ class CreateGoodsSkusTable extends Migration $table->unsignedInteger('reserve')->default(0)->comment('预留量'); $table->timestamps(); // 索引 - $table->unique('sku_code'); }); } diff --git a/routes/api.php b/routes/api.php index 4a81b5c..8b891ba 100644 --- a/routes/api.php +++ b/routes/api.php @@ -14,21 +14,23 @@ use App\Http\Controllers\Auth\LoginController; */ Route::middleware('auth:api')->group(function () { // 用户 - Route::resource('users', 'User\UsersController', ['only' => ['index', 'store', 'show', 'update', 'destory']]); + Route::resource('users', 'User\UsersController', ['only' => ['index', 'store', 'show', 'update', 'destroy']]); // 商品种类 - Route::resource('goods_types', 'Goods\GoodsTypesController', ['only' => ['index', 'store', 'show', 'update', 'destory']]); + Route::resource('goods_types', 'Goods\GoodsTypesController', ['only' => ['index', 'store', 'show', 'update', 'destroy']]); // 商品品牌 - Route::resource('goods_brands', 'Goods\GoodsBrandsController', ['only' => ['index', 'store', 'show', 'update', 'destory']]); + Route::resource('goods_brands', 'Goods\GoodsBrandsController', ['only' => ['index', 'store', 'show', 'update', 'destroy']]); // 日志 Route::resource('logs', 'Log\LogsController', ['only' => ['index', 'show']]); // 商品 - Route::resource('goods', 'Log\LogsController', ['only' => ['index', 'show']]); + Route::resource('goods', 'Goods\GoodsController', ['only' => ['index', 'store']]); + // 商品规格 + Route::resource('goods_skus', 'Goods\GoodsSkusController', ['only' => ['index', 'show', 'update']]); // 店铺 - Route::resource('shops', 'Log\LogsController', ['only' => ['index', 'show']]); + Route::resource('shops', 'Log\LogsController', ['only' => ['index', 'store', 'show', 'update', 'destroy']]); // 角色 - Route::resource('roles', 'Role\RolesController', ['only' => ['index', 'show']]); + Route::resource('roles', 'Role\RolesController', ['only' => ['index', 'store', 'show', 'update', 'destroy']]); // 权限 - Route::resource('permissions', 'Permissions\LogsController', ['only' => ['index', 'show']]); + Route::resource('permissions', 'Permissions\LogsController', ['only' => ['index', 'store', 'show', 'update', 'destroy']]); }); Route::post('/auth/login', [LoginController::class, 'login'])->name('auth.login');