crmeb
最后更新于:2022-04-02 04:33:57
## crmeb
[TOC=3,8]
----
### 订单 与 支付
#### 订单表
```sql
CREATE TABLE IF NOT EXISTS `eb_store_order` (
`paid` tinyint(1) NOT NULL DEFAULT '0' COMMENT '支付状态 0: 未支付 1:已支付',
`pay_time` int(11) UNSIGNED NOT NULL DEFAULT '0' COMMENT '支付时间',
`pay_type` varchar(32) NOT NULL DEFAULT '' COMMENT '支付方式',
`add_time` int(11) UNSIGNED NOT NULL DEFAULT '0' COMMENT '创建时间',
`status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '订单状态(-1 : 申请退款 -2 : 退货成功 0:待发货;1:待收货;2:已收货;3:待评价;-1:已退款)',
`refund_status` tinyint(1) UNSIGNED NOT NULL DEFAULT '0' COMMENT '0 未退款 1 申请中 2 已退款',
`refund_type` tinyint(1) NOT NULL DEFAULT '0' COMMENT '退款申请类型',
`refund_express` varchar(255) NOT NULL DEFAULT '' COMMENT '退货快递单号',
`refund_express_name` varchar(255) NOT NULL DEFAULT '' COMMENT '退货快递名称',
`refund_reason_wap_img` varchar(2000) NOT NULL DEFAULT '' COMMENT '退款图片',
`refund_reason_wap_explain` varchar(255) NOT NULL DEFAULT '' COMMENT '退款用户说明',
`refund_reason_time` int(11) UNSIGNED NOT NULL DEFAULT '0' COMMENT '退款时间',
`refund_reason_wap` varchar(255) NOT NULL DEFAULT '' COMMENT '前台退款原因',
`refund_reason` varchar(255) NOT NULL DEFAULT '' COMMENT '不退款的理由',
`refund_price` decimal(8,2) UNSIGNED NOT NULL DEFAULT '0.00' COMMENT '退款金额',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='订单表';
```
#### 分析
支付回执
AliPayService.php
```php
public static function handleNotify()
{
return self::instance()->notify(function ($notify) {
if (isset($notify->out_trade_no)) {
if (isset($notify->attach) && $notify->attach) {
if (($count = strpos($notify->out_trade_no, '_')) !== false) {
$notify->trade_no = $notify->out_trade_no;
$notify->out_trade_no = substr($notify->out_trade_no, $count + 1);
}
return (new Hook(PayNotifyServices::class, 'aliyun'))->listen($notify->attach, $notify->out_trade_no, $notify->trade_no);
}
return false;
}
});
}
public function notify(callable $notifyFn)
{
app()->request->filter(['trim']);
//商户订单号
$postOrder['out_trade_no'] = $paramInfo['out_trade_no'] ?? '';
//支付宝交易号
$postOrder['trade_no'] = $paramInfo['trade_no'] ?? '';
//交易状态
$postOrder['trade_status'] = $paramInfo['trade_status'] ?? '';
//备注
$postOrder['attach'] = isset($paramInfo['passback_params']) ? urldecode($paramInfo['passback_params']) : '';
if (in_array($paramInfo['trade_status'], ['TRADE_SUCCESS', 'TRADE_FINISHED']) && $this->verifyNotify($paramInfo)) {
try {
if ($notifyFn((object)$postOrder)) {
return 'success';
}
} catch (\Exception $e) {
Log::error('支付宝异步会回调成功,执行函数错误。错误单号:' . $postOrder['out_trade_no']);
}
}
return 'fail';
}
```
业务回执处理
PayNotifyServices.php
```php
public function aliyunProduct(string $order_id = null, string $trade_no = null)
{
if (!$order_id || !$trade_no) {
return false;
}
try {
/** @var StoreOrderSuccessServices $services */
$services = app()->make(StoreOrderSuccessServices::class);
$orderInfo = $services->getOne(['order_id' => $order_id]);
if (!$orderInfo) return true;
if ($orderInfo->paid) return true;
return $services->paySuccess($orderInfo->toArray(), PayServices::ALIAPY_PAY, ['trade_no' => $trade_no]);
} catch (\Throwable $e) {
return false;
}
}
```
StoreOrderSuccessServices.php
```php
public function paySuccess(array $orderInfo, string $paytype = PayServices::WEIXIN_PAY, array $other = [])
{
$updata = ['paid' => 1, 'pay_type' => $paytype, 'pay_time' => time()];
if ($other && isset($other['trade_no'])) {
$updata['trade_no'] = $other['trade_no'];
}
$res1 = $this->dao->update($orderInfo['id'], $updata);
$resPink = true;
if ($orderInfo['combination_id'] && $res1 && !$orderInfo['refund_status']) {
/** @var StorePinkServices $pinkServices */
$pinkServices = app()->make(StorePinkServices::class);
/** @var StoreOrderServices $orderServices */
$orderServices = app()->make(StoreOrderServices::class);
$resPink = $pinkServices->createPink($orderServices->tidyOrder($orderInfo, true));//创建拼团
}
//缓存抽奖次数 除过线下支付
if (isset($orderInfo['pay_type']) && $orderInfo['pay_type'] != 'offline') {
/** @var LuckLotteryServices $luckLotteryServices */
$luckLotteryServices = app()->make(LuckLotteryServices::class);
$luckLotteryServices->setCacheLotteryNum((int)$orderInfo['uid'], 'order');
}
//订单支付成功后置事件
event('order.orderPaySuccess', [$orderInfo]);
//用户推送消息事件
event('notice.notice', [$orderInfo, 'order_pay_success']);
//支付成功给客服发送消息
event('notice.notice', [$orderInfo, 'admin_pay_success_code']);
$res = $res1 && $resPink;
return false !== $res;
}
```
#### 总结
1. 只在 PayNotifyServices 成功 才对 三方 响应 'success'
2. PayNotifyServices 成功
a. 订单不存在 true
b. 订单已支付 true
c. StoreOrderSuccessServices->paySuccess() true
a. 更新订单支付状态 成功
b. 订单支付成功后置事件 结果不关心
1. 只在 PayNotifyServices 成功 才对 三方 响应 'success'
2. PayNotifyServices 成功
a. 订单不存在 true
b. 订单已支付 true
c. wechatUserRecharge->rechargeSuccess() true
a. 更新订单 true
b. 更新用户余额
b. 订单支付成功后置事件 结果不关心
不过任何 异常 代码错误,都会使 PayNotifyServices false ,所以如果 代码问题,业务履行失败 都不会 对三方响应 'success'
----
### 架构
Dao 基于模型对数据获取、操作进行封装,Services 使用 Dao 进行数据操作。调用 Services 上不存在的方法 会调用 到 其 Dao 上,所以 Services 可看作 是 “继承” Dao 的。
优点:将 SQL 拼装 从 业务层拆分到 Dao 上了,Services 层的业务成分就更明显了。
缺点:由于优点,于是 业务也被分散到 Services 和 Dao 上了,有点不清晰,只能说 Dao 的 业务成分 没有 Services 明显,但并不是完全没有。
结论:
1. 所以 Services + Dao = 我们 的 Logic
2. Services > Dao > Model ;我们 Logic > Model
PS: ~~我们的 Logic、Service 层确实没什么区别,区分的理由实在太过牵强,没有区分的意义。~~(有待商榷,Logic 注重特定的业务场景 如 User,Service 纯技术方案,无特定业务场景特征,如 JWT)
----
```php
abstract class BaseServices
{
public function __call($name, $arguments)
{
// TODO: Implement __call() method.
return call_user_func_array([$this->dao, $name], $arguments);
}
}
class StoreOrderServices extends BaseServices
{
public function setOrderTypePayOffline(string $orderId)
{
return $this->dao->update($orderId, ['pay_type' => 'offline'], 'order_id');
}
public function removeOrder(string $uni, int $uid)
{
$order = $this->getUserOrderDetail($uni, $uid);
if (!$order) {
throw new ValidateException('订单不存在!');
}
...
}
}
```
```php
abstract class BaseDao
{
abstract protected function setModel(): string;
protected function getModel()
{
return app()->make($this->setModel());
}
protected function getPk()
{
return $this->getModel()->getPk();
}
public function update($id, array $data, ?string $key = null)
{
if (is_array($id)) {
$where = $id;
} else {
$where = [is_null($key) ? $this->getPk() : $key => $id];
}
return $this->getModel()::update($data, $where);
}
}
class StoreOrderDao extends BaseDao
{
protected function setModel(): string
{
return StoreOrder::class;
}
public function getUserOrderDetail(string $key, int $uid, $with = [])
{
return $this->getOne(['order_id|unique' => $key, 'uid' => $uid, 'is_del' => 0], '*', $with);
}
public function batchUpdateOrder(array $ids, array $data, ?string $key = null)
{
return $this->getModel()::whereIn(is_null($key) ? $this->getPk() : $key, $ids)->update($data);
}
}
```
----
[分层 DAO层,Service层,Controller层、View层 - 简书](https://www.jianshu.com/p/df659d7fbbf7)
> DAO完成连接数据库修改删除添加等的实现细节,例如sql语句是怎么写的,怎么把对象放入数据库的。service层是面向功能的,一个个功能模块比如说银行登记并完成一次存款,UI要把请求给service层,然后service曾将这一个case分解成许多步骤调用底层的实现完成这次存款,dao就是下面那层。
';