用户端:付费单策略内取消自动退款

活动取消已下单自动取消并退全款流程
订单未支付自动取消定时任务
活动更新状态定时任务
活动结算单生成和结算单解冻定时任务
This commit is contained in:
焦钰锟 2025-04-11 18:17:29 +08:00
parent 4e4c48d872
commit e9bbe382a2
11 changed files with 947 additions and 41 deletions

View File

@ -0,0 +1,37 @@
<?php
namespace app\admin\controller\school\activity;
use app\common\controller\Backend;
/**
* 活动单售后退款原因
*
* @icon fa fa-circle-o
*/
class RefundRason extends Backend
{
/**
* RefundRason模型对象
* @var \app\admin\model\school\activity\RefundRason
*/
protected $model = null;
public function _initialize()
{
parent::_initialize();
$this->model = new \app\admin\model\school\activity\RefundRason;
}
/**
* 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法
* 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑
* 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改
*/
}

View File

@ -0,0 +1,10 @@
<?php
return [
'Title' => '选项',
'Refund_scale_json' => '额外参数',
'Weigh' => '权重',
'Createtime' => '创建时间',
'Updatetime' => '修改时间',
'Deletetime' => '删除时间'
];

View File

@ -0,0 +1,50 @@
<?php
namespace app\admin\model\school\activity;
use think\Model;
use traits\model\SoftDelete;
class RefundRason extends Model
{
use SoftDelete;
// 表名
protected $name = 'school_activity_refund_rason';
// 自动写入时间戳字段
protected $autoWriteTimestamp = 'integer';
// 定义时间戳字段名
protected $createTime = 'createtime';
protected $updateTime = 'updatetime';
protected $deleteTime = 'deletetime';
// 追加属性
protected $append = [
];
protected static function init()
{
self::afterInsert(function ($row) {
if (!$row['weigh']) {
$pk = $row->getPk();
$row->getQuery()->where($pk, $row[$pk])->update(['weigh' => $row[$pk]]);
}
});
}
}

View File

@ -127,7 +127,7 @@ class NewActivity extends Base
$params["city"] = $this->request->get('city/s', ''); //机构店铺id
$params["district"] = $this->request->get('district/s', ''); //机构店铺id
$params["status"] = $this->request->get('status/s', '-2'); //机构店铺id
$params["status"] = $this->request->get('status/s', ''); //机构店铺id
$params["recommend"] = $this->request->get('recommend/s', ''); //机构店铺id
$params["hot"] = $this->request->get('hot/s', ''); //机构店铺id
$params["new"] = $this->request->get('new/s', ''); //机构店铺id

View File

@ -226,7 +226,34 @@ class Order extends Base
$order_no = $this->request->post('order_no/s', ''); //订单号
try{
//当前申请状态
$res = $this->model->freeCancel($order_no,$user_id,true,'user',0,true);
$res = $this->model->freeCancel($order_no,$user_id,true,'user',$user_id,true);
}catch (\Throwable $e){
$this->error($e->getMessage());
}
$this->success('活动订单取消成功', $res);
}
/**
* @ApiTitle(付费活动支付后取消接口)
* @ApiSummary(付费活动支付后取消接口(支付后,活动开始前))
* @ApiMethod(POST)
* @ApiParams(name = "order_no", type = "string",required=true,description = "订单号")
* @ApiReturn({
*
*})
*/
public function paidcancel(){
$user_id = 0;
$user = $this->auth->getUser();//登录用户
if($user)$user_id = $user['id'];
$order_no = $this->request->post('order_no/s', ''); //订单号
try{
//当前申请状态
$res = $this->model->paidCancel($order_no,$user_id,true,'user',$user_id,false,true);
}catch (\Throwable $e){
$this->error($e->getMessage());
}

View File

@ -285,11 +285,14 @@ class Pay extends Base
Log::write('notifyr-result:' . json_encode($data));
$out_refund_no = $data['out_refund_no'];
$out_trade_no = $data['out_trade_no'];
//获取退款金额
$price = 0;
// 退款
$this->refundFinish($out_trade_no, $out_refund_no,$platform,$data);
$this->refundFinish($out_trade_no, $out_refund_no,$platform,$data,$price);
return $pay->success()->send();
@ -303,7 +306,7 @@ class Pay extends Base
}
private function refundFinish($out_trade_no, $out_refund_no,$platform,$refund_json) {
private function refundFinish($out_trade_no, $out_refund_no,$platform,$refund_json,$price=0) {
// $order = Order::where('order_sn', $out_trade_no)->find();
// $refundLog = \app\admin\model\shopro\order\RefundLog::where('refund_sn', $out_refund_no)->find();
@ -318,7 +321,7 @@ class Pay extends Base
// Db::transaction(function () use ($order, $item, $refundLog) {
// \app\admin\model\shopro\order\Order::refundFinish($order, $item, $refundLog);
// });
$this->model::refundSuccess($out_refund_no,$refund_json,true);
$this->model::refundSuccess($out_refund_no,$refund_json,$price,true);
return true;
}

View File

@ -290,19 +290,24 @@ $newactivityOrderHooks = [
'activity_order_cancel_after' => [ // 订单取消后
'app\\common\\listener\\activity\\OrderHook'
],
'activity_order_auth_fail_after' => [ // 新活动订单售后退款发起
'app\\common\\listener\\activity\\OrderHook'
],
// 'classes_activity_order_auth_success_after' => [ // 订单审核通过后
// 'app\\common\\listener\\activityorder\\OrderHook'
// ],
// 'classes_activity_order_auth_fail_after' => [ // 订单审核失败后
// 'app\\common\\listener\\activityorder\\OrderHook'
// ],
//
// 'classes_activity_order_refund_success_after'=> [ // 订单审核失败后退款成功
// 'app\\common\\listener\\activityorder\\OrderHook'
// ],
// 'classes_activity_order_refund_fail_after'=> [ // 订单审核失败后退款成功
// 'app\\common\\listener\\activityorder\\OrderHook'
// ],
'activity_order_refund_success_after'=> [ // 订单退款成功
'app\\common\\listener\\activity\\OrderHook'
],
'activity_order_refund_fail_after'=> [ // 订单售后退款失败
'app\\common\\listener\\activity\\OrderHook'
],
'activity_order_finish_after' => [ // 订单完成后
'app\\common\\listener\\activity\\OrderHook'
],

View File

@ -298,5 +298,193 @@ class OrderHook
// 新活动订单售后退款发起
public function activityOrderAuthFailAfter(&$params)
{
['order' => $order,"user_id"=>$user_id,"oper_type"=>$oper_type,"oper_id"=>$oper_id] = $params;
$order = Order::where("id" , $order["id"])->find();
$detail = $order->detail;
$user = $order->user;
//记录订单日志
$mini_type = "activity_order";
$to_id = $order["user_id"];
$status ="activity";
$params=[
"event"=>"activity_order_auth_fail_after",
"order_id"=>$order["id"],
"order_no"=>$order["order_no"],
"activity_id"=>$order["activity_id"],
];
$param = [
"title"=>$detail["title"],
"order_no" => $order["order_no"],
"nickname" => $user["nickname"],
"realname" => $user["realname"],
"mobile" => $user["mobile"],
"price" => $order["totalprice"],
"payprice" => $order["payprice"],
"real_refundprice" => $order["real_refundprice"],
"sub_refundprice" => $order["sub_refundprice"],
"reason" => $order["reason"],
"address"=>$detail["address"]."(".$detail["address_detail"].")",
"start_time" => date("Y-m-d H:i",$detail["start_time"]), //格式化日期格式 $order["start_time"], //格式化日期格式
"end_time" => date("Y-m-d H:i",$detail["end_time"]),
"sign_start_time" => date("Y-m-d H:i",$detail["sign_start_time"]), //格式化日期格式 $order["start_time"], //格式化日期格式
"sign_end_time" => date("Y-m-d H:i",$detail["sign_end_time"]),
];
//发用户
(new MessageConfig)
->setTemplate($params["event"]."_user")
->setTemplateData($param)
->setToUid($to_id)
->setMessageStatus($status)
->setMessageMiniType($mini_type)
->setMessageParams($params)
->sendMessage();
//发所有核销员
(new MessageConfig)
->setTemplate($params["event"]."_verification")
->setTemplateData($param)
->setToUid($detail["user_id"])
->setMessageStatus($status)
->setMessageMiniType($mini_type)
->setMessageParams($params)
->sendMessage();
}
// 订单退款成功
public function activityOrderRefundSuccessAfter(&$params)
{
["order"=>$order] = $params;
$order = Order::where("id" , $order["id"])->find();
$detail = $order->detail;
$user = $order->user;
//记录订单日志
$mini_type = "activity_order";
$to_id = $order["user_id"];
$status ="activity";
$params=[
"event"=>"activity_order_refund_success_after",
"order_id"=>$order["id"],
"order_no"=>$order["order_no"],
"activity_id"=>$order["activity_id"],
];
$param = [
"title"=>$detail["title"],
"order_no" => $order["order_no"],
"nickname" => $user["nickname"],
"realname" => $user["realname"],
"mobile" => $user["mobile"],
"price" => $order["totalprice"],
"payprice" => $order["payprice"],
"real_refundprice" => $order["real_refundprice"],
"sub_refundprice" => $order["sub_refundprice"],
"reason" => $order["reason"],
"address"=>$detail["address"]."(".$detail["address_detail"].")",
"start_time" => date("Y-m-d H:i",$detail["start_time"]), //格式化日期格式 $order["start_time"], //格式化日期格式
"end_time" => date("Y-m-d H:i",$detail["end_time"]),
"sign_start_time" => date("Y-m-d H:i",$detail["sign_start_time"]), //格式化日期格式 $order["start_time"], //格式化日期格式
"sign_end_time" => date("Y-m-d H:i",$detail["sign_end_time"]),
];
(new MessageConfig)
->setTemplate($params["event"])
->setTemplateData($param)
->setToUid($to_id)
->setMessageStatus($status)
->setMessageMiniType($mini_type)
->setMessageParams($params)
->sendMessage();
}
// 订单支付成功后
public function activityOrderRefundFailAfter(&$params)
{
["order"=>$order] = $params;
$order = Order::where("id" , $order["id"])->find();
$detail = $order->detail;
$user = $order->user;
//记录订单日志
$mini_type = "activity_order";
$to_id = $order["user_id"];
$status ="activity";
$params=[
"event"=>"activity_order_refund_fail_after",
"order_id"=>$order["id"],
"order_no"=>$order["order_no"],
"activity_id"=>$order["activity_id"],
];
$param = [
"title"=>$detail["title"],
"order_no" => $order["order_no"],
"nickname" => $user["nickname"],
"realname" => $user["realname"],
"mobile" => $user["mobile"],
"price" => $order["totalprice"],
"payprice" => $order["payprice"],
"real_refundprice" => $order["real_refundprice"],
"sub_refundprice" => $order["sub_refundprice"],
"reason" => $order["reason"],
"address"=>$detail["address"]."(".$detail["address_detail"].")",
"start_time" => date("Y-m-d H:i",$detail["start_time"]), //格式化日期格式 $order["start_time"], //格式化日期格式
"end_time" => date("Y-m-d H:i",$detail["end_time"]),
"sign_start_time" => date("Y-m-d H:i",$detail["sign_start_time"]), //格式化日期格式 $order["start_time"], //格式化日期格式
"sign_end_time" => date("Y-m-d H:i",$detail["sign_end_time"]),
];
//发用户
(new MessageConfig)
->setTemplate($params["event"]."_user")
->setTemplateData($param)
->setToUid($to_id)
->setMessageStatus($status)
->setMessageMiniType($mini_type)
->setMessageParams($params)
->sendMessage();
//发所有核销员
(new MessageConfig)
->setTemplate($params["event"]."_verification")
->setTemplateData($param)
->setToUid($detail["user_id"])
->setMessageStatus($status)
->setMessageMiniType($mini_type)
->setMessageParams($params)
->sendMessage();
}
}

View File

@ -493,6 +493,12 @@ class Activity extends BaseModel
if(!$refund){
throw new \Exception("退款策略不存在");
}
$paid_activity_min_price = config("site.paid_activity_min_price");
if($params["price"]<$paid_activity_min_price){
throw new \Exception("活动售价不能低于".$paid_activity_min_price."");
}
}
if($params["price"]<0)$params["price"]=0;
@ -724,6 +730,9 @@ class Activity extends BaseModel
if (isset($my) && $my && isset($my_user_id) && $my_user_id) {
$selfetch = $selfetch->where("{$a}user_id", 'in', ''.$my_user_id);
}
if($my ==1 && empty($my_user_id)){
$selfetch = $selfetch->where("{$a}user_id", 'in', [-3]);
}
@ -1212,7 +1221,7 @@ class Activity extends BaseModel
if($trans){
self::rollbackTrans();
}
throw new \Exception($e->getMessage().$e->getFile().$e->getLine());
throw new \Exception($e->getMessage());
}
return $row;
}
@ -1298,7 +1307,7 @@ class Activity extends BaseModel
$row = self::where("id",$id)->where("status","in",["1","2","3","4"])->find();
if(!$row) throw new \Exception("活动取消或已结束");
if(!$row) throw new \Exception("活动取消或已结束");
if($check) {
if($oper_type=='user' && $row["user_id"] != $oper_id) throw new \Exception("您无权取消该活动");
@ -1322,7 +1331,7 @@ class Activity extends BaseModel
\think\Hook::listen('new_activity_cancel_success_after', $data);
//自动退款检测:如果有订单,则自动取消
//...待实现
$this->orderAllCancel($id);
if($trans){
self::commitTrans();
@ -1337,6 +1346,47 @@ class Activity extends BaseModel
}
public function orderAllCancel($activity_id,$trans=false){
//判断逻辑
if($trans){
self::beginTrans();
}
$res = true;
try{
//查询待支付订单,执行订单取消
$orders = Order::where("activity_id",$activity_id)->where("status","in",['0'])->select();
foreach ($orders as $order){
(new Order)->freeCancel($order['id'],0,false,'admin',0);
}
//查询所有免费订单,执行取消
$orders = Order::where("activity_id",$activity_id)->where("payprice",0)->where("status","in",['0','2','3',"9"])->select();
foreach ($orders as $order){
(new Order)->freeCancel($order['id'],0,false,'admin',0);
}
//查询所有付费订单,执行退款取消
$orders = Order::where("activity_id",$activity_id)->where("payprice",">",0)->where("status","in",["2","3","9"])->select();
foreach ($orders as $order){
(new Order)->paidCancel($order['id'],0,false,'admin',0,true);
}
//处于售后中的直接售后同意按全款退?
if($trans){
self::commitTrans();
}
}catch (\Exception $e){
if($trans){
self::rollbackTrans();
}
throw new \Exception($e->getMessage().$e->getFile().$e->getLine());
}
return $res;
}
public function getActivityAuthIds($user_id,$vaild=false)
{

View File

@ -13,6 +13,8 @@ use think\Cache;
use think\Model;
use traits\model\SoftDelete;
use Yansongda\Pay\Pay;
class Order extends BaseModel
{
@ -622,7 +624,9 @@ class Order extends BaseModel
$order_data["settle_log_time"] = $activity_info["settlement_time"];
//根据手续费比例$activity_info["fee_scale"] 计算手续费
$order_data["fee_price"] = bcmul($order_data["totalprice"],$order_data["fee_scale"],2);
//手续费保底不能低于site.activity_settle_min_fee
$min_fee = config("site.activity_settle_min_fee");
$order_data["fee_price"] = $order_data["fee_price"] < $min_fee ? $min_fee : $order_data["fee_price"];
return compact('order_data','activity_info','user_data',"num");
}
@ -709,7 +713,7 @@ class Order extends BaseModel
self::paySetData($order,$notify);
//释放结算订单
(new SettleLog)->generatorLog($order['id']);
// (new SettleLog)->generatorLog($order['id']);
// //如果需要快捷预约
// $classes_lib_spec_id = $order['classes_lib_spec_id'];
@ -1211,7 +1215,7 @@ class Order extends BaseModel
//释放结算订单
(new SettleLog)->generatorLog($order['id']);
// (new SettleLog)->generatorLog($order['id']);
//调用支付成功事件
$data = ['order' => $order];
@ -1232,7 +1236,7 @@ class Order extends BaseModel
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
*/
public static function getHaveCancelFreeOrder($order_no){
public static function getHaveCancelFreeOrder($order_no,$check=false){
// $where = [self::STATUS_NOPAY,self::STATUS_PAYED];
$order = self::where('order_no|id|pay_no',$order_no)->find();
$detail = $order->detail;
@ -1245,11 +1249,12 @@ class Order extends BaseModel
throw new \Exception("只有未支付的单才可以取消!");
}
}else{
if(!in_array($order['status'],['0','2'])){
if($check && !in_array($order['status'],['0','2'])){
throw new \Exception("免费单只有进行中订单可取消,已取消请忽重复取消!");
}
}
if(!$check) return $order;
//活动开始后不可取消
$time = time();
if ($detail['start_time'] < $time) throw new \Exception("活动开始后不可取消!");
@ -1265,8 +1270,8 @@ class Order extends BaseModel
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
*/
public static function updateFreeCancel($order){
if(is_string($order))$order = self::getHaveCancelFreeOrder($order);
public static function updateFreeCancel($order,$check=false){
if(is_string($order))$order = self::getHaveCancelFreeOrder($order,$check);
$order->status = "-3";//refund_status
$order->canceltime = time();
$order->save();
@ -1283,7 +1288,7 @@ class Order extends BaseModel
*/
public function freeCancel($order_no,$user_id=0,$check=false,$oper_type='user',$oper_id=0,$trans=false){
//得到可取消订单
$order = self::getHaveCancelFreeOrder($order_no);
$order = self::getHaveCancelFreeOrder($order_no,$check);
if($check){
//用户操作权限检测
self::checkOptionAuth($order['id'],$user_id ?: $oper_id,$oper_type);
@ -1300,7 +1305,7 @@ class Order extends BaseModel
//事务逻辑
//更新订单取消状态
$order = self::updateFreeCancel($order);
$order = self::updateFreeCancel($order,$check);
//插入订单取消日志
if(!$user_id ||$order["user_id"] !=$user_id ){
OrderLog::log($order['id'],"[系统操作]活动订单取消成功",$oper_type ?: 'user', $oper_id ?: $order['user_id']);
@ -1354,7 +1359,7 @@ class Order extends BaseModel
foreach ($list as $order)
{
//取消订单
self::freeCancel($order['id'],0,false,'admin',0);
(new self)->freeCancel($order['id'],0,false,'admin',0);
$count++;
}
if ($trans) {
@ -1382,7 +1387,7 @@ class Order extends BaseModel
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
*/
public static function getHaveCancelPaidOrder($order_no){
public static function getHaveCancelPaidOrder($order_no,$check=true){
// $where = [self::STATUS_NOPAY,self::STATUS_PAYED];
$order = self::where('order_no|id|pay_no',$order_no)->find();
$detail = $order->detail;
@ -1391,12 +1396,14 @@ class Order extends BaseModel
//非免费单进行中无法取消
if($order['totalprice'] != 0){
//非免费单只有未退款可退
if(!in_array($order['status'],["2","3"])){
if(!in_array($order['status'],["2","3","9"])){
throw new \Exception("只有已报名和核销中的单才可以取消!");
}
}else{
throw new \Exception("免费单请走免费订单取消接口!");
}
if($check){
//根据不同退款策略判断当前时间点是否可取消并退款
$refund_status = $detail["refund_status"];
//活动开始后不可取消,只能走售后
@ -1414,30 +1421,78 @@ class Order extends BaseModel
throw new \Exception("不支持的退款策略,请走售后流程!");
}
}
return $order;
}
/**更新免费订单取消状态以及未支付取消
/**更新付费订单取消状态
* @param $order
* @return array|false|\PDOStatement|string|Model
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
*/
public static function updatePaidCancel($order){
if(is_string($order))$order = self::getHaveCancelFreeOrder($order);
public static function updatePaidCancel($order,$check=true){
if(is_string($order))$order = self::getHaveCancelPaidOrder($order,$check);
$order->status = "-3";//refund_status
$order->canceltime = time();
$order->save();
//将所有资金结算记录作废
$data = [
"status" =>"-1",
"canceltime" => time(),
];
SettleLog::where("activity_order_id",$order["id"])->where("status","in",['1','2'])->update($data);
return $order;
}
/**得到可审核订单
* @param $order_no
* @return array|false|\PDOStatement|string|Model
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
*/
public static function getHaveExamineOrder($order_no){
// $where = [self::STATUS_NOPAY,self::STATUS_PAYED];
$order = self::where('order_no|id|pay_no',$order_no)->where("status","in",["-3",'4'])->find();
if(!$order)throw new \Exception("不是待审核的订单!");
return $order;
}
/**更新订单结算中状态
* @param $order
* @return array|false|\PDOStatement|string|Model
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
*/
public static function updateExamineFailSettlement($order,$reason="",$oper_id = 0,$oper_type='user'){
if(is_string($order))$order = self::getHaveExamineOrder($order);
$order->before_status = $order->status;
$order->status = "5";
$order->auth_status = "1";
$order->server_status = "6";
$order->auth_time = time();
$order->refundsendtime = time();
$order->reason = $reason;
$order->auth_user_id = $oper_id;
$order->auth_type = $oper_type;
$order->save();
return $order;
}
/**免费订单取消以及未支付取消
/**付费订单取消
* @param $order_no
* @param int $user_id
* @param bool $check
@ -1445,9 +1500,9 @@ class Order extends BaseModel
* @return bool
* @throws \Exception
*/
public function paidCancel($order_no,$user_id=0,$check=false,$oper_type='user',$oper_id=0,$trans=false){
public function paidCancel($order_no,$user_id=0,$check=false,$oper_type='user',$oper_id=0,$activity_cancel=false,$trans=false){
//得到可取消订单
$order = self::getHaveCancelFreeOrder($order_no);
$order = self::getHaveCancelPaidOrder($order_no,$check);
if($check){
//用户操作权限检测
self::checkOptionAuth($order['id'],$user_id ?: $oper_id,$oper_type);
@ -1464,12 +1519,12 @@ class Order extends BaseModel
//事务逻辑
//更新订单取消状态
$order = self::updateFreeCancel($order);
$order = self::updatePaidCancel($order,$check);
//插入订单取消日志
if(!$user_id ||$order["user_id"] !=$user_id ){
OrderLog::log($order['id'],"[系统操作]活动订单取消成功",$oper_type ?: 'user', $oper_id ?: $order['user_id']);
OrderLog::log($order['id'],"[系统操作]活动订单取消成功,款项在一个工作日内将自动退还",$oper_type ?: 'user', $oper_id ?: $order['user_id']);
}else{
OrderLog::log($order['id'],"活动订单取消成功",$oper_type ?: 'user', $oper_id ?: $order['user_id']);
OrderLog::log($order['id'],"活动订单取消成功,款项在一个工作日内将自动退还",$oper_type ?: 'user', $oper_id ?: $order['user_id']);
}
//调用订单取消事件
@ -1478,6 +1533,23 @@ class Order extends BaseModel
//执行课时数更新
$res1 = self::statisticsAndUpdateClassesNumber($order['id']);
//执行退款
//调用退款发起
//更新订单状态
$reason = "活动取消自动退款";
$order = self::updateExamineFailSettlement($order['order_no'],"",$user_id ?: $oper_id,$oper_type);
//审核失败逻辑
OrderLog::log($order['id'],"活动取消退款,原因;{$reason},如有退款额度,该活动单将自动退款以便重新下单(没有请忽略)",$oper_type ?: 'user', $oper_id ?: $order['user_id']);
//调用订单事件
$data = ['order' => self::where("id",$order['id'])->find(),"user_id"=>$user_id,"oper_type"=>$oper_type,"oper_id"=>$oper_id];
\think\Hook::listen('activity_order_auth_fail_after', $data);
//计算自动退款额度
$refundprice = $this->countRefundAmount($order["id"],$activity_cancel);
if($refundprice>0){
//执行退款
self::orderRefund($order['order_no'],$refundprice,$oper_type,$oper_id);
}
if($trans){
self::commitTrans();
@ -1492,6 +1564,382 @@ class Order extends BaseModel
}
/** 如果是自动退款,根据退款策略计算应退款额
* @param $order_id
* @param $trans
* @return true
* @throws \Exception
*/
public function countRefundAmount($order_id,$activity_cancel=false,$trans=false){
$price = 0;
$order = self::where('id',$order_id)->find();
if(!$order) return $price;
$detail = $order->detail;
if(!$detail) return $price;
//
//判断逻辑
if($trans){
self::beginTrans();
}
$res = true;
try{
//根据不同退款策略判断当前时间点是否可取消并退款
$refund_status = $detail["refund_status"];
//活动开始后不可取消,只能走售后
$time = time();
if($activity_cancel){
//活动取消退全款
$price = $order["sub_refundprice"];
}else{
switch ($refund_status){
case "1": //不退款
throw new \Exception("当前活动不支持取消退款,请走售后流程!");
break;
case "3": //开始前退
case "5": //随时退 退全款
$price = $order["sub_refundprice"];
break;
default:
throw new \Exception("不支持的退款策略,请走售后流程!");
}
}
if($trans){
self::commitTrans();
}
}catch (\Exception $e){
if($trans){
self::rollbackTrans();
}
throw new \Exception($e->getMessage().$e->getFile().$e->getLine());
}
return $price;
}
/**得到可直接退款订单(正常取消退全款非售后)
* @param $order_no
* @return array|false|\PDOStatement|string|Model
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
*/
public static function getHaveRefundOrder($refund_sn){
// $where = [self::STATUS_NOPAY,self::STATUS_PAYED];
$order = self::where('refund_no|order_no|id',$refund_sn)->where('status','5')->where("sub_refundprice",">",0)->find();
if(!$order)throw new \Exception("不是可直接退款的订单!");
return $order;
}
/**预约订单退款(退全款)
* @param $order
* @param $refund_money
* @param $trans
*/
public static function orderRefund($order,$refund_money,$oper_type='user',$oper_id=0,$trans=false,$admin=false){
if(is_numeric($order))$order = self::getHaveRefundOrder($order);
if(!$order)throw new \Exception("找不到订单");
//判断逻辑
if($trans){
self::beginTrans();
}
$res = true;
try{
//生成退款单号
if(!$order['refund_no']){
$order['refund_no'] = get_order_sn();
// $order->save();
}
if(!$refund_money)$refund_money = $order['sub_refundprice'];
if($refund_money<=0)$refund_money = 0;
if(!$refund_money)throw new \Exception("退款金额异常!");
//保存应退款额
$order->total_refundprice = $refund_money;
$order->save();
//事务逻辑
switch ($order['pay_type']) {
case "wechat": //微信退款
self::wechatRefund($order,$refund_money,$oper_type,$oper_id);
break;
// case "alipay": //支付宝退款
// self::alipayRefund($order,$refund_money,$oper_type,$oper_id);
// break;
// case "wallet": //钱包支付退款
// self::walletRefund($order,$refund_money,$oper_type,$oper_id);
// break;
// case "offline": //线下支付退款
//
// break;
default:
throw new \Exception("订单币种异常!");
}
if($trans){
self::commitTrans();
}
}catch (\Exception $e){
if($trans){
self::rollbackTrans();
}
throw new \Exception($e->getMessage());
}
return $res;
}
/**微信退款
* @param $order
* @param $refund_money
*/
public static function wechatRefund($order,$refund_money,$refund_desc="",$oper_type='user',$oper_id=0,$trans=false){
//判断逻辑
if($trans){
self::beginTrans();
}
$res = true;
try{
//事务逻辑
//创建订单退款订单
$classesorder = $order;
$detail = $order->detail;
//执行微信退款sdk
$order_data = [
'out_trade_no' => $classesorder->order_no
];
$total_fee = $classesorder->payprice * 100;
$refund_fee = $refund_money * 100;
$order_data = array_merge($order_data, [
'out_refund_no' => $order->refund_no,
'total_fee' => $total_fee,
'refund_fee' => $refund_fee,
'refund_desc' => $refund_desc ?: "活动[{$detail["title"]}]订单[ID:{$classesorder['id']}]退款{$refund_money}已到账户",
]);
$config = Service::getConfig('wechat',[],$classesorder->platform);
$notify_url = request()->domain() . '/api/school.newactivity.pay/notifyr/payment/' . $classesorder->pay_type . '/platform/' . $classesorder->platform;
$config['notify_url'] = $notify_url;
$pay = Pay::wechat($config);
// throw new \Exception($trans."111测试错误".$order["status"]);
$result = $pay->refund($order_data);
\think\Log::write('refund-result' . json_encode($result));
if ($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS') {
$res = true;
} else {
throw new \Exception($result['return_msg']);
}
if($trans){
self::commitTrans();
}
}catch (\Exception $e){
if($trans){
self::rollbackTrans();
}
throw new \Exception($e->getMessage().$e->getFile().$e->getLine());
}
return $res;
}
/**同意并发放退款金额
* @param $order 退款单号
* @return array|false|\PDOStatement|string|Model
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
*/
public static function updateRefundOrder($order,$price=0,$pay_json=[]){
if(is_string($order))$order = self::getHaveRefundOrder($order);
// //如果$price为0尝试从$pay_json退款回调中获取退款金额
// if(!$price && $pay_json){
// if(isset($pay_json['refund_fee']))$price = bcdiv($pay_json['refund_fee'],100,2);
// }
// $order->before_status = $order->status;//refund_status
$order->status = "6";
$order->server_status = "6";
$order->real_refundprice = bcadd($order->real_refundprice ?:'0', $price?:$order->total_refundprice ,2);
$order->sub_refundprice = bcsub($order->sub_refundprice ?:'0', $price?:$order->total_refundprice ,2);
$order->refundtime = time();
$order->refund_json = json_encode($pay_json);
$order->save();
return $order;
}
/**退款成功处理逻辑(需修改)
* @param $refund_sn
* @param bool $trans
* @return bool
* @throws \Exception
*/
public static function refundSuccess($refund_sn,$refund_json=[],$price=0,$trans=false){
//得到机构售后提交确认订单
$order = self::getHaveRefundOrder($refund_sn);
//判断逻辑
if($trans){
self::beginTrans();
}
$res = true;
try{
//事务逻辑
//更新订单状态为同意
$order = self::updateRefundOrder($refund_sn,$price,$refund_json);
//插入订单日志
OrderLog::log($order['id'],"活动订单退款已原路退回", 'admin', 0);
//执行课时数更新
$res1 = self::statisticsAndUpdateClassesNumber($order['id']);
//调用订单取消事件
$data = ['order' => self::where("id",$order['id'])->find(),"user_id"=>$order['user_id'],"oper_type"=>'admin',"oper_id"=>0];
\think\Hook::listen('activity_order_refund_success_after', $data);
if($trans){
self::commitTrans();
}
}catch (\Exception $e){
if($trans){
self::rollbackTrans();
}
throw new \Exception($e->getMessage());
}
return $res;
}
/**订单退款失败记录
* @param $order
* @param $refund_money
* @param $trans
*/
public static function orderRefundFail($order,$msg,$oper_type='user',$oper_id=0,$trans=false,$admin=false){
if(is_numeric($order))$order = self::getOrder($order);
if(!$order)throw new \Exception("找不到订单");
//判断逻辑
if($trans){
self::beginTrans();
}
$res = true;
try{
//事务逻辑
$order->refund_error = $msg;
$order->save();
//插入订单日志
OrderLog::log($order['id'],"活动订单退款失败:".$msg, $oper_type, $oper_id);
//调用订单取消事件
$data = ['order' => self::where("id",$order['id'])->find(),"user_id"=>$order['user_id'],"oper_type"=>$oper_type,"oper_id"=>$oper_id];
\think\Hook::listen('activity_order_refund_fail_after', $data);
if($trans){
self::commitTrans();
}
}catch (\Throwable $e){
if($trans){
self::rollbackTrans();
}
throw new \Exception($e->getMessage());
}
return $res;
}
/**得到可直接退款订单(正常取消退全款非售后)
* @param $order_no
* @return array|false|\PDOStatement|string|Model
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
*/
public static function getHaveAfterSalesOrder($refund_sn){
// $where = [self::STATUS_NOPAY,self::STATUS_PAYED];
$order = self::where('refund_no|order_no|id',$refund_sn)->where('status','5')->where("sub_refundprice",">",0)->find();
if(!$order)throw new \Exception("不是可直接退款的订单!");
return $order;
}
/** 发起售后
* @param $order_no
* @param $trans
* @return true
* @throws \Exception
*/
public function afterSales($order,$trans=false){
if(is_numeric($order))$order = self::getHaveAfterSalesOrder($order);
if(!$order)throw new \Exception("找不到订单");
//
//判断逻辑
if($trans){
self::beginTrans();
}
$res = true;
try{
if($trans){
self::commitTrans();
}
}catch (\Exception $e){
if($trans){
self::rollbackTrans();
}
throw new \Exception($e->getMessage().$e->getFile().$e->getLine());
}
return $res;
}

View File

@ -3,6 +3,7 @@
namespace app\common\model\school\activity\order;
use app\common\model\BaseModel;
use app\common\model\school\activity\Activity;
use app\common\model\user\withdrawal\UserwithdrawalLog;
use think\Model;
use traits\model\SoftDelete;
@ -109,6 +110,93 @@ class SettleLog extends BaseModel
}
/**
* 检测订单结算
*/
public static function timeoutSettleActivityCheck($activity_id,$trans = false){
$count = 0;
if ($trans) {
self::beginTrans();
}
try {
$orders = Order::where("payprice",">",0) //支付单
->where("pay_type",'wechat') //微信支付
->where("sub_refundprice",">",0) //剩余未退大于0
->where("activity_id",$activity_id) //当前活动
->where("status","in",["-3","9","6"])
->select();
foreach ($orders as $order){
//剩余金额大于手续费的订单,并且未插入此结算单的
$sub_refundprice = bcsub($order["sub_refundprice"],$order["fee_price"]);
if($sub_refundprice > 0){
$log = self::where("activity_order_id",$order["id"])->where("status","not in",["-1"])->find();
if(!$log){
(new self)->generatorLog($order["id"]);
}
}
}
if ($trans) {
self::commitTrans();
}
} catch (\Exception $e) {
if ($trans) {
self::rollbackTrans();
}
throw new \Exception($e->getMessage());
}
return $count;
}
/**
* 检测订单结算
*/
public function timeoutSettleCheck($trans = false){
$count = 0;
if ($trans) {
self::beginTrans();
}
try {
//查询更新所有活动状态
Activity::timeoutCheck();
//延后时间(秒)
$delay = config("site.activity_end_settle") ?: 0;
$time = time();
//查询处于可结算状态的订单(活动结束时间戳 延后时间 =超时可以结算的活动)
$time = $time - $delay;
$activityList = Activity::where("status",'5')->where("end_time",'<',$time)->select();
foreach ($activityList as $activity){
//对活动订单进行结算记录插入
$this->timeoutSettleActivityCheck($activity["id"]);
}
//检测更新所有结算订状态
SettleLog::timeoutCheck(null,null);
if ($trans) {
self::commitTrans();
}
} catch (\Exception $e) {
if ($trans) {
self::rollbackTrans();
}
throw new \Exception($e->getMessage());
}
return $count;
}
/** 生成结算记录
* @param $order_id
* @param $trans