| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500 |
- <?php
- namespace addons\Seed\common\logic;
- use addons\Seed\common\enums\SeedEnum;
- use addons\Seed\common\models\SeedCapitalStatistics;
- use addons\Seed\common\models\SeedEnergyPool;
- use addons\Seed\common\models\SeedLog;
- use addons\Seed\common\models\SeedOrder;
- use addons\Seed\common\models\SeedStoreSetting;
- use addons\Seed\common\models\SeedUser;
- use addons\Seed\common\models\SeedUserStatistics;
- use addons\Store\common\models\Store;
- use addons\Store\common\models\StoreCashierOrder;
- use addons\Store\common\models\StoreOrder;
- use addons\Store\common\models\StoreOrderCredit;
- use addons\Store\common\models\StoreVideoOrder;
- use common\enums\RabbitMqEnum;
- use common\enums\RedisKeyEnum;
- use common\enums\StatusEnum;
- use common\helpers\LockHelper;
- use common\models\mall\MallAddons;
- use common\traits\ErrorTrait;
- use yii\db\Exception;
- use yii\db\Expression;
- use yii\helpers\Json;
- use Yii;
- /**
- * Class SeedLogic
- * @package addons\Seed\common\logic
- */
- class SeedLogic
- {
- use ErrorTrait;
- /**
- * 支付成功/订单完成处理
- * @Author: lun
- * @DateTime: 2022/7/27 0027 15:30
- * @Copyright: copyright (c) 2021 广东七件事集团
- * @param $order_id
- * @param $order_status
- * @param int $event_pay 事件类型[1=普通订单,2=收银台订单]
- * @return bool
- */
- public static function handle($order_id, $order_status, $event_pay = 1)
- {
- $trans = Yii::$app->db->beginTransaction();
- try {
- // 查询订单信息
- if ($event_pay == 1) {
- $order = StoreOrder::getOne([['id' => $order_id]], true, ['detail']);
- } elseif($event_pay == 2) {
- $order = StoreCashierOrder::getOne([['id' => $order_id]], true);
- } else {
- $order = StoreVideoOrder::getOne([['id' => $order_id]], true);
- }
- if (empty($order)) throw new \Exception('暂无用户订单信息');
- $order['order_status'] = $order_status;// 订单状态赋值
- $order['event_pay'] = $event_pay;
- if ($event_pay == 1) {
- $pay_money = $order['pay_money'] - $order['shipping_money'];// 支付金额-运费
- } else {
- $pay_money = $order['pay_money'];// 实付金额
- }
- if ($pay_money <= 0) {
- $trans->commit();
- return true;
- }
- $percent = $start_percent = $end_percent = 0;// 获取种子金数量百分比
- // 获取基础设置内容
- $config = Yii::$app->services->setting->addons->get($order['mall_id'], SeedEnum::ADDONS_NAME, 'basic');
- // 不开启直接退出
- if ($config['seed_status'] == 0) {
- $trans->commit();
- return true;
- }
- // 判断商家是否参与种子金活动
- $no_partake_stores = empty($config['no_partake_stores']) ? [] : Json::decode($config['no_partake_stores'], true);
- if ($no_partake_stores && in_array($order['store_id'], $no_partake_stores)) {
- $trans->commit();
- return true;
- }// 不参与直接退出
- // 检查数据库中是否已存在该订单记录,存在则不插入避免资金叠加
- $has_order = SeedOrder::find()->where(['order_id' => $order_id, 'event_pay' => $event_pay, 'status' => StatusEnum::ENABLED])->asArray()->one();
- if ($has_order) {
- $trans->commit();
- return true;
- }
- // 根据订单实付金额获取可发的种子金数量
- $quota_setting = empty($config['quota_setting']) ? [] : Json::decode($config['quota_setting'], true);
- if (empty($quota_setting)) {
- $trans->commit();
- return true;
- }// 无设置种子金数量额度直接退出
- // 循环获取种子金比例
- srand();
- foreach ($quota_setting as $item) {
- if ($item['start_pay_money'] <= $pay_money && $item['end_pay_money'] >= $pay_money) {
- $start_percent = $item['start_percent'];
- $end_percent = $item['end_percent'];
- $percent = rand($start_percent, $end_percent);
- break;
- }
- }
- $seed_num = floor($pay_money * $percent / 100);// 种子金数量
- if (empty($seed_num)) {// 获取的数量为0直接退出
- $trans->commit();
- return true;
- }
- // 检查订单是否是赊账订单
- $check_order = StoreOrderCredit::find()->where(['order_id' => $order['id']])->asArray()->one();
- // 计算存入能量池金额
- $ad_pool_percent = $event_pay == 3 ? $config['ad_percent'] : $config['pool_percent'];
- $join_pool_money = sprintf("%.2f",substr(sprintf("%.3f", ($pay_money * $ad_pool_percent / 100)), 0, -1));// 不四舍五入保留两位小数
- // 额外参数
- $extra = [
- 'join_pool_money' => $join_pool_money,
- 'desc' => '店铺消费',
- 'capital_type' => SeedEnum::LOG_CONSUME,
- 'setting' => Json::encode(['percent' => $percent, 'ad_pool_percent' => $ad_pool_percent, 'multiple' => $config['multiple'], 'quota_setting' => $config['quota_setting']]),
- ];
- // 消费者获得种子金
- self::ConsumeGetSeed($order['user_id'], $seed_num, $order, $config, $extra, $check_order, 1);
- // 获取店铺绑定的会员id
- $store_info = Store::find()->select('user_id')->where(['id' => $order['store_id']])->asArray()->one();
- // 获取店长种子金数量、价值、单价计算
- $store_seed_num = self::getSeedCalculate($order['mall_id'], $order['store_id'], $pay_money);
- // 店长获得种子金 - 店长发布广告不参与
- if ($event_pay <= 2) {
- self::ConsumeGetSeed($store_info['user_id'], $store_seed_num, $order, $config, $extra, $check_order, 2);
- }
- // 种子金/能量池累计
- $total_seed_num = $seed_num + $store_seed_num;
- $res = SeedCapitalStatistics::saveStatistics($order['mall_id'], [
- 'total_seed_num' => $total_seed_num,
- 'total_energy_pool' => $join_pool_money,
- 'cumulative_energy_pool' => $join_pool_money,
- ]);
- if ($res === false) throw new \Exception(SeedUserStatistics::getStaticError());
- $trans->commit();
- return true;
- } catch (\Exception $e) {
- $trans->rollBack();
- self::setStaticError('第'.$e->getLine().'行,原因:'.$e->getMessage());
- return false;
- }
- }
- /**
- * 能量池释放 - N个小时释放一次
- * @Author: lun
- * @DateTime: 2022/8/1 0001 16:45
- * @Copyright: copyright (c) 2021 广东七件事集团
- * @param $mall_id
- */
- public static function release($mall_id)
- {
- $trans = Yii::$app->db->beginTransaction();
- $lock_key = RedisKeyEnum::suffix(SeedEnum::LOCK_POOL , $mall_id);
- try {
- $time = time();
- // 获取释放的设置
- $config = Yii::$app->services->setting->addons->get($mall_id, SeedEnum::ADDONS_NAME, 'basic');
- if (empty($config)) {
- $trans->commit();
- return true;
- }
- // 获取能量池数据
- $pool = SeedCapitalStatistics::findOne(['mall_id' => $mall_id, 'status' => StatusEnum::ENABLED]);
- // 判断是否已到释放时间 - 时间误差在2分钟内
- if ($pool->last_release_at + $config['release_cycle'] * 3600 > $time + 120) {
- $trans->commit();
- return true;
- }
- // 加锁
- LockHelper::lock($lock_key, SeedEnum::LOCK_POOL_UNIQID, 30, 60);
- // 获取未纳入能量池释放的能量池总额
- $frozen_seed_num = SeedOrder::find()->where(['mall_id' => $mall_id, 'seed_status' => SeedEnum::SEED_ADD, 'status' => StatusEnum::ENABLED])->andWhere(['>', 'can_send_at', $time])->sum("seed_num") ?? 0;
- // 已停止增长的种子金总数
- $stop_seed_num = SeedOrder::find()->where(['mall_id' => $mall_id, 'seed_status' => SeedEnum::SEED_STOP, 'status' => StatusEnum::ENABLED])->sum("seed_num") ?? 0;
- // 当前收取种子金数量 = 累计 - 已收取 - 退款 - 坏账 - 未到释放时间的种子金 - 已停止增长的种子金
- $seed_num = $pool->total_seed_num - $pool->collect_seed_num - $pool->refund_seed_num - $pool->bad_seed_num - $frozen_seed_num - $stop_seed_num;
- if ($seed_num == 0) {// 没有可收取的种子金则直接退出
- $trans->commit();
- // 解锁
- LockHelper::uLock($lock_key, SeedEnum::LOCK_POOL_UNIQID);
- return true;
- } elseif ($seed_num < 0) {
- throw new \Exception("释放的种子金数量小于零:总数={$pool->total_seed_num},已收取={$pool->collect_seed_num},退款={$pool->refund_seed_num},坏账={$pool->bad_seed_num},未到释放时间总数={$frozen_seed_num}");
- }
- $total_energy_pool = $pool->total_energy_pool;// 当前能量池总额
- // 可释放的单价 = 当前能量池总额 / 当前收取种子金数量;保留两位小数不四舍五入
- $unit_price = sprintf("%.2f",substr(sprintf("%.3f", ($total_energy_pool / $seed_num)), 0, -1));
- $release_energy_pool = $unit_price * $seed_num;// 本次释放能量
- if ($release_energy_pool <= 0) $release_energy_pool = $unit_price = 0;// 本次释放的金不足则改为不释放
- if ($release_energy_pool > $pool->total_energy_pool) throw new \Exception("释放的能量池额度大于现有的能量池总额:释放={$release_energy_pool},现有={$pool->total_energy_pool}");
- $pool->total_energy_pool -= $release_energy_pool;
- $pool->release_energy_pool += $release_energy_pool;
- $pool->release_num += 1;
- $pool->last_release_at = $time;
- if (!$pool->save()) throw new Exception($pool->getErrorMessage());
- // 增加释放记录
- $pool_log = SeedEnergyPool::setData([
- 'mall_id' => $pool->mall_id,
- 'release_num' => $pool->release_num,
- 'total_seed_num' => $seed_num,
- 'release_energy_pool' => $release_energy_pool,
- 'unit_price' => $unit_price,
- 'release_at' => $time,
- ]);
- if ($pool_log === false) throw new \Exception(SeedEnergyPool::getStaticError());
- $trans->commit();
- // 释放能量跟单价大于0则执行后续队列
- if ($release_energy_pool > 0 && $unit_price > 0) {
- // 异步队列进行每个订单的能量释放
- Yii::$app->services->rabbitMq->push(RabbitMqEnum::EXCHANGE_TASK, RabbitMqEnum::TASK_QUEUE, [
- 'pool_id' => $pool_log['id'],
- 'mall_id' => $mall_id,
- 'unit_price' => $unit_price,
- 'release_at' => $time,
- ], self::class, 'handleSync');
- }
- return true;
- } catch (\Exception $e) {
- self::setStaticError('第'.$e->getLine().'行,原因:'.$e->getMessage());
- LockHelper::uLock($lock_key, SeedEnum::LOCK_POOL_UNIQID);
- $trans->rollBack();
- return false;
- }
- }
- /**
- * 异步处理发放种子金价值
- * @Author: lun
- * @DateTime: 2022/8/3 0003 10:09
- * @Copyright: copyright (c) 2021 广东七件事集团
- * @param $params
- */
- public static function handleSync($params)
- {
- $trans = Yii::$app->db->beginTransaction();
- $lock_key = RedisKeyEnum::suffix(SeedEnum::LOCK_POOL , $params['mall_id']);
- try {
- $part = 1;
- $page_size = 1000;
- $time = time();
- // 获取基础配置信息
- $config = Yii::$app->services->setting->addons->get($params['mall_id'], SeedEnum::ADDONS_NAME, 'basic');
- // 获取此次发放的池记录
- $pool = SeedEnergyPool::findOne(['id' => $params['pool_id']]);
- if (empty($pool)) {
- $trans->commit();
- LockHelper::uLock($lock_key, SeedEnum::LOCK_POOL_UNIQID);
- return true;
- }
- while ($list = SeedOrder::find()
- ->where(['mall_id' => $params['mall_id'], 'seed_status' => SeedEnum::SEED_ADD,'status' => StatusEnum::ENABLED])
- ->andWhere(['<=','can_send_at',$params['release_at']])
- ->offset(($part - 1) * $page_size)->limit($page_size)->asArray()->all()) {
- $seed_users = $auto_collect = $handle_credit = [];
- foreach ($list as $item) {
- $now_unit_price = $item['unit_price'] + $params['unit_price'];
- $add_seed_cost = sprintf("%.2f",substr(sprintf("%.3f", ($params['unit_price'] * $item['seed_num'])), 0, -1));// 总价值
- $seed_cost = $item['seed_cost'] + $add_seed_cost;
- // 修改用户的种子金价值
- $seed_users[$item['user_id']] = empty($seed_users[$item['user_id']]) ? $add_seed_cost : $seed_users[$item['user_id']] + $add_seed_cost;
- // 修改订单的单价、总价值
- SeedOrder::updateAll([
- 'unit_price' => $now_unit_price,
- 'seed_cost' => $seed_cost,
- 'increase_num' => $item['increase_num'] + 1,
- 'last_send_at' => $time,
- 'updated_at' => $time,
- ],['id' => $item['id']]);
- // 增加种子金变动记录
- $rows[] = [
- 'seed_order_id' => $item['id'],
- 'mall_id' => $item['mall_id'],
- 'store_id' => $item['store_id'],
- 'user_id' => $item['user_id'],
- 'order_id' => $item['order_id'],
- 'event_pay' => $item['event_pay'],
- 'seed_num' => $item['seed_num'],
- 'seed_cost' => $seed_cost,
- 'change_unit_price' => $item['unit_price'],
- 'after_unit_price' => $now_unit_price,
- 'desc' => '能量池释放',
- 'capital_type' => SeedEnum::LOG_RELEASE,
- 'pool_id' => $params['pool_id'],
- 'created_at' => $time,
- 'updated_at' => $time,
- ];
- // 检查是否是赊账订单
- if ($item['is_credit'] == StatusEnum::DISABLED) {
- // 判断满N倍自动释放到余额
- if ($now_unit_price / $item['basic_unit_price'] >= $config['multiple']) $auto_collect[$item['event_pay']][] = $item['id'];
- } else {
- // 判断增长金额 >= 赊账金额
- if ($seed_cost >= $item['credit_money']) {
- // 消费者处理 - 全部自动收取
- if ($item['identity'] == 1) {
- // 未还款订单无论是否达到倍数都将自动收取
- if ($item['is_able_collect'] == StatusEnum::DISABLED) {
- $auto_collect[$item['event_pay']][] = $item['id'];
- } else {
- // 已还款,当倍数大于设定倍数是将自动收取
- if ($now_unit_price / $item['basic_unit_price'] >= $config['multiple']) $auto_collect[$item['event_pay']][] = $item['id'];
- }
- } else {
- // 达到倍数则设置自动收取
- if ($now_unit_price / $item['basic_unit_price'] >= $config['multiple']) $auto_collect[$item['event_pay']][] = $item['id'];
- }
- } else {
- // 增长金额 < 赊账金额
- if ($item['identity'] == 1) {
- // 达到倍数,但是价值又不足以还款,则将此订单设为坏账(冻结)订单
- if ($now_unit_price / $item['basic_unit_price'] >= $config['multiple']) {
- // 判断是否属于已还款类型,未还款插入冻结,已还款则自动收取
- if ($item['is_able_collect'] == StatusEnum::DISABLED) {
- $bad_order_arr[] = $item['id'];
- } else {
- $auto_collect[$item['event_pay']][] = $item['id'];
- }
- }
- } else {
- // 达到倍数则设置自动收取
- if ($now_unit_price / $item['basic_unit_price'] >= $config['multiple']) $auto_collect[$item['event_pay']][] = $item['id'];
- }
- }
- }
- }
- // 修改用户种子金的价值
- if ($seed_users) {
- foreach ($seed_users as $user_id => $seed_cost) {
- SeedUser::updateAll([
- 'total_seed_cost' => new Expression('total_seed_cost+'.$seed_cost),
- 'updated_at' => $time,
- ], ['user_id' => $user_id, 'status' => StatusEnum::ENABLED]);
- }
- }
- // 批量插入种子金变动记录
- if (!empty($rows)) {
- $field = ['seed_order_id','mall_id','store_id','user_id','order_id','event_pay','seed_num','seed_cost','change_unit_price','after_unit_price','desc','capital_type','pool_id','created_at','updated_at'];
- Yii::$app->db->createCommand()->batchInsert(SeedLog::tableName(), $field, $rows)->execute();
- }
- // 达到N倍后自动收取种子金
- if ($auto_collect) {
- foreach ($auto_collect as $type => $seed_order_ids) {
- $handle_credit = SeedOrder::batchCollect($params['mall_id'], $seed_order_ids, $type);
- if ($handle_credit === false) throw new \Exception(SeedOrder::getStaticError());
- }
- }
- // 坏账订单处理
- if ($bad_order_arr) {
- $res = SeedOrder::badOrderHandle($params['mall_id'], $bad_order_arr);
- if ($res === false) throw new \Exception(SeedOrder::getStaticError());
- }
- // 赊账订单处理
- if ($handle_credit && MallAddons::isInstall($params['mall_id'], 'Store')) {
- foreach ($handle_credit as $type => $credit_order) {
- foreach ($credit_order as $order_id) {
- // 调用门店订单处理
- StoreOrderCredit::repayment($order_id,$type);
- }
- }
- }
- $part++;
- }
- // 修改释放状态
- $pool->release_status = SeedEnum::RELEASE_SUCCESS;
- if (!$pool->save()) throw new Exception($pool->getErrorMessage());
- $trans->commit();
- // 解锁
- LockHelper::uLock($lock_key, SeedEnum::LOCK_POOL_UNIQID);
- return true;
- } catch (\Exception $e) {
- $trans->rollBack();
- // 解锁
- LockHelper::uLock($lock_key, SeedEnum::LOCK_POOL_UNIQID);
- SeedCapitalStatistics::handleReleaseFail($params, '第'.$e->getLine().'行,原因:'.$e->getMessage());
- return true;
- }
- }
- /**
- * 用户消费获得种子金
- * @Author: lun
- * @DateTime: 2022/8/22 0022 14:30
- * @Copyright: copyright (c) 2021 广东七件事集团
- * @param $user_id
- * @param $seed_num
- * @param $order
- * @param $config
- * @param $extra
- * @param $check_order
- * @param $identity
- * @throws \Exception
- */
- public static function ConsumeGetSeed($user_id, $seed_num, $order, $config, $extra, $check_order, $identity = 1)
- {
- // 保存用户的种子金数量
- if ($seed_num) {
- $res = SeedOrder::consumeOrder([
- 'mall_id' => $order['mall_id'],
- 'store_id' => $order['store_id'],
- 'user_id' => $user_id,
- 'identity' => $identity,
- 'order_id' => $order['id'],
- 'order_no' => $order['order_no'],
- 'event_pay' => $order['event_pay'],
- 'goods_name' => $order['event_pay'] == 1 ? $order['detail'][0]['goods_name'] : $order['event_pay'] == 2 ? '收银台消费' : '发布广告',
- 'seed_num' => $seed_num,
- 'basic_unit_price' => $config['basic_unit_price'],
- 'unit_price' => $config['basic_unit_price'],
- 'seed_status' => SeedEnum::SEED_ADD,
- 'join_pool_money' => $identity == 1 ? $extra['join_pool_money'] : 0,// 消费者才增加能量池金额
- 'can_send_at' => time() + $config['release_cycle'] * 3600,
- 'credit_money' => empty($check_order) ? 0 : $check_order['credit_money'],
- 'is_credit' => empty($check_order) ? StatusEnum::DISABLED : StatusEnum::ENABLED,
- 'is_able_collect' => empty($check_order) ? StatusEnum::ENABLED : $identity == 1 ? ($order['event_pay'] <= 2 ? StatusEnum::DISABLED : StatusEnum::ENABLED) : StatusEnum::ENABLED,
- ], $extra);
- if ($res === false) throw new \Exception(SeedOrder::getStaticError());
- // 用户数据统计 - 为了防止数据并发,使用updateAllCounters做累加
- $res = SeedUserStatistics::setData([
- 'mall_id' => $order['mall_id'],
- 'store_id' => $order['store_id'],
- 'user_id' => $user_id,
- ], ['pay_order_num' => 1, 'seed_num' => $seed_num]);
- if ($res === false) throw new \Exception(SeedUserStatistics::getStaticError());
- }
- }
- /**
- * 计算店长可得种子金
- * @Author: lun
- * @DateTime: 2022/8/22 0022 14:23
- * @Copyright: copyright (c) 2021 广东七件事集团
- * @param $mall_id
- * @param $store_id
- * @param $pay_money
- * @return false|float
- */
- public static function getSeedCalculate($mall_id, $store_id, $pay_money)
- {
- // 店铺独立设置
- $percent = SeedStoreSetting::find()->select('percent')->where(['mall_id' => $mall_id, 'store_id' => $store_id, 'status' => StatusEnum::ENABLED])->scalar();
- if (empty($percent)) {
- $percent = Yii::$app->services->setting->addons->get($mall_id, SeedEnum::ADDONS_NAME, 'store.percent') ?? 0;
- }
- $seed_num = empty($percent) ? 0 : floor($pay_money * $percent / 100);// 种子金数量
- return $seed_num;
- }
- }
|