Files
huangjingfen/pro_v3.5.1_副本/app/services/activity/promotions/StorePromotionsServices.php
apple 434aa8c69d feat(fsgx): 完成全部24项开发任务 Phase1-7
Phase1 后端核心:
- 新增 fsgx_v1.sql 迁移脚本(is_queue_goods/frozen_points/available_points/no_assess)
- SystemConfigServices 返佣设置扩展(周期人数/分档比例/范围/时机)
- StoreOrderCreateServices 周期循环佣金计算
- StoreOrderTakeServices 佣金发放后同步冻结积分
- StoreProductServices/StoreProduct 保存 is_queue_goods

Phase2 后端接口:
- GET /api/hjf/brokerage/progress 佣金周期进度
- GET /api/hjf/assets/overview 资产总览
- HjfPointsServices 每日 frozen_points 0.4‰ 释放定时任务
- PUT /adminapi/hjf/member/{uid}/no_assess 不考核接口
- GET /adminapi/hjf/points/release_log 积分日志接口

Phase3 前端清理:
- hjfCustom.js 路由精简(仅保留 points/log)
- hjfQueue.js/hjfMember.js API 清理/重定向至 CRMEB 原生接口
- pages.json 公排→推荐佣金/佣金记录/佣金规则

Phase4-5 前端改造:
- queue/status.vue 推荐佣金进度页整体重写
- 商品详情/订单确认/支付结果页文案与逻辑改造
- 个人中心/资产页/引导页/规则页文案改造
- HjfQueueProgress/HjfRefundNotice/HjfAssetCard 组件改造
- 推广中心嵌入佣金进度摘要
- hjfMockData.js 全量更新(公排字段→佣金字段)

Phase6 Admin 增强:
- 用户列表新增 frozen_points/available_points 列及不考核操作按钮
- hjfPoints.js USE_MOCK=false 对接真实积分日志接口

Phase7 配置文档:
- docs/fsgx-phase7-config-checklist.md 后台配置与全链路验收清单

Made-with: Cursor
2026-03-23 22:32:19 +08:00

2139 lines
111 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?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\activity\promotions;
use app\dao\activity\promotions\StorePromotionsDao;
use app\services\activity\coupon\StoreCouponIssueServices;
use app\services\activity\coupon\StoreCouponUserServices;
use app\services\order\StoreOrderCreateServices;
use app\services\order\StoreOrderComputedServices;
use app\services\BaseServices;
use app\services\product\brand\StoreBrandServices;
use app\services\product\label\StoreProductLabelServices;
use app\services\product\product\StoreProductRelationServices;
use app\services\product\product\StoreProductServices;
use app\services\user\label\UserLabelServices;
use app\services\order\StoreOrderServices;
use app\services\order\StoreOrderCartInfoServices;
use app\services\order\StoreCartServices;
use app\services\product\category\StoreProductCategoryServices;
use crmeb\exceptions\AdminException;
use think\annotation\Inject;
use think\exception\ValidateException;
use \crmeb\traits\OptionTrait;
/**
* 促销活动
* Class StorePromotionsServices
* @package app\services\activity\promotions
* @mixin StorePromotionsDao
*/
class StorePromotionsServices extends BaseServices
{
use OptionTrait;
/**
* 活动类型
* @var string[]
*/
protected array $promotionsType = [
1 => '限时折扣',
2 => '第N件N折',
3 => '满减满折',
4 => '满送',
5 => '活动边框',
6 => '活动背景',
];
/**
* 参与活动商品类型
* @var string[]
*/
protected array $productPartakeType = [
1 => '全部商品',
2 => '指定商品参与',
3 => '指定商品不参与',
4 => '指定品牌参与',
5 => '指定商品标签参与',
];
/**
* 优惠内容数据
* @var array
*/
protected array $promotionsData = [
'threshold_type' => 1,//门槛类型1:满N元2:满N件
'threshold' => 0,//优惠门槛
'discount_type' => 1,//优惠类型1:满减2:满折
'n_piece_n_discount' => 3,//n件n折类型1:第二件半件2:买1送1 3:自定义
'discount' => 0,//优惠
'give_integral' => 0,//赠送积分
'give_coupon_id' => [],//赠送优惠券ID
'give_product_id' => [],//赠送商品ID
];
/**
* @var StorePromotionsDao
*/
#[Inject]
protected StorePromotionsDao $dao;
/**
* 获取打折折扣 || 优惠折扣
* @param $num
* @param int $unit
* @param int $type 1:打折折扣 2:优惠折扣
* @return float
*/
public function computedDiscount($num, int $unit = 100, int $type = 1)
{
if ((float)$num < 0) {
$num = 0;
} elseif ((float)$num > 100) {
$num = 100;
}
$discount = bcdiv((string)$num, (string)$unit, 2);
if ($type == 2) {//优惠折扣 打9折扣优惠就是1折
$discount = bcsub('1', (string)$discount, 2);
}
return (float)$discount;
}
/**
* 获取优惠活动标题,内容详情
* @param int $type
* @param int $promotions_cate
* @param array $promotions
* @return array
*/
public function getPromotionsDesc(int $type, int $promotions_cate, array $promotions)
{
$title = '';
$desc = [];
if ($promotions) {
switch ($type) {
case 1:
$title = '限时折扣';
$base = '限时打' . $this->computedDiscount($promotions[0]['discount'] ?? 0, 10) . '折';
if (isset($promotions['is_limit']) && isset($promotions['limit_num']) && (int)$promotions['is_limit'] && (int)$promotions['limit_num']) {
$base .= ',每人限购' . (int)$promotions['limit_num'] . '件';
}
$desc[] = $base;
break;
case 2:
switch ($promotions[0]['n_piece_n_discount'] ?? 3) {
case 1:
$title = '第二件半价';
break;
case 2:
$title = '买1送1';
break;
case 3:
$title = '第' . floatval($promotions[0]['threshold'] ?? 0) . '件' . $this->computedDiscount($promotions[0]['discount'] ?? 0, 10) . '折';
break;
}
$desc[] = '买' . floatval($promotions[0]['threshold'] ?? 0) . '件商品,其中一件享' . $this->computedDiscount($promotions[0]['discount'] ?? 0, 10) . '折优惠';
break;
case 3:
$title = '满减满折';
foreach ($promotions as $p) {
if ($promotions_cate == 2) {
$give = '每满' . floatval($p['threshold'] ?? 0);
} else {
$give = '满' . floatval($p['threshold'] ?? 0);
}
$give .= $p['threshold_type'] == 1 ? '元' : '件';
$give .= $p['discount_type'] == 1 ? ('减' . floatval($p['discount'] ?? 0) . '元') : '打' . $this->computedDiscount($p['discount'] ?? 0, 10) . '折';
$desc[] = $give;
}
break;
case 4:
$title = '满送活动';
foreach ($promotions as $p) {
if ($promotions_cate == 2) {
$base = '每满' . floatval($p['threshold'] ?? 0);
} else {
$base = '满' . floatval($p['threshold'] ?? 0);
}
$base .= $p['threshold_type'] == 1 ? '元送' : '件送';
if ($p['give_integral']) {
$desc[] = $base . floatval($p['give_integral'] ?? 0) . '积分';
}
if ($p['give_coupon_id']) {
$desc[] = $base . '优惠券';
}
if ($p['give_product_id']) {
$desc[] = $base . '赠品';
}
}
break;
default:
break;
}
}
return [$title, $desc];
}
/**
* 返回目前进行中的活动ids
* @param array $promotions_type
* @param string $field
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getAllShowActivityIds(array $promotions_type = [], string $field = 'id')
{
$where = ['type' => 1, 'store_id' => 0, 'pid' => 0, 'is_del' => 0, 'status' => 1, 'promotionsTime' => true];
if ($promotions_type) $where['promotions_type'] = $promotions_type;
$promotions = $this->dao->getList($where, $field);
$ids = [];
if ($promotions) {
if ($field == 'id') {
$ids = array_column($promotions, 'id');
} else {
$ids = $promotions;
}
}
return $ids;
}
/**
* 获取商品所属活动
* @param array $productIds
* @param array $promotions_type
* @param string $field
* @param array $with
* @param string $group
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getProductsPromotions(array $productIds, array $promotions_type = [], string $field = '*', array $with = [], string $group = '')
{
if (!$productIds) {
return [[], []];
}
$promotionsIds = $this->getAllShowActivityIds($promotions_type, 'id,promotions_type,product_partake_type');
if (!$promotionsIds) {
return [[], []];
}
$ids = [];
$productArr = [];
/** @var StoreProductRelationServices $productRelationServices */
$productRelationServices = app()->make(StoreProductRelationServices::class);
foreach ($productIds as $productId) {
$productArr[$productId] = $productRelationServices->getProductRelationCache((int)$productId, [2, 3]);
}
/** @var StorePromotionsAuxiliaryServices $promotionsAuxiliaryServices */
$promotionsAuxiliaryServices = app()->make(StorePromotionsAuxiliaryServices::class);
$preType = [];
foreach ($promotionsIds as $info) {
$pid = (int)$info['id'];
if ($group && in_array($info['promotions_type'], $preType)) {
continue;
}
if ($info['product_partake_type'] == 1) {//所有商品
$ids[] = $pid;
$preType[] = $info['promotions_type'];
} else {
$promotionsAuxiliaryData = $promotionsAuxiliaryServices->getPromotionsAuxiliaryCache($pid);
if ($info['product_partake_type'] == 2) {
if (array_intersect($promotionsAuxiliaryData, $productIds)) {
$ids[] = $pid;
$preType[] = $info['promotions_type'];
}
} else {
foreach ($productArr as $productInfo) {
$data = [];
switch ($info['product_partake_type']) {
case 4://品牌
$data = $productInfo[2] ?? [];
break;
case 5://商品标签
$data = $productInfo[3] ?? [];
break;
}
if (array_intersect($promotionsAuxiliaryData, $data)) {//一个商品满足活动
$ids[] = $pid;
$preType[] = $info['promotions_type'];
break;
}
}
}
}
}
$ids = array_unique($ids);
$result = [];
if ($ids) {
$order = 'promotions_type asc,update_time desc';
$promotions = $this->dao->getList(['ids' => $ids], $field, 0, 0, $with, $order);
if ($promotions) {
$data = $this->promotionsData;
$data['giveCoupon'] = [];
$data['giveProducts'] = [];
foreach ($promotions as &$item) {
if (!isset($item['promotions'])) {
$item['promotions'] = [];
}
$first = array_merge($data, array_intersect_key($item, $data));
array_unshift($item['promotions'], $first);
$item['promotions'] = $this->handelPromotions($item['promotions']);
if (isset($item['giveProducts']) && $item['giveProducts']) {
$item['giveProducts'] = array_column($item['giveProducts'], 'productInfo');
}
if (isset($item['giveCoupon']) && $item['giveCoupon']) {
$item['giveCoupon'] = array_column($item['giveCoupon'], 'coupon');
}
$result[] = $item;
}
}
}
return [$result, $productArr];
}
/**
* 获取商品所有优惠活动 所属活动详情
* @param array $productIds
* @param string $field
* @param array $with
* @param array $promotions_type
* @param string $group
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getProductsPromotionsDetail(array $productIds, string $field = '*', array $with = [], array $promotions_type = [], string $group = '')
{
$productDetails = [];
$promotionsDetails = [];
$promotions = [];
if ($productIds) {
[$promotions, $productRelation] = $this->getProductsPromotions($productIds, $promotions_type, $field, $with, $group);
if ($promotions) {
/** @var StorePromotionsAuxiliaryServices $promotionsAuxiliaryServices */
$promotionsAuxiliaryServices = app()->make(StorePromotionsAuxiliaryServices::class);
foreach ($promotions as $info) {
$id = (int)$info['id'];
$promotionsAuxiliaryData = $promotionsAuxiliaryServices->getPromotionsAuxiliaryCache($id);
foreach ($productIds as $productId) {
$newProductId = $productId;
$products = $info['products'] ?? [];
$pIds = $products ? array_unique(array_column($products, 'product_id')) : [];
switch ($info['product_partake_type']) {
case 1://全部商品
$productDetails[$productId][] = $id;
$promotionsDetails[$id][] = $productId;
break;
case 2://选中商品参与
if (in_array($newProductId, $pIds)) {
$productDetails[$productId][] = $id;
$promotionsDetails[$id][] = $productId;
}
break;
case 3://选中商品不参与
$products = $info['products'] ?? [];
if ($products) $products = array_combine(array_column($products, 'product_id'), $products);
if (!in_array($newProductId, $pIds) || (isset($products[$newProductId]['is_all']) && $products[$newProductId]['is_all'] == 0)) {
$productDetails[$productId][] = $id;
$promotionsDetails[$id][] = $productId;
}
break;
case 4://品牌
$data = $productRelation[$productId][2] ?? [];
if (array_intersect($promotionsAuxiliaryData, $data)) {//一个商品满足活动
$productDetails[$productId][] = $id;
$promotionsDetails[$id][] = $productId;
}
break;
case 5://商品标签
$data = $productRelation[$productId][3] ?? [];
if (array_intersect($promotionsAuxiliaryData, $data)) {//一个商品满足活动
$productDetails[$productId][] = $id;
$promotionsDetails[$id][] = $productId;
}
break;
}
}
}
}
}
return [$promotions, $productDetails, $promotionsDetails];
}
/**
* 检测活动内容,格式数据
* @param int $type
* @param array $data
* @return array
*/
public function checkPromotions(int $type, array $data)
{
if (!$data) {
throw new AdminException('请添加活动优惠内容');
}
$data = array_merge($this->promotionsData, array_intersect_key($data, $this->promotionsData));
switch ($type) {
case 1:
case 2:
$data['promotions_cate'] = 1;
$data['discount_type'] = 2;
$data['give_coupon_id'] = $data['give_product_id'] = [];
if ($type == 2) {
$data['threshold_type'] = 2;
if (!$data['threshold']) {
throw new AdminException('请输入打折门槛');
}
}
if ($data['discount'] === '') {
throw new AdminException('请添加折扣');
}
if ($data['discount'] < 0 || $data['discount'] > 100) {
throw new AdminException('折扣必须为099数字');
}
break;
case 3:
$data['give_coupon_id'] = $data['give_product_id'] = [];
if (!$data['threshold']) {
throw new AdminException('请输入优惠门槛');
}
if ($data['discount'] === '') {
throw new AdminException($data['discount_type'] == 1 ? '请输入优惠金额' : '请输入打折折扣');
}
if ($data['discount_type'] == 2 && ($data['discount'] < 0 || $data['discount'] > 100)) {
throw new AdminException('折扣必须为099数字');
}
break;
case 4:
if (!$data['threshold']) {
throw new AdminException('请输入优惠门槛');
}
if (!$data['give_integral'] && !$data['give_coupon_id'] && !$data['give_product_id']) {
throw new AdminException('请至少选择一项赠送内容');
}
if ($data['give_coupon_id']) {
$couponsIds = array_column($data['give_coupon_id'], 'give_coupon_id');
$giveCoupon = array_combine($couponsIds, $data['give_coupon_id']);
/** @var StoreCouponIssueServices $storeCouponServices */
$storeCouponServices = app()->make(StoreCouponIssueServices::class);
$coupons = $storeCouponServices->getValidGiveCoupons($couponsIds, 'id,is_permanent,remain_count');
if (!$coupons || count($coupons) != count($couponsIds)) {
throw new AdminException('优惠券已失效请重新选择');
}
foreach ($coupons as $coupon) {
if (!isset($giveCoupon[$coupon['id']]['give_coupon_num']) || !$giveCoupon[$coupon['id']]['give_coupon_num']) {
throw new AdminException('请输入赠送优惠券数量');
}
if ($coupon['is_permanent'] == 0 && $coupon['remain_count'] < $giveCoupon[$coupon['id']]['give_coupon_num']) {
throw new AdminException('赠送优惠券数量不能超出优惠券限量');
}
}
}
if ($data['give_product_id']) {
$productIds = array_column($data['give_product_id'], 'give_product_id');
$giveProduct = array_combine($productIds, $data['give_product_id']);
/** @var StoreProductServices $storeProductServices */
$storeProductServices = app()->make(StoreProductServices::class);
$products = $storeProductServices->getSearchList(['ids' => $productIds], 0, 0, ['id,stock']);
if (!$products || count($products) != count(array_unique($productIds))) {
throw new AdminException('商品已失效请重新选择');
}
foreach ($products as $product) {
if (!isset($giveProduct[$product['id']]['give_product_num']) || !$giveProduct[$product['id']]['give_product_num']) {
throw new AdminException('请输入赠送商品数量');
}
if ($product['stock'] < $giveProduct[$product['id']]['give_product_num']) {
throw new AdminException('赠送商品数量不能超出商品库存');
}
}
}
break;
default:
throw new AdminException('暂不支持该类型优惠活动');
}
return $data;
}
/**
* 获取列表
* @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, ['giveProducts' => function ($query) {
$query->field('promotions_id,product_id,limit_num,surplus_num')->with(['productInfo' => function ($query) {
$query->field('id,store_name');
}]);
}, 'giveCoupon' => function ($query) {
$query->field('promotions_id,coupon_id,limit_num,surplus_num')->with(['coupon' => function ($query) {
$query->field('id,type,coupon_type,coupon_title,coupon_price,use_min_price');
}]);
}, 'promotions' => function ($query) {
$query->field('id,pid,promotions_type,promotions_cate,threshold_type,threshold,discount_type,n_piece_n_discount,discount,give_integral,give_coupon_id,give_product_id,give_product_unique')->with(['giveProducts' => function ($query) {
$query->field('promotions_id, product_id,limit_num,surplus_num')->with(['productInfo' => function ($query) {
$query->field('id,store_name');
}]);
}, 'giveCoupon' => function ($query) {
$query->field('promotions_id, coupon_id,limit_num,surplus_num')->with(['coupon' => function ($query) {
$query->field('id,type,coupon_type,coupon_title,coupon_price,use_min_price');
}]);
}]);
}]);
$count = 0;
if ($list) {
$count = $this->dao->count($where);
$data = $this->promotionsData;
$data['giveCoupon'] = [];
$data['giveProducts'] = [];
/** @var StoreOrderCartInfoServices $storeOrderCartInfoServices */
$storeOrderCartInfoServices = app()->make(StoreOrderCartInfoServices::class);
/** @var StoreOrderServices $storeOrderServices */
$storeOrderServices = app()->make(StoreOrderServices::class);
/** @var StoreProductServices $storeProductServices */
$storeProductServices = app()->make(StoreProductServices::class);
/** @var StoreProductRelationServices $storeProductRelationServices */
$storeProductRelationServices = app()->make(StoreProductRelationServices::class);
/** @var StorePromotionsAuxiliaryServices $promotionsAuxiliaryServices */
$promotionsAuxiliaryServices = app()->make(StorePromotionsAuxiliaryServices::class);
$getPromotions = function ($cartList, $oids) {
$promotionsPrice = 0;
$uids = [];
$oldUids = [];
$ids = [];
foreach ($cartList as $key => $cart) {
if (!in_array($cart['oid'], $oids)) continue;
$info = is_string($cart['cart_info']) ? json_decode($cart['cart_info'], true) : $cart['cart_info'];
$promotionsPrice = bcadd((string)$promotionsPrice, (string)($info['sum_promotions_price'] ?? 0), 2);
if (!in_array($cart['oid'], $ids)) {
$ids[] = $cart['oid'];
if (!in_array($cart['uid'], $uids)) {
$uids[] = $cart['uid'];
} else {
$oldUids[] = $cart['uid'];
}
}
}
return [$promotionsPrice, $uids, $oldUids];
};
foreach ($list as &$item) {
if ($item['status']) {
if ($item['start_time'] > time())
$item['start_name'] = '未开始';
else if ((int)$item['stop_time'] < time())
$item['start_name'] = '已结束';
else if ((int)$item['stop_time'] > time() && $item['start_time'] < time()) {
$item['start_name'] = '进行中';
}
} else $item['start_name'] = '已结束';
$end_time = $item['stop_time'] ? date('Y-m-d', (int)$item['stop_time']) : '';
$item['_stop_time'] = $end_time;
$item['stop_status'] = $item['stop_time'] < time() ? 1 : 0;
$item['sum_pay_price'] = 0.00;
$item['sum_promotions_price'] = 0.00;
$item['sum_order'] = 0;
$item['sum_user'] = 0;
$item['old_user'] = 0;
$item['new_user'] = 0;
$pids = array_merge([$item['id']], array_column($item['promotions'], 'id'));
$cartInfos = $storeOrderCartInfoServices->getColumn(['promotions_id' => $pids], 'oid,uid,cart_info', 'id', true);
if ($cartInfos) {
$oids = $storeOrderServices->getColumn(['id' => array_unique(array_column($cartInfos, 'oid')), 'is_del' => 0, 'pid' => 0, 'is_system_del' => 0, 'refund_status' => [0, 3]], 'id', '');
$item['sum_pay_price'] = $storeOrderServices->sum(['id' => $oids, 'is_del' => 0, 'pid' => 0, 'is_system_del' => 0, 'refund_status' => [0, 3]], 'pay_price', true);
[$promotionsPrice, $uids, $oldUids] = $getPromotions($cartInfos, $oids);
$item['sum_promotions_price'] = $promotionsPrice;
$item['sum_order'] = $storeOrderServices->count(['id' => $oids, 'is_del' => 0, 'pid' => 0, 'is_system_del' => 0, 'refund_status' => [0, 3]]);
$item['sum_user'] = count($uids);
$item['old_user'] = count(array_unique($oldUids));
$item['new_user'] = bcsub((string)$item['sum_user'], (string)$item['old_user'], 0);
}
$first = array_merge($data, array_intersect_key($item, $data));
array_unshift($item['promotions'], $first);
$item['promotions'] = $this->handelPromotions($item['promotions']);
$promotionsAuxiliaryData = $promotionsAuxiliaryServices->getPromotionsAuxiliaryCache($item['id']);
switch ($item['product_partake_type']) {
case 1://所有商品
$item['product_count'] = $storeProductServices->count(['is_show' => 1, 'is_del' => 0, 'is_verify' => 1]);
break;
case 2://选中商品参与
$product_ids = $promotionsAuxiliaryData;
$item['product_count'] = $product_ids ? $storeProductServices->count(['is_show' => 1, 'is_del' => 0, 'id' => $product_ids, 'is_verify' => 1]) : 0;
break;
case 3:
$item['product_count'] = 0;
break;
case 4://品牌
$product_ids = $promotionsAuxiliaryData ? $storeProductRelationServices->getIdsByWhere(['type' => 2, 'relation_id' => $promotionsAuxiliaryData]) : [];
$item['product_count'] = $product_ids ? $storeProductServices->count(['is_show' => 1, 'is_del' => 0, 'id' => $product_ids, 'is_verify' => 1]) : 0;
break;
case 5://商品标签
$product_ids = $promotionsAuxiliaryData ? $storeProductRelationServices->getIdsByWhere(['type' => 3, 'relation_id' => $promotionsAuxiliaryData]) : [];
$item['product_count'] = $product_ids ? $storeProductServices->count(['is_show' => 1, 'is_del' => 0, 'id' => $product_ids, 'is_verify' => 1]) : 0;
break;
}
}
}
return compact('list', 'count');
}
/**
* 获取信息
* @param int $id
* @return array|\think\Model|null
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getInfo(int $id)
{
$info = $this->dao->get($id, ['*'], ['products' => function ($query) {
$query->field('promotions_id,product_id,unique')->with(['productInfo', 'attrValue']);
}, 'brands' => function ($query) {
$query->field('promotions_id,brand_id')->with(['brandInfo' => function ($q) {
$q->field('id,brand_name');
}]);
}, 'productLabels' => function ($query) {
$query->field('promotions_id,store_label_id')->with(['productLabelInfo' => function ($q) {
$q->field('id,label_name');
}]);
}, 'giveProducts' => function ($query) {
$query->field('type,promotions_id,product_id,limit_num,unique')->with(['productInfo' => function ($q) {
$q->field('id,store_name');
}, 'giveAttrValue']);
}, 'giveCoupon' => function ($query) {
$query->field('type,promotions_id,coupon_id,limit_num')->with(['coupon']);
}, 'promotions' => function ($query) {
$query->field('id,pid,promotions_type,promotions_cate,threshold_type,threshold,discount_type,n_piece_n_discount,discount,give_integral,give_coupon_id,give_product_id,give_product_unique')->with(['giveProducts' => function ($query) {
$query->field('type,promotions_id, product_id,limit_num,unique')->with(['productInfo' => function ($q) {
$q->field('id,store_name');
}, 'giveAttrValue']);
}, 'giveCoupon' => function ($query) {
$query->field('type,promotions_id, coupon_id,limit_num')->with(['coupon']);
}]);
}]);
if (!$info) {
throw new AdminException('数据不存在');
}
$info = $info->toArray();
if ($info['start_time'])
$start_time = $info['promotions_type'] == 1 ? date('Y-m-d H:i', (int)$info['start_time']) : date('Y-m-d', (int)$info['start_time']);
if ($info['stop_time'])
$stop_time = $info['promotions_type'] == 1 ? date('Y-m-d H:i', (int)$info['stop_time']) : date('Y-m-d', (int)$info['stop_time']);
if (isset($start_time) && isset($stop_time))
$info['section_time'] = [$start_time, $stop_time];
else
$info['section_time'] = [];
unset($info['start_time'], $info['stop_time']);
$info['is_label'] = $info['label_id'] ? 1 : 0;
if ($info['is_label']) {
$label_id = is_array($info['label_id']) ? $info['label_id'] : explode(',', $info['label_id']);
/** @var UserLabelServices $userLabelServices */
$userLabelServices = app()->make(UserLabelServices::class);
$info['label_id'] = $userLabelServices->getLabelList(['ids' => $label_id], ['id', 'label_name']);
} else {
$info['label_id'] = [];
}
$info['threshold'] = floatval($info['threshold']);
$info['discount'] = floatval($info['discount']);
$info['give_integral'] = intval($info['give_integral']);
$info['is_overlay'] = $info['overlay'] ? 1 : 0;
$info['promotions'] = $info['promotions'] ?? [];
$info['products'] = $info['products'] ?? [];
$info['giveCoupon'] = $info['giveCoupon'] ?? [];
$info['giveProducts'] = $info['giveProducts'] ?? [];
if ($info['products']) {
/** @var StoreProductLabelServices $storeProductLabelServices */
$storeProductLabelServices = app()->make(StoreProductLabelServices::class);
$products = [];
foreach ($info['products'] as &$item) {
$product = is_object($item) ? $item->toArray() : $item;
$product = array_merge($product, $product['productInfo'] ?? []);
$product['store_label'] = '';
if (isset($product['store_label_id']) && $product['store_label_id']) {
$storeLabelList = $storeProductLabelServices->getColumn([['relation_id', '=', 0], ['type', '=', 0], ['id', 'IN', $product['store_label_id']]], 'id,label_name');
$product['store_label'] = $storeLabelList ? implode(',', array_column($storeLabelList, 'label_name')) : '';
}
$unique = is_string($product['unique']) ? explode(',', $product['unique']) : $product['unique'];
foreach ($product['attrValue'] as $key => $value) {
if (!in_array($value['unique'], $unique)) {
unset($product['attrValue'][$key]);
}
}
$product['attrValue'] = array_merge($product['attrValue']);
unset($product['productInfo']);
$products[] = $product;
}
if ($products) {
$cateIds = implode(',', array_column($products, 'cate_id'));
/** @var StoreProductCategoryServices $categoryService */
$categoryService = app()->make(StoreProductCategoryServices::class);
$cateList = $categoryService->getCateParentAndChildName($cateIds);
foreach ($products as $key => &$item) {
$item['cate_name'] = '';
if (isset($item['cate_id']) && $item['cate_id']) {
$cate_ids = explode(',', $item['cate_id']);
$cate_name = $categoryService->getCateName($cate_ids, $cateList);
if ($cate_name) {
$item['cate_name'] = is_array($cate_name) ? implode(',', $cate_name) : '';
}
}
foreach ($item['attrValue'] as $key => &$value) {
$value['store_label'] = $item['store_label'] ?? '';
$value['cate_name'] = $item['cate_name'] ?? '';
}
}
}
unset($info['products']);
$info['products'] = $products;
}
$data = $this->promotionsData;
$data['giveCoupon'] = [];
$data['giveProducts'] = [];
$first = array_merge($data, array_intersect_key($info, $data));
array_unshift($info['promotions'], $first);
$info['promotions'] = $this->handelPromotions($info['promotions']);
$info['brand_id'] = isset($info['brands']) && $info['brands'] ? array_column($info['brands'], 'brand_id') : [];
$info['store_label_id'] = [];
if (isset($info['productLabels']) && $info['productLabels']) {
foreach ($info['productLabels'] as $label) {
if (isset($label['productLabelInfo']) && $label['productLabelInfo']) {
$info['store_label_id'][] = $label['productLabelInfo'];
}
}
}
unset($info['brands'], $info['productLabels']);
return $info;
}
/**
* 处理阶梯优惠赠送商品、优惠券
* @param array $promotions
* @return array
*/
public function handelPromotions(array $promotions)
{
if ($promotions) {
foreach ($promotions as &$p) {
$p['threshold'] = (float)$p['threshold'];
$p['discount'] = (float)$p['discount'];
if (isset($p['giveCoupon']) && $p['giveCoupon']) {
$coupons = [];
foreach ($p['giveCoupon'] as &$coupon) {
$coupon = is_object($coupon) ? $coupon->toArray() : $coupon;
$coupon = array_merge($coupon, $coupon['coupon'] ?? []);
unset($coupon['coupon']);
$coupons[] = $coupon;
}
unset($p['giveCoupon']);
$p['giveCoupon'] = $coupons;
}
if (isset($p['giveProducts']) && $p['giveProducts']) {
$products = [];
foreach ($p['giveProducts'] as &$product) {
$product = is_object($product) ? $product->toArray() : $product;
$product = array_merge($product, $product['productInfo'] ?? []);
$product = array_merge($product, $product['giveAttrValue'] ?? []);
unset($product['productInfo'], $product['giveAttrValue']);
$products[] = $product;
}
unset($p['giveProducts']);
$p['giveProducts'] = $products;
}
}
}
return $promotions;
}
/**
* 保存促销活动
* @param int $id
* @param array $data
* @return bool
*/
public function saveData(int $id, array $data)
{
if (!$data['section_time'] || count($data['section_time']) != 2) {
throw new AdminException('请选择活动时间');
}
[$start_time, $end_time] = $data['section_time'];
if (strtotime($end_time) < time()) {
throw new AdminException('活动结束时间不能小于当前时间');
}
if ($id) {
$info = $this->dao->get((int)$id);
if (!$info) {
throw new AdminException('数据不存在');
}
}
$data['start_time'] = strtotime($start_time);
$data['stop_time'] = $data['promotions_type'] == 1 ? strtotime($end_time) : strtotime($end_time) + 86399;
$data['label_id'] = $data['label_id'] ? implode(',', $data['label_id']) : '';
$data['overlay'] = $data['overlay'] ? implode(',', $data['overlay']) : '';
$promotionsAuxiliaryData = [];
switch ($data['product_partake_type']) {
case 1://全部
$data['product_id'] = [];
break;
case 2://指定ID参与
case 3://指定ID不参与
$promotionsAuxiliaryData = $productData = $data['product_id'];
$productIds = $productData ? array_column($productData, 'product_id') : [];
$data['product_id'] = $productIds ? implode(',', $productIds) : '';
/** @var StoreProductServices $storeProductServices */
$storeProductServices = app()->make(StoreProductServices::class);
$count = $storeProductServices->count(['is_show' => 1, 'is_del' => 0, 'id' => $productIds]);
$productCount = count(array_unique($productIds));
if ($count != $productCount) {
throw new AdminException('选择商品中有已下架或移入回收站');
}
break;
case 4://指定品牌
$data['brand_id'] = array_unique($data['brand_id']);
/** @var StoreBrandServices $storeBrandServices */
$storeBrandServices = app()->make(StoreBrandServices::class);
$brandCount = $storeBrandServices->count(['is_show' => 1, 'is_del' => 0, 'id' => $data['brand_id']]);
if (count(array_unique($data['brand_id'])) != $brandCount) {
throw new AdminException('选择商品品牌中有已下架或删除的');
}
$promotionsAuxiliaryData['brand_id'] = $data['brand_id'];
break;
case 5://指定商品标签
$data['store_label_id'] = array_unique($data['store_label_id']);
/** @var StoreProductLabelServices $storeProductLabelServices */
$storeProductLabelServices = app()->make(StoreProductLabelServices::class);
$labelCount = $storeProductLabelServices->count(['id' => $data['store_label_id']]);
if (count(array_unique($data['store_label_id'])) != $labelCount) {
throw new ValidateException('选择商品标签中有已下架或删除的');
}
$promotionsAuxiliaryData['store_label_id'] = $data['store_label_id'];
break;
default:
throw new ValidateException('暂不支持该类型商品');
break;
}
$promotions = $data['promotions'];
$threshold = -1;
foreach ($promotions as &$value) {
if ($threshold != -1 && $value['threshold'] <= $threshold) {
throw new AdminException('优惠门槛只能递增(例如二级必须大于一级优惠)');
}
$threshold = $value['threshold'];
$value = $this->checkPromotions((int)$data['promotions_type'], $value);
$value['threshold_type'] = $data['threshold_type'];
}
[$title, $desc] = $this->getPromotionsDesc((int)$data['promotions_type'], (int)$data['promotions_cate'], $data['promotions_type'] == 1 ? array_merge($promotions, ['is_limit' => $data['is_limit'], 'limit_num' => $data['limit_num']]) : $promotions);
$data['title'] = $title;
$data['desc'] = implode(',', $desc);
$first = array_shift($promotions);
$giveCoupon = $first['give_coupon_id'] ?? [];
$giveProduct = $first['give_product_id'] ?? [];
unset($first['give_coupon_id'], $first['give_product_id']);
$first['give_coupon_id'] = $giveCoupon ? implode(',', array_unique(array_column($giveCoupon, 'give_coupon_id'))) : '';
$first['give_product_id'] = $giveProduct ? implode(',', array_unique(array_column($giveProduct, 'give_product_id'))) : '';
$first['give_product_unique'] = $giveProduct ? implode(',', array_unique(array_column($giveProduct, 'unique'))) : '';
$data = array_merge($data, $first);
unset($data['section_time'], $data['promotions']);
$this->transaction(function () use ($id, $data, $promotions, $promotionsAuxiliaryData, $giveCoupon, $giveProduct) {
$time = time();
$data['update_time'] = $time;
if ($id) {
$this->dao->update($id, $data);
//删除之前阶梯数据
$this->dao->delete(['pid' => $id]);
} else {
$data['add_time'] = $time;
$res = $this->dao->save($data);
$id = $res->id;
}
/** @var StorePromotionsAuxiliaryServices $storePromotionsAuxiliaryServices */
$storePromotionsAuxiliaryServices = app()->make(StorePromotionsAuxiliaryServices::class);
$storePromotionsAuxiliaryServices->savePromotionsRelation((int)$id, (int)$data['product_partake_type'], $promotionsAuxiliaryData, $giveCoupon, $giveProduct);
if ($promotions) {
foreach ($promotions as $item) {
$giveCoupon = $item['give_coupon_id'] ?? [];
$giveProduct = $item['give_product_id'] ?? [];
unset($item['give_coupon_id'], $item['give_product_id']);
$item['give_coupon_id'] = $giveCoupon ? implode(',', array_unique(array_column($giveCoupon, 'give_coupon_id'))) : '';
$item['give_product_id'] = $giveProduct ? implode(',', array_unique(array_column($giveProduct, 'give_product_id'))) : '';
$item['give_product_unique'] = $giveProduct ? implode(',', array_unique(array_column($giveProduct, 'unique'))) : '';
$item['pid'] = $id;
$item['promotions_type'] = (int)$data['promotions_type'];
$item['promotions_cate'] = (int)$data['promotions_cate'];
$item['add_time'] = $time;
$res = $this->dao->save($item);
$storePromotionsAuxiliaryServices->savePromotionsRelation((int)$res->id, (int)$data['product_partake_type'], $promotionsAuxiliaryData, $giveCoupon, $giveProduct);
}
}
});
return true;
}
/**
* 验证购买商品规格是否在优惠活动适用商品选择规格中
* @param array $productIds
* @param array $cartList
* @param array $promotions
* @return array
*/
public function checkProductCanUsePromotions(array $productIds, array $cartList, array $promotions)
{
$ids = $uniques = [];
if (!$productIds || !$cartList || !$promotions) return [$ids, $uniques];
$useProducts = $promotions['products'] ?? [];
if ($useProducts) {
$useProducts = array_combine(array_column($useProducts, 'product_id'), $useProducts);
}
foreach ($cartList as $cart) {
if (!in_array($cart['product_id'], $productIds)) continue;
$productUnique = $cart['product_attr_unique'] ?? '';
$product_id = $cart['product_id'] ?? 0;
if (!$productUnique) continue;
if (in_array($promotions['product_partake_type'], [2, 3])) {
$useUniques = $useProducts[$product_id]['unique'] ?? [];
if ($promotions['product_partake_type'] == 2) {
$uniques[$product_id] = $useUniques;
if (!in_array($productUnique, $useUniques)) {
continue;
}
} else {
$uniques[$product_id] = $useUniques;
if (in_array($productUnique, $useUniques)) {
continue;
}
}
}
$ids[] = $product_id;
}
return [$ids, $uniques];
}
/**
* 检查优惠活动限量
* @param StoreOrderCartInfoServices $storeOrderCartInfoServices
* @param int $uid
* @param int $id
* @param array $productIds
* @param array $promotions
* @return array
*/
public function checkPromotionsLimit(StoreOrderCartInfoServices $storeOrderCartInfoServices, int $uid, int $id, array $productIds, array $promotions)
{
if (!$productIds) {
return [];
}
if ($uid && $promotions['promotions_type'] == 1 && $promotions['is_limit']) {//限时折扣 存在限量
//获取包含子级获取ids
$ids = array_unique(array_merge([$id], array_column($promotions['promotions'] ?? [], 'id')));
$data = [];
foreach ($productIds as $key => $product_id) {
if ($storeOrderCartInfoServices->count(['uid' => $uid, 'product_id' => $product_id, 'promotions_id' => $ids]) < $promotions['limit_num']) {
$data[] = $product_id;
}
}
return $data;
}
return $productIds;
}
/**
* 计算几个商品总金额 并返回商品信息
* @param int $promotions_type
* @param array $productIds
* @param array $cartList
* @param array $uniques
* @param int $product_partake_type
* @param bool $isGive
* @return array
*/
protected function getPromotionsProductInfo(int $promotions_type, array $productIds, array $cartList, array $uniques = [], int $product_partake_type = 1, bool $isGive = false)
{
$sumPrice = $sumCount = 0;
$p = [];
if (!$cartList || !$productIds) {
return [$sumPrice, $sumCount, $p];
}
$productComputedArr = $this->getItem('productComputedArr', []);
$computedArr = $this->getItem('computedArr', []);
foreach ($cartList as $product) {
if (!in_array($product['product_id'], $productIds)) continue;
$cart_num = $product['cart_num'] ?? 1;
$unique = $product['product_attr_unique'] ?? '';
$product_id = $product['product_id'];
if (!$this->checkProductUnque($unique, $uniques[$product_id] ?? [], $product_partake_type)) {
continue;
}
if ($isGive) {
$key = 'true_price';
} else {
if ($promotions_type == 1) {
$key = 'price';
} else {
if (isset($productComputedArr[$product_id]['typeArr']) && in_array(1, $productComputedArr[$product_id]['typeArr'])) {
$key = 'price';
} else {
$key = 'true_price';
}
}
}
if ($key == 'price') {
$price = isset($product['productInfo']['attrInfo']['price']) ? $product['productInfo']['attrInfo']['price'] : ($product['productInfo']['price'] ?? 0);
} else {
$price = $product['truePrice'];
}
$isOverlay = $productComputedArr[$product_id]['is_overlay'] ?? false;
if ($isOverlay && $computedArr) {
foreach ($computedArr as $key => $computedDetail) {
if (isset($computedDetail[$unique]['promotions_true_price'])) {
$price = bcsub((string)$price, (string)$computedDetail[$unique]['promotions_true_price'], 2);
}
}
}
$product['price'] = floatval($price) > 0 ? $price : 0;
$sumPrice = bcadd((string)$sumPrice, (string)bcmul((string)$price, (string)$cart_num, 2), 2);
if ($isGive && isset($product['coupon_price'])) {
$sumPrice = bcsub((string)$sumPrice, (string)$product['coupon_price'], 2);
}
$sumCount = bcadd((string)$sumCount, (string)$cart_num, 0);
$p[] = $product;
}
return [$sumPrice, $sumCount, $p];
}
/**
* 验证是否叠加其他活动
* @param int $promotions_type
* @param array $overlay
* @return bool
*/
public function checkOverlay(int $promotions_type, array $overlay)
{
$data = [1, 2, 3];
return boolval(array_intersect($overlay, array_diff($data, [$promotions_type])));
}
/**
* 获取商品活动是否叠加计算
* @param array $promotionsList
* @param array $productDetails
* @param array $promotionsDetail
* @return array[]
*/
public function getProductComputedPromotions(array $promotionsList, array $productDetails, array $promotionsDetail)
{
$productArr = [];
$promotionsArr = [];
$productComputedArr = [];
if (!$promotionsList) {
return [$productArr, $promotionsArr, $productComputedArr];
}
//验证是否能叠加使用
foreach ($productDetails as $product_id => $promotionsIds) {
$pIds = [];
$overlayPIds = [];
$unOverlayPIds = [];
$prevType = [];
$preOverlay = [];
$isOverlay = true;
foreach ($promotionsIds as $id) {
$promotions = $promotionsList[$id] ?? [];
if (!$promotions) continue;
$overlay = is_string($promotions['overlay']) ? explode(',', $promotions['overlay']) : $promotions['overlay'];
//同一个商品 同一类型活动取最新一个
if (!in_array($promotions['promotions_type'], $prevType)) {
if (!$prevType) {
$overlayPIds[] = $unOverlayPIds[] = $id;
$prevType[] = $promotions['promotions_type'];
$preOverlay[$promotions['promotions_type']] = $overlay;
} else {
//有限时折扣
if (isset($preOverlay[1]) && !$this->checkOverlay(1, $preOverlay[1])) {
if ($promotions['promotions_type'] == 4) {
$prevType[] = $promotions['promotions_type'];
$preOverlay[$promotions['promotions_type']] = $overlay;
$unOverlayPIds[] = $id;
}
} else {
if ($promotions['promotions_type'] == 4) {
$prevType[] = $promotions['promotions_type'];
$preOverlay[$promotions['promotions_type']] = $overlay;
$overlayPIds[] = $id;
$unOverlayPIds[] = $id;
} else {
foreach ($preOverlay as $key => $value) {
if (in_array($key, $overlay) && in_array($promotions['promotions_type'], $value)) {
$overlayPIds[] = $id;
} else {
$unOverlayPIds[] = $id;
}
}
}
$prevType[] = $promotions['promotions_type'];
$preOverlay[$promotions['promotions_type']] = $overlay;
}
}
}
}
$overlayPIds = array_unique($overlayPIds);
$unOverlayPIds = array_unique($unOverlayPIds);
if (count($overlayPIds) > 1) {
$isOverlay = true;
$pIds = $overlayPIds;
} else if (count($overlayPIds) == 1 && count($unOverlayPIds) == 1) {
$isOverlay = false;
$pIds = $overlayPIds;
} else {
$isOverlay = false;
$pIds = $unOverlayPIds;
}
$productComputedArr[$product_id]['is_overlay'] = $pIds && $isOverlay;
$typeArr = [];
//重新整理商品关联ids数据
foreach ($pIds as $id) {
$promotions = $promotionsList[$id] ?? [];
if ($promotions) {
$productArr[$product_id][] = $id;
$typeArr[] = $promotions['promotions_type'];
}
}
//存在限时折扣 不叠加
if (count($typeArr) > 1 && in_array(1, $typeArr) && !$productComputedArr[$product_id]['is_overlay']) {
$typeArr = [];
$productArr[$product_id] = [];
foreach ($pIds as $id) {
$promotions = $promotionsList[$id] ?? [];
if ($promotions) {
if (in_array($promotions['promotions_type'], [1, 4])) {
$productArr[$product_id][] = $id;
$typeArr[] = $promotions['promotions_type'];
}
}
}
}
$productComputedArr[$product_id]['typeArr'] = $typeArr;
}
//重新整理活动关联商品ids数据
foreach ($promotionsDetail as $promotions_id => $productIds) {
foreach ($productIds as $pid) {
$pIds = $productArr[$pid] ?? [];
if ($pIds && in_array($promotions_id, $pIds)) {
$promotionsArr[$promotions_id][] = $pid;
}
}
}
return [$productArr, $promotionsArr, $productComputedArr];
}
/**
* 组合使用的优惠活动详情
* @param int $uid
* @param array $usePromotionsIds
* @param array $promotionsArr
* @param array $computedArr
* @return array
*/
public function getUsePromotiosnInfo(int $uid, array $usePromotionsIds, array $promotionsArr, array $computedArr)
{
$usePromotions = [];
if (!$usePromotionsIds || !$promotionsArr || !$computedArr) {
return $usePromotions;
}
$giveCoupon = $giveProduct = $giveCartList = [];
$giveIntegral = 0;
foreach ($usePromotionsIds as $id) {
$promotionsInfo = $promotionsArr[$id] ?? [];
if (!$promotionsInfo) continue;
$details = $computedArr[$id] ?? [];
$promotionsInfo['details'] = $details;
$promotionsInfo = array_merge($promotionsInfo, $details['give'] ?? []);
$promotionsInfo['is_valid'] = $details['is_valid'] ?? 0;
$promotionsInfo['reach_threshold'] = $details['reach_threshold'] ?? 0;
$promotionsInfo['sum_promotions_price'] = $details['sum_promotions_price'] ?? 0;
$promotionsInfo['differ_threshold'] = $details['differ_threshold'] ?? 0;//下一级优惠差多少元|件
$promotionsInfo['differ_price'] = $details['differ_price'] ?? 0;//下一级优惠金额
$promotionsInfo['differ_discount'] = $details['differ_discount'] ?? 0;//下一级享受折扣
$giveProductIds = $giveCart = [];
if (isset($promotionsInfo['give_product']) && $promotionsInfo['give_product']) {
$giveCart = $this->createGiveProductCart($uid, (int)$id, $promotionsInfo['give_product'], $promotionsInfo);
if ($giveCart) {
$giveProductIds = array_column($giveProductIds, 'product_id');
$giveCartList = array_merge($giveCartList, $giveCart);
}
}
$promotionsInfo['product_ids'] = $promotionsDetail[$id] ?? [];
$promotionsInfo['product_ids'] = array_merge($promotionsInfo['product_ids'], $giveProductIds);
$promotionsInfo['give_product'] = $giveProductIds;
$giveCoupon = array_merge($giveCoupon, $promotionsInfo['give_coupon'] ?? []);
$giveProduct = array_merge($giveProduct, $promotionsInfo['give_product'] ?? []);
$giveIntegral = bcadd((string)$giveIntegral, (string)($promotionsInfo['give_integral'] ?? 0), 0);
$usePromotions[] = $promotionsInfo;
}
return [$usePromotions, $giveIntegral, $giveCoupon, $giveCartList];
}
/**
* 验证购物车商品规格是否在活动中
* @param string $unique
* @param array $uniques
* @param int $product_partake_type
* @return bool
*/
public function checkProductUnque(string $unique, array $uniques, int $product_partake_type = 1)
{
if (!$unique) {
return false;
}
switch ($product_partake_type) {
case 1:
case 4:
case 5:
break;
case 2:
if (!$uniques) {
return true;
}
if (!in_array($unique, $uniques)) {
return false;
}
break;
case 3:
if (!$uniques) {
return true;
}
if (in_array($unique, $uniques)) {
return false;
}
break;
default:
return false;
break;
}
return true;
}
/**
* 计算商品优惠价格
* @param int $uid
* @param array $cartList
* @param int $couponId
* @param bool $isCart
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function computedPromotions(int $uid, array $cartList, int $couponId = 0, bool $isCart = false)
{
$giveIntegral = $couponPrice = 0;
$giveCoupon = $giveCartList = $usePromotions = $useCoupon = [];
if ($cartList) {
$productIds = array_column($cartList, 'product_id');
// $productArr = array_combine($productIds, $cartList);
$with = ['products' => function ($query) {
$query->field('promotions_id,product_id,is_all,unique');
}, 'giveProducts' => function ($query) {
$query->field('type,promotions_id,product_id,limit_num,surplus_num,unique')->with(['productInfo' => function ($query) {
$query->field('id,store_name');
}]);
}, 'giveCoupon' => function ($query) {
$query->field('type,promotions_id,coupon_id,limit_num,surplus_num')->with(['coupon' => function ($query) {
$query->field('id,type,coupon_type,coupon_title,coupon_price,use_min_price,remain_count,is_permanent');
}]);
}, 'promotions' => function ($query) {
$query->field('id,pid,promotions_type,promotions_cate,threshold_type,threshold,discount_type,n_piece_n_discount,discount,give_integral,give_coupon_id,give_product_id,give_product_unique')->with(['giveProducts' => function ($query) {
$query->field('type,promotions_id,product_id,limit_num,surplus_num,unique')->with(['productInfo' => function ($query) {
$query->field('id,store_name');
}]);
}, 'giveCoupon' => function ($query) {
$query->field('type,promotions_id, coupon_id,limit_num,surplus_num')->with(['coupon' => function ($query) {
$query->field('id,type,coupon_type,coupon_title,coupon_price,use_min_price,remain_count,is_permanent');
}]);
}]);
}];
//获取购物车商品所有活动
[$promotionsArr, $productDetails, $promotionsDetail] = $this->getProductsPromotionsDetail($productIds, '*', $with, [1, 2, 3, 4]);
$computedArr = [];
$usePromotionsIds = [];
if ($promotionsArr) {
$promotionsArr = array_combine(array_column($promotionsArr, 'id'), $promotionsArr);
//获取商品活动是否叠加计算
[$productDetails, $promotionsDetail, $productComputedArr] = $this->getProductComputedPromotions($promotionsArr, $productDetails, $promotionsDetail);
//计算优惠金额
$computedArr = $this->doComputeV1($uid, $cartList, $promotionsDetail, $promotionsArr, $productComputedArr);
foreach ($cartList as &$cart) {
$sum_promotions_true_price = 0;//所有单件优惠金额累加
$sum_promotions_price = 0;//所有优惠金额累加
$product_id = (int)($cart['product_id'] ?? 0);
$unique = $cart['product_attr_unique'] ?? '';
$promotionsIds = $productDetails[$product_id] ?? [];
$cart['promotions_id'] = [];
if (!$promotionsIds || !$unique) {
continue;
}
$price = isset($cart['productInfo']['attrInfo']['price']) ? $cart['productInfo']['attrInfo']['price'] : ($cart['productInfo']['price'] ?? 0);
$total_price = $cart['total_price'] ?? bcmul((string)$price, (string)$cart['cart_num'], 2);
//叠加
$typeArr = $productComputedArr[$product_id]['typeArr'] ?? [];
$isOverly = isset($productComputedArr[$product_id]['is_overlay']) && $productComputedArr[$product_id]['is_overlay'];
foreach ($promotionsIds as $promotions_id) {
$promotionsInfo = $promotionsArr[$promotions_id] ?? [];
$trueDetail = $computedArr[$promotions_id] ?? [];
if (!$promotionsInfo || !$trueDetail) continue;
if (!$this->checkProductUnque($unique, $trueDetail['uniques'][$product_id] ?? [], (int)$promotionsInfo['product_partake_type'])) {
continue;
}
$trueArr = $trueDetail[$unique] ?? [];
if (!isset($trueDetail['is_valid']) || $trueDetail['is_valid'] == 0) {
if ($isCart) {//购物车不满足也展示
$cart['promotions_id'][] = $promotions_id;
}
} else {
//活动叠加商品 单件总计优惠金额
if ($isOverly) {
$cart['promotions_id'][] = $promotions_id;
$sum_promotions_true_price = bcadd((string)$sum_promotions_true_price, (string)($trueArr['promotions_true_price'] ?? 0), 2);
$sum_promotions_price = bcadd((string)$sum_promotions_price, (string)($trueArr['sum_promotions_price'] ?? 0), 2);
} else { //不叠加取最优惠
if ($isCart) {
$cart['promotions_id'][] = $promotions_id;
}
if ($sum_promotions_true_price < ($trueArr['promotions_true_price'] ?? 0)) {
$sum_promotions_true_price = $trueArr['promotions_true_price'] ?? 0;
$sum_promotions_price = $trueArr['sum_promotions_price'] ?? 0;
if (!$isCart) {
$cart['promotions_id'] = [$promotions_id];
}
}
}
}
}
if ($sum_promotions_true_price) {
//是否有限时折扣
if (in_array(1, $typeArr)) {
$true_price = (float)bcsub((string)$price, (string)$sum_promotions_true_price, 2);
$pay_price = (float)bcsub((string)$total_price, (string)$sum_promotions_price, 2);
$true_price = max($true_price, 0);
$pay_price = max($pay_price, 0);
//比较与用户等级、svip优惠后金额
if ($true_price && $cart['truePrice'] > $true_price) {
$cart['truePrice'] = $true_price;
$cart['pay_price'] = $pay_price;
$cart['promotions_true_price'] = $sum_promotions_true_price;
$cart['sum_promotions_price'] = $sum_promotions_price;
$cart['price_type'] = 'promotions';
$cart['vip_truePrice'] = 0;
} else { //使用了用户等级、svip价格 去掉优惠活动关联
$cart['promotions_id'] = [];
}
} else {//svip 用户等级价格上继续优惠
$true_price = (float)bcsub((string)$cart['truePrice'], (string)$sum_promotions_true_price, 2);
$pay_price = (float)bcsub((string)$cart['pay_price'], (string)$sum_promotions_price, 2);
$cart['truePrice'] = max($true_price, 0);
$cart['pay_price'] = max($pay_price, 0);
$cart['promotions_true_price'] = $sum_promotions_true_price;
$cart['sum_promotions_price'] = $sum_promotions_price;
$cart['price_type'] = 'promotions';
}
}
$usePromotionsIds = array_unique(array_merge($usePromotionsIds, $cart['promotions_id']));
//排出不叠加 去最优的其他优惠活动金额
foreach ($promotionsIds as $promotions_id) {
//使用了优惠会跳过
if (in_array($promotions_id, $cart['promotions_id'])) continue;
$trueDetail = $computedArr[$promotions_id] ?? [];
$trueArr = $trueDetail[$unique] ?? [];
if (!$trueDetail || !$trueArr) {
continue;
}
$sum_promotions_price = bcsub((string)($trueDetail['sum_promotions_price'] ?? '0'), (string)($trueArr['sum_promotions_price'] ?? 0), 2);
$computedArr[$promotions_id]['sum_promotions_price'] = max($sum_promotions_price, 0);//这个商品的没有使用优惠活动
unset($computedArr[$promotions_id][$unique]);
}
}
}
//使用优惠券
$coupon_id = 0;
if ($uid) {
if ($usePromotionsIds) [$usePromotions, $giveIntegral, $giveCoupon, $giveCartList] = $this->getUsePromotiosnInfo($uid, $usePromotionsIds, $promotionsArr, $computedArr);
if ($couponId) {
try {
[$useCoupon, $couponPrice] = $this->useCoupon($couponId, $uid, $cartList, $usePromotions);
} catch (\Throwable $e) {
}
$coupon_id = $couponId;
} else {
//获取最优优惠券
if ($isCart) {
/** @var StoreCouponIssueServices $couponServices */
$couponServices = app()->make(StoreCouponIssueServices::class);
$couponServices->setItem('isVip', $this->getItem('isVip', 0));
$useCoupon = $couponServices->getCanUseCoupon($uid, $cartList, $usePromotions);
$couponServices->reset();
$couponPrice = $useCoupon['true_coupon_price'] ?? 0;
$coupon_id = $useCoupon['used']['id'] ?? 0;
}
}
//计算每一件商品优惠券优惠金额
if ($coupon_id && $useCoupon && $couponPrice) {
/** @var StoreOrderComputedServices $computedServices */
$computedServices = app()->make(StoreOrderComputedServices::class);
$payPrice = $computedServices->getOrderSumPrice($cartList, 'pay_price', 'false');
if ($couponPrice > $payPrice) {
$couponPrice = $payPrice;
}
/** @var StoreOrderCreateServices $createServices */
$createServices = app()->make(StoreOrderCreateServices::class);
$priceData = ['coupon_id' => $coupon_id, 'coupon_price' => $couponPrice];
$cartList = $createServices->computeOrderProductCoupon($cartList, $priceData, $usePromotions);
}
}
//获取赠送积分、优惠券、商品
[$cartList, $computedArr, $useGivePromotionsIds] = $this->getPromotionsGive($uid, $cartList, $computedArr, $promotionsDetail, $productDetails, $promotionsArr, $isCart);
$usePromotionsIds = array_unique(array_merge($usePromotionsIds, $useGivePromotionsIds));
//整合返回数据
if ($usePromotionsIds) [$usePromotions, $giveIntegral, $giveCoupon, $giveCartList] = $this->getUsePromotiosnInfo($uid, $usePromotionsIds, $promotionsArr, $computedArr);
}
return [$cartList, $couponPrice, $useCoupon, $usePromotions, $giveIntegral, $giveCoupon, $giveCartList];
}
/**
* 实际计算商品优惠金额
* @param int $uid
* @param array $cartList
* @param array $promotionsDetail
* @param array $promotionsArr
* @param array $productComputedArr
* @return array
*/
public function doComputeV1(int $uid, array $cartList, array $promotionsDetail, array $promotionsArr, array $productComputedArr)
{
$computedArr = [];
if (!$cartList || !$promotionsDetail) {
return $computedArr;
}
/** @var StoreOrderCartInfoServices $storeOrderCartInfoServices */
$storeOrderCartInfoServices = app()->make(StoreOrderCartInfoServices::class);
foreach ($promotionsDetail as $promotions_id => $productIds) {
$promotions = $promotionsArr[$promotions_id] ?? [];
$productCount = count($productIds);
if (!$promotions || !$productCount || $promotions['promotions_type'] == 4) continue;
//验证商品规格是否满足活动
[$productIds, $uniques] = $this->checkProductCanUsePromotions($productIds, $cartList, $promotions);
if (!$productIds) {
continue;
}
//验证限量
if ($uid) {
$productIds = $this->checkPromotionsLimit($storeOrderCartInfoServices, $uid, (int)$promotions_id, $productIds, $promotions);
if (!$productIds) {
continue;
}
}
$promotions_type = (int)$promotions['promotions_type'] ?? 1;
$this->setItem('productComputedArr', $productComputedArr)->setItem('computedArr', $computedArr);
[$sumPrice, $sumCount, $promotionsProductArr] = $this->getPromotionsProductInfo($promotions_type, $productIds, $cartList, $uniques, (int)$promotions['product_partake_type']);
$this->reset();
$compute_price = $sum_promotions_true_price = $sum_promotions_price = 0;
$data = ['is_valid' => 0, 'reach_threshold' => 0, 'differ_threshold' => 0, 'promotions_type' => $promotions['promotions_type'], 'sum_promotions_price' => 0, 'product_id' => [], 'uniques' => $uniques];
switch ($promotions['promotions_type']) {
case 1://限制折扣
$p = $promotions['promotions'][0] ?? [];
if ($p) {
$promotionsDiscount = $this->computedDiscount($p['discount'], 100, 2);
$sum_promotions_price = 0;
if ($promotions['is_limit']) {//是否限量
$limit_num = $promotions['limit_num'];
if ($limit_num <= 0) break;
$sumCount = 0;
foreach ($promotionsProductArr as $product) {
$product_id = $product['product_id'];
$unique = $product['product_attr_unique'];
$price = $product['price'];
$cartNum = $newCartNum = $product['cart_num'] ?? 1;
if ($sumCount >= $limit_num) {
break;
}
if (bcadd((string)$sumCount, (string)$cartNum) > $limit_num) {//加上下一个商品数量 大于限购数量
$newCartNum = bcsub((string)$limit_num, (string)$sumCount);
$sumCount = $limit_num;
}
$promotions_true_price = (float)bcmul((string)$price, (string)$promotionsDiscount, 2);
$one_promotions_sum_price = (float)bcmul((string)$promotions_true_price, (string)$cartNum, 2);
if ($promotions_true_price < 0.01) {//实际优惠小于0.01 就不计算优惠
$promotions_true_price = 0;
}
//部分享受折扣
if ($cartNum != $newCartNum) {
$one_promotions_sum_price = (float)bcmul((string)$promotions_true_price, (string)$newCartNum, 2);
$promotions_true_price = bcdiv((string)$one_promotions_sum_price, (string)$cartNum, 2);
}
$data[$unique] = ['product_id' => $product_id, 'unique' => $unique, 'price' => $price, 'sum_promotions_price' => $one_promotions_sum_price, 'promotions_true_price' => $promotions_true_price];
$sum_promotions_price = bcadd((string)$sum_promotions_price, (string)$one_promotions_sum_price, 2);
}
} else {
foreach ($promotionsProductArr as $product) {
$product_id = $product['product_id'];
$unique = $product['product_attr_unique'];
$price = $product['price'];
$promotions_true_price = (float)bcmul((string)$price, (string)$promotionsDiscount, 2);
$one_promotions_sum_price = (float)bcmul((string)$promotions_true_price, (string)($product['cart_num'] ?? 1), 2);
if ($promotions_true_price < 0.01) {//实际优惠小于0.01 就不计算优惠
$promotions_true_price = 0;
}
$data[$unique] = ['product_id' => $product_id, 'unique' => $unique, 'price' => $price, 'sum_promotions_price' => $one_promotions_sum_price, 'promotions_true_price' => $promotions_true_price];
$sum_promotions_price = bcadd((string)$sum_promotions_price, (string)$one_promotions_sum_price, 2);
}
}
$data['is_valid'] = 1;
$data['sum_promotions_price'] = $sum_promotions_price;
}
break;
case 2://n件n折
$p = $promotions['promotions'][0] ?? [];
if ($p) {
$promotionsDiscount = $this->computedDiscount($p['discount'], 100, 2);
if ($sumCount >= $p['threshold']) {//满足
$useProductArr = $promotionsProductArr;
//商品价格升序
array_multisort(array_column($promotionsProductArr, 'price'), SORT_ASC, $promotionsProductArr);
$minPrice = $promotionsProductArr[0]['price'] ?? 0;
//算出n件实际优惠金额
$sum_promotions_price = (float)bcmul((string)$minPrice, (string)$promotionsDiscount, 2);
if ($sum_promotions_price < 0.01) {
$sum_promotions_price = 0;
}
if ($sum_promotions_price) {
$useCount = $productCount;
$useSumPrice = $sumPrice;
$count = count($useProductArr);
$compute_price = 0;
array_multisort(array_column($useProductArr, 'cart_num'), SORT_DESC, $useProductArr);
foreach ($useProductArr as $value) {
$product_id = $value['product_id'];
$unique = $value['product_attr_unique'];
$price = $value['price'];
if ($count > 1) {
// 提高分摊比例精度从4位到8位减少精度损失
$promotions_true_price = (float)bcmul((string)bcdiv((string)$price, (string)$useSumPrice, 8), (string)$sum_promotions_price, 2);
$one_promotions_sum_price = (float)bcmul((string)$promotions_true_price, (string)$value['cart_num'], 2);
$compute_price = (float)bcadd((string)$compute_price, (string)$one_promotions_sum_price, 2);
} else {
// 最后一件商品使用补偿机制,确保总优惠金额精确
$one_promotions_sum_price = (float)bcsub((string)$sum_promotions_price, (string)$compute_price, 2);
$promotions_true_price = (float)bcdiv((string)$one_promotions_sum_price, (string)$value['cart_num'], 4);
}
$data[$unique] = ['product_id' => $product_id, 'unique' => $unique, 'price' => $price, 'sum_promotions_price' => $one_promotions_sum_price, 'promotions_true_price' => $promotions_true_price];
$count--;
}
}
$data['is_valid'] = 1;
$data['reach_threshold'] = $p['threshold'];
$data['sum_promotions_price'] = $sum_promotions_price;
} else {
$data['differ_discount'] = $p['discount'];
$data['differ_threshold'] = bcsub((string)$p['threshold'], (string)$sumCount, 0);
}
}
break;
case 3://满减折
if ($promotions['promotions_cate'] == 1) {//阶梯 享受最高
$valid = $invalid = [];
foreach ($promotions['promotions'] as $key => $p) {
if (($p['threshold_type'] == 1 ? $sumPrice : $sumCount) >= $p['threshold']) {
$valid = $p;
} else {
$invalid = $p;
break;
}
}
if ($valid) {
if ($valid['discount_type'] == 1) {//免减
$sum_promotions_price = (string)$valid['discount'];
} else {//折扣
$promotionsDiscount = $this->computedDiscount($valid['discount'], 100, 2);
$sum_promotions_price = 0;
}
array_multisort(array_column($promotionsProductArr, 'cart_num'), SORT_DESC, $promotionsProductArr);
foreach ($promotionsProductArr as $product) {
$product_id = $product['product_id'];
$unique = $product['product_attr_unique'];
$price = $product['price'];
if ($valid['discount_type'] == 1) {
if ($productCount > 1) {
// 提高分摊比例精度从4位到8位减少精度损失
$promotions_true_price = (float)bcmul((string)bcdiv((string)$price, (string)$sumPrice, 8), (string)$sum_promotions_price, 2);
$one_promotions_sum_price = (float)bcmul((string)$promotions_true_price, (string)$product['cart_num'], 2);
$compute_price = bcadd((string)$compute_price, (string)$one_promotions_sum_price, 2);
} else {
// 最后一件商品使用补偿机制,确保总优惠金额精确
$one_promotions_sum_price = (float)bcsub((string)$sum_promotions_price, (string)$compute_price, 2);
$promotions_true_price = (float)bcdiv((string)$one_promotions_sum_price, (string)$product['cart_num'], 4);
}
} else {
$promotions_true_price = (float)bcmul((string)$price, (string)$promotionsDiscount, 2);
if ($promotions_true_price < 0.01) {//实际优惠小于0.01 就不计算优惠
$promotions_true_price = 0;
}
$one_promotions_sum_price = (float)bcmul((string)$promotions_true_price, (string)($product['cart_num'] ?? 1), 2);
$sum_promotions_price = (float)bcadd((string)$sum_promotions_price, (string)$one_promotions_sum_price, 2);
}
$data[$unique] = ['product_id' => $product_id, 'unique' => $unique, 'price' => $price, 'sum_promotions_price' => $one_promotions_sum_price, 'promotions_true_price' => $promotions_true_price];
$productCount--;
}
$data['is_valid'] = 1;
$data['reach_threshold'] = $valid['threshold'];
$data['sum_promotions_price'] = $sum_promotions_price;
}
if ($invalid) {
if ($valid) $data['is_valid'] = 2;
if ($invalid['discount_type'] == 1) {//免减
$data['differ_price'] = $invalid['discount'];
} else {
$data['differ_discount'] = $invalid['discount'];
}
$data['differ_threshold'] = bcsub((string)$invalid['threshold'], (string)($invalid['threshold_type'] == 1 ? $sumPrice : $sumCount), 0);
}
} else {//循环
$p = $promotions['promotions'][0] ?? [];
$validCount = floor((float)bcdiv((string)($p['threshold_type'] == 1 ? $sumPrice : $sumCount), (string)$p['threshold'], 2));
if ($validCount) {//满足次数
$promotionsDiscount = $this->computedDiscount($p['discount'], 100, 2);
$sum_promotions_price = 0;
$useProductArr = $promotionsProductArr;
if ($p['discount_type'] == 2 && $p['threshold_type'] == 2) {//满n件 打n折
// $suprplusDiscount = bcsub((string)$productCount, bcmul((string)$validCount, (string)$p['threshold'], 0), 0);
//商品价格升序
array_multisort(array_column($promotionsProductArr, 'price'), SORT_ASC, $promotionsProductArr);
$minPrice = $promotionsProductArr[0]['price'] ?? 0;
//算出n件实际优惠金额
$sum_promotions_price = (float)bcmul((string)$minPrice, (string)$promotionsDiscount, 2);
if ($sum_promotions_price < 0.01) {
$sum_promotions_price = 0;
}
$sum_promotions_price = bcmul((string)$validCount, (string)$sum_promotions_price, 2);
} else {
if ($p['discount_type'] == 1) {//免减n元、n件
$sum_promotions_price = bcmul((string)$validCount, (string)$p['discount'], 2);
} else {//打折
if ($p['threshold_type'] == 1) {//满n元
$sum_promotions_price = bcmul((string)bcmul((string)$validCount, (string)$p['threshold'], 2), (string)$promotionsDiscount, 2);
}
}
}
$useCount = 0;
array_multisort(array_column($useProductArr, 'cart_num'), SORT_DESC, $useProductArr);
foreach ($useProductArr as $product) {
$price = $product['price'];
$product_id = $product['product_id'];
$unique = $product['product_attr_unique'];
if ($productCount > 1) {
// 提高分摊比例精度从4位到8位减少精度损失
$promotions_true_price = (float)bcmul((string)bcdiv((string)$price, (string)$sumPrice, 8), (string)$sum_promotions_price, 2);
$one_promotions_sum_price = (float)bcmul((string)$promotions_true_price, (string)$product['cart_num'], 2);
$compute_price = (float)bcadd((string)$compute_price, (string)$one_promotions_sum_price, 2);
} else {
// 最后一件商品使用补偿机制,确保总优惠金额精确
$one_promotions_sum_price = (float)bcsub((string)$sum_promotions_price, (string)$compute_price, 2);
$promotions_true_price = (float)bcdiv((string)$one_promotions_sum_price, (string)$product['cart_num'], 4);
}
// }
$data[$unique] = ['product_id' => $product_id, 'unique' => $unique, 'price' => $price, 'sum_promotions_price' => $one_promotions_sum_price, 'promotions_true_price' => $promotions_true_price];
$productCount--;
}
$data['is_valid'] = 1;
$data['reach_threshold'] = $p['threshold'];
$data['sum_promotions_price'] = $sum_promotions_price;
} else {
if ($p['discount_type'] == 1) {//免减
$data['differ_price'] = $p['discount'];
} else {
$data['differ_discount'] = $p['discount'];
}
$data['differ_threshold'] = bcsub((string)$p['threshold'], (string)($p['threshold_type'] == 1 ? $sumPrice : $sumCount), 0);
}
}
break;
case 4://满送
break;
default:
break;
}
$data['give'] = ['give_integral' => 0, 'give_coupon' => [], 'give_product' => []];
$computedArr[$promotions_id] = $data;
}
return $computedArr;
}
/**
* 使用优惠卷
* @param int $couponId
* @param int $uid
* @param array $cartInfo
* @param array $promotions
* @return array
*/
public function useCoupon(int $couponId, int $uid, array $cartInfo, array $promotions)
{
//使用优惠劵
$couponPrice = 0;
$couponInfo = [];
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((string)$price, (string)$cart['pay_price'], 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((string)$price, (string)$cart['pay_price'], 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((string)$price, (string)$cart['pay_price'], 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)$cart['pay_price'], 2);
$count++;
}
}
}
break;
}
if ($count && $couponInfo['use_min_price'] <= $price) {
$flag = true;
}
if (!$flag) {
// return [[], 0];
throw new ValidateException('不满足优惠劵的使用条件!');
}
//满减券
if ($couponInfo['coupon_type'] == 1) {
$couponPrice = $couponInfo['coupon_price'] > $price ? $price : $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);
}
}
}
return [$couponInfo, $couponPrice];
}
/**
* 实际获取优惠活动赠送赠品
* @param int $uid
* @param array $cartList
* @param array $computedArr
* @param array $promotionsDetail
* @param array $productDetails
* @param array $promotionsArr
* @param bool $isCart
* @return array
*/
public function getPromotionsGive(int $uid, array $cartList, array $computedArr, array $promotionsDetail, array $productDetails, array $promotionsArr, bool $isCart = false)
{
$usePromotionsIds = [];
if (!$cartList || !$promotionsDetail) {
return [$cartList, $computedArr, $usePromotionsIds];
}
foreach ($promotionsDetail as $promotions_id => $productIds) {
$promotions = $promotionsArr[$promotions_id] ?? [];
$productCount = count($productIds);
if (!$promotions || !$productCount || $promotions['promotions_type'] != 4) continue;
//验证商品规格是否满足活动
[$productIds, $uniques] = $this->checkProductCanUsePromotions($productIds, $cartList, $promotions);
if (!$productIds) {
continue;
}
$promotions_type = (int)$promotions['promotions_type'] ?? 1;
[$sumPrice, $sumCount, $promotionsProductArr] = $this->getPromotionsProductInfo($promotions_type, $productIds, $cartList, $uniques, (int)$promotions['product_partake_type'], true);
$give_product_id = [];
$give_coupon_ids = [];
$give_integral = 0;
$data = ['is_valid' => 0, 'reach_threshold' => 0, 'differ_threshold' => 0, 'promotions_type' => $promotions['promotions_type'], 'sum_promotions_price' => 0, 'product_id' => []];
if ($promotions['promotions_cate'] == 1) {//阶梯
$valid = $invalid = [];
foreach ($promotions['promotions'] as $key => $p) {
if (($p['threshold_type'] == 1 ? $sumPrice : $sumCount) >= $p['threshold']) {
$valid = $p;
} else {
$invalid = $p;
break;
}
}
if ($valid) {
if ($valid['give_integral']) {
$give_integral = bcadd((string)$give_integral, (string)$valid['give_integral'], 0);
}
if ($valid['give_coupon_id']) {
$coupon_ids = is_string($valid['give_coupon_id']) ? explode(',', $valid['give_coupon_id']) : $valid['give_coupon_id'];
$give_coupon_ids = array_merge($give_coupon_ids, $coupon_ids);
}
if ($valid['giveProducts']) {
foreach ($valid['giveProducts'] as $value) {
if (isset($give_product_id[$value['unique']])) {
$give_product_id[$value['unique']]['cart_num'] = $give_product_id[$value['unique']]['cart_num'] + 1;
} else {
$give_product_id[$value['unique']] = ['promotions_id' => $value['promotions_id'], 'unique' => $value['unique'], 'product_id' => $value['product_id'], 'cart_num' => 1];
}
}
}
$data['is_valid'] = 1;
$data['reach_threshold'] = $valid['threshold'];
}
if ($invalid) {
if ($valid) $data['is_valid'] = 2;
$data['differ_threshold'] = bcsub((string)$invalid['threshold'], (string)($invalid['threshold_type'] == 1 ? $sumPrice : $sumCount), 0);
}
} else {//循环
$p = $promotions['promotions'][0] ?? [];
$validCount = floor((float)bcdiv((string)($p['threshold_type'] == 1 ? $sumPrice : $sumCount), (string)$p['threshold'], 2));
if ($validCount) {//满足次数
if ($p['give_integral']) {
$give_integral = bcadd((string)$give_integral, (string)bcmul((string)$validCount, (string)$p['give_integral'], 0), 0);
}
if ($p['give_coupon_id']) {
$coupon_ids = is_string($p['give_coupon_id']) ? explode(',', $p['give_coupon_id']) : $p['give_coupon_id'];
$give_coupon_ids = array_merge($give_coupon_ids, $coupon_ids);
}
if ($p['giveProducts']) {
foreach ($p['giveProducts'] as $value) {
if (!$value || !isset($value['surplus_num']) || !$value['surplus_num']) continue;
$minValidCunt = min($validCount, $value['surplus_num']);
if (isset($give_product_id[$value['unique']])) {
$give_product_id[$value['unique']]['cart_num'] = bcadd((string)$give_product_id[$value['unique']]['cart_num'], (string)$minValidCunt);
} else {
$give_product_id[$value['unique']] = ['promotions_id' => $value['promotions_id'], 'unique' => $value['unique'], 'product_id' => $value['product_id'], 'cart_num' => $minValidCunt];
}
}
}
$data['is_valid'] = 1;
$data['reach_threshold'] = $p['threshold'];
} else {
$data['differ_threshold'] = bcsub((string)$p['threshold'], (string)($p['threshold_type'] == 1 ? $sumPrice : $sumCount), 0);
}
}
$ids = [];
//验证优惠券限量
if ($give_coupon_ids) {
foreach ($give_coupon_ids as $give_coupon_id) {
foreach ($promotions['promotions'] as $value) {
$giveCoupon = $value['giveCoupon'] ?? [];
if ($giveCoupon) $giveCoupon = array_combine(array_column($giveCoupon, 'coupon_id'), $giveCoupon);
if ($giveCoupon && ($giveCoupon[$give_coupon_id]['surplus_num'] ?? 0) >= 1) {
$ids[] = $give_coupon_id;
break;
}
}
}
}
$data['give'] = ['give_integral' => $give_integral, 'give_coupon' => $ids, 'give_product' => $give_product_id];
$computedArr[$promotions_id] = $data;
if (!$isCart && $data['is_valid'] > 0) {
$usePromotionsIds[] = $promotions_id;
} elseif ($isCart) {
$usePromotionsIds[] = $promotions_id;
}
}
foreach ($cartList as &$cart) {
$product_id = $cart['product_id'] ?? 0;
$promotionsIds = $productDetails[$product_id] ?? [];
if (!$promotionsIds) {
continue;
}
$useIds = array_intersect($promotionsIds, $usePromotionsIds);
$cart['promotions_id'] = array_unique(array_merge($cart['promotions_id'] ?? [], $useIds));
}
return [$cartList, $computedArr, $usePromotionsIds];
}
/**
* 生成赠送商品购物车数据
* @param int $uid
* @param int $promotions_id
* @param array $giveProduct
* @param array promotions
* @param int $store_id
* @return array
*/
public function createGiveProductCart(int $uid, int $promotions_id, array $giveProduct, array $promotions, int $store_id = 0)
{
$cart = [];
if ($giveProduct) {
/** @var StoreOrderCreateServices $storeOrderCreateService */
$storeOrderCreateService = app()->make(StoreOrderCreateServices::class);
/** @var StoreCartServices $storeCartServices */
$storeCartServices = app()->make(StoreCartServices::class);
$promotionsArr = $promotions['promotions'] ?? [];
foreach ($giveProduct as $unique => $give) {
$product_id = $give['product_id'] ?? 0;
$cart_num = $give['cart_num'] ?? 1;
if (!$product_id) {
continue;
}
try {
[$attrInfo, $product_attr_unique, $bargainPriceMin, $cart_num, $productInfo] = $storeCartServices->checkProductStock($uid, (int)$product_id, (int)$cart_num, $unique, true, 0, 0, 0, true);
} catch (\Throwable $e) {
continue;
}
$is_limit = false;
foreach ($promotionsArr as $key => $value) {
$giveProducts = $value['giveProducts'] ?? [];
if ($giveProducts) $giveProducts = array_combine(array_column($giveProducts, 'product_id'), $giveProducts);
if ($giveProducts && $cart_num <= ($giveProducts[$product_id]['surplus_num'] ?? 0)) {
$is_limit = true;
break;
}
}
if (!$is_limit) {
continue;
}
$key = $storeOrderCreateService->getNewOrderId((string)$uid);
$info['id'] = $key;
$info['type'] = 0;
$info['product_type'] = $productInfo['product_type'];
$info['promotions_id'] = [$give['promotions_id'] ?? $promotions_id];
$info['activity_id'] = 0;
$info['discount_product_id'] = 0;
$info['product_id'] = $product_id;
$info['is_gift'] = 1;
$info['is_valid'] = 1;
$info['product_attr_unique'] = $product_attr_unique;
$info['cart_num'] = $cart_num;
$info['productInfo'] = $productInfo ? $productInfo->toArray() : [];
if (isset($info['productInfo']['delivery_type'])) {
$info['productInfo']['delivery_type'] = is_string($info['productInfo']['delivery_type']) ? explode(',', $info['productInfo']['delivery_type']) : $info['productInfo']['delivery_type'];
} else {
$info['productInfo']['delivery_type'] = [];
}
$info['productInfo']['attrInfo'] = $attrInfo->toArray();
$info['attrStatus'] = (bool)$info['productInfo']['attrInfo'];
$info['sum_price'] = $info['productInfo']['attrInfo']['price'] ?? $info['productInfo']['price'] ?? 0;
$info['total_price'] = bcmul((string)$info['sum_price'], (string)$info['cart_num'], 2);
$info['pay_price'] = $info['truePrice'] = 0;
$info['vip_truePrice'] = 0;
$info['trueStock'] = $info['productInfo']['attrInfo']['stock'] ?? 0;
$info['costPrice'] = $info['productInfo']['attrInfo']['cost'] ?? 0;
$info['limit_num'] = $giveProducts[$product_id]['limit_num'] ?? 0;
$cart[] = $info;
}
}
return $cart;
}
/**
* 根据活动类别查询商品
* @param array $type
* @return array|array[]
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function collectProductByType(array $type = [])
{
if ($type) {
$where['promotions_type'] = $type;
}
$where['type'] = 1;
$where['store_id'] = 0;
$where['pid'] = 0;
$where['is_del'] = 0;
$where['status'] = 1;
$where['promotionsTime'] = true;
//存在一个全部商品折扣优惠活动 直接返回商品
if ($this->dao->count($where + ['product_partake_type' => 1])) {
$product_where = [];
} else {
/** @var StoreProductRelationServices $storeProductRelationServices */
$storeProductRelationServices = app()->make(StoreProductRelationServices::class);
/** @var StorePromotionsAuxiliaryServices $promotionsAuxiliaryServices */
$promotionsAuxiliaryServices = app()->make(StorePromotionsAuxiliaryServices::class);
$promotionsIds = $this->getAllShowActivityIds($type, 'id,promotions_type,product_partake_type');
$product_ids = [];
$product_where = ['ids' => []];
foreach ($promotionsIds as $item) {
$promotionsAuxiliaryData = $promotionsAuxiliaryServices->getPromotionsAuxiliaryCache($item['id']);
switch ($item['product_partake_type']) {
case 1://所有商品
break;
case 2://选中商品参与
$product_ids = $promotionsAuxiliaryData;
break;
case 3:
$item['product_count'] = 0;
break;
case 4://品牌
$product_ids = $promotionsAuxiliaryData ? $storeProductRelationServices->getIdsByWhere(['type' => 2, 'relation_id' => $promotionsAuxiliaryData]) : [];
break;
case 5://商品标签
$product_ids = $promotionsAuxiliaryData ? $storeProductRelationServices->getIdsByWhere(['type' => 3, 'relation_id' => $promotionsAuxiliaryData]) : [];
break;
}
$product_where['ids'] = array_merge($product_where['ids'], $product_ids ? $product_ids : []);
}
}
return $product_where;
}
/**
* 获取凑单商品ids
* @param int $promotions_id
* @return array
*/
public function collectProductById(int $promotions_id)
{
$product_where = [];
$promotions = $this->dao->get($promotions_id, ['*']);
if ($promotions) {
$promotions = $promotions->toArray();
/** @var StoreProductRelationServices $storeProductRelationServices */
$storeProductRelationServices = app()->make(StoreProductRelationServices::class);
/** @var StorePromotionsAuxiliaryServices $promotionsAuxiliaryServices */
$promotionsAuxiliaryServices = app()->make(StorePromotionsAuxiliaryServices::class);
$promotionsAuxiliaryData = $promotionsAuxiliaryServices->getPromotionsAuxiliaryCache($promotions_id);
switch ($promotions['product_partake_type']) {
case 1://所有商品
break;
case 2://选中商品参与
$product_ids = $promotionsAuxiliaryData;
$product_where['ids'] = $product_ids;
break;
case 3:
$ids = is_string($promotions['product_id']) ? explode(',', $promotions['product_id']) : $promotions['product_id'];
if ($ids) {//商品全部规格 不参与 才不显示该商品
/** @var StorePromotionsAuxiliaryServices $auxiliaryService */
$auxiliaryService = app()->make(StorePromotionsAuxiliaryServices::class);
$ids = $auxiliaryService->getColumn(['promotions_id' => $promotions['id'], 'type' => 1, 'is_all' => 1, 'product_id' => $ids], 'product_id', '', true);
}
$product_where['not_ids'] = $ids;
break;
case 4://品牌
$product_ids = $promotionsAuxiliaryData ? $storeProductRelationServices->getIdsByWhere(['type' => 2, 'relation_id' => $promotionsAuxiliaryData]) : [];
$product_where['ids'] = $product_ids;
break;
case 5://商品标签
$product_ids = $promotionsAuxiliaryData ? $storeProductRelationServices->getIdsByWhere(['type' => 3, 'relation_id' => $promotionsAuxiliaryData]) : [];
$product_where['ids'] = $product_ids;
break;
}
}
return $product_where;
}
}