{{wikiTitle}}
事件说明
CRMEB PRO 事件说明
概述
CRMEB PRO 使用 ThinkPHP 8.0 的事件机制实现业务解耦。事件系统允许你在不修改核心代码的情况下,通过监听器响应特定的业务动作。
事件配置文件
事件定义位于 app/event.php 文件中:
<?php
// 事件定义文件
return [
'bind' => [
// 事件绑定
],
'listen' => [
// 事件监听
'AppInit' => [],
'HttpRun' => [],
'HttpEnd' => [\app\listener\system\http\HttpEnd::class],
// ... 更多事件
],
'subscribe' => [
// 事件订阅
],
];
系统事件列表
Swoole相关事件
| 事件名称 | 说明 | 监听器 |
|---|---|---|
swoole.init |
Swoole初始化 | InitSwooleLockListen |
swoole.workerStart |
Worker启动 | - |
swoole.workerExit |
Worker退出 | - |
swoole.workerError |
Worker错误 | - |
swoole.workerStop |
Worker停止 | - |
swoole.shutDown |
Swoole关闭 | SwooleShutdownListen |
swoole.websocket.user |
用户WebSocket | UserHandler |
swoole.websocket.admin |
后台WebSocket | AdminHandler |
swoole.websocket.kefu |
客服WebSocket | KefuHandler |
swoole.websocket.supplier |
供应商WebSocket | SupplierHandler |
用户相关事件
| 事件名称 | 说明 | 监听器 |
|---|---|---|
user.register |
用户注册 | Register |
user.cancelUser |
用户注销 | CancelUser |
user.login |
用户登录 | Login |
user.vipPay |
VIP支付成功 | VipUser |
user.recharge |
充值支付成功 | Recharge |
user.rechargeRefund |
充值退款成功 | RechargeRefund |
user.extract |
申请提现 | Extract |
user.work |
绑定企业微信 | UserBindWorkMember |
user.create |
用户创建 | CreateSuccess |
user.update |
用户更新 | UpdateSuccess |
user.status |
用户状态修改 | - |
user.client |
绑定企业微信客户 | UserBindClient |
user.member.update |
会员更新 | MemberUpdateSuccess |
user.address.update |
地址更新 | AddressUpdateSuccess |
user.activate.level |
激活会员卡 | ActivateLevel |
user.auto.label |
自动打标签 | AutoLabelListener |
支付相关事件
| 事件名称 | 说明 | 监听器 |
|---|---|---|
pay.notify |
支付异步回调 | PayNotifyListener |
pay.mchNotify |
商家转账回调 | PayMchNotifyListener |
pay.scan.notify |
扫码支付回调 | ScannedNotifyListener |
pay.refunded.notify |
退款结果回调 | - |
订单相关事件
| 事件名称 | 说明 | 监听器 |
|---|---|---|
cart.add |
加入购物车 | AddCart |
order.create |
订单创建 | Create |
order.price |
订单改价 | PriceRevision |
order.applyRefund |
申请退款 | ApplyRefund |
order.refuseRefund |
拒绝退款 | RefuseRefund |
order.refund |
订单退款 | Refund |
order.pay |
订单支付 | Pay |
order.delivery |
订单发货 | Delivery |
order.take |
订单收货 | Take |
order.comment |
订单评价 | Comment |
order.cancel |
订单取消 | Cancel |
order.writeoff |
订单核销 | Writeoff |
order.routine.shipping |
小程序发货 | OrderShipping |
商品相关事件
| 事件名称 | 说明 | 监听器 |
|---|---|---|
product.create |
商品创建 | CreateSuccess |
product.delete |
商品删除 | DeleteSuccess |
product.status |
商品状态 | StatusSuccess |
product.reply |
商品评价 | Reply |
product.collect |
商品收藏 | Collect |
product.reply.update |
评论更新 | ReplyUpdateSuccess |
product.shipping.update |
运费模板更新 | ShippingUpdateSuccess |
product.stock.create |
出入库添加 | Stock |
product.stock.record |
库存变动记录 | StockRecords |
系统相关事件
| 事件名称 | 说明 | 监听器 |
|---|---|---|
HttpEnd |
HTTP请求结束 | HttpEnd |
crontab |
定时任务 | SystemTimer |
notice.notice |
消息通知 | Notice |
sms.sendAfter |
短信发送后 | SendAfterSuccess |
admin.login |
后台登录 | LoginSuccess |
admin.logout |
后台退出 | LogoutSuccess |
diy.update |
DIY更新 | DiyUpdateSuccess |
config.create |
创建配置 | CreateSuccess |
config.delete |
删除配置 | DeleteSuccess |
config.status |
配置状态 | StatusSuccess |
门店/供应商相关事件
| 事件名称 | 说明 | 监听器 |
|---|---|---|
store.create |
创建门店 | StoreSuccess |
store.delete |
删除门店 | DeleteSuccess |
store.status |
门店状态 | StatusSuccess |
supplier.verify |
供应商审核 | ApplyVerify |
supplier.create |
创建供应商 | SupplierSuccess |
supplier.delete |
删除供应商 | DeleteSuccess |
supplier.status |
供应商状态 | StatusSuccess |
社区相关事件
| 事件名称 | 说明 | 监听器 |
|---|---|---|
community.operate |
帖子操作 | CommunityOperate |
community.delete |
帖子删除 | CommunityDelete |
community.like |
帖子点赞 | CommunityLike |
community.comment.operate |
评论操作 | CommunityCommentOperate |
community.comment.delete |
评论删除 | CommunityCommentDelete |
企业微信相关事件
| 事件名称 | 说明 | 监听器 |
|---|---|---|
work.welcome |
欢迎语 | WelcomeSendListener |
work.label |
客户标签 | ClientLabelListener |
work.user |
客户绑定用户 | ClientBindUserListener |
work.message |
应用消息 | MessageSendListener |
对外推送事件
| 事件名称 | 说明 | 监听器 |
|---|---|---|
out.outPush |
对外推送 | OutPush |
监听器实现
监听器目录结构
app/listener/
├── activity/ # 活动监听器
├── community/ # 社区监听器
├── erp/ # ERP监听器
├── notice/ # 通知监听器
├── order/ # 订单监听器
├── out/ # 对外推送监听器
├── pay/ # 支付监听器
├── product/ # 商品监听器
├── store/ # 门店监听器
├── supplier/ # 供应商监听器
├── system/ # 系统监听器
├── user/ # 用户监听器
├── wechat/ # 微信监听器
└── work/ # 企业微信监听器
监听器示例代码
订单支付监听器
<?php
// app/listener/order/Pay.php
namespace app\listener\order;
use app\services\order\StoreOrderServices;
use crmeb\interfaces\ListenerInterface;
class Pay implements ListenerInterface
{
/**
* 订单支付成功事件
* @param $event
* @return void
*/
public function handle($event): void
{
[$orderInfo] = $event;
try {
/** @var StoreOrderServices $orderServices */
$orderServices = app()->make(StoreOrderServices::class);
// 处理订单支付成功后的业务逻辑
// 1. 更新订单状态
// 2. 发送通知
// 3. 赠送积分
// 4. 计算佣金
// 5. 其他业务处理
} catch (\Throwable $e) {
// 记录日志
\think\facade\Log::error('订单支付事件处理失败:' . $e->getMessage());
}
}
}
用户注册监听器
<?php
// app/listener/user/Register.php
namespace app\listener\user;
use crmeb\interfaces\ListenerInterface;
class Register implements ListenerInterface
{
/**
* 用户注册事件
* @param $event
* @return void
*/
public function handle($event): void
{
[$user, $spreadUid] = $event;
try {
// 处理用户注册后的业务
// 1. 绑定推广关系
// 2. 发送欢迎消息
// 3. 赠送新人优惠券
// 4. 记录注册日志
} catch (\Throwable $e) {
\think\facade\Log::error('用户注册事件处理失败:' . $e->getMessage());
}
}
}
支付通知监听器
<?php
// app/listener/pay/PayNotifyListener.php
namespace app\listener\pay;
use crmeb\interfaces\ListenerInterface;
use app\services\pay\PayNotifyServices;
class PayNotifyListener implements ListenerInterface
{
/**
* 支付异步通知事件
* @param $event
* @return void
*/
public function handle($event): void
{
[$orderInfo, $payType] = $event;
/** @var PayNotifyServices $payNotifyServices */
$payNotifyServices = app()->make(PayNotifyServices::class);
// 根据订单类型处理不同的支付回调
$payNotifyServices->handleNotify($orderInfo, $payType);
}
}
触发事件
使用 event() 函数触发
// 触发订单支付事件
event('order.pay', [$orderInfo]);
// 触发用户注册事件
event('user.register', [$user, $spreadUid]);
// 触发商品创建事件
event('product.create', [$productInfo]);
使用 Event 门面触发
use think\facade\Event;
// 触发事件
Event::trigger('order.pay', [$orderInfo]);
// 触发并获取返回值
$result = Event::trigger('pay.notify', [$orderInfo, $payType], true);
自定义事件
1. 定义事件
在 app/event.php 中注册新事件:
'listen' => [
// ... 其他事件
// 自定义事件
'custom.myEvent' => [\app\listener\custom\MyEventListener::class],
],
2. 创建监听器
创建监听器文件 app/listener/custom/MyEventListener.php:
<?php
namespace app\listener\custom;
use crmeb\interfaces\ListenerInterface;
class MyEventListener implements ListenerInterface
{
/**
* 处理自定义事件
* @param $event
* @return void
*/
public function handle($event): void
{
[$data] = $event;
// 处理业务逻辑
$this->processData($data);
}
/**
* 处理数据
* @param $data
*/
protected function processData($data)
{
// 具体业务处理
}
}
3. 触发事件
// 在需要的地方触发事件
event('custom.myEvent', [$data]);
监听器接口
所有监听器都应实现 ListenerInterface 接口:
<?php
// crmeb/interfaces/ListenerInterface.php
namespace crmeb\interfaces;
interface ListenerInterface
{
/**
* 事件处理方法
* @param $event
* @return mixed
*/
public function handle($event);
}
事件订阅者
对于需要监听多个事件的类,可以使用事件订阅者:
<?php
namespace app\subscribe;
class OrderSubscribe
{
/**
* 订阅事件
* @param $events
*/
public function subscribe($events)
{
// 监听订单创建
$events->listen('order.create', [$this, 'onCreate']);
// 监听订单支付
$events->listen('order.pay', [$this, 'onPay']);
// 监听订单发货
$events->listen('order.delivery', [$this, 'onDelivery']);
}
public function onCreate($event)
{
// 处理订单创建
}
public function onPay($event)
{
// 处理订单支付
}
public function onDelivery($event)
{
// 处理订单发货
}
}
在 app/event.php 中注册订阅者:
'subscribe' => [
\app\subscribe\OrderSubscribe::class,
],
定时任务事件
CRMEB PRO 使用 Swoole 的定时器实现定时任务:
// app/event.php
'crontab' => [
\app\listener\system\timer\SystemTimer::class,
],
定时任务监听器示例:
<?php
// app/listener/system/timer/SystemTimer.php
namespace app\listener\system\timer;
use crmeb\interfaces\ListenerInterface;
class SystemTimer implements ListenerInterface
{
public function handle($event): void
{
// 每分钟执行的任务
$this->everyMinute();
// 每小时执行的任务
$this->everyHour();
// 每天执行的任务
$this->everyDay();
}
protected function everyMinute()
{
// 订单超时未支付自动取消
// 拼团超时自动处理
}
protected function everyHour()
{
// 清理临时文件
// 同步数据
}
protected function everyDay()
{
// 统计数据
// 清理过期数据
}
}
事件使用最佳实践
1. 保持监听器简洁
监听器应该只负责调度,具体业务逻辑放在 Services 层:
public function handle($event): void
{
[$orderInfo] = $event;
// 调用服务层处理业务
app()->make(OrderPayServices::class)->handlePaySuccess($orderInfo);
}
2. 异常处理
监听器中要做好异常处理,避免影响主流程:
public function handle($event): void
{
try {
// 业务处理
} catch (\Throwable $e) {
\think\facade\Log::error('事件处理异常:' . $e->getMessage());
}
}
3. 使用队列处理耗时操作
对于耗时操作,建议在监听器中投递队列任务:
public function handle($event): void
{
[$orderInfo] = $event;
// 投递到队列异步处理
\crmeb\utils\Queue::instance()->job(\app\jobs\order\OrderPayJob::class)
->data($orderInfo)
->push();
}
4. 事件参数传递
事件参数建议使用数组传递,便于扩展:
// 触发事件时
event('order.pay', [
'orderInfo' => $orderInfo,
'payType' => $payType,
'payTime' => time()
]);
// 监听器中
public function handle($event): void
{
$orderInfo = $event['orderInfo'] ?? null;
$payType = $event['payType'] ?? '';
$payTime = $event['payTime'] ?? 0;
}
调试事件
查看事件触发日志
// 在事件触发前后记录日志
\think\facade\Log::info('触发事件:order.pay', ['order_id' => $orderInfo['order_id']]);
event('order.pay', [$orderInfo]);
\think\facade\Log::info('事件处理完成:order.pay');
事件断点调试
在监听器中设置断点,使用 Xdebug 等工具调试。
注意事项
- 事件命名规范:事件名称建议使用
模块.动作的格式(如order.pay、user.register),保持命名一致性 - 监听器执行顺序:同一事件的多个监听器按照
event.php中的注册顺序执行,如需控制顺序请合理安排配置 - 避免循环触发:监听器中触发新事件时要注意避免形成循环,可能导致死循环或栈溢出
- 耗时操作异步化:监听器中的耗时操作(如发送短信、邮件、外部API调用)应投递到队列异步处理,避免阻塞主流程
- 异常处理:监听器中的异常默认会向上抛出,建议在监听器内部捕获异常并记录日志,避免影响其他监听器执行
- 事件参数不可变:传递给事件的参数在监听器中修改不会影响其他监听器,如需共享状态请使用引用或其他方式
- Swoole 环境注意:在 Swoole 环境下,事件监听器中使用的静态变量需要注意协程安全问题
- 事件订阅者类:复杂业务场景可以使用事件订阅者类统一管理相关事件,提高代码组织性
常见问题
Q: 为什么事件触发后监听器没有执行?
A: 检查以下几点:1) 确认监听器已在app/event.php中正确注册;2) 确认事件名称拼写正确;3) 检查监听器类的命名空间和类名是否正确;4) 确认监听器的handle方法签名正确Q: 如何判断某个事件有哪些监听器?
A: 查看app/event.php配置文件中的listen数组,找到对应事件名称下注册的监听器列表Q: 监听器执行出错会影响主业务流程吗?
A: 如果监听器中抛出未捕获的异常,会中断后续监听器的执行并影响主流程。建议在监听器中使用 try-catch 捕获异常Q: 如何在监听器中获取依赖注入的服务?
A: 监听器支持构造函数依赖注入,可以在构造函数中声明需要的服务类,框架会自动注入Q: 事件参数传递数组和对象有什么区别?
A: 传递数组时每个监听器收到的是独立副本,传递对象时所有监听器共享同一个对象引用。根据业务需求选择合适的方式Q: 如何临时禁用某个监听器?
A: 可以在event.php中注释掉对应的监听器配置,或在监听器handle方法开头添加return提前退出
