1067 lines
35 KiB
PHP
1067 lines
35 KiB
PHP
<?php
|
|
|
|
namespace app\common\model\school\classes\order;
|
|
|
|
|
|
use app\admin\model\Admin;
|
|
use app\common\model\BaseModel;
|
|
use app\common\model\dyqc\ManystoreShop;
|
|
use app\common\model\school\classes\ClassesLib;
|
|
use app\common\model\User;
|
|
use bw\Common;
|
|
use fast\Random;
|
|
use think\Cache;
|
|
use think\Model;
|
|
use traits\model\SoftDelete;
|
|
|
|
class Order extends BaseModel
|
|
{
|
|
|
|
use SoftDelete;
|
|
|
|
|
|
|
|
// 表名
|
|
protected $name = 'school_classes_order';
|
|
|
|
// 自动写入时间戳字段
|
|
protected $autoWriteTimestamp = 'integer';
|
|
|
|
// 定义时间戳字段名
|
|
protected $createTime = 'createtime';
|
|
protected $updateTime = 'updatetime';
|
|
protected $deleteTime = 'deletetime';
|
|
|
|
// 追加属性
|
|
protected $append = [
|
|
'pay_type_text',
|
|
'status_text',
|
|
'before_status_text',
|
|
'server_status_text',
|
|
'result_status_text',
|
|
'canceltime_text',
|
|
'paytime_text',
|
|
'finishtime_text',
|
|
'refundtime_text'
|
|
];
|
|
|
|
|
|
|
|
public function getPayTypeList()
|
|
{
|
|
return ['yue' => __('Pay_type yue'), 'wechat' => __('Pay_type wechat')];
|
|
}
|
|
|
|
public function getStatusList()
|
|
{
|
|
return ['-3' => __('Status -3'), '0' => __('Status 0'), '3' => __('Status 3'), '6' => __('Status 6'), '9' => __('Status 9')];
|
|
}
|
|
|
|
public function getBeforeStatusList()
|
|
{
|
|
return ['-3' => __('Before_status -3'), '0' => __('Before_status 0'), '3' => __('Before_status 3'), '6' => __('Before_status 6'), '9' => __('Before_status 9')];
|
|
}
|
|
|
|
public function getServerStatusList()
|
|
{
|
|
return ['0' => __('Server_status 0'), '3' => __('Server_status 3'), '6' => __('Server_status 6')];
|
|
}
|
|
|
|
public function getResultStatusList()
|
|
{
|
|
return ['0' => __('Result_status 0'), '3' => __('Result_status 3'), '6' => __('Result_status 6')];
|
|
}
|
|
|
|
|
|
public function getPayTypeTextAttr($value, $data)
|
|
{
|
|
$value = $value ? $value : (isset($data['pay_type']) ? $data['pay_type'] : '');
|
|
$list = $this->getPayTypeList();
|
|
return isset($list[$value]) ? $list[$value] : '';
|
|
}
|
|
|
|
|
|
public function getStatusTextAttr($value, $data)
|
|
{
|
|
$value = $value ? $value : (isset($data['status']) ? $data['status'] : '');
|
|
$list = $this->getStatusList();
|
|
return isset($list[$value]) ? $list[$value] : '';
|
|
}
|
|
|
|
|
|
public function getBeforeStatusTextAttr($value, $data)
|
|
{
|
|
$value = $value ? $value : (isset($data['before_status']) ? $data['before_status'] : '');
|
|
$list = $this->getBeforeStatusList();
|
|
return isset($list[$value]) ? $list[$value] : '';
|
|
}
|
|
|
|
|
|
public function getServerStatusTextAttr($value, $data)
|
|
{
|
|
$value = $value ? $value : (isset($data['server_status']) ? $data['server_status'] : '');
|
|
$list = $this->getServerStatusList();
|
|
return isset($list[$value]) ? $list[$value] : '';
|
|
}
|
|
|
|
|
|
public function getResultStatusTextAttr($value, $data)
|
|
{
|
|
$value = $value ? $value : (isset($data['result_status']) ? $data['result_status'] : '');
|
|
$list = $this->getResultStatusList();
|
|
return isset($list[$value]) ? $list[$value] : '';
|
|
}
|
|
|
|
|
|
public function getCanceltimeTextAttr($value, $data)
|
|
{
|
|
$value = $value ? $value : (isset($data['canceltime']) ? $data['canceltime'] : '');
|
|
return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value;
|
|
}
|
|
|
|
|
|
public function getPaytimeTextAttr($value, $data)
|
|
{
|
|
$value = $value ? $value : (isset($data['paytime']) ? $data['paytime'] : '');
|
|
return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value;
|
|
}
|
|
|
|
|
|
public function getFinishtimeTextAttr($value, $data)
|
|
{
|
|
$value = $value ? $value : (isset($data['finishtime']) ? $data['finishtime'] : '');
|
|
return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value;
|
|
}
|
|
|
|
|
|
public function getRefundtimeTextAttr($value, $data)
|
|
{
|
|
$value = $value ? $value : (isset($data['refundtime']) ? $data['refundtime'] : '');
|
|
return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value;
|
|
}
|
|
|
|
protected function setCanceltimeAttr($value)
|
|
{
|
|
return $value === '' ? null : ($value && !is_numeric($value) ? strtotime($value) : $value);
|
|
}
|
|
|
|
protected function setPaytimeAttr($value)
|
|
{
|
|
return $value === '' ? null : ($value && !is_numeric($value) ? strtotime($value) : $value);
|
|
}
|
|
|
|
protected function setFinishtimeAttr($value)
|
|
{
|
|
return $value === '' ? null : ($value && !is_numeric($value) ? strtotime($value) : $value);
|
|
}
|
|
|
|
protected function setRefundtimeAttr($value)
|
|
{
|
|
return $value === '' ? null : ($value && !is_numeric($value) ? strtotime($value) : $value);
|
|
}
|
|
|
|
|
|
public function manystore()
|
|
{
|
|
return $this->belongsTo('app\admin\model\Manystore', 'manystore_id', 'id', [], 'LEFT')->setEagerlyType(0);
|
|
}
|
|
|
|
|
|
public function user()
|
|
{
|
|
return $this->belongsTo('app\common\model\User', 'user_id', 'id', [], 'LEFT')->setEagerlyType(0);
|
|
}
|
|
|
|
|
|
public function shop()
|
|
{
|
|
return $this->belongsTo(ManystoreShop::class, 'shop_id', 'id', [], 'LEFT')->setEagerlyType(0);
|
|
}
|
|
|
|
|
|
public function lib()
|
|
{
|
|
return $this->belongsTo(ClassesLib::class, 'classes_lib_id', 'id', [], 'LEFT')->setEagerlyType(0);
|
|
}
|
|
|
|
|
|
public function detail()
|
|
{
|
|
return $this->belongsTo(OrderDetail::class, 'classes_order_detail_id', 'id', [], 'LEFT')->setEagerlyType(0);
|
|
}
|
|
|
|
|
|
public function admin()
|
|
{
|
|
return $this->belongsTo('app\admin\model\Admin', 'admin_id', 'id', [], 'LEFT')->setEagerlyType(0);
|
|
}
|
|
|
|
public function getCodeimageAttr($value, $data)
|
|
{
|
|
if (!empty($value)) return cdnurl($value, true);
|
|
}
|
|
|
|
public function getCodeoneimageAttr($value, $data)
|
|
{
|
|
if (!empty($value)) return cdnurl($value, true);
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* 设置订单缓存
|
|
* @param $uid
|
|
* @param $data
|
|
* @return bool
|
|
*/
|
|
public static function setOrderCache($uid, $data)
|
|
{
|
|
//缓存名 = uid + order_no
|
|
$cacheNmae = 'classes_order_cache' . $uid . $data['order_no'];
|
|
// 缓存在3600秒之后过期
|
|
return Cache::set($cacheNmae, $data, config("site.unpaid_order_expire_time"));
|
|
}
|
|
|
|
/**
|
|
* 得到订单缓存
|
|
* @param $uid
|
|
* @param $order_no
|
|
* @return mixed
|
|
*/
|
|
public static function getOrderCache($uid, $order_no)
|
|
{
|
|
//缓存名 = uid + order_no
|
|
$cacheNmae = 'classes_order_cache' . $uid . $order_no;
|
|
// 缓存在3600秒之后过期
|
|
return Cache::get($cacheNmae);
|
|
}
|
|
|
|
|
|
/**
|
|
* 删除订单缓存
|
|
* @param $uid
|
|
* @param $order_no
|
|
* @return mixed
|
|
*/
|
|
public static function deleteOrderCache($uid, $order_no)
|
|
{
|
|
//缓存名 = uid + order_no
|
|
$cacheNmae = 'classes_order_cache' . $uid . $order_no;
|
|
// 缓存在3600秒之后过期
|
|
return Cache::rm($cacheNmae);
|
|
}
|
|
|
|
|
|
/**展示订单信息
|
|
* @param $order_no
|
|
* @param $price_info
|
|
* @return array
|
|
*/
|
|
public static function showInfo($order_no, $price_info = []){
|
|
$data = [];
|
|
$data['order_no'] =$order_no;
|
|
$data['order_info'] = self::getDetail($order_no);
|
|
return array_merge($data,$price_info);
|
|
}
|
|
|
|
|
|
|
|
|
|
/**得到订单详情
|
|
* @param $order_no
|
|
*/
|
|
public static function getDetail($order_no,$classes_lib_id = []){
|
|
$model = self::where('order_no|id|pay_no|code',$order_no);
|
|
if($classes_lib_id)$model = $model->where("classes_lib_id","in",$classes_lib_id);
|
|
$data = $model->find();
|
|
|
|
if(!$data) return $data;
|
|
//加载订单详情
|
|
$data->detail->teacher;
|
|
//订单用户
|
|
// $data->user;
|
|
$data->user->visible(['id','nickname','mobile','avatar','realname']);
|
|
//订单机构
|
|
$data->shop;
|
|
// //得到二维码
|
|
// $data->code_url = Common::getQrcode([
|
|
// 'text' => $data['code'],
|
|
// 'size' => 200,
|
|
// ]);
|
|
// $data->one_code_url = Common::getBarcode([
|
|
// 'text' => $data['code'],
|
|
// 'size' => 200,
|
|
// ]);
|
|
|
|
return $data;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** 订单确认(订单计算)
|
|
* @param $user_id 下单用户
|
|
* @param $order_no 订单号(缓存标识)
|
|
* @param $classes_lib_id 課程id
|
|
* @param $param 額外参数(扩展用)
|
|
* @param bool $is_compute 是否重新计算订单
|
|
* @return array
|
|
*/
|
|
public function confirm($user_id, $classes_lib_id,$order_no,$param=[], $is_compute = false)
|
|
{
|
|
if ($order_no && !$is_compute) {
|
|
//得到缓存
|
|
$data = self::getOrderCache($user_id, $order_no);
|
|
if (!$data) throw new \Exception('請您完善預約信息!');
|
|
$price_info = $data['price_info'];
|
|
} else {
|
|
//订单信息计算
|
|
// if(!$param) throw new \Exception('缺少必要信息');
|
|
$this->orderVaild($user_id,$classes_lib_id,$order_no, $param);
|
|
//订单支付信息
|
|
$price_info = $this->getCost($user_id,$classes_lib_id,$param);
|
|
//生成订单号
|
|
if (!$order_no) $order_no = get_order_sn();
|
|
//生成缓存
|
|
$data = compact('user_id', 'classes_lib_id','param', 'order_no', 'price_info');
|
|
self::setOrderCache($user_id, $data);
|
|
}
|
|
|
|
\think\Hook::listen('classes_order_create_before', $data);
|
|
|
|
//下单数据展示
|
|
return $this->showInfo($order_no, $price_info);
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
* 根据缓存创建订单
|
|
*/
|
|
public function cacheCreateOrder($order_no, $user_id,$remark="", $trans = false)
|
|
{
|
|
//得到缓存
|
|
$orderInfo = self::getOrderCache($user_id, $order_no); //得到下单信息
|
|
if (!$orderInfo) throw new \Exception('請您完善預約信息!');
|
|
|
|
if ($trans) {
|
|
self::beginTrans();
|
|
}
|
|
try {
|
|
//1订单执行创建
|
|
$order = $this->createOrder($user_id,$orderInfo['classes_lib_id'],$order_no,$orderInfo['param'],$remark);
|
|
//5删除缓存
|
|
self::deleteOrderCache($user_id, $order_no);
|
|
|
|
//如果是免费订单,则直接调用支付完成
|
|
if ($order['totalprice'] == 0) {
|
|
//调用订单支付成功事件
|
|
$this->paySuccess($order_no,['platform'=>"miniapp",'pay_type'=>'yue']);
|
|
}
|
|
|
|
if ($trans) {
|
|
self::commitTrans();
|
|
}
|
|
} catch (\Exception $e) {
|
|
if ($trans) {
|
|
self::rollbackTrans();
|
|
}
|
|
throw new \Exception($e->getMessage());
|
|
}
|
|
return self::showInfo($order_no);
|
|
}
|
|
|
|
|
|
|
|
|
|
public function createOrder($user_id,$classes_lib_id,$order_no,$param,$remark='',$other_params=[]){
|
|
|
|
$this->orderVaild($user_id,$classes_lib_id, $order_no, $param,true);
|
|
//订单支付信息
|
|
$order_info = self::getCost($user_id,$classes_lib_id,$param,$other_params,true);
|
|
|
|
//组装订单数据
|
|
$order_data = $order_info['order_data'];
|
|
$order_data["order_no"] = $order_no;
|
|
|
|
$res1 = self::create($order_data);
|
|
if (!$res1) throw new \Exception('创建订单失败');
|
|
|
|
//課程详情
|
|
$classes_lib_info = $order_info['classes_lib_info'];
|
|
|
|
$order_detail_data = [];
|
|
$order_detail_data = array_merge($order_detail_data,$classes_lib_info);
|
|
$order_detail_data["classes_lib_id"] = $classes_lib_info['id'];
|
|
$order_detail_data["classes_order_id"] = $res1['id'];
|
|
|
|
unset($order_detail_data['id']);
|
|
unset($order_detail_data['createtime']);;
|
|
|
|
$orderDetail = (new OrderDetail());
|
|
$orderDetail->allowField(true)->save($order_detail_data);
|
|
//更新订单详情id
|
|
$res1->classes_order_detail_id = $orderDetail->id;
|
|
$res1->save();
|
|
|
|
//记录订单日志
|
|
OrderLog::log($res1['id'],"课程订单创建成功,等待【学员】支付",'user',$user_id);
|
|
//7事件
|
|
$data = ['order' => $res1];
|
|
\think\Hook::listen('classes_order_create_after', $data);
|
|
|
|
//更新订单数据
|
|
self::statisticsAndUpdateClassesNumber($res1);
|
|
|
|
|
|
return $res1;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**订单校验
|
|
* @param $user_id 用户id
|
|
* @param $classes_lib_id 课程id
|
|
* @param $order_no 订单号
|
|
* @param $param 表单扩展参数
|
|
* @return bool
|
|
*/
|
|
public function orderVaild($user_id,$classes_lib_id, $order_no, $param,$check=false){
|
|
if(!$user_id||!$classes_lib_id )throw new \Exception("缺少必要参数");
|
|
|
|
//默认校验订单是否已创建
|
|
if($check){
|
|
//判断订单是否已创建
|
|
$order_info = self::where(['order_no'=>$order_no])->find();
|
|
if($order_info) throw new \Exception("订单已生成,如需重新下单请退出页面重新进入!");
|
|
}
|
|
//校验订单参数
|
|
//课程是否存在并上架
|
|
$classes_lib_info = ClassesLib::where('id',$classes_lib_id)->find();
|
|
if(!$classes_lib_info || $classes_lib_info['status']!='1') throw new \Exception("该课程不存在或已下架!");
|
|
|
|
//用户存不存在
|
|
$user_info = User::where('id',$user_id)->find();
|
|
if(!$user_info) throw new \Exception("用户不存在!");
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//计算订单所需返回数据接口
|
|
public static function getCost($user_id,$classes_lib_id,$param=[],$other_params=[],$check = false){
|
|
//用户
|
|
$user_info = User::get($user_id);
|
|
$user_data = [
|
|
"nickname"=>$user_info["nickname"],
|
|
"realname"=>$user_info["realname"],
|
|
"avatar"=>$user_info["avatar"],
|
|
"mobile"=>$user_info["mobile"],
|
|
"money" =>$user_info["money"],
|
|
"score" =>$user_info["score"],
|
|
];
|
|
//课程
|
|
$classes_lib_info = ClassesLib::get($classes_lib_id);
|
|
$classes_lib_info = $classes_lib_info->toArray();
|
|
|
|
$classes_lib_info['use_num'] = 0;//已使用课时
|
|
$classes_lib_info['sub_num'] = $classes_lib_info['classes_num'];//剩余课时
|
|
//单价 = 课程价格/总课时
|
|
$classes_lib_info["unit_price"] = bcdiv($classes_lib_info['price'],$classes_lib_info['classes_num'],2);
|
|
$classes_lib_info["used_price"] = 0;
|
|
|
|
|
|
//组装订单下单数据
|
|
$order_data = [];
|
|
$order_data["user_id"] = $user_id;
|
|
$order_data["manystore_id"] = $classes_lib_info["manystore_id"];
|
|
$order_data["shop_id"] = $classes_lib_info["shop_id"];
|
|
$order_data["classes_lib_id"] = $classes_lib_id;
|
|
$order_data["beforeprice"] = $classes_lib_info["price"];
|
|
$order_data["totalprice"] = $classes_lib_info["price"];
|
|
$order_data["payprice"] = $classes_lib_info["price"];
|
|
$order_data["status"] = '0';
|
|
|
|
|
|
return compact('order_data','classes_lib_info','user_data');
|
|
}
|
|
|
|
|
|
|
|
/**得到待支付订单
|
|
* @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 getNopayOrder($order_no){
|
|
$order = self::where('order_no|id|pay_no|code',$order_no)->where("status",'0')->find();
|
|
if(!$order)throw new \Exception("待支付订单不存在");
|
|
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 getOrder($order_no){
|
|
$order = self::where('order_no|id|pay_no|code',$order_no)->find();
|
|
if(!$order)throw new \Exception("订单不存在");
|
|
return $order;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* 检测支付开关(待扩展)
|
|
*/
|
|
public static function checkPaySwitch($pay_type,$user_id){
|
|
|
|
|
|
}
|
|
|
|
|
|
/**订单支付前判断
|
|
* @param $order_no
|
|
* @param $pay_type
|
|
* @return array|false|\PDOStatement|string|Model
|
|
*/
|
|
public function checkPay($order_no,$pay_type,$password = null){
|
|
$order = self::getNopayOrder($order_no);
|
|
|
|
//支付前判断
|
|
//TODO通用判
|
|
//...
|
|
$user = User::where('id',$order['user_id'])->find();
|
|
if(!$user)throw new \Exception("支付用户异常");
|
|
|
|
//是否开通此支付方式
|
|
self::checkPaySwitch($pay_type,$order['user_id']);
|
|
|
|
//各自币种判断
|
|
switch ($pay_type) {
|
|
case "wechat": //微信支付
|
|
//..
|
|
break;
|
|
case "alipay": //支付宝支付
|
|
//...
|
|
break;
|
|
case "offline": //线下支付
|
|
//..
|
|
break;
|
|
case "yue": //余额支付
|
|
//支付密码判断
|
|
// $res = User::checkPayPassword($order['user_id'],$password,true);
|
|
// if(!$res['is_setting'])throw new \Exception("您还未设置支付密码,请您先去设置支付密码");
|
|
// if(!$res['is_right'])throw new \Exception("支付密码错误");
|
|
//判断余额是否充足
|
|
//得到支付余额
|
|
$total_amount = $order['totalprice'] ?:0;
|
|
//得到用户余额
|
|
$money = $user['money'];
|
|
if(bccomp($money,$total_amount)==-1)throw new \Exception("当前余额不足以完成本次支付!");
|
|
break;
|
|
default:
|
|
throw new \Exception("不支持的支付类型验证");
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**余额支付
|
|
* @throws \Exception
|
|
*/
|
|
public function moneyPay($order_no,$method,$trans=false){
|
|
$order = self::getNopayOrder($order_no);
|
|
//支付前判断
|
|
// self::checkPay($order_no,'wallet');
|
|
//判断逻辑
|
|
if($trans){
|
|
self::beginTrans();
|
|
}
|
|
$res = true;
|
|
try{
|
|
//事务逻辑
|
|
if($order['totalprice']){
|
|
$memo = "课时订单[ID:{$order['id']}]下单,支付余额{$order['totalprice']}";
|
|
try{
|
|
//扣除用户余额
|
|
User::money(-$order['totalprice'], $order['user_id'], $memo,'class_order_pay' ,[
|
|
'order_id' => $order->id,
|
|
'order_sn' => $order->order_sn,
|
|
]);
|
|
}catch (\Exception $e){
|
|
throw new \Exception("余额不足!");
|
|
}
|
|
}
|
|
//调用订单支付成功事件
|
|
$this->paySuccess($order_no,['platform'=>$method,'pay_type'=>'yue']);
|
|
if($trans){
|
|
self::commitTrans();
|
|
}
|
|
}catch (\Exception $e){
|
|
if($trans){
|
|
self::rollbackTrans();
|
|
}
|
|
throw new \Exception($e->getMessage());
|
|
}
|
|
return $res;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**调用订单支付成功事件
|
|
* @param $order_no
|
|
* @param $pay_type
|
|
* @param $price
|
|
* @param $price_check
|
|
* @param bool $trans
|
|
* @return bool
|
|
* @throws \Exception
|
|
*/
|
|
public function paySuccess($order_no,$notify=[],$price=0,$price_check=false,$trans=false){
|
|
$order = self::getNopayOrder($order_no);
|
|
//金额校验 :第三方支付时回调入口判断
|
|
if($price_check){
|
|
if(bccomp($price,$order['totalprice'])==-1)throw new \Exception("支付金额与订单需要支付金额对应不上,回调失败!");
|
|
}
|
|
//判断逻辑
|
|
if($trans){
|
|
self::beginTrans();
|
|
}
|
|
$res = true;
|
|
try{
|
|
//事务逻辑
|
|
//不拆分订单,直接执行
|
|
self::paySetData($order,$notify);
|
|
if($trans){
|
|
self::commitTrans();
|
|
}
|
|
}catch (\Exception $e){
|
|
if($trans){
|
|
self::rollbackTrans();
|
|
}
|
|
throw new \Exception($e->getMessage());
|
|
}
|
|
return $res;
|
|
}
|
|
|
|
|
|
public static function paySetData($order,$notify=[]){
|
|
//订单支付更新
|
|
$order = self::updatePay($order,$notify);
|
|
//生成订单一维码和二维码
|
|
$order = self::buildCode($order);
|
|
//记录订单日志
|
|
OrderLog::log($order['id'],"课程订单支付成功,核销码生成,可以正常约课",'user',$order['user_id']);
|
|
//调用支付成功事件
|
|
$data = ['order' => $order];
|
|
\think\Hook::listen('classes_order_payed_after', $data);
|
|
//更新订单数据
|
|
self::statisticsAndUpdateClassesNumber($order);
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static function updatePay($order,$notify=[]){
|
|
if(is_string($order)||is_numeric($order))$order = self::getNopayOrder($order);
|
|
$order->status ='3';
|
|
$order->paytime = time();
|
|
$order->pay_no = $notify['transaction_id'] ?? null;
|
|
$order->pay_json = $notify['payment_json'] ?? '{}';
|
|
$order->pay_type = $notify['pay_type'] ?? 'yue';
|
|
//如果订单创建时间大于预约时间,则等于预约时间
|
|
// if($order['createtime']>$order['starttime'])$order['createtime'] = $order['starttime'];
|
|
switch ($order->pay_type) {
|
|
case "offline": //线下付款,线上不需要支付
|
|
|
|
break;
|
|
|
|
default:
|
|
$order->payprice = $notify['pay_fee'] ?? $order->totalprice;
|
|
}
|
|
$order->platform = $notify['platform'] ?? 'miniapp';
|
|
$order->save();
|
|
return $order;
|
|
}
|
|
|
|
|
|
|
|
public static function buildCode($order){
|
|
if(is_string($order)||is_numeric($order))$order = self::getNopayOrder($order);
|
|
//生成核销二维码和一维码
|
|
//生成code
|
|
$vcode = en_code($order['id']). Random::alnum();
|
|
$order['code'] = $vcode;
|
|
$params = [
|
|
"type"=>"classes_verification",
|
|
"vcode" =>$vcode,
|
|
"id"=>$order['classes_lib_id'],
|
|
];
|
|
//$params生成get参数
|
|
$params = http_build_query($params);
|
|
//生成二维码和一维码
|
|
//二维码
|
|
$order->codeimage = (Common::getQrcode([
|
|
'text' => $params,
|
|
'size' => 200,
|
|
]))['url'];
|
|
//一维码
|
|
$order->codeoneimage = (Common::getBarcode([
|
|
'text' => $params,
|
|
'size' => 200,
|
|
]))['url'];
|
|
$order->save();
|
|
return $order;
|
|
}
|
|
|
|
|
|
|
|
|
|
/**得到基础条件
|
|
* @param $status
|
|
* @param null $model
|
|
* @param string $alisa
|
|
*/
|
|
public static function getBaseWhere($whereData = [], $model = null, $alisa = '',$with = false)
|
|
{
|
|
|
|
if (!$model) {
|
|
$model = new static;
|
|
if ($alisa&&!$with) $model = $model->alias($alisa);
|
|
}
|
|
if ($alisa) $alisa = $alisa . '.';
|
|
$tableFields = (new static)->getTableFields();
|
|
foreach ($tableFields as $fields)
|
|
{
|
|
if(in_array($fields, ['status','classes_lib_id']))continue;
|
|
// if (isset($whereData[$fields]) && $whereData[$fields]) $model = $model->where("{$alisa}{$fields}", '=', $whereData[$fields]);
|
|
|
|
if (isset($whereData[$fields]) && $whereData[$fields]){
|
|
if(is_array($whereData[$fields])){
|
|
$model = $model->where("{$alisa}{$fields}", $whereData[$fields][0], $whereData[$fields][1]);
|
|
}else{
|
|
$model = $model->where("{$alisa}{$fields}", '=', $whereData[$fields]);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
}
|
|
if (isset($whereData['status'])) $model = $model->where("{$alisa}status", 'in', $whereData['status']);
|
|
if (isset($whereData['not_status'])) $model = $model->where("{$alisa}status", 'not in', $whereData['not_status']);
|
|
if (isset($whereData['keywords'])&&$whereData['keywords']) $model = $model->where("{$alisa}order_no|{$alisa}pay_no|{$alisa}code", '=', $whereData['keywords']);
|
|
if (isset($whereData['time'])&&$whereData['time']){
|
|
|
|
$model = $model->time($whereData['time']);
|
|
}
|
|
if (isset($whereData['user_id']) && $whereData['user_id']) $model = $model->where("{$alisa}user_id", '=', $whereData['user_id']);
|
|
|
|
if (isset($whereData['classes_lib_ids']) && $whereData['classes_lib_ids']) $model = $model->where("{$alisa}classes_lib_id", 'in', $whereData['classes_lib_ids']);
|
|
if (isset($whereData['classes_lib_id']) && $whereData['classes_lib_id']) $model = $model->where("{$alisa}classes_lib_id", 'in', $whereData['classes_lib_id']);
|
|
|
|
return $model;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static function allList($user_id,$page, $limit,$keywords,$status,$classes_lib_id=[]){
|
|
$with_field = [
|
|
'user'=>['nickname','mobile','avatar','realname'],
|
|
'base'=>['*'],
|
|
'shop'=>['*'],
|
|
'detail'=>['*'],
|
|
];
|
|
$CANCEL = '-3';
|
|
$NOPAY = '0';
|
|
$PAYED = '3';
|
|
$REFUND = '6';
|
|
$FINISH = '9';
|
|
$alisa = (new self)->getWithAlisaName();
|
|
$sort = "field({$alisa}.status,'{$NOPAY}','{$PAYED}','{$FINISH}','{$REFUND}','{$CANCEL}') asc,{$alisa}.id desc";
|
|
$serch_where = ['status'=>$status,'user_id'=>$user_id,'keywords'=>$keywords,"classes_lib_id"=>$classes_lib_id];
|
|
// if($type)$serch_where['type'] = $type;
|
|
return (new self)->getBaseList($serch_where, $page, $limit,$sort,$with_field);
|
|
}
|
|
|
|
|
|
|
|
|
|
public static function baseCount($where = []){
|
|
$CANCEL = '-3';
|
|
$NOPAY = '0';
|
|
$PAYED = '3';
|
|
$REFUND = '6';
|
|
$FINISH = '9';
|
|
|
|
$cancel_number = self::getBaseWhere(array_merge(['status'=>$CANCEL],$where))->count();
|
|
$nopay_number = self::getBaseWhere(array_merge(['status'=>$NOPAY],$where))->count();
|
|
$payed_number = self::getBaseWhere(array_merge(['status'=>$PAYED],$where))->count();
|
|
$retund_number = self::getBaseWhere(array_merge(['status'=>$REFUND],$where))->count();
|
|
$finish_number = self::getBaseWhere(array_merge(['status'=>$FINISH],$where))->count();
|
|
return compact('cancel_number','nopay_number','payed_number','retund_number','finish_number');
|
|
}
|
|
|
|
|
|
|
|
/**订单数量统计
|
|
* @param int $user_id
|
|
* @return array
|
|
*/
|
|
public static function orderCount($user_id = 0,$classes_lib_id=[]){
|
|
return self::baseCount(['user_id'=>$user_id,"classes_lib_id"=>$classes_lib_id]);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static function workList($page, $limit,$keywords,$status,$classes_lib_id=[],$classes_lib_ids=[]){
|
|
$with_field = [
|
|
'user'=>['nickname','mobile','avatar','realname'],
|
|
'base'=>['*'],
|
|
'shop'=>['*'],
|
|
'detail'=>['*'],
|
|
];
|
|
$CANCEL = '-3';
|
|
$NOPAY = '0';
|
|
$PAYED = '3';
|
|
$REFUND = '6';
|
|
$FINISH = '9';
|
|
$alisa = (new self)->getWithAlisaName();
|
|
$sort = "field({$alisa}.status,'{$NOPAY}','{$PAYED}','{$FINISH}','{$REFUND}','{$CANCEL}') asc,{$alisa}.id desc";
|
|
$serch_where = ['status'=>$status,'keywords'=>$keywords,"classes_lib_id"=>$classes_lib_id,"classes_lib_ids"=>$classes_lib_ids];
|
|
// if($type)$serch_where['type'] = $type;
|
|
return (new self)->getBaseList($serch_where, $page, $limit,$sort,$with_field);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**订单数量统计
|
|
* @param int $user_id
|
|
* @return array
|
|
*/
|
|
public static function workCount($classes_lib_id=[],$classes_lib_ids=[]){
|
|
return self::baseCount(["classes_lib_id"=>$classes_lib_id,"classes_lib_ids"=>$classes_lib_ids]);
|
|
}
|
|
|
|
|
|
|
|
//统计并更新课时数等相关统计数据
|
|
public static function statisticsAndUpdateClassesNumber($order){
|
|
if(is_string($order)||is_numeric($order))$order = self::getOrder($order);
|
|
$detail = $order->detail;
|
|
if(!$detail)throw new \think\Exception("订单信息缺失!");
|
|
//得到当前订单的所有未取消课时订单
|
|
$hourorderOrderCount = \app\common\model\school\classes\hourorder\Order::where("status","<>",'-3')
|
|
->where("classes_order_id","=",$order["id"])
|
|
->count();
|
|
//更新已用课程数和剩余课程数
|
|
$detail->use_num = $hourorderOrderCount;
|
|
$detail->sub_num = $detail->classes_num - $detail->use_num;
|
|
//更新已用课程金额和剩余课程金额
|
|
$detail->used_price = bcmul($detail->unit_price , $detail->use_num,2);
|
|
$detail->save();
|
|
//更新订单应退金额
|
|
$order->total_refundprice = bcsub($order->totalprice,$detail->used_price,2);
|
|
$order->save();
|
|
|
|
|
|
//课程下单时已核销人数更新
|
|
$lib = $order->lib;
|
|
if($lib){
|
|
$lib->sale = self::where("classes_lib_id",$lib["id"])->where("status","<>","-3")->count();
|
|
|
|
//遍历课程课时规格,更新课时统计数据
|
|
$specs = $lib->specs;
|
|
if($specs){
|
|
foreach ($specs as $spec){
|
|
// '已核销人数',
|
|
$spec->verification_num = \app\common\model\school\classes\hourorder\Order::where("classes_lib_spec_id",$spec["id"])->where("status","=","3")->count();
|
|
//已报名人数
|
|
$spec->sign_num = \app\common\model\school\classes\hourorder\Order::where("classes_lib_spec_id",$spec["id"])->where("status","in",["-1","0"])->count();
|
|
$spec->save();
|
|
}
|
|
|
|
}
|
|
//统计课程总报名和总核销
|
|
$lib->sign_num = \app\common\model\school\classes\hourorder\Order::where("classes_lib_id",$lib["id"])->where("status","in",["-1","0"])->count();
|
|
$lib->verification_num = \app\common\model\school\classes\hourorder\Order::where("classes_lib_id",$lib["id"])->where("status","=","3")->count();
|
|
$lib->save();
|
|
}
|
|
|
|
//检测订单完成状态
|
|
self::statisticsAndUpdateOrderFinish($order->id);
|
|
|
|
|
|
return $order;
|
|
}
|
|
|
|
/** 检测订单完成状态
|
|
* @param $order
|
|
* @return void
|
|
* @throws \think\Exception
|
|
* @throws \think\db\exception\DataNotFoundException
|
|
* @throws \think\db\exception\ModelNotFoundException
|
|
* @throws \think\exception\DbException
|
|
*/
|
|
public static function statisticsAndUpdateOrderFinish($order){
|
|
if(is_string($order)||is_numeric($order))$order = self::getOrder($order);
|
|
$detail = $order->detail;
|
|
if(!$detail)throw new \think\Exception("订单信息缺失!");
|
|
//得到当前订单的所有已完成课时订单
|
|
$hourorderOrderCount = \app\common\model\school\classes\hourorder\Order::where("status","=",'3')
|
|
->where("classes_order_id","=",$order["id"])
|
|
->count();
|
|
//可完成的订单
|
|
if($order["status"] == '3' && (in_array($order["server_status"],['0','6'])) && !$order['finishtime']){
|
|
//判定是否达成完成条件
|
|
//条件:课程课时已全部完成
|
|
if($hourorderOrderCount >= $detail->classes_num){
|
|
//执行订单完成的更新逻辑
|
|
self::updateFinish($order);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** 更新订单成已完成
|
|
* @param $order
|
|
* @return void
|
|
*/
|
|
public static function updateFinish($order){
|
|
if(is_string($order)||is_numeric($order))$order = self::getOrder($order);
|
|
//更新订单
|
|
$order["status"] = '9';
|
|
$order["finishtime"] = time();
|
|
|
|
$order->save();
|
|
//记录订单日志
|
|
OrderLog::log($order['id'],"课程订单,课时已全部完成",'user',$order['user_id']);
|
|
//调用支付成功事件
|
|
$data = ['order' => $order];
|
|
\think\Hook::listen('classes_order_finish_after', $data);
|
|
}
|
|
|
|
|
|
|
|
|
|
/**得到可取消订单
|
|
* @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 getHaveCancelOrder($order_no){
|
|
// $where = [self::STATUS_NOPAY,self::STATUS_PAYED];
|
|
$order = self::where('order_no|id|pay_no|code',$order_no)
|
|
->where("status","in",['0'])
|
|
->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 updateCancel($order){
|
|
if(is_string($order))$order = self::getHaveCancelOrder($order);
|
|
$order->status = "-3";//refund_status
|
|
$order->canceltime = time();
|
|
$order->save();
|
|
return $order;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**订单取消
|
|
* @param $order_no
|
|
* @param int $user_id
|
|
* @param bool $check
|
|
* @param bool $trans
|
|
* @return bool
|
|
* @throws \Exception
|
|
*/
|
|
public function cancel($order_no,$user_id=0,$check=false,$oper_type='user',$oper_id=0,$trans=false){
|
|
//得到可取消订单
|
|
$order = self::getHaveCancelOrder($order_no);
|
|
if($check){
|
|
//用户操作权限检测
|
|
\app\common\model\school\classes\hourorder\Order::checkOptionAuth($order['id'],$user_id ?: $oper_id,$oper_type);
|
|
}
|
|
|
|
//判断逻辑
|
|
if($trans){
|
|
self::beginTrans();
|
|
}
|
|
$res = true;
|
|
try{
|
|
//事务逻辑
|
|
//更新订单取消状态
|
|
$order = Order::updateCancel($order);
|
|
//插入订单取消日志
|
|
if(!$user_id ||$order["user_id"] !=$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']);
|
|
}
|
|
|
|
//调用订单取消事件
|
|
$data = ['order' => $order,"user_id"=>$user_id,"oper_type"=>$oper_type,"oper_id"=>$oper_id];
|
|
\think\Hook::listen('classes_order_cancel_after', $data);
|
|
//执行课时数更新
|
|
$res1 = self::statisticsAndUpdateClassesNumber($order['id']);
|
|
|
|
|
|
if($trans){
|
|
self::commitTrans();
|
|
}
|
|
}catch (\Exception $e){
|
|
if($trans){
|
|
self::rollbackTrans();
|
|
}
|
|
throw new \Exception($e->getMessage());
|
|
}
|
|
return $res1;
|
|
}
|
|
|
|
|
|
}
|