You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

158 lines
7.4 KiB
PHP

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<?php
namespace App\Services;
use Carbon\Carbon;
use DateTime;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
class DayCutService
{
public static function Cut($day){
if(empty($day)){
return ['status'=>false,'msg'=>'请输入日切日期'];
}
if($day>date('Y-m-d')){
return ['status'=>false,'msg'=>'不可提前日切'];
}
$Carbon_day = Carbon::parse($day);
$members = DB::table("members")->where("is_del", 0)->get();
$configs = DB::table("configs")->get();
$config_wait_day=null;//计息等待天数
$cunkuan_rate=null;//存款利率
$start_time=null;//日切开始时间
foreach($configs as $config){
if($config->label == "计息等待"){
$config_wait_day = $config->value;
}
if($config->label == "存款利率"){
$cunkuan_nian_rate = bcdiv($config->value, '100', 10);//年化利率
$cunkuan_rate = bcdiv($cunkuan_nian_rate, '365', 10);//日利率
}
if($config->label == "日切开始时间"){
$start_time =$config->value;
}
}
// 兼容全角冒号,并补全秒
$start_time = trim(str_replace('', ':', $start_time));
if (substr_count($start_time, ':') === 1) {
$start_time .= ':00';
}
// 构造当天的日切时间点
$cutoff_datetime = $day . ' ' . $start_time;
// 比较当前时间是否已到日切时间
// dd($day);
if ($day == date('Y-m-d')) {
if (Carbon::now()->lt(Carbon::parse($cutoff_datetime))) {
return ['status' => false, 'msg' => '未到日切开始时间'];
}
}
if($config_wait_day === null){
return ['status'=>false,'msg'=>'请配置计息等待天数'];
}
if($cunkuan_rate === null){
return ['status'=>false,'msg'=>'请配置存款利率'];
}
$daysToAdd = $config_wait_day;
$successCount = 0;
$failures = [];
foreach($members as $member){
if (!empty($member->day_cut_at)) {
$member_cutday = Carbon::parse($member->day_cut_at)->startOfDay();
$target_day = $Carbon_day->copy()->startOfDay();
if ($member_cutday>=$target_day) {//只要用户已经日切过 >= 目标日期,就跳过
continue;
}
}
try{
DB::transaction(function () use ($member, $Carbon_day, $daysToAdd, $cunkuan_rate, $day) {
self::processMemberDayCut($member, $Carbon_day, $daysToAdd, $cunkuan_rate, $day);
}, 3); // 每个用户最多重试3次应对死锁
$successCount++;
}catch(\Exception $e) {
// 记录失败,但继续处理下一个用户
Log::error("日切失败 - 用户ID: {$member->id}, 错误: " . $e->getMessage(), [
'member_id' => $member->id,
'date' => $day,
'exception' => $e,
]);
$failures[] = [
'member_id' => $member->id,
'error' => $e->getMessage(),
];
}
}
DB::table('configs')->where('label', '日切日期')->update(['value' => $day]);
$msg = "日切完成:成功 {$successCount}";
if (!empty($failures)) {
$msg .= ",失败 " . count($failures) . " 人(详见日志)";
}
return ['status'=>true,'msg'=>$msg];
}
private static function processMemberDayCut($member, $Carbon_day, $daysToAdd, $cunkuan_rate, $day){
$transactions = DB::table("transactions")->where([ //查找未计算利息的交易
"member_id" => $member->id,
"is_del" => 0,
"status" => 3,
"is_interest_eligible" => 0
])->whereIn("type",[1,3])->orderBy("verify_time","asc")->get();
$member_interest_balance = (string)$member->interest_balance;//用户表计息本金余额
foreach($transactions as $transaction){
Log::info("处理交易 - 用户ID: {$member->id}, 交易ID: {$transaction->id}, 审核时间: {$transaction->verify_time}, 类型: {$transaction->type}");
$shenHeDate_n = Carbon::parse($transaction->verify_time)->startOfDay()->addDays($daysToAdd);
$trans_amount= (string)$transaction->amount; //本笔流水金额
if($transaction->type == 1){//充值
if ($shenHeDate_n->lte($Carbon_day->startOfDay())) { // 如果审核通过时间加上指定天数小于等于目标日期
$member_interest_balance = bcadd($trans_amount, $member_interest_balance, 2);//新的计息本金余额
// $up_member=DB::table("members")->where("id", $member->id)->update(["interest_balance" => $new_interest_balance]);//更新用户表计息本金余额
$up_trans=DB::table("transactions")->where("id", $transaction->id)->update(["is_interest_eligible" => 1]);//更新交易表为已更新计入利息本金
}
}
if($transaction->type == 3){//提现
if ($shenHeDate_n->lte($Carbon_day->startOfDay())) { // 如果审核通过时间加上指定天数小于等于目标日期
$member_interest_balance = bcsub($member_interest_balance, $trans_amount, 2);//新的计息本金余额
if (bccomp($member_interest_balance, '0', 2) < 0) {
throw new \DomainException("用户 {$member->id} 计息余额不足,当前: {$member_interest_balance}, 尝试扣除: {$trans_amount}");
}
// $up_member=DB::table("members")->where("id", $member->id)->update(["interest_balance" => $new_interest_balance]);//更新用户表计息本金余额
$up_trans=DB::table("transactions")->where("id", $transaction->id)->update(["is_interest_eligible" => 1]);//更新交易表为更新计入利息本金
}
}
Log::info("处理交易 - 用户ID: {$member->id}, 交易ID: {$transaction->id}, 新计息本金余额: {$member_interest_balance}");
}
//开始计息
// $member_new=DB::table("members")->where("id", $member->id)->first();
$day_interest = bcmul($member_interest_balance, $cunkuan_rate, 2);//新的利息
$new_all_interest_balance = bcadd($member_interest_balance, $day_interest, 2);//新的 计息本金 余额
$new_all_balance=bcadd($member->balance,$day_interest,2);//新的 余额
$total_interest=bcadd($member->total_interest,$day_interest,2);//新的 总利息
//插入利息日志表
$insert_interest_log=DB::table("daily_interest_logs")->insert([
"member_id" => $member->id,
"date" => $day,
"date_balance" =>$member_interest_balance,
"interest_rate" => $cunkuan_rate,
"interest_amount" => $day_interest,
"is_del" => 0,
"created_at" => date("Y-m-d H:i:s"),
]);
$up_member=DB::table("members")->where("id", $member->id)->update([
"balance"=>$new_all_balance,
"interest_balance" => $new_all_interest_balance,
"total_interest"=>$total_interest,
"day_cut_at"=>$day,
]);//更新用户表计息本金余额
}
}