From 957d639dec6256058ba24c5df78fd66e0b0d673d Mon Sep 17 00:00:00 2001 From: jsasg <735273025@qq.com> Date: Wed, 12 Feb 2025 14:33:39 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E4=BA=A7=E5=93=81?= =?UTF-8?q?=E5=B1=9E=E6=80=A7=E5=A2=9E/=E5=88=A0/=E6=94=B9/=E6=9F=A5?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/admin/controller/v1/ProductAttr.php | 191 ++++++++++++++++++ app/admin/model/v1/ProductAttrModel.php | 39 ++++ app/admin/model/v1/ProductAttrPropModel.php | 19 ++ app/admin/route/v1.php | 18 ++ app/admin/validate/v1/ProductAttrValidate.php | 43 ++++ app/common/model/ProductAttrPropBaseModel.php | 3 +- ...0241219054243_create_product_attr_prop.php | 2 + 7 files changed, 314 insertions(+), 1 deletion(-) create mode 100644 app/admin/controller/v1/ProductAttr.php create mode 100644 app/admin/model/v1/ProductAttrModel.php create mode 100644 app/admin/model/v1/ProductAttrPropModel.php create mode 100644 app/admin/validate/v1/ProductAttrValidate.php diff --git a/app/admin/controller/v1/ProductAttr.php b/app/admin/controller/v1/ProductAttr.php new file mode 100644 index 00000000..9f4b448b --- /dev/null +++ b/app/admin/controller/v1/ProductAttr.php @@ -0,0 +1,191 @@ +param([ + 'keywords' => '', + 'page/d' => 1, + 'size/d' => 10 + ]); + $attrs = ProductAttrModel::withoutField([ + 'language_id', + 'created_at', + 'updated_at', + 'deleted_at', + ]) + ->with(['props' => function ($query) { + $query->withoutField(['created_at'])->hidden(['attr_id']); + }]) + ->language(request()->lang_id) + ->withSearch(['attr_name_nullable'], [ + 'attr_name_nullable' => $params['keywords']??null, + ]) + ->paginate([ + 'list_rows' => $params['size'], + 'page' => $params['page'], + ]); + + return success("获取成功", $attrs); + } + + /** + * 属性详情 + */ + public function read() + { + $id = request()->param('id'); + + $attr = ProductAttrModel::withoutField([ + 'language_id', + 'created_at', + 'updated_at', + 'deleted_at', + ]) + ->with(['props' => function ($query) { + $query->withoutField(['created_at'])->hidden(['attr_id']); + }]) + ->find($id); + if (empty($attr)) { + return error("获取失败"); + } + + return success("获取成功", $attr); + } + + /** + * 属性新增 + */ + public function save() + { + $post = request()->post([ + 'attr_name' => '', + 'is_system' => 0, + ]); + $attr = array_merge($post, ['language_id' => request()->lang_id]); + + // $[*].prop_type + // $[*].prop_name + // $[*].prop_value + $props = json_decode(request()->post('props/s', ''), true); + + // 检验参数 + $validate = new ProductAttrValidate; + $check_data = array_merge($attr, ['props' => $props]); + if (!$validate->check($check_data)) { + return error($validate->getError()); + } + + ProductAttrModel::startTrans(); + try { + // 添加属性 + $attr_ret = ProductAttrModel::create($attr); + if ($attr_ret->isEmpty()) { + throw new \Exception("操作失败"); + } + + // 添加属性特征 + foreach ($props as &$prop) { + $prop['attr_id'] = $attr_ret->id; + } + unset($prop); + $props_ret = (new ProductAttrPropModel)->saveAll($props); + if ($props_ret->isEmpty()) { + throw new \Exception("操作失败"); + } + + ProductAttrModel::commit(); + } catch (\Throwable $th) { + ProductAttrModel::rollback(); + return error($th->getMessage()); + } + + return success("操作成功"); + } + + /** + * 属性更新 + */ + public function update() + { + $id = request()->param('id'); + $put = request()->put([ + 'attr_name' => '', + 'is_system' => 0, + ]); + $attr = array_merge($put, ['language_id' => request()->lang_id]); + + // $[*].prop_type + // $[*].prop_name + // $[*].prop_value + $props = json_decode(request()->post('props/s', ''), true); + + // 检验参数 + $validate = new ProductAttrValidate; + $check_data = array_merge($attr, ['id' => $id], ['props' => $props]); + if (!$validate->check($check_data)) { + return error($validate->getError()); + } + + ProductAttrModel::startTrans(); + try { + // 更新属性 + $attr_ret = ProductAttrModel::bypk($id)->find(); + if ($attr_ret->isEmpty()) { + throw new \Exception("请确认操作对象是否正确"); + } + if (!$attr_ret->save($attr)) { + throw new \Exception("操作失败"); + } + + // 删除旧属性特征 + ProductAttrPropModel::attrId($attr_ret->id)->delete(); + // 保存新属性特征 + foreach ($props as &$prop) { + $prop['attr_id'] = $attr_ret->id; + } + unset($prop); + $props_ret = (new ProductAttrPropModel)->saveAll($props); + if ($props_ret->isEmpty()) { + throw new \Exception("操作失败"); + } + + ProductAttrModel::commit(); + } catch (\Throwable $th) { + ProductAttrModel::rollback(); + return error($th->getMessage()); + } + + return success("操作成功"); + } + + /** + * 属性删除 + */ + public function delete() + { + $id = request()->param('id'); + + // 删除属性 + $attr = ProductAttrModel::bypk($id)->find(); + if (empty($attr)) { + return error("请确认操作对象是否正确"); + } + if (!$attr->delete()) { + return error("操作失败"); + } + + return success("操作成功"); + } +} diff --git a/app/admin/model/v1/ProductAttrModel.php b/app/admin/model/v1/ProductAttrModel.php new file mode 100644 index 00000000..17b4b107 --- /dev/null +++ b/app/admin/model/v1/ProductAttrModel.php @@ -0,0 +1,39 @@ +hasMany(ProductAttrPropModel::class, 'attr_id', 'id'); + } + + // 所属语言查询 + public function scopeLanguage($query, $value) + { + $query->where('language_id', '=', $value); + } + + // 属性名称模糊搜索 + public function searchAttrNameNullableAttr($query, $value, $data) + { + $query->where('attr_name', 'like', "%{$value}%"); + } +} diff --git a/app/admin/model/v1/ProductAttrPropModel.php b/app/admin/model/v1/ProductAttrPropModel.php new file mode 100644 index 00000000..250e4964 --- /dev/null +++ b/app/admin/model/v1/ProductAttrPropModel.php @@ -0,0 +1,19 @@ +where('attr_id', '=', $value); + } +} diff --git a/app/admin/route/v1.php b/app/admin/route/v1.php index 3eb4a912..9a38c052 100644 --- a/app/admin/route/v1.php +++ b/app/admin/route/v1.php @@ -136,6 +136,24 @@ Route::group('v1', function () { Route::delete('delete/:id', 'ProductCategory/delete'); }); + // 产品属性 + Route::group('attr', function () { + // 属性列表 + Route::get('index', 'ProductAttr/index'); + + // 属性详情 + Route::get('read/:id', 'ProductAttr/read'); + + // 属性新增 + Route::post('save', 'ProductAttr/save'); + + // 属性更新 + Route::put('update/:id', 'ProductAttr/update'); + + // 属性删除 + Route::delete('delete/:id', 'ProductAttr/delete'); + }); + // 产品分页列表 Route::get('index', 'Product/index'); diff --git a/app/admin/validate/v1/ProductAttrValidate.php b/app/admin/validate/v1/ProductAttrValidate.php new file mode 100644 index 00000000..7d0ad233 --- /dev/null +++ b/app/admin/validate/v1/ProductAttrValidate.php @@ -0,0 +1,43 @@ + ['规则1','规则2'...] + * + * @var array + */ + protected $rule = [ + 'language_id' => 'require|number', + 'attr_name' => 'require|max:64', + 'is_system' => 'in:0,1', + 'props.*.prop_type' => 'in:1,2', + 'props.*.prop_name' => 'require|max:64', + 'props.*.prop_value' => 'require|max:64', + ]; + + /** + * 定义错误信息 + * 格式:'字段名.规则名' => '错误信息' + * + * @var array + */ + protected $message = [ + 'language_id.require' => '语言ID不能为空', + 'language_id.number' => '语言ID必须是数字', + 'attr_name.require' => '属性名称不能为空', + 'attr_name.max' => '属性名称不能超过64个字符', + 'is_system.in' => '是否系统属性只能是0或1', + 'props.*.prop_type.in' => '属性特征类型只能是1或2', + 'props.*.prop_name.require' => '属性特征名称不能为空', + 'props.*.prop_name.max' => '属性特征名称不能超过64个字符', + 'props.*.prop_value.require' => '属性特征值不能为空', + 'props.*.prop_value.max' => '属性特征值不能超过64个字符', + ]; +} diff --git a/app/common/model/ProductAttrPropBaseModel.php b/app/common/model/ProductAttrPropBaseModel.php index 552d0a2b..9d7d67c0 100644 --- a/app/common/model/ProductAttrPropBaseModel.php +++ b/app/common/model/ProductAttrPropBaseModel.php @@ -17,7 +17,8 @@ class ProductAttrPropBaseModel extends BaseModel // 字段信息 protected $schema = [ - 'id' => 'int', + 'attr_id' => 'int', + 'prop_type' => 'int', 'prop_name' => 'string', 'prop_value' => 'string', 'created_at' => 'datetime', diff --git a/database/migrations/20241219054243_create_product_attr_prop.php b/database/migrations/20241219054243_create_product_attr_prop.php index e730b8fe..9c60acc9 100644 --- a/database/migrations/20241219054243_create_product_attr_prop.php +++ b/database/migrations/20241219054243_create_product_attr_prop.php @@ -1,5 +1,6 @@ table('product_attr_prop', ['id' => false, 'engine' => 'InnoDB', 'comment' => '商品属性特征表']); $table->addColumn('attr_id', 'integer', ['signed' => false, 'null' => false, 'comment' => '属性ID']) + ->addColumn('prop_type', MysqlAdapter::INT_TINY, ['limit' => 3, 'null' => false, 'default' => 2, 'comment' => '类型:1为选项类型,2为文本输入类型']) ->addColumn('prop_name', 'string', ['limit' => 64, 'null' => false, 'comment' => '特征名']) ->addColumn('prop_value', 'string', ['limit' => 64, 'null' => false, 'comment' => '特征值']) ->addColumn('created_at', 'timestamp', ['null' => false, 'default' => 'CURRENT_TIMESTAMP', 'comment' => '新增时间'])