课程订单正向流程接口

This commit is contained in:
15090180611 2024-11-08 18:18:06 +08:00
parent b0bb197771
commit 8c91a1e794
11 changed files with 1205 additions and 23 deletions

View File

@ -0,0 +1,123 @@
<?php
namespace app\api\controller\school;
use app\common\model\school\classes\order\Order as OrderModel;
use app\common\model\school\classes\Teacher as Teachermodel;
/**
* 课时订单接口
*/
class HourOrder extends Base
{
protected $noNeedLogin = [];
protected $noNeedRight = '*';
protected $model = null;
/**
* 初始化操作
* @access protected
*/
protected function _initialize()
{
$this->model = new OrderModel;
parent::_initialize();
//判断登录用户是否是员工
}
/**
* @ApiTitle( 订单详情)
* @ApiSummary(订单详情)
* @ApiRoute(/api/school/order/detail)
* @ApiMethod(GET)
* @ApiParams(name = "id", type = "int",required=true,description = "订单id或订单号")
* @ApiReturn({
*
*})
*/
public function detail(){
$id = $this->request->get('id/d','');
if(empty($id)){
$this->error(__('缺少必要参数'));
}
try {
$res = OrderModel::getDetail($id);
} catch (\Exception $e){
// Log::log($e->getMessage());
$this->error($e->getMessage(),['errcode'=>$e->getCode()]);
}
$this->success('获取成功', ['detail' => $res]);
}
/**
* @ApiTitle( 订单确认/订单计算接口)
* @ApiSummary(订单确认/订单计算接口【有过期时间】)
* @ApiRoute(/api/school/order/confirm)
* @ApiMethod(POST)
* @ApiParams(name = "classes_lib_id", type = "int",required=true,description = "订单id或订单号")
* @ApiParams(name = "order_no", type = "string",required=false,description = "缓存key")
* @ApiParams(name = "is_compute", type = "int",required=false,description = "是否重新计算并更新缓存 默认传1")
* @ApiReturn({
*
*})
*/
public function confirm(){
$user_id = 0;
$user = $this->auth->getUser();//登录用户
if($user)$user_id = $user['id'];
$classes_lib_id = $this->request->post('classes_lib_id/d', 0); //课程id
// $param = urldecode($this->request->post('param/s', "{}")); //参数
$param = []; //参数
$order_no = $this->request->post('order_no/s', ''); //订单号
$is_compute = $this->request->post('is_compute/d', 1); //是否重新计算并更新缓存
try{
//当前申请状态
$res = $this->model->confirm($user_id, $classes_lib_id,$order_no,$param, $is_compute);
}catch (\Exception $e){
// Log::log($e->getMessage());
$this->error($e->getMessage(),['errcode'=>$e->getCode()]);
}
$this->success('执行成功可用缓存key下单', $res);
}
/**
* @ApiTitle( 订单下单接口)
* @ApiSummary(订单下单接口)
* @ApiRoute(/api/school/order/create)
* @ApiMethod(POST)
* @ApiParams(name = "order_no", type = "string",required=true,description = "缓存key")
* @ApiReturn({
*
*})
*/
public function create(){
$user_id = 0;
$user = $this->auth->getUser();//登录用户
if($user)$user_id = $user['id'];
$order_no = $this->request->post('order_no/s', ''); //订单号
$remark = $this->request->post('remark/s', ''); //下单备注
// repeat_filter("appointment\order\create".$user_id, 2);
try{
//当前申请状态
$res = $this->model->cacheCreateOrder($order_no,$user_id,$remark,true);
}catch (\Throwable $e){
// file_put_contents("test.txt",$e->getMessage().$e->getFile().$e->getLine());//写入文件,一般做正式环境测试
// Log::log($e->getMessage());
$this->error($e->getMessage(),['errcode'=>$e->getCode()]);;
}
$this->success('订单创建成功缓存key被消耗', $res);
}
}

View File

@ -2,6 +2,7 @@
namespace app\api\controller\school;
use app\common\model\school\classes\order\Order as OrderModel;
use app\common\model\school\classes\Teacher as Teachermodel;
/**
@ -9,7 +10,7 @@ use app\common\model\school\classes\Teacher as Teachermodel;
*/
class Order extends Base
{
protected $noNeedLogin = ["detail",'people','spec'];
protected $noNeedLogin = [];
protected $noNeedRight = '*';
protected $model = null;
@ -21,7 +22,7 @@ class Order extends Base
protected function _initialize()
{
$this->model = new Teachermodel;
$this->model = new OrderModel;
parent::_initialize();
//判断登录用户是否是员工
@ -29,11 +30,11 @@ class Order extends Base
/**
* @ApiTitle( x详情)
* @ApiSummary(xx详情)
* @ApiRoute(/api/school/teacher/detail)
* @ApiTitle( 订单详情)
* @ApiSummary(订单详情)
* @ApiRoute(/api/school/order/detail)
* @ApiMethod(GET)
* @ApiParams(name = "id", type = "int",required=true,description = "老师id")
* @ApiParams(name = "id", type = "int",required=true,description = "订单id或订单号")
* @ApiReturn({
*
*})
@ -46,12 +47,77 @@ class Order extends Base
}
try {
$res = $this->model->detail($id);
$res = OrderModel::getDetail($id);
} catch (\Exception $e){
// Log::log($e->getMessage());
$this->error($e->getMessage(),['errcode'=>$e->getCode()]);
}
$this->success('获取成功', ['detail' => $res]);
}
/**
* @ApiTitle( 订单确认/订单计算接口)
* @ApiSummary(订单确认/订单计算接口【有过期时间】)
* @ApiRoute(/api/school/order/confirm)
* @ApiMethod(POST)
* @ApiParams(name = "classes_lib_id", type = "int",required=true,description = "订单id或订单号")
* @ApiParams(name = "order_no", type = "string",required=false,description = "缓存key")
* @ApiParams(name = "is_compute", type = "int",required=false,description = "是否重新计算并更新缓存 默认传1")
* @ApiReturn({
*
*})
*/
public function confirm(){
$user_id = 0;
$user = $this->auth->getUser();//登录用户
if($user)$user_id = $user['id'];
$classes_lib_id = $this->request->post('classes_lib_id/d', 0); //课程id
// $param = urldecode($this->request->post('param/s', "{}")); //参数
$param = []; //参数
$order_no = $this->request->post('order_no/s', ''); //订单号
$is_compute = $this->request->post('is_compute/d', 1); //是否重新计算并更新缓存
try{
//当前申请状态
$res = $this->model->confirm($user_id, $classes_lib_id,$order_no,$param, $is_compute);
}catch (\Exception $e){
// Log::log($e->getMessage());
$this->error($e->getMessage(),['errcode'=>$e->getCode()]);
}
$this->success('执行成功可用缓存key下单', $res);
}
/**
* @ApiTitle( 订单下单接口)
* @ApiSummary(订单下单接口)
* @ApiRoute(/api/school/order/create)
* @ApiMethod(POST)
* @ApiParams(name = "order_no", type = "string",required=true,description = "缓存key")
* @ApiReturn({
*
*})
*/
public function create(){
$user_id = 0;
$user = $this->auth->getUser();//登录用户
if($user)$user_id = $user['id'];
$order_no = $this->request->post('order_no/s', ''); //订单号
$remark = $this->request->post('remark/s', ''); //下单备注
// repeat_filter("appointment\order\create".$user_id, 2);
try{
//当前申请状态
$res = $this->model->cacheCreateOrder($order_no,$user_id,$remark,true);
}catch (\Throwable $e){
// file_put_contents("test.txt",$e->getMessage().$e->getFile().$e->getLine());//写入文件,一般做正式环境测试
// Log::log($e->getMessage());
$this->error($e->getMessage(),['errcode'=>$e->getCode()]);;
}
$this->success('订单创建成功缓存key被消耗', $res);
}
}

View File

@ -0,0 +1,245 @@
<?php
namespace app\api\controller\school;
use addons\epay\library\Service;
use addons\third\model\Third;
use app\common\model\school\classes\order\Order as OrderModel;
use Exception;
use think\Log;
/**
* 课程支付接口
*/
class Pay extends Base
{
protected $noNeedLogin = '*';
protected $noNeedRight = '*';
protected $model = null;
/**
* 初始化操作
* @access protected
*/
protected function _initialize()
{
$this->model = new OrderModel;
parent::_initialize();
//判断登录用户是否是员工
}
/**
* @ApiTitle( 课程第三方支付)
* @ApiSummary(课程第三方支付(微信等支付))
* @ApiRoute(/api/school/pay/payment)
* @ApiMethod(POST)
* @ApiParams(name = "order_no", type = "string",required=true,description = "订单id或订单号")
* @ApiParams(name = "type", type = "string",required=true,description = "服务商:alipay=支付宝,wechat=微信")
* @ApiParams(name = "platform", type = "string",required=true,description = "平台:web=PC网页支付,wap=H5手机网页支付,app=APP支付,scan=扫码支付,mp=微信公众号支付,miniapp=微信小程序支付")
* @ApiParams(name = "openid", type = "string",required=false,description = "用户openid(非必填)")
* @ApiReturn({
*
*})
*/
public function payment()
{
$order_no = $this->request->post('order_no/s');
$type = $this->request->post('type/s');
$method = $this->request->post('platform/s');
$openid = $this->request->post('openid/s', "");
$platform = $method;
try {
//订单支付前校验
$this->model->checkPay($order_no,$type);
$order = OrderModel::where('order_no', $order_no)->find();
$amount = $order["totalprice"];
} catch (\Exception $e) {
$this->error($e->getMessage());
}
if (!$amount || $amount < 0) {
$this->error("支付金额必须大于0");
}
if (!$type || !in_array($type, ['alipay', 'wechat'])) {
$this->error("支付类型不能为空");
}
if (in_array($method, ['miniapp', 'mp']) && !$openid) {
// 微信公众号,小程序支付,必须有 openid
if (isset($openid) && $openid) {
// 如果传的有 openid
} else {
// 没有 openid 默认拿下单人的 openid
$oauth = Third::where([
'user_id' => $order->user_id,
'platform' => $type,
'apptype' => $platform
])->find();
$openid = $oauth ? $oauth->openid : '';
}
if (!$openid) {
// 缺少 openid
$this->error("授权过期,请您重新授权登录");
}
}
//订单号
$out_trade_no = $order_no;
//订单标题
$title = '课程订单['.$out_trade_no."]支付";
//回调链接
$notifyurl = $this->request->root(true) . '/api/school/pay/notifyx/paytype/' . $type. '/platform/' . $method;
$returnurl = $this->request->root(true) . '/api/school/pay/returnx/paytype/' . $type . '/out_trade_no/' . $out_trade_no;
$response = Service::submitOrder($amount, $out_trade_no, $type, $title, $notifyurl, $returnurl, $method, $openid);
return $response;
}
/**
* @ApiTitle( 第三方支付支付成功回调)
* @ApiSummary(第三方支付成功回调)
* @ApiRoute(/api/school/pay/notifyx)
* @ApiMethod(GET)
* @ApiReturn({
*
*})
*/
public function notifyx()
{
$paytype = $this->request->param('paytype');
$platform = $this->request->param('platform');
$pay = Service::checkNotify($paytype);
if (!$pay) {
return json(['code' => 'FAIL', 'message' => '失败'], 500, ['Content-Type' => 'application/json']);
}
$payment = $paytype;
// 获取回调数据V3和V2的回调接收不同
$data = Service::isVersionV3() ? $pay->callback() : $pay->verify();
Log::write('notifyx-result:'. json_encode($data));
$out_trade_no = $data['out_trade_no'];
$out_refund_no = $data['out_biz_no'] ?? '';
// 判断是否是支付宝退款(支付宝退款成功会通知该接口)
if ($paytype == 'alipay' // 支付宝支付
&& $data['notify_type'] == 'trade_status_sync' // 同步交易状态
&& $data['trade_status'] == 'TRADE_CLOSED' // 交易关闭
&& $out_refund_no // 退款单号
) {
// 退款回调
// $this->refundFinish($out_trade_no, $out_refund_no,$platform);
return $pay->success()->send();
}
// 判断支付宝微信是否是支付成功状态,如果不是,直接返回响应
if ($payment == 'alipay' && $data['trade_status'] != 'TRADE_SUCCESS') {
// 不是交易成功的通知,直接返回成功
return $pay->success()->send();
}
if ($payment == 'wechat' && ($data['result_code'] != 'SUCCESS' || $data['return_code'] != 'SUCCESS')) {
// 微信交易未成功,返回 false让微信再次通知
return false;
}
try {
//微信支付V3返回和V2不同
if (Service::isVersionV3() && $paytype === 'wechat') {
$data = $data['resource']['ciphertext'];
$data['total_fee'] = $data['amount']['total'];
}
\think\Log::record($data);
$payamount = $paytype == 'alipay' ? $data['total_amount'] : $data['total_fee'] / 100;
$out_trade_no = $data['out_trade_no'];
\think\Log::record("回调成功,订单号:{$out_trade_no},金额:{$payamount}");
//你可以在此编写订单逻辑
// 支付成功流程
//获取支付金额、订单号
$pay_fee = $payment == 'alipay' ? $data['total_amount'] : $data['total_fee'] / 100;
//你可以在此编写订单逻辑
$order = OrderModel::getNopayOrder($out_trade_no);
if (!$order || $order["status"] != '0') {
// 订单不存在,或者订单已支付
return $pay->success()->send();
}
$notify = [
'order_no' => $data['out_trade_no'],
'transaction_id' => $payment == 'alipay' ? $data['trade_no'] : $data['transaction_id'],
'notify_time' => date('Y-m-d H:i:s', strtotime($data['time_end'])),
'buyer_email' => $payment == 'alipay' ? $data['buyer_logon_id'] : $data['openid'],
'payment_json' => json_encode($data->all()),
'pay_fee' => $pay_fee,
'pay_type' => $payment, // 支付方式
'platform' => $platform,
];
$this->model->paySuccess($order['order_sn'],$notify,$pay_fee,true,true);
} catch (Exception $e) {
\think\Log::record("回调逻辑处理错误:" . $e->getMessage(), "error");
}
//下面这句必须要执行,且在此之前不能有任何输出
if (Service::isVersionV3()) {
return $pay->success()->getBody()->getContents();
} else {
return $pay->success()->send();
}
}
/**
* 支付返回,网页支付版本(小程序忽略)
*/
public function returnx()
{
$paytype = $this->request->param('paytype');
$out_trade_no = $this->request->param('out_trade_no');
$pay = Service::checkReturn($paytype);
if (!$pay) {
$this->error('签名错误', '');
}
//你可以在这里定义你的提示信息,但切记不可在此编写逻辑
$this->success("请返回网站查看支付结果", addon_url("epay/index/index"));
}
}

View File

@ -507,3 +507,66 @@ EOT;
return $icon;
}
}
/**
获取订单号
*/
if (!function_exists('get_order_sn')) {
function get_order_sn()
{
$time = explode(' ', microtime());
$time = $time[1] . $time[0] * 1000;
$time = explode('.', $time);
$time = isset($time[1]) ? $time[1] : 0;
$time = date('YmdHis') + $time;
mt_srand((double) microtime() * 1000000);
return $time . str_pad(mt_rand(1, 99999), 5, '0', 0);
}
}
if (!function_exists('en_code')) {
/**
* 针对于ID的可逆加密函数也可以用作于邀请码之类
* @param int $code
*/
function en_code($code)
{
static $source_string = 'E5FCDG3HQA4B1NOPIJ2RSTUV67MWX89KLYZ';
$num = $code;
$code = '';
while ($num > 0) {
$mod = $num % 35;
$num = ($num - $mod) / 35;
$code = $source_string[$mod] . $code;
}
if (empty($code[3])) {
$code = str_pad($code, 4, '0', STR_PAD_LEFT);
}
return $code;
}
}
if (!function_exists('de_code')) {
/**
* 针对于ID的可逆加密函数也可以用作于邀请码之类
* @param string $code
*/
function de_code($code)
{
static $source_string = 'E5FCDG3HQA4B1NOPIJ2RSTUV67MWX89KLYZ';
if (strrpos($code, '0') !== false) {
$code = substr($code, strrpos($code, '0') + 1);
}
$len = strlen($code);
$code = strrev($code);
$num = 0;
for ($i = 0; $i < $len; $i++) {
$num += strpos($source_string, $code[$i]) * pow(35, $i);
}
return $num;
}
}

View File

@ -0,0 +1,34 @@
<?php
namespace app\common\listener\classesorder;
class OrderHook
{
// 订单创建成功前(订单计算)
public function classesOrderCreateBefore(&$params)
{
}
// 订单创建成功后
public function classesOrderCreateAfter(&$params)
{
}
// 订单支付成功后
public function classesOrderPayedAfter(&$params)
{
}
// 订单取消成功后
public function classesOrderCancelAfter(&$params)
{
}
}

View File

@ -11,6 +11,9 @@ class BaseModel extends Model
{
use \traits\ModelTrait;
// use \traits\ErrorTrait;
public $timeKey = 'createtime';
public static $staticTimeKey = 'createtime';

View File

@ -2,8 +2,11 @@
namespace app\common\model;
use app\common\library\Auth;
use app\common\library\Sms;
use app\common\model\school\classes\Teacher;
use app\common\model\school\classes\Verification;
use fast\Random;
use think\Db;
use think\Model;
@ -88,7 +91,7 @@ class User extends Model
* @param int $user_id 会员ID
* @param string $memo 备注
*/
public static function money($money, $user_id, $memo)
public static function money($money, $user_id, $memo,$type='',$ext = [])
{
Db::startTrans();
try {
@ -100,7 +103,7 @@ class User extends Model
//更新会员信息
$user->save(['money' => $after]);
//写入日志
MoneyLog::create(['user_id' => $user_id, 'money' => $money, 'before' => $before, 'after' => $after, 'memo' => $memo]);
MoneyLog::create(['user_id' => $user_id, 'money' => $money, 'before' => $before, 'after' => $after, 'memo' => $memo, 'ext' => $ext?:"","type" =>$type]);
}
Db::commit();
} catch (\Exception $e) {
@ -114,7 +117,7 @@ class User extends Model
* @param int $user_id 会员ID
* @param string $memo 备注
*/
public static function score($score, $user_id, $memo)
public static function score($score, $user_id, $memo,$type='',$ext = [])
{
Db::startTrans();
try {
@ -126,7 +129,7 @@ class User extends Model
//更新会员信息
$user->save(['score' => $after, 'level' => $level]);
//写入日志
ScoreLog::create(['user_id' => $user_id, 'score' => $score, 'before' => $before, 'after' => $after, 'memo' => $memo]);
ScoreLog::create(['user_id' => $user_id, 'score' => $score, 'before' => $before, 'after' => $after, 'memo' => $memo, 'ext' => $ext?:"","type" =>$type]);
}
Db::commit();
} catch (\Exception $e) {
@ -179,4 +182,75 @@ class User extends Model
}
/**检测用户设置密码情况
* @param $user_id
* @param $password
* @param bool $check
*/
public static function checkPayPassword($user_id,$password=null,$check=true){
$user = self::where('id',$user_id)->find();
if(!$user) throw new \Exception("用户不存在!");
// if(!$user["mobile"]) throw new \Exception("您未绑定手机号,请您先去绑定手机号!");
$mobile = $user["mobile"];
$auth = Auth::instance();
//是否设置支付密码
$is_setting = false;
//密码是否正确
$is_right = false;
//检测是否设置支付密码
if($user['pay_password'])$is_setting = true;
if($check){
//校验密码是否正确
$pay_password = $auth->getEncryptPassword($password, $user['pay_salt']);
if($user['pay_password']==$pay_password)$is_right = true;
}
return compact('is_setting','is_right','mobile');
}
/** 设置用户支付密码
* @param $user_id
* @param $code
* @param $password
* @param bool $trans
* @return bool
* @throws \Exception
*/
public static function setPayPassword($user_id, $code,$password,$trans=false,$check_sms=true){
$user = self::where('id',$user_id)->find();
if(!$user) throw new \Exception("用户不存在!");
if($check_sms){
$ret = Sms::check($user['mobile'], $code, 'pay_password');
if (!$ret)throw new \Exception("验证码错误");
}
if(!is_numeric($password) || strlen($password)!=6)throw new \Exception("支付密碼只能設置六位數字!");
//判断逻辑
if($trans){
self::beginTrans();
}
$res = true;
try{
$auth = Auth::instance();
//事务逻辑
$user['pay_salt'] = Random::alnum();
$user['pay_password'] = $auth->getEncryptPassword($password, $user['pay_salt']);
$user->save();
//清除验证码
if($check_sms)Sms::flush($user['mobile'], 'pay_password');
if($trans){
self::commitTrans();
}
}catch (\Exception $e){
if($trans){
self::rollbackTrans();
}
throw new \Exception($e->getMessage());
}
return $res;
}
}

View File

@ -243,9 +243,9 @@ class ClassesLib extends Model
public function detail($id){
$self = $this->get($id,['shop','teacher']);
//下架判断
if($self['status'] != '1'){
$this->error("该课程已下架");
}
// if($self['status'] != '1'){
// $this->error("该课程已下架");
// }
//参与人数 = 虚拟人数 + 平台人数
return $self;

View File

@ -2,10 +2,18 @@
namespace app\common\model\school\classes\order;
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 Model
class Order extends BaseModel
{
use SoftDelete;
@ -160,25 +168,25 @@ class Order extends Model
public function user()
{
return $this->belongsTo('app\admin\model\User', 'user_id', 'id', [], 'LEFT')->setEagerlyType(0);
return $this->belongsTo('app\common\model\User', 'user_id', 'id', [], 'LEFT')->setEagerlyType(0);
}
public function shop()
{
return $this->belongsTo('app\admin\model\manystore\Shop', 'shop_id', 'id', [], 'LEFT')->setEagerlyType(0);
return $this->belongsTo(ManystoreShop::class, 'shop_id', 'id', [], 'LEFT')->setEagerlyType(0);
}
public function lib()
{
return $this->belongsTo('app\admin\model\school\classes\Lib', 'classes_lib_id', 'id', [], 'LEFT')->setEagerlyType(0);
return $this->belongsTo(ClassesLib::class, 'classes_lib_id', 'id', [], 'LEFT')->setEagerlyType(0);
}
public function detail()
{
return $this->belongsTo('Detail', 'classes_order_detail_id', 'id', [], 'LEFT')->setEagerlyType(0);
return $this->belongsTo(OrderDetail::class, 'classes_order_detail_id', 'id', [], 'LEFT')->setEagerlyType(0);
}
@ -186,4 +194,519 @@ class Order extends Model
{
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){
$data = self::where('order_no|id|pay_no',$order_no)->find();
if(!$data) return $data;
//加载订单详情
$data->detail->teacher;
//订单用户
$data->user;
//订单机构
$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订单执行创建
$this->createOrder($user_id,$orderInfo['classes_lib_id'],$order_no,$orderInfo['param'],$remark);
//5删除缓存
self::deleteOrderCache($user_id, $order_no);
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);
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 ||!$param )throw new \Exception("缺少必要参数");
//默认校验订单是否已创建
if($check){
//判断订单是否已创建
$order_info = self::where(['order_no'=>$order_no]);
if($order_info) throw new \Exception("订单已生成,如需重新下单请退出页面重新进入!");
}
//校验订单参数
//课程是否存在并上架
$classes_lib_info = ClassesLib::where('classes_lib_id',$classes_lib_id);
if(!$classes_lib_info || $classes_lib_info['status']!='1') throw new \Exception("该课程不存在或已下架!");
//用户存不存在
$user_info = User::where('user_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',$order_no)->where("status",'0')->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);
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,
]);
//一维码
$order->codeoneimage = Common::getBarcode([
'text' => $params,
'size' => 200,
]);
$order->save();
return $order;
}
}

View File

@ -2,6 +2,7 @@
namespace app\common\model\school\classes\order;
use app\common\model\dyqc\ManystoreShop;
use think\Model;
use traits\model\SoftDelete;
@ -74,9 +75,9 @@ class OrderDetail extends Model
public function order()
public function classorder()
{
return $this->belongsTo('app\admin\model\school\classes\Order', 'classes_order_id', 'id', [], 'LEFT')->setEagerlyType(0);
return $this->belongsTo(Order::class, 'classes_order_id', 'id', [], 'LEFT')->setEagerlyType(0);
}
@ -88,7 +89,7 @@ class OrderDetail extends Model
public function shop()
{
return $this->belongsTo('app\admin\model\manystore\Shop', 'shop_id', 'id', [], 'LEFT')->setEagerlyType(0);
return $this->belongsTo(ManystoreShop::class, 'shop_id', 'id', [], 'LEFT')->setEagerlyType(0);
}
@ -96,4 +97,9 @@ class OrderDetail extends Model
{
return $this->belongsTo('app\admin\model\User', 'user_id', 'id', [], 'LEFT')->setEagerlyType(0);
}
public function teacher()
{
return $this->belongsTo('app\common\model\school\classes\Teacher', 'teacher_id', 'id', [], 'LEFT')->setEagerlyType(0);
}
}

View File

@ -92,4 +92,49 @@ class OrderLog extends Model
{
return $this->belongsTo('app\admin\model\school\classes\Order', 'classes_order_id', 'id', [], 'LEFT')->setEagerlyType(0);
}
/**记录订单日志
* @param $params
* @param bool $trans
* @throws \Exception
*/
public static function log($order,$mark='更新订单状态',$oper_type='user',$oper_id = 0,$trans=false){
if(is_numeric($order))$order = Order::where('order_no|id|pay_no',$order)->find();
if(!$order)throw new \Exception("找不到订单");
//操作人信息(可扩展)
$data = [
'oper_type'=>$oper_type ?: 'user',
'oper_id'=>$oper_id ?: $order['user_id'],
'remark'=>$mark,
];
//判断逻辑
if($trans){
self::beginTrans();
}
$res = true;
try{
//事务逻辑
$log_data = $order->toArray();
$log_data["classes_order_id"] = $order['id'];
unset($log_data['id']);
unset($log_data['createtime']);
if($mark)$log_data['log_text'] = $mark;
$log = (new self);
$log->allowField(true)->save($log_data);
if($trans){
self::commitTrans();
}
}catch (\Exception $e){
if($trans){
self::rollbackTrans();
}
throw new \Exception($e->getMessage());
}
return $log;
}
}