[ ['pid', '=', 0], ['is_home_recommended', '=', 1], ['is_enable', '=', 1], ]]); if(!empty($data)) { // 楼层左侧商品分类从配置中读取 $floor_left_top_category = MyC('home_index_floor_left_top_category'); if(!empty($floor_left_top_category)) { $floor_left_top_category = json_decode($floor_left_top_category, true); } // 楼层关键字从配置中读取 $floor_keywords = MyC('home_index_floor_top_right_keywords'); if(!empty($floor_keywords)) { $floor_keywords = json_decode($floor_keywords, true); } // 数据模式 // 0 自动模式 // 1 手动模式 // 2 拖拽模式 $floor_data_type = MyC('home_index_floor_data_type', 0, true); // 数据处理 switch($floor_data_type) { // 自动模式 case 0 : // 商品数量 $goods_count = MyC('home_index_floor_goods_max_count', 8, true); // 排序配置 $floor_order_by_type_list = MyConst('common_goods_order_by_type_list'); $floor_order_by_rule_list = MyConst('common_data_order_by_rule_list'); $floor_order_by_type = MyC('home_index_floor_goods_order_by_type', 0, true); $floor_order_by_rule = MyC('home_index_floor_goods_order_by_rule', 0, true); // 排序字段名称 $order_by_field = array_key_exists($floor_order_by_type, $floor_order_by_type_list) ? $floor_order_by_type_list[$floor_order_by_type]['value'] : $floor_order_by_type_list[0]['value']; // 排序规则 $order_by_rule = array_key_exists($floor_order_by_rule, $floor_order_by_rule_list) ? $floor_order_by_rule_list[$floor_order_by_rule]['value'] : $floor_order_by_rule_list[0]['value']; // 排序 $order_by = implode(' '.$order_by_rule.', ', explode(',', $order_by_field)).' '.$order_by_rule; break; // 手动模式 case 1 : $manual_mode = MyC('home_index_floor_manual_mode_goods'); if(!empty($manual_mode)) { $floor_manual_mode_goods = json_decode($manual_mode, true); } break; } // 首页获取数据信息钩子 $hook_name = 'plugins_service_home_floor_data_begin'; MyEventTrigger($hook_name, [ 'hook_name' => $hook_name, 'is_backend' => true, 'params' => $params, 'data' => &$data, ]); // 根据分类获取楼层商品 foreach($data as &$v) { // 数据模式 switch($floor_data_type) { // 自动模式 case 0 : if(isset($goods_count) && isset($order_by)) { // 获取分类ids $category_ids = GoodsCategoryService::GoodsCategoryItemsIds([$v['id']], 1); // 获取商品id $goods_params = [ 'where' => [ ['gci.category_id', 'in', $category_ids], ['g.is_shelves', '=', 1], ['g.is_delete_time', '=', 0], ], 'order_by' => $order_by, 'field' => 'g.id', 'n' => $goods_count, 'is_data_handle' => 0, 'is_spec' => $is_spec, 'is_cart' => $is_cart, ]; $res = self::CategoryGoodsList($goods_params); $v['goods_ids'] = empty($res) ? [] : array_column($res, 'id'); } break; // 手动模式 case 1 : if(!empty($floor_manual_mode_goods) && is_array($floor_manual_mode_goods) && array_key_exists($v['id'], $floor_manual_mode_goods)) { $v['goods_ids'] = $floor_manual_mode_goods[$v['id']]; } break; } // 商品数据、后面实时读取这里赋空值 $v['goods'] = []; // 楼层左侧分类 if(!empty($floor_left_top_category) && !empty($floor_left_top_category[$v['id']])) { $v['items'] = GoodsCategoryService::GoodsCategoryList(['where'=>[['id', 'in', explode(',', $floor_left_top_category[$v['id']])]], 'm'=>0, 'n'=>0, 'is_spec'=>0, 'is_cart'=>0]); } else { $v['items'] = []; } // 楼层关键字 $v['config_keywords'] = (empty($floor_keywords) || empty($floor_keywords[$v['id']])) ? [] : explode(',', $floor_keywords[$v['id']]); } } else { $data = []; } // 存储缓存 MyCache($key, $data, 180); } // 商品读取、商品信息需要实时读取 if(!empty($data) && is_array($data)) { // 商品id一次性读取商品 $goods_ids = []; foreach($data as $cg) { if(!empty($cg['goods_ids']) && is_array($cg['goods_ids'])) { $goods_ids = array_merge($goods_ids, $cg['goods_ids']); } } // 读取商品 $goods_list = []; if(!empty($goods_ids)) { $res = self::GoodsList([ 'where' => [ ['id', 'in', array_unique($goods_ids)], ['is_shelves', '=', 1], ], 'm' => 0, 'n' => 0, 'field' => '*', 'is_spec' => $is_spec, 'is_cart' => $is_cart, ]); $goods_list = empty($res['data']) ? [] : array_column($res['data'], null, 'id'); } // 根据分类获取楼层商品 if(!empty($goods_list)) { foreach($data as &$cv) { if(!empty($cv['goods_ids']) && is_array($cv['goods_ids'])) { $temp = []; foreach($cv['goods_ids'] as $gid) { if(array_key_exists($gid, $goods_list)) { $temp[] = $goods_list[$gid]; } } $cv['goods'] = $temp; } } } } return $data; } /** * 商品数据 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-09-07 * @desc description * @param array $where [条件] */ public static function GoodsData($goods_id, $field = '*') { // 获取商品数据 $data = Db::name('Goods')->field($field)->find($goods_id); // 商品数据钩子 $hook_name = 'plugins_service_goods_data'; MyEventTrigger($hook_name, [ 'hook_name' => $hook_name, 'is_backend' => true, 'goods_id' => $goods_id, 'data' => &$data, ]); return $data; } /** * 获取分类与商品关联总数 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-09-07 * @desc description * @param array $where [条件] */ public static function CategoryGoodsTotal($where = []) { // 商品与分类联表总数读取前钩子 $hook_name = 'plugins_service_category_goods_total_begin'; MyEventTrigger($hook_name, [ 'hook_name' => $hook_name, 'is_backend' => true, 'where' => &$where, ]); // 获取总数 return (int) Db::name('Goods')->alias('g')->join('goods_category_join gci', 'g.id=gci.goods_id')->where($where)->count('DISTINCT g.id'); } /** * 获取分类与商品关联列表 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-08-29 * @desc description * @param array $params [输入参数: where, field, is_photo] */ public static function CategoryGoodsList($params = []) { $where = empty($params['where']) ? [] : $params['where']; $field = empty($params['field']) ? 'g.*' : $params['field']; $order_by = empty($params['order_by']) ? 'g.sort_level desc, g.id desc' : trim($params['order_by']); $m = isset($params['m']) ? intval($params['m']) : 0; $n = isset($params['n']) ? intval($params['n']) : 10; // 商品列表读取前钩子 $hook_name = 'plugins_service_category_goods_list_begin'; MyEventTrigger($hook_name, [ 'hook_name' => $hook_name, 'is_backend' => true, 'params' => &$params, 'where' => &$where, 'field' => &$field, 'order_by' => &$order_by, 'm' => &$m, 'n' => &$n, ]); // 条件处理 $where_g = []; $where_gci = []; foreach($where as $v) { if(is_array($v) && count($v) == 3) { if(substr($v[0], 0, 4) == 'gci.') { $where_gci[] = $v; } else { $where_g[] = $v; } } } // 只有商品条件、排序、字段 if(empty($where_gci) && stripos($field, 'gci.') === false && stripos($order_by, 'gci.') === false) { $data = Db::name('Goods')->alias('g')->where($where_g)->field($field)->order($order_by)->limit($m, $n)->select()->toArray(); // 排序存在商品分类、商品 } else if((stripos($order_by, 'gci.') !== false && stripos($order_by, 'g.') !== false) || (stripos($field, 'gci.') !== false && stripos($field, 'g.') !== false)) { $data = Db::name('Goods')->alias('g')->join('goods_category_join gci', 'g.id=gci.goods_id')->field($field)->where($where)->group('g.id')->order($order_by)->limit($m, $n)->select()->toArray(); // 子查询 } else { $data = Db::name('Goods')->alias('g')->where($where_g)->where('g.id', 'IN', function($query) use($where_gci) { $query->name('GoodsCategoryJoin')->alias('gci')->where($where_gci)->field('gci.goods_id'); })->order($order_by)->limit($m, $n)->select()->toArray(); } // 数据处理 if(!isset($params['is_data_handle']) || $params['is_data_handle'] == 1) { $data = self::GoodsDataHandle($data, $params); } return $data; } /** * 商品数据处理 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @datetime 2018-12-08T23:16:42+0800 * @param [array] $data [商品列表] * @param [array] $params [输入参数] */ public static function GoodsDataHandle($data, $params = []) { if(!empty($data)) { // 商品列表钩子-前面 $hook_name = 'plugins_service_goods_list_handle_begin'; MyEventTrigger($hook_name, [ 'hook_name' => $hook_name, 'is_backend' => true, 'params' => &$params, 'data' => &$data, ]); // 其它额外处理 $is_photo = !isset($params['is_photo']) || (isset($params['is_photo']) && $params['is_photo'] == 1); $is_spec = isset($params['is_spec']) && $params['is_spec'] == 1; $is_content_app = isset($params['is_content_app']) && $params['is_content_app'] == 1; $is_category = isset($params['is_category']) && $params['is_category'] == 1; $is_params = isset($params['is_params']) && $params['is_params'] == 1; $is_cart = isset($params['is_cart']) && $params['is_cart'] == 1; $is_favor = isset($params['is_favor']) && $params['is_favor'] == 1; $is_score = isset($params['is_score']) && $params['is_score'] == 1; $data_key_field = empty($params['data_key_field']) ? 'id' : $params['data_key_field']; $goods_ids = array_filter(array_column($data, $data_key_field)); $currency_symbol = ResourcesService::CurrencyDataSymbol(); $common_goods_sales_price_status = MyC('common_goods_sales_price_status', 0, true); $common_goods_original_price_status = MyC('common_goods_original_price_status', 0, true); $common_goods_sales_price_unit_status = MyC('common_goods_sales_price_unit_status', 0, true); $common_goods_original_price_unit_status = MyC('common_goods_original_price_unit_status', 0, true); $common_goods_sales_number_status = MyC('common_goods_sales_number_status', 0, true); $common_goods_inventory_status = MyC('common_goods_inventory_status', 0, true); // 字段列表 $keys = ArrayKeys($data); // 品牌名称 if(in_array('brand_id', $keys)) { $brand_list = BrandService::BrandName(array_column($data, 'brand_id')); } // 产地名称 if(in_array('produce_region', $keys)) { $produce_region_list = RegionService::RegionName(array_column($data, 'produce_region')); } // 相册 $photo = $is_photo ? self::GoodsPhotoData($goods_ids) : []; // 商品分类 $category_group = $is_category ? self::GoodsListCategoryGroupList($goods_ids, $params) : []; // 规格 $spec_group = $is_spec ? self::GoodsSpecificationsData($goods_ids, $params) : []; // 参数 $params_group = $is_params ? self::GoodsParametersData($goods_ids, $params) : []; // app数据 $app_group = $is_content_app ? self::GoodsAppData($goods_ids, $params) : []; // 获取商品购物车数量 $user_cart = $is_cart ? self::UserCartGoodsCountData($goods_ids, $params) : []; // 获取商品购物车数量 $user_favor = $is_favor ? self::UserFavorGoodsCountData($goods_ids, $params) : []; // 评分 $goods_score = $is_score ? self::GoodsScoreData($goods_ids, $params) : []; // 基础字段 $base_fields = MyConst('common_goods_base_data_fields'); // 商品详情基础自动展示配置 $base_fields_show = MyC('common_goods_detail_base_fields_show', [], true); // 开始处理数据 foreach($data as $k=>&$v) { // 增加索引 $v['data_index'] = $k+1; // 数据主键id $data_id = isset($v[$data_key_field]) ? $v[$data_key_field] : 0; // 当前计量单位 $inventory_unit = empty($v['inventory_unit']) ? '' : ' / '.$v['inventory_unit']; // 原价基础字段数据 // 原价标题名称 $v['show_field_original_price_text'] = MyLang('goods_original_price_title'); // 售价符号 $v['show_original_price_symbol'] = $currency_symbol; // 售价符号 $v['show_original_price_unit'] = $common_goods_original_price_unit_status == 1 ? $inventory_unit : ''; // 是否展示原价(否0, 是1) $v['show_field_original_price_status'] = $common_goods_original_price_status; // 售价基础字段数据 // 售价标题名称 $v['show_field_price_text'] = MyLang('goods_sales_price_title'); // 售价符号 $v['show_price_symbol'] = $currency_symbol; // 售价符号 $v['show_price_unit'] = $common_goods_sales_price_unit_status == 1 ? $inventory_unit : ''; // 是否展示售价(否0, 是1) $v['show_field_price_status'] = $common_goods_sales_price_status; // 是否展示销量和库存 $v['show_sales_number_status'] = $common_goods_sales_number_status; $v['show_inventory_status'] = $common_goods_inventory_status; // 公共插件数据 // 商品详情面板提示数据、一维数组 $v['plugins_view_panel_data'] = []; // 商品详情icon数据、二维数组 // name 必填(建议不超过6个字符) // bg_color 默认(#666) // br_color 默认(#666) // color 默认($fff) // url 默认空(手机端请自行调整url地址) // [ // 'name' => 'icon名称', // 'bg_color' => '#666', // 'br_color' => '#666', // 'color' => '#fff', // 'url' => 'url地址' // ] $v['plugins_view_icon_data'] = []; // 商品价格容器 $v['price_container'] = [ 'price' => isset($v['price']) ? $v['price'] : 0.00, 'min_price' => isset($v['min_price']) ? $v['min_price'] : 0.00, 'max_price' => isset($v['max_price']) ? $v['max_price'] : 0.00, 'original_price' => isset($v['original_price']) ? $v['original_price'] : 0.00, 'min_original_price' => isset($v['min_original_price']) ? $v['min_original_price'] : 0.00, 'max_original_price' => isset($v['max_original_price']) ? $v['max_original_price'] : 0.00, ]; // 商品处理前钩子 $hook_name = 'plugins_service_goods_handle_begin'; $ret = EventReturnHandle(MyEventTrigger($hook_name, [ 'hook_name' => $hook_name, 'is_backend' => true, 'params' => &$params, 'goods' => &$v, 'goods_id' => $data_id, ])); if(isset($ret['code']) && $ret['code'] != 0) { return $ret; } // 商品url地址 if(!empty($data_id)) { $v['goods_url'] = self::GoodsUrlCreate($data_id); } // 获取相册 if($is_photo && !empty($data_id)) { $v['photo'] = (empty($photo) || empty($photo[$data_id])) ? [] : $photo[$data_id]; } // 商品封面图片 if(isset($v['images'])) { // 无封面图片 if(empty($v['images'])) { // 获取商品封面图片 $v['images'] = ResourcesService::AttachmentPathHandle(self::GoodsImagesCoverHandle($data_id, isset($v['photo']) ? $v['photo'] : [])); } $v['images'] = ResourcesService::AttachmentPathViewHandle($v['images']); } // 视频 if(isset($v['video'])) { $v['video'] = ResourcesService::AttachmentPathViewHandle($v['video']); } // 分享图片 if(isset($v['share_images'])) { $v['share_images'] = ResourcesService::AttachmentPathViewHandle($v['share_images']); } // PC内容处理 if(isset($v['content_web'])) { $v['content_web'] = ResourcesService::ContentStaticReplace($v['content_web'], 'get'); // 手机端富文本处理 if(APPLICATION == 'app') { $v['content_web'] = ResourcesService::ApMiniRichTextContentHandle($v['content_web']); } } // 虚拟商品展示数据 if(isset($v['fictitious_goods_value'])) { // 非后台模块移除该字段、避免数据泄露 if(RequestModule() != 'admin') { unset($v['fictitious_goods_value']); } else { $v['fictitious_goods_value'] = ResourcesService::ContentStaticReplace($v['fictitious_goods_value'], 'get'); } } // 使用指南数据 if(isset($v['use_guide'])) { $v['use_guide'] = ResourcesService::ContentStaticReplace($v['use_guide'], 'get'); } // 产地 if(isset($v['produce_region'])) { $v['produce_region_name'] = (!empty($produce_region_list) && is_array($produce_region_list) && array_key_exists($v['produce_region'], $produce_region_list)) ? $produce_region_list[$v['produce_region']] : ''; } // 品牌 if(isset($v['brand_id'])) { $v['brand_name'] = (!empty($brand_list) && is_array($brand_list) && array_key_exists($v['brand_id'], $brand_list)) ? $brand_list[$v['brand_id']] : ''; $v['brand_goods_url'] = (APPLICATION == 'app') ? '/pages/goods-search/goods-search?brand='.$v['brand_id'] : MyUrl('index/search/index', ['brand'=>$v['brand_id']]); } // 时间 if(isset($v['approval_number_expire'])) { $v['approval_number_expire'] = empty($v['approval_number_expire']) ? '' : date('Y-m-d', $v['approval_number_expire']); } if(isset($v['batch_number_expire'])) { $v['batch_number_expire'] = empty($v['batch_number_expire']) ? '' : date('Y-m-d', $v['batch_number_expire']); } if(isset($v['add_time'])) { $v['add_time'] = date('Y-m-d H:i:s', $v['add_time']); } if(isset($v['upd_time'])) { $v['upd_time'] = empty($v['upd_time']) ? '' : date('Y-m-d H:i:s', $v['upd_time']); } // 是否需要分类名称 if($is_category && !empty($data_id)) { if(array_key_exists($data_id, $category_group)) { $temp = $category_group[$data_id]; $v['category_ids'] = $temp['category_ids']; $v['category_text'] = empty($temp['category_names']) ? '' : implode(',', $temp['category_names']); } else { $v['category_ids'] = []; $v['category_text'] = ''; } } // 规格基础 if(isset($v['spec_base'])) { $v['spec_base'] = empty($v['spec_base']) ? '' : json_decode($v['spec_base'], true); } // 获取规格 if($is_spec && !empty($data_id)) { $v['specifications'] = (!empty($spec_group) && array_key_exists($data_id, $spec_group)) ? $spec_group[$data_id] : []; } // 获取商品参数 if($is_params && !empty($data_id)) { $v['parameters'] = (!empty($params_group) && array_key_exists($data_id, $params_group)) ? $params_group[$data_id] : []; } // 获取app内容 if($is_content_app && !empty($data_id)) { $v['content_app'] = (!empty($app_group) && array_key_exists($data_id, $app_group)) ? $app_group[$data_id] : []; } // 用户购物车总数 if($is_cart && !empty($data_id)) { $v['user_cart_count'] = (!empty($user_cart) && array_key_exists($data_id, $user_cart)) ? $user_cart[$data_id] : 0; } // 用户收藏 if($is_favor) { $v['user_is_favor'] = (!empty($user_favor) && in_array($data_id, $user_favor)) ? 1 : 0; } // 评分 if($is_score) { $v['goods_score'] = (!empty($goods_score) && array_key_exists($data_id, $goods_score)) ? $goods_score[$data_id] : ['avg'=>0, 'score'=>0, 'count'=>0]; } // 基础数据 if(!empty($base_fields) && is_array($base_fields) && !empty($base_fields_show) && is_array($base_fields_show)) { $base_data = []; foreach($base_fields as $bfk=>$bfv) { if(in_array($bfk, $base_fields_show) && array_key_exists($bfk, $v) && $v[$bfk] != 0 && $v[$bfk] != '') { $base_data[] = [ 'field' => $bfk, 'name' => $bfv, 'value' => $v[$bfk], ]; } } $v['base_data'] = empty($base_data) ? '' : $base_data; } // 商品处理后钩子 $hook_name = 'plugins_service_goods_handle_end'; $ret = EventReturnHandle(MyEventTrigger($hook_name, [ 'hook_name' => $hook_name, 'is_backend' => true, 'params' => &$params, 'goods' => &$v, 'goods_id' => isset($data_id) ? $data_id : 0, ])); if(isset($ret['code']) && $ret['code'] != 0) { return $ret; } } // 商品列表钩子-后面 $hook_name = 'plugins_service_goods_list_handle_end'; MyEventTrigger($hook_name, [ 'hook_name' => $hook_name, 'is_backend' => true, 'params' => &$params, 'data' => &$data, ]); // 错误处理 if(!empty($data) & is_array($data)) { foreach($data as &$gv) { // 数据主键id $data_id = isset($v[$data_key_field]) ? $v[$data_key_field] : 0; // 错误处理 if(!isset($gv['is_error']) || $gv['is_error'] == 0) { $gv['is_error'] = 0; $gv['error_msg'] = ''; } if($gv['is_error'] == 0 && array_key_exists('is_delete_time', $gv) && $gv['is_delete_time'] != 0) { $gv['is_error'] = 1; $gv['error_msg'] = MyLang('goods_already_nullify_title'); } // 是否上架 if($gv['is_error'] == 0 && array_key_exists('is_shelves', $gv) && $gv['is_shelves'] != 1) { $gv['is_error'] = 1; $gv['error_msg'] = MyLang('goods_already_shelves_title'); } // 是否有库存 if($gv['is_error'] == 0 && array_key_exists('inventory', $gv) && $gv['inventory'] <= 0) { $gv['is_error'] = 1; $gv['error_msg'] = MyLang('goods_no_inventory_title'); } // 没错误则判断类型是否一致 if($gv['is_error'] == 0 && array_key_exists('site_type', $gv) && !empty($data_id)) { $ret = self::IsGoodsSiteTypeConsistent($data_id, $gv['site_type']); if($ret['code'] != 0) { $gv['is_error'] = 1; $gv['error_msg'] = $ret['msg']; } } } } } return DataReturn('success', 0, $data); } /** * 商品列表获取产品分类分组信息 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2022-10-13 * @desc description * @param [array] $goods_ids [商品id] * @param [array] $params [输入参数] */ public static function GoodsListCategoryGroupList($goods_ids, $params = []) { $result = []; $category_join = Db::name('GoodsCategoryJoin')->where(['goods_id'=>$goods_ids])->field('goods_id,category_id')->select()->toArray(); if(!empty($category_join)) { $category_name = Db::name('GoodsCategory')->where(['id'=>array_unique(array_column($category_join, 'category_id'))])->column('name', 'id'); if(!empty($category_name)) { foreach($category_join as $v) { if(array_key_exists($v['category_id'], $category_name)) { if(!array_key_exists($v['goods_id'], $result)) { $result[$v['goods_id']] = [ 'category_ids' => [], 'category_names' => [], ]; } $result[$v['goods_id']]['category_ids'][] = $v['category_id']; $result[$v['goods_id']]['category_names'][] = $category_name[$v['category_id']]; } } } } return $result; } /** * 获取商品封面图片 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2020-08-19 * @desc description * @param [int] $goods_id [商品id] * @param [array] $photo [商品相册] */ public static function GoodsImagesCoverHandle($goods_id = 0, $photo = []) { // 是否已存在相册 if(!empty($photo)) { $photo = self::GoodsPhotoData($goods_id); if(!empty($photo[0]) && !empty($photo[0]['images'])) { $images = $photo[0]['images']; } } // 无主图,并且有商品id if(empty($images) && !empty($goods_id)) { $images = Db::name('GoodsPhoto')->where(['goods_id'=>$goods_id, 'is_show'=>1])->order('sort asc')->value('images'); } return isset($images) ? $images : ''; } /** * 商品相册 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2020-08-19 * @desc description * @param [int|array] $goods_ids [商品id] */ public static function GoodsPhotoData($goods_ids) { $data = Db::name('GoodsPhoto')->where(['goods_id'=>$goods_ids, 'is_show'=>1])->order('sort asc')->select()->toArray(); if(is_array($goods_ids)) { $group = []; if(!empty($data)) { foreach($data as $v) { if(!array_key_exists($v['goods_id'], $group)) { $group[$v['goods_id']] = []; } $v['images'] = ResourcesService::AttachmentPathViewHandle($v['images']); $group[$v['goods_id']][] = $v; } } return $group; } return $data; } /** * 商品评分 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2023-03-20 * @desc description * @param [array] $goods_ids [商品id] * @param [array] $params [输入参数] */ public static function GoodsScoreData($goods_ids, $params = []) { $data = Db::name('GoodsComments')->where(['goods_id'=>$goods_ids])->group('goods_id')->column('goods_id, AVG(rating) AS avg, COUNT(goods_id) AS count', 'goods_id'); if(!empty($data)) { foreach($data as &$v) { unset($v['goods_id']); $v['avg'] = PriceNumberFormat($v['avg'], 1); $v['score'] = ($v['avg'] <= 0) ? 100 : intval(($v['avg']/5)*100); } } return $data; } /** * 获取用户收藏商品数据 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2023-03-20 * @desc description * @param [array] $goods_ids [商品id] * @param [array] $params [输入参数] */ public static function UserFavorGoodsCountData($goods_ids, $params = []) { $result = []; $user = UserService::LoginUserInfo(); if(!empty($user)) { $where = [ ['goods_id', 'in', $goods_ids], ['user_id', '=', $user['id']], ]; $result = Db::name('GoodsFavor')->where($where)->column('goods_id'); } return $result; } /** * 获取用户购物车商品总数 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2023-03-20 * @desc description * @param [array] $goods_ids [商品id] * @param [array] $params [输入参数] */ public static function UserCartGoodsCountData($goods_ids, $params = []) { $result = []; $user = UserService::LoginUserInfo(); if(!empty($user)) { $where = [ ['goods_id', 'in', $goods_ids], ['user_id', '=', $user['id']], ]; $result = Db::name('Cart')->where($where)->field('SUM(stock) AS count, goods_id')->group('goods_id')->select()->toArray(); if(!empty($result)) { $result = array_column($result, 'count', 'goods_id'); } } return $result; } /** * 获取商品手机详情 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-07-10 * @desc description * @param [array] $goods_ids [商品id] * @param [array] $params [输入参数] * @return [array] [app内容] */ public static function GoodsAppData($goods_ids, $params = []) { $data = []; $list = Db::name('GoodsContentApp')->where(['goods_id'=>$goods_ids])->field('id,goods_id,images,content')->order('sort asc')->select()->toArray(); if(!empty($list)) { foreach($list as $v) { // 数据处理 $v['images'] = ResourcesService::AttachmentPathViewHandle($v['images']); $v['content_old'] = $v['content']; $v['content'] = empty($v['content']) ? null : explode("\n", $v['content']); // 数据组合 if(!array_key_exists($v['goods_id'], $data)) { $data[$v['goods_id']] = []; } $data[$v['goods_id']][] = $v; } } // 商品手机详情钩子 $hook_name = 'plugins_service_goods_content_app_data'; MyEventTrigger($hook_name, [ 'hook_name' => $hook_name, 'is_backend' => true, 'data' => &$data, 'goods_ids' => $goods_ids, ]); return $data; } /** * 获取商品规格 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2020-07-16 * @desc description * @param [array] $goods_ids [商品id] * @param [array] $params [输入参数] */ public static function GoodsSpecificationsData($goods_ids, $params = []) { // 静态数据容器,确保每一个商品只读取一次规格,避免重复读取浪费资源 static $goods_spec_group_static_data = []; $temp_goods_ids = []; foreach($goods_ids as $gid) { if(empty($goods_spec_group_static_data) || !array_key_exists($gid, $goods_spec_group_static_data)) { $temp_goods_ids[] = $gid; } } // 存在未读取的规格咋数据库读取 if(!empty($temp_goods_ids)) { $temp_group = []; $data = Db::name('GoodsSpecType')->where(['goods_id'=>$temp_goods_ids])->order('id asc')->select()->toArray(); if(!empty($data)) { // 分组 foreach($data as $v) { if(!array_key_exists($v['goods_id'], $temp_group)) { $temp_group[$v['goods_id']] = ['choose'=>[]]; } $temp_group[$v['goods_id']]['choose'][] = $v; } // 数据处理 foreach($temp_group as $gid=>&$gv) { if(!empty($gv['choose'])) { // 基础处理 foreach($gv['choose'] as &$gvs) { $gvs_value = json_decode($gvs['value'], true); foreach($gvs_value as &$gvss) { $gvss['images'] = ResourcesService::AttachmentPathViewHandle($gvss['images']); } $gvs['value'] = $gvs_value; $gvs['add_time'] = date('Y-m-d H:i:s', $gvs['add_time']); } // 只有一个规格的时候直接获取规格值的库存数 if(count($gv['choose']) == 1) { foreach($gv['choose'][0]['value'] as &$temp_spec) { $temp_spec_params = array_merge($params, [ 'id' => $gid, 'spec' => [ ['type' => $gv['choose'][0]['name'], 'value' => $temp_spec['name']] ], ]); $temp = self::GoodsSpecDetail($temp_spec_params); if($temp['code'] == 0) { $temp_spec['is_only_level_one'] = 1; $temp_spec['inventory'] = $temp['data']['spec_base']['inventory']; } } } } $goods_spec_group_static_data[$gid] = $gv; } } // 空数据记录、避免重复查询 foreach($temp_goods_ids as $gid) { if(!array_key_exists($gid, $goods_spec_group_static_data)) { $goods_spec_group_static_data[$gid] = []; } } } // 返回当前指定的商品id对应的规格数据 $data = []; if(!empty($goods_spec_group_static_data)) { foreach($goods_ids as $gid) { if(!empty($goods_spec_group_static_data[$gid])) { $data[$gid] = $goods_spec_group_static_data[$gid]; } } } // 商品规格钩子 $hook_name = 'plugins_service_goods_spec_data'; MyEventTrigger($hook_name, [ 'hook_name' => $hook_name, 'is_backend' => true, 'data' => &$data, 'goods_ids' => $goods_ids, ]); return $data; } /** * 获取商品参数 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2020-08-31 * @desc description * @param [array] $goods_ids [商品id] * @param [array] $params [输入参数] */ public static function GoodsParametersData($goods_ids, $params = []) { $data = []; $list = Db::name('GoodsParams')->where(['goods_id'=>$goods_ids])->order('id asc')->select()->toArray(); if(!empty($list)) { // 分组 foreach($list as $v) { if(!array_key_exists($v['goods_id'], $data)) { $data[$v['goods_id']] = ['base'=>[], 'detail'=>[]]; } // 基础 if(in_array($v['scope'], [0,2])) { $data[$v['goods_id']]['base'][] = $v; } // 详情 if(in_array($v['scope'], [0,1])) { $data[$v['goods_id']]['detail'][] = $v; } } } // 商品参数钩子 $hook_name = 'plugins_service_goods_parameters_data'; MyEventTrigger($hook_name, [ 'hook_name' => $hook_name, 'is_backend' => true, 'data' => &$data, 'goods_ids' => $goods_ids, ]); return $data; } /** * 商品规格简洁的数据处理 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2020-07-15 * @desc description * @param [array] $data [规格数据] */ public static function GoodsSpecificationsConcise($data) { $result = []; if(!empty($data)) { foreach($data as $v) { $result[] = array_column($v['value'], 'name'); } } return $result; } /** * 获取商品当前实际存在的规格 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2020-07-16 * @desc description * @param [int] $goods_id [商品id] */ public static function GoodsSpecificationsActual($goods_id) { // 规格名称 $where = ['goods_id'=>$goods_id]; $title = Db::name('GoodsSpecType')->where($where)->column('name'); // 规格值 $value = Db::name('GoodsSpecValue')->where($where)->field('goods_spec_base_id,value')->select()->toArray(); $group = []; if(!empty($value)) { foreach($value as $v) { // 不存在则添加 if(!isset($group[$v['goods_spec_base_id']])) { $group[$v['goods_spec_base_id']] = []; $group[$v['goods_spec_base_id']]['base_id'] = $v['goods_spec_base_id']; } // 多个规格组合 $group[$v['goods_spec_base_id']]['value'][] = $v['value']; } foreach($group as &$gv) { $gv['value'] = implode(self::$goods_spec_to_string_separator, $gv['value']); } sort($group); } return [ 'title' => $title, 'value' => $group, ]; } /** * 商品访问统计加1 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-10-15 * @desc description * @param [array] $params [输入参数] */ public static function GoodsAccessCountInc($params = []) { if(!empty($params['goods_id'])) { return Db::name('Goods')->where(['id'=>intval($params['goods_id'])])->inc('access_count')->update(); } return false; } /** * 获取商品总数 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-09-07 * @desc description * @param [array] $where [条件] */ public static function GoodsTotal($where = []) { // 商品总数读取前钩子 $hook_name = 'plugins_service_goods_total_begin'; MyEventTrigger($hook_name, [ 'hook_name' => $hook_name, 'is_backend' => true, 'where' => &$where, ]); // 获取总数 return (int) Db::name('Goods')->where($where)->count(); } /** * 获取商品列表 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-08-29 * @desc description * @param array $params [输入参数: where, field, is_photo] */ public static function GoodsList($params = []) { $where = empty($params['where']) ? [] : $params['where']; $field = empty($params['field']) ? '*' : $params['field']; $order_by = empty($params['order_by']) ? 'id desc' : trim($params['order_by']); $m = isset($params['m']) ? intval($params['m']) : 0; $n = isset($params['n']) ? intval($params['n']) : 10; // 商品列表读取前钩子 $hook_name = 'plugins_service_goods_list_begin'; MyEventTrigger($hook_name, [ 'hook_name' => $hook_name, 'is_backend' => true, 'params' => &$params, 'where' => &$where, 'field' => &$field, 'order_by' => &$order_by, 'm' => &$m, 'n' => &$n, ]); // 查询商品 $data = Db::name('Goods')->field($field)->where($where)->order($order_by)->limit($m, $n)->select()->toArray(); // 数据处理 return self::GoodsDataHandle($data, $params); } /** * 商品保存 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @datetime 2018-12-10T01:02:11+0800 * @param [array] $params [输入参数] */ public static function GoodsSave($params = []) { // 商品分类id $category_ids = empty($params['category_id']) ? [] : (is_array($params['category_id']) ? $params['category_id'] : explode(',', $params['category_id'])); // 商品基础字段必填配置数据 $required_fields = empty($category_ids) ? [] : self::GoodsBaseFieldsRequiredConfigData($category_ids); $is_simple_desc = !empty($required_fields) && isset($required_fields['simple_desc']) && $required_fields['simple_desc'] == 1; $is_spec_desc = !empty($required_fields) && isset($required_fields['spec_desc']) && $required_fields['spec_desc'] == 1; $is_approval_number = !empty($required_fields) && isset($required_fields['approval_number']) && $required_fields['approval_number'] == 1; $is_approval_number_expire = !empty($required_fields) && isset($required_fields['approval_number_expire']) && $required_fields['approval_number_expire'] == 1; $is_batch_number = !empty($required_fields) && isset($required_fields['batch_number']) && $required_fields['batch_number'] == 1; $is_batch_number_expire = !empty($required_fields) && isset($required_fields['batch_number_expire']) && $required_fields['batch_number_expire'] == 1; $is_coding = !empty($required_fields) && isset($required_fields['coding']) && $required_fields['coding'] == 1; $is_model = !empty($required_fields) && isset($required_fields['model']) && $required_fields['model'] == 1; $is_brand_id = !empty($required_fields) && isset($required_fields['brand_id']) && $required_fields['brand_id'] == 1; $is_produce_company = !empty($required_fields) && isset($required_fields['produce_company']) && $required_fields['produce_company'] == 1; $is_produce_region = !empty($required_fields) && isset($required_fields['produce_region']) && $required_fields['produce_region'] == 1; // 请求参数 $p = [ [ 'checked_type' => 'length', 'key_name' => 'title', 'checked_data' => '2,160', 'error_msg' => MyLang('common_service.goods.form_item_title_message'), ], [ 'checked_type' => 'length', 'key_name' => 'simple_desc', 'checked_data' => $is_simple_desc ? '1,230' : '230', 'is_checked' => $is_simple_desc ? null : 1, 'error_msg' => MyLang('common_service.goods.form_item_simple_desc_message'), ], [ 'checked_type' => 'length', 'key_name' => 'spec_desc', 'checked_data' => $is_spec_desc ? '1,230' : '230', 'is_checked' => $is_spec_desc ? null : 1, 'error_msg' => MyLang('common_service.goods.form_item_spec_desc_message'), ], [ 'checked_type' => 'length', 'key_name' => 'approval_number', 'checked_data' => $is_approval_number ? '1,180' : '180', 'is_checked' => $is_approval_number ? null : 1, 'error_msg' => MyLang('common_service.goods.form_item_approval_number_message'), ], [ 'checked_type' => 'empty', 'key_name' => 'approval_number_expire', 'is_checked' => $is_approval_number_expire ? null : 1, 'error_msg' => MyLang('common_service.goods.form_item_approval_number_expire_message'), ], [ 'checked_type' => 'length', 'key_name' => 'batch_number', 'checked_data' => $is_batch_number ? '1,180' : '180', 'is_checked' => $is_batch_number ? null : 1, 'error_msg' => MyLang('common_service.goods.form_item_batch_number_message'), ], [ 'checked_type' => 'empty', 'key_name' => 'batch_number_expire', 'is_checked' => $is_batch_number_expire ? null : 1, 'error_msg' => MyLang('common_service.goods.form_item_batch_number_expire_message'), ], [ 'checked_type' => 'length', 'key_name' => 'coding', 'checked_data' => $is_coding ? '1,180' : '180', 'is_checked' => $is_coding ? null : 1, 'error_msg' => MyLang('common_service.goods.form_item_coding_message'), ], [ 'checked_type' => 'length', 'key_name' => 'model', 'checked_data' => $is_model ? '1,180' : '180', 'is_checked' => $is_model ? null : 1, 'error_msg' => MyLang('common_service.goods.form_item_model_message'), ], [ 'checked_type' => 'empty', 'key_name' => 'brand_id', 'is_checked' => $is_brand_id ? null : 1, 'error_msg' => MyLang('common_service.goods.form_item_brand_id_message'), ], [ 'checked_type' => 'length', 'key_name' => 'produce_company', 'checked_data' => $is_produce_company ? '1,180' : '180', 'is_checked' => $is_produce_company ? null : 1, 'error_msg' => MyLang('common_service.goods.form_item_produce_company_message'), ], [ 'checked_type' => 'empty', 'key_name' => 'produce_region', 'is_checked' => $is_produce_region ? null : 1, 'error_msg' => MyLang('common_service.goods.form_item_produce_region_message'), ], [ 'checked_type' => 'empty', 'key_name' => 'category_id', 'error_msg' => MyLang('common_service.goods.form_item_category_id_message'), ], [ 'checked_type' => 'length', 'key_name' => 'inventory_unit', 'checked_data' => '1,6', 'error_msg' => MyLang('common_service.goods.form_item_inventory_unit_message'), ], [ 'checked_type' => 'in', 'key_name' => 'site_type', 'checked_data' => array_column(MyConst('common_site_type_list'), 'value'), 'is_checked' => 1, 'error_msg' => MyLang('common_service.goods.save_site_type_error_tips'), ], [ 'checked_type' => 'length', 'key_name' => 'seo_title', 'checked_data' => '100', 'is_checked' => 1, 'error_msg' => MyLang('form_seo_title_message'), ], [ 'checked_type' => 'length', 'key_name' => 'seo_keywords', 'checked_data' => '130', 'is_checked' => 1, 'error_msg' => MyLang('form_seo_keywords_message'), ], [ 'checked_type' => 'length', 'key_name' => 'seo_desc', 'checked_data' => '230', 'is_checked' => 1, 'error_msg' => MyLang('form_seo_desc_message'), ], ]; $ret = ParamsChecked($params, $p); if($ret !== true) { return DataReturn($ret, -1); } // 规格基础 $specifications_base = self::GetFormGoodsSpecificationsBaseParams($params); if($specifications_base['code'] != 0) { return $specifications_base; } // 规格值 $specifications = self::GetFormGoodsSpecificationsParams($params); if($specifications['code'] != 0) { return $specifications; } // 商品参数 $parameter = GoodsParamsService::GoodsParamsTemplateSaveHandle($params); // 相册 $photo = self::GetFormGoodsPhotoParams($params); if($photo['code'] != 0) { return $photo; } // 手机端详情 $content_app = self::GetFormGoodsContentAppParams($params); if($content_app['code'] != 0) { return $content_app; } // 其它附件 $attachment = ResourcesService::AttachmentParams($params, ['images', 'video', 'share_images']); if($attachment['code'] != 0) { return $attachment; } // 编辑器内容 $content_web = empty($params['content_web']) ? '' : str_replace("\n", '', ResourcesService::ContentStaticReplace(htmlspecialchars_decode($params['content_web']), 'add')); $fictitious_goods_value = empty($params['fictitious_goods_value']) ? '' : str_replace("\n", '', ResourcesService::ContentStaticReplace(htmlspecialchars_decode($params['fictitious_goods_value']), 'add')); $use_guide = empty($params['use_guide']) ? '' : str_replace("\n", '', ResourcesService::ContentStaticReplace(htmlspecialchars_decode($params['use_guide']), 'add')); // 封面图片、默认相册第一张 $images = empty($attachment['data']['images']) ? (isset($photo['data'][0]) ? $photo['data'][0] : '') : $attachment['data']['images']; // 基础数据 $data = [ 'title' => $params['title'], 'title_color' => empty($params['title_color']) ? '' : $params['title_color'], 'simple_desc' => empty($params['simple_desc']) ? '' : $params['simple_desc'], 'spec_desc' => empty($params['spec_desc']) ? '' : $params['spec_desc'], 'approval_number' => empty($params['approval_number']) ? '' : $params['approval_number'], 'approval_number_expire' => empty($params['approval_number_expire']) ? 0 : strtotime($params['approval_number_expire']), 'batch_number' => empty($params['batch_number']) ? '' : $params['batch_number'], 'batch_number_expire' => empty($params['batch_number_expire']) ? 0 : strtotime($params['batch_number_expire']), 'coding' => empty($params['coding']) ? '' : $params['coding'], 'model' => empty($params['model']) ? '' : $params['model'], 'produce_company' => empty($params['produce_company']) ? '' : $params['produce_company'], 'produce_region' => isset($params['produce_region']) ? intval($params['produce_region']) : 0, 'inventory_unit' => $params['inventory_unit'], 'is_deduction_inventory' => isset($params['is_deduction_inventory']) ? intval($params['is_deduction_inventory']) : 0, 'is_shelves' => isset($params['is_shelves']) ? intval($params['is_shelves']) : 0, 'content_web' => $content_web, 'photo_count' => count($photo['data']), 'images' => $images, 'brand_id' => isset($params['brand_id']) ? intval($params['brand_id']) : 0, 'video' => $attachment['data']['video'], 'seo_title' => empty($params['seo_title']) ? '' : $params['seo_title'], 'seo_keywords' => empty($params['seo_keywords']) ? '' : $params['seo_keywords'], 'seo_desc' => empty($params['seo_desc']) ? '' : $params['seo_desc'], 'is_exist_many_spec' => empty($specifications['data']['title']) ? 0 : 1, 'spec_base' => empty($specifications_base['data']) ? '' : json_encode($specifications_base['data'], JSON_UNESCAPED_UNICODE), 'fictitious_goods_value' => $fictitious_goods_value, 'use_guide' => $use_guide, 'site_type' => (isset($params['site_type']) && $params['site_type'] != '') ? $params['site_type'] : -1, 'sort_level' => empty($params['sort_level']) ? 0 : intval($params['sort_level']), 'share_images' => $attachment['data']['share_images'], ]; // 是否存在赠送积分 if(array_key_exists('give_integral', $params)) { $data['give_integral'] = intval($params['give_integral']); } // 商品保存处理钩子 $hook_name = 'plugins_service_goods_save_handle'; $ret = EventReturnHandle(MyEventTrigger($hook_name, [ 'hook_name' => $hook_name, 'is_backend' => true, 'params' => &$params, 'data' => &$data, 'spec' => &$specifications['data'], 'parameter' => &$parameter['data'], 'photo' => &$photo['data'], 'content_app' => &$content_app['data'], 'goods_id' => isset($params['id']) ? intval($params['id']) : 0, ])); if(isset($ret['code']) && $ret['code'] != 0) { return $ret; } // 启动事务 Db::startTrans(); // 捕获异常 try { // 商品保存事物前钩子 $hook_name = 'plugins_service_goods_save_thing_begin'; $ret = MyEventTrigger($hook_name, [ 'hook_name' => $hook_name, 'is_backend' => true, 'params' => $params, 'data' => &$data, 'goods_id' => isset($params['id']) ? intval($params['id']) : 0, ]); if(isset($ret['code']) && $ret['code'] != 0) { throw new \Exception($ret['msg']); } // 添加/编辑 if(empty($params['id'])) { $data['add_time'] = time(); $goods_id = Db::name('Goods')->insertGetId($data); if($goods_id <= 0) { throw new \Exception(MyLang('insert_fail')); } } else { $data['upd_time'] = time(); if(Db::name('Goods')->where(['id'=>intval($params['id'])])->update($data)) { $goods_id = $params['id']; } else { throw new \Exception(MyLang('update_fail')); } } // 分类 $ret = self::GoodsCategoryInsert($category_ids, $goods_id); if($ret['code'] != 0) { throw new \Exception($ret['msg']); } // 规格 $ret = self::GoodsSpecificationsInsert($specifications['data'], $goods_id); if($ret['code'] != 0) { throw new \Exception($ret['msg']); } // 更新商品基础信息 $ret = self::GoodsSaveBaseUpdate($goods_id); if($ret['code'] != 0) { throw new \Exception($ret['msg']); } // 相册 $ret = self::GoodsPhotoInsert($photo['data'], $goods_id); if($ret['code'] != 0) { throw new \Exception($ret['msg']); } // 手机详情 $ret = self::GoodsContentAppInsert($content_app['data'], $goods_id); if($ret['code'] != 0) { throw new \Exception($ret['msg']); } // 商品参数 $ret = self::GoodsParamsInsert($parameter['data'], $goods_id); if($ret['code'] != 0) { throw new \Exception($ret['msg']); } // 仓库规格库存同步 $ret = WarehouseGoodsService::GoodsSpecChangeInventorySync($goods_id); if($ret['code'] != 0) { throw new \Exception($ret['msg']); } // 商品保存事物后钩子 $hook_name = 'plugins_service_goods_save_thing_end'; $ret = MyEventTrigger($hook_name, [ 'hook_name' => $hook_name, 'is_backend' => true, 'params' => $params, 'data' => $data, 'goods_id' => $goods_id, ]); if(isset($ret['code']) && $ret['code'] != 0) { throw new \Exception($ret['msg']); } // 完成 Db::commit(); } catch(\Exception $e) { Db::rollback(); return DataReturn($e->getMessage(), -1); } // 商品保存后钩子 $hook_name = 'plugins_service_goods_save_end'; MyEventTrigger($hook_name, [ 'hook_name' => $hook_name, 'is_backend' => true, 'params' => $params, 'data' => $data, 'goods_id' => $goods_id, ]); // 返回信息 return DataReturn(MyLang('operate_success'), 0); } /** * 商品参数添加 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2020-08-31 * @desc description * @param [array] $data [商品参数数据] * @param [int] $goods_id [商品id] */ public static function GoodsParamsInsert($data, $goods_id) { // 删除商品参数 Db::name('GoodsParams')->where(['goods_id'=>$goods_id])->delete(); // 获取参数解析并添加 if(!empty($data)) { foreach($data as &$v) { $v['goods_id'] = $goods_id; $v['add_time'] = time(); } if(Db::name('GoodsParams')->insertAll($data) < count($data)) { return DataReturn(MyLang('common_service.goods.save_params_add_fail_tips'), -1); } } return DataReturn(MyLang('operate_success'), 0); } /** * 商品保存基础信息更新 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @datetime 2018-12-16T01:56:42+0800 * @param [int] $goods_id [商品id] * @param [array] $params [输入参数] */ public static function GoodsSaveBaseUpdate($goods_id, $params = []) { // 商品基础数据 $base = Db::name('GoodsSpecBase')->where(['goods_id'=>$goods_id])->select()->toArray(); if(empty($base)) { return DataReturn(MyLang('common_service.goods.save_goods_base_empty_tips'), -1); } // 汇总处理 $data = [ 'min_price' => min(array_column($base, 'price')), 'max_price' => max(array_column($base, 'price')), 'min_original_price' => min(array_column($base, 'original_price')), 'max_original_price' => max(array_column($base, 'original_price')), 'inventory' => array_sum(array_column($base, 'inventory')), ]; // 起购数、限购数处理 $data['buy_min_number'] = min(array_column($base, 'buy_min_number')); if($data['buy_min_number'] <= 0) { $data['buy_min_number'] = 1; } $buy_max_number = max(array_column($base, 'buy_max_number')); $data['buy_max_number'] = ($buy_max_number > 0 && min(array_column($base, 'buy_max_number')) > 0) ? $buy_max_number : 0; // 销售价格 - 展示价格 $data['price'] = (!empty($data['max_price']) && $data['min_price'] != $data['max_price']) ? $data['min_price'].'-'.$data['max_price'] : $data['min_price']; // 原价价格 - 展示价格 $data['original_price'] = (!empty($data['max_original_price']) && $data['min_original_price'] != $data['max_original_price']) ? $data['min_original_price'].'-'.$data['max_original_price'] : $data['min_original_price']; // 更新商品表 $data['upd_time'] = time(); if(Db::name('Goods')->where(['id'=>$goods_id])->update($data) === false) { return DataReturn(MyLang('common_service.goods.save_goods_base_update_fail_tips'), -1); } // 商品基础数据更新钩子 $hook_name = 'plugins_service_goods_base_update'; $ret = EventReturnHandle(MyEventTrigger($hook_name, [ 'hook_name' => $hook_name, 'is_backend' => true, 'goods_id' => $goods_id, 'params' => $params, ])); if(isset($ret['code']) && $ret['code'] != 0) { return $ret; } return DataReturn(MyLang('operate_success'), 0); } /** * 获取规格值参数 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-07-09 * @desc description * @param [array] $params [输入参数] * @param [array] $base_count [教程参数个数,默认9] */ public static function GetFormGoodsSpecificationsParams($params = [], $base_count = 9) { $data = []; $title = []; $images = []; // 基础字段数据字段长度 // 销售价、原价、起购数、限购数、重量、体积、编码、条形码、扩展 // 规格值 foreach($params as $k=>$v) { if(substr($k, 0, 15) == 'specifications_') { $keys = explode('_', $k); if(count($keys) > 1) { if($keys[1] != 'name') { foreach($v as $ks=>$vs) { if($keys[1] == 'extends') { $data[$ks][] = empty($vs) ? null : htmlspecialchars_decode($vs); } else { $data[$ks][] = trim($vs); } } } } } } // 规格处理 if(!empty($data[0])) { $count = count($data[0])-$base_count; if($count > 0) { // 列之间是否存在相同的值 $column_value = []; foreach($data as $data_value) { foreach($data_value as $temp_key=>$temp_value) { if($temp_key < $count) { $column_value[$temp_key][] = $temp_value; } } } if(!empty($column_value) && count($column_value) > 1) { $temp_column = []; foreach($column_value as $column_key=>$column_val) { foreach($column_value as $column_keys=>$column_vals) { if($column_key != $column_keys) { $temp = array_intersect($column_val, $column_vals); $temp_column = array_merge($temp_column, $temp); } } } if(!empty($temp_column)) { return DataReturn(MyLang('common_service.goods.save_spec_column_repeat_tips').'['.implode(',', array_unique($temp_column)).']', -1); } } // 规格值是否重复 if(!empty($column_value[0])) { $temp_row_data = []; $temp_row_count = count($column_value); foreach($column_value[0] as $row_key=>$row_value) { for($i=0; $i<$temp_row_count; $i++) { if(isset($column_value[$i][$row_key])) { if(isset($temp_row_data[$row_key])) { $temp_row_data[$row_key] .= $column_value[$i][$row_key]; } else { $temp_row_data[$row_key] = $column_value[$i][$row_key]; } } } } if(!empty($temp_row_data)) { $unique_all = array_unique($temp_row_data); $repeat_rows_all = array_diff_assoc($temp_row_data, $unique_all); if(!empty($repeat_rows_all)) { return DataReturn(MyLang('common_service.goods.save_spec_value_repeat_tips').'['.implode(',', array_unique($repeat_rows_all)).']', -1); } } } // 规格名称 $names_value = []; $names = array_slice($data[0], 0, $count); foreach($names as $v) { foreach($params as $ks=>$vs) { if(substr($ks, 0, 21) == 'specifications_value_') { if(in_array($v, $vs)) { $key = substr($ks, 21); if(!empty($params['specifications_name_'.$key])) { $spec_name = trim($params['specifications_name_'.$key]); $title[$spec_name] = [ 'name' => $spec_name, 'value' => array_unique($vs), ]; $names_value[] = $params['specifications_name_'.$key]; } } } } } // 规格名称列之间是否存在重复 $unique_all = array_unique($names_value); $repeat_names_all = array_diff_assoc($names_value, $unique_all); if(!empty($repeat_names_all)) { return DataReturn(MyLang('common_service.goods.save_spec_name_column_repeat_tips').'['.implode(',', array_unique($repeat_names_all)).']', -1); } } else { if(!isset($data[0][0]) || $data[0][0] < 0) { return DataReturn(MyLang('common_service.goods.save_spec_base_price_error_tips'), -1); } } } else { return DataReturn(MyLang('common_service.goods.save_spec_empty_tips'), -1); } // 规格图片 if(!empty($params['spec_images_name']) && !empty($params['spec_images'])) { foreach($params['spec_images_name'] as $k=>$v) { if(!empty($params['spec_images'][$k])) { $images[$v] = $params['spec_images'][$k]; } } } return DataReturn('success', 0, ['data'=>$data, 'title'=>$title, 'images'=>$images]); } /** * 获取规格基础参数 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2019-09-23 * @desc description * @param [array] $params [输入参数] */ public static function GetFormGoodsSpecificationsBaseParams($params = []) { $result = []; foreach($params as $k=>$v) { if(substr($k, 0, 16) == 'spec_base_title_') { $key = substr($k, 16); $result[] = [ 'title' => $v, 'value' => isset($params['spec_base_value_'.$key]) ? $params['spec_base_value_'.$key] : [], ]; } } return DataReturn('success', 0, $result); } /** * 获取商品相册 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-07-10 * @desc description * @param [array] $params [输入参数] * @return [array] [一维数组但图片地址] */ public static function GetFormGoodsPhotoParams($params = []) { if(empty($params['photo'])) { return DataReturn(MyLang('common_service.goods.save_photo_empty_tips'), -1); } $result = ResourcesService::AttachmentPathHandle($params['photo']); return DataReturn('success', 0, $result); } /** * 获取app内容 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-07-09 * @desc description * @param [array] $params [输入参数] */ public static function GetFormGoodsContentAppParams($params = []) { // 开始处理 $result = []; $name = 'content_app_'; foreach($params AS $k=>$v) { if(substr($k, 0, 12) == $name) { $key = explode('_', str_replace($name, '', $k)); if(count($key) == 2) { $result[$key[1]][$key[0]] = $v; if($key[0] == 'images') { $result[$key[1]][$key[0]] = ResourcesService::AttachmentPathHandle($v); } } } } return DataReturn('success', 0, $result); } /** * 商品分类添加 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-07-10 * @desc description * @param [array] $data [数据] * @param [int] $goods_id [商品id] * @return [array] [boolean | msg] */ public static function GoodsCategoryInsert($data, $goods_id) { Db::name('GoodsCategoryJoin')->where(['goods_id'=>$goods_id])->delete(); if(!empty($data)) { foreach($data as $category_id) { $temp_category = [ 'goods_id' => $goods_id, 'category_id' => $category_id, 'add_time' => time(), ]; if(Db::name('GoodsCategoryJoin')->insertGetId($temp_category) <= 0) { return DataReturn(MyLang('common_service.goods.save_category_add_fail_tips'), -1); } } } return DataReturn(MyLang('insert_success'), 0); } /** * 商品手机详情添加 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-07-10 * @desc description * @param [array] $data [数据] * @param [int] $goods_id [商品id] * @return [array] [boolean | msg] */ public static function GoodsContentAppInsert($data, $goods_id) { Db::name('GoodsContentApp')->where(['goods_id'=>$goods_id])->delete(); if(!empty($data)) { foreach(array_values($data) as $k=>$v) { $temp_content = [ 'goods_id' => $goods_id, 'images' => empty($v['images']) ? '' : $v['images'], 'content' => $v['text'], 'sort' => $k, 'add_time' => time(), ]; if(Db::name('GoodsContentApp')->insertGetId($temp_content) <= 0) { return DataReturn(MyLang('common_service.goods.save_app_content_add_fail_tips'), -1); } } } return DataReturn(MyLang('insert_success'), 0); } /** * 商品相册添加 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-07-10 * @desc description * @param [array] $data [数据] * @param [int] $goods_id [商品id] * @return [array] [boolean | msg] */ public static function GoodsPhotoInsert($data, $goods_id) { Db::name('GoodsPhoto')->where(['goods_id'=>$goods_id])->delete(); if(!empty($data)) { foreach($data as $k=>$v) { $temp_photo = [ 'goods_id' => $goods_id, 'images' => $v, 'is_show' => 1, 'sort' => $k, 'add_time' => time(), ]; if(Db::name('GoodsPhoto')->insertGetId($temp_photo) <= 0) { return DataReturn(MyLang('common_service.goods.save_photo_add_fail_tips'), -1); } } } return DataReturn(MyLang('insert_success'), 0); } /** * 商品规格添加 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-07-10 * @desc description * @param [array] $data [数据] * @param [int] $goods_id [商品id] * @return [array] [boolean | msg] */ public static function GoodsSpecificationsInsert($data, $goods_id) { // 删除原来的数据 Db::name('GoodsSpecType')->where(['goods_id'=>$goods_id])->delete(); Db::name('GoodsSpecValue')->where(['goods_id'=>$goods_id])->delete(); Db::name('GoodsSpecBase')->where(['goods_id'=>$goods_id])->delete(); // 类型 if(!empty($data['title'])) { foreach($data['title'] as &$v) { $spec = []; foreach($v['value'] as $vs) { $spec[] = [ 'name' => $vs, 'images' => isset($data['images'][$vs]) ? ResourcesService::AttachmentPathHandle($data['images'][$vs]) : '', ]; } $v['goods_id'] = $goods_id; $v['value'] = json_encode($spec, JSON_UNESCAPED_UNICODE); $v['add_time'] = time(); } if(Db::name('GoodsSpecType')->insertAll($data['title']) < count($data['title'])) { return DataReturn(MyLang('common_service.goods.save_spec_type_add_fail_tips'), -1); } } // 基础/规格值 if(!empty($data['data'])) { // 基础字段 $count = count($data['data'][0]); $temp_key = self::GoodsSpecBaseFields(); $key_count = count($temp_key); // 等于key总数则只有一列基础规格 if($count == $key_count) { $temp_data = [ 'goods_id' => $goods_id, 'add_time' => time(), ]; for($i=0; $i<$count; $i++) { $temp_data[$temp_key[$i]] = $data['data'][0][$i]; } // 获取仓库规格库存 $temp_data['inventory'] = WarehouseGoodsService::WarehouseGoodsSpecInventory($goods_id); // 规格基础添加 if(Db::name('GoodsSpecBase')->insertGetId($temp_data) <= 0) { return DataReturn(MyLang('common_service.goods.save_spec_base_add_fail_tips'), -1); } // 多规格操作 } else { $base_start = $count-$key_count; foreach($data['data'] as $v) { $temp_value = []; $temp_data = [ 'goods_id' => $goods_id, 'add_time' => time(), ]; for($i=0; $i<$count; $i++) { if($i < $base_start) { $temp_value[] = [ 'goods_id' => $goods_id, 'value' => $v[$i], 'md5_key' => empty($v[$i]) ? '' : md5($v[$i]), 'add_time' => time() ]; } else { $temp_data[$temp_key[$i-$base_start]] = $v[$i]; } } // 获取仓库规格库存 $temp_data['inventory'] = WarehouseGoodsService::WarehouseGoodsSpecInventory($goods_id, implode('', array_column($temp_value, 'value'))); // 规格基础添加 $base_id = Db::name('GoodsSpecBase')->insertGetId($temp_data); if(empty($base_id)) { return DataReturn(MyLang('common_service.goods.save_spec_base_add_fail_tips'), -1); } // 规格值添加 foreach($temp_value as &$value) { $value['goods_spec_base_id'] = $base_id; } if(Db::name('GoodsSpecValue')->insertAll($temp_value) < count($temp_value)) { return DataReturn(MyLang('common_service.goods.save_spec_value_add_fail_tips'), -1); } } } } return DataReturn(MyLang('insert_success'), 0); } /** * 商品规格基础数据字段 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2025-09-16 * @desc description * @param [array] $params [输入参数] */ public static function GoodsSpecBaseFields($params = []) { return [ 'price', 'original_price', 'buy_min_number', 'buy_max_number', 'weight', 'volume', 'coding', 'barcode', 'inventory_unit', 'extends', ]; } /** * 商品删除 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @datetime 2018-12-07T00:24:14+0800 * @param [array] $params [输入参数] */ public static function GoodsDelete($params = []) { // 参数是否有误 if(empty($params['ids'])) { return DataReturn(MyLang('data_id_error_tips'), -1); } // 是否数组 if(!is_array($params['ids'])) { $params['ids'] = explode(',', $params['ids']); } // 启动事务 Db::startTrans(); // 捕获异常 try { // 删除商品操作 self::GoodsDeleteHandle($params['ids'], $params); // 商品删除钩子 $hook_name = 'plugins_service_goods_delete'; MyEventTrigger($hook_name, [ 'hook_name' => $hook_name, 'is_backend' => true, 'params' => $params, 'goods_ids' => $params['ids'], ]); // 提交事务 Db::commit(); return DataReturn(MyLang('delete_success'), 0); } catch(\Exception $e) { Db::rollback(); return DataReturn($e->getMessage(), -1); } } /** * 商品删除操作 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2021-01-27 * @desc description * @param [array] $goods_ids [商品id] * @param [array] $params [输入参数] */ public static function GoodsDeleteHandle($goods_ids, $params = []) { // 是否删除附件 $del_attachment_data = []; $is_del_images = isset($params['is_del_images']) && $params['is_del_images'] == 1; if($is_del_images) { // 商品主图 $goods_images = Db::name('Goods')->where([['id', 'in', $goods_ids], ['images', '<>', '']])->column('images'); if(!empty($goods_images)) { $del_attachment_data = array_unique(array_merge($del_attachment_data, $goods_images)); } // 商品视频 $goods_video = Db::name('Goods')->where([['id', 'in', $goods_ids], ['video', '<>', '']])->column('video'); if(!empty($goods_video)) { $del_attachment_data = array_unique(array_merge($del_attachment_data, $goods_video)); } // 商品分享图 $goods_share_images = Db::name('Goods')->where([['id', 'in', $goods_ids], ['share_images', '<>', '']])->column('share_images'); if(!empty($goods_share_images)) { $del_attachment_data = array_unique(array_merge($del_attachment_data, $goods_share_images)); } // 商品规格图 $goods_spec_type = Db::name('GoodsSpecType')->where(['goods_id'=>$goods_ids])->column('value'); if(!empty($goods_spec_type)) { foreach($goods_spec_type as $v) { $v = json_decode($v, true); if(!empty($v) && is_array($v)) { $temp = array_unique(array_filter(array_column($v, 'images'))); if(!empty($temp)) { $del_attachment_data = array_unique(array_merge($del_attachment_data, $temp)); } } } } // 商品相册 $goods_photo = Db::name('GoodsPhoto')->where(['goods_id'=>$goods_ids])->column('images'); if(!empty($goods_photo)) { $del_attachment_data = array_unique(array_merge($del_attachment_data, $goods_photo)); } // 商品详情内容 $goods_content = Db::name('Goods')->where([['id', 'in', $goods_ids], ['content_web', '<>', '']])->column('content_web'); if(!empty($goods_content)) { foreach($goods_content as $v) { $temp = ResourcesService::RichTextMatchContentAttachment($v, 'goods', 'images'); if(!empty($temp)) { $del_attachment_data = array_unique(array_merge($del_attachment_data, $temp)); } $temp = ResourcesService::RichTextMatchContentAttachment($v, 'goods', 'video'); if(!empty($temp)) { $del_attachment_data = array_unique(array_merge($del_attachment_data, $temp)); } $temp = ResourcesService::RichTextMatchContentAttachment($v, 'goods', 'file'); if(!empty($temp)) { $del_attachment_data = array_unique(array_merge($del_attachment_data, $temp)); } } } // 商品虚拟内容 $goods_fictitious = Db::name('Goods')->where([['id', 'in', $goods_ids], ['fictitious_goods_value', '<>', '']])->column('fictitious_goods_value'); if(!empty($goods_fictitious)) { foreach($goods_fictitious as $v) { $temp = ResourcesService::RichTextMatchContentAttachment($v, 'goods', 'images'); if(!empty($temp)) { $del_attachment_data = array_unique(array_merge($del_attachment_data, $temp)); } $temp = ResourcesService::RichTextMatchContentAttachment($v, 'goods', 'video'); if(!empty($temp)) { $del_attachment_data = array_unique(array_merge($del_attachment_data, $temp)); } $temp = ResourcesService::RichTextMatchContentAttachment($v, 'goods', 'file'); if(!empty($temp)) { $del_attachment_data = array_unique(array_merge($del_attachment_data, $temp)); } } } // 商品使用指南内容 $goods_use_guide = Db::name('Goods')->where([['id', 'in', $goods_ids], ['use_guide', '<>', '']])->column('use_guide'); if(!empty($goods_use_guide)) { foreach($goods_use_guide as $v) { $temp = ResourcesService::RichTextMatchContentAttachment($v, 'goods', 'images'); if(!empty($temp)) { $del_attachment_data = array_unique(array_merge($del_attachment_data, $temp)); } $temp = ResourcesService::RichTextMatchContentAttachment($v, 'goods', 'video'); if(!empty($temp)) { $del_attachment_data = array_unique(array_merge($del_attachment_data, $temp)); } $temp = ResourcesService::RichTextMatchContentAttachment($v, 'goods', 'file'); if(!empty($temp)) { $del_attachment_data = array_unique(array_merge($del_attachment_data, $temp)); } } } // 商品app $goods_app = Db::name('GoodsContentApp')->where([['goods_id', 'in', $goods_ids], ['images', '<>', '']])->column('images'); if(!empty($goods_app)) { $del_attachment_data = array_unique(array_merge($del_attachment_data, $goods_app)); } } // 删除商品 if(Db::name('Goods')->where(['id'=>$goods_ids])->delete() === false) { throw new \Exception(MyLang('common_service.goods.delete_goods_fail_tips')); } // 商品规格 if(Db::name('GoodsSpecType')->where(['goods_id'=>$goods_ids])->delete() === false) { throw new \Exception(MyLang('common_service.goods.delete_spec_type_fail_tips')); } if(Db::name('GoodsSpecValue')->where(['goods_id'=>$goods_ids])->delete() === false) { throw new \Exception(MyLang('common_service.goods.delete_spec_value_fail_tips')); } if(Db::name('GoodsSpecBase')->where(['goods_id'=>$goods_ids])->delete() === false) { throw new \Exception(MyLang('common_service.goods.delete_spec_base_fail_tips')); } // 关联分类 if(Db::name('GoodsCategoryJoin')->where(['goods_id'=>$goods_ids])->delete() === false) { throw new \Exception(MyLang('common_service.goods.delete_goods_category_fail_tips')); } // 相册 if(Db::name('GoodsPhoto')->where(['goods_id'=>$goods_ids])->delete() === false) { throw new \Exception(MyLang('common_service.goods.delete_goods_photo_fail_tips')); } // app内容 if(Db::name('GoodsContentApp')->where(['goods_id'=>$goods_ids])->delete() === false) { throw new \Exception(MyLang('common_service.goods.delete_app_content_fail_tips')); } // 商品参数 if(Db::name('GoodsParams')->where(['goods_id'=>$goods_ids])->delete() === false) { throw new \Exception(MyLang('common_service.goods.delete_params_fail_tips')); } // 商品关联仓库信息+库存 if(Db::name('WarehouseGoods')->where(['goods_id'=>$goods_ids])->delete() === false) { throw new \Exception(MyLang('common_service.goods.delete_warehouse_goods_fail_tips')); } if(Db::name('WarehouseGoodsSpec')->where(['goods_id'=>$goods_ids])->delete() === false) { throw new \Exception(MyLang('common_service.goods.delete_warehouse_goods_spec_fail_tips')); } // 是否删除图片 if($is_del_images && !empty($del_attachment_data)) { AttachmentService::AttachmentUrlDelete($del_attachment_data); } } /** * 商品状态更新 * @author Devil * @blog http://gong.gg/ * @version 0.0.1 * @datetime 2016-12-06T21:31:53+0800 * @param [array] $params [输入参数] */ public static function GoodsStatusUpdate($params = []) { // 请求参数 $p = [ [ 'checked_type' => 'empty', 'key_name' => 'id', 'error_msg' => MyLang('data_id_error_tips'), ], [ 'checked_type' => 'empty', 'key_name' => 'field', 'error_msg' => MyLang('operate_field_error_tips'), ], [ 'checked_type' => 'in', 'key_name' => 'state', 'checked_data' => [0,1], 'error_msg' => MyLang('form_status_range_message'), ], ]; $ret = ParamsChecked($params, $p); if($ret !== true) { return DataReturn($ret, -1); } // 启动事务 Db::startTrans(); // 捕获异常 try { // 基础参数 $goods_id = intval($params['id']); $field = $params['field']; $status = intval($params['state']); // 数据更新 if(!Db::name('Goods')->where(['id'=>$goods_id])->update([$field=>$status, 'upd_time'=>time()])) { throw new \Exception(MyLang('operate_fail')); } // 商品状态更新钩子 $hook_name = 'plugins_service_goods_field_status_update'; MyEventTrigger($hook_name, [ 'hook_name' => $hook_name, 'is_backend' => true, 'params' => $params, 'goods_id' => $goods_id, 'field' => $field, 'status' => $status, ]); // 提交事务 Db::commit(); return DataReturn(MyLang('operate_success'), 0); } catch(\Exception $e) { Db::rollback(); return DataReturn($e->getMessage(), -1); } } /** * 获取商品编辑规格 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-12-14 * @desc description * @param [int] $goods_id [商品id] */ public static function GoodsEditSpecifications($goods_id) { $where = ['goods_id'=>$goods_id]; // 获取规格类型 $type = Db::name('GoodsSpecType')->where($where)->order('id asc')->field('id,name,value')->select()->toArray(); $value = []; if(!empty($type)) { // 数据处理 foreach($type as &$temp_type) { $temp_type_value = json_decode($temp_type['value'], true); foreach($temp_type_value as &$vs) { $vs['images'] = ResourcesService::AttachmentPathViewHandle($vs['images']); } $temp_type['value'] = $temp_type_value; } // 获取规格值 $temp_value = Db::name('GoodsSpecValue')->where($where)->field('goods_spec_base_id,value')->order('id asc')->select()->toArray(); if(!empty($temp_value)) { foreach($temp_value as $value_v) { $key = ''; foreach($type as $type_v) { foreach($type_v['value'] as $type_vs) { if(trim($type_vs['name']) == trim($value_v['value'])) { $key = $type_v['id']; break; } } } if(!empty($key)) { $value[$value_v['goods_spec_base_id']][] = [ 'data_type' => 'spec', 'data' => [ 'key' => $key, 'value' => trim($value_v['value']), ], ]; } } } if(!empty($value)) { foreach($value as $k=>&$v) { $base = Db::name('GoodsSpecBase')->find($k); $base['weight'] = PriceBeautify($base['weight']); $base['volume'] = PriceBeautify($base['volume']); $v[] = [ 'data_type' => 'base', 'data' => $base, ]; } } } else { $base = Db::name('GoodsSpecBase')->where($where)->find(); if(!empty($base)) { $base['weight'] = PriceBeautify($base['weight']); $base['volume'] = PriceBeautify($base['volume']); $value[] = [ [ 'data_type' => 'base', 'data' => $base, ] ]; } } return [ 'type' => $type, 'value' => array_values($value), ]; } /** * 获取商品编辑参数 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2020-08-31 * @desc description * @param [int] $goods_id [商品id] * @param [array] $data [指定参数数据] */ public static function GoodsEditParameters($goods_id = 0, $data = []) { $data = empty($data) ? Db::name('GoodsParams')->where(['goods_id'=>$goods_id])->order('id asc')->select()->toArray() : $data; if(!empty($data)) { foreach($data as &$v) { $v['key'] = (isset($v['scope']) && isset($v['name']) && isset($v['data_type'])) ? md5($v['scope'].$v['name'].$v['data_type']) : ''; if(isset($v['value']) && isset($v['data_type']) && in_array($v['data_type'], [2])) { $v['value'] = explode(',', $v['value']); } } $data = array_column($data, null, 'key'); } return $data; } /** * 商品规格信息 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-12-14 * @desc description * @param [array] $params [输入参数] */ public static function GoodsSpecDetail($params = []) { // 请求参数 $p = [ [ 'checked_type' => 'empty', 'key_name' => 'id', 'error_msg' => MyLang('goods_id_error_tips'), ], [ 'checked_type' => 'empty', 'key_name' => 'spec', 'is_checked' => 1, 'error_msg' => MyLang('common_service.goods.base_spec_not_choice_tips'), ], ]; $ret = ParamsChecked($params, $p); if($ret !== true) { return DataReturn($ret, -1); } // 条件 $goods_id = intval($params['id']); $where = [ 'goods_id' => $goods_id, ]; // 规格数据 // 规格不为数组则为json字符串 $spec = []; if(!empty($params['spec'])) { if(!is_array($params['spec'])) { $params['spec'] = json_decode(htmlspecialchars_decode($params['spec']), true); } $spec = array_column($params['spec'], 'value'); } // 规格基础静态临时存储 static $goods_service_goods_spec_base_static_data = []; $key = $goods_id.(empty($spec) ? '' : md5(json_encode($spec, JSON_UNESCAPED_UNICODE))); if(array_key_exists($key, $goods_service_goods_spec_base_static_data)) { $base = Db::name('GoodsSpecBase')->find($goods_service_goods_spec_base_static_data[$key]); } else { // 商品信息 $info = Db::name('Goods')->where(['id'=>$goods_id])->field('id,title,is_exist_many_spec')->find(); if(empty($info)) { return DataReturn('【'.$goods_id.'】'.MyLang('no_goods'), -1); } // 规格值校验处理 $base = []; if(empty($spec)) { // 没有指定规格、但是商品已存在规则则报错 if($info['is_exist_many_spec'] == 1) { return DataReturn('【'.$info['title'].'】'.MyLang('common_service.goods.base_spec_not_choice_tips'), -1); } // 单个规则则直接获取规格基础 $base = Db::name('GoodsSpecBase')->where($where)->find(); } else { // 指定规格规格、但是商品没有规格则报错 if($info['is_exist_many_spec'] == 0) { return DataReturn('【'.$info['title'].'】'.MyLang('common_service.goods.base_spec_empty_tips'), -1); } // 获取规格值基础值id $where['value'] = $spec; $ids = Db::name('GoodsSpecValue')->where($where)->column('goods_spec_base_id'); if(!empty($ids)) { // 根据基础值id获取规格值列表 $temp_data = Db::name('GoodsSpecValue')->where(['goods_spec_base_id'=>$ids])->field('goods_spec_base_id,value')->order('id asc')->select()->toArray(); if(!empty($temp_data)) { // 根据基础值id分组 $data = []; foreach($temp_data as $v) { $data[$v['goods_spec_base_id']][] = $v; } // 从条件中匹配对应的规格值得到最终的基础值id $base_id = 0; $spec_str = implode('', array_column($params['spec'], 'value')); foreach($data as $value_v) { $temp_str = implode('', array_column($value_v, 'value')); if($temp_str == $spec_str) { $base_id = $value_v[0]['goods_spec_base_id']; break; } } // 获取基础值数据 if(!empty($base_id)) { $base = Db::name('GoodsSpecBase')->find($base_id); } } } } if(!empty($base)) { $goods_service_goods_spec_base_static_data[$key] = $base['id']; } } // 是否有规格 if(!empty($base)) { // 单位 .00 处理 $base['weight'] = PriceBeautify($base['weight']); // 处理好的数据 // 扩展元素标记与html内容数据 // extends_element下包含多个元素 ['element'=>'', 'content'=>''] $data = [ 'spec_base' => $base, 'extends_element' => [], ]; // 商品获取规格钩子 $hook_name = 'plugins_service_goods_spec_base'; $ret = EventReturnHandle(MyEventTrigger($hook_name, [ 'hook_name' => $hook_name, 'is_backend' => true, 'params' => $params, 'spec' => $spec, 'data' => &$data, 'goods_id' => $goods_id ])); if(isset($ret['code']) && $ret['code'] != 0) { return $ret; } return DataReturn(MyLang('operate_success'), 0, $data); } return DataReturn(MyLang('common_service.goods.base_spec_empty_tips'), -100); } /** * 商品规格类型 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-12-14 * @desc description * @param [array] $params [输入参数] */ public static function GoodsSpecType($params = []) { // 请求参数 $p = [ [ 'checked_type' => 'empty', 'key_name' => 'id', 'error_msg' => MyLang('goods_id_error_tips'), ], [ 'checked_type' => 'empty', 'key_name' => 'spec', 'error_msg' => MyLang('common_service.goods.base_spec_not_choice_tips'), ], ]; $ret = ParamsChecked($params, $p); if($ret !== true) { return DataReturn($ret, -1); } // 条件 $goods_id = intval($params['id']); $where = [ 'goods_id' => intval($params['id']), ]; // 规格不为数组则为json字符串 if(!is_array($params['spec'])) { $params['spec'] = json_decode(htmlspecialchars_decode($params['spec']), true); } $where['value'] = array_column($params['spec'], 'value'); // 获取规格值基础值id $ids = Db::name('GoodsSpecValue')->where($where)->column('goods_spec_base_id'); if(!empty($ids)) { // 根据基础值id获取规格值列表 $temp_data = Db::name('GoodsSpecValue')->where(['goods_spec_base_id'=>$ids])->field('goods_spec_base_id,value')->order('id asc')->select()->toArray(); if(!empty($temp_data)) { // 根据基础值id分组 $group = []; foreach($temp_data as $v) { $group[$v['goods_spec_base_id']][] = $v; } // 获取当前操作元素索引 $index = count($params['spec'])-1; $spec_str = implode('', array_column($params['spec'], 'value')); $spec_type = []; foreach($group as $v) { $temp_str = implode('', array_column($v, 'value')); if(isset($v[$index+1]) && stripos($temp_str, $spec_str) !== false) { // 判断是否还有库存 $inventory = Db::name('GoodsSpecBase')->where(['id'=>$v[$index+1]['goods_spec_base_id']])->value('inventory'); if($inventory > 0) { $spec_type[$v[$index+1]['value']] = $v[$index+1]['value']; } } } // 处理好的数据 // 扩展元素标记与html内容数据 // extends_element下包含多个元素 ['element'=>'', 'content'=>''] $data = [ 'spec_type' => array_values($spec_type), 'extends_element' => [], ]; // 商品获取规格类型钩子 $hook_name = 'plugins_service_goods_spec_type'; $ret = EventReturnHandle(MyEventTrigger($hook_name, [ 'hook_name' => $hook_name, 'is_backend' => true, 'params' => $params, 'data' => &$data, 'goods_id' => $goods_id ])); if(isset($ret['code']) && $ret['code'] != 0) { return $ret; } return DataReturn(MyLang('operate_success'), 0, $data); } } return DataReturn(MyLang('common_service.goods.base_spec_type_empty_tips'), -100); } /** * 商品购买数量获取商品信息 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2021-10-05 * @desc description * @param [array] $params [输入参数] */ public static function GoodsStock($params = []) { // 是否批量 if(!empty($params['goods_data'])) { // 是否数组 if(!is_array($params['goods_data'])) { $params['goods_data'] = json_decode(htmlspecialchars_decode($params['goods_data']), true); } if(empty($params['goods_data'])) { return DataReturn(MyLang('params_error_tips'), -1); } // 循环处理 $result = []; foreach($params['goods_data'] as $v) { // 请求参数 $p = [ [ 'checked_type' => 'empty', 'key_name' => 'id', 'error_msg' => MyLang('goods_id_error_tips'), ], [ 'checked_type' => 'isset', 'key_name' => 'stock', 'error_msg' => MyLang('common_service.goods.base_buy_stock_error_tips'), ], ]; $ret = ParamsChecked($v, $p); if($ret !== true) { return DataReturn($ret, -1); } // 获取商品基础信息 $result[] = self::GoodsSpecDetail($v); } return DataReturn(MyLang('operate_success'), 0, $result); } else { // 请求参数 $p = [ [ 'checked_type' => 'empty', 'key_name' => 'id', 'error_msg' => MyLang('goods_id_error_tips'), ], [ 'checked_type' => 'isset', 'key_name' => 'stock', 'error_msg' => MyLang('common_service.goods.base_buy_stock_error_tips'), ], ]; $ret = ParamsChecked($params, $p); if($ret !== true) { return DataReturn($ret, -1); } // 获取商品基础信息 return self::GoodsSpecDetail($params); } } /** * 商品规格扩展数据 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @datetime 2019-07-21T16:08:34+0800 * @param [array] $params [输入参数] */ public static function GoodsSpecificationsExtends($params = []) { // 数据 $data = []; // 规格扩展数据钩子 $hook_name = 'plugins_service_goods_spec_extends_handle'; MyEventTrigger($hook_name, [ 'hook_name' => $hook_name, 'is_backend' => true, 'params' => $params, 'data' => &$data, ]); return DataReturn(MyLang('get_success'), 0, $data); } /** * 商品类型校验 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2020-07-03 * @desc description * @param [int] $goods_id [商品 id] * @param [int] $site_type [商品类型] */ public static function IsGoodsSiteTypeConsistent($goods_id, $site_type = null) { // 是否已指定商品类型 if($site_type === null) { $site_type = Db::name('Goods')->where(['id'=>$goods_id])->value('site_type'); } // 是否展示型商品 if($site_type == 4) { return DataReturn(MyLang('goods_only_show_title'), -1, $site_type); } // 商品类型与当前系统的类型是否一致包含其中 if(IsGoodsSiteTypeConsistent($site_type) == 1) { return DataReturn('success', 0, $site_type); } // 仅可单独购买 $site_type_arr = MyConst('common_site_type_list'); $msg = array_key_exists($site_type, $site_type_arr) ? MyLang('only_title').$site_type_arr[$site_type]['name'] : MyLang('goods_only_buy_title'); return DataReturn($msg, -1, $site_type); } /** * 商品销售默认类型 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2020-07-03 * @desc description * @param [int] $goods_id [商品 id] * @param [int] $site_type [商品类型] */ public static function GoodsSalesModelType($goods_id, $site_type = null) { // 是否已指定商品类型 if($site_type === null) { $site_type = Db::name('Goods')->where(['id'=>$goods_id])->value('site_type'); } // 匹配商品销售模式 return DataReturn('success', 0, GoodsSalesModelType($site_type)); } /** * 商品底部左侧小导航 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2021-02-19 * @desc description * @param [array] $goods [商品信息] */ public static function GoodsBuyLeftNavList($goods) { // 当前用户是否已收藏 $user_is_favor = isset($goods['user_is_favor']) && $goods['user_is_favor'] == 1; // 导航数据 // name 名称 // icon icon图标 // type 类型 // url url跳转地址 // active 是否选中(0否、1是) // class 自定义class // document 元素数据 $data = [ [ 'name' => MyLang('home_title'), 'icon' => (APPLICATION == 'web') ? 'iconfont icon-index' : StaticAttachmentUrl('home-icon.png'), 'type' => 'home', 'url' => SystemService::DomainUrl(), 'active' => 0, 'class' => '', 'document' => '', ], [ 'name' => $user_is_favor ? MyLang('already_favor_title') : MyLang('favor_title'), 'icon' => (APPLICATION == 'web') ? ('iconfont '.($user_is_favor ? 'icon-heart' : 'icon-heart-o')) : StaticAttachmentUrl('favor'.($user_is_favor ? '-active' : '').'-icon.png'), 'type' => 'favor', 'active' => $user_is_favor ? 1 : 0, 'class' => '', 'document' => '', ] ]; // 手机端是否存在客服 if(APPLICATION == 'app' && MyC('common_app_is_online_service') == 1) { // 是否存在自定义客服 $custom = MyC('common_app_customer_service_custom', []); if(empty($custom) || empty($custom[APPLICATION_CLIENT_TYPE])) { // h5,ios,android端必须存在电话 $status = in_array(APPLICATION_CLIENT_TYPE, ['h5', 'ios', 'android']); if($status) { $tel = MyC('common_app_customer_service_tel'); $status = !empty($tel); } else { $status = true; } } else { $status = true; } if($status) { $data[] = [ 'name' => MyLang('chat_title'), 'icon' => StaticAttachmentUrl('chat-icon.png'), 'type' => 'chat', ]; } } // 商品购买导航左侧钩子 $hook_name = 'plugins_service_goods_buy_left_nav_handle'; MyEventTrigger($hook_name, [ 'hook_name' => $hook_name, 'is_backend' => true, 'goods' => $goods, 'data' => &$data, ]); return $data; } /** * 商品购买按钮列表 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2021-02-19 * @desc description * @param [array] $goods [商品信息] */ public static function GoodsBuyButtonList($goods) { // 错误信息 $error = ''; // 是否已下架 if(isset($goods['is_shelves']) && $goods['is_shelves'] != 1) { $error = MyLang('goods_already_shelves_title'); } // 按钮列表 // color 颜色类型[main主, second次](默认 main) // type 类型[show展示, buy购买, cart加入购物车, other其他值] // name 名称 // title 元素title说明(可选) // value 数据值(可选) // icon icon类名称(可选) // class 自定义类名称(可选) // business 业务 $data = []; if(empty($error)) { if(isset($goods['id']) && isset($goods['site_type'])) { // 获取商品类型 $model_type = self::GoodsSalesModelType($goods['id'], $goods['site_type']); // 是否展示型 if($model_type['data'] == 4) { $name = MyC('common_is_exhibition_mode_btn_text'); $data[] = [ 'color' => 'main', 'type' => 'show', 'name' => empty($name) ? MyLang('goods_show_title') : $name, 'value' => MyC('common_customer_store_chat_tel'), 'icon' => 'am-icon-phone', ]; $error = MyLang('goods_only_show_title'); } else { // 还有库存 if(isset($goods['inventory']) && $goods['inventory'] <= 0) { $error = MyLang('goods_no_inventory_title'); } if(empty($error)) { // web端class $class_name = (APPLICATION == 'web') ? 'buy-event login-event' : ''; // 购买 $name = (MyC('common_order_is_booking', 0, true) == 1) ? MyLang('goods_booking_title') : MyLang('goods_buy_title'); $buy = [ 'color' => 'main', 'type' => 'buy', 'title' => $name, 'name' => $name, 'class' => $class_name, 'icon' => '', ]; // 商品类型是否和当前站点类型一致 $cart = []; $ret = self::IsGoodsSiteTypeConsistent($goods['id'], $goods['site_type']); if($ret['code'] == 0) { // 加入购物车 $name = MyLang('goods_cart_title'); $cart = [ 'color' => 'second', 'type' => 'cart', 'title' => $name, 'name' => $name, 'class' => $class_name, 'icon' => 'am-icon-opencart', ]; } else { $error = $ret['msg']; } // 是否关闭下单按钮 $close_buy_button = MyC('common_goods_close_buy_button'); if(!empty($close_buy_button) && is_array($close_buy_button)) { if(in_array('cart', $close_buy_button)) { $cart = []; } if(in_array('buy', $close_buy_button)) { $buy = []; } } // 按钮加到数据 if(!empty($buy)) { $data[] = $buy; } if(!empty($cart)) { $data[] = $cart; } // 主按钮顺序处理,手机端立即购买放在最后面 if(APPLICATION == 'app') { $data = array_reverse($data); } } } } } // 商品购买导航按钮钩子 $hook_name = 'plugins_service_goods_buy_nav_button_handle'; MyEventTrigger($hook_name, [ 'hook_name' => $hook_name, 'is_backend' => true, 'goods' => $goods, 'data' => &$data, 'error' => &$error, ]); // 是否存在按钮数据 if(empty($data) && empty($error)) { $error = MyLang('goods_stop_sale_title'); } // 返回数据 $count = 0; $types = []; if(!empty($data) && is_array($data)) { $count = count($data); $types = array_column($data, 'type'); } return [ 'data' => $data, 'count' => $count, 'error' => $error, 'is_buy' => in_array('buy', $types) ? 1 : 0, 'is_cart' => in_array('cart', $types) ? 1 : 0, 'is_show' => in_array('show', $types) ? 1 : 0, ]; } /** * 商品详情中间tabs导航列表 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2021-02-19 * @desc description * @param [array] $goods [商品信息] */ public static function GoodsDetailMiddleTabsNavList($goods) { // 从缓存获取 $key = SystemService::CacheKey('shopxo.cache_goods_detail_middle_tabs_key').APPLICATION; $data = MyCache($key); if($data === null || MyEnv('app_debug') || MyC('common_data_is_use_cache') != 1) { // 是否展示商品评价 $is_comments = MyC('common_is_goods_detail_show_comments', 1); // app与web端不一致 if(APPLICATION == 'app') { // 这里的 ent 值必须和系统中区域块定义的一致 $data = [ [ 'type' => 'main', 'name' => MyLang('goods_main_title'), 'ent' => '.page', ], ]; // 是否展示商品评价 if($is_comments == 1) { $data[] = [ 'type' => 'comments', 'name' => MyLang('comment_title'), 'ent' => '.goods-comment', ]; } // 商品详情介绍 $data[] = [ 'type' => 'detail', 'name' => MyLang('detail_title'), 'ent' => '.goods-detail', ]; } else { // 评论总数 $comments_count = isset($goods['comments_count']) ? $goods['comments_count'] : GoodsCommentsService::GoodsCommentsTotal(['goods_id'=>$goods['id'], 'is_show'=>1]); // 列表 // type 类型 // name 名称 // active 选中(可选) // value 数据值(可选) $data = [ [ 'type' => 'detail', 'name' => MyLang('detail_title'), 'active' => 1, ], ]; // 是否展示商品评价 if($is_comments == 1) { $data[] = [ 'type' => 'comments', 'name' => MyLang('comment_title').'('.$comments_count.')', ]; } // 猜你喜欢,目前以销量最高推荐 if(MyC('common_is_goods_detail_show_guess_you_like', 0) == 1) { $data[] = [ 'type' => 'guess_you_like', 'name' => MyLang('goods_guess_you_like_title'), ]; } } // 商品详情中间导航钩子 $hook_name = 'plugins_service_goods_detail_middle_tabs_nav_handle'; MyEventTrigger($hook_name, [ 'hook_name' => $hook_name, 'is_backend' => true, 'goods' => $goods, 'data' => &$data, ]); // 格式集合 $data = [ 'nav' => $data, 'type' => array_column($data, 'type'), ]; // 存储缓存 MyCache($key, $data, 180); } return $data; } /** * 商品二维码生成 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2024-09-05 * @desc description * @param [array] $goods [商品数据] * @param [array] $user [用户数据] */ public static function GoodsQrcode($goods = [], $user = []) { // 数据 if(empty($goods)) { return DataReturn(MyLang('no_data'), -1); } // pc地址 $url_params = ['id'=>$goods['id']]; if(!empty($user)) { $url_params['referrer'] = $user['id']; } $web_url = MyUrl('index/goods/index', $url_params); // h5地址 $h5_url = MyC('common_app_h5_url'); // 生成各平台二维码 $qrcode = []; $platform = MyConst('common_platform_type'); if(!empty($platform) && is_array($platform)) { // 自定义路径和名称 $time_dir = date('Y/m/d', is_numeric($goods['add_time']) ? $goods['add_time'] : strtotime($goods['add_time'])).(empty($user) ? '' : '/'.$user['id']); $filename = $goods['id'].'.png'; // 地址信息 $page = 'pages/goods-detail/goods-detail'; $query = 'id='.$goods['id']; if(!empty($user)) { $query .= '&referrer='.$user['id']; } // h5地址拼接 if(!empty($h5_url)) { $h5_url .= $page.'?'.$query; } foreach($platform as $v) { // 存储信息 $path = 'download'.DS.'goods_qrcode'.DS.APPLICATION_CLIENT_TYPE.DS.$v['value'].DS.$time_dir.DS; // 二维码处理参数 $dir_params = [ 'path' => DS.$path, 'filename' => $filename, 'dir' => ROOT.'public'.DS.$path.$filename, ]; $status = false; if(!file_exists($dir_params['dir'])) { // 根据平台处理 switch($v['value']) { // pc case 'pc' : $ret = (new \base\Qrcode())->Create(array_merge($dir_params, ['content'=>$web_url])); if($ret['code'] == 0) { $status = true; } break; // h5 case 'h5' : if(!empty($h5_url)) { $ret = (new \base\Qrcode())->Create(array_merge($dir_params, ['content'=>$h5_url])); if($ret['code'] == 0) { $status = true; } } break; // 微信 case 'weixin' : $appid = AppMiniUserService::AppMiniConfig('common_app_mini_weixin_appid'); $appsecret = AppMiniUserService::AppMiniConfig('common_app_mini_weixin_appsecret'); if(!empty($appid) && !empty($appsecret)) { $request_params = [ 'page' => $page, 'scene' => $query, 'width' => 300, ]; $ret = (new \base\Wechat($appid, $appsecret))->MiniQrCodeCreate($request_params); if($ret['code'] == 0) { if(\base\FileUtil::CreateDir(ROOT.'public'.DS.$path)) { if(@file_put_contents($dir_params['dir'], $ret['data']) !== false) { $status = true; } } } } break; // 支付宝小程序 case 'alipay' : $appid = AppMiniUserService::AppMiniConfig('common_app_mini_alipay_appid'); if(!empty($appid)) { $request_params = [ 'appid' => $appid, 'page' => $page, 'scene' => $query, 'width' => 300, ]; $ret = (new \base\Alipay())->MiniQrCodeCreate($request_params); if($ret['code'] == 0) { if(\base\FileUtil::CreateDir(ROOT.'public'.DS.$path)) { if(@file_put_contents($dir_params['dir'], RequestGet($ret['data'])) !== false) { $status = true; } } } } break; // 头条小程序 case 'toutiao' : $config = [ 'appid' => AppMiniUserService::AppMiniConfig('common_app_mini_toutiao_appid'), 'secret' => AppMiniUserService::AppMiniConfig('common_app_mini_toutiao_appsecret'), ]; if(!empty($config['appid']) && !empty($config['secret'])) { $request_params = [ 'page' => $page, 'scene' => $query, 'width' => 300, ]; $ret = (new \base\Toutiao($config))->MiniQrCodeCreate($request_params); if($ret['code'] == 0) { if(\base\FileUtil::CreateDir(ROOT.'public'.DS.$path)) { if(@file_put_contents($dir_params['dir'], $ret['data']) !== false) { $status = true; } } } } break; // 百度小程序 case 'baidu' : $config = [ 'appid' => AppMiniUserService::AppMiniConfig('common_app_mini_baidu_appid'), 'key' => AppMiniUserService::AppMiniConfig('common_app_mini_baidu_appkey'), 'secret' => AppMiniUserService::AppMiniConfig('common_app_mini_baidu_appsecret'), ]; if(!empty($config['appid']) && !empty($config['key']) && !empty($config['secret'])) { $request_params = [ 'page' => $page, 'scene' => $query, 'width' => 300, ]; $ret = (new \base\Baidu($config))->MiniQrCodeCreate($request_params); if($ret['code'] == 0) { if(\base\FileUtil::CreateDir(ROOT.'public'.DS.$path)) { if(@file_put_contents($dir_params['dir'], $ret['data']) !== false) { $status = true; } } } } break; // 快手小程序 case 'kuaishou' : $appid = AppMiniUserService::AppMiniConfig('common_app_mini_kuaishou_appid'); if(!empty($appid)) { $url = 'kwai://miniapp?appId='.$appid.'&KSMP_source=011012&KSMP_internal_source=011012&path='.urlencode($page.'?'.$query); $ret = (new \base\Qrcode())->Create(array_merge($dir_params, ['content'=>$url])); if($ret['code'] == 0) { $status = true; } } break; } } else { $status = true; } if($status) { if(($v['value'] == 'h5' && !empty($h5_url)) || $v['value'] != 'h5') { $qrcode[] = [ 'name' => $v['name'], 'type' => $v['value'], 'url' => ($v['value'] == 'h5') ? $h5_url : (($v['value'] == 'pc') ? $web_url : ''), 'qrcode' => ResourcesService::AttachmentPathViewHandle($dir_params['path'].$dir_params['filename']), ]; } } } } return DataReturn('success', 0, [ 'qrcode' => $qrcode, 'h5_url' => $h5_url, 'web_url' => $web_url, ]); } /** * 商品url生成 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2022-02-12 * @desc description * @param [int] $goods_id [商品id] */ public static function GoodsUrlCreate($goods_id) { return (APPLICATION_CLIENT_TYPE == 'pc') ? MyUrl('index/goods/index', ['id'=>$goods_id]) : '/pages/goods-detail/goods-detail?id='.$goods_id; } /** * 获取商品列表 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-09-07 * @desc description * @param [array] $params [输入参数] */ public static function GoodsSearchList($params = []) { // 分页 $page = max(1, isset($params['page']) ? intval($params['page']) : 1); $page_size = empty($params['page_size']) ? 20 : min(intval($params['page_size']), 100); $page_start = intval(($page-1)*$page_size); // 返回格式 $result = [ 'page' => $page, 'page_start' => $page_start, 'page_size' => $page_size, 'page_total' => 0, 'total' => 0, 'data' => [], ]; // 搜索条件 $where_base = empty($params['where_base']) ? [] : $params['where_base']; $where_keywords = empty($params['where_keywords']) ? [] : $params['where_keywords']; // 排序 $order_by = empty($params['order_by']) ? 'access_count desc, sales_count desc, id desc' : $params['order_by']; // 指定字段 $field = empty($params['field']) ? '*' : $params['field']; // 商品搜索列表读取前钩子 $hook_name = 'plugins_service_goods_search_list_begin'; MyEventTrigger($hook_name, [ 'hook_name' => $hook_name, 'is_backend' => true, 'params' => $params, 'where_base' => &$where_base, 'where_keywords' => &$where_keywords, 'field' => &$field, 'order_by' => &$order_by, 'page' => &$result['page'], ]); // 获取商品总数 $result['total'] = (int) Db::name('Goods')->where($where_base)->where(function($query) use($where_keywords) { $query->whereOr($where_keywords); })->count(); // 获取商品列表 if($result['total'] > 0) { // 查询数据 $goods = self::GoodsDataHandle(Db::name('Goods')->field($field)->where($where_base)->where(function($query) use($where_keywords) { $query->whereOr($where_keywords); })->order($order_by)->limit($result['page_start'], $result['page_size'])->select()->toArray(), $params); // 返回数据 $result['data'] = $goods['data']; $result['page_total'] = ceil($result['total']/$result['page_size']); } return DataReturn(MyLang('handle_success'), 0, $result); } /** * 商品基础模板 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2022-08-26 * @desc description * @param [array] $params [输入参数] */ public static function GoodsBaseTemplate($params = []) { // 请求类型 $p = [ [ 'checked_type' => 'empty', 'key_name' => 'category_ids', 'error_msg' => MyLang('form_goods_category_message'), ], ]; $ret = ParamsChecked($params, $p); if($ret !== true) { return DataReturn($ret, -1); } if(!is_array($params['category_ids'])) { $params['category_ids'] = explode(',', $params['category_ids']); } // 规格模板 $spec = GoodsSpecService::GoodsCategorySpecTemplateList($params); // 参数模板 $parameter = GoodsParamsService::GoodsCategoryParamsTemplateList($params); // 商品基础字段必填配置数据 $required_fields = self::GoodsBaseFieldsRequiredConfigData($params['category_ids']); return DataReturn(MyLang('operate_success'), 0, [ 'spec' => $spec['data'], 'params' => $parameter['data'], 'required_fields' => empty($required_fields) ? null : $required_fields, ]); } /** * 商品基础字段必填配置数据 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2025-11-06 * @desc description * @param [array] $category_ids [商品分类id] */ public static function GoodsBaseFieldsRequiredConfigData($category_ids) { if(!is_array($category_ids)) { $category_ids = explode(',', $category_ids); } $required_fields = []; $goods_base_fields_required = MyC('common_goods_base_fields_required_data'); if(!empty($goods_base_fields_required) && is_array($goods_base_fields_required)) { foreach($goods_base_fields_required as $k=>$v) { $ids = GoodsCategoryService::GoodsCategoryItemsIds($v); $temp = array_diff($ids, $category_ids); $required_fields[$k] = (count($temp) < count($ids)) ? 1 : 0; } } return $required_fields; } /** * 商品详情页面猜你喜欢的相关商品 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2023-12-29 * @desc description * @param [int] $goods_id [商品id] * @param [array] $params [输入参数] */ public static function GoodsDetailGuessYouLikeData($goods_id, $params = []) { $goods_list = []; if(!empty($goods_id) && MyC('common_is_goods_detail_show_guess_you_like', 0) == 1) { $category_ids = Db::name('GoodsCategoryJoin')->where(['goods_id'=>$goods_id])->column('category_id'); if(!empty($category_ids)) { $category_ids = GoodsCategoryService::GoodsCategoryParentIds(GoodsCategoryService::GoodsCategoryItemsIds($category_ids)); $data_params = [ 'where' => [ ['g.is_shelves', '=', 1], ['g.is_delete_time', '=', 0], ['gci.category_id', 'in', $category_ids], ['g.id', 'not in', $goods_id], ], 'order_by' => 'g.sales_count desc', 'n' => 16, 'is_spec' => (!isset($params['is_spec']) || $params['is_spec'] == 1) ? 1 : 0, 'is_cart' => (!isset($params['is_cart']) || $params['is_cart'] == 1) ? 1 : 0, ]; $ret = self::CategoryGoodsList($data_params); $goods_list = empty($ret['data']) ? [] : $ret['data']; } } return $goods_list; } /** * 商品详情页面看了又看的相关商品 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2023-12-29 * @desc description * @param [int] $goods_id [商品id] * @param [array] $params [输入参数] */ public static function GoodsDetailSeeingYouData($goods_id, $params = []) { $goods_list = []; if(!empty($goods_id) && MyC('common_is_goods_detail_show_seeing_you', 0) == 1) { $data_params = [ 'where' => [ ['is_shelves', '=', 1], ['is_delete_time', '=', 0], ['id', 'not in', $goods_id], ], 'order_by' => 'access_count desc', 'n' => 10, 'is_spec' => (!isset($params['is_spec']) || $params['is_spec'] == 1) ? 1 : 0, 'is_cart' => (!isset($params['is_cart']) || $params['is_cart'] == 1) ? 1 : 0, ]; $ret = self::GoodsList($data_params); if(!empty($ret['data'])) { $goods_list = $ret['data']; } } return $goods_list; } /** * 指定读取商品列表 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2020-09-29 * @desc description * @param [array] $params [输入参数] */ public static function AppointGoodsList($params = []) { $result = []; if(!empty($params['goods_ids'])) { // 非数组则转为数组 if(!is_array($params['goods_ids'])) { $params['goods_ids'] = explode(',', $params['goods_ids']); } // 基础条件 $params['where'] = [ ['is_delete_time', '=', 0], ['is_shelves', '=', 1], ['id', 'in', array_unique($params['goods_ids'])] ]; // 获取数据 $params['is_spec'] = isset($params['is_spec']) ? $params['is_spec'] : 0; $params['is_cart'] = isset($params['is_cart']) ? $params['is_cart'] : 0; $params['is_favor'] = isset($params['is_favor']) ? $params['is_favor'] : 0; $params['m'] = 0; $params['n'] = 0; $params['field'] = '*'; $params['is_appoint_goods_list'] = 1; $ret = self::GoodsList($params); if(!empty($ret['data'])) { $temp = array_column($ret['data'], null, 'id'); foreach($params['goods_ids'] as $id) { if(!empty($id) && array_key_exists($id, $temp)) { $result[] = $temp[$id]; } } } } return $result; } /** * 自动读取商品列表 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2020-09-29 * @desc description * @param [array] $params [输入参数] */ public static function AutoGoodsList($params = []) { // 基础条件 $params['where'] = [ ['g.is_delete_time', '=', 0], ['g.is_shelves', '=', 1], ]; // 商品关键字 if(!empty($params['goods_keywords'])) { $params['where'][] = ['g.title|g.simple_desc', 'like', '%'.$params['goods_keywords'].'%']; } // 分类条件 if(!empty($params['goods_category_ids'])) { if(!is_array($params['goods_category_ids'])) { $params['goods_category_ids'] = explode(',', $params['goods_category_ids']); } $params['where'][] = ['gci.category_id', 'in', GoodsCategoryService::GoodsCategoryItemsIds($params['goods_category_ids'], 1)]; } // 品牌条件 if(!empty($params['goods_brand_ids'])) { if(!is_array($params['goods_brand_ids'])) { $params['goods_brand_ids'] = explode(',', $params['goods_brand_ids']); } $params['where'][] = ['g.brand_id', 'in', $params['goods_brand_ids']]; } // 排序 $order_by_type_list = MyConst('common_goods_order_by_type_list'); $order_by_rule_list = MyConst('common_data_order_by_rule_list'); // 排序类型 $order_by_type = !isset($params['goods_order_by_type']) || !array_key_exists($params['goods_order_by_type'], $order_by_type_list) ? $order_by_type_list[0]['value'] : $order_by_type_list[$params['goods_order_by_type']]['value']; // 排序值 $order_by_rule = !isset($params['goods_order_by_rule']) || !array_key_exists($params['goods_order_by_rule'], $order_by_rule_list) ? $order_by_rule_list[0]['value'] : $order_by_rule_list[$params['goods_order_by_rule']]['value']; // 拼接排序 $params['order_by'] = $order_by_type.' '.$order_by_rule; $params['m'] = 0; $params['n'] = empty($params['goods_number']) ? 10 : intval($params['goods_number']); $params['field'] = 'g.*'; // 获取数据 $params['is_spec'] = isset($params['is_spec']) ? $params['is_spec'] : 0; $params['is_cart'] = isset($params['is_cart']) ? $params['is_cart'] : 0; $params['is_favor'] = isset($params['is_favor']) ? $params['is_favor'] : 0; $params['is_auto_goods_list'] = 1; $ret = self::CategoryGoodsList($params); return empty($ret['data']) ? [] : $ret['data']; } /** * 商品基础禁止操作数据 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2025-07-02 * @desc description * @param [int] $goods_id [商品id] * @param [array] $goods [商品数据] * @param [array] $params [输入参数] */ public static function GoodsBaseForbidOperateData($goods_id = 0, $goods = [], $params = []) { // 禁止的数据字段 $data = [ // 比如把标题和简述加入禁止修改 // title // simple_desc // spec_desc // approval_number // approval_number_expire // batch_number // batch_number_expire // coding // brand_id // produce_company // site_type // images // inventory_unit // produce_region // model // category_ids // sort_level // give_integral // photo // video ]; // 商品参数操作数据钩子 $hook_name = 'plugins_service_goods_base_forbid_operate_data'; MyEventTrigger($hook_name, [ 'hook_name' => $hook_name, 'is_backend' => true, 'goods_id' => $goods_id, 'goods' => $goods, 'params' => $params, 'data' => &$data, ]); return $data; } /** * 商品参数操作数据 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2025-07-02 * @desc description * @param [int] $goods_id [商品id] * @param [array] $goods [商品数据] * @param [array] $params [输入参数] */ public static function GoodsParamsOperateData($goods_id = 0, $goods = [], $params = []) { // 操作数据 $data = [ // 创建 'is_created' => 1, // 添加 'is_add' => 1, // 编辑 'is_edit' => 1, // 复制 'is_copy' => 1, // 移除 'is_remove' => 1, // 提示信息 'msg' => '', ]; // 商品参数操作数据钩子 $hook_name = 'plugins_service_goods_params_operate_data'; MyEventTrigger($hook_name, [ 'hook_name' => $hook_name, 'is_backend' => true, 'goods_id' => $goods_id, 'goods' => $goods, 'params' => $params, 'data' => &$data, ]); return $data; } /** * 商品规格操作数据 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2025-07-02 * @desc description * @param [int] $goods_id [商品id] * @param [array] $goods [商品数据] * @param [array] $params [输入参数] */ public static function GoodsSpecOperateData($goods_id = 0, $goods = [], $params = []) { // 操作数据 $data = [ // 创建 'is_created' => 1, // 添加 'is_add' => 1, // 编辑 'is_edit' => 1, // 复制 'is_copy' => 1, // 移除 'is_remove' => 1, // 提示信息 'msg' => '', ]; // 商品规格操作数据钩子 $hook_name = 'plugins_service_goods_spec_operate_data'; MyEventTrigger($hook_name, [ 'hook_name' => $hook_name, 'is_backend' => true, 'goods_id' => $goods_id, 'goods' => $goods, 'params' => $params, 'data' => &$data, ]); return $data; } /** * 商品数据编辑状态 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2025-09-16 * @desc description * @param [int] $goods_id [商品id] * @param [int] $data [商品编辑的数据] */ public static function GoodsDataEditStatusCheck($goods_id, $data) { $result = [ // 基础 'is_base' => 0, // 相册 'is_photo' => 0, // 手机详情 'is_content_app' => 0, // 参数 'is_parameter' => 0, // 规格 'is_spec' => 0, ]; if(!empty($goods_id)) { // 基础 if(empty($data['data'])) { $data['data'] = []; } $goods = Db::name('Goods')->where(['id'=>$goods_id])->find(); $temp_data = []; foreach($data['data'] as $k=>$v) { if(array_key_exists($k, $goods)) { $temp_data[$k] = $goods[$k]; } } $diff1 = array_diff($data['data'], $temp_data); $diff2 = array_diff($temp_data, $data['data']); $result['is_base'] = (count($diff1) == 0 && count($diff2) == 0) ? 0 : 1; // 相册 $temp_photo = empty($data['photo']) ? [] : $data['photo']; $photo = Db::name('GoodsPhoto')->where(['goods_id'=>$goods_id])->order('id asc')->column('images'); $diff1 = array_diff($temp_photo, $photo); $diff2 = array_diff($photo, $temp_photo); $result['is_photo'] = (count($diff1) == 0 && count($diff2) == 0) ? 0 : 1; // 手机详情 $temp_content_app = empty($data['content_app']) ? [] : array_map(function($item) { return implode(';', array_filter($item)); }, array_values($data['content_app'])); $content_app = Db::name('GoodsContentApp')->where(['goods_id'=>$goods_id])->order('sort asc, id asc')->field('images,content as text')->select()->toArray(); if(!empty($content_app)) { $content_app = array_map(function($item) { return implode(';', array_filter($item)); }, array_values($content_app)); } $diff1 = array_diff($temp_content_app, $content_app); $diff2 = array_diff($content_app, $temp_content_app); $result['is_content_app'] = (count($diff1) == 0 && count($diff2) == 0) ? 0 : 1; // 商品参数 $temp_parameter = empty($data['parameter']) ? [] : array_map(function($item) { return implode(';', $item); }, array_values($data['parameter'])); $parameter = Db::name('GoodsParams')->where(['goods_id'=>$goods_id])->order('id asc')->field('scope,data_type,name,value')->select()->toArray(); if(!empty($parameter)) { $parameter = array_map(function($item) { return implode(';', $item); }, array_values($parameter)); } $diff1 = array_diff($temp_parameter, $parameter); $diff2 = array_diff($parameter, $temp_parameter); $result['is_parameter'] = (count($diff1) == 0 && count($diff2) == 0) ? 0 : 1; // 商品规格 // 类型标题 $temp_spec_title = []; if(!empty($data['spec']['title'])) { foreach($data['spec']['title'] as $k=>$v) { $spec = []; foreach($v['value'] as $vs) { $spec[] = [ 'name' => $vs, 'images' => isset($data['spec']['images'][$vs]) ? ResourcesService::AttachmentPathHandle($data['spec']['images'][$vs]) : '', ]; } $v['value'] = json_encode($spec, JSON_UNESCAPED_UNICODE); $temp_spec_title[] = $v; } if(!empty($temp_spec_title)) { $temp_spec_title = array_map(function($item) { return implode(';', $item); }, $temp_spec_title); } } $spec_title = Db::name('GoodsSpecType')->where(['goods_id'=>$goods_id])->order('id asc')->field('name,value')->select()->toArray(); if(!empty($spec_title)) { $spec_title = array_map(function($item) { return implode(';', $item); }, $spec_title); } $diff1 = array_diff($temp_spec_title, $spec_title); $diff2 = array_diff($spec_title, $temp_spec_title); $is_spec_title = (count($diff1) == 0 && count($diff2) == 0) ? 0 : 1; // 基础/规格值 $temp_spec_value = []; $temp_spec_base = []; // 处理规格数据 if(!empty($data['spec']) && !empty($data['spec']['data'])) { // 规格基础字段默认数据 $spec_field_defult_value = ['price'=>'0.00', 'original_price'=>'0.00', 'weight'=>'0.00', 'volume'=>'0.00', 'buy_min_number'=>0, 'buy_max_number'=>0]; // 基础字段 $count = count($data['spec']['data'][0]); $temp_key = self::GoodsSpecBaseFields(); $key_count = count($temp_key); // 等于key总数则只有一列基础规格 if($count == $key_count) { $temp_spec_base_item = []; for($i=0; $i<$count; $i++) { if(in_array($temp_key[$i], ['price', 'original_price', 'weight', 'volume'])) { $temp_spec_base_item[$temp_key[$i]] = PriceNumberFormat($data['spec']['data'][0][$i]); } else if(in_array($temp_key[$i], ['buy_min_number', 'buy_max_number'])) { $temp_spec_base_item[$temp_key[$i]] = intval($data['spec']['data'][0][$i]); } else { $temp_spec_base_item[$temp_key[$i]] = $data['spec']['data'][0][$i]; } } $temp_spec_base[] = $temp_spec_base_item; // 多规格 } else { $base_start = $count-$key_count; foreach($data['spec']['data'] as $v) { $temp_spec_base_item = []; for($i=0; $i<$count; $i++) { if($i < $base_start) { $temp_spec_value[] = $v[$i]; } else { if(in_array($temp_key[$i-$base_start], ['price', 'original_price', 'weight', 'volume'])) { $temp_spec_base_item[$temp_key[$i-$base_start]] = PriceNumberFormat($v[$i]); } else if(in_array($temp_key[$i-$base_start], ['buy_min_number', 'buy_max_number'])) { $temp_spec_base_item[$temp_key[$i-$base_start]] = intval($v[$i]); } else { $temp_spec_base_item[$temp_key[$i-$base_start]] = $v[$i]; } } } $temp_spec_base[] = $temp_spec_base_item; } } if(!empty($temp_spec_base)) { $temp_spec_base = array_map(function($item) { return implode(';', $item); }, array_values($temp_spec_base)); } } // 查询规格数据 $spec_base = Db::name('GoodsSpecBase')->where(['goods_id'=>$goods_id])->field($temp_key)->order('id asc')->select()->toArray(); if(!empty($spec_base)) { $spec_base = array_map(function($item) { return implode(';', $item); }, array_values($spec_base)); } $spec_value = Db::name('GoodsSpecValue')->where(['goods_id'=>$goods_id])->order('id asc')->column('value'); // 规格基础 $diff1 = array_diff($temp_spec_base, $spec_base); $diff2 = array_diff($spec_base, $temp_spec_base); $is_spec_base = (count($diff1) == 0 && count($diff2) == 0) ? 0 : 1; // 规格值 $diff1 = array_diff($temp_spec_value, $spec_value); $diff2 = array_diff($spec_value, $temp_spec_value); $is_spec_value = (count($diff1) == 0 && count($diff2) == 0) ? 0 : 1; // 规格是否修改 $result['is_spec'] = ($is_spec_title+$is_spec_base+$is_spec_value > 0) ? 1 : 0; } return $result; } } ?>