field($field)->where(['is_enable'=>1, 'id'=>intval($params['id'])])->find()]); return empty($data[0]) ? null : $data[0]; } /** * 获取所有分类 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-08-29 * @desc description * @param [array] $params [输入参数] */ public static function GoodsCategoryAll($params = []) { // 从缓存获取 $key = MyConfig('shopxo.cache_goods_category_key'); $data = MyCache($key); if($data === null || MyEnv('app_debug')) { // 获取分类 $params['where'] = [ ['pid', '=', 0], ['is_enable', '=', 1], ]; $data = self::GoodsCategory($params); // 存储缓存 MyCache($key, $data, 180); } return $data; } /** * 获取分类 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-08-29 * @desc description * @param [array] $params [输入参数] */ public static function GoodsCategory($params = []) { // 获取分类 if(empty($params['where'])) { $where = [ ['pid', '=', 0], ['is_enable', '=', 1], ]; } else { $where = $params['where']; } $data = self::GoodsCategoryList(['where'=>$where]); if(!empty($data)) { // 基础条件、去除pid $where_base = $where; $temp_column = array_column($where, 0); if(in_array('pid', $temp_column)) { unset($where_base[array_search('pid', $temp_column)]); sort($where_base); } foreach($data as &$v) { $v['items'] = self::GoodsCategoryList(['where'=>array_merge($where_base, [['pid', '=', $v['id']]])]); if(!empty($v['items'])) { // 一次性查出所有二级下的三级、再做归类、避免sql连接超多 $itemss = self::GoodsCategoryList(['where'=>array_merge($where_base, [['pid', 'in', array_column($v['items'], 'id')]])]); if(!empty($itemss)) { foreach($v['items'] as &$vs) { foreach($itemss as $vss) { if($vs['id'] == $vss['pid']) { $vs['items'][] = $vss; } } } } } } } else { $data = []; } return $data; } /** * 根据pid获取商品分类列表 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-08-29 * @desc description * @param [array] $params [输入参数] */ public static function GoodsCategoryList($params = []) { // 条件、附加必须启用状态 $where = empty($params['where']) ? [] : $params['where']; // 增加启用条件 $where[] = ['is_enable', '=', 1]; // 数量、默认0,0则全部 $m = isset($params['m']) ? intval($params['m']) : 0; $n = isset($params['n']) ? intval($params['n']) : 0; // 排序 $order_by = empty($params['order_by']) ? 'sort asc' : trim($params['order_by']); $field = empty($params['field']) ? 'id,pid,icon,name,vice_name,describe,bg_color,big_images,sort,is_home_recommended,seo_title,seo_keywords,seo_desc' : $params['field']; $data = Db::name('GoodsCategory')->field($field)->where($where)->order($order_by)->limit($m, $n)->select()->toArray(); return self::GoodsCategoryDataHandle($data); } /** * 商品分类数据处理 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-09-06 * @desc description * @param [array] $data [商品分类数据 二维数组] */ public static function GoodsCategoryDataHandle($data) { if(!empty($data) && is_array($data)) { foreach($data as &$v) { if(is_array($v)) { if(array_key_exists('icon', $v)) { $v['icon'] = ResourcesService::AttachmentPathViewHandle($v['icon']); } if(array_key_exists('big_images', $v)) { $v['big_images'] = ResourcesService::AttachmentPathViewHandle($v['big_images']); } } } } return $data; } /** * 获取首页楼层数据 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-08-29 * @desc description * @param [array] $params [输入参数] */ public static function HomeFloorList($params = []) { // 缓存 $key = MyConfig('shopxo.cache_goods_floor_list_key'); $data = MyCache($key); if($data === null || MyEnv('app_debug')) { // 商品大分类 $where = [ ['pid', '=', 0], ['is_home_recommended', '=', 1], ['is_enable', '=', 1], ]; $data = self::GoodsCategoryList(['where'=>$where]); 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('goods_order_by_type_list'); $floor_order_by_rule_list = MyConst('goods_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; } // 根据分类获取楼层商品 foreach($data as &$v) { // 数据模式 switch($floor_data_type) { // 自动模式 case 0 : if(isset($goods_count) && isset($order_by)) { // 获取分类ids $category_ids = self::GoodsCategoryItemsIds([$v['id']], 1); // 获取商品ids $where = [ 'gci.category_id' => $category_ids, 'g.is_shelves' => 1, ]; $v['goods_ids'] = Db::name('Goods')->alias('g')->join('goods_category_join gci', 'g.id=gci.goods_id')->where($where)->group('g.id')->order($order_by)->limit($goods_count)->column('g.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'] = self::GoodsCategoryList(['where'=>[['id', 'in', explode(',', $floor_left_top_category[$v['id']])]], 'm'=>0, 'n'=>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)) { $where = [ ['id', 'in', array_unique($goods_ids)], ['is_shelves', '=', 1], ]; $res = self::GoodsList(['where'=>$where, 'm'=>0, 'n'=>0, 'field'=>'*']); $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; } /** * 获取商品分类下的所有分类id * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-08-29 * @desc description * @param [array] $ids [分类id数组] * @param [int] $is_enable [是否启用 null, 0否, 1是] * @param [string] $order_by [排序, 默认sort asc] */ public static function GoodsCategoryItemsIds($ids = [], $is_enable = null, $order_by = 'sort asc') { if(!is_array($ids)) { $ids = explode(',', $ids); } $where = ['pid'=>$ids]; if($is_enable !== null) { $where['is_enable'] = $is_enable; } $data = Db::name('GoodsCategory')->where($where)->order($order_by)->column('id'); if(!empty($data)) { $temp = self::GoodsCategoryItemsIds($data, $is_enable, $order_by); if(!empty($temp)) { $data = array_merge($data, $temp); } } $data = empty($data) ? $ids : array_unique(array_merge($ids, $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 = []) { 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.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, ]); $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(); // 数据处理 return self::GoodsDataHandle($data, $params); } /** * 商品数据处理 * @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']) && $params['is_photo'] == true) ? true : false; $is_spec = (isset($params['is_spec']) && $params['is_spec'] == true) ? true : false; $is_content_app = (isset($params['is_content_app']) && $params['is_content_app'] == true) ? true : false; $is_category = (isset($params['is_category']) && $params['is_category'] == true) ? true : false; $is_params = (isset($params['is_params']) && $params['is_params'] == true) ? true : false; $data_key_field = empty($params['data_key_field']) ? 'id' : $params['data_key_field']; // 字段列表 $keys = ArrayKeys($data); // 品牌名称 if(in_array('brand_id', $keys)) { $brand_list = BrandService::BrandName(array_column($data, 'brand_id')); } // 产地名称 if(in_array('place_origin', $keys)) { $place_origin_list = RegionService::RegionName(array_column($data, 'place_origin')); } // 开始处理数据 foreach($data as &$v) { // 数据主键id $data_id = isset($v[$data_key_field]) ? $v[$data_key_field] : 0; // 商品处理前钩子 $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; } // 商品价格容器 $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, ]; // 商品url地址 if(!empty($data_id)) { $v['goods_url'] = MyUrl('index/goods/index', ['id'=>$data_id]); } // 获取相册 if($is_photo && !empty($data_id)) { $v['photo'] = self::GoodsPhotoData($data_id); if(!empty($v['photo'])) { foreach($v['photo'] as &$vs) { $vs['images_old'] = $vs['images']; $vs['images'] = ResourcesService::AttachmentPathViewHandle($vs['images']); } } } // 商品封面图片 if(isset($v['images'])) { // 无封面图片 if(empty($v['images'])) { // 获取商品封面图片 $v['images'] = ResourcesService::AttachmentPathHandle(self::GoodsImagesCoverHandle($data_id, isset($v['photo']) ? $v['photo'] : [])); } $v['images_old'] = $v['images']; $v['images'] = ResourcesService::AttachmentPathViewHandle($v['images']); } // 视频 if(isset($v['video'])) { $v['video_old'] = $v['video']; $v['video'] = ResourcesService::AttachmentPathViewHandle($v['video']); } // PC内容处理 if(isset($v['content_web'])) { $v['content_web'] = ResourcesService::ContentStaticReplace($v['content_web'], 'get'); } // 虚拟商品展示数据 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['place_origin'])) { $v['place_origin_name'] = (!empty($place_origin_list) && is_array($place_origin_list) && array_key_exists($v['place_origin'], $place_origin_list)) ? $place_origin_list[$v['place_origin']] : null; } // 品牌 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']] : null; } // 时间 if(!empty($v['add_time'])) { $v['add_time'] = date('Y-m-d H:i:s', $v['add_time']); } if(!empty($v['upd_time'])) { $v['upd_time'] = date('Y-m-d H:i:s', $v['upd_time']); } // 是否需要分类名称 if($is_category && !empty($data_id)) { $v['category_ids'] = Db::name('GoodsCategoryJoin')->where(['goods_id'=>$data_id])->column('category_id'); $category_name = Db::name('GoodsCategory')->where(['id'=>$v['category_ids']])->column('name'); $v['category_text'] = implode(',', $category_name); } // 规格基础 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'] = self::GoodsSpecificationsData($data_id); } // 获取商品参数 if($is_params && !empty($data_id)) { $v['parameters'] = self::GoodsParametersData($data_id); } // 获取app内容 if($is_content_app && !empty($data_id)) { $v['content_app'] = self::GoodsContentAppData(['goods_id'=>$data_id]); } // 价格字段 // 原价 // 价格 $v['show_field_original_price_text'] = '原价'; $v['show_field_price_text'] = '价格'; // 公共插件数据 // 商品详情面板提示数据、一维数组 $v['plugins_view_panel_data'] = []; // 商品详情icon数据、二维数组 // name 必填(建议不超过6个字符) // bg_color 默认(#fff) // br_color 默认(#3bb4f2) // color 默认($3bb4f2) // url 默认空(手机端请自行调整url地址) // [ // 'name' => 'icon名称', // 'bg_color' => '#fff', // 'br_color' => '#3bb4f2', // 'color' => '#3bb4f2', // 'url' => 'url地址' // ] $v['plugins_view_icon_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, ]); } return DataReturn('success', 0, $data); } /** * 获取商品封面图片 * @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] $goods_id [商品id] */ public static function GoodsPhotoData($goods_id) { return Db::name('GoodsPhoto')->where(['goods_id'=>$goods_id, 'is_show'=>1])->order('sort asc')->select()->toArray(); } /** * 获取商品手机详情 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-07-10 * @desc description * @param [array] $params [输入参数] * @return [array] [app内容] */ public static function GoodsContentAppData($params = []) { $data = Db::name('GoodsContentApp')->where(['goods_id'=>$params['goods_id']])->field('id,images,content')->order('sort asc')->select()->toArray(); if(!empty($data)) { foreach($data as &$v) { $v['images_old'] = $v['images']; $v['images'] = ResourcesService::AttachmentPathViewHandle($v['images']); $v['content_old'] = $v['content']; $v['content'] = empty($v['content']) ? null : explode("\n", $v['content']); } } return $data; } /** * 获取商品规格 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2020-07-16 * @desc description * @param [int] $goods_id [商品id] */ public static function GoodsSpecificationsData($goods_id) { // 条件 $where = ['goods_id'=>$goods_id]; // 规格类型 $choose = Db::name('GoodsSpecType')->where($where)->order('id asc')->select()->toArray(); if(!empty($choose)) { // 数据处理 foreach($choose 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_type['add_time'] = date('Y-m-d H:i:s'); } // 只有一个规格的时候直接获取规格值的库存数 if(count($choose) == 1) { foreach($choose[0]['value'] as &$temp_spec) { $temp_spec_params = [ 'id' => $goods_id, 'spec' => [ ['type' => $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']; } } } } return ['choose'=>$choose]; } /** * 获取商品参数 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2020-08-31 * @desc description * @param [int] $goods_id [商品id] */ public static function GoodsParametersData($goods_id) { $base = []; $detail = []; $list = Db::name('GoodsParams')->where(['goods_id'=>$goods_id])->order('id asc')->select()->toArray(); if(!empty($list)) { foreach($list as $v) { $temp = [ 'name' => $v['name'], 'value' => $v['value'], ]; // 基础 if(in_array($v['type'], [0,2])) { $base[] = $temp; } // 详情 if(in_array($v['type'], [0,1])) { $detail[] = $temp; } } } // 返回的数据 $data = [ 'base' => $base, 'detail' => $detail, ]; // 商品参数钩子 $hook_name = 'plugins_service_goods_parameters_data'; MyEventTrigger($hook_name, [ 'hook_name' => $hook_name, 'is_backend' => true, 'data' => &$data, 'goods_id' => $goods_id, ]); 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 = []) { 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 = []) { // 请求参数 $p = [ [ 'checked_type' => 'length', 'key_name' => 'title', 'checked_data' => '2,160', 'error_msg' => '标题名称格式 2~160 个字符', ], [ 'checked_type' => 'length', 'key_name' => 'simple_desc', 'checked_data' => '230', 'is_checked' => 1, 'error_msg' => '商品简述格式 最多230个字符', ], [ 'checked_type' => 'length', 'key_name' => 'model', 'checked_data' => '30', 'is_checked' => 1, 'error_msg' => '商品型号格式 最多30个字符', ], [ 'checked_type' => 'empty', 'key_name' => 'category_id', 'error_msg' => '请至少选择一个商品分类', ], [ 'checked_type' => 'length', 'key_name' => 'inventory_unit', 'checked_data' => '1,6', 'error_msg' => '库存单位格式 1~6 个字符', ], [ 'checked_type' => 'empty', 'key_name' => 'buy_min_number', 'error_msg' => '请填写有效的最低起购数量', ], [ 'checked_type' => 'in', 'key_name' => 'site_type', 'checked_data' => array_merge([-1], array_column(MyConst('common_site_type_list'), 'value')), 'is_checked' => 2, 'error_msg' => '商品类型数据值范围有误', ], [ 'checked_type' => 'length', 'key_name' => 'seo_title', 'checked_data' => '100', 'is_checked' => 1, 'error_msg' => 'SEO标题格式 最多100个字符', ], [ 'checked_type' => 'length', 'key_name' => 'seo_keywords', 'checked_data' => '130', 'is_checked' => 1, 'error_msg' => 'SEO关键字格式 最多130个字符', ], [ 'checked_type' => 'length', 'key_name' => 'seo_desc', 'checked_data' => '230', 'is_checked' => 1, 'error_msg' => 'SEO描述格式 最多230个字符', ], ]; $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; } // 相册 $photo = self::GetFormGoodsPhotoParams($params); if($photo['code'] != 0) { return $photo; } // 手机端详情 $content_app = self::GetFormGoodsContentAppParams($params); if($content_app['code'] != 0) { return $content_app; } // 其它附件 $data_fields = ['images', 'video']; $attachment = ResourcesService::AttachmentParams($params, $data_fields); if($attachment['code'] != 0) { return $attachment; } // 编辑器内容 $content_web = empty($params['content_web']) ? '' : ResourcesService::ContentStaticReplace(htmlspecialchars_decode($params['content_web']), 'add'); $fictitious_goods_value = empty($params['fictitious_goods_value']) ? '' : ResourcesService::ContentStaticReplace(htmlspecialchars_decode($params['fictitious_goods_value']), 'add'); // 赠送积分 $give_integral = max(0, (isset($params['give_integral']) && $params['give_integral'] <= 100) ? intval($params['give_integral']) : 0); // 封面图片、默认相册第一张 $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' => $params['simple_desc'], 'model' => $params['model'], 'place_origin' => isset($params['place_origin']) ? intval($params['place_origin']) : 0, 'inventory_unit' => $params['inventory_unit'], 'give_integral' => $give_integral, 'buy_min_number' => max(1, isset($params['buy_min_number']) ? intval($params['buy_min_number']) : 1), 'buy_max_number' => isset($params['buy_max_number']) ? intval($params['buy_max_number']) : 0, '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, 'site_type' => isset($params['site_type']) ? $params['site_type'] : -1, ]; // 商品保存处理钩子 $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'], 'goods_id' => isset($params['id']) ? intval($params['id']) : 0, ])); if(isset($ret['code']) && $ret['code'] != 0) { return $ret; } // 启动事务 Db::startTrans(); // 捕获异常 try { // 添加/编辑 if(empty($params['id'])) { $data['add_time'] = time(); $goods_id = Db::name('Goods')->insertGetId($data); if($goods_id <= 0) { throw new \Exception('添加失败'); } } else { $data['upd_time'] = time(); if(Db::name('Goods')->where(['id'=>intval($params['id'])])->update($data)) { $goods_id = $params['id']; } else { throw new \Exception('更新失败'); } } // 分类 $ret = self::GoodsCategoryInsert(explode(',', $params['category_id']), $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']); } else { // 更新商品基础信息 $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($params, $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']); } // 完成 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('操作成功', 0); } /** * 商品参数添加 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2020-08-31 * @desc description * @param [array] $params [输入参数] * @param [int] $goods_id [商品id] */ public static function GoodsParamsInsert($params, $goods_id) { // 删除商品参数 Db::name('GoodsParams')->where(['goods_id'=>$goods_id])->delete(); // 获取参数解析并添加 $config = GoodsParamsService::GoodsParamsTemplateHandle($params); if($config['code'] == 0 && !empty($config['data'])) { foreach($config['data'] as &$v) { $v['goods_id'] = $goods_id; $v['add_time'] = time(); } if(Db::name('GoodsParams')->insertAll($config['data']) < count($config['data'])) { return DataReturn('规格参数添加失败', -1); } } return DataReturn('操作成功', 0); } /** * 商品保存基础信息更新 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @datetime 2018-12-16T01:56:42+0800 * @param [int] $goods_id [商品id] */ public static function GoodsSaveBaseUpdate($goods_id) { $data = Db::name('GoodsSpecBase')->field('min(price) AS min_price, max(price) AS max_price, sum(inventory) AS inventory, min(original_price) AS min_original_price, max(original_price) AS max_original_price')->where(['goods_id'=>$goods_id])->find(); if(empty($data)) { return DataReturn('没找到商品基础信息', -1); } // 销售价格 - 展示价格 $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('商品基础更新失败', -1); } // 商品基础数据更新钩子 $hook_name = 'plugins_service_goods_base_update'; $ret = EventReturnHandle(MyEventTrigger($hook_name, [ 'hook_name' => $hook_name, 'is_backend' => true, 'goods_id' => $goods_id ])); if(isset($ret['code']) && $ret['code'] != 0) { return $ret; } return DataReturn('操作成功', 0); } /** * 获取规格值参数 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-07-09 * @desc description * @param [array] $params [输入参数] */ public static function GetFormGoodsSpecificationsParams($params = []) { $data = []; $title = []; $images = []; // 基础字段数据字段长度 $base_count = 6; // 规格值 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('规格值列之间不能重复['.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('规格值不能重复['.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('规格名称列之间不能重复['.implode(',', array_unique($repeat_names_all)).']', -1); } } else { if(!isset($data[0][0]) || $data[0][0] < 0) { return DataReturn('请填写有效的规格销售价格', -1); } if(!isset($data[0][1]) || $data[0][1] < 0) { return DataReturn('请填写规格库存', -1); } } } else { return DataReturn('请填写规格', -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('请上传相册', -1); } $result = []; if(!empty($params['photo']) && is_array($params['photo'])) { foreach($params['photo'] as $v) { $result[] = ResourcesService::AttachmentPathHandle($v); } } 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('商品分类添加失败', -1); } } } return DataReturn('添加成功', 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('手机详情添加失败', -1); } } } return DataReturn('添加成功', 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('相册添加失败', -1); } } } return DataReturn('添加成功', 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('规格类型添加失败', -1); } } // 基础/规格值 if(!empty($data['data'])) { // 基础字段 $count = count($data['data'][0]); $temp_key = ['price', 'weight', 'coding', 'barcode', 'original_price', 'extends']; $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('规格基础添加失败', -1); } // 多规格操作 } else { $base_start = $count-$key_count; $value = []; $base = []; 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], '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('规格基础添加失败', -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('规格值添加失败', -1); } } } } return DataReturn('添加成功', 0); } /** * 商品删除 * @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('操作id有误', -1); } // 是否数组 if(!is_array($params['ids'])) { $params['ids'] = explode(',', $params['ids']); } // 启动事务 Db::startTrans(); // 捕获异常 try { // 删除商品操作 self::GoodsDeleteHandle($params['ids']); // 商品删除钩子 $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('删除成功', 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] */ public static function GoodsDeleteHandle($goods_ids) { // 删除商品 if(!Db::name('Goods')->where(['id'=>$goods_ids])->delete()) { throw new \Exception('商品删除失败'); } // 商品规格 if(Db::name('GoodsSpecType')->where(['goods_id'=>$goods_ids])->delete() === false) { throw new \Exception('规格类型删除失败'); } if(Db::name('GoodsSpecValue')->where(['goods_id'=>$goods_ids])->delete() === false) { throw new \Exception('规格值删除失败'); } if(Db::name('GoodsSpecBase')->where(['goods_id'=>$goods_ids])->delete() === false) { throw new \Exception('规格基础删除失败'); } // 相册 if(Db::name('GoodsPhoto')->where(['goods_id'=>$goods_ids])->delete() === false) { throw new \Exception('相册删除失败'); } // app内容 if(Db::name('GoodsContentApp')->where(['goods_id'=>$goods_ids])->delete() === false) { throw new \Exception('手机端内容删除失败'); } // 商品参数 if(Db::name('GoodsParams')->where(['goods_id'=>$goods_ids])->delete() === false) { throw new \Exception('规格参数删除失败'); } // 商品关联仓库信息+库存 if(Db::name('WarehouseGoods')->where(['goods_id'=>$goods_ids])->delete() === false) { throw new \Exception('仓库商品删除失败'); } if(Db::name('WarehouseGoodsSpec')->where(['goods_id'=>$goods_ids])->delete() === false) { throw new \Exception('仓库商品库存删除失败'); } } /** * 商品状态更新 * @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' => '操作id有误', ], [ 'checked_type' => 'empty', 'key_name' => 'field', 'error_msg' => '未指定操作字段', ], [ 'checked_type' => 'in', 'key_name' => 'state', 'checked_data' => [0,1], 'error_msg' => '状态有误', ], ]; $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('操作失败'); } // 商品删除钩子 $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('操作成功'); } 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_old'] = $vs['images']; $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']); $v[] = [ 'data_type' => 'base', 'data' => $base, ]; } } } else { $base = Db::name('GoodsSpecBase')->where($where)->find(); $base['weight'] = PriceBeautify($base['weight']); $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] */ public static function GoodsEditParameters($goods_id) { return Db::name('GoodsParams')->where(['goods_id'=>$goods_id])->order('id asc')->select()->toArray(); } /** * 商品规格信息 * @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' => '商品id有误', ], [ 'checked_type' => 'empty', 'key_name' => 'spec', 'is_checked' => 1, 'error_msg' => '请选择规格', ], ]; $ret = ParamsChecked($params, $p); if($ret !== true) { return DataReturn($ret, -1); } // 条件 $goods_id = intval($params['id']); $where = [ 'goods_id' => intval($params['id']), ]; // 有规格值 if(!empty($params['spec'])) { // 规格不为数组则为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分组 $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); } } } } else { $base = Db::name('GoodsSpecBase')->where($where)->find(); } // 是否有规格 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, 'data' => &$data, 'goods_id' => $goods_id ])); if(isset($ret['code']) && $ret['code'] != 0) { return $ret; } // 价格处理 if(array_key_exists('price', $data['spec_base'])) { $data['spec_base']['price'] = PriceNumberFormat($data['spec_base']['price']); } if(array_key_exists('original_price', $data['spec_base'])) { $data['spec_base']['original_price'] = PriceNumberFormat($data['spec_base']['original_price']); } // 返回成功 return DataReturn('操作成功', 0, $data); } return DataReturn('没有相关规格', -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' => '商品id有误', ], [ 'checked_type' => 'empty', 'key_name' => 'spec', 'error_msg' => '请选择规格', ], ]; $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('操作成功', 0, $data); } } return DataReturn('没有相关规格类型', -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 = []) { // 请求参数 $p = [ [ 'checked_type' => 'empty', 'key_name' => 'id', 'error_msg' => '商品id有误', ], [ 'checked_type' => 'empty', 'key_name' => 'stock', 'error_msg' => '购买数量有误', ], [ 'checked_type' => 'min', 'key_name' => 'stock', 'checked_data' => 1, 'error_msg' => '购买数量有误', ], ]; $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 2018-12-16T23:54:46+0800 * @param [array] $params [输入参数] */ public static function GoodsCategoryNodeSon($params = []) { // id $id = isset($params['id']) ? intval($params['id']) : 0; // 获取数据 $field = 'id,pid,icon,name,sort,is_enable,bg_color,big_images,vice_name,describe,is_home_recommended,seo_title,seo_keywords,seo_desc'; $data = Db::name('GoodsCategory')->field($field)->where(['pid'=>$id])->order('sort asc')->select()->toArray(); if(!empty($data)) { $data = self::GoodsCategoryDataHandle($data); foreach($data as &$v) { $v['is_son'] = (Db::name('GoodsCategory')->where(['pid'=>$v['id']])->count() > 0) ? 'ok' : 'no'; $v['json'] = json_encode($v); } return DataReturn('操作成功', 0, $data); } return DataReturn('没有相关数据', -100); } /** * 商品分类保存 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @datetime 2018-12-17T01:04:03+0800 * @param [array] $params [输入参数] */ public static function GoodsCategorySave($params = []) { // 请求参数 $p = [ [ 'checked_type' => 'length', 'key_name' => 'name', 'checked_data' => '2,16', 'error_msg' => '名称格式 2~16 个字符', ], [ 'checked_type' => 'length', 'key_name' => 'vice_name', 'checked_data' => '60', 'is_checked' => 1, 'error_msg' => '副名称格式 最多30个字符', ], [ 'checked_type' => 'length', 'key_name' => 'describe', 'checked_data' => '200', 'is_checked' => 1, 'error_msg' => '描述格式 最多200个字符', ], [ 'checked_type' => 'length', 'key_name' => 'seo_title', 'checked_data' => '100', 'is_checked' => 1, 'error_msg' => 'SEO标题格式 最多100个字符', ], [ 'checked_type' => 'length', 'key_name' => 'seo_keywords', 'checked_data' => '130', 'is_checked' => 1, 'error_msg' => 'SEO关键字格式 最多130个字符', ], [ 'checked_type' => 'length', 'key_name' => 'seo_desc', 'checked_data' => '230', 'is_checked' => 1, 'error_msg' => 'SEO描述格式 最多230个字符', ], ]; $ret = ParamsChecked($params, $p); if($ret !== true) { return DataReturn($ret, -1); } // 其它附件 $data_fields = ['icon', 'big_images']; $attachment = ResourcesService::AttachmentParams($params, $data_fields); if($attachment['code'] != 0) { return $attachment; } // 数据 $data = [ 'name' => $params['name'], 'pid' => isset($params['pid']) ? intval($params['pid']) : 0, 'vice_name' => isset($params['vice_name']) ? $params['vice_name'] : '', 'describe' => isset($params['describe']) ? $params['describe'] : '', 'bg_color' => isset($params['bg_color']) ? $params['bg_color'] : '', 'is_home_recommended' => isset($params['is_home_recommended']) ? intval($params['is_home_recommended']) : 0, 'sort' => isset($params['sort']) ? intval($params['sort']) : 0, 'is_enable' => isset($params['is_enable']) ? intval($params['is_enable']) : 0, 'icon' => $attachment['data']['icon'], 'big_images' => $attachment['data']['big_images'], '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'], ]; // 父级id宇当前id不能相同 if(!empty($params['id']) && $params['id'] == $data['pid']) { return DataReturn('父级不能与当前相同', -10); } // 添加/编辑 if(empty($params['id'])) { $data['add_time'] = time(); $data['id'] = Db::name('GoodsCategory')->insertGetId($data); if($data['id'] <= 0) { return DataReturn('添加失败', -100); } } else { $data['upd_time'] = time(); if(Db::name('GoodsCategory')->where(['id'=>intval($params['id'])])->update($data) === false) { return DataReturn('编辑失败', -100); } else { $data['id'] = $params['id']; } } // 删除大分类缓存 MyCache(MyConfig('shopxo.cache_goods_category_key'), null); $res = self::GoodsCategoryDataHandle([$data]); return DataReturn('操作成功', 0, json_encode($res[0])); } /** * 商品分类删除 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @datetime 2018-12-17T02:40:29+0800 * @param [array] $params [输入参数] */ public static function GoodsCategoryDelete($params = []) { // 请求参数 $p = [ [ 'checked_type' => 'empty', 'key_name' => 'id', 'error_msg' => '删除数据id有误', ], [ 'checked_type' => 'empty', 'key_name' => 'admin', 'error_msg' => '用户信息有误', ], ]; $ret = ParamsChecked($params, $p); if($ret !== true) { return DataReturn($ret, -1); } // 获取分类下所有分类id $ids = self::GoodsCategoryItemsIds([$params['id']]); $ids[] = $params['id']; // 开始删除 if(Db::name('GoodsCategory')->where(['id'=>$ids])->delete()) { // 删除大分类缓存 MyCache(MyConfig('shopxo.cache_goods_category_key'), null); return DataReturn('删除成功', 0); } return DataReturn('删除失败', -100); } /** * 根据商品id获取分类名称 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-08-29 * @desc description * @param [int] $goods_id [商品id] */ public static function GoodsCategoryNames($goods_id) { $data = Db::name('GoodsCategory')->alias('gc')->join('goods_category_join gci', 'gc.id=gci.category_id')->where(['gci.goods_id'=>$goods_id])->column('gc.name'); return DataReturn('获取成功', 0, $data); } /** * 商品规格扩展数据 * @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('获取成功', 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(IsGoodsSiteTypeConsistent($site_type) == 1) { return DataReturn('success', 0, $site_type); } // 是否展示型商品 if($site_type == 1) { return DataReturn('仅展示', -1, $site_type); } // 仅可单独购买 return DataReturn('仅单买', -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 GoodsBuyButtonList($goods) { // 错误信息 $error = ''; // 是否已下架、还有库存 if($goods['is_shelves'] != 1) { $error = '已下架'; } else { if($goods['inventory'] <= 0) { $error = '没货了'; } } // 按钮列表 // color 颜色类型[main主, second次](默认 main) // type 类型[show展示, buy购买, cart加入购物车, other其他值] // name 名称 // title 元素title说明(可选) // value 数据值(可选) // icon icon类名称(可选) // class 自定义类名称(可选) $data = []; if(empty($error)) { // 获取商品类型 $res = self::GoodsSalesModelType($goods['id'], $goods['site_type']); // 是否展示型 if($res['data'] == 1) { $data[] = [ 'color' => 'main', 'type' => 'show', 'name' => MyC('common_is_exhibition_mode_btn_text', '立即咨询', true), 'value' => MyC('common_customer_store_tel'), 'icon' => 'am-icon-phone', ]; } else { // web端class $class_name = (APPLICATION == 'web') ? 'buy-event login-event' : ''; // 购买 $data[] = [ 'color' => 'main', 'type' => 'buy', 'title' => '点此按钮到下一步确认购买信息', 'name' => (MyC('common_order_is_booking', 0, true) == 1) ? '立即预约' : '立即购买', 'class' => $class_name, 'icon' => '', ]; // 商品类型是否和当前站点类型一致 $res = self::IsGoodsSiteTypeConsistent($goods['id'], $goods['site_type']); if($res['code'] == 0) { // 加入购物车 $data[] = [ 'color' => 'second', 'type' => 'cart', 'title' => '加入购物车', 'name' => '加入购物车', 'class' => $class_name, 'icon' => 'am-icon-opencart', ]; } } } // 商品购买导航按钮钩子 $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 = '暂停销售'; } // 返回数据 $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) { // app与web端不一致 if(APPLICATION == 'app') { // 这里的 ent 值必须和系统中区域块定义的一致 $data = [ ['name'=>'商品', 'ent'=>'.page'], ['name'=>'评价', 'ent'=>'.goods-comment'], ['name'=>'详情', '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' => '详情', 'active' => 1, ], [ 'type' => 'comments', 'name' => '评价('.$comments_count.')', ], [ 'type' => 'guess_you_like', 'name' => '猜你喜欢', ] ]; } // 商品详情中间导航钩子 $hook_name = 'plugins_service_goods_detail_middle_tabs_nav_handle'; MyEventTrigger($hook_name, [ 'hook_name' => $hook_name, 'is_backend' => true, 'goods' => $goods, 'data' => &$data, ]); // 返回数据 if(APPLICATION == 'app') { return $data; } else { return [ 'nav' => $data, 'type' => array_column($data, 'type'), ]; } } /** * 商品二维码生成 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2020-08-13 * @desc description * @param [int] $goods_id [商品id] * @param [int] $add_time [商品创建时间] */ public static function GoodsQrcode($goods_id, $add_time) { // 时间格式、是否已是时间格式 if(strstr($add_time, '-') != false) { $add_time = strtotime($add_time); } // 自定义路径 $path = 'static'.DS.'upload'.DS.'images'.DS.'goods_qrcode'.DS.APPLICATION_CLIENT_TYPE.DS.date('Y', $add_time).DS.date('m', $add_time).DS.date('d', $add_time).DS; // 名称增加站点模式(站点模式不一样商品url地址也会不一样) $filename = $goods_id.SystemBaseService::SiteTypeValue().'.png'; // 二维码处理参数 $params = [ 'path' => DS.$path, 'filename' => $filename, 'content' => MyUrl('index/goods/index', ['id'=>$goods_id], true, true), ]; // 创建二维码 return (new \base\Qrcode())->Create($params); } } ?>