OrderActionDecorator.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. <?php
  2. namespace app\common\Service\Order;
  3. use app\common\Enum\OrderActionEnum;
  4. use think\Exception;
  5. /**
  6. * 订单操作装饰器 - 实现AOP模式自动记录操作
  7. */
  8. class OrderActionDecorator
  9. {
  10. /**
  11. * 被装饰的服务对象
  12. */
  13. protected $service;
  14. /**
  15. * 当前操作上下文
  16. */
  17. protected $context = [];
  18. /**
  19. * 构造函数
  20. * @param object $service 被装饰的服务对象
  21. */
  22. public function __construct($service)
  23. {
  24. $this->service = $service;
  25. }
  26. /**
  27. * 设置操作上下文
  28. * @param array $context 上下文信息
  29. */
  30. public function setContext($context)
  31. {
  32. $this->context = array_merge($this->context, $context);
  33. }
  34. /**
  35. * 获取操作上下文
  36. * @return array
  37. */
  38. public function getContext()
  39. {
  40. return $this->context;
  41. }
  42. /**
  43. * 魔术方法,代理所有方法调用
  44. * @param string $method 方法名
  45. * @param array $args 参数
  46. * @return mixed
  47. */
  48. public function __call($method, $args)
  49. {
  50. // 前置处理
  51. $this->beforeAction($method, $args);
  52. try {
  53. // 调用原始方法
  54. $result = call_user_func_array([$this->service, $method], $args);
  55. // 后置处理
  56. $this->afterAction($method, $args, $result);
  57. return $result;
  58. } catch (Exception $e) {
  59. // 异常处理
  60. $this->onException($method, $args, $e);
  61. throw $e;
  62. }
  63. }
  64. /**
  65. * 前置处理
  66. * @param string $method 方法名
  67. * @param array $args 参数
  68. */
  69. protected function beforeAction($method, $args)
  70. {
  71. // 记录方法调用开始
  72. $this->context['method'] = $method;
  73. $this->context['args'] = $args;
  74. $this->context['start_time'] = microtime(true);
  75. $this->context['start_memory'] = memory_get_usage();
  76. }
  77. /**
  78. * 后置处理
  79. * @param string $method 方法名
  80. * @param array $args 参数
  81. * @param mixed $result 结果
  82. */
  83. protected function afterAction($method, $args, $result)
  84. {
  85. // 记录方法调用结束
  86. $this->context['end_time'] = microtime(true);
  87. $this->context['end_memory'] = memory_get_usage();
  88. $this->context['execution_time'] = $this->context['end_time'] - $this->context['start_time'];
  89. $this->context['memory_usage'] = $this->context['end_memory'] - $this->context['start_memory'];
  90. $this->context['result'] = $result;
  91. // 根据方法名自动记录操作
  92. $this->autoRecordAction($method, $args, $result);
  93. }
  94. /**
  95. * 异常处理
  96. * @param string $method 方法名
  97. * @param array $args 参数
  98. * @param Exception $e 异常对象
  99. */
  100. protected function onException($method, $args, $e)
  101. {
  102. // 记录异常
  103. $this->context['exception'] = $e->getMessage();
  104. $this->context['exception_code'] = $e->getCode();
  105. $this->context['exception_trace'] = $e->getTraceAsString();
  106. // 可以在这里记录异常日志
  107. \think\Log::error('订单操作异常: ' . $e->getMessage(), [
  108. 'method' => $method,
  109. 'args' => $args,
  110. 'context' => $this->context
  111. ]);
  112. }
  113. /**
  114. * 自动记录操作
  115. * @param string $method 方法名
  116. * @param array $args 参数
  117. * @param mixed $result 结果
  118. */
  119. protected function autoRecordAction($method, $args, $result)
  120. {
  121. // 方法名到操作类型的映射
  122. $methodActionMap = [
  123. 'create' => OrderActionEnum::ACTION_CREATE,
  124. 'pay' => OrderActionEnum::ACTION_PAY,
  125. 'ship' => OrderActionEnum::ACTION_SHIP,
  126. 'receive' => OrderActionEnum::ACTION_RECEIVE,
  127. 'cancel' => OrderActionEnum::ACTION_CANCEL,
  128. 'refund' => OrderActionEnum::ACTION_REFUND,
  129. 'return' => OrderActionEnum::ACTION_RETURN,
  130. 'complete' => OrderActionEnum::ACTION_COMPLETE,
  131. 'comment' => OrderActionEnum::ACTION_COMMENT,
  132. 'modify' => OrderActionEnum::ACTION_MODIFY,
  133. 'remind' => OrderActionEnum::ACTION_REMIND,
  134. ];
  135. // 查找匹配的操作类型
  136. $actionType = null;
  137. foreach ($methodActionMap as $keyword => $action) {
  138. if (stripos($method, $keyword) !== false) {
  139. $actionType = $action;
  140. break;
  141. }
  142. }
  143. if (!$actionType) {
  144. return; // 没有匹配的操作类型,不记录
  145. }
  146. // 尝试从参数中提取订单编号
  147. $orderSn = $this->extractOrderSn($args);
  148. if (!$orderSn) {
  149. return; // 没有订单编号,不记录
  150. }
  151. // 获取操作者信息
  152. $operator = $this->context['operator'] ?? 'unknown';
  153. $userType = $this->context['user_type'] ?? OrderActionEnum::USER_TYPE_ADMIN;
  154. $userId = $this->context['user_id'] ?? 0;
  155. // 构建备注信息
  156. $memo = $this->buildMemo($method, $args, $result);
  157. // 构建额外数据
  158. $extra = [
  159. 'method' => $method,
  160. 'execution_time' => $this->context['execution_time'],
  161. 'memory_usage' => $this->context['memory_usage'],
  162. 'user_agent' => request()->server('HTTP_USER_AGENT', ''),
  163. 'ip' => request()->ip(),
  164. ];
  165. // 记录操作
  166. try {
  167. switch ($userType) {
  168. case OrderActionEnum::USER_TYPE_CUSTOMER:
  169. OrderActionService::recordUserAction($orderSn, $actionType, $operator, $memo, $userId, $extra);
  170. break;
  171. case OrderActionEnum::USER_TYPE_ADMIN:
  172. OrderActionService::recordAdminAction($orderSn, $actionType, $operator, $memo, $userId, $extra);
  173. break;
  174. case OrderActionEnum::USER_TYPE_SYSTEM:
  175. OrderActionService::recordSystemAction($orderSn, $actionType, $memo, $extra);
  176. break;
  177. }
  178. } catch (Exception $e) {
  179. // 记录操作失败不应该影响主流程
  180. \think\Log::error('自动记录订单操作失败: ' . $e->getMessage());
  181. }
  182. }
  183. /**
  184. * 从参数中提取订单编号
  185. * @param array $args 参数数组
  186. * @return string|null
  187. */
  188. protected function extractOrderSn($args)
  189. {
  190. // 尝试从不同位置提取订单编号
  191. foreach ($args as $arg) {
  192. if (is_string($arg) && preg_match('/^[A-Z0-9]{10,}$/', $arg)) {
  193. return $arg; // 假设订单编号是10位以上的大写字母和数字组合
  194. }
  195. if (is_array($arg) && isset($arg['order_sn'])) {
  196. return $arg['order_sn'];
  197. }
  198. if (is_array($arg) && isset($arg['out_trade_no'])) {
  199. return $arg['out_trade_no'];
  200. }
  201. if (is_object($arg) && property_exists($arg, 'order_sn')) {
  202. return $arg->order_sn;
  203. }
  204. }
  205. return null;
  206. }
  207. /**
  208. * 构建备注信息
  209. * @param string $method 方法名
  210. * @param array $args 参数
  211. * @param mixed $result 结果
  212. * @return string
  213. */
  214. protected function buildMemo($method, $args, $result)
  215. {
  216. $memo = "执行方法: {$method}";
  217. // 添加执行时间信息
  218. if (isset($this->context['execution_time'])) {
  219. $memo .= sprintf(", 耗时: %.3fs", $this->context['execution_time']);
  220. }
  221. // 添加内存使用信息
  222. if (isset($this->context['memory_usage'])) {
  223. $memo .= sprintf(", 内存: %s", $this->formatBytes($this->context['memory_usage']));
  224. }
  225. // 添加自定义备注
  226. if (isset($this->context['memo'])) {
  227. $memo .= ", " . $this->context['memo'];
  228. }
  229. return $memo;
  230. }
  231. /**
  232. * 格式化字节数
  233. * @param int $bytes 字节数
  234. * @return string
  235. */
  236. protected function formatBytes($bytes)
  237. {
  238. if ($bytes >= 1024 * 1024) {
  239. return sprintf('%.2fMB', $bytes / (1024 * 1024));
  240. } elseif ($bytes >= 1024) {
  241. return sprintf('%.2fKB', $bytes / 1024);
  242. } else {
  243. return $bytes . 'B';
  244. }
  245. }
  246. /**
  247. * 创建装饰器实例
  248. * @param object $service 被装饰的服务对象
  249. * @param array $context 上下文信息
  250. * @return OrderActionDecorator
  251. */
  252. public static function create($service, $context = [])
  253. {
  254. $decorator = new static($service);
  255. $decorator->setContext($context);
  256. return $decorator;
  257. }
  258. /**
  259. * 装饰服务方法的静态工厂方法
  260. * @param object $service 被装饰的服务对象
  261. * @param string $operator 操作者
  262. * @param string $userType 用户类型
  263. * @param int $userId 用户ID
  264. * @param array $extra 额外上下文
  265. * @return OrderActionDecorator
  266. */
  267. public static function decorate($service, $operator, $userType = OrderActionEnum::USER_TYPE_ADMIN, $userId = 0, $extra = [])
  268. {
  269. $context = array_merge([
  270. 'operator' => $operator,
  271. 'user_type' => $userType,
  272. 'user_id' => $userId,
  273. ], $extra);
  274. return self::create($service, $context);
  275. }
  276. /**
  277. * 为用户操作创建装饰器
  278. * @param object $service 被装饰的服务对象
  279. * @param string $operator 操作者
  280. * @param int $userId 用户ID
  281. * @param array $extra 额外上下文
  282. * @return OrderActionDecorator
  283. */
  284. public static function forUser($service, $operator, $userId = 0, $extra = [])
  285. {
  286. return self::decorate($service, $operator, OrderActionEnum::USER_TYPE_CUSTOMER, $userId, $extra);
  287. }
  288. /**
  289. * 为管理员操作创建装饰器
  290. * @param object $service 被装饰的服务对象
  291. * @param string $operator 操作者
  292. * @param int $adminId 管理员ID
  293. * @param array $extra 额外上下文
  294. * @return OrderActionDecorator
  295. */
  296. public static function forAdmin($service, $operator, $adminId = 0, $extra = [])
  297. {
  298. return self::decorate($service, $operator, OrderActionEnum::USER_TYPE_ADMIN, $adminId, $extra);
  299. }
  300. /**
  301. * 为系统操作创建装饰器
  302. * @param object $service 被装饰的服务对象
  303. * @param array $extra 额外上下文
  304. * @return OrderActionDecorator
  305. */
  306. public static function forSystem($service, $extra = [])
  307. {
  308. return self::decorate($service, 'system', OrderActionEnum::USER_TYPE_SYSTEM, 0, $extra);
  309. }
  310. }