EventService.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. <?php
  2. //事件分发服务
  3. namespace services\common;
  4. use Closure;
  5. use common\components\BaseEvent;
  6. use common\enums\RedisKeyEnum;
  7. use common\enums\StatusEnum;
  8. use common\helpers\ArrayHelper;
  9. use common\helpers\StringHelper;
  10. use common\interfaces\ListenerInterface;
  11. use common\models\common\AddonsEvent;
  12. use common\models\mall\Mall;
  13. use common\models\mall\MallAddons;
  14. use common\traits\ErrorTrait;
  15. use Exception;
  16. use ReflectionClass;
  17. use Yii;
  18. use yii\base\Component;
  19. use yii\helpers\Json;
  20. class EventService extends Component
  21. {
  22. use ErrorTrait;
  23. protected $listen = [];
  24. public function init(){
  25. parent::init();
  26. }
  27. /**
  28. * 获取listen数组
  29. * @Author bing
  30. * @DateTime 2021-03-01 10:36:55
  31. * @copyright: Copyright (c) 2020 广东七件事集团
  32. * @return array|mixed
  33. */
  34. public function getListen(){
  35. return $this->listen;
  36. }
  37. /**
  38. * 设置listen数组
  39. * @Author bing
  40. * @DateTime 2021-03-01 10:37:27
  41. * @copyright: Copyright (c) 2020 广东七件事集团
  42. * @param array $listen
  43. * @return void
  44. */
  45. public function setListen(array $listen){
  46. $this->listen = $listen;
  47. }
  48. /**
  49. * 事件调度器
  50. * @Author bing
  51. * @DateTime 2021-03-01 10:35:25
  52. * @copyright: Copyright (c) 2020 广东七件事集团
  53. * @param \yii\base\Event $event
  54. * @param [type] $data
  55. * @param [type] $listeners
  56. * @return void
  57. */
  58. public function dispatch(BaseEvent $event, $data = null, $listeners = null){
  59. $listeners = is_object($listeners) ? [$listeners] : (array) $listeners;
  60. $addons_listeners = $this->addonsEventBind($event->name, $event->mall_id ?? 0);
  61. asort($addons_listeners);//将插件监听数据进行排序
  62. $listeners = array_unique(
  63. array_merge(
  64. isset($this->listen[get_class($event)]) ? $this->listen[get_class($event)] : [],
  65. $listeners,
  66. $addons_listeners
  67. )
  68. );
  69. foreach ($listeners as $listener) {
  70. if ($listener instanceof Closure || function_exists($listener)) {
  71. $this->on($event->name, $listener, $data);
  72. continue;
  73. }
  74. if(!class_exists($listener)) continue;
  75. if(empty($event->name)){
  76. $reflect_class = new ReflectionClass($event);
  77. $event->name = $reflect_class->name;
  78. }
  79. $listener = Yii::createObject($listener);
  80. if (! $listener instanceof ListenerInterface) {
  81. throw new Exception(sprintf('The %s muse be implement %s.', get_class($listener), ListenerInterface::class));
  82. }
  83. $this->on($event->name, [$listener, 'handle'], $data);
  84. }
  85. $this->trigger($event->name, $event);
  86. $this->off($event->name);//常驻内存,内存泄漏处理
  87. }
  88. /**
  89. * 获取事件对应的插件
  90. * @Author: lun
  91. * @DateTime: 2022/3/17 0017 17:54
  92. * @Copyright: copyright (c) 2021 广东七件事集团
  93. * @param $event_name
  94. * @param $mall_id
  95. * @return array|mixed
  96. */
  97. public function addonsEventBind($event_name, $mall_id){
  98. // 获取该商城所有有效的插件
  99. $mall_addons = MallAddons::getValidAddons($mall_id);
  100. if (empty($mall_addons)) return [];
  101. // 根据事件名称获取所有属于该事件的所有插件
  102. $redis = Yii::$app->redis;
  103. $redis_key = RedisKeyEnum::suffix(RedisKeyEnum::EVENT_LISTENERS, '',true) .':'. $event_name;
  104. $event_addons = $redis->hgetall($redis_key);// 获取缓存中的事件数据
  105. if (empty($event_addons)) $event_addons = $this->getEventsByMysql($event_name);
  106. // 处理返回的hash数组,$event_addons数组的hash返回格式['Agent','addons\Agent\common\listeners\OrderPaidListener','Boss','addons\Boss\common\listeners\OrderPaidListener']
  107. $event_addons_arr = $event_addons_keys = $event_addons_values = [];
  108. foreach ($event_addons as $key => $value) {
  109. if ($key % 2 == 0) {
  110. $event_addons_keys[] = $value;// 获取该事件的所有key
  111. } else {
  112. $event_addons_values[] = $value;// 获取该事件的所有value
  113. }
  114. }
  115. // 组合为简直相对的数组
  116. foreach ($event_addons_values as $key => $value) {
  117. $event_addons_arr[$event_addons_keys[$key]] = $value;
  118. }
  119. $intersect = array_intersect($mall_addons, $event_addons_keys);// 获取应用名称交集
  120. $listener = '';
  121. foreach($event_addons_arr as $addons_name => $listener_str) {
  122. // 将商城所有属于此事件名称的方法进行字符串组合
  123. if (in_array($addons_name, $intersect)) $listener .= $listener_str . ',';
  124. }
  125. if (empty($listener)) return [];
  126. // 移除右边逗号,再组合成数组返回
  127. return explode(',', rtrim($listener, ','));
  128. }
  129. /**
  130. * 从数据库中读取事件对应的listeners
  131. * @param $event_name
  132. * @return array
  133. */
  134. public function getEventsByMysql($event_name)
  135. {
  136. // 事件为空时,则直接读取数据库
  137. $event_list = AddonsEvent::find()->select('addons_name,event_name,listeners')->where(['event_name' => $event_name, 'status' => StatusEnum::ENABLED])->asArray()->all();
  138. $event_addons = [];
  139. foreach ($event_list as $item) {
  140. $event_addons[] = $item['addons_name'];
  141. $event_addons[] = implode(',', json_decode($item['listeners'], true));
  142. }
  143. return $event_addons;
  144. }
  145. /**
  146. * 创建链接菜单
  147. * @Author: lun
  148. * @DateTime: 2021/10/14 0014 14:16
  149. * @Copyright: copyright (c) 2021 广东七件事集团
  150. * @param string $addons_name
  151. * @throws \yii\db\Exception
  152. */
  153. public function create(string $addons_name, array $eventsMenu)
  154. {
  155. try {
  156. // 重组数组
  157. foreach ($eventsMenu as $key => $menu) {
  158. $allMenu[$key] = ArrayHelper::regroupMapToArr($menu);
  159. }
  160. $time = time();
  161. $event_addons = [];
  162. foreach ($allMenu as $key => $item) {
  163. foreach ($item as $k => $value) {
  164. // 根据事件名保存为数组
  165. $event_addons[$value['event_name']] = implode(',', $value['listeners']);
  166. $listeners = Json::encode($value['listeners']);
  167. $event = AddonsEvent::findOne(['addons_name' => $addons_name, 'event_name' => $value['event_name'], 'status' => StatusEnum::ENABLED]);
  168. if ($event) {
  169. if (Json::encode($event['listeners']) != $listeners) {
  170. $event->listeners = $value['listeners'];
  171. $event->save();
  172. }
  173. } else {
  174. $row = [];
  175. $row['addons_name'] = $addons_name;
  176. $row['event_name'] = $value['event_name'] ?? [];
  177. $row['listeners'] = $listeners;
  178. $row['created_at'] = $time;
  179. $row['updated_at'] = $time;
  180. $rows[] = $row;
  181. }
  182. }
  183. }
  184. if (!empty($rows)) {
  185. $field = ['addons_name', 'event_name', 'listeners', 'created_at', 'updated_at'];
  186. // 批量插入数据
  187. $res = Yii::$app->db->createCommand()->batchInsert(AddonsEvent::tableName(), $field, $rows)->execute();
  188. if (!$res) throw new \yii\base\Exception("事件创建失败");
  189. }
  190. // 获取所有事件再进行组合
  191. $list = AddonsEvent::find()->where(['status' => StatusEnum::ENABLED])->asArray()->all();
  192. $addons_event = [];
  193. foreach ($list as $item) {
  194. $addons_event[$item['event_name']][$item['addons_name']] = implode(',', Json::decode($item['listeners'], true));
  195. }
  196. // 将组合后的事件入存入redis中
  197. $redis = Yii::$app->redis;
  198. foreach ($addons_event as $event_name => $item) {
  199. $redis_key = RedisKeyEnum::suffix(RedisKeyEnum::EVENT_LISTENERS, '',true) .':'. $event_name;
  200. // 删除对应事件名称hash
  201. $redis->del($redis_key);
  202. foreach ($item as $name => $value) {
  203. $redis->hset($redis_key, $name, $value);// 存入hash
  204. }
  205. }
  206. return true;
  207. } catch (Exception $e) {
  208. $this->setError($e->getMessage());
  209. return false;
  210. }
  211. }
  212. /**
  213. * 商城安装插件绑定事件
  214. * @Author: lun
  215. * @DateTime: 2022/1/15 0015 19:07
  216. * @Copyright: copyright (c) 2021 广东七件事集团
  217. * @param $addons_name
  218. * @param $mall_id
  219. * @return array|mixed
  220. */
  221. public function mallEventBind($addons_name, $mall_id){
  222. $cache = Yii::$app->cache;
  223. // 缓存无数据则对表进行查询
  224. $cond[] = ['addons_name' => $addons_name, 'status' => StatusEnum::ENABLED];
  225. $event_list = AddonsEvent::getAddonsEvent($cond,[],true,'listeners,event_name');
  226. if ($event_list) {
  227. // 循环组合对应事件的监听地址
  228. foreach ($event_list as $listeners) {
  229. $cache_key = RedisKeyEnum::suffix(RedisKeyEnum::EVENT, $listeners['event_name'],true);
  230. $cache_key .= $mall_id;
  231. $listenerArr = Json::decode($listeners['listeners'], true);
  232. $event = [];
  233. foreach ($listenerArr as $listener) {
  234. $event[] = $listener;
  235. }
  236. if ($event) {
  237. $cache_data = $cache->get($cache_key);
  238. if ($cache_data) {
  239. $event = array_unique(array_merge($cache_data, $event));
  240. }
  241. $cache->set($cache_key, $event);// 保存至缓存
  242. }
  243. }
  244. }
  245. }
  246. /**
  247. * 所有商城重新对应应用的事件绑定
  248. * @Author: lun
  249. * @DateTime: 2022/3/8 0008 17:44
  250. * @Copyright: copyright (c) 2021 广东七件事集团
  251. * @param $addons_name
  252. * @return bool
  253. */
  254. public function allMallEventBind($addons_name)
  255. {
  256. try {
  257. // 获取已购买了该应用的商城
  258. $list = MallAddons::getMallAddons([['addons_name' => $addons_name]], [],true, 'mall_id');
  259. foreach ($list as $item) {
  260. $this->mallEventBind($addons_name, $item['mall_id']);
  261. }
  262. return true;
  263. } catch (Exception $e) {
  264. $this->setError($e->getMessage());
  265. return false;
  266. }
  267. }
  268. }