chore: 添加静态资源文件和版本配置文件

添加大量图片、字体、音频等静态资源文件,包括用户界面图片、模板文件和安装界面素材
添加系统版本配置文件(.version)和常量定义文件(.constant)
更新供应商依赖文件,包括Symfony VarDumper组件和相关许可证文件
添加验证码配置文件(captcha.php)和.gitignore规则
This commit is contained in:
2026-03-06 20:14:23 +08:00
commit c9a98f342c
4485 changed files with 490986 additions and 0 deletions

View File

@@ -0,0 +1,496 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\order;
use app\dao\order\OtherOrderDao;
use app\services\BaseServices;
use app\services\pay\PayServices;
use app\services\statistic\TradeStatisticServices;
use app\services\user\member\MemberShipServices;
use app\services\user\UserServices;
use think\annotation\Inject;
use think\exception\ValidateException;
/**
* Class OtherOrderServices
* @package app\services\order
* @mixin OtherOrderDao
*/
class OtherOrderServices extends BaseServices
{
/**
* 订单类型
* @var string[]
*/
protected array $type = [
0 => '免费领取',
1 => '购买会员卡',
2 => '卡密激活',
3 => '收银订单',
4 => '赠送'
];
/**
* @var OtherOrderDao
*/
#[Inject]
protected OtherOrderDao $dao;
/**
* 生成会员购买订单数据
* @param array $data
* @return mixed
*/
public function addOtherOrderData(array $data)
{
if (!$data) throw new ValidateException('数据不能为空');
$add = [
'uid' => $data['uid'],
'store_id' => $data['store_id'] ?? 0,
'staff_id' => $data['staff_id'] ?? 0,
'type' => $data['type'] ?? 1,
'order_id' => $data['order_id'],
'channel_type' => $data['channel_type'],
'pay_type' => $data['pay_type'] ?? 0,
'member_type' => $data['member_type'] ?? 0,
'member_price' => $data['member_price'] ?? 0.00,
'pay_price' => $data['pay_price'] ?? 0.00,
'code' => $data['member_code'] ?? '',
'vip_day' => $data['vip_day'] ?? 0,
'is_permanent' => $data['is_permanent'] ?? 0,
'is_free' => $data['is_free'] ?? 0,
'overdue_time' => $data['overdue_time'] ?? 0,
'status' => 0,
'paid' => $data['paid'] ?? 0,
'pay_time' => $data['pay_time'] ?? 0,
'money' => $data['money'] ?? 0,
'add_time' => time(),
];
return $this->dao->save($add);
}
/**
* 能否领取免费
* @param int $uid
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function isCanGetFree(int $uid)
{
/** @var MemberShipServices $memberShipService */
$memberShipService = app()->make(MemberShipServices::class);
$freeDay = $memberShipService->getVipDay(['type' => "free"]);
$freeConfig = [];
$freeConfig['price'] = 0;
$freeConfig['pre_price'] = 0;
$freeConfig['title'] = "免费会员";
$freeConfig['type'] = "free";
$freeConfig['vip_day'] = $freeDay ? $freeDay : 0;
$freeConfig['is_record'] = 0;
$record = $this->dao->getOneByWhere(['uid' => $uid, 'is_free' => 1]);
if ($record) {
$freeConfig['is_record'] = 1;
}
/** @var UserServices $userService */
$userService = app()->make(UserServices::class);
$user = $userService->getUserCacheInfo($uid);
$userInfo = [
'uid' => $user['uid'],
'nickname' => $user['nickname'],
'avatar' => $user['avatar'],
'phone' => $user['phone'],
'now_money' => $user['now_money'],
'is_ever_level' => $user['is_ever_level'],
'is_money_level' => $user['is_money_level'],
'overdue_time' => $user['overdue_time'],
];
/** @var TradeStatisticServices $tradeService */
$tradeService = app()->make(TradeStatisticServices::class);
$registerTime = $tradeService->TimeConvert(['start_time' => date('Y-m-d H:i:s', $user['add_time']), 'end_time' => date('Y-m-d H:i:s', time())]);
$userInfo['register_days'] = $registerTime['days'];
/** @var StoreOrderEconomizeServices $economizeService */
$economizeService = app()->make(StoreOrderEconomizeServices::class);
$userInfo['economize_money'] = $economizeService->sumEconomizeMoney($uid);
$userInfo['shop_name'] = sys_config('site_name');
$freeConfig['user_info'] = $userInfo;
return $freeConfig;
}
/**
* 查询会员卡订单数据
* @param array $where
* @param string $field
* @return array|\think\Model|null
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getOne(array $where, string $field = '*')
{
return $this->dao->getOne($where, $field);
}
/**
* 创建订单
* @param int $uid
* @param string $channelType 支付渠道
* @param bool $memberType 会员卡类型
* @param string $payPrice 支付金额
* @param string $payType 支付方式
* @param $type 订单类型
* @param int $money
* @param int $store_id
* @param int $staff_id
* @return mixed
* @throws \Exception
*/
public function createOrder(int $uid, string $channelType, string $memberType, string $payPrice, string $payType, $type, $money = 0, int $store_id = 0, int $staff_id = 0)
{
/** @var StoreOrderCreateServices $storeOrderCreateService */
$storeOrderCreateService = app()->make(StoreOrderCreateServices::class);
$orderInfo = [
'uid' => $uid,
'order_id' => $storeOrderCreateService->getNewOrderId('hy'),
'pay_price' => $payPrice,
'pay_type' => $payType,
'channel_type' => $channelType,
'member_code' => "",
'store_id' => $store_id,
'staff_id' => $staff_id,
'overdue_time' => 0
];
if ($type != 3) { //区别 0:免费领取会员 1:购买会员 2:卡密领取会员 3:线下付款
if (!$memberType) throw new ValidateException('memberType miss');
[$memberPrice, $isFree, $isPermanent, $overdueTime, $newMemberRight] = $this->checkPayMemberType($uid, (int)$memberType);
$orderInfo['member_price'] = $memberPrice;
$orderInfo['money'] = $memberPrice;
$orderInfo['vip_day'] = $newMemberRight['vip_day'];
$orderInfo['member_type'] = $memberType;
$orderInfo['overdue_time'] = $overdueTime;
$orderInfo['is_permanent'] = $isPermanent;
$orderInfo['is_free'] = $isFree;
$orderInfo['type'] = $type;
$changeType = "create_member_order";
} else {
$orderInfo['type'] = $type;
$orderInfo['member_code'] = "";
$changeType = "create_offline_scan_order";
$orderInfo['money'] = $money ? $money : $payPrice;
}
$memberOrder = $this->addOtherOrderData($orderInfo);
if (!$memberOrder) {
throw new ValidateException('订单生成失败!');
}
/** @var OtherOrderStatusServices $statusService */
$statusService = app()->make(OtherOrderStatusServices::class);
$statusService->save([
'oid' => $memberOrder['id'],
'change_type' => $changeType,
'change_message' => '订单生成',
'change_time' => time(),
'shop_type' => $type,
]);
return $memberOrder;
}
/**
* 免费卡领取支付
* @param $orderInfo
* @return bool
*/
public function zeroYuanPayment($orderInfo)
{
if ($orderInfo['paid']) {
throw new ValidateException('该订单已支付!');
}
/** @var MemberShipServices $memberShipServices */
$memberShipServices = app()->make(MemberShipServices::class);
$member_type = $memberShipServices->value(['id' => $orderInfo['member_type']], 'type');
if ($member_type != 'free') {
throw new ValidateException('支付失败!');
}
$res = $this->paySuccess($orderInfo, 'yue');//余额支付成功
return $res;
}
/**
* 会员卡支付成功
* @param array $orderInfo
* @param string $paytype
* @return bool
*/
public function paySuccess(array $orderInfo, string $paytype = PayServices::WEIXIN_PAY, array $other = [])
{
/** @var OtherOrderStatusServices $statusService */
$statusService = app()->make(OtherOrderStatusServices::class);
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
/** @var MemberShipServices $memberShipServices */
$memberShipServices = app()->make(MemberShipServices::class);
$orderInfo['member_type'] = $memberShipServices->value(['id' => $orderInfo['member_type']], 'type');
switch ($orderInfo['type']) {
case 0 :
case 1:
case 2 :
$res1 = $userServices->setMemberOverdueTime($orderInfo['vip_day'], $orderInfo['uid'], 1, $orderInfo['member_type']);
break;
case 3:
$res1 = true;
break;
}
// if ($paytype == PayServices::ALIPAY_PAY && isset($other['trade_no'])) {
// $updata['trade_no'] = $other['trade_no'];
// }
$updata['trade_no'] = $other['trade_no'] ?? '';
$updata['paid'] = 1;
$updata['pay_type'] = $paytype;
$updata['pay_time'] = time();
$res2 = $this->dao->update($orderInfo['id'], $updata);
$res3 = $statusService->save([
'oid' => $orderInfo['id'],
'change_type' => 'pay_success',
'change_message' => '用户付款成功',
'shop_type' => $orderInfo['type'],
'change_time' => time()
]);
$orderInfo['pay_type'] = $paytype;
$orderInfo['pay_time'] = time();
//支付成功后发送消息
$orderInfo = $this->dao->get($orderInfo['id']);
event('user.vipPay', [$orderInfo]);
$res = $res1 && $res2 && $res3;
return false !== $res;
}
/**
* 修改
* @param array $where
* @param array $data
* @return mixed
*/
public function update(array $where, array $data)
{
return $this->dao->update($where, $data);
}
/**
* 购买会员卡数据校验
* @param int $uid
* @param int $memberId
* @param string $payPrice
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function checkPayMemberType(int $uid, int $memberId)
{
/** @var MemberShipServices $memberShipService */
$memberShipService = app()->make(MemberShipServices::class);
$memberInfo = $memberShipService->getMemberInfo($memberId);
/** @var UserServices $userService */
$userService = app()->make(UserServices::class);
$userInfo = $userService->get($uid);
if ($userInfo['is_money_level'] > 0 && $userInfo['is_ever_level'] > 0) throw new ValidateException('您已是永久会员无需再购买!');
$memberTypes = $memberInfo['type'] ?? '';
$price = $memberInfo['pre_price'];
if ($memberTypes == 'free' && $memberInfo['vip_day'] <= 0) throw new ValidateException('网络错误!');
if ($userInfo['overdue_time'] > time()) {
$time = $userInfo['overdue_time'];
} else {
$time = time();
}
switch ($memberTypes) {
case "free"://免费会员
$isCanGetFree = $this->isCanGetFree($uid);
if ($isCanGetFree['is_record'] == 1) throw new ValidateException('您已经领取过免费会员!');
$memberPrice = 0.00; //会员卡价格
$isFree = 1;//代表免费
$isPermanent = 0;//代表非永久
$overdueTime = bcadd(bcmul(abs($memberInfo['vip_day']), "86400", 0), $time, 0);
break;
case "ever":
$memberPrice = $price;
$isFree = 0;
$isPermanent = 1;
$overdueTime = -1;
break;
default:
$memberPrice = $price;
$isFree = 0;
$isPermanent = 0;
$overdueTime = $memberShipService->getOverdueTime($uid, $memberId, $userInfo, $memberInfo);
break;
}
return [$memberPrice, $isFree, $isPermanent, $overdueTime, $memberInfo];
}
/**
* 根据查询用户购买会员金额
* @param array $where
* @return mixed
*/
public function getMemberMoneyByWhere(array $where, string $sumField, string $selectType, string $group = "")
{
switch ($selectType) {
case "sum" :
return $this->dao->getWhereSumField($where, $sumField);
case "group" :
return $this->dao->getGroupField($where, $sumField, $group);
}
}
/**
* 线下收银列表
* @param array $where
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getScanOrderList(array $where)
{
$where['type'] = 3;
$where['paid'] = 1;
[$page, $limit] = $this->getPageValue();
if ($where['add_time']) {
[$startTime, $endTime] = explode('-', $where['add_time']);
if ($startTime || $endTime) {
$startTime = strtotime($startTime);
$endTime = strtotime($endTime . ' 23:59:59');
$where['add_time'] = [$startTime, $endTime];
}
}
if ($where['name']) {
/** @var UserServices $userService */
$userService = app()->make(UserServices::class);
$userInfo = $userService->getUserInfoList(['nickname' => $where['name']], "uid");
if ($userInfo) $where['uid'] = array_column($userInfo, 'uid');
}
$list = $this->dao->getScanOrderList($where, $page, $limit);
/** @var UserServices $userService */
$userService = app()->make(UserServices::class);
if ($list) {
$userInfos = $userService->getColumn([['uid', 'IN', array_unique(array_column($list, 'uid'))]], 'uid,phone,nickname', 'uid');
foreach ($list as &$v) {
$v['add_time'] = date('Y-m-d H:i:s', $v['add_time']);
$v['pay_time'] = $v['pay_time'] ? date('Y-m-d H:i:s', $v['pay_time']) : '';
$v['phone'] = $userInfos[$v['uid']]['phone'] ?? '';
$v['nickname'] = $userInfos[$v['uid']]['nickname'] ?? '';
$v['pay_type'] = PayServices::PAY_TYPE[$v['pay_type']] ?? '其他方式';
$v['true_price'] = bcsub($v['money'], $v['pay_price'], 2);
}
}
$count = $this->dao->count($where);
return compact('list', 'count');
}
/**
* 获取会员记录
* @param array $where
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getMemberRecord(array $where, int $limit = 0)
{
$where['type'] = [0, 1, 2, 4];
if (isset($where['add_time']) && $where['add_time']) {
$where['time'] = $where['add_time'];
unset($where['add_time']);
}
if ($limit) {
[$page] = $this->getPageValue();
} else {
[$page, $limit] = $this->getPageValue();
}
$list = $this->dao->getMemberRecord($where, '*', ['user', 'staff'], $page, $limit);
if ($list) {
/** @var MemberShipServices $memberShipService */
$memberShipService = app()->make(MemberShipServices::class);
$shipInfo = $memberShipService->getColumn([], 'title,type', 'id');
foreach ($list as &$v) {
$v['overdue_time'] = $v['member_type'] == 'ever' || ($shipInfo[$v['member_type']]['type'] ?? '') == 'ever' ? '永久' : ($v['overdue_time'] ? date('Y-m-d H:i:s', $v['overdue_time']) : '');
$v['vip_day'] = $v['member_type'] == 'ever' || ($shipInfo[$v['member_type']]['type'] ?? '') == 'ever' ? '永久' : $v['vip_day'];
// 如果$v['member_type']不是数字
if (!is_numeric($v['member_type'])) {
if ($v['member_type'] == 'admin') $v['member_type'] = '赠送';
if ($v['member_type'] == 'card') $v['member_type'] = '卡密兑换';
} else {
$v['member_type'] = $shipInfo[$v['member_type']]['title'] ?? '';
}
$v['pay_time'] = $v['pay_time'] ? date('Y-m-d H:i:s', $v['pay_time']) : '';
$v['add_time'] = date('Y-m-d H:i:s', $v['add_time']);
switch ($v['pay_type']) {
case "yue" :
$v['pay_type'] = "余额";
break;
case "weixin" :
$v['pay_type'] = "微信";
break;
case "alipay" :
$v['pay_type'] = "支付宝";
break;
case "admin" :
$v['pay_type'] = "后台赠送";
break;
}
if ($v['type'] == 0) $v['pay_type'] = "免费领取";
if ($v['type'] == 2) {
$v['pay_type'] = "卡密领取";
$v['member_type'] = "卡密激活";
}
if ($v['type'] == 1 && $v['is_free'] == 1) $v['pay_type'] = "免费领取";
$v['user']['overdue_time'] = isset($v['user']['overdue_time']) ? (date('Y-m-d', $v['user']['overdue_time']) == "1970-01-01" ? "" : date('Y-m-d H:i:s', $v['user']['overdue_time'])) : '';
}
}
$count = $this->dao->count($where);
return compact('list', 'count');
}
/**
* 门店付费会员统计详情列表
* @param int $store_id
* @param int $staff_id
* @param array $time
* @return array|array[]
*/
public function time(int $store_id, int $staff_id, array $time = [])
{
if (!$time) {
return [[], []];
}
[$start, $stop, $front, $front_stop] = $time;
$where = ['store_id' => $store_id, 'paid' => 1, 'type' => [0, 1, 2, 4]];
if ($staff_id) {
$where['staff_id'] = $staff_id;
}
$frontPrice = $this->dao->sum($where + ['time' => [$front, $front_stop]], 'pay_price', true);
$nowPrice = $this->dao->sum($where + ['time' => [$start, $stop]], 'pay_price', true);
[$page, $limit] = $this->getPageValue();
$list = $this->dao->getMemberRecord($where + ['time' => [$start, $stop]], 'id,order_id,uid,pay_price,add_time', ['userInfo'], $page, $limit);
foreach ($list as &$item) {
$item['add_time'] = $item['add_time'] ? date('Y-m-d H:i:s', $item['add_time']) : '';
}
return [[$nowPrice, $frontPrice], $list];
}
}

View File

@@ -0,0 +1,44 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\order;
use app\dao\order\OtherOrderStatusDao;
use app\services\BaseServices;
use think\annotation\Inject;
/**
* Class OtherOrderStatusServices
* @package app\services\order
* @mixin OtherOrderStatusDao
*/
class OtherOrderStatusServices extends BaseServices
{
/**
* @var OtherOrderStatusDao
*/
#[Inject]
protected OtherOrderStatusDao $dao;
/**
* 获取订单状态列表
* @param int $id
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getStatusList(int $id)
{
return $this->dao->getStatusList(['oid' => $id], 0, 0);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,694 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\order;
use app\dao\order\StoreDeliveryOrderDao;
use app\jobs\order\OrderStatusJob;
use app\jobs\order\OrderTakeJob;
use app\services\BaseServices;
use app\services\other\CityAreaServices;
use app\services\store\SystemStoreServices;
use app\services\supplier\SystemSupplierServices;
use crmeb\services\DeliverySevices;
use crmeb\services\FormBuilder as Form;
use crmeb\services\SystemConfigService;
use crmeb\traits\OptionTrait;
use think\annotation\Inject;
use think\exception\ValidateException;
use think\facade\Log;
use think\facade\Route as Url;
/**
* 发货单
* Class StoreDeliveryOrderServices
* @package app\services\order
* @mixin StoreDeliveryOrderDao
*/
class StoreDeliveryOrderServices extends BaseServices
{
use OptionTrait;
protected array $statusData = [
2 => '待取货',
3 => '配送中',
4 => '已完成',
-1 => '已取消',
9 => '物品返回中',
10 => '物品返回完成',
100 => '骑士到店',
];
/**
* 平台达达门店
* @var string
*/
public string $platCityShopId = 'plat_delivery_city_shop_001';
/**
* @var StoreDeliveryOrderDao
*/
#[Inject]
protected StoreDeliveryOrderDao $dao;
/**
* 配送信息
* @return string[]
*/
public function getStatusMsg()
{
return $this->statusData;
}
/**
* @param array $where
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function systemPage(array $where)
{
[$page, $limit] = $this->getPageValue();
$list = $this->dao->getList($where, '*', $page, $limit, ['orderInfo', 'storeInfo']);
$count = $this->dao->count();
if ($list) {
foreach ($list as &$item) {
$item['add_time'] = $item['add_time'] ? date('Y-m-d H:i:s') : '';
$item['distance'] = $item['distance'] ? bcdiv($item['distance'], '1000', 3) : 0;
}
}
return compact('count', 'list');
}
/**
* 生成订单号
* @return string
* @throws \Exception
*/
public function getOrderSn(string $key = '')
{
[$msec, $sec] = explode(' ', microtime());
$msectime = number_format((floatval($msec) + floatval($sec)) * 1000, 0, '', '');
$orderId = $key . $msectime . random_int(10000, max(intval($msec * 10000) + 10000, 98369));
return $orderId;
}
/**
* 地址转经纬度
* @param $region
* @param $address
* @return mixed|null
*/
public function lbs_address($region, $address)
{
$key = sys_config('tengxun_map_key', '');
if (!$key) {
throw new ValidateException('请先配置地图KEY');
}
$locationOption = new \Joypack\Tencent\Map\Bundle\AddressOption($key);
$locationOption->setAddress($region . $address);
// $locationOption->setRegion($region);
$location = new \Joypack\Tencent\Map\Bundle\Address($locationOption);
$res = $location->request();
if ($res->error) {
throw new ValidateException($res->error);
}
if ($res->status) {
throw new ValidateException($res->message);
}
if (!$res->result) {
throw new ValidateException('获取失败');
}
return $res->result;
}
/**
* 处理订单数据
* @param array $station
* @param array $order
* @param int $type
* @return array
*/
public function getPriceParams(array $station, array $order, int $type)
{
$data = [];
$type = (int)$type;
switch ($type) {
case 1:
$city = DeliverySevices::init(DeliverySevices::DELIVERY_TYPE_DADA)->getCity([]);
$res = [];
foreach ($city as $item) {
$res[$item['label']] = $item['key'];
}
//达达城市数据:西安
$city_name = str_replace(['市', '自治州', '地区', '区划', '县'], '', $station['city_name'] ?? '');
$data = [
'shop_no' => $station['origin_shop_id'],
'city_code' => $res[$city_name],
'cargo_price' => $order['pay_price'],
'is_prepay' => 0,
'receiver_name' => $order['real_name'],
'receiver_address' => $order['user_address'],
'cargo_weight' => 0,
'receiver_phone' => $order['user_phone'],
'is_finish_code_needed' => 1,
];
break;
case 2://uu城市数据:西安市
$business = DeliverySevices::init(DeliverySevices::DELIVERY_TYPE_UU)->getBusiness();
$business = array_combine(array_column($business, 'key'), $business);
$data = [
'from_address' => $station['station_address'],
'to_address' => $order['user_address'],
'city_name' => $station['city_name'] ?? '西安',
'goods_type' => $business[$station['business'] ?? 1]['label'],
'send_type' => '0',
'to_lat' => $order['latitude'],
'to_lng' => $order['longitude'],
'from_lat' => $station['lat'],
'from_lng' => $station['lng'],
];
break;
}
return $data;
}
/**
* 创建配送单
* @param int $id
* @param array $data
* @param int $type
* @param $order
* @return bool
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function create(int $id, array $data, int $type = 1, $order = [])
{
if (!$order) {
/** @var StoreOrderServices $orderServices */
$orderServices = app()->make(StoreOrderServices::class);
$order = $orderServices->get($id);
}
if (!$order) {
throw new ValidateException('订单信息获取失败');
}
if (isset($order['user_address']) && !$order['user_address']) {
throw new ValidateException('自提订单不支持同城配送');
}
$order = is_object($order) ? $order->toArray() : $order;
//处理地址定位
if (isset($order['user_location']) && $order['user_location']) {
[$longitude, $latitude] = explode(' ', $order['user_location']);
$order['longitude'] = $longitude;
$order['latitude'] = $latitude;
if (!$order['longitude'] || !$order['latitude']) {
$addressArr = $this->addressHandle($order['user_address']);
$city_name = $addressArr['city'] ?? '';
try {
$addres = $this->lbs_address($city_name, $order['user_address']);
$order['latitude'] = $addres['location']['lat'] ?? '';
$order['longitude'] = $addres['location']['lng'] ?? '';
} catch (\Exception $e) {
throw new ValidateException('获取经纬度失败');
}
}
}
if ($order['store_id']) {//门店
$resType = 1;
$relationId = (int)$order['store_id'];
} elseif ($order['supplier_id']) {//供应商
$resType = 2;
$relationId = (int)$order['supplier_id'];
} else {//平台信息
$resType = 0;
$relationId = 0;
}
//获取发货信息
$station = $this->syncCityShop($relationId, $resType, $type);
if (!$station || !isset($station['lat']) || !$station['lat'] || !isset($station['lng']) || !$station['lng']) {
throw new ValidateException('获取发货信息失败');
}
$getPriceParams = $this->getPriceParams($station, $order, $type);
$orderSn = $this->getOrderSn($type == 1 ? 'dd' : 'uu');
$getPriceParams['origin_id'] = $orderSn;
$getPriceParams['cargo_weight'] = $data['cargo_weight'] ?? '';
$service = DeliverySevices::init($type);
try {
//计算价格
$priceData = $service->getOrderPrice($getPriceParams);
if ($type == DeliverySevices::DELIVERY_TYPE_UU) { //uu
$priceData['receiver'] = $order['real_name'];
$priceData['receiver_phone'] = $order['user_phone'];
$priceData['note'] = $data['delivery_remark'] ?? '';
$priceData['push_type'] = 2;
$priceData['special_type'] = $data['special_type'] ?? 0;
}
$res = $service->addOrder($priceData);
$ret = [
'type' => $resType,
'relation_id' => $relationId,
'oid' => $id,
'order_id' => $orderSn,
'delivery_no' => $type == 2 ? $res['ordercode'] : $priceData['deliveryNo'] ?? '',
'city_code' => $station['city_name'] ?? '西安',
'receiver_phone' => $order['user_phone'],
'user_name' => $order['real_name'],
'from_address' => $station['station_address'] ?? '',
'to_address' => $order['user_address'],
'info' => $data['delivery_remark'] ?? '',
'status' => 0,
'station_type' => $type,
'to_lat' => $order['latitude'],
'to_lng' => $order['longitude'],
'from_lat' => $station['lat'] ?? '',
'from_lng' => $station['lng'] ?? '',
'distance' => $priceData['distance'],
'fee' => $priceData['fee'] ?? $priceData['need_paymoney'] ?? 0,
'mark' => $data['delivery_remark'] ?? '',
'uid' => $order['uid'],
'add_time' => time()
];
//入库操作
$this->dao->save($ret);
return true;
} catch (\Throwable $e) {
if (isset($res['status']) && $res['status'] == 'success') {
$error['origin_id'] = $orderSn;
$error['reason'] = $type == 1 ? 36 : '信息错误';
$error['delivery_no'] = $type == 2 ? $res['ordercode'] : $priceData['deliveryNo'];
$service->cancelOrder($error);
}
throw new ValidateException($e->getMessage());
}
}
/**
* 配送订单详情
* @param int $id
* @return array|\think\Model
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function detail(int $id)
{
$res = $this->dao->get($id, ['orderInfo']);
if (!$res) {
throw new ValidateException('配送订单不存在,或已取消');
}
$res = $res->toArray();
$order = DeliverySevices::init((int)$res['station_type'])->getOrderDetail($res);
if (!$res) throw new ValidateException('订单不存在');
$res['data'] = [
'order_id' => $order['order_code'],
'to_address' => $order['to_address'],
'from_address' => $order['from_address'],
'state' => $order['state'],
'note' => $order['note'],
'order_price' => $order['order_price'],
'distance' => round(($order['distance'] / 1000), 2) . ' km',
];
return $res;
}
/**
* 根据订单获取配送信息
* @param int $oid
* @return array|string[]
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* User: liusl
* DateTime: 2024/12/25 下午4:54
*/
public function orderDetail(int $oid)
{
// 获取订单数据
$res = $this->dao->get(['oid' => $oid]);
if (!$res) {
return [
'status_name' => '',
'porter_name' => '',
'porter_phone' => ''
];
}
// 获取派送服务实例
$order = DeliverySevices::init((int)$res['station_type'])->getOrderDetail($res);
return [
'status_name' => $order['status_name'] ?? '',
'porter_name' => $order['porter_name'] ?? '',
'porter_phone' => $order['porter_phone'] ?? ''
];
}
/**
* 删除
* @param int $id
* @return mixed
*/
public function delete(int $id)
{
$res = $this->dao->get($id);
if (!$res) throw new ValidateException('订单不存在');
return $this->dao->delete($id);
}
/**
* @param $id
* @return mixed
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function cancelForm($id)
{
$order = $this->dao->get($id);
if (!$order) throw new ValidateException('订单不存在');
if ($order['status'] == -1) throw new ValidateException('订单已取消,无法操作');
$field = [];
if ($order['station_type'] == 1) {
$options = DeliverySevices::init(1)->reasons();
$field[] = Form::select('reason', '取消原因')->setOptions(Form::setOptions($options))->filterable(1)->col(12);
$field[] = Form::input('cancel_reason', '其他原因说明')->required('请输入原因');
} else {
$field[] = Form::input('reason', '取消原因')->required('请输入原因');
}
return create_form('取消同城配送订单', $field, Url::buildUrl('/order/delivery_order/cancel/' . $id), 'POST');
}
/**
* 取消订单
* @param int $id
* @param array $reason
* @return mixed
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function cancel(int $id, array $reason)
{
$order = $this->dao->get($id);
if (!$order) throw new ValidateException('配送订单不存在');
if ($order['status'] == -1) throw new ValidateException('请勿重复操作');
$data = [
'origin_id' => $order['order_id'],
'order_code' => $order['delivery_no'],
'reason' => $reason['reason'],
'cancel_reason' => $reason['cancel_reason'],
];
$this->transaction(function () use ($id, $order, $data) {
$mark = $data['reason'];
$delivery = DeliverySevices::init((int)$order['station_type']);
if ($order['station_type'] == DeliverySevices::DELIVERY_TYPE_DADA) {
$options = $delivery->reasons();
if ($options) $options = array_combine(array_column($options, 'value'), $options);
$mark = $options[$data['reason']]['label'] ?? '';
}
if ($data['cancel_reason']) $mark .= ',' . $data['cancel_reason'];
$res = $delivery->cancelOrder($data);
$deduct_fee = $res['deduct_fee'] ?? 0;
$this->doCancel($id, $order, $deduct_fee, $mark);
});
return true;
}
/**
* 执行取消订单
* @param int $id
* @param $deliveryOrder
* @param $deduct_fee
* @param $mark
* @return bool
*/
public function doCancel(int $id, $deliveryOrder, $deduct_fee, $mark)
{
//修改配送单
$this->dao->update($id, ['status' => 1, 'mark' => $mark, 'deduct_fee' => $deduct_fee]);
//修改愿订单
/** @var StoreOrderServices $storeOrderServices */
$storeOrderServices = app()->make(StoreOrderServices::class);
$storeOrderServices->update($deliveryOrder['oid'], ['status' => 0, 'delivery_type' => '', 'delivery_name' => '', 'delivery_id' => '', 'delivery_uid' => '']);
//记录订单状态
OrderStatusJob::dispatch([$deliveryOrder['oid'], 'city_delivery_cancel', ['change_message' => '同城配送取消', 'change_manager_id' => request()->adminId(), 'change_manager_type' => 'admin']]);
return true;
}
/**
* TODO 回调
* @param $data
* @author Qinii
* @day 2/17/22
*/
public function notify($data)
{
//达达
/**
* 订单状态(待接单=1,待取货=2,配送中=3,已完成=4,已取消=5, 指派单=8,妥投异常之物品返回中=9, 妥投异常之物品返回完成=10, 骑士到店=100,创建达达运单失败=1000 可参考文末的状态说明)
*/
Log::info('同城回调参数:' . var_export(['=======', $data, '======='], 1));
if (isset($data['data'])) {
$data = json_decode($data['data'], 1);
}
$reason = '';
$deductFee = 0;
$delivery = [];
if (isset($data['order_status'])) {
$order_id = $data['order_id'];
if ($data['order_status'] == 1) {
$orderData = $this->dao->getOne(['order_id' => $data['order_id']]);
if (!$orderData['finish_code']) {
$orderData->finish_code = $data['finish_code'];
$orderData->save();
}
return;
} else if (in_array($data['order_status'], [2, 3, 4, 5, 9, 10, 100])) {
$status = $data['order_status'];
if ($data['order_status'] == 5) {
$msg = [
'取消:',
'达达配送员取消:',
'商家主动取消:',
'系统或客服取消:',
];
//1:达达配送员取消;2:商家主动取消;3:系统或客服取消;0:默认值
$status = -1;
$reason = $msg[$data['cancel_from']] . $data['cancel_reason'];
}
$deductFee = $data['deductFee'] ?? 0;
if (isset($data['dm_name']) && $data['dm_name']) {
$delivery = [
'delivery_name' => $data['dm_name'],
'delivery_id' => $data['dm_mobile'],
];
}
}
} else if (isset($data['state'])) { //uu
if (!$data['origin_id']) $deliveryOrder = $this->dao->getOne(['delivery_no' => $data['order_code']]);
$order_id = $data['origin_id'] ?: $deliveryOrder['order_id'];
//当前状态 1下单成功 3跑男抢单 4已到达 5已取件 6到达目的地 10收件人已收货 -1订单取消
switch ($data['state']) {
case 3:
$status = 2;
break;
case 4:
$status = 100;
break;
case 5:
$status = 3;
break;
case 10:
$status = 4;
break;
case -1:
$status = -1;
$reason = $data['state_text'];
break;
default:
break;
}
if (isset($data['driver_name']) && $data['driver_name']) {
$delivery = [
'delivery_name' => $data['driver_name'],
'delivery_id' => $data['driver_mobile'],
];
}
}
if (isset($order_id) && isset($status)) {
$deliveryOrder = $this->dao->getOne(['order_id' => $order_id]);
if ($deliveryOrder) {
$this->notifyAfter($status, $reason, $deliveryOrder, $delivery, $deductFee);
} else {
Log::info('同城配送回调,未查询到订单:' . $order_id);
}
}
}
/**
* @param $status
* @param $reason
* @param $res
* @param $data
* @param $deductFee
* @return bool
*/
public function notifyAfter($status, $reason, $deliveryOrder, $data, $deductFee)
{
if (!isset($this->statusData[$status])) return true;
/** @var StoreOrderServices $orderServices */
$orderServices = app()->make(StoreOrderServices::class);
$orderData = $orderServices->get($deliveryOrder['oid']);
/** @var StoreOrderStatusServices $statusServices */
$statusServices = app()->make(StoreOrderStatusServices::class);
$change_type = 'city_delivery_' . $status;
$oid = (int)$deliveryOrder['oid'];
if ($orderData['status'] != $status && !$statusServices->count(['oid' => $oid, 'change_type' => $change_type])) {
$order = $orderServices->get($oid);
//修改配送单
$this->dao->update($deliveryOrder['id'], ['status' => $status, 'reason' => $reason]);
//增加
$message = '订单已配送【' . $this->statusData[$status] . '】';
//记录原订单状态
OrderStatusJob::dispatch([$oid, $change_type, ['change_message' => $message, 'change_manager_type' => 'system']]);
switch ($status) {
case 2:
if (!empty($data)) $orderServices->update($oid, $data);
break;
case 4:
//订单收货
if ($order['status'] != 2) {
OrderTakeJob::dispatch([$order]);
}
break;
case -1:
$this->doCancel((int)$deliveryOrder['id'], $deliveryOrder, $deductFee, $reason);
break;
}
}
return true;
}
/**
* 同步达达门店信息
* @param int $id
* @param bool $is_new
* @param int $type
* @param int $station_type
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function syncCityShop(int $id, int $type = 1, int $station_type = 1)
{
$data = [];
if ($station_type == 1) {
$status = sys_config('dada_delivery_status');
} else {
$status = sys_config('uu_delivery_status');
}
if (!$status) {//未开启
return $data;
}
if ($type == 1) {//门店
/** @var SystemStoreServices $storeServices */
$storeServices = app()->make(SystemStoreServices::class);
$station = $storeServices->getStoreInfo($id);
/** @var CityAreaServices $cityArea */
$cityArea = app()->make(CityAreaServices::class);
$station['city_name'] = $cityArea->value(['id' => $station['city']], 'name');
$station['address'] = $station['detailed_address'];
if (!$station['city_shop_id']) {
$station['city_shop_id'] = 'deliver_store_' . $station['id'] . '_' . $this->getOrderSn();
$storeServices->update($id, ['city_shop_id' => $station['city_shop_id']]);
}
} elseif ($type == 2) {//供应商
/** @var SystemSupplierServices $services */
$services = app()->make(SystemSupplierServices::class);
$station = $services->getSupplierInfo($id);
if ($station) {
$station['name'] = $station['supplier_name'] ?? '';
}
} else {//平台
$station = SystemConfigService::more(['refund_address', 'refund_name', 'refund_phone']);
$address = $station['refund_address'];
if ($address) {
$station['address'] = $station['refund_address'];
$station['name'] = $station['refund_name'];
$station['phone'] = $station['refund_phone'];
$station['city_shop_id'] = $this->platCityShopId;
}
}
if (!isset($station['latitude'])) {//地址转经纬度
if (!isset($station['address'])) {
throw new ValidateException($type == 0 ? '请检查(设置->商城设置->交易设置->退货收货人地址)完整性' : '请检查该订单关联供应商地址信息是否正确');
}
$addressArr = $this->addressHandle($station['address']);
if (!$addressArr['province'] || !$addressArr['city']) {
throw new ValidateException($type == 0 ? '退货收货地址格错误,应包含完整的省市地址' : '请检查该订单关联供应商地址信息是否正确');
}
$station['city_name'] = $addressArr['city'] ?? '';
try {
$addres = $this->lbs_address($station['city_name'], $station['address']);
$station['latitude'] = $addres['location']['lat'] ?? '';
$station['longitude'] = $addres['location']['lng'] ?? '';
} catch (\Exception $e) {
throw new ValidateException('获取经纬度失败');
}
}
$data = [
'lng' => (float)($station['longitude'] ?? 0),
'lat' => (float)($station['latitude'] ?? 0),
'phone' => $station['phone'] ?? '',
'business' => (int)($station['business'] ?? 5),
'contact_name' => $station['name'] ?? '',
'station_name' => $station['name'],
'station_address' => $station['address'] ?? '',
'status' => 1,
'origin_shop_id' => $station['city_shop_id'],
];
if ($data) {
$serve = DeliverySevices::init($station_type);
try {
$shop = $serve->getShopDetail($data['origin_shop_id']);
} catch (\Throwable $e) {
$shop = [];
}
try {
if (!$shop) {
$serve->addShop($data);
} else {
$serve->updateShop($data);
}
} catch (\Throwable $e) {
throw new ValidateException('创建达达门店失败,原因:' . $e->getMessage());
}
}
$data['city_name'] = $station['city_name'];
return $data;
}
}

View File

@@ -0,0 +1,403 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\order;
use app\dao\order\StoreOrderCartInfoDao;
use app\services\BaseServices;
use crmeb\services\CacheService;
use crmeb\services\SystemConfigService;
use crmeb\traits\OptionTrait;
use crmeb\utils\Arr;
use think\annotation\Inject;
use think\exception\ValidateException;
/**
* Class StoreOrderCartInfoServices
* @package app\services\order
* @mixin StoreOrderCartInfoDao
*/
class StoreOrderCartInfoServices extends BaseServices
{
use OptionTrait;
/**
* @var StoreOrderCartInfoDao
*/
#[Inject]
protected StoreOrderCartInfoDao $dao;
/**
* 检测订单商品是否核销完
* @param int $oid
* @return bool
*/
public function checkWriteOff(int $oid): bool
{
return !$this->dao->count(['oid' => $oid, 'is_writeoff' => 0]);
}
/**
* 清空订单商品缓存
* @param int $oid
* @return bool
* @throws \Psr\SimpleCache\InvalidArgumentException
*/
public function clearOrderCartInfo(int $oid)
{
return CacheService::delete(md5('store_order_cart_info_' . $oid));
}
/**
* 获取指定订单下的商品详情
* @param int $oid
* @return array|mixed
*/
public function getOrderCartInfo(int $oid)
{
$key = md5('store_order_cart_info_' . $oid);
return $this->dao->cacheTag()->remember($key, function () use ($oid) {
$cart_info = $this->dao->getCartColunm(['oid' => $oid], 'cart_info', 'cart_id');
$info = [];
foreach ($cart_info as $k => $v) {
$_info = is_string($v) ? json_decode($v, true) : $v;
if (!isset($_info['productInfo'])) $_info['productInfo'] = [];
//缩略图处理
if (isset($_info['productInfo']['attrInfo'])) {
$_info['productInfo']['attrInfo'] = get_thumb_water($_info['productInfo']['attrInfo']);
}
$_info['product_type'] = $_info['productInfo']['product_type'] ?? 0;
$_info['supplier_id'] = $_info['productInfo']['supplier_id'] ?? 0;
$_info['is_support_refund'] = $_info['productInfo']['is_support_refund'] ?? 1;
$_info['store_name'] = $_info['store_name'] ?? $_info['title'] ?? '';
$_info['productInfo'] = get_thumb_water($_info['productInfo']);
$_info['refund_num'] = $this->dao->sum(['cart_id' => $_info['id']], 'refund_num');
$info[$k]['cart_info'] = $_info;
unset($_info);
}
return $info;
}, 7 * 24 * 3600);
}
/**
* 获取指定订单下的商品详情 供应商
* @param int $oid
* @return array
*/
public function getOrderCartInfoSettlePrice(int $oid)
{
$cart_info = $this->dao->getCartColunm(['oid' => $oid], 'cart_num,refund_num,cart_info', 'id');
$settlePrice = 0;
$refundSettlePrice = 0;
foreach ($cart_info as $k => &$v) {
$_info = is_string($v['cart_info']) ? json_decode($v['cart_info'], true) : $v['cart_info'];
$v['cart_info'] = $_info;
if (!isset($_info['productInfo'])) $_info['productInfo'] = [];
$settle_price = $_info['productInfo']['attrInfo']['settle_price'] ?? 0;
$_info['settlePrice'] = bcmul((string)$settle_price, (string)$v['cart_num'], 2); //购买结算价格
$settlePrice = bcadd($_info['settlePrice'], $settlePrice, 2);
$_info['refundSettlePrice'] = bcmul((string)$settle_price, (string)$v['cart_num'], 2);//退款结算价格
$refundSettlePrice = bcadd($_info['refundSettlePrice'], $refundSettlePrice, 2);
}
return ['settlePrice' => $settlePrice, 'refundSettlePrice' => $refundSettlePrice, 'info' => $cart_info];
}
/**
* 查找购物车里的所有商品标题
* @param int $oid
* @param false $goodsNum
* @return bool|mixed|string
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getCarIdByProductTitle(int $oid, bool $goodsNum = false)
{
$key = md5('store_order_cart_product_title_' . ($goodsNum ? 1 : 0) . '_' . $oid);
$title = CacheService::get($key);
if (!$title) {
$orderCart = $this->dao->getCartInfoList(['oid' => $oid], ['cart_info']);
foreach ($orderCart as $item) {
if (isset($item['cart_info']['productInfo']['store_name'])) {
if ($goodsNum && isset($item['cart_info']['cart_num'])) {
$title .= $item['cart_info']['productInfo']['store_name'] . ' * ' . $item['cart_info']['cart_num'] . ' | '.$item['cart_info']['productInfo']['attrInfo']['suk']. ' |';
} else {
$title .= $item['cart_info']['productInfo']['store_name'] . '|';
}
}
}
if ($title) {
$title = substr($title, 0, strlen($title) - 1);
}
CacheService::set($key, $title, 7 * 24 * 3600);
}
return $title ? $title : '';
}
/**
* 获取打印订单的商品信息
* @param int $oid
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getCartInfoPrintProduct(int $oid)
{
$cartInfo = $this->dao->getCartInfoList(['oid' => $oid], ['is_gift', 'cart_info']);
$product = [];
foreach ($cartInfo as $item) {
$value = is_string($item['cart_info']) ? json_decode($item['cart_info'], true) : $item['cart_info'];
$value['productInfo']['store_name'] = $value['productInfo']['store_name'] ?? "";
$value['productInfo']['store_name'] = substrUTf8($value['productInfo']['store_name'], 10, 'UTF-8', '');
$value['is_gift'] = $item['is_gift'];
$product[] = $value;
}
return $product;
}
/**
* 保存购物车info
* @param $oid
* @param array $cartInfo
* @param $uid
* @param array $promotions
* @return int
*/
public function setCartInfo($oid, array $cartInfo, $uid, array $promotions = [])
{
$group = [];
foreach ($cartInfo as $cart) {
$group[] = [
'oid' => $oid,
'uid' => $uid,
'cart_id' => $cart['id'],
'type' => $cart['type'] == 5 ? ($cart['productInfo']['plat_type'] ?? 0) : ($cart['productInfo']['type'] ?? 0),
'relation_id' => $cart['productInfo']['relation_id'] ?? 0,
'product_id' => $cart['product_id'] ?? $cart['productInfo']['id'],//原商品ID
'product_type' => $cart['productInfo']['product_type'] ?? 0,
'sku_unique' => $cart['product_attr_unique'] ?? '',
'promotions_id' => implode(',', $cart['promotions_id'] ?? []),
'is_gift' => isset($cart['is_gift']) && $cart['is_gift'] ? 1 : 0,
'is_support_refund' => isset($cart['is_gift']) && $cart['is_gift'] ? 0 : ($cart['productInfo']['is_support_refund'] ?? 1),
'cart_info' => json_encode($cart),
'cart_num' => $cart['cart_num'],
'surplus_num' => $cart['cart_num'],
'split_surplus_num' => $cart['cart_num'],
'write_times' => bcmul((string)$cart['cart_num'], (string)(max($cart['productInfo']['attrInfo']['write_times'] ?? 1, 1)), 0),
'write_surplus_times' => bcmul((string)$cart['cart_num'], (string)(max($cart['productInfo']['attrInfo']['write_times'] ?? 1, 1)), 0),
'unique' => md5($cart['id'] . '' . $oid),
'total_price' => $cart['total_price'] ?? 0,
'pay_price' => $cart['pay_price'] ?? 0,
'pay_postage' => $cart['postage_price'] ?? 0,
'coupon_price' => $cart['coupon_price'] ?? 0,
'promotions_price' => $cart['sum_promotions_price'] ?? 0,
'first_order_price' => $cart['first_order_price'] ?? 0,
];
}
if ($promotions) {
/** @var StoreOrderPromotionsServices $services */
$services = app()->make(StoreOrderPromotionsServices::class);
$services->setPromotionsDetail((int)$uid, (int)$oid, $cartInfo, $promotions);
}
return $this->dao->saveAll($group);
}
/**
* 订单创建成功之后计算订单(实际优惠、积分、佣金、上级、上上级)
* @param $oid
* @param array $cartInfo
* @return bool
*/
public function updateCartInfo($oid, array $cartInfo)
{
foreach ($cartInfo as $cart) {
$group = [
'cart_info' => json_encode($cart),
'pay_price' => max($cart['pay_price'],0),
'deduction_price' => $cart['integral_price'] ?? 0,
'change_price' => $cart['change_price'] ?? 0
];
$this->dao->update(['oid' => $oid, 'cart_id' => $cart['id']], $group);
}
$this->dao->cacheTag()->clear();
return true;
}
/**
* 商品编号
* @param int $oid
* @return array
*/
public function getCartIdsProduct(int $oid)
{
$where = [
'oid' => $oid,
];
return $this->dao->getColumn($where, 'product_id', 'oid', true);
}
/**
* 检测这些商品是否还可以拆分
* @param int $oid
* @param array $cart_data
* @return bool
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function checkCartIdsIsSplit(int $oid, array $cart_data)
{
if (!$cart_data) return false;
$ids = array_unique(array_column($cart_data, 'cart_id'));
if ($this->dao->getCartInfoList(['oid' => $oid, 'cart_id' => $ids, 'split_status' => 2], ['cart_id'])) {
throw new ValidateException('您选择的商品已经拆分完成,请刷新或稍后重新选择');
}
$cartInfo = $this->getSplitCartList($oid, 'surplus_num,split_surplus_num,cart_info,cart_num', 'cart_id');
if (!$cartInfo) {
throw new ValidateException('该订单已发货完成');
}
foreach ($cart_data as $cart) {
$surplus_num = $cartInfo[$cart['cart_id']]['surplus_num'] ?? 0;
if (!$surplus_num) {//兼容之前老数据
$_info = $cartInfo[$cart['cart_id']]['cart_info'] ?? [];
$surplus_num = $_info['cart_num'] ?? 0;
}
if ($cart['cart_num'] > $surplus_num) {
throw new ValidateException('您选择商品拆分数量大于购买数量');
}
}
return true;
}
/**
* 获取可退款商品
* @param int $oid
* @param string $field
* @param string $key
* @return array
*/
public function getRefundCartList(int $oid, string $field = '*', string $key = '')
{
$cartInfo = $this->dao->getColumn([['oid', '=', $oid]], $field, $key);
foreach ($cartInfo as $key => &$item) {
if ($field == 'cart_info') {
$item = is_string($item) ? json_decode($item, true) : $item;
} else {
if (isset($item['cart_info'])) $item['cart_info'] = is_string($item['cart_info']) ? json_decode($item['cart_info'], true) : $item['cart_info'];
if (isset($item['cart_num']) && !$item['cart_num']) {//兼容之前老数据
$item['cart_num'] = $item['cart_info']['cart_num'] ?? 0;
}
}
$surplus = (int)bcsub((string)$item['cart_num'], (string)$item['refund_num'], 0);
if ($surplus > 0) {
$item['surplus_num'] = $surplus;
} else {
unset($cartInfo[$key]);
}
}
return array_merge($cartInfo);
}
/**
* 获取某个订单还可以拆分商品 split_status 0:未拆分1:部分拆分2:拆分完成
* @param int $oid
* @param string $field
* @param string $key
* @return array
*/
public function getSplitCartList(int $oid, string $field = '*', string $key = '')
{
/** @var StoreOrderServices $orderServices */
$orderServices = app()->make(StoreOrderServices::class);
$order = $orderServices->get($oid);
if (!$order) {
throw new ValidateException('订单不存在');
}
$store_id = $this->getItem('store_id', 0);
$supplier_id = $this->getItem('supplier_id', 0);
//拆分完整主订单查询未发货子订单
if ($order['pid'] == -1) {
$oid = $orderServices->value(['pid' => $oid, 'status' => 0, 'supplier_id' => $supplier_id, 'store_id' => $store_id, 'refund_type' => [0, 3]], 'id');
}
$cartInfo = $this->dao->getColumn([['oid', '=', $oid], ['split_status', 'IN', [0, 1]]], $field, $key);
foreach ($cartInfo as &$item) {
if ($field == 'cart_info') {
$item = is_string($item) ? json_decode($item, true) : $item;
} else {
if (isset($item['cart_info'])) $item['cart_info'] = is_string($item['cart_info']) ? json_decode($item['cart_info'], true) : $item['cart_info'];
if (isset($item['cart_num']) && !$item['cart_num']) {//兼容之前老数据
$item['cart_num'] = $item['cart_info']['cart_num'] ?? 0;
}
$item['surplus_num'] = $item['split_surplus_num'];
}
}
return $cartInfo;
}
/**
* 次卡商品未核销短信提醒
* @return bool
*/
public function reminderUnverifiedRemind()
{
/** @var StoreOrderServices $orderServices */
$orderServices = app()->make(StoreOrderServices::class);
// 临期
//系统预设取消订单时间段
$keyValue = ['reminder_deadline_second_card_time'];
//获取配置
$systemValue = SystemConfigService::more($keyValue);
//格式化数据
$systemValue = Arr::setValeTime($keyValue, is_array($systemValue) ? $systemValue : []);
$reminder_deadline_second_card_time = $systemValue['reminder_deadline_second_card_time'];
$reminder_deadline_second_card_time = (int)bcmul((string)$reminder_deadline_second_card_time, '3600', 0);
$writeTime = time() + $reminder_deadline_second_card_time;
$adventList = $this->dao->getAdventCartInfoList(time(), $writeTime);
if ($adventList) {
foreach ($adventList as $key => $item) {
$cart_info = is_string($item['cart_info']) ? json_decode($item['cart_info'], true) : $item['cart_info'];
$store_name = substrUTf8($cart_info['productInfo']['store_name'], 10, 'UTF-8', '');
$data['store_name'] = $store_name;
$order = $orderServices->get($item['oid'], ['id', 'uid', 'pay_time', 'user_phone']);
if (!$order) {
continue;
}
$data['uid'] = $order['uid'];
$data['end_time'] = date('Y-m-d H:i', $item['write_end']);
$data['pay_time'] = date('Y-m-d H:i', $order['pay_time']);
$data['phone'] = $order['user_phone'];
event('notice.notice', [$data, 'reminder_brink_death']);
$this->dao->update($item['id'], ['is_advent_sms' => 1]);
}
}
// 过期
$expireList = $this->dao->getExpireCartInfoList(time());
if ($expireList) {
foreach ($expireList as $key => $item) {
$cart_info = is_string($item['cart_info']) ? json_decode($item['cart_info'], true) : $item['cart_info'];
$store_name = substrUTf8($cart_info['productInfo']['store_name'], 10, 'UTF-8', '');
$data['store_name'] = $store_name;
$data['end_time'] = date('Y-m-d H:i', $item['write_end']);
$order = $orderServices->get($item['oid'], ['id', 'uid', 'pay_time', 'user_phone']);
if (!$order) {
continue;
}
$data['uid'] = $order['uid'];
$data['phone'] = $order['user_phone'];
event('notice.notice', [$data, 'expiration_reminder']);
$this->dao->update($item['id'], ['is_expire_sms' => 1]);
}
}
return true;
}
}

View File

@@ -0,0 +1,219 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\order;
use app\dao\order\StoreOrderDao;
use app\jobs\order\AutoCommentOrderJob;
use app\services\BaseServices;
use app\services\product\product\StoreProductReplyServices;
use app\services\user\UserServices;
use think\annotation\Inject;
use think\exception\ValidateException;
/**
* 订单评价
* Class StoreOrderCommentServices
* @package app\services\order
* @mixin StoreOrderDao
*/
class StoreOrderCommentServices extends BaseServices
{
/**
* 评论默认数据
* @var string[]
*/
protected array $commentArr = [
'reply_score' => 3,
'product_score' => 5,
'service_score' => 5,
'delivery_score' => 5,
'comment' => '',
'pics' => [],
];
/**
* @var StoreOrderDao
*/
#[Inject]
protected StoreOrderDao $dao;
/**
* 订单评价
* @param int $oid
* @param int $uid
* @param array $group
* @param string $unique
* @return bool
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function comment(int $oid, int $uid = 0, array $group = [], string $unique = '')
{
if (!$oid) throw new ValidateException('参数错误!');
$order = $this->dao->get($oid);
if (!$order) {
throw new ValidateException('订单不存在!');
}
$where = ['oid' => $oid];
if ($unique) {
$where['unique'] = $unique;
}
/** @var StoreOrderCartInfoServices $cartInfoServices */
$cartInfoServices = app()->make(StoreOrderCartInfoServices::class);
$cartInfos = $cartInfoServices->getColumn($where, 'oid,product_id,cart_info,unique');
if (!$cartInfos) {
throw new ValidateException('评价商品不存在');
}
$is_sync = $group['is_sync'] ?? 0;
unset($group['is_sync']);
$group = array_merge($this->commentArr, array_intersect_key($group, $this->commentArr));
$group['comment'] = htmlspecialchars(trim($group['comment']));
if ($group['product_score'] < 1) throw new ValidateException('请为商品评分');
if ($group['service_score'] < 1) throw new ValidateException('请为商家服务评分');
if ($group['delivery_score'] < 1) throw new ValidateException('请为物流服务评分');
if ($group['pics']){
$pics = is_array($group['pics']) ? $group['pics'] : explode(',', $group['pics']);
$group['pics'] = json_encode($pics);
}
$user_info = [];
if ($uid) {
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
$user_info = $userServices->getUserInfo($uid);
}
if ($order['store_id']) {
$group['type'] = 1;
$group['relation_id'] = $order['store_id'];
} elseif ($order['supplier_id']) {
$group['type'] = 2;
$group['relation_id'] = $order['supplier_id'];
} else {
$group['type'] = $group['relation_id'] = 0;
}
$group = array_merge($group, [
'uid' => $uid,
'nickname' => $user_info['nickname'] ?? substr(md5($oid . time()), 0, 12),
'avatar' => $user_info['avatar'] ?? sys_config('h5_avatar'),
'oid' => $oid,
'add_time' => time(),
'reply_type' => 'product'
]);
/** @var StoreProductReplyServices $replyServices */
$replyServices = app()->make(StoreProductReplyServices::class);
$data_all = [];
$ids = [];
$sync = [];
foreach ($cartInfos as $cartInfo) {
if ($replyServices->be(['oid' => $cartInfo['oid'], 'unique' => $cartInfo['unique']])) continue;
$_info = is_string($cartInfo['cart_info']) ? json_decode($cartInfo['cart_info'], true) : $cartInfo['cart_info'];
if (isset($_info['activity_id']) && $_info['activity_id']) $productId = $_info['product_id'];
else $productId = $cartInfo['product_id'];
$ids[] = $productId;
$group['unique'] = $cartInfo['unique'];
$group['product_id'] = $productId;
$group['sku_unique'] = $_info['productInfo']['attrInfo']['unique'] ?? '';
$group['sku'] = $_info['productInfo']['attrInfo']['suk'] ?? '';
$data_all[] = $group;
}
if ($is_sync) {
$sync = [
'content_type' => 1,
'content' => $group['comment'] ?? '',
'slider_image' => $pics ?? [],
'relation_id' => $uid,
'product_id' => $ids
];
}
if ($data_all) {
$res = $replyServices->saveAll($data_all);
if (!$res) {
throw new ValidateException('评价失败!');
}
}
try {
/** @var StoreOrderServices $orderServices */
$orderServices = app()->make(StoreOrderServices::class);
$orderServices->checkOrderOver($replyServices, $cartInfoServices->getCartColunm(['oid' => $oid, 'is_gift' => 0], 'unique', ''), $oid);
} catch (\Exception $e) {
throw new ValidateException($e->getMessage());
}
//清除评价缓存
$replyServices->cacheTag()->clear();
//订单评价成功事件
event('order.comment', [$order]);
if ($ids) {
//商品评价
event('product.reply', [$ids, $is_sync, $sync]);
}
return true;
}
/**
* 执行自动默认好评
* @param array $where
* @param int $page
* @param int $maxLimit
* @return bool
*/
public function runAutoCommentOrder(array $where, int $page = 0, int $maxLimit = 0)
{
/** @var StoreOrderStoreOrderStatusServices $service */
$service = app()->make(StoreOrderStoreOrderStatusServices::class);
$orderList = $service->getOrderIds($where, $page, $maxLimit);
foreach ($orderList as $order) {
if ($order['status'] == 3) {
continue;
}
$group = ['comment' => '此用户未作评价'];
$this->comment((int)$order['id'], (int)$order['uid'], $group);
}
return true;
}
/**
* 自动默认好评
* @return bool
*/
public function autoCommentOrder()
{
//7天前时间戳
$systemCommentTime = (int)sys_config('system_comment_time', 0);
//0为取消自动默认好评功能
if ($systemCommentTime == 0) {
return true;
}
$sevenDay = strtotime(date('Y-m-d H:i:s', strtotime('-' . $systemCommentTime . ' day')));
/** @var StoreOrderStoreOrderStatusServices $service */
$service = app()->make(StoreOrderStoreOrderStatusServices::class);
$where = [
'change_time' => $sevenDay,
'is_del' => 0,
'paid' => 1,
'status' => 2,
'change_type' => ['user_take_delivery', 'take_delivery', 'writeoff']
];
$maxLimit = 20;
$count = $service->getOrderCount($where);
if ($count > $maxLimit) {
$pages = ceil($count / $maxLimit);
for ($i = 1; $i <= $pages; $i++) {
AutoCommentOrderJob::dispatch([$where, $i, $maxLimit]);
}
} else {
$this->runAutoCommentOrder($where);
}
return true;
}
}

View File

@@ -0,0 +1,743 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\order;
use app\services\activity\discounts\StoreDiscountsServices;
use app\services\activity\holiday\HolidayGiftPushServices;
use app\services\BaseServices;
use app\dao\order\StoreOrderDao;
use app\services\other\CityAreaServices;
use app\services\pay\PayServices;
use app\services\product\brand\StoreBrandServices;
use app\services\product\category\StoreProductCategoryServices;
use app\services\user\member\MemberCardServices;
use app\services\user\UserServices;
use think\annotation\Inject;
use think\exception\ValidateException;
use app\services\user\UserAddressServices;
use app\services\activity\coupon\StoreCouponUserServices;
use app\services\product\shipping\ShippingTemplatesFreeServices;
use app\services\product\shipping\ShippingTemplatesRegionServices;
use app\services\product\shipping\ShippingTemplatesServices;
use app\services\user\UserBillServices;
use function Swoole\Coroutine\batch;
/**
* 订单计算金额
* Class StoreOrderComputedServices
* @package app\services\order
* @mixin StoreOrderDao
*/
class StoreOrderComputedServices extends BaseServices
{
/**
* 支付类型
* @var string[]
*/
public array $payType = ['weixin' => '微信支付', 'yue' => '余额支付', 'offline' => '线下支付', 'pc' => 'pc'];
/**
* 额外参数
* @var array
*/
protected array $paramData = [];
/**
* @var StoreOrderDao
*/
#[Inject]
protected StoreOrderDao $dao;
/**
* 设置额外参数
* @param array $paramData
* @return $this
*/
public function setParamData(array $paramData)
{
$this->paramData = $paramData;
return $this;
}
/**
* 计算订单金额
* @param int $uid
* @param array $userInfo
* @param array $cartGroup
* @param int $addressId
* @param string $payType
* @param bool $useIntegral
* @param int $couponId
* @param int $shippingType
* @return array
*/
public function computedOrder(int $uid, array $userInfo, array $cartGroup, int $addressId, string $payType, bool $useIntegral = false, int $couponId = 0, int $shippingType = 1, int $isSendGift = 0)
{
$offlinePayStatus = (int)sys_config('offline_pay_status') ?? (int)2;
$systemPayType = PayServices::PAY_TYPE;
if ($offlinePayStatus == 2) unset($systemPayType['offline']);
if ($payType && !array_key_exists($payType, $systemPayType)) {
throw new ValidateException('选择支付方式有误');
}
if ($uid && !$userInfo) {
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
$userInfo = $userServices->getUserCacheInfo($uid);
if (!$userInfo) {
throw new ValidateException('用户不存在!');
}
}
$cartInfo = $cartGroup['cartInfo'];
$priceGroup = $cartGroup['priceGroup'];
$deduction = $cartGroup['deduction'];
$other = $cartGroup['other'];
$promotions = $other['promotions'] ?? [];
$payPrice = (float)$priceGroup['totalPrice'];
$payIntegral = (int)$priceGroup['totalIntegral'] ?? 0;
$couponPrice = (float)$priceGroup['couponPrice'];
$firstOrderPrice = (float)$priceGroup['firstOrderPrice'];
$changePrice = (float)$priceGroup['changePrice'] ?? 0.00;
$addr = $cartGroup['addr'] ?? [];
$postage = $priceGroup;
if (!$addr || $addr['id'] != $addressId) {
/** @var UserAddressServices $addressServices */
$addressServices = app()->make(UserAddressServices::class);
$addr = $addressServices->getAdderssCache($addressId);
//改变地址重新计算邮费
$postage = [];
}
$type = (int)$deduction['type'] ?? 0;
$results = batch([
'promotions' => function () use ($cartInfo, $type) {
$promotionsPrice = 0;
if ($type == 8) return $promotionsPrice;
foreach ($cartInfo as $key => $cart) {
if (isset($cart['sum_promotions_price']) && isset($cart['price_type']) && $cart['price_type'] == 'promotions') {
$promotionsPrice = bcadd((string)$promotionsPrice, (string)$cart['sum_promotions_price'], 2);
}
}
return $promotionsPrice;
},
'postage' => function () use ($uid, $shippingType, $payType, $cartInfo, $addr, $payPrice, $postage, $other, $type, $isSendGift) {
if ($type == 8) $shippingType = 2;
return $this->computedPayPostage($uid, $shippingType, $payType, $cartInfo, $addr, $payPrice, $postage, $other, $isSendGift);
},
]);
$promotionsDetail = [];
if ($promotions) {
foreach ($promotions as $key => $value) {
if (isset($value['details']['sum_promotions_price']) && $value['details']['sum_promotions_price']) {
$promotionsDetail[] = ['id' => $value['id'], 'name' => $value['name'], 'title' => $value['title'], 'desc' => $value['desc'], 'promotions_price' => $value['details']['sum_promotions_price'], 'promotions_type' => $value['promotions_type']];
}
}
if ($promotionsDetail) {
$typeArr = array_column($promotionsDetail, 'promotions_type');
array_multisort($typeArr, SORT_ASC, $promotionsDetail);
}
}
// [$p, $couponPrice] = $results['coupon'];
[$p, $payPostage, $storePostageDiscount, $storeFreePostage, $isStoreFreePostage] = $results['postage'];
if ($couponPrice < $payPrice) {//优惠券金额
$payPrice = bcsub((string)$payPrice, (string)$couponPrice, 2);
} else {
$payPrice = 0;
}
if ($type == 8) {
$firstOrderPrice = 0;
$payPrice = 0;
}
if ($firstOrderPrice < $payPrice) {//首单优惠金额
$payPrice = bcsub((string)$payPrice, (string)$firstOrderPrice, 2);
} else {
$payPrice = 0;
}
if ($uid && sys_config('integral_ratio_status') && in_array($type, [0, 6])) {
//使用积分
[$payPrice, $deductionPrice, $usedIntegral, $SurplusIntegral] = $this->useIntegral($useIntegral, $userInfo, $payPrice, $other);
}
$payPrice = (float)bcadd((string)$payPrice, (string)$payPostage, 2);
foreach ($cartInfo as &$item) {
$item['invalid'] = false;
if ($shippingType === 2 && in_array(2, $item['productInfo']['delivery_type'])) {
$item['invalid'] = true;
}
if($shippingType == 2){
$item['postage_price'] = 0;
}
}
$payPrice = bcadd($payPrice, $priceGroup['sendGiftPrice'], 2);
$result = [
'total_price' => $priceGroup['totalPrice'],
'sendGiftPrice' => $priceGroup['sendGiftPrice'],
'pay_price' => max($payPrice, 0),
'pay_integral' => max($payIntegral, 0),
'total_postage' => bcadd((string)$payPostage, (string)($storePostageDiscount ?? 0), 2),
'pay_postage' => $payPostage,
'first_order_price' => $firstOrderPrice ?? 0,
'coupon_price' => $couponPrice ?? 0,
'promotions_price' => $results['promotions'] ?? 0,
'promotions_detail' => $promotionsDetail,
'deduction_price' => $deductionPrice ?? 0,
'usedIntegral' => $usedIntegral ?? 0,
'SurplusIntegral' => $SurplusIntegral ?? 0,
'storePostageDiscount' => $storePostageDiscount ?? 0,
'isStoreFreePostage' => $isStoreFreePostage ?? false,
'storeFreePostage' => $storeFreePostage ?? 0,
'change_price' => $changePrice,
'cartInfo' => $cartInfo
];
$this->paramData = [];
return $result;
}
/**
* 使用优惠卷
* @param int $couponId
* @param int $uid
* @param $cartInfo
* @param $payPrice
*/
public function useCouponId(int $couponId, int $uid, $cartInfo, $payPrice, $promotions)
{
//使用优惠劵
$couponPrice = 0;
if ($couponId && $cartInfo) {
/** @var StoreCouponUserServices $couponServices */
$couponServices = app()->make(StoreCouponUserServices::class);
$couponInfo = $couponServices->getOne([['id', '=', $couponId], ['uid', '=', $uid], ['is_fail', '=', 0], ['status', '=', 0], ['start_time', '<=', time()], ['end_time', '>=', time()]], '*', ['issue']);
if (!$couponInfo) {
throw new ValidateException('选择的优惠劵无效!');
}
$type = $couponInfo['applicable_type'] ?? 0;
$flag = false;
$price = 0;
$count = 0;
$promotionsList = [];
if ($promotions) {
$promotionsList = array_combine(array_column($promotions, 'id'), $promotions);
}
$isOverlay = function ($cart) use ($promotionsList) {
$productInfo = $cart['productInfo'] ?? [];
if (!$productInfo) {
return false;
}
if (isset($cart['promotions_id']) && $cart['promotions_id']) {
foreach ($cart['promotions_id'] as $key => $promotions_id) {
$promotions = $promotionsList[$promotions_id] ?? [];
if ($promotions && $promotions['promotions_type'] != 4) {
$overlay = is_string($promotions['overlay']) ? explode(',', $promotions['overlay']) : $promotions['overlay'];
if (!in_array(5, $overlay)) {
return false;
}
}
}
}
return true;
};
switch ($type) {
case 0:
foreach ($cartInfo as $cart) {
if (!$isOverlay($cart)) continue;
$price = bcadd($price, bcmul((string)$cart['truePrice'], (string)$cart['cart_num'], 2), 2);
$count++;
}
break;
case 1://品类券
/** @var StoreProductCategoryServices $storeCategoryServices */
$storeCategoryServices = app()->make(StoreProductCategoryServices::class);
$cateGorys = $storeCategoryServices->getAllById((int)$couponInfo['category_id']);
if ($cateGorys) {
$cateIds = array_column($cateGorys, 'id');
foreach ($cartInfo as $cart) {
if (!$isOverlay($cart)) continue;
if (isset($cart['productInfo']['cate_id']) && array_intersect(explode(',', $cart['productInfo']['cate_id']), $cateIds)) {
$price = bcadd($price, bcmul((string)$cart['truePrice'], (string)$cart['cart_num'], 2), 2);
$count++;
}
}
}
break;
case 2:
foreach ($cartInfo as $cart) {
if (!$isOverlay($cart)) continue;
if (isset($cart['product_id']) && in_array($cart['product_id'], explode(',', $couponInfo['product_id']))) {
$price = bcadd($price, bcmul((string)$cart['truePrice'], (string)$cart['cart_num'], 2), 2);
$count++;
}
}
break;
case 3:
/** @var StoreBrandServices $storeBrandServices */
$storeBrandServices = app()->make(StoreBrandServices::class);
$brands = $storeBrandServices->getAllById((int)$couponInfo['brand_id']);
if ($brands) {
$brandIds = array_column($brands, 'id');
foreach ($cartInfo as $cart) {
if (!$isOverlay($cart)) continue;
if (isset($cart['productInfo']['brand_id']) && in_array($cart['productInfo']['brand_id'], $brandIds)) {
$price = bcadd((string)$price, (string)bcmul((string)$cart['truePrice'], (string)$cart['cart_num'], 2), 2);
$count++;
}
}
}
break;
}
if ($count && $couponInfo['use_min_price'] <= $price) {
$flag = true;
}
if (!$flag) {
return [$payPrice, 0];
// throw new ValidateException('不满足优惠劵的使用条件!');
}
//满减券
if ($couponInfo['coupon_type'] == 1) {
$couponPrice = $couponInfo['coupon_price'];
} else {
if ($couponInfo['coupon_price'] <= 0) {//0折
$couponPrice = $price;
} else if ($couponInfo['coupon_price'] >= 100) {
$couponPrice = 0;
} else {
$truePrice = (float)bcmul((string)$price, bcdiv((string)$couponInfo['coupon_price'], '100', 2), 2);
$couponPrice = (float)bcsub((string)$price, (string)$truePrice, 2);
}
}
if ($couponPrice < $payPrice) {
$payPrice = (float)bcsub((string)$payPrice, (string)$couponPrice, 2);
} else {
$couponPrice = $payPrice;
$payPrice = 0;
}
}
return [$payPrice, $couponPrice];
}
/**
* 使用积分
* @param $useIntegral
* @param $userInfo
* @param $payPrice
* @param $other
* @return array
*/
public function useIntegral(bool $useIntegral, $userInfo, string $payPrice, array $other)
{
$SurplusIntegral = 0;
$deductionPrice = 0;
$usedIntegral = 0;
if ($userInfo && $userInfo['integral'] > 0) {
/** @var UserBillServices $userBillServices */
$userBillServices = app()->make(UserBillServices::class);
// 可用积分
$usable = bcsub((string)$userInfo['integral'], (string)$userBillServices->getBillSum(['uid' => $userInfo['uid'], 'is_frozen' => 1]), 0);
$SurplusIntegral = $usable;
if ($useIntegral && $payPrice && $usable > 0) {
$integralMaxType = sys_config('integral_max_type', 1);//积分抵用上限类型1:积分、2:订单金额比例
if ($integralMaxType == 1) {//最多抵用积分
$integralMaxNum = sys_config('integral_max_num', 200);
if ($integralMaxNum > 0 && $usable > $integralMaxNum) {
$integral = $integralMaxNum;
} else {
$integral = $usable;
}
$deductionPrice = (float)bcmul((string)$integral, (string)$other['integralRatio'], 2);
if ($deductionPrice < $payPrice) {
$payPrice = bcsub((string)$payPrice, (string)$deductionPrice, 2);
$usedIntegral = $integral;
} else {
if ($other['integralRatio']) {
$deductionPrice = $payPrice;
$usedIntegral = (int)ceil(bcdiv((string)$payPrice, (string)$other['integralRatio'], 2));
}
$payPrice = 0;
}
} else {//最高抵用比率
$integralMaxRate = sys_config('integral_max_rate', 0);
$deductionPrice = (float)bcmul((string)$usable, (string)$other['integralRatio'], 2);
if ($integralMaxRate > 0 && $integralMaxRate <= 100) {
$integralMaxPrice = (float)bcmul((string)$payPrice, (string)bcdiv((string)$integralMaxRate, '100', 2), 2);
} else {
$integralMaxPrice = $payPrice;
}
$deductionPrice = min($deductionPrice, $integralMaxPrice);
$payPrice = bcsub((string)$payPrice, (string)$deductionPrice, 2);
$usedIntegral = ceil(bcdiv((string)$deductionPrice, (string)$other['integralRatio'], 2));
}
$SurplusIntegral = (int)bcsub((string)$SurplusIntegral, (string)$usedIntegral, 0);
if ($payPrice <= 0) $payPrice = 0;
}
}
return [$payPrice, $deductionPrice, $usedIntegral, $SurplusIntegral];
}
/**
* 计算邮费
* @param int $uid
* @param int $shipping_type
* @param string $payType
* @param array $cartInfo
* @param array $addr
* @param string $payPrice
* @param array $postage
* @param array $other
* @return array
* @throws \Throwable
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function computedPayPostage(int $uid, int $shipping_type, string $payType, array $cartInfo, array $addr, string $payPrice, array $postage = [], array $other = [], int $isSendGift = 0)
{
$storePostageDiscount = 0;
$storeFreePostage = $postage['storeFreePostage'] ?? 0;
$isStoreFreePostage = false;
if ($isSendGift == 1) {
$shipping_type = 0;
$addr = [];
}
if (!$storeFreePostage) {
$storeFreePostage = floatval(sys_config('store_free_postage')) ?: 0;//满额包邮金额
}
if (!$addr && !isset($addr['id']) || !$cartInfo) {
$payPostage = 0;
} else {
//$shipping_type = 1 快递发货 $shipping_type = 2 门店自提
if ($shipping_type == 2) {
if (!sys_config('store_func_status', 1) || !sys_config('store_self_mention', 1)) $shipping_type = 1;
}
//门店自提 || (线下支付 && 线下支付包邮) 没有邮费支付 || 节日任务赠送
$holidayGiftPushServices = app()->make(HolidayGiftPushServices::class);
$holidayGift = $holidayGiftPushServices->receiveHolidayGift($uid, 1);
if ($shipping_type === 2 || ($payType == 'offline' && ((isset($other['offlinePostage']) && $other['offlinePostage']) || sys_config('offline_postage')) == 1) || $holidayGift) {
$payPostage = 0;
} else {
if (!$postage || !isset($postage['storePostage']) || !isset($postage['storePostageDiscount'])) {
$postage = $this->getOrderPriceGroup($uid, $cartInfo, $addr, $storeFreePostage);
}
$payPostage = $postage['storePostage'];
$storePostageDiscount = $postage['storePostageDiscount'];
/** @var UserServices $userService */
$userService = app()->make(UserServices::class);
//享受svip 运费折扣
if ($userService->checkUserIsSvip($uid)) {
$payPostage = bcsub((string)$payPostage, (string)$storePostageDiscount, 2);
} else {
$storePostageDiscount = 0;
}
$isStoreFreePostage = $postage['isStoreFreePostage'] ?? false;
$payPrice = (float)bcadd((string)$payPrice, (string)$payPostage, 2);
}
}
return [$payPrice, $payPostage, $storePostageDiscount, $storeFreePostage, $isStoreFreePostage];
}
/**
* 运费计算,总金额计算
* @param int $uid
* @param $cartInfo
* @param $addr
* @param $storeFreePostage
* @return array
* @throws \Throwable
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getOrderPriceGroup(int $uid, $cartInfo, $addr, $storeFreePostage = null, $isSendGift = 0)
{
$storePostage = 0;
$sendGiftPrice = 0;
$storePostageDiscount = 0;
$isStoreFreePostage = false;//是否满额包邮
if (is_null($storeFreePostage)) {
$storeFreePostage = floatval(sys_config('store_free_postage')) ?: 0;//满额包邮金额
}
$sumPrice = $this->getOrderSumPrice($cartInfo, 'sum_price');//获取订单原总金额
$totalPrice = $this->getOrderSumPrice($cartInfo, 'pay_price', false);//获取订单svip、用户等级优惠之后总金额
$costPrice = $this->getOrderSumPrice($cartInfo, 'costPrice');//获取订单成本价
$totalIntegral = (int)$this->getOrderSumPrice($cartInfo, 'integral');//获取订单总积分
$vipPrice = $this->getOrderSumPrice($cartInfo, 'vip_truePrice');//获取订单等级和付费会员总优惠金额
$levelPrice = $this->getOrderSumPrice($cartInfo, 'level');//获取会员等级优惠
$memberPrice = $this->getOrderSumPrice($cartInfo, 'member');//获取付费会员优惠
$changePrice = (float)$this->getOrderSumPrice($cartInfo, 'change_price', false);//获取改价优惠金额
$channelPrice = (float)$this->getOrderSumPrice($cartInfo, 'channel_price', false);//采购商优惠价
// 如果送礼订单,不计算邮费,只计算送礼附加费
if ($isSendGift) {
foreach ($cartInfo as $item) {
if ($item['productInfo']['is_send_gift']) {
$giftPrice = bcmul($item['productInfo']['send_gift_price'], $item['cart_num'], 2);
$sendGiftPrice = bcadd((string)$sendGiftPrice, (string)$giftPrice, 2);
}
}
return compact('storePostage', 'storeFreePostage', 'isStoreFreePostage', 'sumPrice', 'totalPrice', 'totalIntegral', 'costPrice', 'vipPrice', 'levelPrice', 'memberPrice', 'storePostageDiscount', 'changePrice', 'cartInfo', 'channelPrice', 'sendGiftPrice');
}
//节日任务赠送包邮
$holidayGiftPushServices = app()->make(HolidayGiftPushServices::class);
if ($holidayGiftPushServices->receiveHolidayGift($uid, 1)) {
return compact('storePostage', 'storeFreePostage', 'isStoreFreePostage', 'sumPrice', 'totalPrice', 'totalIntegral', 'costPrice', 'vipPrice', 'levelPrice', 'memberPrice', 'storePostageDiscount', 'changePrice', 'cartInfo', 'channelPrice', 'sendGiftPrice');
}
//如果满额包邮等于0
$free_shipping = 0;
$postageArr = [];
if (isset($cartInfo[0]['productInfo']['product_type']) && in_array($cartInfo[0]['productInfo']['product_type'], [1, 2])) {
$storePostage = 0;
} elseif ($cartInfo && $addr) {
//优惠套餐包邮判断
if (isset($cartInfo[0]['type']) && $cartInfo[0]['type'] == 5 && isset($cartInfo[0]['activity_id']) && $cartInfo[0]['activity_id']) {
/** @var StoreDiscountsServices $discountService */
$discountService = app()->make(StoreDiscountsServices::class);
$free_shipping = $discountService->value(['id' => $cartInfo[0]['activity_id']], 'free_shipping');
}
if ($free_shipping) {
$storePostage = 0;
} else if (sys_config('whole_free_shipping') == 1 && $totalPrice >= $storeFreePostage) {//如果总价大于等于满额包邮 邮费等于0
$isStoreFreePostage = true;
$storePostage = 0;
} else {
// 判断商品包邮和固定运费
foreach ($cartInfo as &$item) {
if (!isset($item['productInfo']['freight'])) continue;
if ($item['productInfo']['freight'] == 1) {
$item['postage_price'] = 0;
} elseif ($item['productInfo']['freight'] == 2) {
$item['postage_price'] = bcmul((string)$item['productInfo']['postage'], (string)$item['cart_num'], 2);
$storePostage = bcadd((string)$storePostage, (string)$item['postage_price'], 2);
}
}
//按照运费模板计算每个运费模板下商品的件数/重量/体积以及总金额 按照首重倒序排列
$cityId = (int)($addr['city_id'] ?? 0);
$ids = [];
if ($cityId) {
/** @var CityAreaServices $cityAreaServices */
$cityAreaServices = app()->make(CityAreaServices::class);
$ids = $cityAreaServices->getRelationCityIds($cityId);
}
$cityIds = array_merge([0], $ids);
$tempIds[] = 1;
foreach ($cartInfo as $key_c => $item_c) {
if (isset($item_c['productInfo']['freight']) && $item_c['productInfo']['freight'] == 3) {
$tempIds[] = $item_c['productInfo']['temp_id'];
}
}
$tempIds = array_unique($tempIds);
/** @var ShippingTemplatesServices $shippServices */
$shippServices = app()->make(ShippingTemplatesServices::class);
$temp = $shippServices->getShippingColumnCache(['id' => $tempIds], 'appoint,group', 'id');
/** @var ShippingTemplatesRegionServices $regionServices */
$regionServices = app()->make(ShippingTemplatesRegionServices::class);
$regions = $regionServices->getTempRegionListCache($tempIds, $cityIds);
$temp_num = [];
foreach ($cartInfo as $cart) {
if (isset($cart['productInfo']['freight']) && in_array($cart['productInfo']['freight'], [1, 2])) {
continue;
}
$tempId = $cart['productInfo']['temp_id'] ?? 1;
$group = isset($temp[$tempId]['group']) ? $temp[$tempId]['group'] : $temp[1]['group'];
if ($group == 1) {
$num = $cart['cart_num'];
} elseif ($group == 2) {
$num = $cart['cart_num'] * $cart['productInfo']['attrInfo']['weight'];
} else {
$num = $cart['cart_num'] * $cart['productInfo']['attrInfo']['volume'];
}
$region = $regions[$tempId] ?? ($regions[1] ?? []);
if (!$region) {
continue;
}
if (!isset($temp_num[$tempId])) {
$temp_num[$tempId] = [
'number' => $num,
'group' => $group,
'price' => bcmul($cart['cart_num'], $cart['truePrice'], 2),
'first' => $region['first'],
'first_price' => $region['first_price'],
'continue' => $region['continue'],
'continue_price' => $region['continue_price'],
'temp_id' => $tempId
];
} else {
$temp_num[$tempId]['number'] += $num;
$temp_num[$tempId]['price'] += $cart['pay_price'];
}
}
if ($temp_num) {
/** @var ShippingTemplatesFreeServices $freeServices */
$freeServices = app()->make(ShippingTemplatesFreeServices::class);
$freeList = $freeServices->isFreeListCache($tempIds, $cityIds);
if ($freeList) {
foreach ($temp_num as $k => $v) {
if (isset($temp[$v['temp_id']]['appoint']) && $temp[$v['temp_id']]['appoint'] && isset($freeList[$v['temp_id']])) {
$free = $freeList[$v['temp_id']];
$condition = $free['number'] <= $v['number'];
if ($free['price'] <= $v['price'] && $condition) {
unset($temp_num[$k]);
}
}
}
}
//首件运费最大值
$maxFirstPrice = $temp_num ? max(array_column($temp_num, 'first_price')) : 0;
//初始运费为0
$storePostage_arr = [];
$i = 0;
//循环运费数组
foreach ($temp_num as $fk => $fv) {
//找到首件运费等于最大值
if ($fv['first_price'] == $maxFirstPrice) {
//每次循环设置初始值
$tempArr = $temp_num;
$Postage = 0;
//计算首件运费
if ($fv['number'] <= $fv['first']) {
$Postage = bcadd($Postage, $fv['first_price'], 2);
} else {
if ($fv['continue'] <= 0) {
$Postage = $Postage;
} else {
$Postage = bcadd(bcadd($Postage, $fv['first_price'], 2), bcmul(ceil(bcdiv(bcsub($fv['number'], $fv['first'], 2), $fv['continue'] ?? 0, 2)), $fv['continue_price'], 4), 2);
}
}
$postageArr[$i]['data'][$fk] = $Postage;
//删除计算过的首件数据
unset($tempArr[$fk]);
//循环计算剩余运费
foreach ($tempArr as $ck => $cv) {
if ($cv['continue'] <= 0) {
$Postage = $Postage;
} else {
$one_postage = bcmul(ceil(bcdiv($cv['number'], $cv['continue'] ?? 0, 2)), $cv['continue_price'], 2);
$Postage = bcadd($Postage, $one_postage, 2);
$postageArr[$i]['data'][$ck] = $one_postage;
}
}
$postageArr[$i]['sum'] = $Postage;
$storePostage_arr[] = $Postage;
$i++;
}
}
$maxStorePostage = $storePostage_arr ? max($storePostage_arr) : 0;
// //获取运费计算中的最大值
$storePostage = bcadd((string)$storePostage, (string)$maxStorePostage, 2);
}
}
}
//会员邮费享受折扣
if ($storePostage) {
//看是否开启会员折扣奖励
/** @var MemberCardServices $memberCardService */
$memberCardService = app()->make(MemberCardServices::class);
$express_rule_number = $memberCardService->isOpenMemberCardCache('express');
$express_rule_number = $express_rule_number <= 0 ? 0 : $express_rule_number;
/** @var UserServices $userService */
$userService = app()->make(UserServices::class);
$userIsSvip = $userService->checkUserIsSvip($uid);
$truePostageArr = [];
foreach ($postageArr as $postageitem) {
if ($postageitem['sum'] == ($maxStorePostage ?? 0)) {
$truePostageArr = $postageitem['data'];
break;
}
}
$cartAlready = [];
foreach ($cartInfo as &$item) {
if (isset($item['productInfo']['freight']) && in_array($item['productInfo']['freight'], [1, 2])) {
if (isset($item['postage_price']) && $item['postage_price'] && $express_rule_number && $express_rule_number < 100 && $userIsSvip) {
$item['postage_price'] = bcmul($item['postage_price'], bcdiv($express_rule_number, 100, 4), 2);
}
continue;
}
$tempId = $item['productInfo']['temp_id'] ?? 0;
$tempPostage = $truePostageArr[$tempId] ?? 0;
$tempNumber = $temp_num[$tempId]['number'] ?? 0;
if (!$tempId || !$tempPostage) continue;
$group = $temp_num[$tempId]['group'];
if ($group == 1) {
$num = $item['cart_num'];
} elseif ($group == 2) {
$num = $item['cart_num'] * $item['productInfo']['attrInfo']['weight'];
} else {
$num = $item['cart_num'] * $item['productInfo']['attrInfo']['volume'];
}
if ((($cartAlready[$tempId]['number'] ?? 0) + $num) >= $tempNumber) {
$price = isset($cartAlready[$tempId]['price']) ? bcsub((string)$tempPostage, (string)$cartAlready[$tempId]['price'], 2) : $tempPostage;
} else {
$price = bcmul((string)$tempPostage, bcdiv((string)$num, (string)$tempNumber, 4), 2);
}
$cartAlready[$tempId]['number'] = bcadd((string)($cartAlready[$tempId]['number'] ?? 0), (string)$num, 2);
$cartAlready[$tempId]['price'] = bcadd((string)($cartAlready[$tempId]['price'] ?? 0.00), (string)$price, 2);
if ($express_rule_number && $express_rule_number < 100 && $userIsSvip) {
$price = bcmul($price, bcdiv($express_rule_number, 100, 4), 2);
}
$price = sprintf("%.2f", $price);
$item['postage_price'] = $price;
}
if ($express_rule_number && $express_rule_number < 100) {
$payPostage = bcmul($storePostage, bcdiv($express_rule_number, 100, 4), 2);
$storePostageDiscount = bcsub($storePostage, $payPostage, 2);
} else {
$storePostageDiscount = 0;
}
}
return compact('storePostage', 'storeFreePostage', 'isStoreFreePostage', 'sumPrice', 'totalPrice', 'totalIntegral', 'costPrice', 'vipPrice', 'levelPrice', 'memberPrice', 'storePostageDiscount', 'changePrice', 'cartInfo', 'channelPrice', 'sendGiftPrice');
}
/**
* 获取某个字段总金额
* @param $cartInfo
* @param string $key
* @param bool $is_unit
* @return int|string
*/
public function getOrderSumPrice($cartInfo, $key = 'truePrice', $is_unit = true)
{
$SumPrice = 0;
foreach ($cartInfo as $cart) {
if (isset($cart['cart_info'])) $cart = $cart['cart_info'];
if (isset($cart['is_gift']) && $cart['is_gift']) {
continue;
}
if ($is_unit) {
if ($key == 'level' || $key == 'member') {
if (isset($cart['price_type']) && $cart['price_type'] == $key) {
$SumPrice = bcadd($SumPrice, bcmul($cart['cart_num'], $cart['vip_truePrice'], 2), 2);
}
} else {
$SumPrice = bcadd($SumPrice, bcmul($cart['cart_num'] ?? 1, $cart[$key] ?? 0, 2), 2);
}
} else {
$SumPrice = bcadd($SumPrice, $cart[$key] ?? 0, 2);
}
}
return $SumPrice;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,681 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\order;
use app\jobs\order\OrderStatusJob;
use app\services\BaseServices;
use app\dao\order\StoreOrderDao;
use app\services\user\UserServices;
use app\services\serve\ServeServices;
use crmeb\exceptions\AdminException;
use think\annotation\Inject;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\exception\ValidateException;
use crmeb\services\FormBuilder as Form;
use app\services\other\ExpressServices;
use app\services\message\sms\SmsSendServices;
use crmeb\traits\OptionTrait;
use think\facade\Log;
/**
* 订单发货
* Class StoreOrderDeliveryServices
* @package app\services\order
* @mixin StoreOrderDao
*/
class StoreOrderDeliveryServices extends BaseServices
{
use OptionTrait;
/**
* @var StoreOrderDao
*/
#[Inject]
protected StoreOrderDao $dao;
/**
* 订单整体发货
* @param int $id
* @param array $data
* @param int $staff_id
* @return array
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public function delivery(int $id, array $data, int $staff_id = 0)
{
$orderInfo = $this->dao->get($id, ['*'], ['pink']);
if (!$orderInfo) {
throw new ValidateException('订单未能查到,不能发货!');
}
if ($orderInfo['paid'] != 1) {
throw new ValidateException('订单未支付');
}
if ($orderInfo->is_del) {
throw new ValidateException('订单已删除,不能发货!');
}
if ($orderInfo->status == 1) {
throw new ValidateException('订单已发货请勿重复操作!');
}
if ($orderInfo->shipping_type == 2) {
throw new ValidateException('核销订单不能发货!');
}
if (isset($orderInfo['pinkStatus']) && $orderInfo['pinkStatus'] != 2) {
throw new ValidateException('拼团未完成暂不能发货!');
}
$store_id = $this->getItem('store_id', 0);
$supplier_id = $this->getItem('supplier_id', 0);
//拆分完整主订单查询未发货子订单
if ($orderInfo['pid'] == -1) {
$orderInfo = $this->dao->get(['pid' => $id, 'status' => 0, 'supplier_id' => $supplier_id, 'refund_type' => [0, 3]], ['*'], ['pink']);
if (!$orderInfo) {
throw new ValidateException('请在详情->发货记录:查看具体发货情况');
}
$id = (int)$orderInfo['id'];
}
/** @var StoreOrderRefundServices $storeOrderRefundServices */
$storeOrderRefundServices = app()->make(StoreOrderRefundServices::class);
if ($storeOrderRefundServices->count(['store_order_id' => $id, 'refund_type' => [1, 2, 4, 5, 6], 'is_cancel' => 0, 'is_del' => 0])) {
throw new ValidateException('订单有售后申请请先处理');
}
//取消预售订单 验证预售活动是否结束
// if ($orderInfo['type'] == 6) {
// /** @var StoreOrderCartInfoServices $storeOrderCartInfoServices */
// $storeOrderCartInfoServices = app()->make(StoreOrderCartInfoServices::class);
// $cartInfo = $storeOrderCartInfoServices->getOrderCartInfo($id);
// $time = time();
// foreach ($cartInfo as $cart) {
// if (isset($cart['cart_info']['productInfo']['presale_end_time']) && $cart['cart_info']['productInfo']['presale_end_time'] > $time) {
// throw new ValidateException('预售活动暂未结束,请稍后发货');
// }
// }
// }
if (isset($data['remark']) && $data['remark'] == '') {
unset($data['remark']);
}
$dump = $this->doDelivery($id, $orderInfo, $data);
if ($staff_id) {
$this->dao->update($id, ['staff_id' => $staff_id]);
}
return compact('orderInfo', 'dump');
}
/**
* 订单拆单发货
* @param int $id
* @param array $data
* @param int $staff_id
* @return bool
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public function splitDelivery(int $id, array $data, int $staff_id = 0)
{
$orderInfo = $this->dao->get($id, ['*'], ['pink']);
if (!$orderInfo) {
throw new ValidateException('订单未能查到,不能发货!');
}
if ($orderInfo['paid'] != 1) {
throw new ValidateException('订单未支付');
}
if ($orderInfo->is_del) {
throw new ValidateException('订单已删除,不能发货!');
}
if ($orderInfo->shipping_type == 2) {
throw new ValidateException('核销订单不能发货!');
}
if (isset($orderInfo['pinkStatus']) && $orderInfo['pinkStatus'] != 2) {
throw new ValidateException('拼团未完成暂不能发货!');
}
$store_id = $this->getItem('store_id', 0);
$supplier_id = $this->getItem('supplier_id', 0);
//拆分完整主订单查询未发货子订单
if ($orderInfo['pid'] == -1) {
$orderInfo = $this->dao->get(['pid' => $id, 'status' => 0, 'supplier_id' => $supplier_id, 'refund_type' => [0, 3]], ['*'], ['pink']);
if (!$orderInfo) {
throw new ValidateException('订单未能查到,不能发货!');
}
$id = (int)$orderInfo['id'];
}
/** @var StoreOrderRefundServices $storeOrderRefundServices */
$storeOrderRefundServices = app()->make(StoreOrderRefundServices::class);
if ($storeOrderRefundServices->count(['store_order_id' => $id, 'refund_type' => [1, 2, 4, 5, 6], 'is_cancel' => 0, 'is_del' => 0])) {
throw new ValidateException('订单有售后申请请先处理');
}
//取消预售订单 验证预售活动是否结束
// if ($orderInfo['type'] == 6) {
// /** @var StoreOrderCartInfoServices $storeOrderCartInfoServices */
// $storeOrderCartInfoServices = app()->make(StoreOrderCartInfoServices::class);
// $cartInfo = $storeOrderCartInfoServices->getOrderCartInfo($id);
// $time = time();
// foreach ($cartInfo as $cart) {
// if (isset($cart['cart_info']['productInfo']['presale_end_time']) && $cart['cart_info']['productInfo']['presale_end_time'] > $time) {
// throw new ValidateException('预售活动暂未结束,请稍后发货');
// }
// }
// }
$cart_ids = $data['cart_ids'];
/** @var StoreOrderCartInfoServices $storeOrderCartInfoServices */
$storeOrderCartInfoServices = app()->make(StoreOrderCartInfoServices::class);
//检测选择商品是否还可拆分
$storeOrderCartInfoServices->checkCartIdsIsSplit($id, $cart_ids);
unset($data['cart_ids']);
return $this->transaction(function () use ($id, $cart_ids, $orderInfo, $data, $storeOrderCartInfoServices, $staff_id) {
/** @var StoreOrderSplitServices $storeOrderSplitServices */
$storeOrderSplitServices = app()->make(StoreOrderSplitServices::class);
//拆单
$splitResult = $storeOrderSplitServices->equalSplit($id, $cart_ids, $orderInfo, 0, false, $data['erp_id'] ?? 0);
if ($splitResult) {//拆分发货
[$orderInfo, $otherOrder] = $splitResult;
//拆分订单执行发货
$this->doDelivery((int)$orderInfo->id, $orderInfo, $data);
//检测原订单商品是否 全部拆分发货完成 改原订单状态
$status_data = ['oid' => $id, 'change_time' => time()];
if (!$storeOrderCartInfoServices->getSplitCartList($id)) {//发货完成
$change_type = 'delivery_split';
$change_message = '已拆分发货';
} else {
$change_type = 'delivery_part_split';
$change_message = '已拆分部分发货';
}
//记录原订单状态
OrderStatusJob::dispatch([$id, $change_type, ['change_message' => $change_message, 'change_manager_type' => 'system']]);
} else {//整体发货
$this->delivery($id, $data, $staff_id);
}
return $splitResult;
});
// return true;
}
/**
* 具体执行发货
* @param int $id
* @param $orderInfo
* @param array $data
* @return bool
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public function doDelivery(int $id, $orderInfo, array $data)
{
$type = (int)$data['type'];
unset($data['type']);
//获取购物车内的商品标题
/** @var StoreOrderCartInfoServices $orderInfoServices */
$orderInfoServices = app()->make(StoreOrderCartInfoServices::class);
$storeName = $orderInfoServices->getCarIdByProductTitle((int)$orderInfo['id']);
if (isset($data['pickup_time']) && count($data['pickup_time']) == 2) {
$data['pickup_start_time'] = $data['pickup_time'][0];
$data['pickup_end_time'] = $data['pickup_time'][1];
} else {
$data['pickup_start_time'] = '';
$data['pickup_end_time'] = '';
}
//修改返回数据
$res = [];
switch ($type) {
case 1://快递发货
$res = $this->orderDeliverGoods($id, $data, $orderInfo, $storeName);
break;
case 2://配送
$this->orderDelivery($id, $data, $orderInfo);
break;
case 3://虚拟发货
$this->orderVirtualDelivery($id, $data);
break;
case 4://门店收银订单自动发货
$this->dao->update($orderInfo['id'], ['delivery_type' => 'cashier']);
OrderStatusJob::dispatch([$orderInfo['id'], 'delivery_cashier', ['change_message' => '门店收银台核销', 'change_manager_type' => 'system']]);
break;
default:
throw new ValidateException('暂时不支持其他发货类型');
}
$orderInfo = $this->dao->get($id);
event('order.delivery', [$orderInfo, $storeName, $data, $type]);
return $res;
}
/**
* 订单快递发货
* @param int $id
* @param array $data
*/
public function orderDeliverGoods(int $id, array $data, $orderInfo, $storeTitle)
{
if (!$data['delivery_name']) {
throw new ValidateException('请选择快递公司');
}
//修改
$dump = [];
$data['delivery_type'] = 'express';
/** @var StoreOrderCartInfoServices $orderInfoServices */
$orderInfoServices = app()->make(StoreOrderCartInfoServices::class);
if ($data['express_record_type'] == 2) {//电子面单
if (!sys_config('config_export_open', 0)) {
throw new ValidateException('系统通知:电子面单已关闭,请选择其他发货方式!');
}
if (!$data['delivery_code']) {
throw new ValidateException('快递公司编缺失');
}
if (!$data['express_temp_id']) {
throw new ValidateException('请选择电子面单模板');
}
if (!$data['to_name']) {
throw new ValidateException('请填写寄件人姓名');
}
if (!$data['to_tel']) {
throw new ValidateException('请填写寄件人电话');
}
if (!$data['to_addr']) {
throw new ValidateException('请填写寄件人地址');
}
$expData['com'] = $data['delivery_code'];
$expData['to_name'] = $orderInfo['real_name'];
$expData['to_tel'] = $orderInfo['user_phone'];
$expData['to_addr'] = $orderInfo['user_address'];
$expData['from_name'] = $data['to_name'];
$expData['from_tel'] = $data['to_tel'];
$expData['from_addr'] = $data['to_addr'];
$expData['siid'] = sys_config('config_export_siid');
$expData['temp_id'] = $data['express_temp_id'];
$expData['count'] = $orderInfo['total_num'];
$expData['weight'] = $this->getOrderSumWeight($id);
$expData['cargo'] = $orderInfoServices->getCarIdByProductTitle((int)$orderInfo['id'], true);
$expData['order_id'] = $orderInfo['order_id'];
$expData['customParam'] = json_encode([
'product_name' => $orderInfoServices->getCarIdByProductTitle((int)$orderInfo['id'], true)
]);
/** @var ServeServices $expressService */
$expressService = app()->make(ServeServices::class);
$dump = $expressService->express()->dump($expData);
$data['express_dump'] = json_encode([
'com' => $expData['com'],
'from_name' => $expData['from_name'],
'from_tel' => $expData['from_tel'],
'from_addr' => $expData['from_addr'],
'temp_id' => $expData['temp_id'],
'cargo' => $expData['cargo'],
]);
$data['delivery_id'] = $dump['kuaidinum'] ?? '';
$orderInfo->delivery_id = $dump['kuaidinum'] ?? '';
//修改快递面单图片写入数据库
if (!empty($dump['label'])) {
$data['kuaidi_label'] = $dump['label'];
}
} elseif ($data['express_record_type'] == 3) {
//商家寄件
if (!$data['delivery_code']) {
throw new ValidateException('快递公司编缺失');
}
if (!$data['express_temp_id']) {
throw new ValidateException('请选择电子面单模板');
}
if (!$data['to_name']) {
throw new ValidateException('请填写寄件人姓名');
}
if (!$data['to_tel']) {
throw new ValidateException('请填写寄件人电话');
}
if (!$data['to_addr']) {
throw new ValidateException('请填写寄件人地址');
}
/** @var ServeServices $expressService */
$expressService = app()->make(ServeServices::class);
$expData['kuaidicom'] = $data['delivery_code'];
$expData['man_name'] = $orderInfo->real_name;
$expData['phone'] = $orderInfo->user_phone;
$expData['address'] = $orderInfo->user_address;
$expData['send_real_name'] = $data['to_name'];
$expData['send_phone'] = $data['to_tel'];
$expData['send_address'] = $data['to_addr'];
$expData['temp_id'] = $data['express_temp_id'];
$expData['weight'] = $this->getOrderSumWeight($id);
$expData['cargo'] = $orderInfoServices->getCarIdByProductTitle((int)$orderInfo->id, true);
$expData['day_type'] = $data['day_type'];
$expData['pickup_start_time'] = $data['pickup_start_time'];
$expData['pickup_end_time'] = $data['pickup_end_time'];
// if (!sys_config('config_shippment_open', 0)) {
// throw new AdminException('商家寄件未开启无法寄件');
// }
$dump = $expressService->express()->shippmentCreateOrder($expData);
Log::error('商家寄件返回数据:' . json_encode($dump));
$orderInfo->delivery_id = $dump['kuaidinum'] ?? '';
$data['express_dump'] = json_encode([
'com' => $expData['kuaidicom'],
'from_name' => $expData['send_real_name'],
'from_tel' => $expData['send_phone'],
'from_addr' => $expData['send_address'],
'temp_id' => $expData['temp_id'],
'cargo' => $expData['cargo'],
]);
$data['delivery_id'] = $dump['kuaidinum'] ?? '';
$data['kuaidi_label'] = $dump['label'] ?? '';
$data['kuaidi_task_id'] = $dump['task_id'] ?? '';
$data['kuaidi_order_id'] = $dump['order_id'] ?? '';
} else {
if (!$data['delivery_id']) {
throw new ValidateException('请输入快递单号');
}
$orderInfo->delivery_id = $data['delivery_id'];
}
if ($data['express_record_type'] != 3) {
$data['status'] = 1;
$this->transaction(function () use ($id, $data) {
$res = $this->dao->update($id, $data);
if (!$res) {
throw new ValidateException('发货失败:数据保存不成功');
}
//记录主订单状态
OrderStatusJob::dispatch([$id, 'delivery_goods', ['change_message' => '已发货 快递公司:' . $data['delivery_name'] . ' 快递单号:' . $data['delivery_id'], 'change_manager_id' => request()->adminId(), 'change_manager_type' => 'admin']]);
});
} else {
$update = [
'is_stock_up' => 1,
'delivery_type' => $data['delivery_type'],
'delivery_name' => $data['delivery_name'],
'delivery_code' => $data['delivery_code'],
'delivery_id' => $data['delivery_id'],
'kuaidi_label' => $data['kuaidi_label'],
'kuaidi_task_id' => $data['kuaidi_task_id'],
'kuaidi_order_id' => $data['kuaidi_order_id'],
'express_dump' => $data['express_dump']
];
/** @var StoreOrderStatusServices $services */
$services = app()->make(StoreOrderStatusServices::class);
$this->transaction(function () use ($id, $data, $services, $update) {
$res = $this->dao->update($id, $update);
$res = $res && $services->save([
'oid' => $id,
'change_time' => time(),
'change_type' => 'stock_up_goods',
'change_message' => '备货中 快递公司:' . $data['delivery_name'] . ' 快递单号:' . $data['delivery_id']
]);
if (!$res) {
throw new AdminException('发货失败:数据保存不成功');
}
});
}
return $dump;
}
/**
* 订单配送
* @param int $id
* @param array $data
* @return bool
*/
public function orderDelivery(int $id, array $data, $orderInfo = [])
{
$data['delivery_name'] = $data['sh_delivery_name'];
$data['delivery_id'] = $data['sh_delivery_id'];
$data['delivery_uid'] = $data['sh_delivery_uid'];
$delivery_type = $data['delivery_type'] ?? 1;
$delivery_type = $delivery_type ? $delivery_type : 1;
$station_type = $data['station_type'] ?? 1;
unset($data['delivery_type'], $data['station_type']);
switch ($delivery_type) {
case 1://自己配送
if (!$data['delivery_name']) {
throw new ValidateException('请输入送货人姓名');
}
if (!$data['delivery_id']) {
throw new ValidateException('请输入送货人电话号码');
}
if (!$data['delivery_uid']) {
throw new ValidateException('请输入送货人信息');
}
if (!check_phone($data['delivery_id'])) {
throw new ValidateException('请输入正确的送货人电话号码');
}
//获取核销码
/** @var StoreOrderCreateServices $storeOrderCreateService */
$storeOrderCreateService = app()->make(StoreOrderCreateServices::class);
$data['verify_code'] = $storeOrderCreateService->getStoreCode();
$data['delivery_type'] = 'send';
break;
case 2://第三方配送
if (!isset($data['cargo_weight']) || !$data['cargo_weight']) {
throw new ValidateException('请填写配送商品重量');
}
$data['delivery_type'] = 'city_delivery';
/** @var StoreDeliveryOrderServices $storeDeliverOrderServices */
$storeDeliverOrderServices = app()->make(StoreDeliveryOrderServices::class);
$storeDeliverOrderServices->create($id, $data, $station_type, $orderInfo);
break;
}
$data['status'] = 1;
unset($data['sh_delivery_name'], $data['sh_delivery_id'], $data['sh_delivery_uid'], $data['delivery_remark']);
$this->transaction(function () use ($id, $data, $delivery_type, $station_type) {
//修改订单发货信息
$this->dao->update($id, $data);
if ($delivery_type == 1) {
$message = '已配送 发货人:' . $data['delivery_name'] . ' 发货人电话:' . $data['delivery_id'];
} else {
$message = '已' . ($station_type == 1 ? '达达' : 'UU跑腿') . '同城配送';
}
//记录主订单状态
OrderStatusJob::dispatch([$id, $delivery_type == 1 ? 'delivery' : 'city_delivery', ['change_message' => $message, 'change_manager_type' => 'system']]);
});
return true;
}
/**
* 虚拟发货
* @param int $id
* @param array $data
*/
public function orderVirtualDelivery(int $id, array $data)
{
$data['delivery_type'] = 'fictitious';
$data['status'] = 1;
unset($data['sh_delivery_name'], $data['sh_delivery_id'], $data['delivery_name'], $data['delivery_id'], $data['delivery_remark']);
//保存信息
return $this->transaction(function () use ($id, $data) {
$this->dao->update($id, $data);
//记录主订单状态
OrderStatusJob::dispatch([$id, 'delivery_fictitious', ['change_message' => '已虚拟发货', 'change_manager_id' => request()->adminId(), 'change_manager_type' => 'admin']]);
return true;
});
}
/**
* 发货发送短信
* @param $orderInfo
*/
public function deliverySmsSendAfter($orderInfo, string $store_name)
{
$order_id = $orderInfo->order_id;
$switch = (bool)sys_config('deliver_goods_switch');
$service = app()->make(UserServices::class);
$nickname = $service->value(['uid' => $orderInfo->uid], 'nickname');
/** @var SmsSendServices $smsServices */
$smsServices = app()->make(SmsSendServices::class);
$smsServices->send($switch, $orderInfo->user_phone, compact('order_id', 'store_name', 'nickname'), 'DELIVER_GOODS_CODE', '用户发货发送短信失败,订单号为:' . $order_id);
}
/**
* 获取修改配送信息表单结构
* @param int $id
* @return array
* @throws \FormBuilder\Exception\FormBuilderException
*/
public function distributionForm(int $id)
{
if (!$orderInfo = $this->dao->get($id))
throw new ValidateException('订单不存在');
$f[] = Form::input('order_id', '订单号', $orderInfo->getData('order_id'))->disabled(1);
switch ($orderInfo['delivery_type']) {
case 'send'://送货
$f[] = Form::input('delivery_name', '送货人姓名', $orderInfo->getData('delivery_name'))->required('请输入送货人姓名');
$f[] = Form::input('delivery_id', '送货人电话', $orderInfo->getData('delivery_id'))->required('请输入送货人电话');
break;
case 'express'://快递
/** @var ExpressServices $expressServices */
$expressServices = app()->make(ExpressServices::class);
$f[] = Form::select('delivery_name', '快递公司', (string)$orderInfo->getData('delivery_name'))->setOptions(array_map(function ($item) {
$item['value'] = $item['label'];
return $item;
}, $expressServices->expressSelectForm(['is_show' => 1])))->filterable(true)->required('请选择快递公司');
$f[] = Form::input('delivery_id', '快递单号', $orderInfo->getData('delivery_id'))->required('请填写快递单号');
break;
case 'fictitious'://虚拟发货
$f[] = Form::input('fictitious_content', '备注', $orderInfo->getData('fictitious_content'));
break;
}
return create_form('配送信息', $f, $this->url('/order/distribution/' . $id), 'PUT');
}
/**
* 修改配送信息
* @param int $id 订单id
* @return mixed
*/
public function updateDistribution(int $id, array $data)
{
$order = $this->dao->get($id);
if (!$order) {
throw new ValidateException('数据不存在!');
}
switch ($order['delivery_type']) {
case 'send':
if (!$data['delivery_name']) {
throw new ValidateException('请输入送货人姓名');
}
if (!$data['delivery_id']) {
throw new ValidateException('请输入送货人电话号码');
}
if (!check_phone($data['delivery_id'])) {
throw new ValidateException('请输入正确的送货人电话号码');
}
break;
case 'express':
if (!$data['delivery_name']) {
throw new ValidateException('请选择快递公司');
}
if (!$data['delivery_id']) {
throw new ValidateException('请输入快递单号');
}
break;
case 'fictitious':
break;
default:
throw new ValidateException('未发货,请先发货再修改配送信息');
break;
}
$this->dao->update($id, $data);
OrderStatusJob::dispatch([$id, 'distribution', ['change_message' => '修改发货信息为' . $data['delivery_name'] . '号' . $data['delivery_id'], 'change_manager_id' => request()->adminId(), 'change_manager_type' => 'admin']]);
return true;
}
/**
* 订单发货后打印电子面单
* @param $orderId
* @return bool|mixed
*/
public function orderDump($orderId)
{
if (!$orderId) throw new ValidateException('订单号缺失');
/** @var StoreOrderServices $orderService */
$orderService = app()->make(StoreOrderServices::class);
$orderInfo = $orderService->getOne(['id' => $orderId]);
if (!$orderInfo) throw new ValidateException('订单不存在');
if (in_array($orderInfo->shipping_type, [2, 4])) throw new ValidateException('订单无法打印');
if (!$orderInfo->express_dump) throw new ValidateException('请先发货');
if (!sys_config('config_export_open', 0)) {
throw new ValidateException('请先在系统设置中打开单子面单打印开关');
}
$dumpInfo = json_decode($orderInfo->express_dump, true);
/** @var ServeServices $expressService */
$expressService = app()->make(ServeServices::class);
$expData['com'] = $dumpInfo['com'];
$expData['to_name'] = $orderInfo->real_name;
$expData['to_tel'] = $orderInfo->user_phone;
$expData['to_addr'] = $orderInfo->user_address;
$expData['from_name'] = $dumpInfo['from_name'];
$expData['from_tel'] = $dumpInfo['from_tel'];
$expData['from_addr'] = $dumpInfo['from_addr'];
$expData['siid'] = sys_config('config_export_siid');
$expData['temp_id'] = $dumpInfo['temp_id'];
$expData['cargo'] = $dumpInfo['cargo'];
$expData['count'] = $orderInfo->total_num;
$expData['weight'] = $this->getOrderSumWeight((int)$orderId);
$expData['order_id'] = $orderInfo->order_id;
try {
$dump = $expressService->express()->dump($expData);
$data['express_dump'] = json_encode([
'com' => $expData['com'],
'from_name' => $expData['from_name'],
'from_tel' => $expData['from_tel'],
'from_addr' => $expData['from_addr'],
'temp_id' => $expData['temp_id'],
'cargo' => $expData['cargo'],
]);
$data['delivery_id'] = $dump['kuaidinum'] ?? '';
//修改快递面单图片写入数据库
if (!empty($dump['label'])) {
$data['kuaidi_label'] = $dump['label'];
}
$orderService->update($orderId, $data);
} catch (\Throwable $e) {
$dump = [];
}
return $dump;
}
/**
* 返回订单商品总重量
* @param int $id
* @return int|string
*/
public function getOrderSumWeight(int $id, $default = false)
{
/** @var StoreOrderCartInfoServices $services */
$services = app()->make(StoreOrderCartInfoServices::class);
$orderGoodInfo = $services->getOrderCartInfo((int)$id);
$weight = 0;
foreach ($orderGoodInfo as $cartInfo) {
$cart = $cartInfo['cart_info'] ?? [];
if ($cart) {
$weight = bcadd((string)$weight, (string)bcmul((string)$cart['cart_num'] ?? '0', (string)$cart['productInfo']['attrInfo']['weight'] ?? '0', 4), 2);
}
}
return $weight ? $weight : ($default === false ? 0 : $default);
}
}

View File

@@ -0,0 +1,82 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\order;
use app\dao\order\StoreOrderEconomizeDao;
use app\services\BaseServices;
use think\annotation\Inject;
use think\exception\ValidateException;
/**
* 计算节省金额
* Class StoreOrderEconomizeServices
* @package app\services\order
* @mixin StoreOrderEconomizeDao
*/
class StoreOrderEconomizeServices extends BaseServices
{
/**
* @var StoreOrderEconomizeDao
*/
#[Inject]
protected StoreOrderEconomizeDao $dao;
/**
* 添加节省金额数据
* @param array $add
* @return \crmeb\basic\BaseModel|\think\Model
*/
public function addEconomize(array $add)
{
if (!$add) throw new ValidateException('数据不存在');
return $this->dao->save($add);
}
/**
* @param array $where
* @return array|\think\Model|null
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getOne(array $where)
{
if (!$where) throw new ValidateException('条件缺失');
return $this->dao->getOne($where);
}
/**
* 汇总付费会员节省金额
* @param $uid
* @return float
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function sumEconomizeMoney($uid)
{
$economizeMoney = 0.00;
if (!$uid) return $economizeMoney;
$list = $this->dao->getList(['uid' => $uid]);
if ($list) {
foreach ($list as $k => $v) {
$economizeMoney += $v['postage_price'];
$economizeMoney += $v['member_price'];
$economizeMoney += $v['offline_price'];
$economizeMoney += $v['coupon_price'];
}
}
return (float)sprintf("%.2f", $economizeMoney);
}
}

View File

@@ -0,0 +1,244 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\services\order;
use app\services\BaseServices;
use think\annotation\Inject;
use think\exception\ValidateException;
use app\dao\order\StoreOrderInvoiceDao;
use app\services\user\UserInvoiceServices;
/**
* Class StoreOrderInvoiceServices
* @package app\services\order
* @mixin StoreOrderInvoiceDao
*/
class StoreOrderInvoiceServices extends BaseServices
{
/**
* @var StoreOrderInvoiceDao
*/
#[Inject]
protected StoreOrderInvoiceDao $dao;
/**
* @author 等风来
* @email 136327134@qq.com
* @date 2023/9/18
* @param array $where
* @return array
*/
public function chart(array $where)
{
//全部
$data['all'] = (string)$this->dao->count(['is_pay' => 1, 'is_del' => 0, 'time' => $where['time']]);
//待开
$data['noOpened'] = (string)$this->dao->count(['is_pay' => 1, 'is_del' => 0, 'time' => $where['time'], 'type' => 1]);
//已开
$data['opened'] = (string)$this->dao->count(['is_pay' => 1, 'is_del' => 0, 'time' => $where['time'], 'type' => 2]);
//退款
$data['refund'] = (string)$this->dao->count(['is_pay' => 1, 'is_del' => 0, 'time' => $where['time'], 'type' => 3]);
return $data;
}
/**
* 后台获取开票列表
* @param $where
* @return array
*/
public function getList(array $where)
{
[$page, $list] = $this->getPageValue();
$field = 'id as invoice_id,order_id,header_type,type,name,invoice_amount,duty_number,drawer_phone,email,tell,address,bank,card_number,is_invoice,invoice_number,remark as invoice_reamrk,invoice_time,add_time as invoice_add_time';
$where['is_pay'] = 1;
$where['is_del'] = 0;
$list = $this->dao->getList($where, $field, ['order' => function ($query) {
$query->field('id,order_id,pay_price,add_time,real_name,user_phone,status,refund_status');
}], 'add_time desc', $page, $list);
foreach ($list as &$item) {
$item['id'] = $item['order']['id'] ?? 0;
$item['order_id'] = $item['order']['order_id'] ?? '';
$item['pay_price'] = $item['order']['pay_price'] ?? 0.00;
$item['real_name'] = $item['order']['real_name'] ?? '';
$item['user_phone'] = $item['order']['user_phone'] ?? '';
$item['status'] = $item['order']['status'] ?? '';
$item['refund_status'] = $item['order']['refund_status'] ?? 0;
$item['add_time'] = date('Y-m-d H:i:s', $item['order']['add_time'] ?? $item['invoice_add_time'] ?? time());
$item['invoice_add_time'] = date('Y-m-d H:i:s', $item['invoice_add_time']);
}
$count = $this->dao->count($where);
return compact('list', 'count');
}
/**
* 前端获取开票列表(带商品信息)
* @param $where
* @return array
*/
public function getOrderInvoiceList(array $where)
{
[$page, $list] = $this->getPageValue();
$where['is_pay'] = 1;
$where['is_del'] = 0;
$where['is_refund'] = 0;
$list = $this->dao->getList($where, '*', ['order'], 'add_time desc', $page, $list);
/** @var StoreOrderServices $storeOrderServices */
$storeOrderServices = app()->make(StoreOrderServices::class);
foreach ($list as &$item) {
if (isset($item['order']) && $item['order']) {
$item['order'] = $storeOrderServices->tidyOrder($item['order'], true);
if (isset($item['order']['_status']['_type']) && $item['order']['_status']['_type'] == 3) {
foreach ($item['order']['cartInfo'] ?: [] as $key => $product) {
$item['order']['cartInfo'][$key]['add_time'] = isset($product['add_time']) ? date('Y-m-d H:i', (int)$product['add_time']) : '时间错误';
}
}
}
}
return $list;
}
/**
* 订单申请开票
* @param int $uid
* @param $order_id
* @param int $invoice_id
* @return mixed
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function makeUp(int $uid, $order_id, int $invoice_id)
{
/** @var StoreOrderServices $storeOrderServices */
$storeOrderServices = app()->make(StoreOrderServices::class);
/** @var UserInvoiceServices $userInvoiceServices */
$userInvoiceServices = app()->make(UserInvoiceServices::class);
$order = $storeOrderServices->getOne(['order_id|id' => $order_id, 'is_del' => 0]);
if (!$order) {
throw new ValidateException('订单不存在');
}
if ($order['pay_price'] <= 0) {
throw new ValidateException('订单实际支付金额为0,不能开发票');
}
//检测再带查询
$invoice = $userInvoiceServices->checkInvoice($invoice_id, $uid);
$info= $this->dao->getOne(['order_id' => $order['id'], 'uid' => $uid]);
if ($info && $info['is_invoice'] != -1) {
throw new ValidateException('发票已申请,正在审核打印中');
}
if ($order['refund_status'] == 2) {
throw new ValidateException('订单已退款');
}
if ($order['refund_status'] == 1) {
throw new ValidateException('正在申请退款中');
}
unset($invoice['id'], $invoice['add_time']);
$data = [];
$data['category'] = 'order';
$data['order_id'] = $order['id'];
$data['invoice_id'] = $invoice_id;
$data['add_time'] = time();
$data['is_pay'] = $order['paid'] == 1 ? 1 : 0;
$data = array_merge($data, $invoice);
if (!$re = $this->dao->save($data)) {
throw new ValidateException('申请失败,请稍后重试');
}
return ['id' => $re->id];
}
/**
* 设置处理发票
* @param int $id
* @param array $data
* @return bool
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function setInvoice(int $id, array $data)
{
$orderInvoice = $this->dao->get($id);
if (!$orderInvoice) {
throw new ValidateException('数据不存在');
}
$data['invoice_time'] = time();
if (!$this->dao->update($id, $data, 'id')) {
throw new ValidateException('设置失败,请重试');
}
return true;
}
/**
* 根据订单id获取发票信息
* @param int $oid
* @return array|\think\Model|null
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getOneInvoice(int $oid)
{
$info = $this->dao->get(['order_id' => $oid]);
$info = $info ? $info->toArray() : [];
if (!$info) {
throw new ValidateException('数据不存在');
}
return $info;
}
/**
* 拆分订单同步拆分申请开票记录
* @param int $oid
* @return bool
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function splitOrderInvoice(int $oid)
{
/** @var StoreOrderServices $storeOrderServices */
$storeOrderServices = app()->make(StoreOrderServices::class);
$orderInfo = $storeOrderServices->getOne(['id' => $oid, 'is_del' => 0]);
if (!$orderInfo) {
throw new ValidateException('订单不存在');
}
$pid = $orderInfo['pid'] > 0 ? $orderInfo['pid'] : $orderInfo['id'];
//查询开票记录
$orderInvoice = $this->dao->get(['order_id' => $oid]);
//查询子订单
$spliteOrder = $storeOrderServices->getColumn(['pid' => $pid, 'is_system_del' => 0], 'id,order_id');
if ($spliteOrder && $orderInvoice) {
$data = $orderInvoice->toArray();
unset($data['id']);
$data['add_time'] = strtotime($data['add_time']);
$data_all = [];
foreach ($spliteOrder as $order) {
if (!$this->dao->count(['order_id' => $order['id']])) {
$data['order_id'] = $order['id'];
$data_all[] = $data;
}
}
if ($data_all) {
$this->transaction(function () use ($data_all, $orderInvoice, $orderInfo) {
$this->dao->saveAll($data_all);
if ($orderInfo['pid'] <= 0) {
$this->dao->delete(['id' => $orderInvoice['id']]);
}
});
}
}
return true;
}
}

View File

@@ -0,0 +1,194 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\order;
use app\dao\order\StoreOrderPromotionsDao;
use app\services\BaseServices;
use think\annotation\Inject;
use think\exception\ValidateException;
/**
* Class StoreOrderPromotionsServices
* @package app\services\order
* @mixin StoreOrderPromotionsDao
*/
class StoreOrderPromotionsServices extends BaseServices
{
/**
* @var StoreOrderPromotionsDao
*/
#[Inject]
protected StoreOrderPromotionsDao $dao;
/**
* 保存订单优惠详情
* @param int $uid
* @param int $oid
* @param array $cartList
* @param array $promotionsList
* @return int
*/
public function setPromotionsDetail(int $uid, int $oid, array $cartList, array $promotionsList)
{
$group = [];
if ($promotionsList) {
$time = time();
foreach ($cartList as $key => $cart) {
foreach ($promotionsList as $promotions) {
$details = $promotions['details'] ?? [];
$unique = $cart['product_attr_unique'] ?? '';
if ($details && isset($details[$unique]['sum_promotions_price'])) {
$group[] = [
'oid' => $oid,
'uid' => $uid,
'product_id' => $cart['productInfo']['id'],
'promotions_id' => $promotions['id'],
'promotions_price' => (float)$details[$unique]['sum_promotions_price'] ?? 0,
'add_time' => $time
];
}
}
}
}
if ($group) {
return $this->dao->saveAll($group);
}
return true;
}
/**
* 获取订单商品实际参与优惠活动 以及优惠金额
* @param int $oid
* @return array
*/
public function getOrderPromotionsDetail(int $oid)
{
$result = $this->dao->getPromotionsDetailList(['oid' => $oid], '*,sum(`promotions_price`) as promotions_price', ['promotions'], 'promotions_id');
if ($result) {
$typeArr = array_column($result, 'promotions_type');
array_multisort($typeArr, SORT_ASC, $result);
}
return $result;
}
/**
* 拆分订单同步拆分优惠活动记录
* @param int $oid
* @return bool
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function splitOrderPromotions(int $oid)
{
/** @var StoreOrderServices $storeOrderServices */
$storeOrderServices = app()->make(StoreOrderServices::class);
$orderInfo = $storeOrderServices->getOne(['id' => $oid, 'is_del' => 0]);
if (!$orderInfo) {
throw new ValidateException('订单不存在');
}
$promotions_give = [];
$promotions = [];
if (isset($orderInfo['promotions_give']) && $orderInfo['promotions_give']) {
$promotions_give = is_string($orderInfo['promotions_give']) ? json_decode($promotions_give, true) : $orderInfo['promotions_give'];
}
$promotions = $promotions_give['promotions'] ?? [];
$pid = $orderInfo['pid'] > 0 ? $orderInfo['pid'] : $orderInfo['id'];
//查询优惠记录
$orderPromotions = $this->dao->getPromotionsDetailList(['order_id' => $oid]);
//查询子订单
$spliteOrder = $storeOrderServices->getColumn(['pid' => $pid, 'is_system_del' => 0], 'id,order_id');
if ($spliteOrder && $orderPromotions && $promotions) {
/** @var StoreOrderCartInfoServices $storeOrderCartInfoServices */
$storeOrderCartInfoServices = app()->make(StoreOrderCartInfoServices::class);
$data_all = $data = [];
$data['uid'] = $orderInfo['uid'];
$data['add_time'] = time();
foreach ($spliteOrder as $order) {
$cartInfo = $storeOrderCartInfoServices->getColumn(['oid' => $order['id']], 'id,product_id,cart_num,cart_info');
if (!$cartInfo) {
continue;
}
$data['oid'] = $order['id'];
foreach ($cartInfo as $key => $cart) {
$data['product_id'] = $cart['product_id'];
$_info = is_string($cart['cart_info']) ? json_decode($cart['cart_info'], true) : $cart['cart_info'];
$unique = $_info['product_attr_unique'] ?? '';
foreach ($promotions as $key => $info) {
if (isset($info['details'][$unique])) {
$data['promotions_id'] = $info['id'];
$data['promotions_price'] = floatval($info['details'][$unique]['sum_promotions_price'] ?? 0);
$data_all[] = $data;
}
}
}
}
if ($data_all) {
$this->transaction(function () use ($data_all, $spliteOrder) {
$this->dao->delete(['oid' => array_column($spliteOrder, 'id')]);
$this->dao->saveAll($data_all);
});
}
}
return true;
}
/**
* 申请退款订单优惠详情处理
* @param int $oid
* @param array $cartInfo
* @return bool
*/
public function applyRefundOrderPromotions(int $oid, array $cartInfo = [])
{
/** @var StoreOrderServices $storeOrderServices */
$storeOrderServices = app()->make(StoreOrderServices::class);
$orderInfo = $storeOrderServices->getOne(['id' => $oid, 'is_del' => 0]);
if (!$orderInfo) {
return [];
}
$promotions_give = [];
$promotions = [];
if (isset($orderInfo['promotions_give']) && $orderInfo['promotions_give']) {
$promotions_give = is_string($orderInfo['promotions_give']) ? json_decode($promotions_give, true) : $orderInfo['promotions_give'];
}
$promotions = $promotions_give['promotions'] ?? [];
if (!$cartInfo || !$promotions) {
return [];
}
$data_all = [];
foreach ($promotions as $key => $info) {
$data = [];
foreach ($cartInfo as $key => $cart) {
if (isset($info['details'][$cart['product_id']])) {
$data['promotions_id'] = $info['id'];
$data['promotions_type'] = $info['promotions_type'];
$data['name'] = $info['name'];
$data['title'] = $info['title'];
$data['desc'] = $info['desc'];
$data['promotions_price'] = bcadd((string)($data['promotions_price'] ?? 0), (string)($info['details'][$cart['product_id']]['sum_promotions_price'] ?? 0), 2);
}
}
if ($data) {
$data_all[] = $data;
}
}
if ($data_all) {
$typeArr = array_column($data_all, 'promotions_type');
array_multisort($typeArr, SORT_ASC, $data_all);
}
return $data_all;
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,556 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\order;
use app\jobs\order\OrderStatusJob;
use app\jobs\order\SpliteOrderAfterJob;
use app\services\BaseServices;
use app\dao\order\StoreOrderDao;
use app\services\store\SystemStoreServices;
use think\annotation\Inject;
use think\exception\ValidateException;
/**
* 订单拆分
* Class StoreOrderSplitServices
* @package app\services\order
* @mixin StoreOrderDao
*/
class StoreOrderSplitServices extends BaseServices
{
/**
* 需要清空恢复默认数据字段
* @var string[]
*/
protected array $order_data = ['id', 'refund_status', 'refund_type', 'refund_express', 'refund_reason_wap_img', 'refund_reason_wap_explain', 'refund_reason_time', 'refund_reason_wap', 'refund_reason', 'refund_price'];
/**
* @var StoreOrderDao
*/
#[Inject]
protected StoreOrderDao $dao;
/**
* 检测要拆分数组
* @param int $id
* @param array $cart_ids
* @param array $orderInfo
* @return bool
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function checkCartNum(int $id, array $cart_ids, $orderInfo = [])
{
if (!$cart_ids) {
return false;
}
if (!$orderInfo) {
$orderInfo = $this->dao->get($id, ['*']);
}
$total_num = array_sum(array_column($cart_ids, 'cart_num'));
//商品和原订单一致,不拆分
if ($total_num >= $orderInfo['total_num']) {
return false;
}
return true;
}
/**
* 平行拆分订单
* @param int $id
* @param array $cart_ids
* @param array $orderInfo
* @param int $store_id
* @param bool $is_refund
* @param int $erp_id
* @return false
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function equalSplit(int $id, array $cart_ids, $orderInfo = [], int $store_id = 0, bool $is_refund = false, int $erp_id = 0)
{
if (!$cart_ids) {
return false;
}
if (!$orderInfo) {
$orderInfo = $this->dao->get($id, ['*']);
}
if (!$orderInfo) {
throw new ValidateException('订单未能查到,不能拆分订单!');
}
if ($orderInfo['pid'] == -1) {
throw new ValidateException('已拆分完毕,请返回列表重新操作');
}
if (!$this->checkCartNum($id, $cart_ids, $orderInfo)) {
return false;
}
$orderInfo = is_object($orderInfo) ? $orderInfo->toArray() : $orderInfo;
/** @var StoreOrderCartInfoServices $storeOrderCartInfoServices */
$storeOrderCartInfoServices = app()->make(StoreOrderCartInfoServices::class);
//订单下原商品信息
$cartInfo = $storeOrderCartInfoServices->getCartColunm(['oid' => $id, 'split_status' => [0, 1]], 'is_gift,cart_num,split_surplus_num', 'cart_id');
$new_cart_ids = array_combine(array_column($cart_ids, 'cart_id'), $cart_ids);
$other_cart_ids = $other = [];
foreach ($cartInfo as $cart_id => $cart) {
if (!isset($new_cart_ids[$cart_id]) && $cart['split_surplus_num']) {//无拆分
$other = ['cart_id' => $cart_id, 'cart_num' => $cart['split_surplus_num']];
} else if ($new_cart_ids[$cart_id]['cart_num'] < $cart['split_surplus_num']) {
$other = ['cart_id' => $cart_id, 'cart_num' => bcsub((string)$cart['split_surplus_num'], (string)$new_cart_ids[$cart_id]['cart_num'], 0)];
} else {
continue;
}
$other_cart_ids[] = $other;
}
$data = [$cart_ids, $other_cart_ids];
$split_cart_ids = $cart_ids;
if ($other_cart_ids && $orderInfo['status'] == 0 && $is_refund) {
//拆分剩余 都是赠品
$is_gift = true;
foreach ($other_cart_ids as $cart) {
if (!isset($cartInfo[$cart['cart_id']]) || !$cartInfo[$cart['cart_id']]['is_gift']) {
$is_gift = false;
break;
}
}
if ($is_gift) {//未发货 申请退款 整单退
return [];
}
}
/** @var StoreOrderServices $storeOrderServices */
$storeOrderServices = app()->make(StoreOrderServices::class);
$orderInfo = $storeOrderServices->tidyOrder($orderInfo, true, true);
//核算优惠金额
$vipTruePrice = 0;
$refund_num = 0;
$all_true_price = 0;
foreach ($orderInfo['cartInfo'] ?? [] as $key => &$cart) {
$vipTruePrice = bcadd((string)$vipTruePrice, (string)$cart['vip_sum_truePrice'], 2);
$refund_num = bcadd((string)$refund_num, (string)$cart['refund_num'], 0);
$all_true_price = bcadd((string)$all_true_price, bcmul((string)($cart['truePrice'] ?? 0), (string)$cart['cart_num'], 4), 2);
}
$orderInfo['vip_true_price'] = $vipTruePrice;
$orderInfo['total_price'] = floatval(bcsub((string)$orderInfo['total_price'], (string)$vipTruePrice, 2));
//订单实际支付金额
$order_pay_price = bcadd((string)$all_true_price, (string)$orderInfo['pay_postage'], 2);
//有改价
$change_price = $order_pay_price != $orderInfo['pay_price'];
$order = $this->transaction(function () use ($id, $data, $orderInfo, $store_id, $storeOrderCartInfoServices, $split_cart_ids, $is_refund, $change_price, $order_pay_price, $erp_id) {
$order = [];
$i = 0;
$yue_price = $orderInfo['yue_price'];
$online_price = $orderInfo['online_price'];
foreach ($data as $key => $cart_ids) {
if ($key == 0 && $erp_id > 0) {
$orderInfo['erpId'] = $erp_id;
}
//生成新订单、处理cart_info
[$new_id, $cart_data] = $this->splitV2($id, $cart_ids, $orderInfo, $i, $store_id, $is_refund, $split_cart_ids);
//处理订单
[$online_price, $yue_price] = $this->splitComputeOrder((int)$new_id, $cart_data, $orderInfo, (float)($change_price ? $order_pay_price : 0), (float)$orderInfo['pay_price'], (float)($order[0]['pay_price'] ?? 0), $online_price, $yue_price);
$order[] = $this->dao->get($new_id);
$i++;
}
//标记主订单拆分完成
if (!$orderInfo['pid']) $this->dao->update($id, ['pid' => -1]);
//处理申请开票记录
/** @var StoreOrderInvoiceServices $storeOrderInvoiceServics */
$storeOrderInvoiceServics = app()->make(StoreOrderInvoiceServices::class);
$storeOrderInvoiceServics->splitOrderInvoice((int)$id);
//处理订单优惠活动记录
/** @var StoreOrderPromotionsServices $storeOrderPromotionsServics */
$storeOrderPromotionsServics = app()->make(StoreOrderPromotionsServices::class);
$storeOrderPromotionsServics->splitOrderPromotions((int)$id);
return $order;
});
//拆分完成后置队列 处理订单状态记录
SpliteOrderAfterJob::dispatch([$id, $order]);
return $order;
}
/**
* 订单拆分
* @param int $id
* @param array $cart_ids
* @param array $orderInfo
* @param int $i
* @param int $store_id
* @param bool $is_refund
* @param array $append
* @return array|false
* @throws \Psr\SimpleCache\InvalidArgumentException
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function splitV2(int $id, array $cart_ids, $orderInfo = [], int $i = 0, int $store_id = 0, bool $is_refund = false, array $append = [])
{
$ids = array_unique(array_column($cart_ids, 'cart_id'));
if (!$cart_ids || !$ids) {
return false;
}
if (!$orderInfo) {
$orderInfo = $this->dao->get($id, ['*']);
}
if (!$orderInfo) {
throw new ValidateException('订单未能查到,不能拆分订单!');
}
/** @var StoreOrderCartInfoServices $storeOrderCartInfoServices */
$storeOrderCartInfoServices = app()->make(StoreOrderCartInfoServices::class);
//订单下原商品信息
$cartInfo = $storeOrderCartInfoServices->getCartColunm(['oid' => $id, 'cart_id' => $ids], '*', 'cart_id');
if (!$cartInfo || count($cartInfo) != count($ids)) {
return false;
}
if ($append) $append = array_combine(array_column($append, 'cart_id'), $append);
$is_not_split = $i && $orderInfo['pid'];
if ($is_not_split) {//保留原订单 不重新生成(只修改订单信息、订单商品信息)
$new_id = $id;
//删除原订单下商品信息
$storeOrderCartInfoServices->delete(['oid' => $id]);
//删除缓存
$storeOrderCartInfoServices->clearOrderCartInfo($id);
} else {
/** @var StoreOrderCreateServices $storeOrderCreateServices */
$storeOrderCreateServices = app()->make(StoreOrderCreateServices::class);
$orderInfo = is_object($orderInfo) ? $orderInfo->toArray() : $orderInfo;
$pid = $orderInfo['pid'] ?: $orderInfo['id'];
foreach ($this->order_data as $field) {
unset($orderInfo[$field]);
}
//核销码重新生成
if ($orderInfo['verify_code']) {
$orderInfo['verify_code'] = $storeOrderCreateServices->getStoreCode();
}
$order_data = $orderInfo;
$order_data['promotions_give'] = is_array($order_data['promotions_give']) ? json_encode($order_data['promotions_give']) : $order_data['promotions_give'];
$order_data['give_coupon'] = is_array($order_data['give_coupon']) ? implode(',', $order_data['give_coupon']) : $order_data['give_coupon'];
//$pid 平行拆分 0:子订单拆分
$order_data['pid'] = $pid ?: $id;
$parentOrder = $this->dao->get($order_data['pid']);
$oldOrderId = $parentOrder['order_id'];
$childCont = $this->dao->count(['pid' => $parentOrder['id']]);
if ($store_id) {//门店分配拆分
/** @var SystemStoreServices $storeServices */
$storeServices = app()->make(SystemStoreServices::class);
if (!$storeInfo = $storeServices->getStoreInfo($store_id)) {
throw new ValidateException('门店不存在');
}
$order_data['store_id'] = $store_id;
$order_data['order_id'] = $storeOrderCreateServices->getNewOrderId();
if ($order_data['shipping_type'] == 1) {
$order_data['shipping_type'] = 3;
} else {//自提订单
$order_data['order_id'] = $storeOrderCreateServices->getNewOrderId();
// $order_data['verify_code'] = $storeOrderCreateServices->getStoreCode();
}
$change_type = 'store_split_create_order';
$change_message = '门店分配拆分生成订单';
} else {//发货拆分
// $order_data['order_id'] = $storeOrderCreateServices->getNewOrderId();
$order_data['order_id'] = $oldOrderId . '_' . ($childCont + 1);
$change_type = 'split_create_order';
$change_message = $is_refund ? '售后退款拆分生成订单' : '发货拆分生成订单';
}
$order_data['cart_id'] = [];
$order_data['unique'] = $storeOrderCreateServices->getNewOrderId('');
$new_order = $this->dao->save($order_data);
if (!$new_order) {
throw new ValidateException('生成新订单失败');
}
$new_id = (int)$new_order->id;
OrderStatusJob::dispatch([$new_id, $change_type, ['change_message' => $change_message, 'change_manager_type' => 'system']]);
}
//生成订单商品信息 保存
$cart_data = $cart_data_all = $update_data = [];
foreach ($cart_ids as $cart) {
$split_surplus_num = $cartInfo[$cart['cart_id']]['split_surplus_num'] ?? 0;
if (!isset($cartInfo[$cart['cart_id']]) || (!$is_not_split && !$split_surplus_num)) continue;
$_info = is_string($cartInfo[$cart['cart_id']]['cart_info']) ? json_decode($cartInfo[$cart['cart_id']]['cart_info'], true) : $cartInfo[$cart['cart_id']]['cart_info'];
$cart_data = $cartInfo[$cart['cart_id']];
$cart_data['oid'] = $new_id;
unset($cart_data['id']);
$cart_data['split_surplus_num'] = $cart['cart_num'];
$unit_write_times = max($_info['productInfo']['attrInfo']['write_times'] ?? 1, 1);
$split_refund_num = $append[$cart['cart_id']]['cart_num'] ?? 0;
if (!$is_not_split) {//新增cart_info
$cart_data['cart_id'] = $storeOrderCreateServices->getNewOrderId('');
$cart_data['product_id'] = $_info['product_id'];
$cart_data['old_cart_id'] = $cart['cart_id'];
$cart_data['unique'] = md5($cart_data['cart_id'] . '_' . $cart_data['oid']);
// $cart_data['surplus_num'] = $cart['cart_num'];
} else {//保留
}
$refund_num = 0;
$cart_num_times = bcmul((string)$cart['cart_num'], (string)$unit_write_times, 0);
//拆出订单
if (!$i) {
//无核销 || 核销数量大于拆分数量
$surplus_num = min($cart_data['write_surplus_times'], $cart_num_times);
if ($is_refund) $refund_num = $cart['cart_num'];
} else {//修改原订单
//无核销 || 核销数量大于拆分数量
if (!$cart_data['writeoff_time']) {
$surplus_num = $cart_num_times;
} else {
$writoff_numm = bcsub((string)$cart_data['write_times'], (string)$cart_data['write_surplus_times'], 0);
$surplus_num = $writoff_numm >= $cart_num_times ? 0 : bcsub((string)$cart_num_times, (string)$writoff_numm, 0);
}
if ($is_refund) $refund_num = $split_refund_num >= $cart_data['refund_num'] ? 0 : bcsub((string)$cart_data['refund_num'], (string)$split_refund_num, 0);
}
$cart_data['cart_num'] = $cart['cart_num'];
$cart_data['write_times'] = bcmul((string)$cart['cart_num'], (string)$unit_write_times, 0);
$cart_data['refund_num'] = max($refund_num, 0);
$cart_data['write_surplus_times'] = max($surplus_num, 0);
if (!$cart_data['write_surplus_times']) {//核销完毕
$cart_data['is_writeoff'] = 1;
} elseif ($cart_data['write_surplus_times'] == $cart_data['write_times']) {//未核销
$cart_data['is_writeoff'] = 0;
$cart_data['writeoff_time'] = 0;
}
if ($cart['cart_num'] >= $split_surplus_num) {//拆分完成
$cart_data['cart_num'] = $split_surplus_num;
$update_data['split_status'] = 2;
$update_data['split_surplus_num'] = 0;
} else {//拆分部分数量
$update_data['split_surplus_num'] = bcsub((string)$split_surplus_num, $cart['cart_num'], 0);
$update_data['split_status'] = $update_data['split_surplus_num'] > 0 ? 1 : 2;
}
$_info = $this->slpitComputeOrderCart($cart_data['cart_num'], $_info, $i, !!($cart_data['is_gift'] ?? 0));
$_info['id'] = $cart_data['cart_id'];
$cart_data['cart_info'] = json_encode($_info);
//修改原来订单商品信息
if (!$is_not_split && false === $storeOrderCartInfoServices->update(['oid' => $id, 'cart_id' => $cart['cart_id']], $update_data)) {
throw new ValidateException('修改原来订单商品拆分状态失败,请稍候重试');
}
$cart_data_all[] = $cart_data;
unset($cartInfo[$cart['cart_id']]);
}
if (!$storeOrderCartInfoServices->saveAll($cart_data_all)) {
throw new ValidateException('新增拆分订单商品信息失败');
}
return [$new_id, $cart_data_all];
// $split_order_info = $this->dao->get($new_id);
// $this->splitComputeOrder($new_id, $cart_data_all, $split_order_info);
// return $split_order_info;
}
/**
* 获取整理后的订单商品信息
* @param int $id
* @param array $cart_ids
* @param array $orderInfo
* @return array|false
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getSplitOrderCartInfo(int $id, array $cart_ids, $orderInfo = [])
{
$ids = array_unique(array_column($cart_ids, 'cart_id'));
if (!$cart_ids || !$ids) {
return false;
}
if (!$orderInfo) {
$orderInfo = $this->dao->get($id, ['*']);
}
if (!$orderInfo) {
throw new ValidateException('订单未能查到,不能拆分订单!');
}
/** @var StoreOrderCartInfoServices $storeOrderCartInfoServices */
$storeOrderCartInfoServices = app()->make(StoreOrderCartInfoServices::class);
$cartInfo = $storeOrderCartInfoServices->getCartColunm(['oid' => $id, 'cart_id' => $ids], '*', 'cart_id');
$cart_data_all = [];
foreach ($cart_ids as $cart) {
$split_surplus_num = $cartInfo[$cart['cart_id']]['split_surplus_num'] ?? 0;
if (!isset($cartInfo[$cart['cart_id']]) || !$split_surplus_num) continue;
$_info = is_string($cartInfo[$cart['cart_id']]['cart_info']) ? json_decode($cartInfo[$cart['cart_id']]['cart_info'], true) : $cartInfo[$cart['cart_id']]['cart_info'];
$cart_data = $cartInfo[$cart['cart_id']];
$cart_data['oid'] = $id;
$cart_data['product_id'] = $_info['product_id'];
$cart_data['old_cart_id'] = $cart['cart_id'];
$cart_data['cart_num'] = $cart['cart_num'];
$cart_data['surplus_num'] = $cart['cart_num'];
$cart_data['split_surplus_num'] = $cart['cart_num'];
$_info = $this->slpitComputeOrderCart($cart_data['cart_num'], $_info);
$_info['id'] = $cart_data['cart_id'];
$cart_data['cart_info'] = $_info;
$cart_data_all[] = $cart_data;
unset($cartInfo[$cart['cart_id']]);
}
return $cart_data_all;
}
/**
* 重新计算新订单中价格等信息
* @param int $id 订单号
* @param array $cart_info_data 重新计算过订单商品信息
* @param array $orderInfo 订单信息
* @param float $order_pay_price 订单原来支付金额 0:没改价继续计算 >0 存在改价 需要计算新订单实际支付金额
* @param float $pay_price 订单实际支付金额
* @param float $pre_pay_price 上一个订单实际支付金额
* @return bool|float[]
*/
public function splitComputeOrder(int $id, array $cart_info_data, $orderInfo = [], float $order_pay_price = 0.00, float $pay_price = 0.00, float $pre_pay_price = 0.00, float $online_price = 0.00, float $yue_price = 0.00)
{
$order_update['cart_id'] = array_column($cart_info_data, 'cart_id');
$order_update['total_num'] = array_sum(array_column($cart_info_data, 'cart_num'));
$cost_price = $total_price = $true_total_price = $true_total_integral = $coupon_price = $deduction_price = $use_integral = $pay_postage = $gainIntegral = $one_brokerage = $two_brokerage = $promotions_price = $first_order_price = 0;
$unwriteoff = $wrrteoffed = 0;
foreach ($cart_info_data as $cart) {
$_info = json_decode($cart['cart_info'], true);
if (isset($cart['is_writeoff']) && $cart['is_writeoff']) {
$wrrteoffed++;
}
//赠品跳过
if (isset($cart['is_gift']) && $cart['is_gift']) {
continue;
}
$true_total_price = bcadd((string)$true_total_price, (string)($_info['sum_true_price'] ?? '0'), 2);
$true_total_integral = bcadd((string)$true_total_integral, (string)bcmul((string)($cart['cart_num'] ?? '1'), (string)($_info['integral'] ?? '0'), 0), 0);
$total_price = bcadd((string)$total_price, (string)bcmul((string)($cart['cart_num'] ?? '1'), (string)($_info['sum_price'] ?? '0'), 2), 2);
$cost_price = bcadd((string)$cost_price, (string)bcmul((string)($cart['cart_num'] ?? '1'), (string)($_info['costPrice'] ?? '0'), 2), 2);
$deduction_price = bcadd((string)$deduction_price, (string)($_info['integral_price'] ?? '0'), 2);
$first_order_price = bcadd((string)$first_order_price, (string)($_info['first_order_price'] ?? '0'), 2);
$coupon_price = bcadd((string)$coupon_price, (string)($_info['coupon_price'] ?? '0'), 2);
$use_integral = bcadd((string)$use_integral, (string)($_info['use_integral'] ?? '0'), 0);
if (!in_array($orderInfo['shipping_type'], [2, 4])) {
$pay_postage = bcadd((string)$pay_postage, (string)($_info['postage_price'] ?? '0'), 2);
}
$cartInfoGainIntegral = bcmul((string)$cart['cart_num'], (string)($_info['productInfo']['give_integral'] ?? '0'), 0);
$gainIntegral = bcadd((string)$gainIntegral, (string)$cartInfoGainIntegral, 0);
$one_brokerage = bcadd((string)$one_brokerage, (string)($_info['one_brokerage'] ?? '0'), 2);
$two_brokerage = bcadd((string)$two_brokerage, (string)($_info['two_brokerage'] ?? '0'), 2);
$promotions_price = bcadd((string)$promotions_price, (string)($_info['sum_promotions_price'] ?? '0'), 2);
}
$order_update['coupon_id'] = array_unique(array_column($cart_info_data, 'coupon_id'));
$order_update['pay_price'] = bcadd((string)$true_total_price, (string)$pay_postage, 2);
//有订单原来支付金额 改价订单
if ($order_pay_price) {
if ($pre_pay_price) {//上一个已经计算 这里减法
$order_update['pay_price'] = bcsub((string)$pay_price, (string)$pre_pay_price, 2);
} else {//按比例计算实际支付金额
$order_update['pay_price'] = bcmul((string)bcdiv((string)$pay_price, (string)$order_pay_price, 4), (string)$order_update['pay_price'], 2);
}
}
if (!$pre_pay_price) {//最后一个订单保留赠送 积分 优惠券信息
$order_update['give_integral'] = 0;
$order_update['give_coupon'] = '';
$order_update['gain_integral'] = 0;
}
$order_update['pay_price'] = max($order_update['pay_price'], 0);
if($online_price >= $order_update['pay_price']){
// online_price足够支付全部金额
$order_update['online_price'] = $order_update['pay_price'];
$order_update['yue_price'] = 0;
$online_price = bcsub((string)$online_price, (string)$order_update['pay_price'], 2); // 剩余online_price
// yue_price不变,全部剩余
}else{
// online_price不够,全部用完,剩余部分用yue_price
$order_update['online_price'] = $online_price;
$remaining_pay = bcsub((string)$order_update['pay_price'], (string)$online_price, 2); // 剩余需要支付的金额
if($yue_price >= $remaining_pay){
// yue_price足够支付剩余金额
$order_update['yue_price'] = $remaining_pay;
$yue_price = bcsub((string)$yue_price, (string)$remaining_pay, 2); // 剩余yue_price
}else{
// yue_price也不够,全部用完
$order_update['yue_price'] = $yue_price;
$yue_price = 0; // yue_price用完
}
$online_price = 0; // online_price已用完
}
$order_update['pay_integral'] = $true_total_integral;
$order_update['total_price'] = $total_price;
$order_update['cost'] = $cost_price;
$order_update['deduction_price'] = $deduction_price;
$order_update['first_order_price'] = $first_order_price;
$order_update['coupon_price'] = $coupon_price;
$order_update['use_integral'] = $use_integral;
$order_update['pay_postage'] = $pay_postage;
$order_update['one_brokerage'] = $one_brokerage;
$order_update['two_brokerage'] = $two_brokerage;
$order_update['promotions_price'] = $promotions_price;
if ($orderInfo['status'] == 5) {//部分核销拆单
if (count($cart_info_data) == $wrrteoffed) {//全部核销
$order_update['status'] = 2;
}
}
if (false === $this->dao->update($id, $order_update, 'id')) {
throw new ValidateException('保存新订单商品信息失败');
}
return [$online_price, $yue_price];
}
/**
* 部分发货重新计算订单商品:实际金额、优惠、积分等金额
* @param int $cart_num
* @param array $cart_info
* @param int $i
* @param bool $is_gift
* @return array
*/
public function slpitComputeOrderCart(int $cart_num, array $cart_info, int $i = 0, bool $is_gift = false)
{
if (!$cart_num || !$cart_info) return [];
$new_cart_info = $cart_info;
if ($cart_num < $cart_info['cart_num']) {
$compute_arr = ['coupon_price', 'integral_price', 'postage_price', 'use_integral', 'one_brokerage', 'two_brokerage', 'sum_true_price', 'first_order_price', 'division_staff_brokerage', 'division_agent_brokerage', 'division_brokerage'];
foreach ($compute_arr as $field) {
if (!isset($cart_info[$field]) || !$cart_info[$field] || $is_gift) {
$new_cart_info[$field] = 0;
continue;
}
$scale = 2;
if ($field == 'use_integral') $scale = 0;
if (!$i) {//拆出
$new_cart_info[$field] = bcmul((string)$cart_num, bcdiv((string)$cart_info[$field], (string)$cart_info['cart_num'], 4), $scale);
} else {
$field_number = bcmul((string)bcsub((string)$cart_info['cart_num'], (string)$cart_num, 0), bcdiv((string)$cart_info[$field], (string)$cart_info['cart_num'], 4), $scale);
$new_cart_info[$field] = bcsub((string)$cart_info[$field], (string)$field_number, $scale);
}
}
}
$new_cart_info['cart_num'] = $cart_num;
return $new_cart_info;
}
}

View File

@@ -0,0 +1,177 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\order;
use app\dao\order\StoreOrderStatusDao;
use app\services\BaseServices;
use app\services\message\service\StoreServiceServices;
use app\services\store\SystemStoreStaffServices;
use app\services\system\admin\SystemAdminServices;
use app\services\user\UserServices;
use think\annotation\Inject;
use think\exception\ValidateException;
use think\facade\Log;
/**
* 订单状态
* Class StoreOrderStatusServices
* @package app\services\order
* @mixin StoreOrderStatusDao
*/
class StoreOrderStatusServices extends BaseServices
{
/**
* @var StoreOrderStatusDao
*/
#[Inject]
protected StoreOrderStatusDao $dao;
/**
* 订单状态分页
* @param array $where
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getStatusList(array $where)
{
$list = $this->dao->getStatusList($where);
foreach ($list as &$item) {
if (is_int($item['change_time'])) $item['change_time'] = date('Y-m-d H:i:s', $item['change_time']);
}
$count = $this->dao->count($where);
return compact('list', 'count');
}
/**
* 保存订单状态变更记录
* @param int $orderId 订单ID
* @param string $changeType 变更类型
* @param array $data 变更数据
* @param int $changeManagerId 操作人ID
* @param string $changeManagerType 操作人类型(user/admin/store/supplier/system)
* @return bool
*/
public function saveStatus(int $orderId, string $changeType = 'create', array $data = [], int $changeManagerId = 0, string $changeManagerType = 'user')
{
if (!$orderId || !$data) {
return true;
}
/** @var StoreOrderServices $orderService */
$orderService = app()->make(StoreOrderServices::class);
$orderInfo = $orderService->get($orderId);
if (!$orderInfo) {
return true;
}
$changeManagerId = !$changeManagerId && $changeManagerType == 'user' ? $orderInfo['uid'] : $changeManagerId;
$statusData = [
'oid' => $orderId,
'change_time' => time(),
'change_type' => $changeType,
'change_message' => $data['change_message'] ?? '',
'change_manager_type' => $changeManagerType,
'change_manager_id' => $changeManagerId
];
if ($changeManagerId) {
switch ($changeManagerType) {
case 'user':
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
$userInfo = $userServices->getUserCacheInfo($changeManagerId);
if ($userInfo) {
$statusData['change_manager'] = $userInfo['nickname'] ?? '';
}
break;
case 'admin':
/** @var SystemAdminServices $adminServices */
$adminServices = app()->make(SystemAdminServices::class);
$adminInfo = $adminServices->get($changeManagerId);
if ($adminInfo) {
$statusData['change_manager'] = $adminInfo['real_name'] ?? 'system';
} else {
$statusData['change_manager'] = 'system';
}
break;
case 'store':
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
$userInfo = $userServices->getUserCacheInfo($changeManagerId);
$statusData['change_manager'] = $userInfo['nickname'] ?? '';
break;
case 'supplier':
/** @var SystemAdminServices $systemAdminServices */
$systemAdminServices = app()->make(SystemAdminServices::class);
$supplierInfo = $systemAdminServices->get($changeManagerId);
if ($supplierInfo) {
$statusData['change_manager'] = $supplierInfo['real_name'] ?? '';
}
break;
case 'system':
$statusData['change_manager'] = '系统执行';
break;
default:
break;
}
}
$this->dao->save($statusData);
return true;
}
/**
* 保存订单记录
* @param $type
* @param $orderId
* @param $data
* @return bool
*/
public function saveStatusOld($type, $orderId, $data)
{
if (!$type || !$orderId) {
return true;
}
$statusData = ['oid' => $orderId, 'change_time' => time(), 'change_type' => ''];
switch ($type) {
case 'create'://创建
$statusData['change_type'] = 'cache_key_create_order';
$statusData['change_message'] = '订单生成';
if (isset($data['changePrice']) && $data['changePrice'] > 0) {//有改价
$this->saveStatus('edit', $orderId, $data);
}
break;
case 'edit'://改价
$statusData['change_type'] = 'order_edit';
$statusData['change_message'] = '商品总价为:' . ($data['totalPrice'] ?? 0) . ' 修改实际支付金额为:' . ($data['payPrice'] ?? 0);
break;
case 'pay'://支付
$statusData['change_type'] = 'pay_success';
$statusData['change_message'] = '用户付款成功';
break;
case 'delivery'://发货
break;
case 'take'://收货
break;
case 'reply'://评价
break;
default:
throw new ValidateException('未知订单类型记录');
break;
}
/** @var StoreOrderStatusServices $statusService */
$statusService = app()->make(StoreOrderStatusServices::class);
$statusService->save($statusData);
return true;
}
}

View File

@@ -0,0 +1,32 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\order;
use app\dao\order\StoreOrderStoreOrderCartInfoDao;
use app\services\BaseServices;
use think\annotation\Inject;
/**
* Class StoreOrderStoreOrderCartInfoServices
* @package app\services\order
* @mixin StoreOrderStoreOrderCartInfoDao
*/
class StoreOrderStoreOrderCartInfoServices extends BaseServices
{
/**
* @var StoreOrderStoreOrderCartInfoDao
*/
#[Inject]
protected StoreOrderStoreOrderCartInfoDao $dao;
}

View File

@@ -0,0 +1,31 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\order;
use app\dao\order\StoreOrderStoreOrderStatusDao;
use app\services\BaseServices;
use think\annotation\Inject;
/**
* Class StoreOrderStoreOrderStatusServices
* @package app\services\order
* @mixin StoreOrderStoreOrderStatusDao
*/
class StoreOrderStoreOrderStatusServices extends BaseServices
{
/**
* @var StoreOrderStoreOrderStatusDao
*/
#[Inject]
protected StoreOrderStoreOrderStatusDao $dao;
}

View File

@@ -0,0 +1,99 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\order;
use app\dao\order\StoreOrderDao;
use app\services\activity\lottery\LuckLotteryServices;
use app\services\BaseServices;
use app\services\pay\IntegralPayServices;
use app\services\pay\PayServices;
use app\services\user\UserServices;
use think\annotation\Inject;
use think\exception\ValidateException;
/**
* Class StoreOrderSuccessServices
* @package app\services\order
* @mixin StoreOrderDao
*/
class StoreOrderSuccessServices extends BaseServices
{
/**
* @var StoreOrderDao
*/
#[Inject]
protected StoreOrderDao $dao;
/**
* 0元支付
* @param array $orderInfo
* @param int $uid
* @return bool
* @throws \think\Exception
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
*/
public function zeroYuanPayment(array $orderInfo, int $uid, string $payType = PayServices::YUE_PAY)
{
$id = $orderInfo['id'] ?? 0;
if (!$orderInfo || !$id) {
throw new ValidateException('订单不存在!');
}
//更新订单信息
$orderInfo = $this->dao->get($id);
if (!$orderInfo) {
throw new ValidateException('订单不存在');
}
$orderInfo = $orderInfo->toArray();
if ($orderInfo['paid']) {
throw new ValidateException('该订单已支付!');
}
return $this->paySuccess($orderInfo, $payType);//余额支付成功
}
/**
* 支付成功
* @param array $orderInfo
* @param string $paytype
* @return bool
*/
public function paySuccess(array $orderInfo, string $paytype = PayServices::WEIXIN_PAY, array $other = [])
{
$updata = ['paid' => 1, 'pay_type' => $paytype, 'pay_time' => time()];
if ($other && isset($other['trade_no'])) {
$updata['trade_no'] = $other['trade_no'];
}
$res1 = $this->dao->update($orderInfo['id'], $updata);
$orderInfo['trade_no'] = $other['trade_no'] ?? '';
$orderInfo['pay_time'] = time();
$orderInfo['pay_type'] = $paytype;
//缓存抽奖次数 除过线下支付和抽奖订单
if (isset($orderInfo['pay_type']) && $orderInfo['pay_type'] != 'offline' && isset($orderInfo['type']) && $orderInfo['type'] != 8 && $orderInfo['type'] != 9) {
/** @var LuckLotteryServices $luckLotteryServices */
$luckLotteryServices = app()->make(LuckLotteryServices::class);
$luckLotteryServices->setCacheLotteryNum((int)$orderInfo['uid'], 'order');
}
$userInfo = app()->make(UserServices::class)->get($orderInfo['uid']);
if ($orderInfo['pay_integral']) {//需要支付积分
/** @var IntegralPayServices $integralPayServices */
$integralPayServices = app()->make(IntegralPayServices::class);
$integralPayServices->integralOrderPay((int)$userInfo['uid'], $orderInfo, $userInfo->toArray());
}
//订单支付成功事件
event('order.pay', [$orderInfo, $userInfo]);
$res = $res1;
return false !== $res;
}
}

View File

@@ -0,0 +1,619 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\order;
use app\dao\order\StoreOrderDao;
use app\jobs\order\AutoTakeOrderJob;
use app\jobs\notice\SmsAdminJob;
use app\jobs\order\OrderStatusJob;
use app\jobs\user\UserLevelJob;
use app\services\activity\holiday\HolidayGiftPushServices;
use app\services\BaseServices;
use app\services\message\service\StoreServiceServices;
use app\services\message\sms\SmsSendServices;
use app\services\user\member\MemberCardServices;
use app\services\user\UserBillServices;
use app\services\user\UserBrokerageServices;
use app\services\user\UserServices;
use think\annotation\Inject;
use think\exception\ValidateException;
use think\facade\Log;
/**
* 订单收货
* Class StoreOrderTakeServices
* @package app\services\order
* @mixin StoreOrderDao
*/
class StoreOrderTakeServices extends BaseServices
{
/**
* @var StoreOrderDao
*/
#[Inject]
protected StoreOrderDao $dao;
/**
* 小程序订单服务收货
* @param $order_id
* @return bool
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*
* @date 2023/05/18
* @author yyw
*/
public function miniOrderTakeOrder($order_id)
{
$orderArr = explode('_', $order_id);
if (count($orderArr) == 2) {
$order_id = $orderArr[1] ?? $order_id;
}
//查找订单信息
$order = $this->dao->getOne(['order_id' => $order_id]);
if (!$order) {
return true;
}
if ($order['pid'] == -1) { // 有子订单
// 查找待收货的子订单
$son_order_list = $this->dao->getSubOrderNotSendList((int)$order['id']);
foreach ($son_order_list as $son_order) {
$this->takeOrder($son_order['order_id'], $son_order['uid']);
}
} else {
$this->takeOrder($order_id, $order['uid']);
}
return true;
}
/**
* 用户订单收货
* @param $uni
* @param $uid
* @return bool
*/
public function takeOrder(string $uni, int $uid)
{
$order = $this->dao->getUserOrderDetail($uni, $uid);
if (!$order) {
throw new ValidateException('订单不存在!');
}
/** @var StoreOrderServices $orderServices */
$orderServices = app()->make(StoreOrderServices::class);
$order = $orderServices->tidyOrder($order);
if ($order['_status']['_type'] != 2) {
throw new ValidateException('订单状态错误!');
}
//存在拆分发货 需要分开收货
if ($this->dao->count(['pid' => $order['id']])) {
throw new ValidateException('拆分发货,请去订单详情中包裹确认收货');
}
$order->status = 2;
$res = $order->save();
OrderStatusJob::dispatch([$order['id'], 'user_take_delivery', ['change_message' => '用户已收货', 'change_manager_type' => 'user']]);
$res = $res && $this->storeProductOrderUserTakeDelivery($order);
if (!$res) {
throw new ValidateException('收货失败');
}
//核销订单 修改订单商品核销状态
if ($order['shipping_type'] == 2 || (in_array($order['shipping_type'], [1, 3]) && $order['delivery_type'] == 'send')) {
//修改原来订单商品信息
$cartData['is_writeoff'] = 1;
$cartData['write_surplus_times'] = 0;
/** @var StoreOrderCartInfoServices $cartInfoServices */
$cartInfoServices = app()->make(StoreOrderCartInfoServices::class);
$cartInfoServices->update(['oid' => $order['id']], $cartData);
}
return $order;
}
/**
* 订单确认收货
* @param $order
* @return bool
*/
public function storeProductOrderUserTakeDelivery($order, bool $isTran = true, bool $isRecord = true)
{
$res = true;
//获取购物车内的商品标题
/** @var StoreOrderCartInfoServices $orderInfoServices */
$orderInfoServices = app()->make(StoreOrderCartInfoServices::class);
$storeName = $orderInfoServices->getCarIdByProductTitle((int)$order['id']);
$storeTitle = substrUTf8($storeName, 20, 'UTF-8', '');
if ($order['uid']) {
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
$userInfo = $userServices->getUserCacheInfo((int)$order['uid']);
$res = $this->transaction(function () use ($order, $userInfo, $storeTitle) {
//赠送积分
$res1 = $this->gainUserIntegral($order, $userInfo, $storeTitle);
//返佣
$res2 = $this->backOrderBrokerage($order, $userInfo);
//经验
$res3 = $this->gainUserExp($order, $userInfo);
//事业部
$res4 = $this->divisionBrokerage($order, $userInfo);
if (!($res1 && $res2 && $res3)) {
throw new ValidateException('收货失败!');
}
return true;
}, $isTran);
}
if ($res) {
//订单收货事件
event('order.take', [$order, $storeTitle, $isRecord]);
return true;
} else {
return false;
}
}
/**
* 赠送积分
* @param $order
* @param $userInfo
* @param $storeTitle
* @return bool
*/
public function gainUserIntegral($order, $userInfo, $storeTitle)
{
$res2 = true;
if (!$userInfo) {
return true;
}
// 营销产品送积分
if (!isset($order['type']) || $order['type'] != 0) {
return true;
}
/** @var UserBillServices $userBillServices */
$userBillServices = app()->make(UserBillServices::class);
$balance = $userInfo['integral'];
if ($order['gain_integral'] > 0) {
$balance = bcadd((string)$balance, (string)$order['gain_integral']);
$res2 = false != $userBillServices->income('pay_give_integral', $order['uid'], (int)$order['gain_integral'], (int)$balance, $order['id']);
}
$order_integral = 0;
$res3 = true;
$order_give_integral = sys_config('order_give_integral');
if ($order['pay_price'] && $order_give_integral) {
//积分倍数
$multiple = 0;
//会员消费返积分翻倍
if ($userInfo['is_money_level'] > 0) {
//看是否开启消费返积分翻倍奖励
/** @var MemberCardServices $memberCardService */
$memberCardService = app()->make(MemberCardServices::class);
$integral_rule_number = $memberCardService->isOpenMemberCardCache('integral');
$multiple = bcadd((string)$multiple, (string)$integral_rule_number, 0);
}
//节日任务赠送积分倍数
$integral_multiple = app()->make(HolidayGiftPushServices::class)->receiveHolidayGift($userInfo['uid'], 2);
if($integral_multiple){
$multiple = bcadd((string)$multiple, (string)$integral_multiple, 0);
}
if($multiple){
$order_integral = bcmul((string)$order['pay_price'], (string)$multiple, 2);
}
$order_integral = bcmul((string)$order_give_integral, (string)($order_integral ? $order_integral : $order['pay_price']), 0);
$balance = bcadd((string)$balance, (string)$order_integral);
$res3 = false != $userBillServices->income('order_give_integral', $order['uid'], (int)$order_integral, (int)$balance, $order['id']);
}
$give_integral = $order_integral + $order['gain_integral'];
if ($give_integral > 0) {
$integral = $userInfo['integral'] + $give_integral;
$userInfo->integral = $integral;
$res1 = false != $userInfo->save();
$res = $res1 && $res2 && $res3;
//发送消息
event('notice.notice', [['order' => $order, 'storeTitle' => $storeTitle, 'give_integral' => $give_integral, 'integral' => $integral], 'integral_accout']);
return $res;
}
return true;
}
/**
* 一级返佣
* @param $orderInfo
* @param $userInfo
* @return bool
*/
public function backOrderBrokerage($orderInfo, $userInfo)
{
// 当前订单|用户不存在 直接返回
if (!$orderInfo || !$userInfo) {
return true;
}
//商城分销功能是否开启 0关闭1开启
if (!sys_config('brokerage_func_status')) return true;
// 营销产品不返佣金
if (!isset($orderInfo['type']) || in_array($orderInfo['type'], [4, 5, 7, 8])) {
return true;
}
//绑定失效
if (isset($orderInfo['spread_uid']) && $orderInfo['spread_uid'] == -1) {
return true;
}
//是否开启自购返佣
$isSelfBrokerage = sys_config('is_self_brokerage', 0);
if (!isset($orderInfo['spread_uid']) || !$orderInfo['spread_uid']) {//兼容之前订单表没有spread_uid情况
//没开启自购返佣 没有上级 或者 当用用户上级时自己 直接返回
if (!$isSelfBrokerage && (!$userInfo['spread_uid'] || $userInfo['spread_uid'] == $orderInfo['uid'])) {
return true;
}
$one_spread_uid = $isSelfBrokerage ? $userInfo['uid'] : $userInfo['spread_uid'];
} else {
$one_spread_uid = $orderInfo['spread_uid'];
}
$one_spread_uid = (int)$one_spread_uid;
//冻结时间
$broken_time = intval(sys_config('extract_time'));
$frozen_time = time() + $broken_time * 86400;
//检测是否是分销员
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
if (!$userServices->checkUserPromoter($one_spread_uid)) {//一级不是分销员 直接二级返佣
return $this->backOrderBrokerageTwo($orderInfo, $userInfo, $isSelfBrokerage, $frozen_time);
}
//订单中取出
$brokeragePrice = $orderInfo['one_brokerage'] ?? 0;
// 返佣金额小于等于0 直接返回不返佣金
if ($brokeragePrice <= 0) {
return true;
}
// 获取上级推广员信息
$spreadPrice = $userServices->value(['uid' => $one_spread_uid], 'brokerage_price');
// 上级推广员返佣之后的金额
$balance = bcadd($spreadPrice, $brokeragePrice, 2);
// 添加佣金记录
/** @var UserBrokerageServices $userBrokerageServices */
$userBrokerageServices = app()->make(UserBrokerageServices::class);
//自购返佣 || 上级
$type = $one_spread_uid == $orderInfo['uid'] ? 'get_self_brokerage' : 'get_brokerage';
$res1 = $userBrokerageServices->income($type, $one_spread_uid, [
'nickname' => $userInfo['nickname'],
'pay_price' => floatval($orderInfo['pay_price']),
'number' => floatval($brokeragePrice),
'frozen_time' => $frozen_time
], $balance, $orderInfo['id']);
// 添加用户佣金
$res2 = $userServices->bcInc($one_spread_uid, 'brokerage_price', $brokeragePrice, 'uid');
//给上级发送获得佣金的模板消息
$this->sendBackOrderBrokerage($orderInfo, $one_spread_uid, $brokeragePrice);
// 一级返佣成功 跳转二级返佣
$res = $res1 && $res2 && $this->backOrderBrokerageTwo($orderInfo, $userInfo, $isSelfBrokerage, $frozen_time);
return $res;
}
/**
* 二级推广返佣
* @param $orderInfo
* @param $userInfo
* @param int $isSelfbrokerage
* @param int $frozenTime
* @return bool
*/
public function backOrderBrokerageTwo($orderInfo, $userInfo, $isSelfbrokerage = 0, $frozenTime = 0)
{
//绑定失效
if (isset($orderInfo['spread_two_uid']) && $orderInfo['spread_two_uid'] == -1) {
return true;
}
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
if (isset($orderInfo['spread_two_uid']) && $orderInfo['spread_two_uid']) {
$spread_two_uid = $orderInfo['spread_two_uid'];
} else {
// 获取上推广人
$userInfoTwo = $userServices->get((int)$userInfo['spread_uid']);
// 订单|上级推广人不存在 直接返回
if (!$orderInfo || !$userInfoTwo) {
return true;
}
//没开启自购返佣 或者 上推广人没有上级 或者 当用用户上上级时自己 直接返回
if (!$isSelfbrokerage && (!$userInfoTwo['spread_uid'] || $userInfoTwo['spread_uid'] == $orderInfo['uid'])) {
return true;
}
$spread_two_uid = $isSelfbrokerage ? $userInfoTwo['uid'] : $userInfoTwo['spread_uid'];
}
$spread_two_uid = (int)$spread_two_uid;
// 获取后台分销类型 1 指定分销 2 人人分销
if (!$userServices->checkUserPromoter($spread_two_uid)) {
return true;
}
//订单中取出
$brokeragePrice = $orderInfo['two_brokerage'] ?? 0;
// 返佣金额小于等于0 直接返回不返佣金
if ($brokeragePrice <= 0) {
return true;
}
// 获取上上级推广员信息
$spreadPrice = $userServices->value(['uid' => $spread_two_uid], 'brokerage_price');
// 获取上上级推广员返佣之后余额
$balance = bcadd($spreadPrice, $brokeragePrice, 2);
// 添加佣金记录
/** @var UserBrokerageServices $userBrokerageServices */
$userBrokerageServices = app()->make(UserBrokerageServices::class);
$res1 = $userBrokerageServices->income('get_two_brokerage', $spread_two_uid, [
'nickname' => $userInfo['nickname'],
'pay_price' => floatval($orderInfo['pay_price']),
'number' => floatval($brokeragePrice),
'frozen_time' => $frozenTime
], $balance, $orderInfo['id']);
// 添加用户佣金
$res2 = $userServices->bcInc($spread_two_uid, 'brokerage_price', $brokeragePrice, 'uid');
//给上级发送获得佣金的模板消息
$this->sendBackOrderBrokerage($orderInfo, $spread_two_uid, $brokeragePrice);
return $res1 && $res2;
}
/**
* 佣金到账发送模板消息
* @param $orderInfo
* @param $spread_uid
* @param $brokeragePrice
*/
public function sendBackOrderBrokerage($orderInfo, $spread_uid, $brokeragePrice, string $type = 'order')
{
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
$user = $userServices->getUserInfo($spread_uid, 'phone,user_type');
$goodsName = '';
$goodsPrice = 0;
if ($type == 'order') {
/** @var StoreOrderCartInfoServices $storeOrderCartInfoService */
$storeOrderCartInfoService = app()->make(StoreOrderCartInfoServices::class);
$cartInfo = $storeOrderCartInfoService->getOrderCartInfo($orderInfo['id']);
if ($cartInfo) {
$cartInfo = array_column($cartInfo, 'cart_info');
foreach ($cartInfo as $k => $v) {
$goodsName .= $v['productInfo']['store_name'];
$price = $v['productInfo']['attrInfo']['price'] ?? $v['productInfo']['price'] ?? 0;
$goodsPrice = bcadd((string)$goodsPrice, (string)bcmul((string)$price, (string)$v['cart_num'], 2), 2);
}
}
} else {
$goodsName = '推广用户获取佣金';
$goodsPrice = $brokeragePrice;
}
//提醒推送
event('notice.notice', [['spread_uid' => $spread_uid, 'phone' => $user['phone'], 'userType' => $user['user_type'], 'brokeragePrice' => $brokeragePrice, 'goodsName' => $goodsName, 'goodsPrice' => $goodsPrice, 'add_time' => $orderInfo['add_time'] ?? time()], 'order_brokerage']);
return true;
}
/**
* 发送短信
* @param $order
* @param $storeTitle
*/
public function smsSend($order, $storeTitle)
{
/** @var SmsSendServices $smsServices */
$smsServices = app()->make(SmsSendServices::class);
$switch = (bool)sys_config('confirm_take_over_switch');
//模板变量
$store_name = $storeTitle;
$order_id = $order['order_id'];
$smsServices->send($switch, $order['user_phone'], compact('store_name', 'order_id'), 'TAKE_DELIVERY_CODE');
}
/**
* 发送确认收货管理员短信
* @param $order
*/
public function smsSendTake($order)
{
$switch = (bool)sys_config('admin_confirm_take_over_switch');
/** @var StoreServiceServices $services */
$services = app()->make(StoreServiceServices::class);
$adminList = $services->getStoreServiceOrderNotice();
SmsAdminJob::dispatchDo('sendAdminConfirmTakeOver', [$switch, $adminList, $order]);
return true;
}
/**
* 赠送经验
* @param $order
* @param $userInfo
* @return bool
*/
public function gainUserExp($order, $userInfo)
{
if (!$userInfo) {
return true;
}
//用户等级是否开启
if (!sys_config('member_func_status', 1)) {
return true;
}
//用户等级未激活
if (isset($userInfo['level_status']) && !$userInfo['level_status']) {
return true;
}
/** @var UserBillServices $userBillServices */
$userBillServices = app()->make(UserBillServices::class);
$order_exp = 0;
$res3 = true;
$order_give_exp = sys_config('order_give_exp');
$balance = $userInfo['exp'];
if ($order['pay_price'] && $order_give_exp) {
$order_exp = bcmul($order_give_exp, (string)$order['pay_price'], 2);
$balance = bcadd((string)$balance, (string)$order_exp, 2);
$res3 = false != $userBillServices->income('order_give_exp', $order['uid'], $order_exp, $balance, $order['id']);
}
$res = true;
if ($order_exp > 0) {
$userInfo->exp = $balance;
$res1 = false != $userInfo->save();
$res = $res1 && $res3;
}
//检测用户等级升级
UserLevelJob::dispatch([(int)$order['uid']]);
return $res;
}
/**
* 执行自动收货
* @param array $where
* @param int $page
* @param int $maxLimit
* @return bool
*/
public function runAutoTakeOrder(array $where, int $page = 0, int $maxLimit = 0)
{
/** @var StoreOrderStoreOrderStatusServices $service */
$service = app()->make(StoreOrderStoreOrderStatusServices::class);
$orderList = $service->getOrderIds($where, $page, $maxLimit);
/** @var StoreOrderRefundServices $storeOrderRefundServices */
$storeOrderRefundServices = app()->make(StoreOrderRefundServices::class);
foreach ($orderList as $order) {
if ($order['status'] == 2) {
continue;
}
if ($order['paid'] == 1 && $order['status'] == 1) {
$data['status'] = 2;
} else if ($order['pay_type'] == 'offline') {
$data['status'] = 2;
} else {
continue;
}
if ($storeOrderRefundServices->count(['store_order_id' => $order['id'], 'refund_type' => [0, 1, 2, 4, 5], 'is_cancel' => 0, 'is_del' => 0])) {
continue;
}
try {
$res = $this->dao->update($order['id'], $data);
$res = $res && $this->storeProductOrderUserTakeDelivery($order);
OrderStatusJob::dispatch([$order['id'], 'take_delivery', ['change_message' => '已收货[自动收货]', 'change_manager_type' => 'system']]);
if (!$res) {
throw new ValidateException('订单号' . $order['order_id'] . '自动收货失败');
}
} catch (\Throwable $e) {
Log::error('自动收货失败,失败原因:' . $e->getMessage());
}
}
return true;
}
/**
* 自动收货
* @return bool
*/
public function autoTakeOrder()
{
//7天前时间戳
$systemDeliveryTime = (int)sys_config('system_delivery_time', 0);
//0为取消自动收货功能
if ($systemDeliveryTime == 0) {
return true;
}
$sevenDay = strtotime(date('Y-m-d H:i:s', strtotime('-' . $systemDeliveryTime . ' day')));
/** @var StoreOrderStoreOrderStatusServices $service */
$service = app()->make(StoreOrderStoreOrderStatusServices::class);
$where = [
'change_time' => $sevenDay,
'is_del' => 0,
'paid' => 1,
'status' => 1,
'change_type' => ['delivery_goods', 'delivery_fictitious', 'delivery', 'city_delivery']
];
$maxLimit = 20;
$count = $service->getOrderCount($where);
if ($count > $maxLimit) {
$page = ceil($count / $maxLimit);
for ($i = 1; $i <= $page; $i++) {
AutoTakeOrderJob::dispatch([$where, $i, $maxLimit]);
}
} else {
$this->runAutoTakeOrder($where);
}
return true;
}
/**
* 事业部返佣
* @param $orderInfo
* @param $userInfo
* @return bool
*/
public function divisionBrokerage($orderInfo, $userInfo)
{
// 当前订单|用户不存在 直接返回
if (!$orderInfo || !$userInfo) {
return true;
}
// 营销产品不返佣金
if (!isset($orderInfo['type']) || in_array($orderInfo['type'], [4, 5, 7, 8])) {
return true;
}
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
if ($orderInfo['division_staff_id'] && $orderInfo['division_staff_brokerage'] > 0) {
$spreadPrice = $userServices->value(['uid' => $orderInfo['division_staff_id']], 'brokerage_price');
$balance = bcadd($spreadPrice, $orderInfo['division_staff_brokerage'], 2);
$userServices->bcInc($orderInfo['division_staff_id'], 'brokerage_price', $orderInfo['division_staff_brokerage'], 'uid');
//冻结时间
$broken_time = intval(sys_config('extract_time'));
$frozen_time = time() + $broken_time * 86400;
// 添加佣金记录
/** @var UserBrokerageServices $userBrokerageServices */
$userBrokerageServices = app()->make(UserBrokerageServices::class);
$userBrokerageServices->income('get_staff_brokerage', $orderInfo['division_staff_id'], [
'nickname' => $userInfo['nickname'],
'pay_price' => floatval($orderInfo['pay_price']),
'number' => floatval($orderInfo['division_staff_brokerage']),
'frozen_time' => $frozen_time
], $balance, $orderInfo['id']);
}
if ($orderInfo['division_agent_id'] && $orderInfo['division_agent_brokerage'] > 0) {
$spreadPrice = $userServices->value(['uid' => $orderInfo['division_agent_id']], 'brokerage_price');
$balance = bcadd($spreadPrice, $orderInfo['division_agent_brokerage'], 2);
$userServices->bcInc($orderInfo['division_agent_id'], 'brokerage_price', $orderInfo['division_agent_brokerage'], 'uid');
//冻结时间
$broken_time = intval(sys_config('extract_time'));
$frozen_time = time() + $broken_time * 86400;
// 添加佣金记录
/** @var UserBrokerageServices $userBrokerageServices */
$userBrokerageServices = app()->make(UserBrokerageServices::class);
$userBrokerageServices->income('get_agent_brokerage', $orderInfo['division_agent_id'], [
'nickname' => $userInfo['nickname'],
'pay_price' => floatval($orderInfo['pay_price']),
'number' => floatval($orderInfo['division_agent_brokerage']),
'frozen_time' => $frozen_time
], $balance, $orderInfo['id']);
}
if ($orderInfo['division_id'] && $orderInfo['division_brokerage'] > 0) {
$spreadPrice = $userServices->value(['uid' => $orderInfo['division_id']], 'brokerage_price');
$balance = bcadd($spreadPrice, $orderInfo['division_brokerage'], 2);
$userServices->bcInc($orderInfo['division_id'], 'brokerage_price', $orderInfo['division_brokerage'], 'uid');
//冻结时间
$broken_time = intval(sys_config('extract_time'));
$frozen_time = time() + $broken_time * 86400;
// 添加佣金记录
/** @var UserBrokerageServices $userBrokerageServices */
$userBrokerageServices = app()->make(UserBrokerageServices::class);
$userBrokerageServices->income('get_division_brokerage', $orderInfo['division_id'], [
'nickname' => $userInfo['nickname'],
'pay_price' => floatval($orderInfo['pay_price']),
'number' => floatval($orderInfo['division_brokerage']),
'frozen_time' => $frozen_time
], $balance, $orderInfo['id']);
}
return true;
}
}

View File

@@ -0,0 +1,151 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\order;
use app\dao\order\StoreOrderDao;
use app\services\BaseServices;
use app\services\product\product\StoreProductLogServices;
use think\annotation\Inject;
/**
* Class StoreOrderWapServices
* @package app\services\order
* @mixin StoreOrderDao
*/
class StoreOrderWapServices extends BaseServices
{
/**
* @var StoreOrderDao
*/
#[Inject]
protected StoreOrderDao $dao;
/**
* 获取 今日 昨日 本月 订单金额
* @return mixed
*/
public function getOrderTimeData(int $store_id = 0)
{
$where = ['pid' => 0, 'timeKey' => 'add_time', 'paid' => 1, 'is_del' => 0, 'is_system_del' => 0, 'refund_status' => [0, 3]];
//今日成交额
$data['todayPrice'] = $this->dao->together($where + ['time' => 'today'], 'pay_price');
//今日订单数
$data['todayCount'] = $this->dao->count($where + ['time' => 'today']);
//昨日成交额
$data['proPrice'] = $this->dao->together($where + ['time' => 'yesterday'], 'pay_price');
//昨日订单数
$data['proCount'] = $this->dao->count($where + ['time' => 'yesterday']);
//本月成交额
$data['monthPrice'] = $this->dao->together($where + ['time' => 'month'], 'pay_price');
//本月订单数
$data['monthCount'] = $this->dao->count($where + ['time' => 'month']);
return $data;
}
/**
* 订单每月统计数据
* @param array $where
* @param int $store_id
* @return array
*/
public function getOrderDataPriceCount(array $where = [], int $store_id = 0)
{
[$page, $limit] = $this->getPageValue();
$data = $this->dao->getOrderDataPriceCount($where + ['pid' => 0, 'is_del' => 0, 'paid' => 1, 'refund_status' => [0, 3], 'is_system_del' => 0], ['sum(pay_price) as price', 'count(id) as count', 'add_time'], $page, $limit);
if (count($data) > 0) {
/** @var StoreProductLogServices $productLogServices */
$productLogServices = app()->make(StoreProductLogServices::class);
foreach ($data as $key => &$time) {
$time['time'] = date('m-d', $time['add_time']);
$time['visit'] = $productLogServices->count(['time' => [$time['add_time'], $time['add_time'] + 86400], 'type' => 'visit']);
}
}
return $data;
}
/**
* 前端订单列表
* @param array $where
* @param array|string[] $field
* @param array $with
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getOrderApiList(array $where, array $field = ['*'], array $with = [])
{
[$page, $limit] = $this->getPageValue();
if (isset($where['status']) && $where['status'] === '') {
$data = $this->dao->getOrderList($where, $field, $page, $limit, $with, 'id DESC');
} else {
$data = $this->dao->getOrderList($where, $field, $page, $limit, $with);
}
/** @var StoreOrderServices $orderServices */
$orderServices = app()->make(StoreOrderServices::class);
foreach ($data as &$item) {
$item['type_name'] = '';
if ($item['type'] > 0) {
[$type_name, $color] = $orderServices->tidyOrderType($item, true);
$item['type_name'] = $type_name;
}
$item = $orderServices->tidyOrder($item, true);
$cart_num = 0;
foreach ($item['cartInfo'] ?: [] as $key => $product) {
if (isset($item['_status']['_type']) && $item['_status']['_type'] == 3) {
$item['cartInfo'][$key]['add_time'] = isset($product['add_time']) ? date('Y-m-d H:i', (int)$product['add_time']) : '时间错误';
}
$item['cartInfo'][$key]['productInfo']['price'] = $product['truePrice'] ?? 0;
if (isset($product['is_gift']) && $product['is_gift']) continue;
$cart_num += $product['cart_num'];
}
if (count($item['refund'])) {
$refund_num = array_sum(array_column($item['refund'], 'refund_num'));
$item['is_all_refund'] = $refund_num == $cart_num;
} else {
$item['is_all_refund'] = false;
}
}
return $data;
}
/**
* 获取手机端订单管理
* @param array $where
* @param array $with
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getWapAdminOrderList(array $where, array $with = [])
{
[$page, $limit] = $this->getPageValue();
$list = $this->dao->getOrderList($where, ['*'], $page, $limit, $with);
/** @var StoreOrderServices $orderServices */
$orderServices = app()->make(StoreOrderServices::class);
$list = $orderServices->tidyOrderList($list);
foreach ($list as &$item) {
$refund_num = array_sum(array_column($item['refund'], 'refund_num'));
$cart_num = 0;
foreach ($item['_info'] as $items) {
if (isset($items['cart_info']['is_gift']) && $items['cart_info']['is_gift']) continue;
$cart_num += $items['cart_info']['cart_num'];
}
$item['is_all_refund'] = $refund_num == $cart_num;
}
return $list;
}
}

View File

@@ -0,0 +1,378 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\order;
use app\dao\order\StoreOrderDao;
use app\dao\order\StoreOrderWriteoffDao;
use app\services\message\service\StoreServiceServices;
use app\services\pay\PayServices;
use app\services\store\DeliveryServiceServices;
use app\services\activity\integral\StoreIntegralOrderServices;
use app\services\activity\integral\StoreIntegralOrderStatusServices;
use app\services\activity\combination\StorePinkServices;
use app\services\BaseServices;
use app\services\store\SystemStoreStaffServices;
use app\services\supplier\SystemSupplierServices;
use app\services\system\admin\SystemAdminServices;
use app\services\user\UserServices;
use think\annotation\Inject;
use think\exception\ValidateException;
/**
* 核销订单
* Class StoreOrderWriteOffServices
* @package app\sservices\order
* @mixin StoreOrderDao
*/
class StoreOrderWriteOffServices extends BaseServices
{
/**
* @var StoreOrderDao
*/
#[Inject]
protected StoreOrderDao $dao;
/**
* @var StoreOrderWriteoffDao
*/
#[Inject]
protected StoreOrderWriteoffDao $writeoffdao;
/**
* 订单核销
* @param string $code
* @param int $confirm
* @param int $uid
* @return mixed
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function writeOffOrder(string $code, int $confirm, int $uid = 0)
{
//订单
$orderInfo = $this->dao->getOne(['verify_code' => $code, 'paid' => 1, 'refund_status' => 0, 'is_del' => 0], '*', ['pink']);
$order_type = 'order';
if (!$orderInfo) {
//积分兑换订单
/** @var StoreIntegralOrderServices $storeIntegralOrderServices */
$storeIntegralOrderServices = app()->make(StoreIntegralOrderServices::class);
$orderInfo = $storeIntegralOrderServices->getOne(['verify_code' => $code]);
$order_type = 'integral';
}
if (!$orderInfo) {
throw new ValidateException('Write off order does not exist');
}
if (!$orderInfo['verify_code'] || ($orderInfo->shipping_type != 2 && $orderInfo->delivery_type != 'send')) {
throw new ValidateException('此订单不能被核销');
}
if ($uid) {
$isAuth = true;
switch ($orderInfo['shipping_type']) {
case 1://配送订单
/** @var DeliveryServiceServices $deliverServiceServices */
$deliverServiceServices = app()->make(DeliveryServiceServices::class);
$isAuth = $deliverServiceServices->getCount(['uid' => $uid, 'status' => 1]) > 0;
break;
case 2://自提订单
/** @var SystemStoreStaffServices $storeStaffServices */
$storeStaffServices = app()->make(SystemStoreStaffServices::class);
$isAuth = $storeStaffServices->getCount(['uid' => $uid, 'verify_status' => 1, 'status' => 1]) > 0;
break;
}
if (!$isAuth) {
throw new ValidateException('您无权限核销此订单,请联系管理员');
}
}
$orderInfo['order_type'] = $order_type;
if ($order_type == 'order') {
if ($orderInfo->status == 2) {
throw new ValidateException('订单已核销');
}
if (isset($orderInfo['pinkStatus']) && $orderInfo['pinkStatus'] != 2) {
throw new ValidateException('拼团未完成暂不能发货!');
}
/** @var StoreOrderCartInfoServices $orderCartInfo */
$orderCartInfo = app()->make(StoreOrderCartInfoServices::class);
$cartInfo = $orderCartInfo->getOne([
['cart_id', '=', $orderInfo['cart_id'][0]]
], 'cart_info');
if ($cartInfo) $orderInfo['image'] = $cartInfo['cart_info']['productInfo']['image'];
if ($orderInfo->shipping_type == 2) {
if ($orderInfo->status > 0) {
throw new ValidateException('Order written off');
}
}
if ($orderInfo['type'] == 3 && $orderInfo['activity_id'] && $orderInfo['pink_id']) {
/** @var StorePinkServices $services */
$services = app()->make(StorePinkServices::class);
$res = $services->getCount([['id', '=', $orderInfo->pink_id], ['status', '<>', 2]]);
if ($res) throw new ValidateException('Failed to write off the group order');
}
if ($confirm == 0) {
/** @var UserServices $services */
$services = app()->make(UserServices::class);
$orderInfo['nickname'] = $services->value(['uid' => $orderInfo['uid']], 'nickname');
return $orderInfo->toArray();
}
$orderInfo->status = 2;
if ($uid) {
if ($orderInfo->shipping_type == 2) {
$orderInfo->clerk_id = $uid;
}
}
if ($orderInfo->save()) {
/** @var StoreOrderTakeServices $storeOrdeTask */
$storeOrdeTask = app()->make(StoreOrderTakeServices::class);
$re = $storeOrdeTask->storeProductOrderUserTakeDelivery($orderInfo);
if (!$re) {
throw new ValidateException('Write off failure');
}
//修改订单商品信息
$cartData = ['writeoff_time' => time()];
$cartData['is_writeoff'] = 1;
$cartData['surplus_num'] = 0;
$orderCartInfo->update(['oid' => $orderInfo['id']], $cartData);
return $orderInfo->toArray();
} else {
throw new ValidateException('Write off failure');
}
} else {
if ($orderInfo['status'] == 3) {
throw new ValidateException('订单已核销');
}
if ($confirm == 0) {
/** @var UserServices $services */
$services = app()->make(UserServices::class);
$orderInfo['nickname'] = $services->value(['uid' => $orderInfo['uid']], 'nickname');
return $orderInfo->toArray();
}
if (!$storeIntegralOrderServices->update($orderInfo['id'], ['status' => 3])) {
throw new ValidateException('Write off failure');
} else {
//增加收货订单状态
/** @var StoreIntegralOrderStatusServices $statusService */
$statusService = app()->make(StoreIntegralOrderStatusServices::class);
$statusService->save([
'oid' => $orderInfo['id'],
'change_type' => 'take_delivery',
'change_message' => '已收货',
'change_time' => time()
]);
}
return $orderInfo->toArray();
}
}
/**
* 获取核销列表
* @param $where
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function writeOffList($where)
{
[$page, $limit] = $this->getPageValue();
$list = $this->writeoffdao->getList($where, '*', $page, $limit, [
'userInfo',
'staffInfo',
'orderInfo' => function ($query) {
$query->field('id,order_id,pay_type');
},
'cartInfo' => function ($query) {
$query->field('id,cart_info');
},
]);
$count = $this->writeoffdao->count($where);
if ($list) {
$supplierIds = [];
foreach ($list as $value) {
switch ($value['type']) {
case 0:
break;
case 2://供应商
$supplierIds[] = $value['relation_id'];
break;
}
}
$supplierIds = array_unique($supplierIds);
$supplierList = [];
if ($supplierIds) {
/** @var SystemSupplierServices $supplierServices */
$supplierServices = app()->make(SystemSupplierServices::class);
$supplierList = $supplierServices->getColumn([['id', 'in', $supplierIds], ['is_del', '=', 0]], 'id,supplier_name', 'id');
}
foreach ($list as &$item) {
$cartInfo = $item['cartInfo'] ?? [];
$cartInfo = is_string($cartInfo['cart_info']) ? json_decode($cartInfo['cart_info'], true) : $cartInfo['cart_info'];
$item['productInfo'] = $cartInfo['productInfo'] ?? [];
$orderInfo = $item['orderInfo'] ?? [];
$item['order_id'] = $orderInfo['order_id'] ?? '';
$item['pay_type'] = PayServices::PAY_TYPE[$item['pay_type']] ?? '其他方式';
$item['plate_name'] = '平台';
switch ($item['type']) {
case 0:
$item['plate_name'] = '平台';
break;
case 2://供应商
$item['plate_name'] = '供应商:' . ($supplierList[$item['relation_id']]['supplier_name'] ?? '');
break;
}
}
}
return compact('list', 'count');
}
/**
* 保存核销记录
* @param int $oid
* @param array $cartIds
* @param array $data
* @param array $orderInfo
* @param array $cartInfo
* @return bool
*/
public function saveWriteOff(int $oid, array $cartIds = [], array $data = [], array $orderInfo = [], array $cartInfo = [], int $admin_id = 0)
{
if (!$oid) {
throw new ValidateException('缺少核销订单信息');
}
if (!$orderInfo) {
/** @var StoreOrderServices $storeOrderServices */
$storeOrderServices = app()->make(StoreOrderServices::class);
$orderInfo = $storeOrderServices->get($oid);
}
if (!$orderInfo) {
throw new ValidateException('核销订单不存在');
}
$orderInfo = is_object($orderInfo) ? $orderInfo->toArray() : $orderInfo;
if (!$cartInfo) {
/** @var StoreOrderCartInfoServices $cartInfoServices */
$cartInfoServices = app()->make(StoreOrderCartInfoServices::class);
if ($cartIds) {//商城存在部分核销
$ids = array_unique(array_column($cartIds, 'cart_id'));
$cartIds = array_combine($ids, $cartIds);
//订单下原商品信息
$cartInfo = $cartInfoServices->getCartColunm(['oid' => $orderInfo['id'], 'cart_id' => $ids], '*', 'cart_id');
} else {//整单核销
$cartInfo = $cartInfoServices->getCartColunm(['oid' => $orderInfo['id']], '*', 'cart_id');
}
}
$writeOffDataAll = [];
$writeOffData = ['uid' => $orderInfo['uid'], 'oid' => $oid, 'writeoff_code' => $orderInfo['verify_code'], 'add_time' => time()];
foreach ($cartInfo as $cart) {
$write = $cartIds[$cart['cart_id']] ?? [];
$info = is_string($cart['cart_info']) ? json_decode($cart['cart_info'], true) : $cart['cart_info'];
if (!$cartIds || $write) {
$writeOffData['order_cart_id'] = $cart['id'];
$writeOffData['writeoff_num'] = $write['cart_num'] ?? $cart['cart_num'];
$writeOffData['type'] = $cart['type'];
$writeOffData['relation_id'] = $cart['relation_id'];
$writeOffData['product_id'] = $cart['product_id'];
$writeOffData['product_type'] = $cart['product_type'];
$writeOffData['writeoff_price'] = (float)bcmul((string)$info['truePrice'], (string)$writeOffData['writeoff_num'], 2);
$writeOffData['staff_id'] = $data['staff_id'] ?? 0;
if ($admin_id) {
$writeOffData['is_admin'] = 1;
$writeOffData['admin_id'] = $admin_id;
}
$writeOffDataAll[] = $writeOffData;
}
}
if ($writeOffDataAll) {
$this->writeoffdao->saveAll($writeOffDataAll);
}
return true;
}
/**
* 核销记录
* @param array $where
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function userOrderWriteOffRecords(array $where = [], int $product_type = 0)
{
[$page, $limit] = $this->getPageValue();
$count = 0;
$times = [];
if ($product_type == 4) {
$list = $this->writeoffdao->getList($where + ['product_type' => 4], '*', $page, $limit);
if ($list) {
foreach ($list as &$item) {
$item['add_time'] = $item['add_time'] ? date('Y-m-d H:i', (int)$item['add_time']) : '';
}
}
} else {
$list = $this->writeoffdao->getList($where, '*', $page, $limit, ['cartInfo']);
$count = $this->writeoffdao->count($where);
if ($list) {
foreach ($list as &$item) {
$item['time_key'] = $item['time'] = $item['add_time'] ? date('Y-m-d H:i', (int)$item['add_time']) : '';
$item['add_time'] = $item['add_time'] ? date('Y-m-d H:i', (int)$item['add_time']) : '';
$value = is_string($item['cartInfo']['cart_info']) ? json_decode($item['cartInfo']['cart_info'], true) : $item['cartInfo']['cart_info'];
$value['productInfo']['store_name'] = $value['productInfo']['store_name'] ?? "";
$value['productInfo']['store_name'] = substrUTf8($value['productInfo']['store_name'], 10, 'UTF-8', '');
$item['cartInfo'] = $value;
}
$times = array_merge(array_unique(array_column($list, 'time_key')));
}
}
return ['count' => $count, 'list' => $list, 'time' => $times];
}
/**
* 核销记录
* @param array $where
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* User: liusl
* DateTime: 2024/9/4 16:51
*/
public function getOrderWriteOffRecords(array $where = [])
{
$list = $this->writeoffdao->getList($where, '*', 0, 0, ['cartInfo']);
if ($list) {
$adminIds = array_unique(array_filter(array_column($list, 'admin_id')));
$staffIds = array_unique(array_filter(array_column($list, 'staff_id')));
$adminList = $staffList = [];
if ($adminIds) {
/** @var SystemAdminServices $adminServices */
$adminServices = app()->make(SystemAdminServices::class);
$adminList = $adminServices->getColumn(['id' => $adminIds], 'real_name', 'id',true);
}
if($staffIds){
/** @var StoreServiceServices $storeServiceServices */
$storeServiceServices = app()->make(StoreServiceServices::class);
$staffList = $storeServiceServices->getColumn(['id' => $staffIds], 'nickname', 'uid',true);
}
foreach ($list as &$item) {
$item['add_time'] = $item['add_time'] ? date('Y-m-d H:i', (int)$item['add_time']) : '';
$value = is_string($item['cartInfo']['cart_info']) ? json_decode($item['cartInfo']['cart_info'], true) : $item['cartInfo']['cart_info'];
$item['store_name'] = $value['productInfo']['store_name'] ?? "";
$item['image'] = $value['productInfo']['image'] ?? "";
$item['writeoff_name'] = $item['is_admin'] == 1 ? ($adminList[$item['admin_id']] ?? '') : ($staffList[$item['staff_id']] ?? '');
unset($item['cartInfo']);
}
}
return $list;
}
}

View File

@@ -0,0 +1,606 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\order\cashier;
use app\dao\order\StoreOrderDao;
use app\jobs\activity\StorePromotionsJob;
use app\jobs\user\MicroPayOrderJob;
use app\services\BaseServices;
use app\services\activity\coupon\StoreCouponUserServices;
use app\services\activity\coupon\StoreCouponIssueServices;
use app\services\order\StoreCartServices;
use app\services\order\StoreOrderCartInfoServices;
use app\services\order\StoreOrderComputedServices;
use app\services\order\StoreOrderCreateServices;
use app\services\order\StoreOrderSuccessServices;
use app\services\pay\PayServices;
use app\services\pay\YuePayServices;
use app\services\user\level\SystemUserLevelServices;
use app\services\user\level\UserLevelServices;
use app\services\user\UserAddressServices;
use app\services\user\UserInvoiceServices;
use app\services\user\UserServices;
use crmeb\services\CacheService;
use crmeb\traits\OptionTrait;
use think\annotation\Inject;
use think\exception\ValidateException;
/**
* 收银台订单
* Class CashierOrderServices
* @package app\services\order\cashier
*/
class CashierOrderServices extends BaseServices
{
/**
* @var StoreOrderDao
*/
#[Inject]
protected StoreOrderDao $dao;
use OptionTrait;
//余额支付
const YUE_PAY = 1;
//线上支付
const ONE_LINE_PAY = 2;
//现金支付
const CASH_PAY = 3;
/**
* 缓存订单信息
* @param int $uid
* @param array $cartInfo
* @param array $priceGroup
* @param array $other
* @param array $addr
* @param array $invalidCartInfo
* @param array $deduction
* @param int $cacheTime
* @return string
* @throws \Psr\SimpleCache\InvalidArgumentException
*/
public function cacheOrderInfo(int $uid, array $cartInfo, array $priceGroup, array $other = [], array $addr = [], array $invalidCartInfo = [], array $deduction = [], int $cacheTime = 600)
{
/** @var StoreOrderCreateServices $storeOrderCreateService */
$storeOrderCreateService = app()->make(StoreOrderCreateServices::class);
$key = md5($storeOrderCreateService->getNewOrderId((string)$uid) . substr(implode('', array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8));
CacheService::redisHandler()->set('admin_user_order_' . $uid . $key, compact('cartInfo', 'priceGroup', 'other', 'addr', 'invalidCartInfo', 'deduction'), $cacheTime);
return $key;
}
/**
* 获取订单缓存信息
* @param int $uid
* @param string $key
* @return |null
*/
public function getCacheOrderInfo(int $uid, string $key)
{
$cacheName = 'admin_user_order_' . $uid . $key;
if (!CacheService::redisHandler()->has($cacheName)) return null;
return CacheService::redisHandler()->get($cacheName);
}
/**
* 获取订单确认数据
* @param array $user
* @param $cartId
* @param bool $new
* @param int $addressId
* @param int $shipping_type
* @param int $store_id
* @param int $coupon_id
* @return array
* @throws \Psr\SimpleCache\InvalidArgumentException
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getOrderConfirmData(int $uid, $cartId, bool $new, int $addressId, int $shipping_type = 1, int $coupon_id = 0)
{
$addr = $data = $user = [];
if ($uid) {
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
$user = $userServices->getUserCacheInfo($uid);
}
/** @var UserAddressServices $addressServices */
$addressServices = app()->make(UserAddressServices::class);
if ($addressId) {
$addr = $addressServices->getAdderssCache($addressId);
}
//没传地址id或地址已删除未找到 ||获取默认地址
if (!$addr && $uid) {
$addr = $addressServices->getUserDefaultAddressCache($uid);
}
/** @var StoreCartServices $cartServices */
$cartServices = app()->make(StoreCartServices::class);
//获取购物车信息
$cartGroup = $cartServices->getUserProductCartListV1($uid, $cartId, $new, $addr, $shipping_type, $coupon_id);
$storeFreePostage = floatval(sys_config('store_free_postage')) ?: 0;//满额包邮金额
$data['storeFreePostage'] = $storeFreePostage;
$validCartInfo = $cartGroup['valid'];
$giveCartList = $cartGroup['giveCartList'] ?? [];
/** @var StoreOrderComputedServices $computedServices */
$computedServices = app()->make(StoreOrderComputedServices::class);
$priceGroup = $computedServices->getOrderPriceGroup($uid, $validCartInfo, $addr, $storeFreePostage);
$priceGroup['couponPrice'] = $cartGroup['couponPrice'] ?? 0;
$priceGroup['firstOrderPrice'] = $cartGroup['firstOrderPrice'] ?? 0;
$validCartInfo = array_merge($priceGroup['cartInfo'] ?? $validCartInfo, $giveCartList);
$other = [
'offlinePostage' => sys_config('offline_postage'),
'integralRatio' => sys_config('integral_ratio'),
'give_integral' => $cartGroup['giveIntegral'] ?? 0,
'give_coupon' => $cartGroup['giveCoupon'] ?? [],
'give_product' => $cartGroup['giveProduct'],
'promotions' => $cartGroup['promotions']
];
$deduction = $cartGroup['deduction'];
$data['product_type'] = $deduction['product_type'] ?? 0;
$data['valid_count'] = count($validCartInfo);
$data['addressInfo'] = $addr;
$data['type'] = $deduction['type'] ?? 0;
$data['activity_id'] = $deduction['activity_id'] ?? 0;
$data['seckill_id'] = $deduction['type'] == 1 ? $deduction['activity_id'] : 0;
$data['bargain_id'] = $deduction['type'] == 2 ? $deduction['activity_id'] : 0;
$data['combination_id'] = $deduction['type'] == 3 ? $deduction['activity_id'] : 0;
$data['storeIntegralId'] = $deduction['type'] == 4 ? $deduction['activity_id'] : 0;
$data['discount_id'] = $deduction['type'] == 5 ? $deduction['activity_id'] : 0;
$data['newcomer_id'] = $deduction['type'] == 7 ? $deduction['activity_id'] : 0;
$data['deduction'] = in_array($deduction['product_type'], [1, 2]) || $deduction['activity_id'] > 0;
$data['cartInfo'] = array_merge($cartGroup['cartInfo'], $giveCartList);
// $data['giveCartInfo'] = $giveCartList;
$data['custom_form'] = [];
$data['give_integral'] = $other['give_integral'];
$data['give_coupon'] = [];
if ($other['give_coupon']) {
/** @var StoreCouponIssueServices $couponIssueService */
$couponIssueService = app()->make(StoreCouponIssueServices::class);
$data['give_coupon'] = $couponIssueService->getColumn([['id', 'IN', $other['give_coupon']]], 'id,coupon_title');
}
$data['orderKey'] = $this->cacheOrderInfo($uid, $validCartInfo, $priceGroup, $other, $addr, $cartGroup['invalid'] ?? [], $deduction);
unset($priceGroup['cartInfo']);
$data['priceGroup'] = $priceGroup;
$userInfo = ['uid' => $user['uid'] ?? 0, 'nickname' => $user['nickname'] ?? '', 'avatar' => $user['avatar'] ?? '', 'phone' => $user['phone'] ?? '', 'now_money' => $user['now_money'] ?? 0, 'integral' => $user['integral'] ?? 0];
//会员
$userInfo['isMember'] = isset($user['is_money_level']) && $user['is_money_level'] > 0 ? 1 : 0;
//等级
$userInfo['level'] = $user['level'] ?? 0;
$userInfo['level_status'] = 0;
$userInfo['level_grade'] = '';
$userInfo['vip'] = isset($priceGroup['vipPrice']) && $priceGroup['vipPrice'] > 0;
$userInfo['vip_id'] = 0;
$userInfo['discount'] = 0;
//用户等级是否开启
if (sys_config('member_func_status', 1)) {
/** @var UserLevelServices $levelServices */
$levelServices = app()->make(UserLevelServices::class);
$userLevel = $levelServices->getUerLevelInfoByUid($uid);
if ($userInfo['vip'] || $userLevel) {
$userInfo['vip'] = true;
$userInfo['vip_id'] = $userLevel['id'] ?? 0;
$userInfo['discount'] = $userLevel['discount'] ?? 0;
}
if ($userInfo['level']) {
/** @var SystemUserLevelServices $levelServices */
$levelServices = app()->make(SystemUserLevelServices::class);
$levelInfo = $levelServices->getOne(['id' => $userInfo['level']], 'id,name,grade');
$userInfo['level_grade'] = $levelInfo['grade'] ?? '';
$userInfo['level_status'] = 1;
}
}
$userInfo['real_name'] = $user['real_name'] ?? $user['nickname'] ?? '';
$userInfo['record_pone'] = $user['record_pone'] ?? $user['phone'] ?? '';
$data['userInfo'] = $userInfo;
$data['offlinePostage'] = $other['offlinePostage'];
$data['integralRatio'] = $other['integralRatio'];
$data['integral_ratio_status'] = (int)(sys_config('integral_ratio_status', 1) && in_array($data['type'], [0, 6]));
$data['store_func_status'] = (int)(sys_config('store_func_status', 1));//门店是否开启
$data['store_self_mention'] = (int)sys_config('store_self_mention');
/** @var UserInvoiceServices $userInvoice */
$userInvoice = app()->make(UserInvoiceServices::class);
$invoice_func = $userInvoice->invoiceFuncStatus();
$data['invoice_func'] = $invoice_func['invoice_func'];
$data['special_invoice'] = $invoice_func['special_invoice'];
return $data;
}
/**
* 计算订单金额
* @param int $uid
* @param array $userInfo
* @param array $cartGroup
* @param int $addressId
* @param string $payType
* @param bool $useIntegral
* @param int $couponId
* @param int $shippingType
* @return array
*/
public function computeOrder(int $uid, array $cartGroup, int $addressId, string $payType, bool $useIntegral = false, int $shippingType = 1)
{
$userInfo = [];
if ($uid) {
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
$userInfo = $userServices->getUserCacheInfo($uid);
if (!$userInfo) {
throw new ValidateException('用户不存在!');
}
}
$cartInfo = $cartGroup['cartInfo'];
$priceGroup = $cartGroup['priceGroup'];
$deduction = $cartGroup['deduction'];
$other = $cartGroup['other'];
$promotions = $other['promotions'] ?? [];
$payPrice = (float)$priceGroup['totalPrice'];
$payIntegral = (int)$priceGroup['totalIntegral'] ?? 0;
$couponPrice = (float)$priceGroup['couponPrice'];
$firstOrderPrice = (float)$priceGroup['firstOrderPrice'];
$type = (int)$deduction['type'] ?? 0;
/** @var StoreOrderComputedServices $computeOrderService */
$computeOrderService = app()->make(StoreOrderComputedServices::class);
$promotionsPrice = $computeOrderService->getOrderSumPrice($cartInfo, 'promotions_true_price');//优惠活动优惠
$promotionsDetail = [];
if ($promotions) {
foreach ($promotions as $key => $value) {
if (isset($value['details']['sum_promotions_price']) && $value['details']['sum_promotions_price']) {
$promotionsDetail[] = ['id' => $value['id'], 'name' => $value['name'], 'title' => $value['title'], 'desc' => $value['desc'], 'promotions_price' => $value['details']['sum_promotions_price'], 'promotions_type' => $value['promotions_type']];
}
}
if ($promotionsDetail) {
$typeArr = array_column($promotionsDetail, 'promotions_type');
array_multisort($typeArr, SORT_ASC, $promotionsDetail);
}
}
$addr = $cartGroup['addr'] ?? [];
$postage = $priceGroup;
if (!$addr || $addr['id'] != $addressId) {
/** @var UserAddressServices $addressServices */
$addressServices = app()->make(UserAddressServices::class);
$addr = $addressServices->getAdderssCache($addressId);
//改变地址重新计算邮费
$postage = [];
}
[$p, $payPostage, $storePostageDiscount, $storeFreePostage, $isStoreFreePostage] = $computeOrderService->computedPayPostage($uid, $shippingType, $payType, $cartInfo, $addr, $payPrice, $postage, $other);
if ($couponPrice < $payPrice) {//优惠券金额
$payPrice = bcsub((string)$payPrice, (string)$couponPrice, 2);
} else {
$payPrice = 0;
}
if ($firstOrderPrice < $payPrice) {//首单优惠金额
$payPrice = bcsub((string)$payPrice, (string)$firstOrderPrice, 2);
} else {
$payPrice = 0;
}
if (sys_config('integral_ratio_status') && in_array($type, [0, 6])) {
//使用积分
[$payPrice, $deductionPrice, $usedIntegral, $SurplusIntegral] = $computeOrderService->useIntegral($useIntegral, $userInfo, $payPrice, $other);
}
$payPrice = (float)bcadd((string)$payPrice, (string)$payPostage, 2);
return [
'total_price' => $priceGroup['totalPrice'],
'pay_price' => max($payPrice, 0),
'pay_integral' => max($payIntegral, 0),
'total_postage' => bcadd((string)$payPostage, (string)($storePostageDiscount ?? 0), 2),
'pay_postage' => $payPostage,
'first_order_price' => $firstOrderPrice ?? 0,
'coupon_price' => $couponPrice ?? 0,
'promotions_price' => $promotionsPrice,
'promotions_detail' => $promotionsDetail,
'deduction_price' => $deductionPrice ?? 0,
'usedIntegral' => $usedIntegral ?? 0,
'SurplusIntegral' => $SurplusIntegral ?? 0,
'storePostageDiscount' => $storePostageDiscount ?? 0,
'isStoreFreePostage' => $isStoreFreePostage ?? false,
'storeFreePostage' => $storeFreePostage ?? 0,
// 'cartInfo' => $cartInfo
];
}
/**
* 收银台用户优惠券
* @param int $uid
* @param int $storeId
* @param array $cartIds
* @return mixed
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getCouponList(int $uid, int $storeId, array $cartIds)
{
/** @var StoreCartServices $cartService */
$cartService = app()->make(StoreCartServices::class);
$cart = $cartService->getUserCartList($uid, 1, $cartIds, $storeId, 0, 4, 0, 0, 0, false);
$cartInfo = $cart['valid'];
if (!$cartInfo) {
throw new ValidateException('购物车暂无货物!');
}
/** @var StoreCouponissueServices $couponIssueServices */
$couponIssueServices = app()->make(StoreCouponissueServices::class);
return $couponIssueServices->getCanUseCoupon($uid, $cartInfo, $cart['promotions'] ?? [], $storeId, false);
}
/**
* 生成订单
* @param int $uid
* @param array $userInfo
* @param array $computeData
* @param int $staffId
* @param array $cartIds
* @param string $payType
* @param bool $integral
* @param bool $coupon
* @param string $remarks
* @param string $changePrice
* @param bool $isPrice
* @param int $coupon_id
* @param int $seckillId
* @param $collate_code_id
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function createOrder(int $uid, string $key, array $cartGroup, int $addressId, string $payType, array $addressInfo, int $staffId, bool $useIntegral = false, $couponId = 0, $mark = '', $shippingType = 1, string $from = '')
{
//兼容门店虚拟用户下单
$field = ['real_name', 'phone', 'province', 'city', 'district', 'street', 'detail'];
$userInfo = [];
if ($uid) {
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
$userInfo = $userServices->getUserCacheInfo($uid);
if (!$userInfo) {
throw new ValidateException('用户不存在!');
}
$userInfo = $userInfo->toArray();
}
if (!$addressInfo) {
foreach ($field as $key) {
$addressInfo[$key] = '';
}
}
/** @var StoreOrderComputedServices $computedServices */
$computedServices = app()->make(StoreOrderComputedServices::class);
$priceData = $computedServices->computedOrder($uid, $userInfo, $cartGroup, $addressId, $payType, $useIntegral, $couponId, $shippingType);
$cartInfo = $cartGroup['cartInfo'];
$priceGroup = $cartGroup['priceGroup'];
$cartIds = [];
$totalNum = 0;
$gainIntegral = 0;
foreach ($cartInfo as $cart) {
$cartIds[] = $cart['id'];
$totalNum += $cart['cart_num'];
$cartInfoGainIntegral = isset($cart['productInfo']['give_integral']) ? bcmul((string)$cart['cart_num'], (string)$cart['productInfo']['give_integral'], 0) : 0;
$gainIntegral = bcadd((string)$gainIntegral, (string)$cartInfoGainIntegral, 0);
}
$cartInfo = $cartGroup['cartInfo'];
$deduction = $cartGroup['deduction'];
$promotions_give = [
'give_integral' => $cartGroup['give_integral'] ?? 0,
'give_coupon' => $cartGroup['giveCoupon'] ?? [],
'give_product' => $cartGroup['giveProduct'] ?? [],
'promotions' => $cartGroup['promotions'] ?? []
];
$type = (int)$deduction['type'] ?? 0;
/** @var StoreOrderCreateServices $orderServices */
$orderServices = app()->make(StoreOrderCreateServices::class);
$product_type = (int)$deduction['product_type'] ?? 0;
$orderInfo = [
'uid' => $uid,
'type' => $type,
'order_id' => $orderServices->getNewOrderId(),
'real_name' => $addressInfo['real_name'] ?: $userInfo['nickname'] ?? '',
'user_phone' => $addressInfo['phone'] ?: $userInfo['phone'] ?? '',
'user_address' => isset($addressInfo['addressInfo']) && $addressInfo['addressInfo'] ? $addressInfo['addressInfo'] : $addressInfo['province'] . ' ' . $addressInfo['city'] . ' ' . $addressInfo['district'] . ' ' . $addressInfo['street'] . ' ' . $addressInfo['detail'],
'cart_id' => $cartIds,
'clerk_id' => 0,
'staff_id' => $staffId,
'total_num' => $totalNum,
'total_price' => $priceGroup['sumPrice'] ?? $priceGroup['totalPrice'],
'total_postage' => $priceData['total_postage'] ?? $priceGroup['storePostage'],
'coupon_id' => $couponId,
'coupon_price' => $priceData['coupon_price'],
'first_order_price' => $priceData['first_order_price'],
'promotions_price' => $priceData['promotions_price'],
'pay_integral' => $priceData['pay_integral'],
'pay_price' => $priceData['pay_price'],
'pay_postage' => $priceData['pay_postage'],
'deduction_price' => $priceData['deduction_price'],
'paid' => 0,
'pay_type' => $payType,
'use_integral' => $priceData['usedIntegral'],
'gain_integral' => $gainIntegral,
'mark' => htmlspecialchars($mark),
'product_type' => $product_type,
'activity_id' => 0,
'pink_id' => 0,
'cost' => $priceGroup['costPrice'],
'is_channel' => 2,
'unique' => $key,
'add_time' => time(),
'shipping_type' => $shippingType,
'channel_type' => $userInfo['user_type'] ?? '',
'province' => '',
'spread_uid' => 0,
'spread_two_uid' => 0,
'promotions_give' => json_encode($promotions_give),
'give_integral' => $promotions_give['give_integral'] ?? 0,
'give_coupon' => implode(',', $promotions_give['give_coupon'] ?? []),
'division_id' => $userInfo['division_id'] ?? 0,
'division_agent_id' => $userInfo['agent_id'] ?? 0,
'division_staff_id' => $userInfo['staff_id'] ?? 0,
];
if ($product_type == 4 || $shippingType == 2) {//次卡商品收银台购买
$orderInfo['verify_code'] = $orderServices->getStoreCode();
$orderInfo['shipping_type'] = 2;//修改门店自提
}
$order = $this->transaction(function () use ($key, $cartInfo, $type, $priceData, $orderInfo, $orderServices, $couponId, $userInfo, $useIntegral, $promotions_give, $payType) {
/** @var StoreOrderCartInfoServices $cartServices */
$cartServices = app()->make(StoreOrderCartInfoServices::class);
$order = $orderServices->save($orderInfo);
if (!$order) {
throw new ValidateException('订单生成失败');
}
//使用优惠券
if ($couponId) {
/** @var StoreCouponUserServices $couponServices */
$couponServices = app()->make(StoreCouponUserServices::class);
$res1 = $couponServices->useCoupon($couponId, (int)($userInfo['uid'] ?? 0), $cartInfo, []);
if (!$res1) {
throw new ValidateException('使用优惠劵失败!');
}
}
//积分抵扣
$orderServices->deductIntegral($userInfo, $useIntegral, $priceData, (int)($userInfo['uid'] ?? 0), $key);
//修改门店库存
$orderServices->decGoodsStock($cartInfo, $type);
//保存购物车商品信息
$cartServices->setCartInfo($order['id'], $cartInfo, $userInfo['uid'] ?? 0, $promotions_give['promotions'] ?? []);
return $order->toArray();
});
$news = false;
$addressId = $type = $activity_id = 0;
$delCart = true;
//扣除优惠活动赠品限量
StorePromotionsJob::dispatchDo('changeGiveLimit', [$promotions_give]);
$group = compact('cartInfo', 'priceData', 'addressId', 'cartIds', 'news', 'delCart');
$orderServices->delCart($group);
$change_manager_id = $staffId;
$change_manager_type = 'store';
event('order.create', [$order, $userInfo, $group, compact('type', 'activity_id', 'change_manager_id', 'change_manager_type'), 0]);
return $order;
}
/**
* 收银台支付
* @param string $orderId
* @param string $payType
* @param string $userCode
* @param string $authCode
* @return array
* @throws \Psr\SimpleCache\InvalidArgumentException
*/
public function paySuccess(string $orderId, string $payType, string $userCode, string $authCode = '')
{
/** @var StoreOrderSuccessServices $orderService */
$orderService = app()->make(StoreOrderSuccessServices::class);
$orderInfo = $orderService->get(['order_id' => $orderId]);
if (!$orderInfo) {
throw new ValidateException('没有查询到订单信息');
}
if ($orderInfo->paid) {
throw new ValidateException('订单已支付');
}
if ($orderInfo->is_del) {
throw new ValidateException('订单已取消');
}
switch ($payType) {
case PayServices::YUE_PAY://余额支付
$is_cashier_yue_pay_verify = (int)sys_config('is_cashier_yue_pay_verify'); // 收银台余额支付是否需要验证【是/否】
if (!$orderInfo['uid']) {
throw new ValidateException('余额支付用户信息不存在无法支付');
}
if (!$userCode && $is_cashier_yue_pay_verify) {
throw new ValidateException('缺少扫码支付参数');
}
/** @var UserServices $userService */
$userService = app()->make(UserServices::class);
$userInfo = $userService->getUserInfo($orderInfo->uid, ['uid', 'rand_code']);
//读取缓存用户code
$rand_code = CacheService::redisHandler()->get('user_rand_code' . $orderInfo->uid);
CacheService::redisHandler()->delete('user_rand_code' . $orderInfo->uid);
if (!$userInfo) {
throw new ValidateException('余额支付用户不存在');
}
if ($rand_code != $userCode && $is_cashier_yue_pay_verify) {
throw new ValidateException('二维码已使用或不正确,请确认后重新扫码');
}
/** @var YuePayServices $payService */
$payService = app()->make(YuePayServices::class);
$pay = $payService->yueOrderPay($orderInfo->toArray(), $orderInfo->uid);
if ($pay['status'] === true)
return ['status' => 'SUCCESS'];
else if ($pay['status'] === 'pay_deficiency') {
throw new ValidateException($pay['msg']);
} else {
return ['status' => 'ERROR', 'message' => is_array($pay) ? $pay['msg'] ?? '余额支付失败' : $pay];
}
case PayServices::WEIXIN_PAY://微信支付
case PayServices::ALIPAY_PAY://支付宝支付
if (!$authCode) {
throw new ValidateException('缺少支付付款二维码CODE');
}
$pay = new PayServices();
/** @var StoreOrderCartInfoServices $orderInfoServices */
$orderInfoServices = app()->make(StoreOrderCartInfoServices::class);
$body = $orderInfoServices->getCarIdByProductTitle((int)$orderInfo['id']);
$body = substrUTf8($body, 30);
try {
//扫码支付
$response = $pay->setAuthCode($authCode)->pay($payType, '', $orderInfo->order_id, $orderInfo->pay_price, 'product', $body);
} catch (\Throwable $e) {
\think\facade\Log::error('收银端' . $payType . '扫码支付失败,原因:' . $e->getMessage());
return ['status' => 'ERROR', 'message' => '支付失败,原因:' . $e->getMessage()];
}
//支付成功paid返回1
if ($response['paid']) {
if (!$orderService->paySuccess($orderInfo->toArray(), $payType, ['trade_no' => $response['payInfo']['transaction_id'] ?? ''])) {
return ['status' => 'ERROR', 'message' => '支付失败'];
}
//支付成功刪除購物車
/** @var StoreCartServices $cartServices */
$cartServices = app()->make(StoreCartServices::class);
$cartServices->deleteCartStatus($orderInfo['cart_id'] ?? []);
return ['status' => 'SUCCESS'];
} else {
if ($payType === PayServices::WEIXIN_PAY) {
if (isset($response['payInfo']['err_code']) && in_array($response['payInfo']['err_code'], ['AUTH_CODE_INVALID', 'NOTENOUGH'])) {
return ['status' => 'ERROR', 'message' => '支付失败'];
}
//微信付款码支付需要同步更改状态
$secs = 5;
if (isset($order_info['payInfo']['err_code']) && $order_info['payInfo']['err_code'] === 'USERPAYING') {
$secs = 10;
}
//放入队列执行
MicroPayOrderJob::dispatchSece($secs, [$orderInfo['order_id'], 0]);
}
return ['status' => 'PAY_ING', 'message' => $response['message']];
}
break;
case PayServices::CASH_PAY://收银台现金支付
if (!$orderService->paySuccess($orderInfo->toArray(), $payType)) {
return ['status' => 'ERROR', 'message' => '支付失败'];
} else {
return ['status' => 'SUCCESS'];
}
break;
default:
throw new ValidateException('暂无支付方式,无法支付');
}
}
}

View File

@@ -0,0 +1,294 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\order\store;
use app\dao\order\StoreOrderDao;
use app\services\BaseServices;
use app\services\order\OtherOrderServices;
use app\services\order\StoreOrderRefundServices;
use app\services\order\StoreOrderServices;
use app\services\order\StoreOrderStatusServices;
use app\services\user\UserRechargeServices;
use think\annotation\Inject;
use think\exception\ValidateException;
/**
* Class StoreOrderWapServices
* @package app\services\order
* @mixin StoreOrderDao
*/
class BranchOrderServices extends BaseServices
{
/**
* @var StoreOrderDao
*/
#[Inject]
protected StoreOrderDao $dao;
/**
* 获取订单数量
* @param int $store_id
* @param int $staff_id
* @return array
*/
public function getOrderData(int $store_id, int $staff_id = 0)
{
$where = ['pid' => 0, 'refund_status' => [0, 3], 'is_del' => 0, 'is_system_del' => 0];
$data['order_count'] = (string)$this->dao->count($where);
$where = $where + ['paid' => 1];
$data['sum_price'] = (string)$this->dao->sum($where, 'pay_price', true);
$countWhere = ['store_id' => $store_id];
if ($staff_id) {
$countWhere['staff_id'] = $staff_id;
}
$pid_where = ['pid' => 0];
$not_pid_where = ['not_pid' => 1];
$data['unpaid_count'] = (string)$this->dao->count(['status' => 0] + $countWhere + $pid_where);
$data['unshipped_count'] = (string)$this->dao->count(['status' => 1] + $countWhere + $pid_where);
$data['unwriteoff_count'] = (string)$this->dao->count(['status' => 5] + $countWhere + $pid_where);
$data['received_count'] = (string)$this->dao->count(['status' => 2] + $countWhere + $pid_where);
$data['evaluated_count'] = (string)$this->dao->count(['status' => 3] + $countWhere + $pid_where);
$data['complete_count'] = (string)$this->dao->count(['status' => 4] + $countWhere + $pid_where);
/** @var StoreOrderRefundServices $storeOrderRefundServices */
$storeOrderRefundServices = app()->make(StoreOrderRefundServices::class);
$refund_where = ['is_cancel' => 0];
$data['refunding_count'] = (string)$storeOrderRefundServices->count($refund_where + ['refund_type' => [0, 1, 2, 4, 5]]);
$data['refunded_count'] = (string)$storeOrderRefundServices->count($refund_where + ['refund_type' => [3, 6]]);
$data['refund_count'] = (string)$storeOrderRefundServices->count($refund_where);
return $data;
}
/**
* 订单统计详情列表
* @param int $store_id
* @param int $staff_id
* @param int $type
* @param array $time
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function time(int $store_id, int $staff_id = 0, int $type = 1, array $time = [])
{
if (!$time) {
return [[], []];
}
[$start, $stop, $front, $front_stop] = $time;
$order_where = ['pid' => 0, 'is_del' => 0, 'paid' => 1];
if ($type != 3) {
$order_where['refund_status'] = [0, 3];
}
if ($staff_id) $order_where['staff_id'] = $staff_id;
switch ($type) {
case 1://配送
case 2://配送
$order_where['type'] = 7;
break;
case 3://退款
$order_where['status'] = -3;
break;
case 4://收银订单
$order_where['type'] = 6;
break;
case 5://核销
$order_where['type'] = 5;
break;
}
if ($type == 2) {//数量
$frontPrice = $this->dao->count($order_where + ['time' => [$front, $front_stop]]);
$nowPrice = $this->dao->count($order_where + ['time' => [$start, $stop]]);
} else {//金额
$frontPrice = $this->dao->sum($order_where + ['time' => [$front, $front_stop]], 'pay_price', true);
$nowPrice = $this->dao->sum($order_where + ['time' => [$start, $stop]], 'pay_price', true);
}
[$page, $limit] = $this->getPageValue();
$list = $this->dao->getOrderList($order_where + ['time' => [$start, $stop]], ['id', 'order_id', 'uid', 'spread_uid', 'pay_price', 'add_time'], $page, $limit);
foreach ($list as &$item) {
$item['add_time'] = $item['add_time'] ? date('Y-m-d H:i:s', $item['add_time']) : '';
}
return [[$nowPrice, $frontPrice], $list];
}
/**
* 订单每月统计数据(按天分组)
* @param array $where
* @param array|string[] $field
* @return array
*/
public function getOrderDataPriceCount(array $where, array $field = ['sum(pay_price) as price', 'count(id) as count', 'FROM_UNIXTIME(add_time, \'%m-%d\') as time'])
{
[$page, $limit] = $this->getPageValue();
$order_where = ['is_del' => 0, 'is_system_del' => 0, 'paid' => 1, 'refund_status' => [0, 3]];
$where = array_merge($where, $order_where);
return $this->dao->getOrderDataPriceCount($where, $field, $page, $limit);
}
/**
* 获取订单列表
* @param array $where
* @param array $with
* @param false $is_count
* @return array|null
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getStoreOrderList(array $where, array $field = ['*'], array $with = [], $is_count = false)
{
[$page, $limit] = $this->getPageValue();
$list = $this->dao->getOrderList($where, $field, $page, $limit, $with, 'id desc');
if ($is_count) {
$count = $this->dao->count($where);
return compact('list', 'count');
}
/** @var StoreOrderServices $orderServices */
$orderServices = app()->make(StoreOrderServices::class);
$list = $orderServices->tidyOrderList($list);
foreach ($list as &$item) {
$refund_num = array_sum(array_column($item['refund'], 'refund_num'));
$cart_num = 0;
foreach ($item['_info'] as $items) {
if (isset($items['cart_info']['is_gift']) && $items['cart_info']['is_gift']) continue;
$cart_num += $items['cart_info']['cart_num'];
}
$item['is_all_refund'] = $refund_num == $cart_num;
}
return $list;
}
/**
* 取消订单
* @param $id
* @param int $store_id
* @return bool
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function cancelOrder($id, int $store_id = 0)
{
$where = ['id' => $id, 'is_del' => 0];
$order = $this->dao->getOne($where);
if (!$order) {
throw new ValidateException('没有查到此订单');
}
if ($order->paid) {
throw new ValidateException('订单已经支付无法取消');
}
/** @var StoreOrderRefundServices $refundServices */
$refundServices = app()->make(StoreOrderRefundServices::class);
$this->transaction(function () use ($refundServices, $order) {
//回退积分和优惠卷
$res = $refundServices->integralAndCouponBack($order);
//回退库存和销量
$res = $res && $refundServices->regressionStock($order);
$order->is_del = 1;
if (!($res && $order->save())) {
throw new ValidateException('取消订单失败');
}
});
return true;
}
/**
* 删除订单
* @param $uni
* @param $uid
* @return bool
*/
public function removeOrder(string $uni, int $uid)
{
$order = $this->getUserOrderDetail($uni, $uid);
if (!$order) {
throw new ValidateException('订单不存在!');
}
$order = $this->tidyOrder($order);
if ($order['_status']['_type'] != 0 && $order['_status']['_type'] != -2 && $order['_status']['_type'] != 4)
throw new ValidateException('该订单无法删除!');
$order->is_del = 1;
/** @var StoreOrderStatusServices $statusService */
$statusService = app()->make(StoreOrderStatusServices::class);
$res = $statusService->save([
'oid' => $order['id'],
'change_type' => 'remove_order',
'change_message' => '删除订单',
'change_time' => time()
]);
if ($order->save() && $res) {
//未支付和已退款的状态下才可以退积分退库存退优惠券
if ($order['_status']['_type'] == 0 || $order['_status']['_type'] == -2) {
/** @var StoreOrderRefundServices $refundServices */
$refundServices = app()->make(StoreOrderRefundServices::class);
$this->transaction(function () use ($order, $refundServices) {
//回退积分和优惠卷
$res = $refundServices->integralAndCouponBack($order);
//回退库存
$res = $res && $refundServices->regressionStock($order);
if (!$res) {
throw new ValidateException('取消订单失败!');
}
});
}
return true;
} else
throw new ValidateException('订单删除失败!');
}
/**
* 首页交易统计
* @param int $store_id
* @param array $time
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function orderChart(int $store_id, array $time)
{
$chartdata = [];
$where = ['time' => $time, 'pid' => 0];
if ($store_id) $where['store_id'] = $store_id;
$order_where = ['paid' => 1, 'pid' => 0, 'is_system_del' => 0, 'refund_status' => [0, 3]];
$list = $this->dao->getOrderList($where + $order_where, ['id', 'order_id', 'uid', 'pay_price', 'pay_time'], 0, 10);
$chartdata['order_list'] = $list;
$chartdata['bing_xdata'] = ['收银订单', '充值订单', '分配订单', '核销订单', '付费会员订单'];
$color = ['#2EC479', '#7F7AE5', '#FFA21B', '#46A3FF', '#FF6046'];
//收银订单
$pay[] = $this->dao->sum(['type' => 106] + $order_where + $where, 'pay_price', true);
/** @var UserRechargeServices $userRecharge */
$userRecharge = app()->make(UserRechargeServices::class);
$pay[] = $userRecharge->sum(['paid' => 1] + $where, 'price', true);
//分配订单
$pay[] = $this->dao->sum(['type' => 107] + $order_where + $where, 'pay_price', true);
//核销订单
$pay[] = $this->dao->sum(['type' => 105] + $order_where + $where, 'pay_price', true);
/** @var OtherOrderServices $vipOrderServices */
$vipOrderServices = app()->make(OtherOrderServices::class);
$pay[] = $vipOrderServices->sum(['paid' => 1, 'type' => [0, 1, 2, 4]] + $where, 'pay_price', true);
foreach ($pay as $key => $item) {
$bing_data[] = ['name' => $chartdata['bing_xdata'][$key], 'value' => $pay[$key], 'itemStyle' => ['color' => $color[$key]]];
}
$chartdata['bing_data'] = $bing_data;
return $chartdata;
}
}

View File

@@ -0,0 +1,423 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\order\store;
use app\dao\order\StoreOrderDao;
use app\services\message\service\StoreServiceServices;
use app\services\order\StoreOrderCartInfoServices;
use app\services\order\StoreOrderCreateServices;
use app\services\order\StoreOrderRefundServices;
use app\services\order\StoreOrderTakeServices;
use app\services\activity\combination\StorePinkServices;
use app\services\BaseServices;
use app\services\store\SystemStoreStaffServices;
use app\services\store\DeliveryServiceServices;
use app\services\user\UserServices;
use crmeb\services\FormBuilder as Form;
use think\annotation\Inject;
use think\exception\ValidateException;
/**
* 核销订单
* Class StoreOrderWriteOffServices
* @package app\sservices\order
* @mixin StoreOrderDao
*/
class WriteOffOrderServices extends BaseServices
{
/**
* @var StoreOrderDao
*/
#[Inject]
protected StoreOrderDao $dao;
/**
* 验证核销订单权限
* @param int $uid
* @param array $orderInfo
* @param int $auth
* @param int $staff_id
* @return bool
*/
public function checkAuth(int $uid, array $orderInfo, int $auth = 1)
{
if (($auth > 0 && !$uid) || !$orderInfo) {
throw new ValidateException('订单不存在');
}
$info = $this->checkUserAuth($uid, $auth);
$isAuth = false;
switch ($auth) {
case 0://平台
$isAuth = true;
break;
case 1://前端客服
if (isset($info['user_service']) && $info['user_service']) {
$isAuth = true;
}
break;
case 2://配送员
if (in_array($orderInfo['shipping_type'], [1, 3]) && $info && $orderInfo['delivery_type'] == 'send' && $orderInfo['delivery_uid'] == $uid) {
$isAuth = true;
}
break;
}
if (!$isAuth) {
throw new ValidateException('您无权限核销此订单,请联系管理员');
}
return true;
}
/**
* 验证核销权限
* @param int $uid
* @param int $auth
* @return array|\think\Model
*/
public function checkUserAuth(int $uid, int $auth = 1)
{
if ($auth > 0 && !$uid) {
throw new ValidateException('用户不存在');
}
$isAuth = false;
$info = [];
switch ($auth) {
case 0://平台客服
$isAuth = true;
break;
case 1://前端客服
/** @var StoreServiceServices $storeService */
$storeService = app()->make(StoreServiceServices::class);
$userService = $storeService->checkoutIsService(['uid' => $uid, 'status' => 1, 'account_status' => 1, 'customer' => 1]);
if ($userService) {
$isAuth = true;
$info['user_service'] = $userService;
}
break;
case 2://配送员
/** @var DeliveryServiceServices $deliverServiceServices */
$deliverServiceServices = app()->make(DeliveryServiceServices::class);
try {
$info = $deliverServiceServices->getDeliveryInfoByUid($uid, 0);
} catch (\Throwable $e) {
}
if ($info) {
$isAuth = true;
}
break;
}
if (!$isAuth) {
throw new ValidateException('您无权限核销,请联系超级管理员');
}
return $info;
}
/**
* 用户码获取待核销订单列表
* @param int $uid
* @param string $code
* @param int $auth
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function userUnWriteoffOrder(int $uid, string $code, int $auth = 1, array $userInfo = [])
{
if (!$userInfo) {
/** @var UserServices $userServices */
$userServices = app()->make(UserServices::class);
$userInfo = $userServices->getOne(['bar_code' => $code]);
}
if (!$userInfo) {
throw new ValidateException('该用户不存在');
}
$info = $this->checkUserAuth($uid, $auth);
$where = [];
if ($info && $auth > 0) {
if ($auth == 1 && isset($info['user_service']) && $info['user_service']) {
$where = ['delivery_uid' => 0];
} elseif ($auth == 2) {
$where = ['delivery_uid' => $info['uid']];
}
$unWriteoffOrder = $this->dao->getUnWirteOffList(['uid' => $userInfo['uid']] + $where, ['id']);
} else {
$unWriteoffOrder = $this->dao->getUnWirteOffList(['uid' => $userInfo['uid']], ['id']);
}
$data = [];
if ($unWriteoffOrder) {
foreach ($unWriteoffOrder as $item) {
try {
$orderInfo = $this->writeoffOrderInfo($uid, '', $auth, $item['id']);
} catch (\Throwable $e) {//无权限或其他异常不返回订单信息
$orderInfo = [];
}
if ($orderInfo) $data[] = $orderInfo;
}
}
return $data;
}
/**
* 获取核销订单信息
* @param int $uid
* @param string $code
* @param int $auth
* @param int $oid
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function writeoffOrderInfo(int $uid, string $code = '', int $auth = 1, int $oid = 0, int $staff_id = 0)
{
if ($oid) {
//订单
$orderInfo = $this->dao->getOne(['id' => $oid, 'is_del' => 0], '*', ['user', 'pink']);
} else {
//订单
$orderInfo = $this->dao->getOne(['verify_code' => $code, 'is_del' => 0], '*', ['user', 'pink']);
}
if (!$orderInfo) {
throw new ValidateException('Write off order does not exist');
}
if (!$orderInfo['paid']) {
throw new ValidateException('订单还未完成支付');
}
if ($orderInfo['refund_status'] != 0) {
throw new ValidateException('该订单状态暂不支持核销');
}
$orderInfo = $orderInfo->toArray();
//验证权限
$this->checkAuth($uid, $orderInfo, $auth);
/** @var StoreOrderCartInfoServices $cartServices */
$cartServices = app()->make(StoreOrderCartInfoServices::class);
$cartInfo = $cartServices->getCartInfoList(['oid' => $orderInfo['id']], ['id', 'oid', 'write_times', 'write_surplus_times', 'write_start', 'write_end']);
$orderInfo['write_off'] = $orderInfo['write_times'] = 0;
$orderInfo['write_day'] = '';
$cart = $cartInfo[0] ?? [];
if ($orderInfo['product_type'] == 4 && $cart) {//次卡商品
$orderInfo['write_off'] = max(bcsub((string)$cart['write_times'], (string)$cart['write_surplus_times'], 0), 0);
$orderInfo['write_times'] = $cart['write_times'] ?? 0;
$start = $cart['write_start'] ?? 0;
$end = $cart['write_end'] ?? 0;
if (!$start && !$end) {
$orderInfo['write_day'] = '不限时';
} else {
$orderInfo['write_day'] = ($start ? date('Y-m-d', $start) : '') . '/' . ($end ? date('Y-m-d', $end) : '');
}
}
return $orderInfo;
}
/**
* 获取订单商品信息
* @param int $uid
* @param int $id
* @param int $auth
* @param int $staff_id
* @param bool $isCasher
* @return array|\think\Model|null
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getOrderCartInfo(int $uid, int $id, int $auth = 1, int $staff_id = 0, bool $isCasher = false)
{
if ($isCasher) {//获取订单信息 暂时不验证权限
$orderInfo = $this->dao->getOne(['id' => $id], '*', ['user', 'pink']);
if (!$orderInfo) {
throw new ValidateException('Write off order does not exist');
}
$orderInfo = $orderInfo->toArray();
} else {
$orderInfo = $this->writeoffOrderInfo($uid, '', $auth, $id, $staff_id);
}
$writeoff_count = 0;
/** @var StoreOrderCartInfoServices $cartInfoServices */
$cartInfoServices = app()->make(StoreOrderCartInfoServices::class);
$cartInfo = $cartInfoServices->getCartColunm(['oid' => $orderInfo['id']], 'id,cart_id,cart_num,surplus_num,is_writeoff,cart_info,product_type,is_support_refund,is_gift,write_times,write_surplus_times');
foreach ($cartInfo as &$item) {
$_info = is_string($item['cart_info']) ? json_decode($item['cart_info'], true) : $item['cart_info'];
if (!isset($_info['productInfo'])) $_info['productInfo'] = [];
//缩略图处理
if (isset($_info['productInfo']['attrInfo'])) {
$_info['productInfo']['attrInfo'] = get_thumb_water($_info['productInfo']['attrInfo']);
}
$_info['productInfo'] = get_thumb_water($_info['productInfo']);
$item['cart_info'] = $_info;
if ($item['write_times'] > $item['write_surplus_times']) {
$writeoff_count = bcadd((string)$writeoff_count, (string)bcsub((string)$item['write_times'], (string)$item['write_surplus_times']));
}
$item['surplus_num'] = $item['write_surplus_times'];
unset($_info);
}
$orderInfo['cart_count'] = count($cartInfo);
$orderInfo['writeoff_count'] = $writeoff_count;
$orderInfo['cart_info'] = $cartInfo;
return $orderInfo;
}
/**
* 核销订单
* @param int $uid
* @param array $orderInfo
* @param array $cartIds
* @param int $auth
* @return array|null
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function writeoffOrder(int $uid, array $orderInfo, array $cartIds = [], int $auth = 1, int $staff_id = 0, int $admin_id = 0)
{
if (!$orderInfo) {
throw new ValidateException('订单不存在');
}
$time = time();
if (!$orderInfo['verify_code'] || ($orderInfo['shipping_type'] != 2 && $orderInfo['delivery_type'] != 'send')) {
throw new ValidateException('此订单不能被核销');
}
/** @var StoreOrderRefundServices $storeOrderRefundServices */
$storeOrderRefundServices = app()->make(StoreOrderRefundServices::class);
if ($storeOrderRefundServices->count(['store_order_id' => $orderInfo['id'], 'refund_type' => [1, 2, 4, 5, 6], 'is_cancel' => 0, 'is_del' => 0])) {
throw new ValidateException('订单有售后申请请先处理');
}
if (isset($orderInfo['pinkStatus']) && $orderInfo['pinkStatus'] != 2) {
throw new ValidateException('拼团未完成暂不能发货!');
}
/** @var StoreOrderCartInfoServices $cartInfoServices */
$cartInfoServices = app()->make(StoreOrderCartInfoServices::class);
if ($orderInfo['status'] >= 2 && !$cartInfoServices->count(['oid' => $orderInfo['id'], 'is_writeoff' => 0])) {
throw new ValidateException('订单已核销');
}
$store_id = $orderInfo['store_id'];
if ($orderInfo['type'] == 3 && $orderInfo['activity_id'] && $orderInfo['pink_id']) {
/** @var StorePinkServices $services */
$services = app()->make(StorePinkServices::class);
$res = $services->getCount([['id', '=', $orderInfo['pink_id']], ['status', '<>', 2]]);
if ($res) throw new ValidateException('Failed to write off the group order');
}
$cartInfo = [];
if ($cartIds) {//商城存在部分核销
$ids = array_unique(array_column($cartIds, 'cart_id'));
//订单下原商品信息
$cartInfo = $cartInfoServices->getCartColunm(['oid' => $orderInfo['id'], 'cart_id' => $ids, 'is_writeoff' => 0], 'cart_id,cart_num,surplus_num,product_id,write_times,write_surplus_times,write_start,write_end', 'cart_id');
if (count($ids) != count($cartInfo)) {
throw new ValidateException('订单中有商品已核销');
}
foreach ($cartIds as $cart) {
$info = $cartInfo[$cart['cart_id']] ?? [];
if (!$info) {
throw new ValidateException('核销商品不存在');
}
if ($cart['cart_num'] > $info['write_surplus_times']) {
throw new ValidateException('核销数量超出剩余总核销次数');
}
}
} else {//整单核销
$cartInfo = $cartInfoServices->getCartColunm(['oid' => $orderInfo['id'], 'is_writeoff' => 0], 'id,cart_id,cart_num,surplus_num,product_id,write_times,write_surplus_times,write_start,write_end', 'cart_id');
}
foreach ($cartInfo as $info) {
if ($info['write_start'] && $time < $info['write_start']) {
throw new ValidateException('还未到指定核销的开始时间,无法核销');
}
if ($info['write_end'] && $time > $info['write_end']) {
throw new ValidateException('已经超过指定核销的结束时间,无法核销');
}
}
$data = ['clerk_id' => $uid];
$cartData = ['writeoff_time' => time()];
if ($auth == 1) {//前端客服 下面数据暂时记录前端客服uid
$data['staff_id'] = $uid ?? 0;
$cartData['staff_id'] = $uid ?? 0;
} else if ($auth == 2) {//配送员
/** @var DeliveryServiceServices $deliverServiceServices */
$deliverServiceServices = app()->make(DeliveryServiceServices::class);
$deliveryInfo = $deliverServiceServices->getDeliveryInfoByUid($uid, $store_id);
$cartData['delivery_id'] = $deliveryInfo['id'] ?? 0;
}
$data = $this->transaction(function () use ($orderInfo, $staff_id, $data, $cartIds, $cartInfoServices, $cartData, $auth, $cartInfo) {
if ($cartIds) {//选择商品、件数核销
foreach ($cartIds as $cart) {
$write_surplus_num = $cartInfo[$cart['cart_id']]['write_surplus_times'] ?? 0;
if (!isset($cartInfo[$cart['cart_id']]) || !$write_surplus_num) continue;
if ($cart['cart_num'] >= $write_surplus_num) {//拆分完成
$cartData['write_surplus_times'] = 0;
$cartData['is_writeoff'] = 1;
} else {//拆分部分数量
$cartData['write_surplus_times'] = bcsub((string)$write_surplus_num, $cart['cart_num'], 0);
$cartData['is_writeoff'] = 0;
}
//修改原来订单商品信息
$cartInfoServices->update(['oid' => $orderInfo['id'], 'cart_id' => $cart['cart_id']], $cartData);
}
} else {//整单核销
//修改原来订单商品信息
$cartData['is_writeoff'] = 1;
$cartData['write_surplus_times'] = 0;
$cartInfoServices->update(['oid' => $orderInfo['id']], $cartData);
}
if (!$cartInfoServices->count(['oid' => (int)$orderInfo['id'], 'is_writeoff' => 0])) {//全部核销
$data['status'] = 2;
/** @var StoreOrderTakeServices $storeOrdeTask */
$storeOrdeTask = app()->make(StoreOrderTakeServices::class);
$re = $storeOrdeTask->storeProductOrderUserTakeDelivery($orderInfo, true, false);
if (!$re) {
throw new ValidateException('Write off failure');
}
} else {//部分核销
/** @var StoreOrderCreateServices $storeOrderCreateServices */
$storeOrderCreateServices = app()->make(StoreOrderCreateServices::class);
$data['verify_code'] = $storeOrderCreateServices->getStoreCode();
$data['status'] = 5;
}
if (!$this->dao->update($orderInfo['id'], $data)) {
throw new ValidateException('Write off failure');
}
return $data;
});
event('order.writeoff', [$orderInfo, $auth, $data, $cartIds, $cartInfo,$admin_id]);
return $orderInfo;
}
/**
* 次卡商品核销表单
* @param int $id
* @param int $staffId
* @param int $cart_num
* @return mixed
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function writeOrderFrom(int $id, int $cart_num = 1)
{
$orderInfo = $this->getOrderCartInfo(0, (int)$id, 0);
$cartInfo = $orderInfo['cart_info'] ?? [];
if (!$cartInfo) {
throw new ValidateException('核销订单商品信息不存在');
}
if ($orderInfo['product_type'] != 4) {
throw new ValidateException('订单商品不支持此类型核销');
}
$name = ($cartInfo[0]['write_surplus_times'] ?? 0) . '/' . ($cartInfo[0]['write_times'] ?? 0);
$f[] = Form::hidden('cart_id', $cartInfo[0]['cart_id'] ?? 0);
$f[] = Form::input('name', '核销数', $name)->disabled(true);
$f[] = Form::number('cart_num', '本次核销数量', min(max($cart_num, 1), $cartInfo[0]['write_surplus_times'] ?? 0))->min(1)->max($cartInfo[0]['write_surplus_times'] ?? 1);
return create_form('次卡核销', $f, $this->url('/order/write/form/' . $id), 'POST');
}
}

View File

@@ -0,0 +1,308 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\order\supplier;
use app\jobs\notice\SocketPushJob;
use app\services\order\StoreOrderRefundServices;
use think\annotation\Inject;
use app\services\BaseServices;
use app\dao\order\StoreOrderDao;
use crmeb\services\CacheService;
use think\exception\ValidateException;
use app\services\order\StoreOrderCartInfoServices;
/**
* 供应商订单
* Class SupplierOrderServices
* @package app\sservices\order\supplier
* @mixin StoreOrderDao
*/
class SupplierOrderServices extends BaseServices
{
/**
* 支付类型
* @var string[]
*/
public array $pay_type = ['weixin' => '微信支付', 'yue' => '余额支付', 'offline' => '线下支付', 'alipay' => '支付宝支付', 'cash' => '现金支付', 'automatic' => '自动转账', 'store' => '微信支付'];
/**
* @var StoreOrderDao
*/
#[Inject]
protected StoreOrderDao $dao;
/**
* 配货单信息
* @param array $id
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getDistribution(array $id): array
{
$orderList = $this->dao->getList(['id' => $id], ['id', 'order_id', 'trade_no', 'real_name', 'user_phone', 'user_address', 'pay_time', 'pay_type', 'pay_postage as freight_price', 'coupon_price', 'deduction_price', 'use_integral', 'pay_price', 'mark', 'first_order_price', 'channel_price']);
if (!$orderList) {
throw new ValidateException('订单不存在');
}
/** @var StoreOrderCartInfoServices $services */
$cartServices = app()->make(StoreOrderCartInfoServices::class);
foreach ($orderList as &$order) {
$order['pay_type_name'] = $this->pay_type[$order['pay_type']] ?? '其他方式';
$order['pay_time'] = $order['pay_time'] ? date('Y-m-d H:i:s', (int)$order['pay_time']) : '';
$cartInfos = $cartServices->getCartColunm(['oid' => $order['id']], 'cart_num,is_writeoff,surplus_num,cart_info,refund_num,product_type,is_support_refund,is_gift,promotions_id', 'unique');
$i = 1;
$list = [];
//核算优惠金额
$vipTruePrice = 0;
foreach ($cartInfos as $cartInfo) {
$cart = json_decode($cartInfo['cart_info'], true);
$vipTruePrice = bcadd((string)$vipTruePrice, bcmul($cart['vip_truePrice'], $cart['cart_num'] ?: 1, 2), 2);
$list[] = [
'index' => $i++,
'store_name' => $cart['productInfo']['store_name'],
'suk' => $cart['productInfo']['attrInfo']['suk'],
'bar_code' => $cart['productInfo']['attrInfo']['bar_code'],
'code' => $cart['productInfo']['attrInfo']['code'],
'truePrice' => sprintf("%.2f", $cart['sum_price']),
'cart_num' => $cart['cart_num'],
'subtotal' => bcmul((string)$cart['sum_price'], (string)$cart['cart_num'], 2)
];
}
$order['user_address'] = str_replace(' ', '', $order['user_address']);
$order['vip_true_price'] = $vipTruePrice;
$order['list'] = $list;
}
return $orderList;
}
/**
* 供应商首页头部统计
* @param int $supplierId
* @param array $time
* @return array
*/
public function homeStatics(int $supplierId, array $time): array
{
$data = [];
$where = ['time' => $time, 'supplier_id' => $supplierId];
if ($supplierId < 1) {
$where['supplier_id'] = -1;
}
$orderWhere = ['paid' => 1, 'is_system_del' => 0, 'refund_status' => 0];
$refundWhere = ['refund_type' => 6];
// 订单金额
$data['pay_price'] = $this->dao->sum($where + $orderWhere, 'pay_price', true);
// 订单量
$data['pay_count'] = $this->dao->count($where + $orderWhere);
/** @var StoreOrderRefundServices $orderRefundServices */
$orderRefundServices = app()->make(StoreOrderRefundServices::class);
// 退款金额
$data['refund_price'] = $orderRefundServices->sum($where + $refundWhere, 'refund_price', true);
// 退款订单数
$data['refund_count'] = $orderRefundServices->count($where + $refundWhere);
return $data;
}
/**
* 订单图表
* @param int $supplierId
* @param array $time
* @return array
*/
public function orderCharts(int $supplierId, array $time): array
{
if (is_int($time[0]) && is_int($time[1])) {
$dayCount = (strtotime(date('Y-m-d', $time[1])) - strtotime(date('Y-m-d', $time[0]))) / 86400 + 1;
} else {
$time[0] = strtotime($time[0]);
$time[1] = strtotime($time[1]);
$dayCount = ($time[1] - $time[0]) / 86400 + 1;
}
if ($dayCount == 1) {
$num = 0;
} elseif ($dayCount > 1 && $dayCount <= 31) {
$num = 1;
} elseif ($dayCount > 31 && $dayCount <= 92) {
$num = 3;
} elseif ($dayCount > 92) {
$num = 30;
}
if ($supplierId) {
$where = ['supplier_id' => $supplierId];
} else {
$where = [['supplier_id', '>', 0]];
}
$data = $xAxis = $series = [];
if ($num == 0) {
$xAxis = ['00', '01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23'];
$timeType = '%H';
} elseif ($num != 0) {
$dt_start = $time[0];
$dt_end = $time[1];
while ($dt_start <= $dt_end) {
if ($num == 30) {
$xAxis[] = date('Y-m', $dt_start);
$dt_start = strtotime("+1 month", $dt_start);
$timeType = '%Y-%m';
} else {
$xAxis[] = date('m-d', $dt_start);
$dt_start = strtotime("+$num day", $dt_start);
$timeType = '%m-%d';
}
}
}
$pay_price = array_column($this->dao->getOrderStatistics($where, $time, $timeType, 'add_time', 'sum(pay_price)', 'pay'), 'num', 'days');
$pay_count = array_column($this->dao->getOrderStatistics($where, $time, $timeType, 'add_time', 'count(id)', 'pay'), 'num', 'days');
$refund_price = array_column($this->dao->getOrderStatistics($where, $time, $timeType, 'add_time', 'sum(refund_price)', 'refund'), 'num', 'days');
$refund_count = array_column($this->dao->getOrderStatistics($where, $time, $timeType, 'add_time', 'count(id)', 'refund'), 'num', 'days');
foreach ($xAxis as $item) {
$data['订单金额'][] = isset($pay_price[$item]) ? floatval($pay_price[$item]) : 0;
$data['订单量'][] = isset($pay_count[$item]) ? floatval($pay_count[$item]) : 0;
$data['退款金额'][] = isset($refund_price[$item]) ? floatval($refund_price[$item]) : 0;
$data['退款订单量'][] = isset($refund_count[$item]) ? floatval($refund_count[$item]) : 0;
}
foreach ($data as $key => $item) {
$series[] = [
'name' => $key,
'data' => $item,
'type' => 'line',
];
}
return compact('xAxis', 'series');
}
/**
* 订单来源
* @param int $supplierId
* @param array string
* @return array
*/
public function getOrderChannel(int $supplierId, array $time): array
{
$bing_xdata = ['公众号', '小程序', 'H5', 'PC', 'APP'];
$color = ['#6DD230', '#FFAB2B', '#4BCAD5', '#1890FF', '#B37FEB'];
$bing_data = [];
if ($supplierId) {
$where = ['supplier_id' => $supplierId];
} else {
$where = ['supplier_id' => -1];
}
foreach ($bing_xdata as $key => $item) {
$bing_data[] = [
'name' => $item,
'value' => $this->dao->count(['paid' => 1, 'pid' => 0, 'is_channel' => $key, 'time' => $time] + $where),
'itemStyle' => ['color' => $color[$key]]
];
}
$list = [];
$count = array_sum(array_column($bing_data, 'value'));
foreach ($bing_data as $item) {
$list[] = [
'name' => $item['name'],
'value' => $item['value'],
'percent' => $count != 0 ? floatval(bcmul((string)bcdiv((string)$item['value'], (string)$count, 4), '100', 2)) : 0,
];
}
array_multisort(array_column($list, 'value'), SORT_DESC, $list);
return compact('bing_xdata', 'bing_data', 'list');
}
/**
* 订单类型
* @param int $supplierId
* @param array string
* @return array
*/
public function getOrderType(int $supplierId, array $time): array
{
$bing_xdata = [0 => '普通订单', 1 => '秒杀订单', 2 => '砍价订单', 3 => '拼团订单', 4 => '积分订单', 5 => '套餐订单', 6 => '预售订单', 7 => '新人专享', 8 => '抽奖订单'];
$color = ['#64a1f4', '#3edeb5', '#70869f', '#ffc653', '#ffc653', '#fc7d6a', '#fc7d2a', '#ffc653', '#ffc653'];
$bing_data = [];
if ($supplierId) {
$where = ['supplier_id' => $supplierId];
} else {
$where = ['supplier_id' => -1];
}
foreach ($bing_xdata as $key => $item) {
if (empty($item)) continue;
$bing_data[] = [
'name' => $item,
'value' => $this->dao->together(['paid' => 1, 'pid' => 0, 'type' => $key, 'time' => $time] + $where, 'pay_price', 'sum'),
'itemStyle' => ['color' => $color[$key]]
];
}
$list = [];
$count = array_sum(array_column($bing_data, 'value'));
foreach ($bing_data as $item) {
$list[] = [
'name' => $item['name'],
'value' => $item['value'],
'percent' => $count != 0 ? floatval(bcmul((string)bcdiv((string)$item['value'], (string)$count, 4), '100', 2)) : 0,
];
}
unset($bing_xdata[4]);
$bing_xdata = array_values($bing_xdata);
array_multisort(array_column($list, 'value'), SORT_DESC, $list);
return compact('bing_xdata', 'bing_data', 'list');
}
/**
* 提醒发货
* @param int $supplierId
* @param int $id
* @return bool
* @throws \Psr\SimpleCache\InvalidArgumentException
*/
public function deliverRemind(int $supplierId, int $id)
{
$order = $this->dao->get($id);
if (!$order) {
throw new ValidateException('订单不存在');
}
$expire = 600;
$val = time() + $expire;
$order = $order->toArray();
$cacheName = 'order_deliver_remind_' . $id;
if (CacheService::has($cacheName)) {
$interval = CacheService::get($cacheName);
$remain = $interval - time();
if ($remain > 0) {
throw new ValidateException('请' . ceil($remain / 60) . '分钟后再次提醒');
}
CacheService::delete($cacheName);
} else {
CacheService::set($cacheName, $val, $expire);
}
//向供应商后台发送待发货订单消息
SocketPushJob::dispatch([$supplierId, 'WAIT_DELIVER_ORDER', ['order_id' => $order['order_id']], 'supplier']);
return true;
}
}