阅读前请先下载项目源码,边读边看源码以加深理解和实操,
源码地址已放于文章末尾!
效果预览:


第八章:核心功能攻坚:下单、支付与“金钱永不眠”
各位同学,请带好你的“氧气瓶”,因为我们即将挑战整个陪玩App项目中最复杂、最核心、也是最能体现商业价值的业务流程——下单与支付。
这个流程,就像人体的“消化系统”,一头连接着用户的“购买欲望”,另一头连接着公司的“银行账户”。它涉及到前后端的紧密配合、数据库的事务处理、以及与第三方支付平台的“亲密互动”。可以说,搞定了它,你对商业项目的理解,将直接提升一个段位。
今天,我们就来把这个“硬骨头”给啃下来,用一张清晰的流程图和前后端核心代码,把“金钱之旅”的每一个细节都安排得明明白白。
一、宏观视角:一次“金钱之旅”的完整路径
在钻进代码的“战壕”之前,我们必须先从“卫星”视角,鸟瞰一下整个下单支付流程的全貌。这能帮助我们建立起清晰的逻辑坐标,不至于在代码细节中迷失方向。
graph TD
subgraph A[前端 App]
A1(用户在陪玩师详情页点击“立即下单”) --> A2(选择服务时长、确认订单信息);
A2 --> A3(点击“确认支付”,调用“创建订单”API);
A3 --> A4(API返回订单号和支付参数);
A4 --> A5(拉起支付网关的支付页面);
A5 -- 用户完成支付 --> A6(支付成功,跳转到订单详情页);
A5 -- 用户取消支付 --> A7(支付失败,停留在订单确认页);
end
subgraph B[后端 API]
B1("“创建订单”API") --> B2{检查库存/陪玩师状态};
B2 -- "状态正常" --> B3(计算订单总价);
B3 --> B4[在数据库`fa_order`表中插入一条记录,状态为“待支付”];
B4 --> B5(调用第三方支付SDK,生成支付参数);
B5 --> B6(将订单号和支付参数返回给前端);
B2 -- "状态异常" --> B7(返回错误信息);
B8("支付回调接口 (Notify)") -- 异步通知 --> B9{验证签名,确认通知来源可靠};
B9 -- "验证通过" --> B10(根据订单号,更新`fa_order`表中的状态为“已支付”);
B10 --> B11(为用户增加相应的服务时长或余额);
B11 --> B12(给陪玩师账户增加对应的提成金额);
B12 --> B13(返回“成功”响应给支付平台);
end
subgraph C[第三方支付平台]
C1(支付页面) -- "用户支付成功" --> C2(向后端API的“支付回调接口”发送异步通知);
end
A3 --> B1;
B6 --> A4;
A5 --> C1;
C2 -.-> B8;
看懂了吗?整个流程分为两条线:
- 主线任务(用户操作流): 用户从点击下单到完成支付的整个过程。这条线是同步的,用户能实时感知到。
- 支线任务(支付回调流): 支付平台在确认用户付钱成功后,从“背后”悄悄地通知我们服务器的过程。这条线是异步的,用户是无感知的,但对保证数据一致性至关重要。
二、后端“铸币厂”:创建订单API
现在,我们聚焦于后端的“创建订单”API。它就像一个“铸币厂”,负责生产出每一笔交易的“凭证”——订单。
1. 代码位置
文件路径: /application/api/controller/Order.php (新建一个订单控制器)
2. 代码片段
// 文件路径:/application/api/controller/Order.php
<?php
namespace app\api\controller;
use app\common\controller\Api;
use think\Db;
class Order extends Api
{
protected $noNeedLogin = []; // 定义哪些方法不需要登录
/**
* 创建订单
*/
public function createOrder()
{
// 接收前端传来的参数
$clerk_id = $this->request->request('clerk_id'); // 陪玩师ID
$category_id = $this->request->request('category_id'); // 服务类目ID
if (!$clerk_id || !$category_id) {
$this->error('参数不完整');
}
// 核心逻辑
// 1. 获取用户信息 (Api基类中已封装好,通过 $this->auth->id 获取当前登录用户ID)
$user_id = $this->auth->id;
// 2. 查询服务价格
$price = Db::name('service_price')->where('category_id', $category_id)->value('price');
if (!$price) {
$this->error('服务不存在或未定价');
}
// 3. 生成订单数据
$order_sn = date('YmdHis') . mt_rand(1000, 9999); // 生成一个简单的订单号
$data = [
'order_sn' => $order_sn,
'user_id' => $user_id,
'clerk_id' => $clerk_id,
'category_id' => $category_id,
'amount' => $price,
'status' => 0, // 0: 待支付
'createtime' => time(),
];
// 4. 写入数据库 (使用数据库事务)
Db::startTrans();
try {
$order_id = Db::name('order')->insertGetId($data);
// 此处可以继续执行其他数据库操作,比如扣减优惠券等
Db::commit();
} catch (\Exception $e) {
Db::rollback();
$this->error('订单创建失败,请重试');
}
// 5. 调用支付,生成支付参数 (此处为伪代码)
// $payParams = PayService::getPayParams($order_sn, $price, 'alipay');
$this->success('订单创建成功', [
'order_id' => $order_id,
'order_sn' => $order_sn,
// 'pay_params' => $payParams
]);
}
}
3. 结构化分析
这个接口是交易流程的起点。它负责整合下单所需的所有信息(谁买、买谁的、买什么),在数据库中生成一条初始状态的订单记录,并为后续的支付环节做好准备。
4. 逐行/逐块详解
protected $noNeedLogin = [];:Api基类里有一个机制,会自动检查所有接口的登录状态。默认所有接口都需要登录。如果某个方法(比如登录、注册)不需要登录,就需要把它写在这个数组里。我们创建订单是需要登录的,所以这里是空数组。$user_id = $this->auth->id;:Api基类在初始化时,已经通过前端传来的Token,验证了用户的合法性,并把用户信息存放在了$this->auth对象里。所以我们可以直接拿到当前操作用户的ID。$order_sn = ...: 生成一个唯一的订单号,是订单的“身份证号”。Db::startTrans(); ... Db::commit(); ... Db::rollback();: 这是保证数据一致性的“杀手锏”——数据库事务! 想象一下,一个完整的下单操作可能既要创建订单,又要扣减用户的优惠券。如果在创建完订单后,扣减优惠券时程序报错了,怎么办?用户没用成优惠券,但订单却生成了,数据就不一致了。Db::startTrans(): 开启一个事务,就像对数据库说:“老哥,我接下来要干一连串的活儿,你先别急着保存。”Db::commit(): 如果try代码块里的所有操作都顺利完成,就执行“提交”,让所有改动永久生效。Db::rollback(): 如果try代码块里任何一个环节出了错(catch到了异常),就执行“回滚”,把这次事务里所有的操作都撤销,恢复到事务开始前的状态,就像什么都没发生过一样。
三、总结
今天我们攻克了项目中含金量最高的一个模块。我们不仅用流程图梳理了复杂的下单支付逻辑,还亲手编写了创建订单的核心API,并且学习了数据库事务这个保证系统稳定性的“大杀器”。
虽然我们没有深入到真实的支付SDK对接,但核心的业务逻辑和数据流转已经了然于胸。支付回调接口的逻辑,其实就是对数据库状态的更新,原理与创建订单类似。
至此,我们App的核心业务闭环(用户 -> 浏览 -> 下单 -> 支付)已经基本打通。
在下一章 《后台管理系统:手握“上帝视角”,掌控App宇宙》 中,我们将把目光投向项目的“幕后”,看看如何为运营人员开发一个强大而易用的后台,来管理我们App里所有的用户、订单和陪玩师。
准备好体验“掌控一切”的快感了吗?我们下章揭晓!

被折叠的 条评论
为什么被折叠?



