作者 赵彬吉

product

@@ -113,7 +113,7 @@ class Handler extends ExceptionHandler @@ -113,7 +113,7 @@ class Handler extends ExceptionHandler
113 $code = $exception->getCode(); 113 $code = $exception->getCode();
114 } elseif ($exception instanceof ValidationException) { 114 } elseif ($exception instanceof ValidationException) {
115 $code = Code::USER_PARAMS_ERROE(); 115 $code = Code::USER_PARAMS_ERROE();
116 - $message = Arr::first(Arr::first($exception->errors())); 116 + $message = $code->description = Arr::first(Arr::first($exception->errors()));
117 } elseif ($exception instanceof NotFoundHttpException || $exception instanceof MethodNotAllowedHttpException) { 117 } elseif ($exception instanceof NotFoundHttpException || $exception instanceof MethodNotAllowedHttpException) {
118 return response('404 Not Found', 404); 118 return response('404 Not Found', 404);
119 } else { 119 } else {
@@ -125,4 +125,64 @@ class Arr extends \Illuminate\Support\Arr @@ -125,4 +125,64 @@ class Arr extends \Illuminate\Support\Arr
125 125
126 return $signle ? $rows[0] : $rows; 126 return $signle ? $rows[0] : $rows;
127 } 127 }
  128 +
  129 +
  130 + /**
  131 + * 数组转字符串
  132 + * @param $arr
  133 + * @return string
  134 + * @author zbj
  135 + * @date 2023/4/17
  136 + */
  137 + public static function a2s($arr): string
  138 + {
  139 + return json_encode($arr, JSON_UNESCAPED_UNICODE);
  140 + }
  141 +
  142 +
  143 + /**
  144 + * 字符串转数组
  145 + * @param $str
  146 + * @return array|mixed
  147 + * @author zbj
  148 + * @date 2023/4/17
  149 + */
  150 + public static function s2a($str)
  151 + {
  152 + if (is_array($str)) {
  153 + return $str;
  154 + }
  155 + return is_object($str) ? (array)$str : json_decode($str, true);
  156 + }
  157 +
  158 +
  159 + /**
  160 + * 数组转set形式字符串
  161 + * @param $arr
  162 + * @param string $format
  163 + * @return string
  164 + * @author zbj
  165 + * @date 2023/4/17
  166 + */
  167 + public static function arrToSet($arr, string $format = 'intval'): string
  168 + {
  169 + $arr = array_unique(array_filter(Arr::splitFilterToArray($arr, $format, ',')));
  170 + return $arr ? implode(',', $arr) : '';
  171 + }
  172 +
  173 + /**
  174 + * set形式字符串转数组
  175 + * @param $str
  176 + * @param string $format
  177 + * @return array
  178 + * @author zbj
  179 + * @date 2023/4/17
  180 + */
  181 + public static function setToArr($str, string $format = 'intval')
  182 + {
  183 + if (is_string($str)) {
  184 + return Arr::splitFilterToArray($str, $format, ',');
  185 + }
  186 + return $str ?: [];
  187 + }
128 } 188 }
@@ -2,6 +2,8 @@ @@ -2,6 +2,8 @@
2 2
3 namespace App\Http\Controllers\Bside\Product; 3 namespace App\Http\Controllers\Bside\Product;
4 4
  5 +use App\Enums\Common\Code;
  6 +use App\Exceptions\BsideGlobalException;
5 use App\Helper\Arr; 7 use App\Helper\Arr;
6 use App\Http\Controllers\Bside\BaseController; 8 use App\Http\Controllers\Bside\BaseController;
7 use App\Http\Logic\Bside\Product\ProductLogic; 9 use App\Http\Logic\Bside\Product\ProductLogic;
@@ -13,7 +15,7 @@ use Illuminate\Http\Request; @@ -13,7 +15,7 @@ use Illuminate\Http\Request;
13 * Class ProductController 15 * Class ProductController
14 * @package App\Http\Controllers\Bside 16 * @package App\Http\Controllers\Bside
15 * @author zbj 17 * @author zbj
16 - * @date 2023/4/12 18 + * @date 2023/4/17
17 */ 19 */
18 class ProductController extends BaseController 20 class ProductController extends BaseController
19 { 21 {
@@ -24,9 +26,12 @@ class ProductController extends BaseController @@ -24,9 +26,12 @@ class ProductController extends BaseController
24 if(!empty($this->param['search'])){ 26 if(!empty($this->param['search'])){
25 $map[] = ['title', 'like', "%{$this->param['search']}%"]; 27 $map[] = ['title', 'like', "%{$this->param['search']}%"];
26 } 28 }
  29 + if(!empty($this->param['created_at'])){
  30 + $map[] = ['created_at', 'between', $this->param['created_at']];
  31 + }
27 $sort = ['id' => 'desc']; 32 $sort = ['id' => 'desc'];
28 - $data = $logic->getList($map, $sort, ['id', 'pid', 'title', 'image', 'keywords', 'describe', 'status','created_at'],0);  
29 - return $this->success(Arr::listToTree($data)); 33 + $data = $logic->getList($map, $sort, ['id', 'title', 'thumb', 'category_id', 'keywords', 'status', 'created_at', 'updated_at']);
  34 + return $this->success($data);
30 } 35 }
31 36
32 public function info(Request $request, ProductLogic $logic){ 37 public function info(Request $request, ProductLogic $logic){
@@ -36,11 +41,47 @@ class ProductController extends BaseController @@ -36,11 +41,47 @@ class ProductController extends BaseController
36 'id.required' => 'ID不能为空' 41 'id.required' => 'ID不能为空'
37 ]); 42 ]);
38 $data = $logic->getInfo($this->param['id']); 43 $data = $logic->getInfo($this->param['id']);
39 - return $this->success(Arr::twoKeepKeys($data, ['id', 'pid', 'title', 'image', 'keywords', 'describe', 'status'])); 44 + return $this->success(Arr::twoKeepKeys($data, ['id', 'title', 'gallery', 'attrs', 'category_id', 'keywords', 'intro', 'content',
  45 + 'describe', 'seo_mate', 'related_product_id', 'status']));
40 } 46 }
41 47
42 public function save(ProductRequest $request, ProductLogic $logic) 48 public function save(ProductRequest $request, ProductLogic $logic)
43 { 49 {
  50 + //验证图片集格式
  51 + foreach ($this->param['gallery'] as $v){
  52 + if(empty($v['url'])){
  53 + throw new BsideGlobalException(Code::SYSTEM_ERROR, '图片链接不能为空');
  54 + }
  55 + }
  56 + //验证产品参数
  57 + foreach ($this->param['attrs'] as $v){
  58 + if(empty($v['key'])){
  59 + throw new BsideGlobalException(Code::SYSTEM_ERROR, '产品属性名不能为空');
  60 + }
  61 + if(empty($v['value'])){
  62 + throw new BsideGlobalException(Code::SYSTEM_ERROR, '产品属性值不能为空');
  63 + }
  64 + }
  65 + //验证seo_mate参数
  66 + if(empty($this->param['seo_mate']['title'])){
  67 + throw new BsideGlobalException(Code::SYSTEM_ERROR, 'SEO标题不能为空');
  68 + }
  69 + if(empty($this->param['seo_mate']['description'])){
  70 + throw new BsideGlobalException(Code::SYSTEM_ERROR, 'SEO描述不能为空');
  71 + }
  72 + if(empty($this->param['seo_mate']['keyword'])){
  73 + throw new BsideGlobalException(Code::SYSTEM_ERROR, 'SEO关键词不能为空');
  74 + }
  75 +
  76 + //关联产品 最多16个
  77 + $this->param['related_product_id'] = array_filter(Arr::splitFilterToArray($this->param['related_product_id']), 'intval');
  78 + if(count($this->param['related_product_id']) > 16){
  79 + throw new BsideGlobalException(Code::SYSTEM_ERROR, '关联产品不能超过16个');
  80 + }
  81 +
  82 + //封面取第一个图片
  83 + $this->param['thumb'] = $this->param['gallery'][0] ?? '';
  84 +
44 $data = $logic->save($this->param); 85 $data = $logic->save($this->param);
45 return $this->success($data); 86 return $this->success($data);
46 } 87 }
@@ -57,5 +98,4 @@ class ProductController extends BaseController @@ -57,5 +98,4 @@ class ProductController extends BaseController
57 return $this->success($data); 98 return $this->success($data);
58 } 99 }
59 100
60 - //todo Ai生成 关键词和描述  
61 } 101 }
@@ -62,17 +62,4 @@ class AttrLogic extends BaseLogic @@ -62,17 +62,4 @@ class AttrLogic extends BaseLogic
62 } 62 }
63 return $this->success(); 63 return $this->success();
64 } 64 }
65 -  
66 - public function delete($ids){  
67 - $ids= array_filter(Arr::splitFilterToArray($ids), 'intval');  
68 - foreach ($ids as $id){  
69 - $info = $this->getCacheInfo($id);  
70 - if(!$info){  
71 - continue;  
72 - }  
73 -  
74 - //todo 是否有关联商品  
75 - }  
76 - return parent::delete($ids);  
77 - }  
78 } 65 }
@@ -20,18 +20,4 @@ class DescribeLogic extends BaseLogic @@ -20,18 +20,4 @@ class DescribeLogic extends BaseLogic
20 20
21 $this->model = new Describe(); 21 $this->model = new Describe();
22 } 22 }
23 -  
24 - public function delete($ids){  
25 - $ids= array_filter(Arr::splitFilterToArray($ids), 'intval');  
26 - foreach ($ids as $id){  
27 - $info = $this->getCacheInfo($id);  
28 - if(!$info){  
29 - continue;  
30 - }  
31 -  
32 - //todo 是否有关联商品  
33 -  
34 - }  
35 - return parent::delete($ids);  
36 - }  
37 } 23 }
@@ -28,7 +28,7 @@ class KeywordLogic extends BaseLogic @@ -28,7 +28,7 @@ class KeywordLogic extends BaseLogic
28 try { 28 try {
29 $res = parent::save($param); 29 $res = parent::save($param);
30 //路由映射 30 //路由映射
31 - RouteMap::setRoute($param['title'], RouteMap::SOURCE_PRODUCT_KEYWORD, $res['id'], $param['project_id'], true); 31 + RouteMap::setRoute($param['title'], RouteMap::SOURCE_PRODUCT_KEYWORD, $res['id'], $param['project_id']);
32 DB::commit(); 32 DB::commit();
33 }catch (\Exception $e){ 33 }catch (\Exception $e){
34 DB::rollBack(); 34 DB::rollBack();
@@ -2,9 +2,10 @@ @@ -2,9 +2,10 @@
2 2
3 namespace App\Http\Logic\Bside\Product; 3 namespace App\Http\Logic\Bside\Product;
4 4
5 -use App\Helper\Arr;  
6 use App\Http\Logic\Bside\BaseLogic; 5 use App\Http\Logic\Bside\BaseLogic;
7 use App\Models\Product\Product; 6 use App\Models\Product\Product;
  7 +use App\Models\RouteMap;
  8 +use Illuminate\Support\Facades\DB;
8 9
9 /** 10 /**
10 * Class ProductLogic 11 * Class ProductLogic
@@ -22,32 +23,18 @@ class ProductLogic extends BaseLogic @@ -22,32 +23,18 @@ class ProductLogic extends BaseLogic
22 } 23 }
23 24
24 public function save($param){ 25 public function save($param){
25 - if(!empty($param['pid'])){  
26 - if(!empty($param['id']) && $param['pid'] == $param['id']){  
27 - $this->fail('上级分类不能是本分类');  
28 - }  
29 - $p_cate = Product::find($param['pid']);  
30 - if(!$p_cate){  
31 - $this->fail('上级分类不存在');  
32 - } 26 + DB::beginTransaction();
  27 + try {
  28 + $data = $param;
  29 + unset($data['route']);
  30 + $res = parent::save($data);
  31 + //路由映射
  32 + RouteMap::setRoute($param['route'], RouteMap::SOURCE_PRODUCT, $res['id'], $param['project_id']);
  33 + DB::commit();
  34 + }catch (\Exception $e){
  35 + DB::rollBack();
  36 + $this->fail('保存失败');
33 } 37 }
34 - return parent::save($param);  
35 - }  
36 -  
37 - public function delete($ids){  
38 - $ids= array_filter(Arr::splitFilterToArray($ids), 'intval');  
39 - foreach ($ids as $id){  
40 - $info = $this->getCacheInfo($id);  
41 - if(!$info){  
42 - continue;  
43 - }  
44 - //是否有子分类  
45 - if(Product::where('pid', $id)->count()){  
46 - $this->fail("分类{$info['title']}存在子分类,不能删除");  
47 - }  
48 - //todo 是否有对应商品  
49 -  
50 - }  
51 - return parent::delete($ids); 38 + return $this->success();
52 } 39 }
53 } 40 }
@@ -2,7 +2,9 @@ @@ -2,7 +2,9 @@
2 2
3 namespace App\Http\Requests\Bside\product; 3 namespace App\Http\Requests\Bside\product;
4 4
  5 +use App\Models\Product\Product;
5 use Illuminate\Foundation\Http\FormRequest; 6 use Illuminate\Foundation\Http\FormRequest;
  7 +use Illuminate\Validation\Rule;
6 8
7 /** 9 /**
8 * Class ProductRequest 10 * Class ProductRequest
@@ -30,23 +32,44 @@ class ProductRequest extends FormRequest @@ -30,23 +32,44 @@ class ProductRequest extends FormRequest
30 public function rules() 32 public function rules()
31 { 33 {
32 return [ 34 return [
33 - 'title'=>'required|max:20',  
34 - 'image'=>'required',  
35 - 'keywords'=>'required|max:50',  
36 - 'describe'=>'required|max:200', 35 + 'title'=>'required|max:200',
  36 + 'route'=>'required|max:100',
  37 + 'gallery'=>'required|array',
  38 + 'attrs'=>'required|array',
  39 + 'category_id'=>'required',
  40 + 'keywords'=>'required',
  41 + 'intro'=>'required|max:500',
  42 + 'content'=>'required',
  43 + 'describe'=>'required|array',
  44 + 'seo_mate'=>'required|array',
  45 + 'related_product_id'=>'required',
  46 + 'status'=>['required', Rule::in(array_keys(Product::statusMap()))],
37 ]; 47 ];
38 } 48 }
39 49
40 public function messages() 50 public function messages()
41 { 51 {
42 return [ 52 return [
43 - 'title.required' => '请输入分类名称',  
44 - 'title.max' => '分类名称不能超过20个字符',  
45 - 'image.required' => '请上传分类图片',  
46 - 'keywords.required' => '请输入分类关键词',  
47 - 'keywords.max' => '分类关键词不能超过50个字符',  
48 - 'describe.required' => '请输入分类描述',  
49 - 'describe.max' => '分类描述不能超过200个字符', 53 + 'title.required' => '请输入产品标题',
  54 + 'title.max' => '产品标题不能超过20个字符',
  55 + 'route.required' => '请输入产品链接',
  56 + 'route.max' => '产品链接不能超过100个字符',
  57 + 'gallery.required' => '请上传产品图片',
  58 + 'gallery.array' => '产品图片格式异常',
  59 + 'attrs.required' => '请添加产品参数',
  60 + 'attrs.array' => '产品参数格式异常',
  61 + 'category_id.required' => '请选择分类',
  62 + 'keywords.required' => '请添加关键词标签',
  63 + 'intro.required' => '请输入短描述',
  64 + 'intro.max' => '短描述不能超过20个字符',
  65 + 'content.required' => '请输入产品描述',
  66 + 'describe.required' => '请添加描述切换栏',
  67 + 'describe.array' => '描述切换栏格式异常',
  68 + 'seo_mate.required' => '请输入SEO',
  69 + 'seo_mate.array' => 'SEO格式异常',
  70 + 'related_product_id.required' => '请选择相关产品',
  71 + 'status.required' => '请选择产品状态',
  72 + 'status.in' => '产品状态值异常',
50 ]; 73 ];
51 } 74 }
52 75
@@ -2,6 +2,7 @@ @@ -2,6 +2,7 @@
2 2
3 namespace App\Models\Product; 3 namespace App\Models\Product;
4 4
  5 +use App\Helper\Arr;
5 use App\Models\Base; 6 use App\Models\Base;
6 use Illuminate\Database\Eloquent\SoftDeletes; 7 use Illuminate\Database\Eloquent\SoftDeletes;
7 8
@@ -12,4 +13,81 @@ class Product extends Base @@ -12,4 +13,81 @@ class Product extends Base
12 //设置关联表名 13 //设置关联表名
13 protected $table = 'gl_product'; 14 protected $table = 'gl_product';
14 15
  16 + const STATUS_DRAFT = 0;
  17 + const STATUS_ON = 1;
  18 + const STATUS_OFF = 2;
  19 +
  20 +
  21 + public static function statusMap(){
  22 + return [
  23 + self::STATUS_DRAFT => '草稿',
  24 + self::STATUS_ON => '已上架',
  25 + self::STATUS_OFF => '未上架',
  26 + ];
  27 + }
  28 +
  29 + public function setThumbAttribute($value){
  30 + $this->attributes['thumb'] = Arr::a2s($value);
  31 + }
  32 +
  33 + public function getThumbAttribute($value){
  34 + return Arr::s2a($value);
  35 + }
  36 +
  37 + public function setGalleryAttribute($value){
  38 + $this->attributes['gallery'] = Arr::a2s($value);
  39 + }
  40 +
  41 + public function getGalleryAttribute($value){
  42 + return Arr::s2a($value);
  43 + }
  44 +
  45 + public function setAttrsAttribute($value){
  46 + $this->attributes['attrs'] = Arr::a2s($value);
  47 + }
  48 +
  49 + public function getAttrsAttribute($value){
  50 + return Arr::s2a($value);
  51 + }
  52 +
  53 + public function setDescribeAttribute($value){
  54 + $this->attributes['describe'] = Arr::a2s($value);
  55 + }
  56 +
  57 + public function getDescribeAttribute($value){
  58 + return Arr::s2a($value);
  59 + }
  60 +
  61 + public function setSeoMateAttribute($value){
  62 + $this->attributes['seo_mate'] = Arr::a2s($value);
  63 + }
  64 +
  65 + public function getSeoMateAttribute($value){
  66 + return Arr::s2a($value);
  67 + }
  68 +
  69 + public function setCategoryIdAttribute($value){
  70 + $this->attributes['category_id'] = Arr::arrToSet($value);
  71 + }
  72 +
  73 + public function getCategoryIdAttribute($value){
  74 + return Arr::setToArr($value);
  75 + }
  76 +
  77 + public function setKeywordsAttribute($value){
  78 + $this->attributes['keywords'] = Arr::arrToSet($value, 'trim');
  79 + }
  80 +
  81 + public function getKeywordsAttribute($value){
  82 + return Arr::setToArr($value, 'trim');
  83 + }
  84 +
  85 + public function setRelatedProductIdAttribute($value){
  86 + $this->attributes['related_product_id'] = Arr::arrToSet($value);
  87 + }
  88 +
  89 + public function getRelatedProductIdAttribute($value){
  90 + return Arr::setToArr($value);
  91 + }
  92 +
15 } 93 }
@@ -74,18 +74,13 @@ class RouteMap extends Model @@ -74,18 +74,13 @@ class RouteMap extends Model
74 * @param $source 74 * @param $source
75 * @param $source_id 75 * @param $source_id
76 * @param int $project_id 76 * @param int $project_id
77 - * @param bool $auto  
78 * @return bool 77 * @return bool
79 * @throws \Exception 78 * @throws \Exception
80 * @author zbj 79 * @author zbj
81 * @date 2023/4/17 80 * @date 2023/4/17
82 */ 81 */
83 - public static function setRoute($title, $source, $source_id, $project_id = 0, $auto=false){  
84 - $route = $title;  
85 -  
86 - if($auto){  
87 - $route = self::generateRoute($title, $source, $source_id, $project_id);  
88 - } 82 + public static function setRoute($title, $source, $source_id, $project_id = 0){
  83 + $route = self::generateRoute($title, $source, $source_id, $project_id);
89 if(!$route){ 84 if(!$route){
90 throw new \Exception('路由不能为空'); 85 throw new \Exception('路由不能为空');
91 } 86 }
@@ -2,6 +2,7 @@ @@ -2,6 +2,7 @@
2 namespace App\Utils; 2 namespace App\Utils;
3 3
4 use App\Enums\Common\Common; 4 use App\Enums\Common\Common;
  5 +use App\Helper\Arr;
5 use Illuminate\Support\Facades\Log; 6 use Illuminate\Support\Facades\Log;
6 use Illuminate\Support\Facades\Route; 7 use Illuminate\Support\Facades\Route;
7 8
@@ -17,7 +18,12 @@ class LogUtils @@ -17,7 +18,12 @@ class LogUtils
17 public static function error($title, $params = [], $response = []) 18 public static function error($title, $params = [], $response = [])
18 { 19 {
19 $route=Route::current(); 20 $route=Route::current();
20 - $side=$route->action['prefix']??Common::A; 21 + if(!empty($route->action['prefix'])){
  22 + $prefix = Arr::splitFilterToArray($route->action['prefix'], 'trim', '/');
  23 + $side = $prefix[0];
  24 + }else{
  25 + $side = Common::A;
  26 + }
21 $params = is_array($params) ? json_encode($params, JSON_UNESCAPED_UNICODE) : $params; 27 $params = is_array($params) ? json_encode($params, JSON_UNESCAPED_UNICODE) : $params;
22 $response = is_array($response) || is_object($response) ? json_encode($response, JSON_UNESCAPED_UNICODE) : $response; 28 $response = is_array($response) || is_object($response) ? json_encode($response, JSON_UNESCAPED_UNICODE) : $response;
23 Log::channel($side.'side')->error("$title::请求参数:$params--------响应:$response"); 29 Log::channel($side.'side')->error("$title::请求参数:$params--------响应:$response");
@@ -33,7 +39,12 @@ class LogUtils @@ -33,7 +39,12 @@ class LogUtils
33 public static function info($title, $params = [], $response = []) 39 public static function info($title, $params = [], $response = [])
34 { 40 {
35 $route=Route::current(); 41 $route=Route::current();
36 - $side=$route->action['prefix']??Common::A; 42 + if(!empty($route->action['prefix'])){
  43 + $prefix = Arr::splitFilterToArray($route->action['prefix'], 'trim', '/');
  44 + $side = $prefix[0];
  45 + }else{
  46 + $side = Common::A;
  47 + }
37 $params = is_array($params) ? json_encode($params, JSON_UNESCAPED_UNICODE) : $params; 48 $params = is_array($params) ? json_encode($params, JSON_UNESCAPED_UNICODE) : $params;
38 $response = is_array($response) || is_object($response) ? json_encode($response, JSON_UNESCAPED_UNICODE) : $response; 49 $response = is_array($response) || is_object($response) ? json_encode($response, JSON_UNESCAPED_UNICODE) : $response;
39 Log::channel($side.'side')->info("$title::请求参数:$params--------响应:$response"); 50 Log::channel($side.'side')->info("$title::请求参数:$params--------响应:$response");