business_line = $business_line; self::$self->setConfig(); } return self::$self; } /** * 设置后缀 */ public function setConfig() { switch ($this->business_line) { case PayConstants::BUSINESS_APP: $this->suffix = '_app'; $this->cache_param = 'APP'; } } /** * 订单扣量 * @param integer $admin_id 后台用户ID * @param integer $channel_id 渠道ID * @param array $order_data 订单数据 * @param bool $is_activity 是否是活动订单 * @return ReturnObject */ public function orderKL($admin_id, $channel_id, $order_data, $is_activity = false) { if (WebUserService::instance()->getUserInfo()->is_black == UserConstants::USER_BLACK_NO) { $log_is_vip = $order_data['day'] ? '会员' : '普通'; $adminConfig = AdminService::instance()->getAdminKlModel()->getAdminKl($channel_id, $this->business_line); //检查渠道是否开启扣量 if (!$adminConfig['is_blacklist']) { LogService::info("NEW-KL逻辑-{$log_is_vip} 渠道商:" . $channel_id . ' 用户ID:' . $order_data['user_id'] . ' IP:' . Ip::ip() . ' 处理:已执行 结果:未命中 原因:渠道未开启KL'); return $this->setData(false)->getReturn(); } //检查代理商ID白名单 $kl_agent_blacklist = explode(',', $this->getConfig('kl_agent_blacklist')); if (in_array($admin_id, $kl_agent_blacklist)) { LogService::info("NEW-KL逻辑-{$log_is_vip} 代理商: {$admin_id} 渠道商:" . $channel_id . ' 用户ID:' . $order_data['user_id'] . ' IP:' . Ip::ip() . ' 处理:已执行 结果:未命中 原因:代理商ID白名单'); return $this->setData(false)->getReturn(); } //检查是否扣量白名单 if (!$this->checkIgnore($admin_id, $channel_id, $order_data, 'KL逻辑', $is_activity)->data) { return $this->setData(false)->getReturn(); } //检查扣量基数 $kl_base = $this->getDeductionBase($adminConfig, $order_data['day'], false); if (!$kl_base || $kl_base < 0) { LogService::info("NEW-KL逻辑-{$log_is_vip} 渠道商:" . $channel_id . ' 用户ID:' . $order_data['user_id'] . ' IP:' . Ip::ip() . ' 处理:已执行 结果:未命中 原因:渠道未开启KL'); return $this->setData(false)->getReturn(); } //检测是否到达扣量比例 if (!$this->checkIsDeductThreshold($channel_id, $adminConfig, $order_data['user_id'])->data) { return $this->setData(false)->getReturn(); } //获取计数器,未到扣量基数时累加,到扣量基数时暂停,直到扣量后继续累加 $kl_index = $this->getDeductionCounter($channel_id, $order_data['day'], false, false)->data; //扣量计数器判断 if ($kl_index % $kl_base != 0) { LogService::info("NEW-KL逻辑-{$log_is_vip} 渠道商:" . $channel_id . ' 用户ID:' . $order_data['user_id'] . ' IP:' . Ip::ip() . ' 处理:已执行 结果:未命中 基数:' . $kl_base . ' 单数:' . $kl_index . ' 取模:' . $kl_index % $kl_base); $this->getDeductionCounter($channel_id, $order_data['day'], false)->data; return $this->setData(false)->getReturn(); } $this->getDeductionCounter($channel_id, $order_data['day'], false)->data; LogService::info("NEW-KL逻辑-{$log_is_vip} 渠道商:" . $channel_id . ' 用户ID:' . $order_data['user_id'] . ' IP:' . Ip::ip() . ' 处理:已执行 结果:已命中 基数:' . $kl_base . ' 单数:' . $kl_index . ' 取模:' . $kl_index % $kl_base); } //获取要转移的渠道 if (!$change_ids = $this->getConfig('change_ids')) { LogService::info("KL逻辑-原因:目标账户为空"); return $this->setData(false)->getReturn(); } //获取渠道信息 $arrchange = explode(',', $change_ids); $admin_id = $arrchange[array_rand($arrchange, 1)]; if (!$thisAdminExtend = model('admin_extend')->where('admin_id', $admin_id)->find()) { LogService::info("KL逻辑-原因:获取目标账户信息失败:" . $admin_id); return $this->setData(false)->getReturn(); } return $this->setData($thisAdminExtend)->getReturn(); } /** * 订单转移 * @param integer $admin_id 后台用户ID * @param integer $channel_id 渠道ID * @param array $order_data 订单数据 * @param bool $is_activity 是否是活动订单 * @param bool $business_line 业务线 * @return array|bool|false|\PDOStatement|string|Model * @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\ModelNotFoundException * @throws \think\exception\DbException */ public function orderTransfer($admin_id, $channel_id, $order_data, $is_activity = false) { $log_is_vip = $order_data['day'] ? '会员' : '普通'; $adminConfig = AdminService::instance()->getAdminKlModel()->getAdminKl($channel_id); //检查是否开启转移 if (!$adminConfig['agent_is_blacklist']) { LogService::info("NEW-订单转移-{$log_is_vip} 渠道商:" . $channel_id . ' 用户ID:' . $order_data['user_id'] . ' IP:' . Ip::ip() . ' 处理:已执行 结果:未命中 原因:渠道未开启KL'); return false; } //检查是否扣量白名单 if (!$this->checkIgnore($admin_id, $channel_id, $order_data, '订单转移', $is_activity)->data) { return false; } //检查扣量基数 $kl_base = $this->getDeductionBase($adminConfig, $order_data['day'], true); if (!$kl_base || $kl_base < 0) { LogService::info("NEW-订单转移-{$log_is_vip} 渠道商:" . $channel_id . ' 用户ID:' . $order_data['user_id'] . ' IP:' . Ip::ip() . ' 处理:已执行 结果:未命中 原因:渠道未开启KL'); return false; } //获取计数器 $kl_index = $this->getDeductionCounter($admin_id, $order_data['day'], true)->data; if ($kl_index % $kl_base != 0) { LogService::info("NEW-订单转移-{$log_is_vip} 渠道商:" . $channel_id . ' 用户ID:' . $order_data['user_id'] . ' IP:' . Ip::ip() . ' 处理:已执行 结果:未命中 基数:' . $kl_base . ' 单数:' . $kl_index . ' 取模:' . $kl_index % $kl_base); return false; } if (!$agentObj = model('admin_extend')->where('admin_id', $admin_id)->find()) { LogService::info("NEW-订单转移-{$log_is_vip} 渠道商:" . $channel_id . ' 用户ID:' . $order_data['user_id'] . ' IP:' . Ip::ip() . ' 处理:已执行 结果:未命中 基数:' . $kl_base . ' 单数:' . $kl_index . ' 取模:' . $kl_index % $kl_base . ' 原因:获取代理信息失败'); return false; } if (!$thisAdminExtend = model('admin_extend')->where('admin_id', $agentObj->create_by)->find()) { LogService::info("NEW-订单转移-{$log_is_vip} 渠道商:" . $channel_id . ' 用户ID:' . $order_data['user_id'] . ' IP:' . Ip::ip() . ' 处理:已执行 结果:未命中 基数:' . $kl_base . ' 单数:' . $kl_index . ' 取模:' . $kl_index % $kl_base . ' 原因:获取代理上级渠道信息失败'); return false; } LogService::info("NEW-订单转移-{$log_is_vip} 渠道商:" . $channel_id . ' 用户ID:' . $order_data['user_id'] . ' IP:' . Ip::ip() . ' 处理:已执行 结果:已命中 基数:' . $kl_base . ' 单数:' . $kl_index . ' 取模:' . $kl_index % $kl_base); return $thisAdminExtend; } /** * 获取计数信息 * @param int $channel_id 渠道ID * @param int $is_vip 是否是VIP * @param bool $is_transfer 是否是转移订单 * @param bool $is_incr 是否自增 * @param bool $business_line 是否自增 * @return ReturnObject */ public function getDeductionCounter($channel_id, $is_vip, $is_transfer = false, $is_incr = true) { if ($is_transfer) { if ($this->cache_param) { $vip_key = implode(':', ['KAV', $this->cache_param, $channel_id]); $nor_key = implode(':', ['KAN', $this->cache_param, $channel_id]); } else { $vip_key = implode(':', ['KAV', $channel_id]); $nor_key = implode(':', ['KAN', $channel_id]); } } else { if ($this->cache_param) { $vip_key = implode(':', ['KCV', $this->cache_param, $channel_id]); $nor_key = implode(':', ['KCN', $this->cache_param, $channel_id]); } else { $vip_key = implode(':', ['KCV', $channel_id]); $nor_key = implode(':', ['KCN', $channel_id]); } } if ($is_vip) { //vip if (Redis::instance()->exists($vip_key)) { if ($is_incr) { return $this->setData(Redis::instance()->incr($vip_key))->getReturn(); } else { return $this->setData(Redis::instance()->get($vip_key))->getReturn(); } } else { Redis::instance()->set($vip_key, 1); return $this->setData(1)->getReturn(); } } else { //普通 if (Redis::instance()->exists($nor_key)) { if ($is_incr) { return $this->setData(Redis::instance()->incr($nor_key))->getReturn(); } else { return $this->setData(Redis::instance()->get($nor_key))->getReturn(); } } else { Redis::instance()->set($nor_key, 1); return $this->setData(1)->getReturn(); } } } /** * 获取扣量基数 * @param array $adminConfig 后台用户信息 * @param int $is_vip 是否是vip * @param bool $is_transfer 是否是转移订单 * @param bool $business_line 是否是转移订单 * @return int */ public function getDeductionBase($adminConfig, $is_vip, $is_transfer = false) { if ($is_transfer) { $vip_key = 'agent_vip_num'; $nor_key = 'agent_normal_num'; } else { $vip_key = 'vip_num'; $nor_key = 'normal_num'; } //扣量基数,默认取渠道商,渠道商没有配置取系统配置 if ($is_vip) { if (intval($adminConfig[$vip_key]) > 0) { return intval($adminConfig[$vip_key]); } else { return intval($this->getConfig($vip_key)); } } else { if (intval($adminConfig[$nor_key]) > 0) { return intval($adminConfig[$nor_key]); } else { return intval($this->getConfig($nor_key)); } } } /** * 检测渠道扣量是否达到比例 * @param $admin_id * @param $adminConfig * @return ReturnObject */ public function checkIsDeductThreshold($admin_id, $adminConfig, $user_id) { try { if (isset($adminConfig['kl_rate'])) { $sys_rate = floatval($adminConfig['kl_rate']) ? $adminConfig['kl_rate'] : $this->getConfig('kl_rate'); } else { $sys_rate = $this->getConfig('kl_rate'); } $all_money = $this->getChannelOrderPayMoney($admin_id); $deduct_money = $this->getChannelOrderPayMoney($admin_id, true); $deduct_rate = $all_money ? strval(round($deduct_money / $all_money, 2)) : 0; if (floatval($deduct_rate) < floatval($sys_rate)) { LogService::info("NEW-KL逻辑-阈值 渠道商:" . $admin_id . ' 用户ID:' . $user_id . ' IP:' . Ip::ip() . ' 处理:已执行 日志: 命中' . ' 已付款金额:' . strval(intval($all_money) / 100) . ' 已扣量金额:' . strval(intval($deduct_money) / 100) . ' 扣量比例:' . $deduct_rate . ' 配置比例:' . $sys_rate . ' 未到阈值'); return $this->setData(true)->getReturn(); } else { LogService::info("NEW-KL逻辑-阈值 渠道商:" . $admin_id . ' 用户ID:' . $user_id . ' IP:' . Ip::ip() . ' 处理:已执行 日志: 命中' . ' 已付款金额:' . strval(intval($all_money) / 100) . ' 已扣量金额:' . strval(intval($deduct_money) / 100) . ' 扣量比例:' . $deduct_rate . ' 配置比例:' . $sys_rate . ' 已到阈值'); return $this->setData(false)->getReturn(); } } catch (\Exception $e) { LogService::error('NEW-OrderDeduct->Info: 检测扣量金额阈值错误:' . $e->getMessage()); return $this->setData(true)->getReturn(); } } /** * 获取当前时间的前24小时,包含当前小时 * @return array */ public function getBeforeTwentyFourTime() { $before_array = []; $current_time = time(); $first_time = strtotime('-1 day'); for ($time = $first_time; $time <= $current_time; $time += 3600) { array_push($before_array, $time); } return $before_array; } /** * 获取当前渠道支付成功的订单金额 * @param $admin_id * @param bool $is_deduct * @return float|int */ public function getChannelOrderPayMoney($admin_id, $is_deduct = false) { $count_money = 0; $before_time = $this->getBeforeTwentyFourTime(); foreach ($before_time as $val) { $key = $this->getOrderPayMoneyKey($is_deduct, $admin_id, $val)->data; if (Redis::instance()->exists($key)) { $count_money += intval(Redis::instance()->get($key)); } } return $count_money; } /** * 获取充值缓存key * @param $is_deduct * @param $admin_id * @param string $val * @return ReturnObject */ public function getOrderPayMoneyKey($is_deduct, $admin_id, $val = '') { if ($is_deduct) { $prefix = 'KDM'; } else { $prefix = 'KAM'; } if ($val) { $hour = date('dH', $val); } else { $hour = date('dH'); } if ($this->cache_param) { $list = [$prefix, $this->cache_param, $admin_id, $hour]; } else { $list = [$prefix, $admin_id, $hour]; } $key = implode(':', $list); return $this->setData($key)->getReturn(); } /** * 添加渠道订单支付金额统计 * @param $admin_id * @param $money * @param $order_no * @param bool $is_deduct 是否是扣量订单 */ public function addChannelOrderMoneyCache($admin_id, $money, $order_no, $is_deduct = false) { try { $group_id = model('AuthGroupAccess')->getGroupId($admin_id); //代理商 if ($group_id == 4) { $adminExtend = model('AdminExtend')->getInfo($admin_id); $admin_id = $adminExtend['create_by']; } $key = $this->getOrderPayMoneyKey($is_deduct, $admin_id)->data; if (Redis::instance()->exists($key)) { Redis::instance()->incrBy($key, intval($money * 100)); } else { Redis::instance()->set($key, intval($money * 100)); Redis::instance()->expire($key, 86400); } LogService::info('NEW-OrderDeduct->Info: 添加渠道订单金额完成 IsDeduct:' . $is_deduct . ' ChannelID:' . $admin_id . ' OrderNo:' . $order_no . ' Monery:' . $money); } catch (\Exception $e) { LogService::error('NEW-OrderDeduct->Info: 添加渠道订单金额错误 IsDeduct:' . $is_deduct . ' ChannelID:' . $admin_id . ' OrderNo:' . $order_no . ' Monery:' . $money); } } /** * 检查扣量白名单 * @param int $admin_id 后台用户ID * @param int $channel_id 渠道ID * @param array $order_data 订单数据 * @param string $msgTitle 消息表头 * @param bool $is_activity 是否是活动订单 * @return ReturnObject */ public function checkIgnore($admin_id, $channel_id, $order_data, $msgTitle, $is_activity) { $user_info = model('User')->getUserInfo($order_data['user_id']); $log_is_vip = $order_data['day'] ? '会员' : '普通'; //用户上一次充值成功时间小于两小时 if (Redis::instance()->exists('UPT:' . $order_data['user_id'])) { WhiteList::addUserIdWhite($order_data['user_id']); LogService::info("NEW-白名单:" . ' 用户ID:' . $order_data['user_id'] . '添加时间: ' . date('Y-m-d H:i:s') . ' 原因:用户上一次充值成功时间小于两小时'); LogService::info("NEW-{$msgTitle}-{$log_is_vip} 渠道商:" . $channel_id . ' 用户ID:' . $order_data['user_id'] . ' City:' . Ip::province() . ' IP:' . Ip::ip() . ' 处理:已执行 结果:未命中 原因:用户上一次充值成功小于' . Config::get('site.pay_time')); return $this->setData(false)->getReturn(); } //用户创建时间与支付时间小于指定时间时加入用户白名单 if (Redis::instance()->exists('UCPT:' . $order_data['user_id'])) { WhiteList::addUserIdWhite($order_data['user_id']); LogService::info("NEW-白名单:" . ' 用户ID:' . $order_data['user_id'] . '添加时间: ' . date('Y-m-d H:i:s') . ' 原因:用户创建时间与支付时间小于指定时间时加入用户白名单'); LogService::info("NEW-{$msgTitle}-{$log_is_vip} 渠道商:" . $channel_id . ' 用户ID:' . $order_data['user_id'] . ' City:' . Ip::province() . ' IP:' . Ip::ip() . ' 处理:已执行 结果:未命中 原因:用户创建时间与下单间隔时间小于' . Config::get('site.create_pay_time')); return $this->setData(false)->getReturn(); } //判断客户在不在ip白名单 if (WhiteList::checkedIpWhite(Ip::ip())) { WhiteList::addUserIdWhite($order_data['user_id']); LogService::info("NEW-{$msgTitle}-{$log_is_vip} 渠道商:" . $channel_id . ' 用户ID:' . $order_data['user_id'] . ' IP:' . Ip::ip() . ' 处理:已执行 结果:未命中 原因:渠道IP白名单'); LogService::info("NEW-白名单:" . ' 用户ID:' . $order_data['user_id'] . '添加时间: ' . date('Y-m-d H:i:s') . ' 原因:用户IP在IP白名单内'); return $this->setData(false)->getReturn(); } //判断是不是用户白名单 if (($user_info['is_white'] ?? 0)) { LogService::info("NEW-{$msgTitle}-{$log_is_vip} 渠道商:" . $channel_id . ' 用户ID:' . $order_data['user_id'] . ' IP:' . Ip::ip() . ' 处理:已执行 结果:未命中 原因:用户ID白名单'); return $this->setData(false)->getReturn(); } //判断客户是否在同一城市 if (WhiteList::checkedCityWhite($admin_id, Ip::province())) { LogService::info("NEW-{$msgTitle}-{$log_is_vip} 渠道商:" . $channel_id . ' 用户ID:' . $order_data['user_id'] . ' City:' . Ip::province() . ' IP:' . Ip::ip() . ' 处理:已执行 结果:未命中 原因:渠道City白名单'); return $this->setData(false)->getReturn(); } //不是活动订单时,直冲不处理 if (!$is_activity && empty($order_data['book_id'])) { LogService::info("NEW-{$msgTitle}-{$log_is_vip} 渠道商:" . $channel_id . ' 用户ID:' . $order_data['user_id'] . ' City:' . Ip::province() . ' IP:' . Ip::ip() . ' 处理:已执行 结果:未命中 原因:不是活动时,直冲用户不扣量'); return $this->setData(false)->getReturn(); } //不是活动订单时,充值看点大于100不处理 if (!$is_activity && $user_info) { if (FinancialService::instance()->getTotalKandian($order_data['user_id'])->data > 100) { LogService::info("NEW-{$msgTitle}-{$log_is_vip} 渠道商:" . $channel_id . ' 用户ID:' . $order_data['user_id'] . ' City:' . Ip::province() . ' IP:' . Ip::ip() . ' 处理:已执行 结果:未命中 原因:不是活动时,充值看点大于100不扣量'); return $this->setData(false)->getReturn(); } } return $this->setData(true)->getReturn(); } /** * 获取配置 * @param $name * @return mixed */ public function getConfig($name) { $config = Config::get('site.' . $name . $this->suffix); return $config ? $config : ''; } }