vr-shopxo-plugin/shopxo/app/service/OrderSplitService.php

399 lines
16 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<?php
// +----------------------------------------------------------------------
// | ShopXO 国内领先企业级B2C免费开源电商系统
// +----------------------------------------------------------------------
// | Copyright (c) 2011~2099 http://shopxo.net All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( https://opensource.org/licenses/mit-license.php )
// +----------------------------------------------------------------------
// | Author: Devil
// +----------------------------------------------------------------------
namespace app\service;
use think\facade\Db;
use app\service\WarehouseService;
use app\service\GoodsService;
/**
* 订单拆分服务层
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2020-07-18
* @desc description
*/
class OrderSplitService
{
/**
* 订单拆分入口
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2020-07-18
* @desc description
* @param [array] $params [输入参数]
*/
public static function Run($params = [])
{
// 请求参数
$p = [
[
'checked_type' => 'is_array',
'key_name' => 'goods',
'error_msg' => MyLang('goods_empty_or_format_error_tips'),
],
];
$ret = ParamsChecked($params, $p);
if($ret !== true)
{
return DataReturn($ret, -1);
}
// 商品仓库集合
$warehouse_goods = self::GoodsWarehouseAggregate($params);
// 存在多个订单,但是订单模式非(快递、同城),则设置为快递模式
if(count($warehouse_goods) > 1 && !in_array($params['site_model'], [0,1]))
{
$params['site_model'] = 0;
$params['common_site_type'] = 0;
}
// 分组商品基础处理
$data = self::GroupGoodsBaseHandle($warehouse_goods, $params);
// 生成订单仓库分组商品数据处理钩子
$hook_name = 'plugins_service_buy_group_goods_handle';
$ret = EventReturnHandle(MyEventTrigger($hook_name, [
'hook_name' => $hook_name,
'is_backend' => true,
'params' => $params,
'data' => &$data,
]));
if(isset($ret['code']) && $ret['code'] != 0)
{
return $ret;
}
// 根据扩展数据重新计算金额
if(!empty($data))
{
foreach($data as &$v)
{
// 是否存在扩展数据
if(!empty($v['order_base']['extension_data']))
{
// 扩展数据金额计算
$ext = self::ExtensionDataPriceHandle($v['order_base']['extension_data']);
// 增加/减少
$v['order_base']['increase_price'] = PriceBeautify(PriceNumberFormat($v['order_base']['increase_price']+$ext['inc']));
$v['order_base']['preferential_price'] = PriceBeautify(PriceNumberFormat($v['order_base']['preferential_price']+$ext['dec']));
// 实际金额/总额处理
$v['order_base']['actual_price'] = PriceBeautify(PriceNumberFormat(($v['order_base']['actual_price']+$ext['inc'])-$ext['dec']));
$v['order_base']['total_price'] = PriceBeautify(PriceNumberFormat($v['order_base']['total_price']));
// 防止实际金额负数
if($v['order_base']['actual_price'] < 0)
{
$v['order_base']['actual_price'] = 0;
}
}
}
}
// 返回数据
return DataReturn(MyLang('operate_success'), 0, $data);
}
/**
* 扩展数据解析金额
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2020-07-23
* @desc description
* @param [array] $data [扩展数据]
*/
public static function ExtensionDataPriceHandle($data)
{
$inc = 0;
$dec = 0;
if(!empty($data))
{
if(!is_array($data))
{
$data = json_decode($data, true);
}
foreach($data as $v)
{
if(isset($v['type']) && isset($v['price']) && $v['price'] > 0)
{
switch($v['type'])
{
// 减
case 0 :
$dec += $v['price'];
break;
// 加
case 1 :
$inc += $v['price'];
break;
}
}
}
}
return [
'inc' => $inc,
'dec' => $dec,
];
}
/**
* 分组商品基础处理
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2020-07-23
* @desc description
* @param [array] $data [分组商品]
* @param [array] $params [输入参数]
*/
public static function GroupGoodsBaseHandle($data, $params)
{
if(!empty($data))
{
foreach($data as &$v)
{
// 当前仓库的商品总价
$total_price = PriceNumberFormat(array_sum(array_column($v['goods_items'], 'total_price')));
// 订单基础信息
$v['order_base'] = [
// 总价
'total_price' => PriceBeautify($total_price),
// 订单实际支付金额(已减去优惠金额, 已加上增加金额)
'actual_price' => PriceBeautify($total_price),
// 优惠金额
'preferential_price' => 0.00,
// 增加金额
'increase_price' => 0.00,
// 商品总数
'goods_count' => count($v['goods_items']),
// 规格重量总计
'spec_weight_total' => array_sum(array_map(function($v) {return $v['spec_weight']*$v['stock'];}, $v['goods_items'])),
// 规格体积总计
'spec_volume_total' => array_sum(array_map(function($v) {return $v['spec_volume']*$v['stock'];}, $v['goods_items'])),
// 购买总数
'buy_count' => array_sum(array_column($v['goods_items'], 'stock')),
// 默认地址
'address' => $params['address'],
// 自提地址列表
'extraction_address' => $params['extraction_address'],
// 当前使用的站点模式
'site_model' => $params['site_model'],
// 公共站点模式
'common_site_type' => $params['common_site_type'],
// 商品售价是否展示
'goods_price_show_status' => (array_sum(array_filter(array_column($v['goods_items'], 'show_field_price_status'))) > 0) ? 1 : 0,
// 商品原价是否展示
'goods_original_price_show_status' => (array_sum(array_filter(array_column($v['goods_items'], 'show_field_original_price_status'))) > 0) ? 1 : 0,
// 仓库组扩展展示数据
// name 名称
// price 金额
// type 类型0减少, 1增加
// tips 提示信息
// business 业务类型(内容格式不限)
// ext 扩展数据(内容格式不限)
// $extension_data = [
// [
// 'name' => '感恩节9折',
// 'price' => 23,
// 'type' => 0,
// 'tips' => '-¥23元',
// 'business' => null,
// 'ext' => null,
// ],
// ];
'extension_data' => [],
];
}
}
return $data;
}
/**
* 商品仓库集合
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2020-07-18
* @desc description
* @param [array] $params [输入参数]
*/
public static function GoodsWarehouseAggregate($params)
{
// 仓库查询数据
$field = 'w.*,wgs.inventory';
$order_by = 'w.level desc,w.is_default desc,wgs.inventory desc';
// 订单地址坐标
$lng = '';
$lat = '';
if(!empty($params['address']))
{
if(isset($params['address']['lng']) && $params['address']['lng'] != 0 && isset($params['address']['lat']) && $params['address']['lat'] != 0)
{
$lng = $params['address']['lng'];
$lat = $params['address']['lat'];
}
}
// 没有地址坐标泽取当前用户坐标
if(empty($lng) && empty($lat))
{
// 当前坐标
$request_params = empty($params['params']) ? [] : $params['params'];
$lng = empty($request_params['lng']) ? (empty($request_params['user_lng']) ? '' : $request_params['user_lng']) : $request_params['lng'];
$lat = empty($request_params['lat']) ? (empty($request_params['user_lat']) ? '' : $request_params['user_lat']) : $request_params['lat'];
}
// 加入坐标优先级
if(!empty($lng) && !empty($lat))
{
$field .= ',ROUND(6378.138*2*ASIN(SQRT(POW(SIN(('.$lat.'*PI()/180-w.lat*PI()/180)/2),2)+COS('.$lat.'*PI()/180)*COS(w.lat*PI()/180)*POW(SIN(('.$lng.'*PI()/180-w.lng*PI()/180)/2),2)))*1000) AS distance';
$order_by = 'distance asc, '.$order_by;
}
// 默认仓库
$warehouse_default = [];
// 数据分组
$result = [];
foreach($params['goods'] as $v)
{
// 不存在规格则使用默认
$spec = empty($v['spec']) ? [['type' => GoodsService::GoodsSpecDefaultName(),'value' => 'default']] : $v['spec'];
// 获取商品库存、仓库商品壳仓库商品规格都必须等于当前商品
$where = [
['wgs.goods_id', '=', $v['goods_id']],
['wgs.md5_key', '=', md5(implode('', array_column($spec, 'value')))],
['wgs.inventory', '>', 0],
['wg.goods_id', '=', $v['goods_id']],
['wg.is_enable', '=', 1],
['w.is_enable', '=', 1],
['w.is_delete_time', '=', 0],
];
$warehouse = Db::name('WarehouseGoodsSpec')->alias('wgs')->join('warehouse_goods wg', 'wgs.warehouse_id=wg.warehouse_id')->join('warehouse w', 'wg.warehouse_id=w.id')->where($where)->field($field)->order($order_by)->select()->toArray();
// 商品仓库分配仓库组合钩子
$hook_name = 'plugins_service_buy_group_goods_warehouse_handle';
MyEventTrigger($hook_name, [
'hook_name' => $hook_name,
'is_backend' => true,
'params' => $params,
'spec' => $spec,
'where' => $where,
'goods' => $v,
'data' => &$warehouse,
]);
// 商品仓库分组
if(!empty($warehouse))
{
foreach($warehouse as $w)
{
// 是否还存在未分配的数量
if($v['stock'] > 0)
{
// 赋值数据
$temp_v = $v;
// 购买数量计算
if($temp_v['stock'] > $w['inventory'] && $w['inventory'] > 0)
{
$temp_v['stock'] = $w['inventory'];
}
// 总价计算
$temp_v['total_price'] = PriceBeautify(PriceNumberFormat(floatval($temp_v['price'])*$temp_v['stock']));
// 减除数量
$v['stock'] -= $w['inventory'];
// 是否第一次赋值
if(!array_key_exists($w['id'], $result))
{
// 仓库
unset($w['is_enable'], $w['is_delete_time'], $w['contacts_name'], $w['contacts_tel'], $w['add_time'], $w['upd_time']);
$warehouse_handle = WarehouseService::WarehouseListHandle([$w]);
$result[$w['id']] = $warehouse_handle[0];
$result[$w['id']]['goods_items'] = [];
unset($result[$w['id']]['is_default'], $result[$w['id']]['level'], $result[$w['id']]['inventory']);
}
// 商品归属到仓库下
$result[$w['id']]['goods_items'] = array_merge($result[$w['id']]['goods_items'], [$temp_v]);
} else {
break;
}
}
} else {
// 未获取到仓库则使用默认仓库
if(empty($warehouse_default))
{
// 先获取默认仓库
$warehouse_default = Db::name('Warehouse')->where(['is_default'=>1, 'is_enable'=>1, 'is_delete_time'=>0])->field('id,name,alias,lng,lat,province,city,county,address')->find();
if(empty($warehouse_default))
{
// 没有默认仓库则获取最早的一个仓库
$warehouse_default = Db::name('Warehouse')->where(['is_enable'=>1, 'is_delete_time'=>0])->field('id,name,alias,lng,lat,province,city,county,address')->order('id asc')->find();
}
}
$temp_warehouse_default = $warehouse_default;
// 商品仓库分配默认仓库组合钩子
$hook_name = 'plugins_service_buy_group_goods_default_warehouse_handle';
MyEventTrigger($hook_name, [
'hook_name' => $hook_name,
'is_backend' => true,
'params' => $params,
'goods' => $v,
'data' => &$temp_warehouse_default,
]);
// 存在默认仓库则继续分配
if(!empty($temp_warehouse_default))
{
if(!array_key_exists($temp_warehouse_default['id'], $result))
{
// 仓库
$warehouse_handle = WarehouseService::WarehouseListHandle([$temp_warehouse_default]);
$result[$temp_warehouse_default['id']] = $warehouse_handle[0];
$result[$temp_warehouse_default['id']]['goods_items'] = [];
}
// 商品归属到仓库下
$result[$temp_warehouse_default['id']]['goods_items'][] = $v;
}
}
}
return array_values($result);
}
}
?>