PointLogCollector.php 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. <?php
  2. namespace App\Module\Game\Logics\UserLogCollectors;
  3. use App\Module\Point\Models\PointLogModel;
  4. use App\Module\Point\Enums\LOG_TYPE;
  5. /**
  6. * 积分日志收集器
  7. *
  8. * 收集point_logs表的新增记录,转换为用户友好的日志消息
  9. */
  10. class PointLogCollector extends BaseLogCollector
  11. {
  12. /**
  13. * 源表名
  14. *
  15. * @var string
  16. */
  17. protected string $sourceTable = 'point_logs';
  18. /**
  19. * 源类型
  20. *
  21. * @var string
  22. */
  23. protected string $sourceType = 'point';
  24. /**
  25. * 获取新的记录
  26. *
  27. * @param int $lastProcessedId 上次处理的最大ID
  28. * @return \Illuminate\Database\Eloquent\Collection
  29. */
  30. protected function getNewRecords(int $lastProcessedId)
  31. {
  32. return PointLogModel::where('id', '>', $lastProcessedId)
  33. ->orderBy('id')
  34. ->limit($this->maxRecords)
  35. ->get();
  36. }
  37. /**
  38. * 获取源表的最大ID
  39. *
  40. * 重写父类方法,使用模型查询以获得更好的性能
  41. *
  42. * @return int
  43. */
  44. public function getSourceTableMaxId(): int
  45. {
  46. return PointLogModel::max('id') ?: 0;
  47. }
  48. /**
  49. * 转换记录为用户日志数据
  50. *
  51. * @param PointLogModel $record 积分日志记录
  52. * @return array|null 用户日志数据,null表示跳过
  53. */
  54. protected function convertToUserLog($record): ?array
  55. {
  56. try {
  57. // 检查是否应该记录此日志
  58. if (!$this->shouldLogRecord($record)) {
  59. return null;
  60. }
  61. // 获取积分类型名称
  62. $pointTypeName = $this->getPointTypeName($record->point_type);
  63. // 判断是获得还是消耗
  64. $amount = abs($record->amount);
  65. $action = $record->amount > 0 ? '获得' : '消耗';
  66. // 解析备注信息,生成用户友好的消息
  67. $message = $this->buildUserFriendlyMessage($record, $pointTypeName, $action, $amount);
  68. // 使用原始记录的时间
  69. $createdAt = date('Y-m-d H:i:s', $record->create_time);
  70. return $this->createUserLogData(
  71. $record->user_id,
  72. $message,
  73. $record->id,
  74. $createdAt
  75. );
  76. } catch (\Exception $e) {
  77. \Illuminate\Support\Facades\Log::error("转换积分日志失败", [
  78. 'record_id' => $record->id,
  79. 'error' => $e->getMessage()
  80. ]);
  81. return null;
  82. }
  83. }
  84. /**
  85. * 构建用户友好的消息
  86. *
  87. * @param PointLogModel $record 积分日志记录
  88. * @param string $pointTypeName 积分类型名称
  89. * @param string $action 操作类型(获得/消耗)
  90. * @param int $amount 积分数量
  91. * @return string 用户友好的消息
  92. */
  93. private function buildUserFriendlyMessage(PointLogModel $record, string $pointTypeName, string $action, int $amount): string
  94. {
  95. // 获取操作类型名称
  96. $operateTypeName = $this->getOperateTypeName($record->operate_type);
  97. // 根据操作类型生成不同的消息格式
  98. $message = match($record->operate_type) {
  99. LOG_TYPE::TASK_COMPLETE => "完成任务{$action} {$amount} {$pointTypeName}",
  100. LOG_TYPE::CHECKIN_REWARD => "签到{$action} {$amount} {$pointTypeName}",
  101. LOG_TYPE::ACTIVITY_REWARD => "活动奖励{$action} {$amount} {$pointTypeName}",
  102. LOG_TYPE::ACHIEVEMENT_REWARD => "成就奖励{$action} {$amount} {$pointTypeName}",
  103. LOG_TYPE::REFERRAL_REWARD => "推荐奖励{$action} {$amount} {$pointTypeName}",
  104. LOG_TYPE::PLANTING_REWARD => "种植作物{$action} {$amount} {$pointTypeName}",
  105. LOG_TYPE::POINT_CONSUME => "消费{$action} {$amount} {$pointTypeName}",
  106. LOG_TYPE::POINT_EXCHANGE => "兑换{$action} {$amount} {$pointTypeName}",
  107. LOG_TYPE::TRANSFER => $this->buildTransferMessage($record, $pointTypeName, $action, $amount),
  108. LOG_TYPE::CIRCULATION => "积分流转{$action} {$amount} {$pointTypeName}",
  109. LOG_TYPE::ADMIN_OPERATE => "管理员操作{$action} {$amount} {$pointTypeName}",
  110. LOG_TYPE::FREEZE => "冻结 {$amount} {$pointTypeName}",
  111. LOG_TYPE::UNFREEZE => "解冻 {$amount} {$pointTypeName}",
  112. LOG_TYPE::REFUND => "退还 {$amount} {$pointTypeName}",
  113. LOG_TYPE::DEDUCT => "扣除 {$amount} {$pointTypeName}",
  114. LOG_TYPE::SYSTEM_REWARD => "系统奖励{$action} {$amount} {$pointTypeName}",
  115. default => "{$operateTypeName}{$action} {$amount} {$pointTypeName}",
  116. };
  117. // 如果有备注且不是默认备注,添加到消息中
  118. if (!empty($record->remark) && !$this->isDefaultRemark($record->remark)) {
  119. $message .= "({$record->remark})";
  120. }
  121. return $message;
  122. }
  123. /**
  124. * 构建转账消息
  125. *
  126. * @param PointLogModel $record 积分日志记录
  127. * @param string $pointTypeName 积分类型名称
  128. * @param string $action 操作类型(未使用,保持接口一致性)
  129. * @param int $amount 积分数量
  130. * @return string 转账消息
  131. */
  132. private function buildTransferMessage(PointLogModel $record, string $pointTypeName, string $action, int $amount): string
  133. {
  134. // 根据金额正负判断是转入还是转出
  135. if ($record->amount > 0) {
  136. return "收到转账 {$amount} {$pointTypeName}";
  137. } else {
  138. return "转账给他人 {$amount} {$pointTypeName}";
  139. }
  140. }
  141. /**
  142. * 获取操作类型名称
  143. *
  144. * @param LOG_TYPE $operateType 操作类型
  145. * @return string 操作类型名称
  146. */
  147. private function getOperateTypeName(LOG_TYPE $operateType): string
  148. {
  149. try {
  150. // 使用静态映射避免调用可能有问题的方法
  151. $operateTypeNames = [
  152. 'TASK_COMPLETE' => '任务完成',
  153. 'CHECKIN_REWARD' => '签到奖励',
  154. 'ACTIVITY_REWARD' => '活动奖励',
  155. 'ACHIEVEMENT_REWARD' => '成就奖励',
  156. 'REFERRAL_REWARD' => '推荐奖励',
  157. 'PLANTING_REWARD' => '种植奖励',
  158. 'POINT_CONSUME' => '积分消费',
  159. 'POINT_EXCHANGE' => '积分兑换',
  160. 'TRANSFER' => '积分转账',
  161. 'CIRCULATION' => '积分流转',
  162. 'ADMIN_OPERATE' => '管理员操作',
  163. 'FREEZE' => '积分冻结',
  164. 'UNFREEZE' => '积分解冻',
  165. 'REFUND' => '积分退还',
  166. 'DEDUCT' => '积分扣除',
  167. 'SYSTEM_REWARD' => '系统奖励',
  168. ];
  169. $typeName = $operateType->name ?? (string)$operateType;
  170. return $operateTypeNames[$typeName] ?? "未知操作";
  171. } catch (\Exception $e) {
  172. return "未知操作";
  173. }
  174. }
  175. /**
  176. * 判断是否为默认备注
  177. *
  178. * @param string $remark 备注内容
  179. * @return bool 是否为默认备注
  180. */
  181. private function isDefaultRemark(string $remark): bool
  182. {
  183. $defaultRemarks = [
  184. '系统操作',
  185. '自动操作',
  186. '默认备注',
  187. '',
  188. ];
  189. return in_array(trim($remark), $defaultRemarks);
  190. }
  191. /**
  192. * 检查是否应该记录此日志
  193. *
  194. * @param PointLogModel $record 积分日志记录
  195. * @return bool 是否应该记录
  196. */
  197. private function shouldLogRecord(PointLogModel $record): bool
  198. {
  199. // 过滤掉测试操作
  200. if ($record->operate_type === LOG_TYPE::TEST) {
  201. return false;
  202. }
  203. // 过滤掉金额为0的记录
  204. if ($record->amount == 0) {
  205. return false;
  206. }
  207. // 过滤掉系统内部操作(可根据需要调整)
  208. $internalOperations = [
  209. // 可以在这里添加需要过滤的内部操作类型
  210. ];
  211. if (in_array($record->operate_type, $internalOperations)) {
  212. return false;
  213. }
  214. return true;
  215. }
  216. /**
  217. * 获取积分类型名称
  218. *
  219. * @param mixed $pointType 积分类型
  220. * @return string
  221. */
  222. private function getPointTypeName($pointType): string
  223. {
  224. // 使用静态映射避免调用可能有问题的方法
  225. $pointTypeNames = [
  226. 1 => '经验积分',
  227. 2 => '活动积分',
  228. 3 => '任务积分',
  229. 4 => '签到积分',
  230. // 可以根据需要添加更多
  231. ];
  232. $pointKey = is_object($pointType) ? $pointType->value : $pointType;
  233. return $pointTypeNames[$pointKey] ?? "积分{$pointKey}";
  234. }
  235. }