feat: #20220728 商品基础接口

This commit is contained in:
赵世界 2022-07-28 13:46:08 +08:00
parent 554105e766
commit 6521b4e746
19 changed files with 536 additions and 23 deletions

121
app/Filters/Filters.php Normal file
View File

@ -0,0 +1,121 @@
<?php
namespace App\Filters;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Illuminate\Support\Arr;
class Filters
{
/**
* @var Request
*/
protected $request;
/**
* The Eloquent builder.
*
* @var \Illuminate\Database\Eloquent\Builder
*/
protected $builder;
/**
* Registered filters to operate upon.
*
* @var array
*/
protected $filters = [];
/**
* Create a new ThreadFilters instance.
*
* @param Request $request
*/
public function __construct()
{
$this->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;
}
}

View File

@ -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' => '',
];
}

View File

@ -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']);
}
}

View File

@ -0,0 +1,79 @@
<?php
namespace App\Http\Controllers\Goods;
use App\Http\Controllers\Controller;
use App\Http\Resources\GoodsResource;
use App\Http\Resources\GoodsSkuResource;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use App\Models\Goods;
use App\Models\GoodsSku;
class GoodsController extends Controller
{
public function index(Request $request)
{
$goods = GoodsSku::query()
->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']);
}
}

View File

@ -0,0 +1,26 @@
<?php
namespace App\Http\Controllers\Goods;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\GoodsSku;
use App\Http\Resources\GoodsSkuResource;
class GoodsSkusController extends Controller
{
public function index(Request $request)
{
return GoodsSku::query()->get(['id', 'title'])->toArray();
}
public function show($id)
{
return new GoodsSkuResource(GoodsSku::query()->with(['goods', 'brand'])->find($id));
}
public function update($id, Request $request)
{
}
}

View File

@ -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']);
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class GoodsResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return parent::toArray($request);
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class GoodsSkuResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return parent::toArray($request);
}
}

View File

@ -2,7 +2,27 @@
namespace App\Models;
use App\Models\traits\Filter;
class Goods extends Model
{
use Filter;
/**
* 多规格
*/
public function skus()
{
return $this->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');
}
}

View File

@ -4,5 +4,10 @@ namespace App\Models;
class GoodsBrand extends Model
{
/**
* 数组中的属性会被隐藏。
*
* @var array
*/
protected $hidden = ['deleted_at'];
}

View File

@ -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');
}
}

View File

@ -4,5 +4,10 @@ namespace App\Models;
class GoodsType extends Model
{
/**
* 数组中的属性会被隐藏。
*
* @var array
*/
protected $hidden = ['deleted_at'];
}

View File

@ -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);
}
}

View File

@ -0,0 +1,52 @@
<?php
namespace App\Models\traits;
trait Filter
{
/**
* 过滤
*/
public function scopeFilter($query)
{
$filters = $this->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;
}
}

View File

@ -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();
});

View File

@ -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');

View File

@ -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');

View File

@ -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');
});
}

View File

@ -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');