'empty', 'key_name' => 'goods_id', 'error_msg' => '商品id有误', ], [ 'checked_type' => 'empty', 'key_name' => 'stock', 'error_msg' => '购买数量有误', ], [ 'checked_type' => 'min', 'key_name' => 'stock', 'checked_data' => 1, 'error_msg' => '购买数量有误', ], [ 'checked_type' => 'empty', 'key_name' => 'user', 'error_msg' => '用户信息有误', ], ]; $ret = ParamsChecked($params, $p); if($ret !== true) { return DataReturn($ret, -1); } // 查询用户状态是否正常 $ret = UserService::UserStatusCheck('id', $params['user']['id']); if($ret['code'] != 0) { return $ret; } // 获取商品 $goods_id = intval($params['goods_id']); $goods = Db::name('Goods')->where(['id'=>$goods_id, 'is_shelves'=>1, 'is_delete_time'=>0])->find(); if(empty($goods)) { return DataReturn('商品不存在或已删除', -2); } // 无封面图片 if(empty($goods['images'])) { $goods['images'] = ResourcesService::AttachmentPathHandle(GoodsService::GoodsImagesCoverHandle($goods_id)); } // 是否支持购物车操作 $ret = GoodsService::IsGoodsSiteTypeConsistent($goods_id, $goods['site_type']); if($ret['code'] != 0) { return $ret; } // 规格处理 $spec = self::GoodsSpecificationsHandle($params); // 获取商品基础信息 $goods_base = GoodsService::GoodsSpecDetail(['id'=>$goods_id, 'spec'=>$spec]); if($goods_base['code'] != 0) { return $goods_base; } // 是否存在规格 if(!empty($spec)) { // 获取商品规格图片 $images = self::BuyGoodsSpecImages($goods_id, $spec); if(!empty($images)) { $goods['images'] = $images; $goods['images_old'] = ResourcesService::AttachmentPathViewHandle($images); } // 从规格获取库存 $base = GoodsService::GoodsSpecDetail(['id'=>$goods_id, 'spec'=>$spec]); if($base['code'] == 0) { // 规格库存赋值 $goods['inventory'] = $base['data']['spec_base']['inventory']; } else { return $base; } } // 数量 $stock = ($goods['buy_max_number'] > 0 && $params['stock'] > $goods['buy_max_number']) ? $goods['buy_max_number'] : $params['stock']; // 库存 if($stock > $goods['inventory']) { return DataReturn('库存不足', -1); } // 添加购物车 $data = [ 'user_id' => $params['user']['id'], 'goods_id' => $goods_id, 'title' => $goods['title'], 'images' => $goods['images'], 'original_price'=> $goods_base['data']['spec_base']['original_price'], 'price' => $goods_base['data']['spec_base']['price'], 'stock' => $stock, 'spec' => empty($spec) ? '' : json_encode($spec, JSON_UNESCAPED_UNICODE), ]; // 存在则更新 $where = ['user_id'=>$data['user_id'], 'goods_id'=>$data['goods_id'], 'spec'=>$data['spec']]; $temp = Db::name('Cart')->where($where)->find(); if(empty($temp)) { $data['add_time'] = time(); if(Db::name('Cart')->insertGetId($data) > 0) { return DataReturn('加入成功', 0, self::UserCartTotal($params)); } } else { $data['upd_time'] = time(); $data['stock'] += $temp['stock']; if($data['stock'] > $goods['inventory']) { $data['stock'] = $goods['inventory']; } if($goods['buy_max_number'] > 0 && $data['stock'] > $goods['buy_max_number']) { $data['stock'] = $goods['buy_max_number']; } if(Db::name('Cart')->where($where)->update($data)) { return DataReturn('加入成功', 0, self::UserCartTotal($params)); } } return DataReturn('加入失败', -100); } /** * 商品规格解析 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-09-21 * @desc description * @param [array] $params [输入参数] */ public static function GoodsSpecificationsHandle($params = []) { $spec = ''; if(!empty($params['spec'])) { if(!is_array($params['spec'])) { $spec = json_decode(htmlspecialchars_decode($params['spec']), true); } else { $spec = $params['spec']; } } return empty($spec) ? '' : $spec; } /** * 获取购物车列表 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-08-29 * @desc description * @param [array] $params [输入参数] */ public static function CartList($params = []) { // 请求参数 $p = [ [ 'checked_type' => 'empty', 'key_name' => 'user', 'error_msg' => '用户信息有误', ], ]; $ret = ParamsChecked($params, $p); if($ret !== true) { return DataReturn($ret, -1); } $where = (!empty($params['where']) && is_array($params['where'])) ? $params['where'] : []; $where['c.user_id'] = $params['user']['id']; $field = 'c.*, g.inventory_unit, g.is_shelves, g.is_delete_time, g.buy_min_number, g.buy_max_number, g.model, g.site_type'; $data = Db::name('Cart')->alias('c')->leftJoin('goods g', 'g.id=c.goods_id')->where($where)->field($field)->order('c.id desc')->select()->toArray(); // 数据处理 if(!empty($data)) { foreach($data as &$v) { // 规格 $v['spec'] = empty($v['spec']) ? null : json_decode($v['spec'], true); // 获取商品基础信息 $goods_base = GoodsService::GoodsSpecDetail(['id'=>$v['goods_id'], 'spec'=>$v['spec'], 'stock'=>$v['stock']]); $v['is_invalid'] = 0; if($goods_base['code'] == 0) { $v['inventory'] = $goods_base['data']['spec_base']['inventory']; $v['price'] = $goods_base['data']['spec_base']['price']; $v['original_price'] = $goods_base['data']['spec_base']['original_price']; $v['spec_weight'] = $goods_base['data']['spec_base']['weight']; $v['spec_coding'] = $goods_base['data']['spec_base']['coding']; $v['spec_barcode'] = $goods_base['data']['spec_base']['barcode']; $v['extends'] = $goods_base['data']['spec_base']['extends']; } else { $v['is_invalid'] = 1; $v['inventory'] = 0; $v['spec_weight'] = 0; $v['spec_coding'] = ''; $v['spec_barcode'] = ''; $v['extends'] = ''; } // 无封面图片 if(empty($v['images'])) { $v['images'] = ResourcesService::AttachmentPathHandle(GoodsService::GoodsImagesCoverHandle($v['goods_id'])); } // 基础信息 $v['goods_url'] = MyUrl('index/goods/index', ['id'=>$v['goods_id']]); $v['images_old'] = $v['images']; $v['images'] = ResourcesService::AttachmentPathViewHandle($v['images']); $v['total_price'] = PriceNumberFormat($v['stock']* $v['price']); $v['buy_max_number'] = ($v['buy_max_number'] <= 0) ? $v['inventory']: $v['buy_max_number']; // 错误处理 $v['is_error'] = 0; $v['error_msg'] = ''; if($v['is_delete_time'] != 0) { $v['is_error'] = 1; $v['error_msg'] = '已作废'; } if(empty($v['error_msg']) && $v['is_invalid'] == 1) { $v['is_error'] = 1; $v['error_msg'] = '已失效'; } if(empty($v['error_msg']) && $v['is_shelves'] != 1) { $v['is_error'] = 1; $v['error_msg'] = '已下架'; } if(empty($v['error_msg']) && $v['inventory'] <= 0) { $v['is_error'] = 1; $v['error_msg'] = '没货了'; } if(empty($v['error_msg'])) { $ret = GoodsService::IsGoodsSiteTypeConsistent($v['goods_id'], $v['site_type']); if($ret['code'] != 0) { $v['is_error'] = 1; $v['error_msg'] = $ret['msg']; } } } } return DataReturn('操作成功', 0, $data); } /** * 购物车删除 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-09-14 * @desc description * @param [array] $params [输入参数] */ public static function CartDelete($params = []) { // 请求参数 $p = [ [ 'checked_type' => 'empty', 'key_name' => 'id', 'error_msg' => '删除数据id有误', ], [ 'checked_type' => 'empty', 'key_name' => 'user', 'error_msg' => '用户信息有误', ], ]; $ret = ParamsChecked($params, $p); if($ret !== true) { return DataReturn($ret, -1); } // 查询用户状态是否正常 $ret = UserService::UserStatusCheck('id', $params['user']['id']); if($ret['code'] != 0) { return $ret; } // 删除 $where = [ 'id' => is_array($params['id']) ? $params['id'] : explode(',', $params['id']), 'user_id' => $params['user']['id'] ]; if(Db::name('Cart')->where($where)->delete()) { return DataReturn('删除成功', 0, self::UserCartTotal($params)); } return DataReturn('删除失败或资源不存在', -100); } /** * 购物车数量保存 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-09-14 * @desc description * @param [array] $params [输入参数] */ public static function CartStock($params = []) { // 请求参数 $p = [ [ 'checked_type' => 'empty', 'key_name' => 'id', 'error_msg' => '数据id有误', ], [ 'checked_type' => 'empty', 'key_name' => 'goods_id', 'error_msg' => '商品id有误', ], [ 'checked_type' => 'empty', 'key_name' => 'stock', 'error_msg' => '购买数量有误', ], [ 'checked_type' => 'min', 'key_name' => 'stock', 'checked_data' => 1, 'error_msg' => '购买数量有误', ], [ 'checked_type' => 'empty', 'key_name' => 'user', 'error_msg' => '用户信息有误', ], ]; $ret = ParamsChecked($params, $p); if($ret !== true) { return DataReturn($ret, -1); } // 查询用户状态是否正常 $ret = UserService::UserStatusCheck('id', $params['user']['id']); if($ret['code'] != 0) { return $ret; } // 条件 $where = [ 'id' => intval($params['id']), 'goods_id' => intval($params['goods_id']), 'user_id' => intval($params['user']['id']), ]; // 数量 $stock = intval($params['stock']); // 获取购物车数据 $data = Db::name('Cart')->where($where)->field('goods_id,title,price,stock,spec')->find(); if(empty($data)) { return DataReturn('请先加入购物车', -1); } $data['stock'] = $stock; $data['spec'] = empty($data['spec']) ? null : json_decode($data['spec'], true); // 商品校验 $ret = self::BuyGoodsCheck(['goods'=>[$data]]); if($ret['code'] != 0) { return $ret; } // 更新数据 $upd_data = [ 'stock' => $stock, 'upd_time' => time(), ]; if(Db::name('Cart')->where($where)->update($upd_data)) { // 获取商品基础信息、更新商品价格信息 $goods_base = GoodsService::GoodsSpecDetail(['id'=>$data['goods_id'], 'spec'=>$data['spec'], 'stock'=>$data['stock']]); if($goods_base['code'] == 0) { $data['price'] = $goods_base['data']['spec_base']['price']; $data['original_price'] = $goods_base['data']['spec_base']['original_price']; } // 增加价格总计 $data['total_price'] = PriceNumberFormat($data['stock']*$data['price']); // 购物车更新成功钩子 $hook_name = 'plugins_service_cart_update_success'; $ret = EventReturnHandle(MyEventTrigger($hook_name, [ 'hook_name' => $hook_name, 'is_backend' => true, 'params' => $params, 'data' => &$data, 'goods_id' => $params['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 2018-09-21 * @desc description * @param [array] $params [输入参数] */ public static function BuyGoods($params = []) { // 请求参数 $p = [ [ 'checked_type' => 'empty', 'key_name' => 'stock', 'error_msg' => '购买数量有误', ], [ 'checked_type' => 'min', 'key_name' => 'stock', 'checked_data' => 1, 'error_msg' => '购买数量有误', ], [ 'checked_type' => 'empty', 'key_name' => 'goods_id', 'error_msg' => '商品id有误', ], [ 'checked_type' => 'isset', 'key_name' => 'spec', 'error_msg' => '规格参数有误', ], ]; $ret = ParamsChecked($params, $p); if($ret !== true) { return DataReturn($ret, -1); } // 获取商品 $p = [ 'where' => [ 'id' => intval($params['goods_id']), 'is_delete_time' => 0, 'is_shelves' => 1, ], 'field' => 'id, id AS goods_id, title, images, inventory_unit, buy_min_number, buy_max_number, model', ]; $ret = GoodsService::GoodsList($p); if(empty($ret['data'][0])) { return DataReturn('资源不存在或已被删除', -10); } $goods = $ret['data'][0]; // 规格 $goods['spec'] = self::GoodsSpecificationsHandle($params); // 获取商品基础信息 $goods_base = GoodsService::GoodsSpecDetail(['id'=>$goods['goods_id'], 'spec'=>$goods['spec'], 'stock'=>$params['stock']]); if($goods_base['code'] == 0) { $goods['inventory'] = $goods_base['data']['spec_base']['inventory']; $goods['price'] = (float) $goods_base['data']['spec_base']['price']; $goods['original_price'] = (float) $goods_base['data']['spec_base']['original_price']; $goods['spec_weight'] = $goods_base['data']['spec_base']['weight']; $goods['spec_coding'] = $goods_base['data']['spec_base']['coding']; $goods['spec_barcode'] = $goods_base['data']['spec_base']['barcode']; $goods['extends'] = $goods_base['data']['spec_base']['extends']; } else { return $goods_base; } // 获取商品规格图片 if(!empty($goods['spec'])) { $images = self::BuyGoodsSpecImages($goods['goods_id'], $goods['spec']); if(!empty($images)) { $goods['images'] = ResourcesService::AttachmentPathViewHandle($images); $goods['images_old'] = $images; } } // 数量/小计 $goods['stock'] = $params['stock']; $goods['total_price'] = $params['stock']* ((float) $goods['price']); $ret['data'] = [$goods]; return $ret; } /** * 下订单 - 购物车 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-09-21 * @desc description * @param [array] $params [输入参数] */ public static function BuyCart($params = []) { // 请求参数 $p = [ [ 'checked_type' => 'empty', 'key_name' => 'ids', 'error_msg' => '购物车id有误', ], [ 'checked_type' => 'empty', 'key_name' => 'user', 'error_msg' => '用户信息有误', ], ]; $ret = ParamsChecked($params, $p); if($ret !== true) { return DataReturn($ret, -1); } // 获取购物车数据 $params['where'] = [ 'g.is_delete_time' => 0, 'g.is_shelves' => 1, 'c.id' => explode(',', $params['ids']), ]; return self::CartList($params); } /** * 获取规格图片 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2019-08-02 * @desc description * @param [int] $goods_id [商品id] * @param [string] $spec [图片地址或空字符串] */ public static function BuyGoodsSpecImages($goods_id, $spec) { if(!empty($spec)) { $data = Db::name('GoodsSpecType')->where(['goods_id'=>$goods_id])->field('name,value')->select()->toArray(); if(!empty($data)) { $spec_images = []; foreach($data as $v) { if(!empty($v['value'])) { foreach(json_decode($v['value'], true) as $vs) { if(!empty($vs['images'])) { $spec_images[$v['name']][$vs['name']] = $vs['images']; } } } } if(!empty($spec_images)) { foreach($spec as $v) { if(array_key_exists($v['type'], $spec_images) && array_key_exists($v['value'], $spec_images[$v['type']])) { return $spec_images[$v['type']][$v['value']]; } } } } } return ''; } /** * 下订单购物车删除 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @datetime 2018-10-12T00:42:49+0800 * @param [array] $params [输入参数] */ public static function BuyCartDelete($params = []) { if(isset($params['buy_type']) && $params['buy_type'] == 'cart' && !empty($params['ids'])) { Db::name('Cart')->where(['id'=>explode(',', $params['ids'])])->delete(); } } /** * 根据购买类型获取商品列表 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-09-26 * @desc description * @param [array] $params [输入参数] */ public static function BuyTypeGoodsList($params = []) { if(isset($params['buy_type'])) { switch($params['buy_type']) { // 正常购买 case 'goods' : $ret = self::BuyGoods($params); break; // 购物车 case 'cart' : $ret = self::BuyCart($params); break; // 默认 default : $ret = DataReturn('参数有误', -1); } } else { $ret = DataReturn('参数有误', -1); } // 数据组装 if($ret['code'] == 0) { // 商品数据 $goods = $ret['data']; // 站点模式 0销售, 2自提, 4销售+自提, 则其它正常模式 $user_site_model = isset($params['site_model']) ? intval($params['site_model']) : 0; $common_site_type = SystemBaseService::SiteTypeValue(); $site_model = ($common_site_type == 4) ? $user_site_model : $common_site_type; // 商品销售模式 // 商品小于等于1则使用商品的类型 if(count($goods) <= 1 && isset($goods[0]) && isset($goods[0]['goods_id'])) { $ret = GoodsService::GoodsSalesModelType($goods[0]['goods_id']); $common_site_type = $ret['data']; $site_model = ($ret['data'] == 4) ? $user_site_model : $ret['data']; } // 数据处理 $address = null; $extraction_address = []; // 站点模式 - 用户收货地址(未选择则取默认地址) // 销售, 销售+自提(指定为销售) if($site_model == 0) { $address_params = [ 'user' => $params['user'], ]; if(!empty($params['address_id'])) { $address_params['where'] = ['id' => $params['address_id']]; } $ads = UserAddressService::UserDefaultAddress($address_params); if(!empty($ads['data'])) { $address = $ads['data']; } } // 自提模式 - 自提点地址 // 自提, 销售+自提(指定为自提) if($site_model == 2) { $extraction = self::SiteExtractionAddress($params); if(!empty($extraction['data']['data_list'])) { $extraction_address = $extraction['data']['data_list']; } if(!empty($extraction['data']['default'])) { $address = $extraction['data']['default']; } } // 订单拆分处理 return self::OrderSplitHandle($site_model, $common_site_type, $address, $extraction_address, $goods, $params); } return $ret; } /** * 订单拆分处理 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2021-09-06 * @desc description * @param [int] $site_model [当前指定类型] * @param [int] $common_site_type [当前站点类型] * @param [array] $address [收货地址] * @param [array] $extraction_address [自提地址] * @param [array] $goods [订单商品] * @param [array] $params [输入参数] */ public static function OrderSplitHandle($site_model, $common_site_type, $address, $extraction_address, $goods, $params) { // 订单拆分 $order_split = OrderSplitService::Run([ 'site_model' => $site_model, 'common_site_type' => $common_site_type, 'address' => $address, 'extraction_address' => $extraction_address, 'goods' => $goods, 'params' => $params, ]); if($order_split['code'] != 0) { return $order_split; } // 订单总计基础信息字段处理 $base_fields = [ 'total_price' => 0, 'actual_price' => 0, 'preferential_price' => 0, 'increase_price' => 0, 'goods_count' => 0, 'spec_weight_total' => 0, 'buy_count' => 0, ]; if(!empty($order_split['data'])) { $order_base = array_column($order_split['data'], 'order_base'); foreach($base_fields as $field=>$value) { $base_fields[$field] = array_sum(array_column($order_base, $field)); } } // 订单总计基础信息组合 $base = [ // 总价 'total_price' => $base_fields['total_price'], // 订单实际支付金额(已减去优惠金额, 已加上增加金额) 'actual_price' => $base_fields['actual_price'], // 优惠金额 'preferential_price' => $base_fields['preferential_price'], // 增加金额 'increase_price' => $base_fields['increase_price'], // 商品数量 'goods_count' => $base_fields['goods_count'], // 规格重量总计 'spec_weight_total' => $base_fields['spec_weight_total'], // 购买总数 'buy_count' => $base_fields['buy_count'], // 默认地址 'address' => $address, // 自提地址列表 'extraction_address' => $extraction_address, // 当前使用的站点模式 'site_model' => $site_model, // 公共站点模式 'common_site_type' => $common_site_type, ]; // 返回数据 $result = [ 'goods' => $order_split['data'], 'base' => $base, ]; // 生成订单数据处理钩子 $hook_name = 'plugins_service_buy_handle'; $ret = EventReturnHandle(MyEventTrigger($hook_name, [ 'hook_name' => $hook_name, 'is_backend' => true, 'params' => $params, 'data' => &$result, ])); if(isset($ret['code']) && $ret['code'] != 0) { return $ret; } // 返回数据再次处理,防止钩子插件处理不够完善 $result['base']['total_price'] = ($result['base']['total_price'] <= 0) ? 0.00 : PriceNumberFormat($result['base']['total_price']); $result['base']['actual_price'] = ($result['base']['actual_price'] <= 0) ? 0.00 : PriceNumberFormat($result['base']['actual_price']); $result['base']['preferential_price'] = ($result['base']['preferential_price'] <= 0) ? 0.00 : PriceNumberFormat($result['base']['preferential_price']); $result['base']['increase_price'] = ($result['base']['increase_price'] <= 0) ? 0.00 : PriceNumberFormat($result['base']['increase_price']); return DataReturn('操作成功', 0, $result); } /** * 购买商品校验 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-09-26 * @desc description * @param [array] $params [输入参数] */ public static function BuyGoodsCheck($params = []) { // 请求参数 $p = [ [ 'checked_type' => 'empty', 'key_name' => 'goods', 'error_msg' => '商品信息为空', ], [ 'checked_type' => 'is_array', 'key_name' => 'goods', 'error_msg' => '商品信息有误', ] ]; $ret = ParamsChecked($params, $p); if($ret !== true) { return DataReturn($ret, -1); } // 商品总数 $count = count($params['goods']); // 是否需要校验商品类型、is_buy、1校验、默认0不校验 // 商品小于等于1不校验 $is_check_goods_site_type = (isset($params['is_buy']) && $params['is_buy'] == 1) ? 1 : 0; if($is_check_goods_site_type == 1 && $count <= 1) { $is_check_goods_site_type = 0; } // 数据校验 foreach($params['goods'] as $v) { // 获取商品信息 $goods = Db::name('Goods')->field('id,title,price,is_shelves,inventory,buy_min_number,buy_max_number,site_type')->find($v['goods_id']); if(empty($goods)) { return DataReturn('商品不存在['.$v['title'].']', -1); } // 基础判断 if($goods['is_shelves'] != 1) { return DataReturn('商品已下架['.$goods['title'].']', -1); } // 限购 if($goods['buy_min_number'] > 1 && $v['stock'] < $goods['buy_min_number']) { return DataReturn('低于商品起购数量['.$goods['title'].']['.$v['stock'].'<'.$goods['buy_min_number'].']', -1); } if($goods['buy_max_number'] > 0 && $v['stock'] > $goods['buy_max_number']) { return DataReturn('超过商品限购数量['.$goods['title'].']['.$v['stock'].'>'.$goods['buy_max_number'].']', -1); } // 是否支持购物车操作 if($is_check_goods_site_type) { $ret = GoodsService::IsGoodsSiteTypeConsistent($goods['id'], $goods['site_type']); if($ret['code'] != 0) { return DataReturn($ret['msg'].'['.$v['title'].']', $ret['code']); } } // 规格 $goods_base = GoodsService::GoodsSpecDetail(['id'=>$v['goods_id'], 'spec'=>isset($v['spec']) ? $v['spec'] : []]); if($goods_base['code'] == 0) { $goods['price'] = $goods_base['data']['spec_base']['price']; $goods['inventory'] = $goods_base['data']['spec_base']['inventory']; } else { return $goods_base; } // 库存 if($v['stock'] > $goods['inventory']) { return DataReturn('购买数量超过商品库存数量['.$goods['title'].']['.$v['stock'].'>'.$goods['inventory'].']', -1); } } return DataReturn('校验成功', 0); } /** * 订单添加 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-09-26 * @desc description * @param [array] $params [输入参数] */ public static function OrderInsert($params = []) { // 站点类型,是否开启了展示型 $common_site_type = SystemBaseService::SiteTypeValue(); if($common_site_type == 1) { return DataReturn('展示型不允许提交订单', -1); } // 销售+自提, 用户自选站点类型 $user_site_model = isset($params['site_model']) ? intval($params['site_model']) : 0; $site_model = ($common_site_type == 4) ? $user_site_model : $common_site_type; // 请求参数 $p = [ [ 'checked_type' => 'empty', 'key_name' => 'user', 'error_msg' => '用户信息有误', ], ]; // // 销售型,自提点,销售+自提 则校验地址 if(in_array($site_model, [0,2])) { $p[] = [ 'checked_type' => 'isset', 'key_name' => 'address_id', 'error_msg' => '请选择地址', ]; } // 非预约模式则校验支付方式 if(MyC('common_order_is_booking', 0) != 1) { $p[] = [ 'checked_type' => 'empty', 'key_name' => 'payment_id', 'error_msg' => '支付方式有误', ]; } $ret = ParamsChecked($params, $p); if($ret !== true) { return DataReturn($ret, -1); } // 查询用户状态是否正常 $ret = UserService::UserStatusCheck('id', $params['user']['id']); if($ret['code'] != 0) { return $ret; } // 清单商品 $params['is_order_submit'] = 1; $buy = self::BuyTypeGoodsList($params); if(!isset($buy['code']) || $buy['code'] != 0) { return $buy; } // 用户留言 $user_note = empty($params['user_note']) ? '' : str_replace(['"', "'"], '', strip_tags($params['user_note'])); // 订单默认状态 $order_status = (intval(MyC('common_order_is_booking', 0)) == 1) ? 0 : 1; // 支付方式 $payment_id = 0; $is_under_line = 0; if(!empty($params['payment_id'])) { $payment = PaymentService::PaymentList(['where'=>['id'=>intval($params['payment_id'])]]); if(empty($payment[0])) { return DataReturn('支付方式有误', -1); } $payment_id = $payment[0]['id']; $is_under_line = in_array($payment[0]['payment'], MyConfig('shopxo.under_line_list')) ? 1 : 0; // 线下支付订单是否直接成功 // 是否开启线下订单正常进入流程 if($is_under_line == 1 && MyC('common_is_under_line_order_normal') == 1) { $order_status = 2; } } // 循环处理数据 $order_data = []; foreach($buy['data']['goods'] as $v) { // 商品销售模式 // 商品小于等于1则使用商品的类型 if($v['order_base']['goods_count'] == 1) { $ret = GoodsService::GoodsSalesModelType($v['goods_items'][0]['goods_id']); $site_model = ($ret['data'] == 4) ? $user_site_model : $ret['data']; } // 商品校验 $check = self::BuyGoodsCheck(['goods'=>$v['goods_items'], 'is_buy'=>1]); if($check['code'] != 0) { return $check; } // 销售型,自提点,销售+自提 地址处理 $address = []; if(in_array($site_model, [0,2])) { if(empty($v['order_base']['address'])) { return DataReturn('地址有误', -1); } else { $address = $v['order_base']['address']; } } // 订单主信息 $order = [ 'user_id' => $params['user']['id'], 'warehouse_id' => $v['id'], 'user_note' => $user_note, 'status' => $order_status, 'preferential_price' => ($v['order_base']['preferential_price'] <= 0.00) ? 0.00 : $v['order_base']['preferential_price'], 'increase_price' => ($v['order_base']['increase_price'] <= 0.00) ? 0.00 : $v['order_base']['increase_price'], 'price' => ($v['order_base']['total_price'] <= 0.00) ? 0.00 : $v['order_base']['total_price'], 'total_price' => ($v['order_base']['actual_price'] <= 0.00) ? 0.00 : $v['order_base']['actual_price'], 'extension_data' => empty($v['order_base']['extension_data']) ? '' : json_encode($v['order_base']['extension_data'], JSON_UNESCAPED_UNICODE), 'payment_id' => $payment_id, 'buy_number_count' => $v['order_base']['buy_count'], 'client_type' => APPLICATION_CLIENT_TYPE, 'order_model' => $site_model, 'is_under_line' => $is_under_line, ]; // 订单地址 $order['address_data'] = $address; // 订单详情 $order['detail_data'] = []; foreach($v['goods_items'] as $vs) { $order['detail_data'][] = [ 'user_id' => $order['user_id'], 'goods_id' => $vs['goods_id'], 'title' => $vs['title'], 'images' => $vs['images'], 'original_price' => $vs['original_price'], 'price' => $vs['price'], 'total_price' => PriceNumberFormat($vs['stock']*$vs['price']), 'spec' => empty($vs['spec']) ? '' : json_encode($vs['spec'], JSON_UNESCAPED_UNICODE), 'spec_weight' => empty($vs['spec_weight']) ? 0.00 : (float) $vs['spec_weight'], 'spec_coding' => empty($vs['spec_coding']) ? '' : $vs['spec_coding'], 'spec_barcode' => empty($vs['spec_barcode']) ? '' : $vs['spec_barcode'], 'buy_number' => intval($vs['stock']), 'model' => $vs['model'], 'extends' => empty($vs['extends']) ? [] : json_decode($vs['extends'], true), ]; } $order_data[] = $order; } // 订单添加处理 $order_ids = []; $ret = self::OrderInsertHandle($order_data, $params); if($ret['code'] == 0) { $order_ids = $ret['data']; } else { return $ret; } // 删除购物车 self::BuyCartDelete($params); // 返回信息 $result = [ 'order_status' => $order_status, 'order_ids' => $order_ids, 'jump_url' => MyUrl('index/order/index'), ]; // 获取订单信息 switch($order_status) { // 预约成功 case 0 : $msg = '预约成功'; break; // 提交成功,进入合并支付 case 1 : $msg = '提交成功'; $result['jump_url'] = MyUrl('index/order/pay', ['ids'=>implode(',', $order_ids)]); break; // 默认操作成功 default : $msg = '操作成功'; } return DataReturn($msg, 0, $result); } /** * 订单添加处理 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2021-09-05 * @desc description * @param [array] $data [订单数据] * @param [array] $params [输入参数] */ public static function OrderInsertHandle($data, $params = []) { // 所有订单id $order_ids = []; // 启动事务 Db::startTrans(); // 捕获异常 try { // 循环处理 foreach($data as $v) { // 订单主信息 $v['order_no'] = self::OrderNoCreate(); $v['add_time'] = time(); // 确认时间 if($v['status'] == 1) { $v['confirm_time'] = time(); } // 订单数据 $order = $v; unset($order['address_data'], $order['detail_data']); // 订单添加前钩子 $hook_name = 'plugins_service_buy_order_insert_begin'; $ret = EventReturnHandle(MyEventTrigger($hook_name, [ 'hook_name' => $hook_name, 'is_backend' => true, 'data' => $v, 'order' => &$order, 'goods' => &$v['detail_data'], 'params' => $params, ])); if(isset($ret['code']) && $ret['code'] != 0) { throw new \Exception($ret['msg']); } // 订单添加 $order_id = Db::name('Order')->insertGetId($order); if($order_id <= 0) { throw new \Exception('订单添加失败'); } $order['id'] = $order_id; // 订单详情添加 foreach($v['detail_data'] as &$vs) { // 添加订单详情数据,data返回自增id $order_detail_id = 0; $ret = self::OrderDetailInsert($order_id, $order['user_id'], $vs); if($ret['code'] == 0) { $order_detail_id = $ret['data']; $vs['id'] = $order_detail_id; } else { throw new \Exception($ret['msg']); } // 订单模式 - 虚拟信息添加 if($order['order_model'] == 3) { $ret = self::OrderFictitiousValueInsert($order_id, $order_detail_id, $order['user_id'], $vs['goods_id']); if($ret['code'] != 0) { throw new \Exception($ret['msg']); } } } // 订单模式处理 // 销售型模式,自提模式,销售+自提 if(in_array($order['order_model'], [0,2])) { // 添加订单(收货|取货)地址 if(!empty($v['address_data'])) { $ret = self::OrderAddressInsert($order_id, $order['user_id'], $v['address_data']); if($ret['code'] != 0) { throw new \Exception($ret['msg']); } } // 自提模式 添加订单取货码 if($order['order_model'] == 2) { $ret = self::OrderExtractionCcodeInsert($order_id, $order['user_id']); if($ret['code'] != 0) { throw new \Exception($ret['msg']); } } } // 订单货币 $ret = OrderCurrencyService::OrderCurrencyInsert($order_id, $order['user_id']); if($ret['code'] != 0) { throw new \Exception($ret['msg']); } // 库存扣除 if(in_array($order['status'], [1,2])) { $ret = self::OrderInventoryDeduct(['order_id'=>$order_id, 'opt_type'=>'confirm']); if($ret['code'] != 0) { throw new \Exception($ret['msg']); } } // 订单添加成功钩子 $hook_name = 'plugins_service_buy_order_insert_end'; $ret = EventReturnHandle(MyEventTrigger($hook_name, [ 'hook_name' => $hook_name, 'is_backend' => true, 'order_id' => $order_id, 'order' => $order, 'data' => $v, 'goods' => $v['detail_data'], 'address' => $v['address_data'], 'params' => $params, ])); if(isset($ret['code']) && $ret['code'] != 0) { throw new \Exception($ret['msg']); } // 订单id集合 $order_ids[] = $order_id; } // 完成 Db::commit(); } catch(\Exception $e) { Db::rollback(); return DataReturn($e->getMessage(), -1); } // 订单添加成功钩子, 不校验返回值 $hook_name = 'plugins_service_buy_order_insert_success'; MyEventTrigger($hook_name, [ 'hook_name' => $hook_name, 'is_backend' => true, 'order_ids' => $order_ids, 'params' => $params, ]); // 返回信息 return DataReturn('操作成功', 0, $order_ids); } /** * 订单号生成 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2021-08-17 * @desc description */ public static function OrderNoCreate() { return date('YmdHis').GetNumberCode(6); } /** * 订单详情添加 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2019-11-20 * @desc description * @param [int] $order_id [订单id] * @param [int] $user_id [用户id] * @param [array] $detail [商品详情数据] */ private static function OrderDetailInsert($order_id, $user_id, $detail) { $data = [ 'order_id' => $order_id, 'user_id' => $user_id, 'goods_id' => $detail['goods_id'], 'title' => $detail['title'], 'images' => ResourcesService::AttachmentPathHandle($detail['images']), 'original_price' => $detail['original_price'], 'price' => $detail['price'], 'total_price' => PriceNumberFormat(isset($detail['total_price']) ? $detail['total_price'] : $detail['total_price']*$detail['buy_number']), 'spec' => empty($detail['spec']) ? '' : (is_array($detail['spec']) ? json_encode($detail['spec'], JSON_UNESCAPED_UNICODE) : $detail['spec']), 'spec_weight' => empty($detail['spec_weight']) ? 0.00 : (float) $detail['spec_weight'], 'spec_coding' => empty($detail['spec_coding']) ? '' : $detail['spec_coding'], 'spec_barcode' => empty($detail['spec_barcode']) ? '' : $detail['spec_barcode'], 'buy_number' => intval($detail['buy_number']), 'model' => $detail['model'], 'add_time' => time(), ]; // 订单详情添加前钩子 $hook_name = 'plugins_service_buy_order_detail_insert_begin'; $ret = EventReturnHandle(MyEventTrigger($hook_name, [ 'hook_name' => $hook_name, 'is_backend' => true, 'order_id' => $order_id, 'user_id' => $user_id, 'data' => &$data, ])); if(isset($ret['code']) && $ret['code'] != 0) { return $ret; } // 添加订单详情数据 $order_detail_id = Db::name('OrderDetail')->insertGetId($data); if($order_detail_id > 0) { return DataReturn('添加成功', 0, $order_detail_id); } return DataReturn('订单详情添加失败', -1); } /** * 订单关联自提取货码添加 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2019-11-20 * @desc description * @param [int] $order_id [订单id] * @param [int] $user_id [用户id] */ private static function OrderExtractionCcodeInsert($order_id, $user_id) { $data = [ 'order_id' => $order_id, 'user_id' => $user_id, 'code' => GetNumberCode(4), 'add_time' => time(), ]; // 订单取货码添加前钩子 $hook_name = 'plugins_service_buy_order_extraction_code_insert_begin'; $ret = EventReturnHandle(MyEventTrigger($hook_name, [ 'hook_name' => $hook_name, 'is_backend' => true, 'user_id' => $user_id, 'order_id' => $order_id, 'data' => &$data, ])); if(isset($ret['code']) && $ret['code'] != 0) { return $ret; } // 添加订单虚拟数据 if(Db::name('OrderExtractionCode')->insertGetId($data) > 0) { return DataReturn('添加成功', 0); } return DataReturn('订单取货码添加失败', -1); } /** * 订单关联虚拟销售数据添加 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2019-11-20 * @desc description * @param [int] $order_id [订单id] * @param [int] $order_detail_id [订单详情id] * @param [int] $user_id [用户id] * @param [int] $goods_id [商品id] */ private static function OrderFictitiousValueInsert($order_id, $order_detail_id, $user_id, $goods_id) { $data = [ 'order_id' => $order_id, 'order_detail_id' => $order_detail_id, 'user_id' => $user_id, 'value' => Db::name('Goods')->where(['id'=>$goods_id])->value('fictitious_goods_value'), 'add_time' => time(), ]; // 订单虚拟数据添加前钩子 $hook_name = 'plugins_service_buy_order_fictitious_insert_begin'; $ret = EventReturnHandle(MyEventTrigger($hook_name, [ 'hook_name' => $hook_name, 'is_backend' => true, 'user_id' => $user_id, 'order_id' => $order_id, 'order_detail_id' => $order_detail_id, 'goods_id' => $goods_id, 'data' => &$data, ])); if(isset($ret['code']) && $ret['code'] != 0) { return $ret; } // 添加订单虚拟数据 if(Db::name('OrderFictitiousValue')->insertGetId($data) > 0) { return DataReturn('添加成功', 0); } return DataReturn('订单虚拟信息添加失败', -1); } /** * 订单关联地址添加 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2019-11-20 * @desc description * @param [int] $order_id [订单id] * @param [int] $user_id [用户id] * @param [array] $address [地址] */ private static function OrderAddressInsert($order_id, $user_id, $address) { // 坐标处理 if(in_array(APPLICATION_CLIENT_TYPE, MyConfig('shopxo.coordinate_transformation'))) { // 坐标转换 火星(高德,谷歌,腾讯坐标) 转 百度 if(isset($address['lng']) && isset($address['lat'])) { $map = \base\GeoTransUtil::GcjToBd($address['lng'], $address['lat']); if(isset($map['lng']) && isset($map['lat'])) { $address['lng'] = $map['lng']; $address['lat'] = $map['lat']; } } } // 订单收货地址 $data = [ 'order_id' => $order_id, 'user_id' => $user_id, 'address_id' => isset($address['id']) ? intval($address['id']) : 0, 'alias' => isset($address['alias']) ? $address['alias'] : '', 'name' => isset($address['name']) ? $address['name'] : '', 'tel' => isset($address['tel']) ? $address['tel'] : '', 'province' => isset($address['province']) ? intval($address['province']) : 0, 'city' => isset($address['city']) ? intval($address['city']) : 0, 'county' => isset($address['county']) ? intval($address['county']) : 0, 'address' => isset($address['address']) ? $address['address'] : '', 'province_name' => isset($address['province_name']) ? $address['province_name'] : '', 'city_name' => isset($address['city_name']) ? $address['city_name'] : '', 'county_name' => isset($address['county_name']) ? $address['county_name'] : '', 'lng' => isset($address['lng']) ? (float) $address['lng'] : '0.0000000000', 'lat' => isset($address['lat']) ? (float) $address['lat'] : '0.0000000000', 'idcard_name' => empty($address['idcard_name']) ? '' : $address['idcard_name'], 'idcard_number' => empty($address['idcard_number']) ? '' : $address['idcard_number'], 'idcard_front' => empty($address['idcard_front']) ? '' : ResourcesService::AttachmentPathHandle($address['idcard_front']), 'idcard_back' => empty($address['idcard_back']) ? '' : ResourcesService::AttachmentPathHandle($address['idcard_back']), 'add_time' => time(), ]; // 订单地址添加前钩子 $hook_name = 'plugins_service_buy_order_address_insert_begin'; $ret = EventReturnHandle(MyEventTrigger($hook_name, [ 'hook_name' => $hook_name, 'is_backend' => true, 'user_id' => $user_id, 'order_id' => $order_id, 'data' => &$data, ])); if(isset($ret['code']) && $ret['code'] != 0) { return $ret; } // 添加订单地址 if(Db::name('OrderAddress')->insertGetId($data) > 0) { return DataReturn('添加成功', 0); } return DataReturn('订单地址添加失败', -1); } /** * 购物车总数 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-09-29 * @desc description * @param [array] $where [条件] */ public static function CartTotal($where = []) { return (int) Db::name('Cart')->where($where)->count(); } /** * 用户购物车总数 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-09-29 * @desc description * @param [array] $params [输入参数] * @return [int|string] [超过99则返回 99+] */ public static function UserCartTotal($params = []) { // 请求参数 $p = [ [ 'checked_type' => 'empty', 'key_name' => 'user', 'error_msg' => '用户信息有误', ], ]; $ret = ParamsChecked($params, $p); if($ret !== true) { return 0; } // 获取购物车总数 $total = self::CartTotal(['user_id'=>$params['user']['id']]); return ($total > 99) ? '99+' : $total; } /** * 单个订单支付前校验 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-11-09 * @desc description * @param [array] $params [输入参数] */ public static function SingleOrderPayBeginCheck($params = []) { // 请求参数 $p = [ [ 'checked_type' => 'empty', 'key_name' => 'order_id', 'error_msg' => '订单id有误', ], ]; $ret = ParamsChecked($params, $p); if($ret !== true) { return DataReturn($ret, -1); } // 是否扣除库存 $common_is_deduction_inventory = MyC('common_is_deduction_inventory', 0); if($common_is_deduction_inventory != 1) { return DataReturn('未开启扣除库存', 0); } // 扣除库存规则 $common_deduction_inventory_rules = MyC('common_deduction_inventory_rules', 1); // 这里仅订单支付规则类型下校验 if($common_deduction_inventory_rules == 1) { // 获取订单商品 $order_detail = Db::name('OrderDetail')->field('id,goods_id,buy_number,spec')->where(['order_id'=>$params['order_id']])->select()->toArray(); if(empty($order_detail)) { return DataReturn('订单详情有误', -1); } // 数据校验 foreach($order_detail as $v) { $ret = self::BuyOrderPayBeginGoodsCheck($v); if($ret['code'] != 0) { return $ret; } } } return DataReturn('校验成功', 0); } /** * 多个订单下单库存校验 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2020-08-04 * @desc description * @param [array] $params [输入参数] */ public static function MoreOrderPayBeginCheck($params = []) { // 请求参数 $p = [ [ 'checked_type' => 'empty', 'key_name' => 'order_data', 'error_msg' => '订单数据不能为空', ], [ 'checked_type' => 'is_array', 'key_name' => 'order_data', 'error_msg' => '订单数据有误', ] ]; $ret = ParamsChecked($params, $p); if($ret !== true) { return DataReturn($ret, -1); } // 是否扣除库存 $common_is_deduction_inventory = MyC('common_is_deduction_inventory', 0); if($common_is_deduction_inventory != 1) { return DataReturn('未开启扣除库存', 0); } // 扣除库存规则 $common_deduction_inventory_rules = MyC('common_deduction_inventory_rules', 1); // 这里仅订单支付规则类型下校验 if($common_deduction_inventory_rules == 1) { // 数据集合 $detail = Db::name('OrderDetail')->field('id,order_id,goods_id,buy_number,spec')->where(['order_id'=>array_column($params['order_data'], 'id')])->select()->toArray(); if(empty($detail)) { return DataReturn('订单详情有误', -1); } // 订单集合 $order_group = []; foreach($params['order_data'] as $o) { $order_group[$o['id']] = $o['warehouse_id']; } // 订单详情 $data = []; foreach($detail as $d) { $key = md5(empty($d['spec']) ? 'default' : $d['spec']); if(!isset($data[$order_group[$d['order_id']]][$d['goods_id']][$key])) { $data[$order_group[$d['order_id']]][$d['goods_id']][$key] = $d; } else { $data[$order_group[$d['order_id']]][$d['goods_id']][$key]['buy_number'] += $d['buy_number']; } } // 数据校验 foreach($data as $w) { foreach($w as $g) { foreach($g as $v) { $ret = self::BuyOrderPayBeginGoodsCheck($v); if($ret['code'] != 0) { return $ret; } } } } } return DataReturn('校验成功', 0); } /** * 订单支付前商品校验 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2020-08-05 * @desc description * @param [array] $detail [订单详情] */ public static function BuyOrderPayBeginGoodsCheck($detail) { // 获取商品 $goods = Db::name('Goods')->field('is_shelves,is_deduction_inventory,inventory,title')->find($detail['goods_id']); if(empty($goods)) { return DataReturn('商品不存在', -10); } // 商品状态 if($goods['is_shelves'] != 1) { return DataReturn('商品已下架['.$goods['title'].']', -10); } // 库存 if(isset($goods['is_deduction_inventory']) && $goods['is_deduction_inventory'] == 1) { // 先判断商品库存是否不足 if($goods['inventory'] < $detail['buy_number']) { return DataReturn('库存不足['.$goods['title'].'('.$goods['inventory'].'<'.$detail['buy_number'].')]', -10); } // 规格库存 $spec = empty($detail['spec']) ? '' : json_decode($detail['spec'], true); $base = GoodsService::GoodsSpecDetail(['id'=>$detail['goods_id'], 'spec'=>$spec]); if($base['code'] == 0) { // 先判断商品规格库存是否不足 if($base['data']['spec_base']['inventory'] < $detail['buy_number']) { return DataReturn('库存不足['.$goods['title'].'('.$base['data']['spec_base']['inventory'].'<'.$detail['buy_number'].')]', -10); } } else { return $base; } } return DataReturn('校验成功', 0); } /** * 库存扣除 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-11-09 * @desc description * @param [array] $params [输入参数] */ public static function OrderInventoryDeduct($params = []) { // 请求参数 $p = [ [ 'checked_type' => 'empty', 'key_name' => 'order_id', 'error_msg' => '订单id有误', ], [ 'checked_type' => 'in', 'key_name' => 'opt_type', 'checked_data' => ['confirm', 'pay', 'delivery'], 'error_msg' => '订单操作类型有误', ], ]; $ret = ParamsChecked($params, $p); if($ret !== true) { return DataReturn($ret, -1); } // 是否扣除库存 $common_is_deduction_inventory = MyC('common_is_deduction_inventory', 0); if($common_is_deduction_inventory != 1) { return DataReturn('未开启扣除库存', 0); } // 扣除库存规则 $common_deduction_inventory_rules = MyC('common_deduction_inventory_rules', 1); switch($common_deduction_inventory_rules) { // 订单确认成功 case 0 : if($params['opt_type'] != 'confirm') { return DataReturn('当前订单状态未操作确认-不扣除库存['.$params['order_id'].']', 0); } break; // 订单支付成功 case 1 : if($params['opt_type'] != 'pay') { return DataReturn('当前订单状态未操作支付-不扣除库存['.$params['order_id'].']', 0); } break; // 订单发货 case 2 : if($params['opt_type'] != 'delivery') { return DataReturn('当前订单状态未操作发货-不扣除库存['.$params['order_id'].']', 0); } break; } // 获取订单商品 $order_detail = Db::name('OrderDetail')->field('id,goods_id,buy_number,spec')->where(['order_id'=>$params['order_id']])->select()->toArray(); if(!empty($order_detail)) { foreach($order_detail as $v) { // 查看是否已扣除过库存,避免更改模式导致重复扣除 $temp = Db::name('OrderGoodsInventoryLog')->where(['order_id'=>$params['order_id'], 'order_detail_id'=>$v['id'], 'goods_id'=>$v['goods_id']])->find(); if(empty($temp)) { $goods = Db::name('Goods')->field('is_deduction_inventory,inventory,title')->find($v['goods_id']); if(isset($goods['is_deduction_inventory']) && $goods['is_deduction_inventory'] == 1) { // 先判断商品库存是否不足 if($goods['inventory'] < $v['buy_number']) { return DataReturn('商品库存不足['.$goods['title'].'('.$goods['inventory'].'<'.$v['buy_number'].')]', -10); } // 扣除操作 if(!Db::name('Goods')->where(['id'=>$v['goods_id']])->dec('inventory', $v['buy_number'])->update()) { return DataReturn('商品库存扣减失败['.$params['order_id'].'-'.$v['id'].'-'.$v['goods_id'].'('.$goods['inventory'].'-'.$v['buy_number'].')]', -10); } // 扣除规格库存 $spec = empty($v['spec']) ? '' : json_decode($v['spec'], true); $base = GoodsService::GoodsSpecDetail(['id'=>$v['goods_id'], 'spec'=>$spec]); if($base['code'] == 0) { // 先判断商品规格库存是否不足 if($base['data']['spec_base']['inventory'] < $v['buy_number']) { return DataReturn('商品规格库存不足['.$goods['title'].'('.$base['data']['spec_base']['inventory'].'<'.$v['buy_number'].']', -10); } // 扣除规格操作 if(!Db::name('GoodsSpecBase')->where(['id'=>$base['data']['spec_base']['id'], 'goods_id'=>$v['goods_id']])->dec('inventory', $v['buy_number'])->update()) { return DataReturn('规格库存扣减失败['.$params['order_id'].'-'.$v['goods_id'].'('.$goods['inventory'].'-'.$v['buy_number'].')]', -10); } } else { return $base; } // 仓库库存扣除 $we_ret = WarehouseGoodsService::WarehouseGoodsInventoryDeduct($params['order_id'], $v['goods_id'], $spec, $v['buy_number']); if($we_ret['code'] != 0) { return $we_ret; } // 扣除日志添加 $log_data = [ 'order_id' => $params['order_id'], 'order_detail_id' => $v['id'], 'goods_id' => $v['goods_id'], 'order_status' => Db::name('Order')->where(['id'=>$params['order_id']])->value('status'), 'original_inventory' => $goods['inventory'], 'new_inventory' => Db::name('Goods')->where(['id'=>$v['goods_id']])->value('inventory'), 'add_time' => time(), ]; if(Db::name('OrderGoodsInventoryLog')->insertGetId($log_data) <= 0) { return DataReturn('库存扣减日志添加失败['.$params['order_id'].'-'.$v['goods_id'].']', -100); } } } } return DataReturn('操作成功', 0); } return DataReturn('没有需要扣除库存的数据', 0); } /** * 库存回滚 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-11-09 * @desc description * @param [array] $params [输入参数] */ public static function OrderInventoryRollback($params = []) { // 请求参数 $p = [ [ 'checked_type' => 'empty', 'key_name' => 'order_id', 'error_msg' => '订单id有误', ], [ 'checked_type' => 'empty', 'key_name' => 'order_data', 'error_msg' => '订单更新数据不能为空', ], [ 'checked_type' => 'is_array', 'key_name' => 'order_data', 'error_msg' => '订单更新数据有误', ] ]; $ret = ParamsChecked($params, $p); if($ret !== true) { return DataReturn($ret, -1); } // 订单状态 if(isset($params['order_data']['status'])) { // 仅订单取消、关闭操作库存回滚 if(!in_array($params['order_data']['status'], [5,6])) { return DataReturn('当前订单状态不允许回滚库存['.$params['order_id'].'-'.$params['order_data']['status'].']', 0); } } // 是否指定商品和数量 $appoint_buy_number = empty($params['appoint_buy_number']) ? 0 : intval($params['appoint_buy_number']); $detail_where = ['order_id' => $params['order_id']]; if(!empty($params['appoint_order_detail_id'])) { $detail_where['id'] = intval($params['appoint_order_detail_id']); } // 获取订单商品 $order_detail = Db::name('OrderDetail')->field('goods_id,buy_number,spec')->where($detail_where)->select()->toArray(); if(!empty($order_detail)) { foreach($order_detail as $v) { // 查看是否已扣除过库存 $temp = Db::name('OrderGoodsInventoryLog')->where(['order_id'=>$params['order_id'], 'goods_id'=>$v['goods_id'], 'is_rollback'=>0])->find(); if(!empty($temp)) { // 数量 $buy_number = ($appoint_buy_number == 0) ? $v['buy_number'] : $appoint_buy_number; // 商品回滚操作 $temp_goods = Db::name('Goods')->where(['id'=>$v['goods_id']])->value('id'); if(!empty($temp_goods)) { if(!Db::name('Goods')->where(['id'=>$v['goods_id']])->inc('inventory', $buy_number)->update()) { return DataReturn('商品库存回滚失败['.$params['order_id'].'-'.$v['goods_id'].']', -10); } } // 回滚规格库存 $spec = empty($v['spec']) ? '' : json_decode($v['spec'], true); $base = GoodsService::GoodsSpecDetail(['id'=>$v['goods_id'], 'spec'=>$spec]); if($base['code'] == 0) { // 回滚规格操作 if(!Db::name('GoodsSpecBase')->where(['id'=>$base['data']['spec_base']['id'], 'goods_id'=>$v['goods_id']])->inc('inventory', $buy_number)->update()) { return DataReturn('规格库存回滚失败['.$params['order_id'].'-'.$v['goods_id'].']', -10); } } // 仓库库存回滚 $we_ret = WarehouseGoodsService::WarehouseGoodsInventoryRollback($params['order_id'], $v['goods_id'], $spec, $v['buy_number']); if($we_ret['code'] != 0) { return $we_ret; } // 回滚日志更新 $log_data = [ 'is_rollback' => 1, 'rollback_time' => time(), ]; if(!Db::name('OrderGoodsInventoryLog')->where(['id'=>$temp['id']])->update($log_data)) { return DataReturn('库存回滚日志更新失败['.$temp['id'].'-'.$params['order_id'].']', -100); } } } return DataReturn('操作成功', 0); } return DataReturn('没有需要回滚的数据', 0); } /** * 自提点地址选中地址获取 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2019-11-18 * @desc description * @param [int] $params['address_id'] [自提点地址索引值] * @param [array] $params['address_id'] [自提点地址列表] */ public static function SiteExtractionAddress($params = []) { // 自提地址列表 $address = ConfigService::SiteTypeExtractionAddressList(); // 选中地址处理 $default = null; if(isset($params['address_id']) && $params['address_id'] !== null && !empty($address['data']) && is_array($address['data'])) { if(isset($address['data'][$params['address_id']])) { $default = $address['data'][$params['address_id']]; } } // 默认地址 if(empty($default) && !empty($address['data'])) { foreach($address['data'] as $v) { if(isset($v['is_default']) && $v['is_default'] == 1) { $default = $v; break; } } } // 返回数据 $result = [ 'data_list' => $address['data'], 'default' => $default, ]; // 自提点地址数据钩子 $hook_name = 'plugins_service_site_extraction_address_handle'; $ret = MyEventTrigger($hook_name, [ 'hook_name' => $hook_name, 'is_backend' => true, 'params' => $params, 'data' => &$result, ]); return DataReturn('操作成功', 0, $result); } } ?>