- 框架初始化
 - 安装插件
 - 修复PHP8.4报错
This commit is contained in:
2025-04-19 17:21:20 +08:00
commit c6a4e1f5f6
5306 changed files with 967782 additions and 0 deletions

View File

@@ -0,0 +1,235 @@
<?php
namespace addons\shopro\library\activity\provider;
use addons\shopro\library\activity\Activity as ActivityManager;
use addons\shopro\library\activity\contract\ActivityInterface;
use addons\shopro\library\activity\traits\CheckActivity;
use app\admin\model\shopro\activity\SkuPrice as ActivitySkuPriceModel;
abstract class Base implements ActivityInterface
{
use CheckActivity;
/**
* ActivityManager
*
* @var ActivityManager
*/
protected $manager = null;
protected $rules = [];
protected $message = [];
protected $default = [];
public function __construct(ActivityManager $activityManager)
{
$this->manager = $activityManager;
}
public function validate($data)
{
$data = array_merge($this->default, $data);
$validate = (new \think\Validate)->message($this->message)->rule($this->rules);
if (!$validate->check($data)) {
error_stop($validate->getError());
}
return $data;
}
public function check($params)
{
if ((isset($params['start_time']) && $params['start_time'] > $params['end_time']) || $params['end_time'] < date('Y-m-d H:i:s')) {
error_stop('请设置正确的活动时间');
}
if (isset($params['prehead_time']) && $params['prehead_time'] > $params['start_time']) {
error_stop('预热时间必须小于活动开始时间');
}
$rules = $this->validate($params['rules']);
// 存在折扣,将折扣按照从小到大排序
if (isset($rules['discounts']) && $rules['discounts']) {
// 处理展示优惠full 从小到大
$discounts = $rules['discounts'];
$discountsKeys = array_column($discounts, null, 'full');
ksort($discountsKeys);
$rules['discounts'] = array_values($discountsKeys); // 优惠按照 full 从小到大排序
}
$params['rules'] = $rules;
return $params;
}
/**
* 附加活动信息
*
* @param string $type
* @param array $rules
* @return array
*/
public function rulesInfo($type, $rules)
{
return $rules;
}
public function save($activity, $params = [])
{
}
public function showSkuPrice($skuPrice)
{
return $skuPrice;
}
public function formatTags($rules, $type)
{
}
public function formatTag($discountData)
{
}
public function formatTexts($rules, $type)
{
}
public function recoverSkuPrices($goods, $activity)
{
return $goods['sku_prices'];
}
public function buyCheck($buyInfo, $activity)
{
return $buyInfo;
}
public function buy($buyInfo, $activity)
{
return $buyInfo;
}
public function buyOk($order, $user)
{
return $order;
}
public function buyFail($order, $type)
{
return $order;
}
public function getPromoInfo($promo, ?array $data = [])
{
}
protected function promoGoodsData($promo)
{
$promo_goods_amount = '0'; // 该活动中商品的总价
$promo_goods_num = '0'; // 该活动商品总件数
$goodsIds = []; // 该活动中所有的商品 id
$promo_dispatch_amount = '0'; // 该活动中总运费
// 活动中的商品总金额,总件数,所有商品 id
foreach ($promo['goods'] as $buyInfo) {
$promo_goods_amount = bcadd($promo_goods_amount, (string)$buyInfo['goods_amount'], 2);
$promo_goods_num = bcadd($promo_goods_num, (string)$buyInfo['goods_num'], 2);
$goodsIds[] = $buyInfo['goods_id'];
$promo_dispatch_amount = bcadd($promo_dispatch_amount, (string)$buyInfo['dispatch_amount'], 2);
}
return compact(
"promo_goods_amount",
"promo_goods_num",
"promo_dispatch_amount",
"goodsIds"
);
}
/**
* 添加编辑活动规格type = stock 只编辑库存
*
* @param array $goodsList 商品列表
* @param int $activity_id 活动 id
* @param string $type type = all 全部编辑type = stock 只编辑库存
* @return void
*/
protected function saveSkuPrice($goodsList, $activity, \Closure $extCb = null)
{
//如果是编辑 先下架所有的规格产品,防止丢失历史销量数据;
$type = 'all';
if (request()->isPut() && $activity->status == 'ing') {
// 修改并且是进行中的活动,只能改库存
$type = 'stock';
}
ActivitySkuPriceModel::where('activity_id', $activity->id)->update(['status' => 'down']);
foreach ($goodsList as $key => $goods) {
$actSkuPrice[$key] = $goods['activity_sku_prices'];
foreach ($actSkuPrice[$key] as $ke => $skuPrice) {
if ($type == 'all') {
$current = $skuPrice;
// 处理 ext 回调
if ($extCb) {
$current = $extCb($current);
}
} else {
$current = [
'id' => $skuPrice['id'],
'stock' => $skuPrice['stock'],
'status' => $skuPrice['status'] // 这个要去掉,不能改参与状态
];
}
if ($current['id'] == 0) {
unset($current['id']);
}
unset($current['sales']);
$current['activity_id'] = $activity->id;
$current['goods_id'] = $goods['id'];
$actSkuPriceModel = new ActivitySkuPriceModel();
if (isset($current['id'])) {
// type == 'edit'
$actSkuPriceModel = $actSkuPriceModel->find($current['id']);
}
if ($actSkuPriceModel) {
unset($current['createtime'], $current['updatetime']);
$actSkuPriceModel->allowField(true)->save($current);
}
}
}
}
}

View File

@@ -0,0 +1,148 @@
<?php
namespace addons\shopro\library\activity\provider;
/**
* 满额包邮
*/
class FreeShipping extends Base
{
protected $rules = [
"type" => "require",
"full_num" => "require|float"
];
protected $message = [
];
protected $default = [
"type" => "money", // money=满足金额|num=满足件数
"province_except" => '', // 不包邮的省份
"city_except" => '', // 不包邮的城市
"district_except" => '', // 不包邮的地区
"district_text" => [], // 中文
"full_num" => 0
];
public function check($params, $activity_id = 0)
{
// 数据验证
$params = parent::check($params);
// 检测活动之间是否存在冲突
$this->checkActivityConflict($params, $params['goods_list'], $activity_id);
return $params;
}
public function formatTags($rules, $type)
{
$full_num = $rules['full_num'] ?? ($rules['discounts'][0]['full_num'] ?? 0); // activity_order 存的格式不一样是在 discount 里面包裹着
$tags[] = $this->formatTag([
'type' => $rules['type'],
'full_num' => $full_num,
]);
return array_values(array_filter($tags));
}
/**
* 格式化 discount 折扣为具体优惠标签
*
* @param array $discountData
* @return string
*/
public function formatTag($discountData)
{
$tag = '满' . $discountData['full_num'] . ($discountData['type'] == 'money' ? '元' : '件') . '包邮';
return $tag;
}
/**
* 格式化 discount 折扣为具体优惠详情
*/
public function formatTexts($rules, $type)
{
$text = '满' . $rules['full_num'] . ($rules['type'] == 'money' ? '元' : '件') . '即可包邮';
if (isset($rules['district_text']) && $rules['district_text']) {
$district = '';
if (isset($rules['district_text']['province']) && $rules['district_text']['province']) {
$district .= join('', $rules['district_text']['province']) . '';
}
if (isset($rules['district_text']['city']) && $rules['district_text']['city']) {
$district .= join('', $rules['district_text']['city']) . '';
}
if (isset($rules['district_text']['district']) && $rules['district_text']['district']) {
$district .= join('', $rules['district_text']['district']) . '';
}
if ($district) {
$text .= " (不支持包邮地区:" . rtrim($district, '') . ")";
}
}
$texts[] = $text;
return array_values(array_filter($texts));
}
public function getPromoInfo($promo, $data = [])
{
extract($this->promoGoodsData($promo));
$rules = $promo['rules'];
$userAddress = $data['userAddress'] ?? null;
// 是按金额,还是按件数比较
$compareif = $rules['type'] == 'num' ? 'promo_goods_num' : 'promo_goods_amount';
// 判断除外的地区
$district_except = isset($rules['district_except']) && $rules['district_except'] ? explode(',', $rules['district_except']) : [];
$city_except = isset($rules['city_except']) && $rules['city_except'] ? explode(',', $rules['city_except']) : [];
$province_except = isset($rules['province_except']) && $rules['province_except'] ? explode(',', $rules['province_except']) : [];
if ($userAddress) {
if (
in_array($userAddress['district_id'], $district_except)
|| in_array($userAddress['city_id'], $city_except)
|| in_array($userAddress['province_id'], $province_except)
) {
// 收货地址在非包邮地区,则继续循环下个活动
return null;
}
} else if ($district_except || $city_except || $province_except) {
// 没有选择收货地址,并且活动中包含地区限制,不计算活动
return null;
}
if (${$compareif} < $rules['full_num']) {
// 不满足条件,接着循环下个规则
return null;
}
// 记录活动信息
$promo_discount_info = [
'activity_id' => $promo['id'], // 活动id
'activity_title' => $promo['title'], // 活动标题
'activity_type' => $promo['type'], // 活动类型
'activity_type_text' => $promo['type_text'], // 活动类型中文
'promo_discount_money' => 0, // 这里无法知道真实运费减免,会在 orderCreate 后续计算完包邮优惠之后,改为真实减免的运费
'promo_goods_amount' => $promo_goods_amount, // 当前活动商品总金额
'rule_type' => $rules['type'], // 满多少元|还是满多少件
'discount_rule' => [
'full_num' => $rules['full_num']
], // 满足的那条规则
'goods_ids' => $goodsIds // 这个活动包含的这次购买的商品
];
return $promo_discount_info;
}
}

View File

@@ -0,0 +1,132 @@
<?php
namespace addons\shopro\library\activity\provider;
/**
* 满额折扣
*/
class FullDiscount extends Base
{
protected $rules = [
"type" => "require",
"discounts" => "require|array"
];
protected $message = [
"discounts.require" => '请填写优惠规则',
"discounts.array" => '请填写优惠规则',
];
protected $default = [
"type" => "money", // money=满足金额|num=满足件数
"discounts" => []
];
public function check($params, $activity_id = 0)
{
// 数据验证
$params = parent::check($params);
// 检测活动之间是否存在冲突
$this->checkActivityConflict($params, $params['goods_list'], $activity_id);
return $params;
}
public function formatTags($rules, $type)
{
$tags = [];
$discounts = $rules['discounts'] ?? [];
foreach ($discounts as $discount) {
$tags[] = $this->formatTag([
'type' => $rules['type'],
'full' => $discount['full'],
'discount' => $discount['discount']
]);
}
return array_values(array_filter($tags));
}
/**
* 格式化 discount 折扣为具体优惠标签
*
* @param string $type
* @param array $discountData
* @return string
*/
public function formatTag($discountData)
{
$tag = '满' . $discountData['full'] . ($discountData['type'] == 'money' ? '元' : '件');
$tag .= $discountData['discount'] . '折';
return $tag;
}
public function formatTexts($rules, $type)
{
$texts = [];
$discounts = $rules['discounts'] ?? [];
foreach ($discounts as $discount) {
$text = '满' . $discount['full'] . ($rules['type'] == 'money' ? '元' : '件');
$text .= ',商品总价打' . $discount['discount'] . '折';
$texts[] = $text;
}
return array_values(array_filter($texts));
}
public function getPromoInfo($promo, $data = [])
{
extract($this->promoGoodsData($promo));
$rules = $promo['rules'];
// 是按金额,还是按件数比较
$compareif = $rules['type'] == 'num' ? 'promo_goods_num' : 'promo_goods_amount';
// 将规则按照从大到校排列,优先比较是否满足最大规则
$rulesDiscounts = isset($rules['discounts']) && $rules['discounts'] ? array_reverse($rules['discounts']) : []; // 数组反转
// 满减, 满折多个规则从大到小匹配最优惠
foreach ($rulesDiscounts as $d) {
if (${$compareif} < $d['full']) {
// 不满足条件,接着循环下个规则
continue;
}
$dis = bcdiv($d['discount'], '10', 3); // 保留三位小数,转化折扣
$dis = $dis > 1 ? 1 : ($dis < 0 ? 0 : $dis); // 定义边界 0 - 1
$promo_dis = 1 - $dis;
$current_promo_discount_money = bcmul($promo_goods_amount, (string)$promo_dis, 3);
$current_promo_discount_money = number_format((float)$current_promo_discount_money, 2, '.', ''); // 计算折扣金额,四舍五入
// 记录该活动的一些统计信息
$promo_discount_info = [
'activity_id' => $promo['id'], // 活动id
'activity_title' => $promo['title'], // 活动标题
'activity_type' => $promo['type'], // 活动类型
'activity_type_text' => $promo['type_text'], // 活动类型中文
'promo_discount_money' => $current_promo_discount_money, // 优惠金额
'promo_goods_amount' => $promo_goods_amount, // 当前活动商品总金额
'rule_type' => $rules['type'], // 满多少元|还是满多少件
'discount_rule' => $d, // 满足的那条规则
'goods_ids' => $goodsIds // 这个活动包含的这次购买的商品
];
break;
}
return $promo_discount_info ?? null;
}
}

View File

@@ -0,0 +1,248 @@
<?php
namespace addons\shopro\library\activity\provider;
use addons\shopro\library\activity\traits\GiveGift;
use app\admin\model\shopro\Coupon;
use app\admin\model\shopro\order\Order;
/**
* 满额赠送
*/
class FullGift extends Base
{
use GiveGift;
protected $rules = [
"limit_num" => "number|egt:0",
"type" => "require",
"event" => "require",
"discounts" => "require|array"
];
protected $message = [
"discounts.require" => '请填写优惠规则',
"discounts.array" => '请填写优惠规则',
];
protected $default = [
"limit_num" => 0, // 每人可参与次数 0不限制
"type" => "money", // money=满足金额|num=满足件数
"event" => "confirm", // 赠送时机 paid=支付完成|confirm=确认收货(必须全部确认收货才可以)|finish=订单完成(评价完成)
"discounts" => [] //{"full":"100",
// "types":"coupon=优惠券|score=积分|money=余额|goods=商品",
// "coupon_ids":"赠优惠券时存在",
// "total":"赠送优惠券总金额",
// "score":"积分",
// "money":"余额",
// "goods_ids":"商品时存在",
// "gift_num":"礼品份数"}
];
protected $giftType = [
'money' => '余额',
'score' => '积分',
'coupon' => '优惠券',
'goods' => '商品',
];
public function check($params, $activity_id = 0)
{
// 数据验证
$params = parent::check($params);
// 验证赠送规则字段
$this->checkGiftDiscount($params['rules']['discounts']);
// 检测活动之间是否存在冲突
$this->checkActivityConflict($params, $params['goods_list'], $activity_id);
return $params;
}
/**
* 获取 赠送的优惠券列表
*
* @param string $type
* @param array $rules
* @return array
*/
public function rulesInfo($type, $rules)
{
$discounts = $rules['discounts'];
foreach ($discounts as &$discount) {
$discount['coupon_list'] = [];
if (in_array('coupon', $discount['types']) && isset($discount['coupon_ids']) && $discount['coupon_ids']) {
$discount['coupon_list'] = Coupon::statusHidden()->whereIn('id', $discount['coupon_ids'])->select();
}
}
$rules['discounts'] = $discounts;
return $rules;
}
public function formatTags($rules, $type)
{
$tags = [];
$discounts = $rules['discounts'] ?? [];
foreach ($discounts as $discount) {
$tags[] = $this->formatTag([
'type' => $rules['type'],
'simple' => $rules['simple'] ?? false, // 简单信息展示
'full' => $discount['full'],
'discount' => $discount
]);
}
return array_values(array_filter($tags));
}
/**
* 格式化 discount 折扣为具体优惠标签
*
* @param string $type
* @param array $discountData
* @return string
*/
public function formatTag($discountData)
{
$discount = $discountData['discount'];
$gift_type_text = '';
foreach ($discount['types'] as $type) {
$gift_type_text = $gift_type_text . (isset($this->giftType[$type]) ? ',' . $this->giftType[$type] : '');
}
$tag = '满' . $discountData['full'] . ($discountData['type'] == 'money' ? '元' : '件');
$tag .= '赠送' . ($discountData['simple'] ? '礼品' : trim($gift_type_text, ','));
return $tag;
}
public function formatTexts($rules, $type)
{
$texts = [];
$discounts = $rules['discounts'] ?? [];
foreach ($discounts as $discount) {
$text = '消费满' . $discount['full'] . ($rules['type'] == 'money' ? '元' : '件');
$text .= '';
foreach ($discount['types'] as $type) {
$text .= '';
if ($type == 'money') {
$text .= '赠送' . $discount['money'] . '元余额 ';
} elseif ($type == 'score') {
$text .= '赠送' . $discount['score'] . '积分 ';
} elseif ($type == 'coupon') {
$text .= '赠送价值' . $discount['total'] . '元优惠券 ';
}
}
$text .= ' (条件:活动礼品共 ' . $discount['gift_num'] . ' 份';
if ($rules['limit_num'] > 0) {
$text .= ',每人仅限参与 ' . $rules['limit_num'] . ' 次';
}
$text .= ')';
$texts[] = $text;
}
return array_values(array_filter($texts));
}
public function getPromoInfo($promo, $data = [])
{
extract($this->promoGoodsData($promo));
$rules = $promo['rules'];
// 是按金额,还是按件数比较
$compareif = $rules['type'] == 'num' ? 'promo_goods_num' : 'promo_goods_amount';
// 将规则按照从大到校排列,优先比较是否满足最大规则
$rulesDiscounts = isset($rules['discounts']) && $rules['discounts'] ? array_reverse($rules['discounts']) : []; // 数组反转
// 满减, 满折多个规则从大到小匹配最优惠
foreach ($rulesDiscounts as $d) {
unset($d['coupon_list']); // 移除规则里面的 coupon_list
if (${$compareif} < $d['full']) {
// 不满足条件,接着循环下个规则
continue;
}
// 记录该活动的一些统计信息
$promo_discount_info = [
'activity_id' => $promo['id'], // 活动id
'activity_title' => $promo['title'], // 活动标题
'activity_type' => $promo['type'], // 活动类型
'activity_type_text' => $promo['type_text'], // 活动类型中文
'promo_discount_money' => 0, // 优惠金额 (赠送,不优惠)
'promo_goods_amount' => $promo_goods_amount, // 当前活动商品总金额
'rule_type' => $rules['type'], // 满多少元|还是满多少件
'discount_rule' => $d, // 满足的那条规则
"limit_num" => $rules['limit_num'], // 每个人可参与次数
"event" => $rules['event'], // 赠送时机
'goods_ids' => $goodsIds // 这个活动包含的这次购买的商品
];
break;
}
return $promo_discount_info ?? null;
}
/**
* 支付成功(货到付款下单),添加礼品记录
*
* @param array|object $order
* @param array|object $user
* @return void
*/
public function buyOk($order, $user)
{
// 满赠送
$ext = $order->ext;
$promoInfos = $ext['promo_infos'];
foreach ($promoInfos as $info) {
if ($info['activity_type'] == 'full_gift') {
// 满赠,开始赠送
$this->addGiftsLog($order, $user, $info);
}
}
$event = $order->status == Order::STATUS_PENDING ? 'pending' : 'paid'; // 货到付款不是真的付款,不能发放礼品 event 改为 pending
// 检测并赠送礼品
$this->checkAndGift($order, $user, $promoInfos, $event);
}
/**
* 促销购买失败(退款)
*
* @param \think\Model $order
* @param string $type
* @return void
*/
public function buyFail($order, $type)
{
if ($type == 'refund') {
// 退款,将礼品标记为已退款,如果已经送出去的不扣除
$this->checkAndFailGift($order, '订单全额退款');
} else if ($type == 'invalid') {
if ($order->pay_mode == 'offline') {
// 只有线下付款取消时才需要标记礼品赠送失败
$this->checkAndFailGift($order, '货到付款订单取消');
}
}
}
}

View File

@@ -0,0 +1,128 @@
<?php
namespace addons\shopro\library\activity\provider;
/**
* 满额立减
*/
class FullReduce extends Base
{
protected $rules = [
"type" => "require",
"discounts" => "require|array"
];
protected $message = [
"discounts.require" => '请填写优惠规则',
"discounts.array" => '请填写优惠规则',
];
protected $default = [
"type" => "money", // money=满足金额|num=满足件数
"discounts" => []
];
public function check($params, $activity_id = 0)
{
// 数据验证
$params = parent::check($params);
// 检测活动之间是否存在冲突
$this->checkActivityConflict($params, $params['goods_list'], $activity_id);
return $params;
}
public function formatTags($rules, $type)
{
$tags = [];
$discounts = $rules['discounts'] ?? [];
foreach ($discounts as $discount) {
$tags[] = self::formatTag([
'type' => $rules['type'],
'full' => $discount['full'],
'discount' => $discount['discount']
]);
}
return array_values(array_filter($tags));
}
/**
* 格式化 discount 折扣为具体优惠标签
*
* @param string $type
* @param array $discountData
* @return string
*/
public function formatTag($discountData)
{
$tag = '满' . $discountData['full'] . ($discountData['type'] == 'money' ? '元' : '件');
$tag .= '减' . $discountData['discount'];
return $tag;
}
public function formatTexts($rules, $type)
{
$texts = [];
$discounts = $rules['discounts'] ?? [];
foreach ($discounts as $discount) {
$text = '满' . $discount['full'] . ($rules['type'] == 'money' ? '元' : '件');
$text .= '减' . $discount['discount'] . '元';
$texts[] = $text;
}
return array_values(array_filter($texts));
}
public function getPromoInfo($promo, $data = [])
{
extract($this->promoGoodsData($promo));
$rules = $promo['rules'];
// 是按金额,还是按件数比较
$compareif = $rules['type'] == 'num' ? 'promo_goods_num' : 'promo_goods_amount';
// 将规则按照从大到校排列,优先比较是否满足最大规则
$rulesDiscounts = isset($rules['discounts']) && $rules['discounts'] ? array_reverse($rules['discounts']) : []; // 数组反转
// 满减, 满折多个规则从大到小匹配最优惠
foreach ($rulesDiscounts as $d) {
if (${$compareif} < $d['full']) {
// 不满足条件,接着循环下个规则
continue;
}
// 满足优惠
$current_promo_discount_money = (isset($d['discount']) && $d['discount']) ? $d['discount'] : 0;
$current_promo_discount_money = number_format((float)$current_promo_discount_money, 2, '.', ''); // 格式化金额,四舍五入
// 记录该活动的一些统计信息
$promo_discount_info = [
'activity_id' => $promo['id'], // 活动id
'activity_title' => $promo['title'], // 活动标题
'activity_type' => $promo['type'], // 活动类型
'activity_type_text' => $promo['type_text'], // 活动类型中文
'promo_discount_money' => $current_promo_discount_money, // 优惠金额
'promo_goods_amount' => $promo_goods_amount, // 当前活动商品总金额
'rule_type' => $rules['type'], // 满多少元|还是满多少件
'discount_rule' => $d, // 满足的那条规则
'goods_ids' => $goodsIds // 这个活动包含的这次购买的商品
];
break;
}
return $promo_discount_info ?? null;
}
}

View File

@@ -0,0 +1,275 @@
<?php
namespace addons\shopro\library\activity\provider;
use addons\shopro\library\activity\traits\Groupon as GrouponTrait;
use addons\shopro\service\StockSale;
use addons\shopro\exception\ShoproException;
/**
* 普通拼团
*/
class Groupon extends Base
{
use GrouponTrait;
protected $rules = [
"is_commission" => "require|boolean",
"is_free_shipping" => "require|boolean",
"sales_show_type" => "require",
"team_num" => "require|number",
"is_alone" => "require|boolean",
"is_fictitious" => "require|boolean",
"fictitious_time" => "requireIf:is_fictitious,1|float|egt:0",
"is_team_card" => "require|boolean",
"is_leader_discount" => "require|boolean",
"valid_time" => "require|float|egt:0",
"limit_num" => "number|egt:0",
"refund_type" => "require", // 退款方式 back=原路退回|money=退回到余额
"order_auto_close" => "float|egt:0",
];
protected $message = [
'team_num.require' => '请填写成团人数',
];
protected $default = [
"is_commission" => 0, // 是否参与分销
"is_free_shipping" => 0, // 是否包邮
"sales_show_type" => "real", // real=真实活动销量|goods=商品总销量(包含虚拟销量)
"team_num" => 2, // 成团人数,最少两人
"is_alone" => 0, // 是否允许单独购买
"is_fictitious" => 0, // 是否允许虚拟成团
"fictitious_num" => 0, // 最多虚拟人数 0:不允许虚拟 '' 不限制
"fictitious_time" => 0, // 开团多长时间自动虚拟成团
"is_team_card" => 0, // 参团卡显示
"is_leader_discount" => 0, // 团长优惠
"valid_time" => 0, // 组团有效时间, 0一直有效
"limit_num" => 0, // 每人限购数量 0:不限购
"refund_type" => "back", // 退款方式 back=原路退回|money=退回到余额
"order_auto_close" => 0, // 订单自动关闭时间,如果为 0 将使用系统级订单自动关闭时间
];
public function check($params, $activity_id = 0)
{
// 数据验证
$params = parent::check($params);
// 验证添加的活动商品是否至少设置了一个活动规格
$this->checkActivitySkuPrice($params['goods_list']);
// 检测活动之间是否存在冲突
$this->checkActivityConflict($params, $params['goods_list'], $activity_id);
return $params;
}
public function save($activity, $params = [])
{
$goodsList = $params['goods_list'];
$this->saveSkuPrice($goodsList, $activity, function ($skuPrice) use ($activity) {
// 处理 团长优惠
$rules = $activity->rules;
$is_leader_discount = $rules['is_leader_discount'] ?? 0;
$leader_price = $skuPrice['price'];
if ($is_leader_discount && isset($skuPrice['leader_price'])) {
$leader_price = $skuPrice['leader_price'];
}
$ext = [
'is_leader_discount' => $is_leader_discount,
'leader_price' => number_format(floatval($leader_price), 2, '.', '')
];
unset($skuPrice['leader_price']);
$skuPrice['ext'] = $ext;
return $skuPrice;
});
}
public function showSkuPrice($skuPrice)
{
$ext = $skuPrice['ext'] ?? [];
$skuPrice['leader_price'] = $ext['leader_price'] ?? $skuPrice['price'];
return $skuPrice;
}
public function recoverSkuPrices($goods, $activity)
{
$activitySkuPrices = $activity['activity_sku_prices'];
$skuPrices = $goods->sku_prices;
foreach ($skuPrices as $key => &$skuPrice) {
$stock = $skuPrice->stock; // 下面要用
$skuPrice->stock = 0;
$skuPrice->sales = 0;
foreach ($activitySkuPrices as $activitySkuPrice) {
if ($skuPrice->id == $activitySkuPrice['goods_sku_price_id']) {
// 采用活动的 规格内容
$is_leader_discount = $activitySkuPrice['ext']['is_leader_discount'];
$leader_price = $activitySkuPrice['ext']['leader_price'];
$skuPrice->old_price = $skuPrice->price; // 保存原始普通商品规格的价格(计算活动的优惠)
$skuPrice->stock = ($activitySkuPrice['stock'] > $stock) ? $stock : $activitySkuPrice['stock']; // 活动库存不能超过商品库存
$skuPrice->sales = $activitySkuPrice['sales'];
$skuPrice->groupon_price = $activitySkuPrice['price']; // 不覆盖原来规格价格,用作单独购买,将活动的价格设置为新的拼团价格
$skuPrice->is_leader_discount = $is_leader_discount; // 是否团长优惠
$skuPrice->leader_price = $leader_price; // 团长优惠价格
$skuPrice->status = $activitySkuPrice['status']; // 采用活动的上下架
$skuPrice->ext = $activitySkuPrice['ext']; // 活动规格 ext, order_item 保存备用
$skuPrice->min_price = $activitySkuPrice['price']; // 当前活动规格最小价格,这里是拼团价
$skuPrice->max_price = $activitySkuPrice['price']; // 用作计算活动中最大价格
// 记录相关活动类型
$skuPrice->activity_type = $activity['type'];
$skuPrice->activity_id = $activity['id'];
// 下单的时候需要存活动 的 sku_price_id
$skuPrice->item_goods_sku_price = $activitySkuPrice;
break;
}
}
}
return $skuPrices;
}
/**
* 这里要使用 shoproException 抛出异常
*
* @param array $buyInfo
* @param array $activity
* @return array
*/
public function buyCheck($buyInfo, $activity)
{
$buy_type = request()->param('buy_type', 'groupon');
$groupon_id = request()->param('groupon_id', 0);
// 拼团
$rules = $activity['rules'];
$is_alone = $rules['is_alone'] ?? 1;
$currentSkuPrice = $buyInfo['current_sku_price'];
$is_leader_discount = $currentSkuPrice['is_leader_discount'];
// 成团人数
$num = $rules['team_num'] ?? 1;
// 额外需要的库存
$need_add_num = 0;
// 要单独购买
if ($buy_type == 'alone') {
// 不允许单独购买
if (!$is_alone) {
throw new ShoproException('该商品不允许单独购买');
}
} else {
// 拼团,临时将拼团价设置为商品价格
if (!$groupon_id && $is_leader_discount) {
// 开新团,并且有团长优惠,使用优惠价格
$buyInfo['current_sku_price']['price'] = $currentSkuPrice['leader_price'];
} else {
// 参与团,或者没有团长优惠
$buyInfo['current_sku_price']['price'] = $currentSkuPrice['groupon_price'];
}
}
// 如果是开新团
if (!$groupon_id && $buy_type == 'groupon') {
// 开团需要的最小库存
$need_add_num = ($num - 1);
}
// 当前库存,小于要购买的数量
$need_num = $buyInfo['goods_num'] + ($need_add_num ?? 0);
if ($currentSkuPrice['stock'] < $need_num) {
if ($need_add_num && $is_alone && !$groupon_id && $buy_type == 'groupon') {
throw new ShoproException('商品库存不足以开团,请选择单独购买');
} else if ($buy_type == 'alone') {
throw new ShoproException('商品库存不足');
} else {
throw new ShoproException('该商品不允商品库存不足以开团许单独购买');
}
}
$buyInfo['is_commission'] = $rules['is_commission'] ?? 0; // 是否参与分销
return $buyInfo;
}
public function buy($buyInfo, $activity)
{
$user = auth_user();
$buy_type = request()->param('buy_type', 'groupon');
$groupon_id = request()->param('groupon_id', 0);
// 参与现有团
if ($buy_type != 'alone' && $groupon_id) {
// 检测并获取要参与的团
$activityGroupon = $this->checkAndGetJoinGroupon($buyInfo, $user, $groupon_id);
}
// 判断 并 增加 redis 销量
$stockSale = new StockSale();
$stockSale->cacheForwardSale($buyInfo);
// (开新团不判断)参与旧团 增加预拼团人数,上面加入团的时候已经判断过一次了,所以这里 99.99% 会加入成功的
if (isset($activityGroupon) && $activityGroupon) {
// 增加拼团预成员人数
$goods = $buyInfo['goods'];
$activity = $goods['activity'];
$this->grouponCacheForwardNum($activityGroupon, $activity, $user);
}
return $buyInfo;
}
public function buyOk($order, $user)
{
$this->joinGroupon($order, $user, function ($activityRules, $itemExt) {
$team_num = $activityRules['team_num'] ?? 1;
return compact('team_num');
});
}
/**
* 拼团购买失败
*
* @param \think\Model $order
* @param string $type
* @return void
*/
public function buyFail($order, $type)
{
if ($type == 'invalid') {
if ($order->pay_mode == 'offline') {
// 肯定是已经货到付款的订单取消订单,这时候已经添加了参团记录
$this->refundGrouponLog($order);
} else {
// 订单失效,扣除预拼团人数(只处理正在进行中的团)
$this->grouponCacheBackNum($order, $type);
}
} else {
// type = refund 退款订单将参团标记为已退款
$this->refundGrouponLog($order);
}
// 判断扣除预销量 (活动信息还在 redis)
$stockSale = new StockSale();
$stockSale->cacheBackSale($order);
}
}

View File

@@ -0,0 +1,320 @@
<?php
namespace addons\shopro\library\activity\provider;
use addons\shopro\library\activity\traits\Groupon as GrouponTrait;
use addons\shopro\service\StockSale;
use addons\shopro\exception\ShoproException;
/**
* 阶梯拼团
*/
class GrouponLadder extends Base
{
use GrouponTrait;
protected $rules = [
"is_commission" => "require|boolean",
"is_free_shipping" => "require|boolean",
"sales_show_type" => "require",
"ladders" => "require|array",
"is_alone" => "require|boolean",
"is_fictitious" => "require|boolean",
"fictitious_time" => "requireIf:is_fictitious,1|float|egt:0",
"is_team_card" => "require|boolean",
"is_leader_discount" => "require|boolean",
"valid_time" => "require|float|egt:0",
"limit_num" => "number|egt:0",
"refund_type" => "require", // 退款方式 back=原路退回|money=退回到余额
"order_auto_close" => "float|egt:0",
];
protected $message = [
'ladders.require' => '请填写拼团阶梯',
'ladders.array' => '请填写拼团阶梯',
];
protected $default = [
"is_commission" => 0, // 是否参与分销
"is_free_shipping" => 0, // 是否包邮
"sales_show_type" => "real", // real=真实活动销量|goods=商品总销量(包含虚拟销量)
"ladders" => [], // {ladder_one:2,ladder_two:2,ladder_three:3}
"is_alone" => 0, // 是否允许单独购买
"is_fictitious" => 0, // 是否允许虚拟成团
"fictitious_num" => 0, // 最多虚拟人数 0:不允许虚拟 '' 不限制
"fictitious_time" => 0, // 开团多长时间自动虚拟成团
"is_team_card" => 0, // 参团卡显示
"is_leader_discount" => 0, // 团长优惠
"valid_time" => 0, // 组团有效时间, 0一直有效
"limit_num" => 0, // 每人限购数量 0:不限购
"refund_type" => "back", // 退款方式 back=原路退回|money=退回到余额
"order_auto_close" => 0, // 订单自动关闭时间,如果为 0 将使用系统级订单自动关闭时间
];
public function check($params, $activity_id = 0)
{
// 数据验证
$params = parent::check($params);
// 验证添加的活动商品是否至少设置了一个活动规格
$this->checkActivitySkuPrice($params['goods_list']);
// 检测活动之间是否存在冲突
$this->checkActivityConflict($params, $params['goods_list'], $activity_id);
return $params;
}
public function save($activity, $params = [])
{
$goodsList = $params['goods_list'];
$this->saveSkuPrice($goodsList, $activity, function ($skuPrice) use ($activity) {
// 处理 阶梯价格,团长优惠
$rules = $activity->rules;
$is_leader_discount = $rules['is_leader_discount'] ?? 0;
$ladders = $rules['ladders'] ?? 0;
$ext = [
'is_leader_discount' => $is_leader_discount,
'ladders' => []
];
foreach ($ladders as $ladder_level => $ladder) {
$ladder_price = isset($skuPrice[$ladder_level]) ? number_format(floatval($skuPrice[$ladder_level]), 2, '.', '') : 0;
$leader_ladder_price = (isset($skuPrice[$ladder_level . '_leader']) && $skuPrice[$ladder_level . '_leader'] > 0) ? number_format(floatval($skuPrice[$ladder_level . '_leader']), 2, '.', '') : $ladder_price; // 默认当前阶梯参团价
$current = [
'ladder_level' => $ladder_level,
'ladder' => $ladder,
'ladder_price' => $ladder_price,
'leader_ladder_price' => $leader_ladder_price
];
unset($skuPrice[$ladder_level], $skuPrice[$ladder_level . '_leader']);
$ext['ladders'][] = $current;
}
$skuPrice['ext'] = $ext;
return $skuPrice;
});
}
public function showSkuPrice($skuPrice)
{
$ext = $skuPrice['ext'] ?? [];
$ladders = $ext['ladders'] ?? [];
if ($ladders) {
foreach ($ladders as $ladder) {
$ladder_level = $ladder['ladder_level'];
$skuPrice[$ladder_level] = $ladder['ladder_price'];
$skuPrice[$ladder_level . '_leader'] = $ladder['leader_ladder_price'];
}
} else {
// 全部初始化为 0
$skuPrice['ladder_one'] = 0;
$skuPrice['ladder_two'] = 0;
$skuPrice['ladder_three'] = 0;
$skuPrice['ladder_one_leader'] = 0;
$skuPrice['ladder_two_leader'] = 0;
$skuPrice['ladder_three_leader'] = 0;
}
return $skuPrice;
}
public function recoverSkuPrices($goods, $activity)
{
$groupon_num = request()->param('groupon_num', 0); // 是否传了开团人数(这里不再使用阶梯,前端没反)
$activitySkuPrices = $activity['activity_sku_prices'];
$skuPrices = $goods->sku_prices;
foreach ($skuPrices as $key => &$skuPrice) {
$stock = $skuPrice->stock; // 下面要用
$skuPrice->stock = 0;
$skuPrice->sales = 0;
foreach ($activitySkuPrices as $activitySkuPrice) {
if ($skuPrice['id'] == $activitySkuPrice['goods_sku_price_id']) {
// 采用活动的 规格内容
$is_leader_discount = $activitySkuPrice['ext']['is_leader_discount'];
$ladders = $activitySkuPrice['ext']['ladders'];
$skuPrice->old_price = $skuPrice->price; // 保存原始普通商品规格的价格(计算活动的优惠)
$skuPrice->stock = ($activitySkuPrice['stock'] > $stock) ? $stock : $activitySkuPrice['stock']; // 活动库存不能超过商品库存
$skuPrice->sales = $activitySkuPrice['sales'];
$skuPrice->is_leader_discount = $is_leader_discount; // 是否团长优惠
$skuPrice->ladders = $ladders; // 阶梯价格,包含团长优惠
$skuPrice->status = $activitySkuPrice['status']; // 采用活动的上下架
$skuPrice->ext = $activitySkuPrice['ext']; // 活动规格 ext, order_item 保存备用
$skuPrice->min_price = min(array_column($ladders, 'ladder_price')); // 当前活动规格最小价格,这里是阶梯最低拼团价(不要团长价)
$skuPrice->max_price = max(array_column($ladders, 'ladder_price')); // 当前活动规格最大价格,这里是阶梯最低拼团价(不要团长价)
$ladders = array_column($ladders, null, 'ladder');
$currentLadder = $ladders[$groupon_num] ?? current($ladders);
$skuPrice->ladder_price = $currentLadder['ladder_price']; // 当前阶梯价格(默认是 ladder_one
$skuPrice->leader_ladder_price = $currentLadder['leader_ladder_price']; // 当前阶梯团长价(默认是 ladder_one
$skuPrice->price = $is_leader_discount ? $skuPrice->leader_ladder_price : $skuPrice->ladder_price; // 默认是计算好的价格,团长价或者普通价
// 记录相关活动类型
$skuPrice->activity_type = $activity['type'];
$skuPrice->activity_id = $activity['id'];
// 下单的时候需要存活动 的 sku_price_id
$skuPrice->item_goods_sku_price = $activitySkuPrice;
break;
}
}
}
return $skuPrices;
}
/**
* 这里要使用 shoproException 抛出异常
*
* @param array $buyInfo
* @param array $activity
* @return array
*/
public function buyCheck($buyInfo, $activity)
{
$buy_type = request()->param('buy_type', 'groupon');
$groupon_id = request()->param('groupon_id', 0);
$groupon_num = request()->param('groupon_num', 0);
// 拼团
$rules = $activity['rules'];
$is_alone = $rules['is_alone'] ?? 1;
$currentSkuPrice = $buyInfo['current_sku_price'];
$is_leader_discount = $currentSkuPrice['is_leader_discount']; // 是否团长优惠
$ladders = $currentSkuPrice['ladders']; // 阶梯数据
$ladders = array_column($ladders, null, 'ladder');
$currentLadder = $ladders[$groupon_num] ?? current($ladders); // 当前阶梯的 价格数据
// 开新团,并且没有找到要参与的阶梯数据
if (!$groupon_id && (!$currentLadder || $currentLadder['ladder'] <= 1)) {
throw new ShoproException('请选择正确的开团阶梯');
}
$buyInfo['ladder'] = $currentLadder; // 存储当前购买的拼团阶梯 ladder
// 额外需要的库存
$need_add_num = 0;
// 要单独购买
if ($buy_type == 'alone') {
// 不允许单独购买
if (!$is_alone) {
throw new ShoproException('该商品不允许单独购买');
}
} else {
// 拼团,临时将拼团价设置为商品价格
if (!$groupon_id && $is_leader_discount) {
// 开新团,并且有团长优惠,使用优惠价格
$buyInfo['current_sku_price']['price'] = $currentLadder['leader_ladder_price'];
} else {
// 参与团,或者没有团长优惠
$buyInfo['current_sku_price']['price'] = $currentSkuPrice['ladder_price'];
}
}
// 如果是开新团
if (!$groupon_id && $buy_type == 'groupon') {
// 成团人数
$num = $currentLadder['ladder'] ?? 1;
// 开团需要的最小库存
$need_add_num = ($num - 1);
}
// 当前库存,小于要购买的数量
$need_num = $buyInfo['goods_num'] + ($need_add_num ?? 0);
if ($currentSkuPrice['stock'] < $need_num) {
if ($need_add_num && $is_alone && !$groupon_id && $buy_type == 'groupon') {
throw new ShoproException('商品库存不足以开团,请选择单独购买');
} else if ($buy_type == 'alone') {
throw new ShoproException('商品库存不足');
} else {
throw new ShoproException('商品库存不足以开团');
}
}
$buyInfo['is_commission'] = $rules['is_commission'] ?? 0; // 是否参与分销
return $buyInfo;
}
public function buy($buyInfo, $activity)
{
$user = auth_user();
$buy_type = request()->param('buy_type', 'groupon');
$groupon_id = request()->param('groupon_id', 0);
// 参与现有团
if ($buy_type != 'alone' && $groupon_id) {
// 检测并获取要参与的团
$activityGroupon = $this->checkAndGetJoinGroupon($buyInfo, $user, $groupon_id);
}
// 判断 并 增加 redis 销量
$stockSale = new StockSale();
$stockSale->cacheForwardSale($buyInfo);
// (开新团不判断)参与旧团 增加预拼团人数,上面加入团的时候已经判断过一次了,所以这里 99.99% 会加入成功的
if (isset($activityGroupon) && $activityGroupon) {
// 增加拼团预成员人数
$goods = $buyInfo['goods'];
$activity = $goods['activity'];
$this->grouponCacheForwardNum($activityGroupon, $activity, $user);
}
return $buyInfo;
}
public function buyOk($order, $user)
{
$this->joinGroupon($order, $user, function ($activityRules, $itemExt) {
// 处理拼团特殊的数据
$ladder = $itemExt['ladder'];
$team_num = $ladder['ladder'];
return compact('team_num');
});
}
/**
* 拼团购买失败
*
* @param \think\Model $order
* @param string $type
* @return void
*/
public function buyFail($order, $type)
{
if ($type == 'invalid') {
if ($order->pay_mode == 'offline') {
// 肯定是已经货到付款的订单取消订单,这时候已经添加了参团记录
$this->refundGrouponLog($order);
} else {
// 订单失效,扣除预拼团人数(只处理正在进行中的团)
$this->grouponCacheBackNum($order, $type);
}
} else {
// type = refund 退款订单将参团标记为已退款
$this->refundGrouponLog($order);
}
// 判断扣除预销量 (活动信息还在 redis)
$stockSale = new StockSale();
$stockSale->cacheBackSale($order);
}
}

View File

@@ -0,0 +1,113 @@
<?php
namespace addons\shopro\library\activity\provider;
/**
* 幸运拼团
*/
class GrouponLucky extends Base
{
protected $rules = [
// "is_commission" => "require|bool",
// "is_free_shipping" => "require|bool",
// "sales_show_type" => "require",
// "team_num" => "require|number",
// "lucky_num" => "require|number",
// "is_fictitious" => "require|bool",
// "fictitious_num" => "number|gt:0",
// "fictitious_time" => "require|float|egt:0",
// "part_gift" => "require|array",
// "is_team_card" => "require|bool",
// "is_leader_discount" => "require|bool",
// "valid_time" => "require|float|gt:0",
// "limit_num" => "number|gt:0",
// "limit_team_buy" => "number|gt:0",
// "refund_type" => "back", // 退款方式 back=原路退回|money=退回到余额
// "order_auto_close" => "float|gt:0",
];
protected $message = [
// 'team_num.require' => '请填写成团人数',
// 'is_alone.require' => '请选择单独购买',
// 'stock.gt' => '请填写补货数量'
];
protected $default = [
"is_commission" => 0, // 是否参与分销
"is_free_shipping" => 0, // 是否包邮
"sales_show_type" => "real", // real=真实活动销量|goods=商品总销量(包含虚拟销量)
"team_num" => 2, // 成团人数,最少两人
"lucky_num" => 1, // 拼中人数,最少一人
"is_fictitious" => 0, // 是否允许虚拟成团
"fictitious_num" => 0, // 最多虚拟人数
"fictitious_time" => 0, // 开团多长时间自动虚拟成团
"part_gift" => [], // {"types": "coupon=优惠券|score=积分|money=余额","coupon_ids":"赠优惠券时存在","total":"赠送优惠券总金额","score":"积分","money":"余额"}
"is_team_card" => 0, // 参团卡显示
"is_leader_discount" => 0, // 团长优惠
"valid_time" => 0, // 组团有效时间
"limit_num" => 0, // 每人限购数量
"limit_team_buy" => 0, // 每人每团可参与次数
"refund_type" => "back", // 退款方式 back=原路退回|money=退回到余额
"order_auto_close" => 0, // 订单自动关闭时间
];
public function check($params, $activity_id = 0)
{
// 数据验证
$params = parent::check($params);
// 验证添加的活动商品是否至少设置了一个活动规格
$this->checkActivitySkuPrice($params['goods_list']);
// 验证赠送规则字段
$this->checkLuckyPartGift($params['rules']['part_gift']);
// 检测活动之间是否存在冲突
$this->checkActivityConflict($params, $params['goods_list'], $activity_id);
return $params;
}
public function save($activity, $params = [])
{
$goodsList = $params['goods_list'];
$this->saveSkuPrice($goodsList, $activity->id);
}
public function recoverSkuPrices($goods, $activity)
{
$activitySkuPrices = $activity['activity_sku_prices'];
$skuPrices = $goods->sku_prices;
foreach ($skuPrices as $key => &$skuPrice) {
$stock = $skuPrice->stock; // 下面要用
$skuPrice->stock = 0;
$skuPrice->sales = 0;
foreach ($activitySkuPrices as $activitySkuPrice) {
if ($skuPrice['id'] == $activitySkuPrice['goods_sku_price_id']) {
// 采用活动的 规格内容
$skuPrice->stock = ($activitySkuPrice['stock'] > $stock) ? $stock : $activitySkuPrice['stock']; // 活动库存不能超过商品库存
$skuPrice->sales = $activitySkuPrice['sales'];
$skuPrice->groupon_price = $activitySkuPrice['price']; // 不覆盖原来规格价格,用作单独购买,将活动的价格设置为新的拼团价格
$skuPrice->status = $activitySkuPrice['status']; // 采用活动的上下架
// 记录相关活动类型
$skuPrice->activity_type = $activity['type'];
$skuPrice->activity_id = $activity['id'];
// 下单的时候需要存活动 的 sku_price_id
$skuPrice->item_goods_sku_price = $activitySkuPrice;
break;
}
}
}
return $skuPrice;
}
}

View File

@@ -0,0 +1,151 @@
<?php
namespace addons\shopro\library\activity\provider;
use addons\shopro\service\StockSale;
use addons\shopro\exception\ShoproException;
/**
* 秒杀
*/
class Seckill extends Base
{
protected $rules = [
"is_commission" => "require|boolean",
"is_free_shipping" => "require|boolean",
"sales_show_type" => "require",
"limit_num" => "number|egt:0",
"order_auto_close" => "float|egt:0",
];
protected $message = [
];
protected $default = [
"is_commission" => 0, // 是否参与分销
"is_free_shipping" => 0, // 是否包邮
"sales_show_type" => "real", // real=真实活动销量|goods=商品总销量(包含虚拟销量)
"limit_num" => 0, // 每人限购数量 0:不限购
"order_auto_close" => 0, // 订单自动关闭时间,如果为 0 将使用系统级订单自动关闭时间
];
public function check($params, $activity_id = 0)
{
// 数据验证
$params = parent::check($params);
// 验证添加的活动商品是否至少设置了一个活动规格
$this->checkActivitySkuPrice($params['goods_list']);
// 检测活动之间是否存在冲突
$this->checkActivityConflict($params, $params['goods_list'], $activity_id);
return $params;
}
public function save($activity, $params = [])
{
$goodsList = $params['goods_list'];
$this->saveSkuPrice($goodsList, $activity);
}
public function recoverSkuPrices($goods, $activity)
{
$activitySkuPrices = $activity['activity_sku_prices'];
$skuPrices = $goods->sku_prices;
foreach ($skuPrices as $key => &$skuPrice) {
$stock = $skuPrice->stock; // 下面要用
$skuPrice->stock = 0;
$skuPrice->sales = 0;
foreach ($activitySkuPrices as $activitySkuPrice) {
if ($skuPrice['id'] == $activitySkuPrice['goods_sku_price_id']) {
// 采用活动的 规格内容
$skuPrice->old_price = $skuPrice->price; // 保存原始普通商品规格的价格(计算活动的优惠)
$skuPrice->stock = ($activitySkuPrice['stock'] > $stock) ? $stock : $activitySkuPrice['stock']; // 活动库存不能超过商品库存
$skuPrice->sales = $activitySkuPrice['sales'];
$skuPrice->price = $activitySkuPrice['price'];
$skuPrice->status = $activitySkuPrice['status']; // 采用活动的上下架
$skuPrice->min_price = $activitySkuPrice['price']; // 当前活动规格最小价格,这里是秒杀价
$skuPrice->max_price = $activitySkuPrice['price']; // 用作计算活动中最大价格
// 记录相关活动类型
$skuPrice->activity_type = $activity['type'];
$skuPrice->activity_id = $activity['id'];
// 下单的时候需要存活动 的 sku_price_id
$skuPrice->item_goods_sku_price = $activitySkuPrice;
break;
}
}
}
return $skuPrices;
}
/**
* 这里要使用 shoproException 抛出异常
*
* @param array $buyInfo
* @param array $activity
* @return array
*/
public function buyCheck($buyInfo, $activity)
{
// 秒杀
$rules = $activity['rules'];
$currentSkuPrice = $buyInfo['current_sku_price'];
// 当前库存,小于要购买的数量
$need_num = $buyInfo['goods_num'] + ($need_add_num ?? 0);
if ($currentSkuPrice['stock'] < $need_num) {
throw new ShoproException('商品库存不足');
}
$buyInfo['is_commission'] = $rules['is_commission'] ?? 0; // 是否参与分销
return $buyInfo;
}
public function buy($buyInfo, $activity)
{
$user = auth_user();
// 判断 并 增加 redis 销量
$stockSale = new StockSale();
$stockSale->cacheForwardSale($buyInfo);
return $buyInfo;
}
public function buyOk($order, $user)
{
// 不需要处理
}
/**
* 购买失败
*
* @param array $order
* @return void
*/
public function buyFail($order, $type)
{
// 判断扣除预销量 (活动信息还在 redis)
$stockSale = new StockSale();
$stockSale->cacheBackSale($order);
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace addons\shopro\library\activity\provider;
/**
* 签到
*/
class Signin extends Base
{
protected $rules = [
"everyday" => "require",
"is_inc" => "require|boolean",
"inc_num" => "require",
"until_day" => "require|egt:0",
"discounts" => "array",
"is_replenish" => "require|boolean",
"replenish_days" => "require|gt:0",
"replenish_limit" => "require|egt:0",
"replenish_num" => "require|gt:0"
];
protected $message = [
];
protected $default = [
"everyday" => 0, // 每日签到固定积分
"is_inc" => 0, // 是否递增签到
"inc_num" => 0, // 递增奖励
"until_day" => 0, // 递增持续天数
"discounts" => [], // 连续签到奖励 {full:5, value:10} // 可以为空
"is_replenish" => 0, // 是否开启补签
"replenish_days" => 1, // 可补签天数,最小 1
"replenish_limit" => 0, // 补签时间限制0 不限制
"replenish_num" => 1, // 补签所消耗积分
];
public function check($params, $activity_id = 0)
{
// 数据验证
$params = parent::check($params);
// 检测活动之间是否存在冲突
$this->checkActivityConflict($params, [], $activity_id);
return $params;
}
}