admin/plugins/red-packet/src/services/TimedTaskServices.php

624 lines
24 KiB
PHP

<?php
/**
* Created by PhpStorm.
* User: yunzhong
* Date: 2019/11/4
* Time: 11:36
*/
namespace Yunshop\RedPacket\services;
use app\common\models\Goods;
use app\common\models\Order;
use app\common\models\OrderGoods;
use app\common\models\UniAccount;
use Illuminate\Support\Facades\Redis;
use Yunshop\RedPacket\models\BonusCreateLogsModel;
use Yunshop\RedPacket\models\RedPacketGoodsModel;
use Yunshop\RedPacket\models\RedPacketLogsModel;
use app\common\facades\Setting;
use Illuminate\Filesystem\Filesystem;
use Yunshop\StoreCashier\common\models\balance\PluginBalanceOrder;
use Yunshop\StoreCashier\common\models\StoreSetting;
class TimedTaskServices
{
const SET_LOG_NAME = 'plugin.red-packet-create_log';
protected $setLog;
//获取前一天的订单额度
public function handle()
{
\Log::debug('---每日红包定时任务---');
date_default_timezone_set('PRC');
$uniAccount = UniAccount::getEnable();
foreach ($uniAccount as $u) {
\YunShop::app()->uniacid = $u->uniacid;
Setting::$uniqueAccountId = $u->uniacid;
$this->setLog = Setting::get(self::SET_LOG_NAME);//生成红包记录时间
$set = Setting::get('plugin.red-packet');
$red_packet = $this->getOrder($set);
$isCreate = $this->validateCreateRedPacketConditions($set);//验证创建红包条件
if (!$isCreate) {
continue;
}
\Log::info("--每日红包基数--");
$this->getData($red_packet, $set);
}
}
/**
* 获取前一天订单(平台,门店,供应商,酒店)
*/
public function getOrder($set)
{
$range = [strtotime(date("Y-m-d 00:00:00", strtotime("-1 day"))), strtotime(date("Y-m-d 00:00:00", time()))];
if ($set['is_open_red_packet'] == 1) {
if ($set['self_order_after'] == 3) {
$price = \Yunshop\RedPacket\common\models\Order::uniacid()->select('price')
->pluginId()
->where('status', $set['self_order_after'])
->whereBetween('finish_time', $range)
->sum('price');
return $price;
} else {
$price = \Yunshop\RedPacket\common\models\Order::uniacid()->select('price')
->pluginId()
->where('status', '>=', $set['self_order_after'])
->whereBetween('pay_time', $range)
->sum('price');
return $price;
}
}
return '';
}
/**
* 每日红包参数
*/
public function getData($red_packet, $set)
{
$uniacid = \YunShop::app()->uniacid;
if (!$uniacid) {
\Log::info('未接入公众号');
return;
}
if (!empty($red_packet)) {
$amount = self::settlement($set);
$min_money = 0.01;
$mim_amount = $set['number_sum'] * $min_money;
if ($amount < $mim_amount) {
\Log::info('每日产生红包低于平均人数红包值---', $mim_amount);
return;
}
$data = array(
'uniacid' => \YunShop::app()->uniacid,
'results_amount' => $red_packet,
'amount' => $amount,
'percentage' => $set['proportion'],
'number_sum' => $set['number_sum'],
'receive_amount' => 0,
'receive' => 0,
);
$redPacketLogsId = RedPacketLogsModel::create($data)->id;
}
if (!empty($redPacketLogsId)) {
\Log::info('生成随机红包数量',$set);
//开启了红包梯度 独一套生成红包方式,领取红包也是独一套领取
if ($set['is_grads']) {
$this->redPacketSum($amount, $set['number_sum']);//同时生成原红包,用作领取中途关闭功能,不带红包最高限制
$this->gradsCreateRedPacket($amount, $set['number_sum'],$set['grads']);
} else {
//开启了红包最高限制
if ($set['is_red_packet_max'] == 1) {
$this->redPacketSum2($set['red_packet_max'], $amount, $set['number_sum']);
} else {
$this->redPacketSum($amount, $set['number_sum']);
}
}
$this->getBonus($amount, $set['number_sum'], $set, $redPacketLogsId);//额外奖励
}
}
/**
* 每日红包结算
* @param $set
*/
public function settlement($set)
{
\Log::debug('每日红包统计');
\Log::info('分部结算前一天订单红包池');
$range = [strtotime(date("Y-m-d 00:00:00", strtotime("-1 day"))), strtotime(date("Y-m-d 00:00:00", time()))];
\Log::debug('时间区间:' . json_encode($range));
\Log::debug('结算订单模式:' . $set['self_order_after']);
\Log::info('结算订单模式', $set['self_order_after']);
//门店订单
$storeOrder = $this->getStoreOrder($set, $range);
//收银台订单
$cashierOrder = $this->getCashierOrder($set, $range);
//cps订单
$cpsOrder = $this->getCpsOrder($set, $range);
//平台,供应商,酒店,拼团订单
if (!$storeOrder->isEmpty()) {
$store = $storeOrder->toArray();
\Log::info('今日门店订单', $store);
$store_price = '';
foreach ($store as $key => $value) {
$setting = StoreSetting::select('value')
->where('store_id', $value['has_one_store_order']['store_id'])
->where('key', 'red_packet')
->first();
$casts = $setting['value'];
\Log::info('门店比例', $casts['store_proportion']);
\Log::info('门店订单金额', $value['price']);
if ($setting) {
if ($casts['is_store'] == 1 && $casts['store_proportion'] != 0) {
$store_price += $value['price'] * $casts['store_proportion'];
} else {
$store_price += $value['price'] * $set['proportion'];
}
}
}
\Log::debug('门店订单金额:' . $store_price);
}
if (!$cashierOrder->isEmpty()) {
$cashier_price = '';
$cashier = $cashierOrder->toArray();
\Log::info('今日收银台订单', $cashier);
foreach ($cashier as $key => $value) {
$setting = RedPacketGoodsModel::where('goods_id', $value['has_one_cashier_order']['cashier_id'])
->where('is_cashier', 1)
->first();
\Log::info('收银台比例', $setting['cashier_proportion']);
\Log::info('收银台订单金额', $value['price']);
if ($setting['is_cashier'] == 1 && $setting['cashier_proportion'] != 0) {
$cashier_price += $value['price'] * $setting['cashier_proportion'];
} else if ($setting['is_cashier'] == 1 && $setting['cashier_proportion'] == 0) {
$cashier_price += $value['price'] * $set['proportion'];
}
}
\Log::debug('收银台订单金额:' . $cashier_price);
}
if (!$cpsOrder->isEmpty()) {
$cps_price = '';
$cps = $cpsOrder->toArray();
foreach ($cps as $key => $value) {
$goods = Goods::uniacid()->where('plugin_id', 70)->first();
$setting = RedPacketGoodsModel::where('goods_id', $goods->id)
->where('is_cps', 1)
->first();
if ($setting['is_cps'] == 1 && $setting['cps_proportion'] != 0) {
$cps_price += round(($value['price'] * $setting['cps_proportion']) / 100, 2);
} else if ($setting['is_cps'] == 1 && $setting['cps_proportion'] == 0) {
$cps_price += round(($value['price'] * $set['proportion']) / 100, 2);
}
}
\Log::debug('cps订单金额:' . $cps_price);
}
// 商城结算金额计算
\Log::debug('红包比例:' . $set['proportion']);
// $order = $this->getOrders($set, $range);
// $amount = $order * $set['proportion'];
$amount = $this->getOrdersV2($set, $range);// 2023-9-13 修改 兼容商品独立计算规则
\Log::debug('(平台,供应商,酒店,拼团)订单统计结果:' . $amount);
// 门店余额充值订单
$balancePriceTotal = $this->getBalanceOrderPriceTotal($set, $range);
\Log::debug('门店余额充值订单结算金额/100', $balancePriceTotal);
//保留两位小数并且不四舍五入
$data = sprintf("%.2f", substr(sprintf("%.3f", (($store_price + $cashier_price + $amount + $cps_price) / 100)), 0, -1));
$data += $balancePriceTotal;
\Log::debug('商城结算金额', $amount);
\Log::debug('门店结算金额', $store_price);
\Log::debug('收银台结算金额', $cashier_price);
\Log::debug('cps结算金额', $cps_price);
\Log::debug('结算总金额/100', $data);
return $data;
}
/**
* @param string $amount
* @param string $number_sum
* 红包
*/
private function redPacketSum($amount = '', $number_sum = '')
{
$min_money = 0.01;
$mim_amount = $number_sum * $min_money;
$uniacid = \YunShop::app()->uniacid;
if ($amount < $mim_amount) {
\Log::debug('每日产生红包低于平均人数红包值');
$red_Packet = storage_path('app/redPacket' . '/' . $uniacid);
app(Filesystem::class)->makeDirectory($red_Packet, 0777, 1);
$path = $red_Packet . '/redPacket.txt';
$arr_redPacket = '';
file_put_contents($path, $arr_redPacket);
} else {
if ($amount == '') {
$arr_redPacket = '';
} else {
for ($i = 1; $i < $number_sum; $i++) {
$safe_total = ($amount - ($number_sum - $i) * $min_money) / ($number_sum - $i);//随机安全上限
$money = round((mt_rand($min_money * 100, $safe_total * 100) / 100), 2);
$amount = $amount - $money;
//红包数据
$readPack[] = [
'money' => round($money, 2),
];
}
//最后一个红包,不用随机
$readPack[] = [
'money' => round($amount, 2)
];
shuffle($readPack);//乱序
\Log::debug('删除每日红包1');
Redis::del('today_redpacket' . $uniacid);
foreach ($readPack as $key => $val) {
$redPacket[$key] = $val['money'];
Redis::lPush('today_redpacket' . $uniacid, $val['money']);
}
$arr_redPacket = serialize($redPacket);
}
\Log::debug('当天红包池额度', $amount);
\Log::debug('当天红包数量', $number_sum);
\Log::debug('当天红包池', $arr_redPacket);
$red_Packet = storage_path('app/redPacket' . '/' . $uniacid);
app(Filesystem::class)->makeDirectory($red_Packet, 0777, 1);
$path = $red_Packet . '/redPacket.txt';
file_put_contents($path, $arr_redPacket);
}
}
//门店订单
public function getStoreOrder($set, $range)
{
$storeOrder = \Yunshop\RedPacket\common\models\Order::uniacid()
->select('id', 'uid', 'order_sn', 'created_at', 'price')
->with([
'hasOneStoreOrder' => function ($q) {
return $q->select('id', 'order_id', 'store_id');
}
]);
$storeOrder = $this->getStatus($set, $storeOrder, $range);
$storeOrder = $storeOrder->where('plugin_id', '32')
->get();
return $storeOrder;
}
// 门店余额充值订单
public function getBalanceOrderPriceTotal($set, $range)
{
$priceTotal = 0;
if (app('plugins')->isEnabled('store-cashier')) {
if ($set['self_order_after'] == 3) {
$orderIds = Order::select()->where('status', $set['self_order_after'])->whereBetween('finish_time', $range)->where('plugin_id', 39)->pluck('id');
} else {
$orderIds = Order::select()->where('status', '>=', $set['self_order_after'])->whereBetween('pay_time', $range)->where('plugin_id', 39)->pluck('id');
}
$balacneOrders = PluginBalanceOrder::with([
'hasOneShopOrder'
])->whereIn('order_id', $orderIds)->get();
if (!$balacneOrders->isEmpty()) {
foreach ($balacneOrders as $balacneOrder) {
$setting = RedPacketGoodsModel::where('goods_id', $balacneOrder->balance_id)
->where('is_cashier', 1)
->first();
$itemRes = proportionMath($balacneOrder->hasOneShopOrder->price, $set['proportion']);
if ($setting['is_cashier'] == 1 && $setting['cashier_proportion'] != 0) {
$itemRes = proportionMath($balacneOrder->hasOneShopOrder->price, $setting['cashier_proportion']);
}
$priceTotal += $itemRes;
}
}
}
return $priceTotal;
}
//收银台订单
public function getCashierOrder($set, $range)
{
$cashierOrder = \Yunshop\RedPacket\common\models\Order::uniacid()
->select('id', 'uid', 'order_sn', 'created_at', 'price')
->with([
'hasOneCashierOrder' => function ($q) {
return $q->select('id', 'order_id', 'cashier_id');
}
]);
$cashierOrder = $this->getStatus($set, $cashierOrder, $range);
$cashierOrder = $cashierOrder->where('plugin_id', '31')
->orderBy('id', 'DESC')
->get();
return $cashierOrder;
}
//cps订单
public function getCpsOrder($set, $range)
{
$cpsOrder = \Yunshop\RedPacket\common\models\Order::uniacid()
->select('id', 'uid', 'order_sn', 'created_at', 'price')
->with([
'hasOneCpsOrder' => function ($q) {
return $q->select('id', 'order_id');
}
]);
$cpsOrder = $this->getStatus($set, $cpsOrder, $range);
$cpsOrder = $cpsOrder->where('plugin_id', '70')
->orderBy('id', 'DESC')
->get();
return $cpsOrder;
}
//(平台,供应商,酒店,拼团)订单
public function getOrders($set, $range)
{
$order = \Yunshop\RedPacket\common\models\Order::uniacid()
->select('price');
$order = $this->getStatus($set, $order, $range);
$order = $order->whereIn('plugin_id', [0, 33, 92, 54])
->orderBy('id', 'DESC')
->sum('price');
\Log::debug('(平台,供应商,酒店,拼团)订单统计:' . $order);
return $order;
}
/**
* Common: 2023-9-13 修改 兼容商品独立计算规则
* Author: wu-hui
* Time: 2023/09/14 10:18
* @param $set
* @param $range
* @return float|int
*/
public function getOrdersV2($set, $range){
$order = \Yunshop\RedPacket\common\models\Order::uniacid();
$order = $this->getStatus($set, $order, $range);
$orderIds = $order->whereIn('plugin_id', [0, 33, 92, 54])->orderBy('id', 'DESC')->pluck('id')->toArray();
$orderGoodsList = OrderGoods::uniacid()
->with(['redPacketGoods'=>function($query){
$query->select(['goods_id','is_open','scale_all']);
}])
->select(['id','goods_id','payment_amount'])
->whereIn('order_id',$orderIds)
->get()
->toArray();
// 循环处理 获取商城结算金额
(float)$amount = 0;
foreach($orderGoodsList as $goodsItem){
if((int)$goodsItem['red_packet_goods']['is_open'] == 1){
$proportion = (float)$goodsItem['red_packet_goods']['scale_all'] > 0 ? (float)$goodsItem['red_packet_goods']['scale_all'] : (float)$set['proportion'];
$amount += $goodsItem['payment_amount'] * $proportion;
}
}
return $amount;
}
//读取订单状态
public function getStatus($set, $order, $range)
{
if ($set['self_order_after'] == 3) {
$order = $order->where('status', $set['self_order_after'])->whereBetween('finish_time', $range);
} else {
$order = $order->where('status', '>=', $set['self_order_after'])->whereBetween('pay_time', $range);
}
return $order;
}
//单独修改红包池
public function handless($uniacid)
{
date_default_timezone_set('PRC');
$set = Setting::get('plugin.red-packet');
\YunShop::app()->uniacid = $uniacid;
Setting::$uniqueAccountId = $uniacid;
$red_packet = $this->getOrder($set);
if ($set['is_open_red_packet'] == 1) {
$this->getData($red_packet, $set);
}
}
/**
* @param int $red_packet_max
* @param string $amount
* @param string $number_sum
* 生成红包 封顶规则
*/
private function redPacketSum2($red_packet_max, $amount = '', $number_sum = '')
{
$min_money = 0.01;
$mim_amount = $number_sum * $min_money;
$uniacid = \YunShop::app()->uniacid;
//红包数*上限 > 总红包金额
$maxRedPacket = bcmul($number_sum, $red_packet_max, 2);
$max_money = $red_packet_max;
if ($maxRedPacket > $amount) {
$max_money = bcdiv($amount, $number_sum, 2);//取平均值为上限
}
if ($amount < $mim_amount) {
\Log::debug('每日产生红包低于平均人数红包值');
$red_Packet = storage_path('app/redPacket' . '/' . $uniacid);
app(Filesystem::class)->makeDirectory($red_Packet, 0777, 1);
$path = $red_Packet . '/redPacket.txt';
$arr_redPacket = '';
file_put_contents($path, $arr_redPacket);
} else {
if ($amount == '') {
$arr_redPacket = '';
} else {
for ($i = 0; $i < $number_sum; $i++) {
$safe_total = $max_money;
$money = round((mt_rand($min_money * 100, $safe_total * 100) / 100), 2);
$amount = $amount - $money;
//红包数据
$readPack[] = [
'money' => round($money, 2),
];
}
\Log::debug('删除每日红包2');
Redis::del('today_redpacket' . $uniacid);
foreach ($readPack as $key => $val) {
$redPacket[$key] = $val['money'];
Redis::lPush('today_redpacket' . $uniacid, $val['money']);
}
$arr_redPacket = serialize($redPacket);
}
\Log::debug('当天红包池额度', $amount);
\Log::debug('当天红包数量', $number_sum);
\Log::debug('当天红包池', $arr_redPacket);
$red_Packet = storage_path('app/redPacket' . '/' . $uniacid);
app(Filesystem::class)->makeDirectory($red_Packet, 0777, 1);
$path = $red_Packet . '/redPacket.txt';
file_put_contents($path, $arr_redPacket);
}
}
//额外奖励
protected function getBonus($redPackData, $number_sum, $set, $redPacketLogsId)
{
if (empty($redPackData) || $set['is_bonus'] == 0) {
return;
}
$uniacid = \YunShop::app()->uniacid;
//奖励金额是固定还是百分比
if ($set['bonus_type'] == 1) {
$reward = round($set['bonus_amount'], 2);//固定金额
} else {
$reward = round(proportionMath($redPackData, $set['bonus_amount']), 2);//红包池的百分比
}
if (!empty($reward)) {
//删除额外红包,生成新额外奖励红包
Redis::del('today_redpacket_bonus' . $uniacid);
for ($i = 0; $i < $number_sum; $i++) {
Redis::lPush('today_redpacket_bonus' . $uniacid, $reward);
}
//生成额外奖励记录
BonusCreateLogsModel::create([
'uniacid' => $uniacid,
'amount' => bcmul($number_sum, $reward, 2),
'number_sum' => $number_sum,
'create_type' => $set['bonus_type'],
'create_amount' => $reward,
'red_packet_logs_id' => $redPacketLogsId
]);
}
}
/**
* @param array $set
* @return bool
*/
protected function validateCreateRedPacketConditions($set)
{
// 红包工具 基础设置 开启, 不执行每日红包业务逻辑
if (app('plugins')->isEnabled('redpack-tool')) {
$toolSetting = Setting::get('plugin.redpack_tool');
if ($toolSetting['is_open']) {
\Log::debug('红包工具基础设置开启,不执行每日红包业务逻辑');
return false;
}
}
//当天是否已生成红包
if ($this->setLog['current_d'] == date('d')) {
\Log::debug('每日红包-' . date('d') . '号已生成红包,当前不可生成');
return false;
}
//未开启
if ($set['is_open_red_packet'] != 1) {
return false;
}
//设置当前日期
$this->setLog['current_d'] = date('d');
Setting::set(self::SET_LOG_NAME, $this->setLog);
return true;
}
/**
* @param string $amount 每日生成红包金额
* @param string $number_sum 最大红包数量
* @param array $grads 梯度设置
* 梯度生成红包(随机生成,不可用于判断是否超限,实际领取的时候再判断不可超限)
*/
protected function gradsCreateRedPacket($red_packet_amount,$number_sum,$grads)
{
$uniacid = \YunShop::app()->uniacid;
//获取梯度设置
$quota = array_column($grads,'amount','quota');
foreach ($quota as $key => $value) {
$amount_section = explode('_',$value);//金额区间
\Log::debug('删除旧梯度:' . 'grads_redpacket_' . $uniacid . '_' . $key);
Redis::del('grads_redpacket_' . $uniacid . '_' . $key);//删除旧梯度
$total = $breakSign = 0;
for ($i = 1;$i <= $number_sum;$i++) {
$amount = (float)sprintf("%.2f",mt_rand($amount_section[0]*100,$amount_section[1]*100)/100);//随机生成范围内浮点
//第一次生成 todo 后续优化成领取的时候实时生成
if ($i === 1 && bccomp($amount,$red_packet_amount,2) == 1) {
Redis::lPush('grads_redpacket_' . $uniacid . '_' . $key, $red_packet_amount);//最大金额
$total += $amount;
continue;
}
//此次生成的金额大于总额,结束生成
if (bccomp($total+$amount,$red_packet_amount,2) == 1) {
\Log::debug('grads_redpacket_' . $uniacid . '_' . $key . ':超总额不生成',[$total+$amount,$red_packet_amount]);
break;
}
$total += $amount;
Redis::lPush('grads_redpacket_' . $uniacid . '_' . $key, $amount);//list放入当前梯度
}
}
}
}