From a1c7ae62ba7d00f06e7b2b223eb29da40716b523 Mon Sep 17 00:00:00 2001 From: jsasg <735273025@qq.com> Date: Fri, 27 Mar 2026 14:03:14 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BA=A7=E5=93=81=E5=88=86=E7=B1=BB?= =?UTF-8?q?=E6=8E=A8=E8=8D=90=E6=95=B0=E6=8D=AE=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 + .../v1/ProductCategoryRecommend.php | 200 ++++++++++++++++++ .../v1/ProductCategoryRecommendModel.php | 47 ++++ app/admin/route/v1.php | 22 +- .../v1/ProductCategoryRecommendValidate.php | 63 ++++++ app/admin/validate/v1/ProductValidate.php | 2 - .../ProductCategoryRecommendBaseModel.php | 33 +++ ...3640_create_product_category_recommend.php | 50 +++++ 8 files changed, 416 insertions(+), 4 deletions(-) create mode 100644 app/admin/controller/v1/ProductCategoryRecommend.php create mode 100644 app/admin/model/v1/ProductCategoryRecommendModel.php create mode 100644 app/admin/validate/v1/ProductCategoryRecommendValidate.php create mode 100644 app/common/model/ProductCategoryRecommendBaseModel.php create mode 100644 database/migrations/20260326073640_create_product_category_recommend.php diff --git a/.gitignore b/.gitignore index 8c9279ce..1ee66503 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,6 @@ public/.well-known /.settings /.buildpath /.project + +CLAUDE.md +/skills diff --git a/app/admin/controller/v1/ProductCategoryRecommend.php b/app/admin/controller/v1/ProductCategoryRecommend.php new file mode 100644 index 00000000..2eae9dd0 --- /dev/null +++ b/app/admin/controller/v1/ProductCategoryRecommend.php @@ -0,0 +1,200 @@ +server(); + $image_host = $server['REQUEST_SCHEME'] . "://" . $server['SERVER_NAME'] . '/'; + $param = request()->get([ + 'keywords', + 'page/d' => 1, + 'size/d' => 10 + ]); + + // 查询数据 + $data = ProductCategoryRecommendModel::with(['category' => function($query) { + $query->field(['id', 'name']); + }]) + ->withoutField(['language_id', 'updated_at', 'deleted_at']) + ->withSearch(['keywords'], ['keywords' => $param['keywords']??null]) + ->language(request()->lang_id) + ->order(['sort' => 'asc', 'id' => 'desc']) + ->paginate([ + 'list_rows' => $param['size'], + 'page' => $param['page'], + ]) + ->bindAttr('category', ['category_name' => 'name']) + ->hidden(['category']) + ?->each(function($item) use($image_host) { + // 拼接完整图片URL + if (!empty($item['image'])) { + $item['image'] = url_join($image_host, $item['image']); + } + }); + + return success('获取成功', $data); + } + + /** + * 导出Excel + */ + public function export() + { + $schema = [ + 'id' => 'ID', + 'image' => '图片', + 'category_name' => '分类名称', + 'desc' => '产品介绍', + 'link' => '链接地址', + 'sort' => '排序', + 'disabled' => '状态', + 'created_at' => '添加时间' + ]; + + // 获取导出数据 + $data = $this->getProductCategoryRecommendData(); + + // 导出 + return xlsx_writer($data, $schema, '产品列表' . date('YmdHis')); + } + // 获取要导出的推荐记录数据 + private function getProductCategoryRecommendData() + { + $server = request()->server(); + $image_host = $server['REQUEST_SCHEME'] . "://" . $server['SERVER_NAME'] . '/'; + $param = request()->get(['keywords']); + + // 查询数据 + return ProductCategoryRecommendModel::with(['category' => function($query) { + $query->field(['id', 'name']); + }]) + ->withoutField(['language_id', 'updated_at', 'deleted_at']) + ->withSearch(['keywords'], ['keywords' => $param['keywords']??null]) + ->language(request()->lang_id) + ->order(['sort' => 'asc', 'id' => 'desc']) + ->select() + ->each(function($item) use($image_host) { + // 拼接完整图片URL + if (!empty($item['image'])) { + $item['image'] = url_join($image_host, $item['image']); + } + // 状态 + $item['disabled'] = $item['disabled'] == 1 ? '禁用' : '启用'; + }) + ->toArray(); + } + + /** + * 获取详细数据 + */ + public function read() + { + $id = request()->param('id/d'); + $record = ProductCategoryRecommendModel::bypk($id) + ->withoutField(['language_id', 'created_at', 'updated_at', 'deleted_at']) + ->find(); + if (empty($record)) { + return error('推荐数据不存在'); + } + + return success('success', $record); + } + + /** + * 新增数据 + */ + public function save() + { + $post = request()->post([ + 'category_id', + 'title', + 'image', + 'desc', + 'link', + 'sort', + 'disabled' + ]); + $data = array_merge($post, ['language_id' => request()->lang_id]); + + // 参数校验 + $validate = new ProductCategoryRecommendValidate(); + if (!$validate->scene('create')->check($data)) { + return error($validate->getError()); + } + + // 保存推荐数据 + $recommend = ProductCategoryRecommendModel::create($data); + if ($recommend->isEmpty()) { + return error('保存失败'); + } + return success('保存成功'); + } + + /** + * 更新数据 + */ + public function update() + { + $id = request()->param('id/d'); + $post = request()->post([ + 'category_id', + 'title', + 'image', + 'desc', + 'link', + 'sort', + 'disabled' + ]); + $data = array_merge($post, ['language_id' => request()->lang_id]); + + // 参数校验 + $validate = new ProductCategoryRecommendValidate(); + $check_data = array_merge($data, ['id' => $id]); + if (!$validate->scene('update')->check($check_data)) { + return error($validate->getError()); + } + + // 更新推荐数据 + $recommend = ProductCategoryRecommendModel::bypk($id)->find(); + if (empty($recommend)) { + return error('请确认操作对象是否存在'); + } + if (!$recommend->save($data)) { + return error('操作失败'); + } + + return success('操作成功'); + } + + /** + * 删除 + */ + public function delete() + { + $id = request()->param('id/d'); + + // 删除推荐记录数据 + $record = ProductCategoryRecommendModel::bypk($id)->find(); + if (empty($record)) { + return error('请确认操作对象是否正确'); + } + if (!$record->delete()) { + return error('操作失败'); + } + + return success('操作成功'); + } +} diff --git a/app/admin/model/v1/ProductCategoryRecommendModel.php b/app/admin/model/v1/ProductCategoryRecommendModel.php new file mode 100644 index 00000000..507681b0 --- /dev/null +++ b/app/admin/model/v1/ProductCategoryRecommendModel.php @@ -0,0 +1,47 @@ +belongsTo(\app\index\model\LanguageModel::class, 'language_id', 'id'); + } + + // 关联产品分类 + public function category() + { + return $this->belongsTo(\app\index\model\ProductCategoryModel::class, 'category_id', 'id'); + } + + // 所属语言范围查询 + public function scopeLanguage($query, $language) + { + $query->where('language_id', '=', $language); + } + + // 关键词搜索 + public function searchKeywordsAttr($query, string|null $keywords) + { + if (is_null($keywords)) { + return; + } + $query->where('title', 'like', "%{$keywords}%") + ->whereOr('desc', 'like', "%{$keywords}%"); + } +} diff --git a/app/admin/route/v1.php b/app/admin/route/v1.php index e2aa35f2..40f261ad 100644 --- a/app/admin/route/v1.php +++ b/app/admin/route/v1.php @@ -87,7 +87,7 @@ Route::group('v1', function () { // 视频分类列表 Route::get('categorys', 'VideoCategory/list'); - + // 视频分类 Route::group('category', function () { // 视频分类分页数据 @@ -311,6 +311,24 @@ Route::group('v1', function () { // 分类删除 Route::delete('delete/:id', 'ProductCategory/delete'); + + // 产品分类推荐数据 + Route::group('recommend', function () { + // 推荐数据分页列表 + Route::get('index', 'ProductCategoryRecommend/index'); + + // 推荐数据详情 + Route::get('read/:id', 'ProductCategoryRecommend/read'); + + // 推荐数据新增 + Route::post('save', 'ProductCategoryRecommend/save'); + + // 推荐数据更新 + Route::put('update/:id', 'ProductCategoryRecommend/update'); + + // 推荐数据删除 + Route::delete('delete/:id', 'ProductCategoryRecommend/delete'); + }); }); // 产品购买链接 @@ -483,7 +501,7 @@ Route::group('v1', function () { // 分页 Route::get('index', 'Navigation/index'); - + // 导航详情 Route::get('read/:id', 'Navigation/read'); diff --git a/app/admin/validate/v1/ProductCategoryRecommendValidate.php b/app/admin/validate/v1/ProductCategoryRecommendValidate.php new file mode 100644 index 00000000..6c0a947b --- /dev/null +++ b/app/admin/validate/v1/ProductCategoryRecommendValidate.php @@ -0,0 +1,63 @@ + ['规则1','规则2'...] + * + * @var array + */ + protected $rule = [ + 'language_id' => 'require|integer', + 'category_id' => 'require|integer', + 'title' => 'require|max:255', + 'image' => 'require|max:255', + 'desc' => 'require|max:255', + 'link' => 'max:500', + 'sort' => 'require|integer', + 'disabled' => 'in:0,1' + ]; + + /** + * 定义错误信息 + * 格式:'字段名.规则名' => '错误信息' + * + * @var array + */ + protected $message = [ + 'id.require' => 'ID不能为空', + 'id.integer' => 'ID必须是整数', + 'language_id.require' => '语言ID不能为空', + 'language_id.integer' => '语言ID必须是整数', + 'category_id.require' => '分类ID不能为空', + 'category_id.integer' => '分类ID必须是整数', + 'title.require' => '标题不能为空', + 'title.max' => '标题长度不能超过:rule个字符', + 'image.require' => '图片不能为空', + 'image.max' => '图片长度不能超过:rule个字符', + 'desc.require' => '描述不能为空', + 'desc.max' => '描述长度不能超过:rule个字符', + 'link.max' => '链接长度不能超过:rule个字符', + 'sort.require' => '排序不能为空', + 'sort.integer' => '排序必须是整数', + 'disabled.in' => '禁用状态只能是0或1', + ]; + + // 新增场景 + protected function sceneCreate() + { + return $this->remove('id', 'require|integer'); + } + + // 更新场景 + protected function sceneUpdate() + { + return $this->append('id', 'require|integer'); + } +} diff --git a/app/admin/validate/v1/ProductValidate.php b/app/admin/validate/v1/ProductValidate.php index 1a1f2d30..0431ba9c 100644 --- a/app/admin/validate/v1/ProductValidate.php +++ b/app/admin/validate/v1/ProductValidate.php @@ -5,8 +5,6 @@ namespace app\admin\validate\v1; use think\Validate; -use function PHPSTORM_META\type; - class ProductValidate extends Validate { /** diff --git a/app/common/model/ProductCategoryRecommendBaseModel.php b/app/common/model/ProductCategoryRecommendBaseModel.php new file mode 100644 index 00000000..13dff1ac --- /dev/null +++ b/app/common/model/ProductCategoryRecommendBaseModel.php @@ -0,0 +1,33 @@ + 'int', + 'language_id' => 'int', + 'category_id' => 'int', + 'title' => 'string', + 'image' => 'string', + 'desc' => 'string', + 'link' => 'string', + 'sort' => 'int', + 'disabled' => 'int', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + 'deleted_at' => 'datetime' + ]; +} diff --git a/database/migrations/20260326073640_create_product_category_recommend.php b/database/migrations/20260326073640_create_product_category_recommend.php new file mode 100644 index 00000000..a93bb986 --- /dev/null +++ b/database/migrations/20260326073640_create_product_category_recommend.php @@ -0,0 +1,50 @@ +table('product_category_recommend', [ + 'engine' => 'MyISAM', + 'collation' => 'utf8mb4_general_ci', + 'comment' => '产品分类推荐表' + ]); + + $table->addColumn('language_id', 'integer', ['signed' => false, 'null' => false, 'comment' => '语言ID']) + ->addColumn('category_id', 'integer', ['signed' => false, 'null' => false, 'comment' => '分类ID']) + ->addColumn('title', 'string', ['limit' => 255, 'comment' => '标题']) + ->addColumn('image', 'string', ['limit' => 255, 'default' => '', 'comment' => '图片']) + ->addColumn('desc', 'string', ['limit' => 255, 'comment' => '描述']) + ->addColumn('link', 'string', ['limit' => 500, 'default' => '', 'comment' => '外链地址']) + ->addColumn('sort', 'integer', ['default' => 0, 'comment' => '排序']) + ->addColumn('disabled', 'boolean', ['default' => 0, 'comment' => '是否禁用 0:启用 1:禁用']) + ->addColumn('created_at', 'timestamp', ["null" => false,'default' => 'CURRENT_TIMESTAMP', 'comment' => '创建时间']) + ->addColumn('updated_at', 'timestamp', ["null" => false,'default' => 'CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP', 'comment' => '更新时间']) + ->addColumn('deleted_at', 'timestamp', ['null' => true, 'comment' => '删除时间']) + ->create(); + } +}