AppMessageService.php 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. <?php
  2. namespace App\Module\AppMessage\Services;
  3. use App\Module\AppMessage\Enums\AppMessageStatus;
  4. use App\Module\AppMessage\Enums\AppMessageType;
  5. use App\Module\AppMessage\Enums\AppSenderType;
  6. use App\Module\AppMessage\Models\AppMessage;
  7. use App\Module\AppMessage\Models\AppMessageRecipient;
  8. use App\Module\AppMessage\Models\AppMessageTemplate;
  9. use App\Module\AppMessage\Queues\SendAppMessageQueue;
  10. use Illuminate\Support\Facades\DB;
  11. use Illuminate\Support\Facades\Log;
  12. use Illuminate\Support\Collection;
  13. use Carbon\Carbon;
  14. /**
  15. * 应用消息服务类
  16. *
  17. * 该服务类提供了消息的发送、模板管理、消息状态管理等功能
  18. */
  19. class AppMessageService
  20. {
  21. /**
  22. * 发送消息
  23. *
  24. * @param array $data [
  25. * 'template_id' => int|null, // 模板ID
  26. * 'template_code' => string|null, // 模板代码
  27. * 'title' => string, // 消息标题
  28. * 'content' => string, // 消息内容
  29. * 'content_type' => string, // 内容类型:text,html,markdown,json
  30. * 'data' => array|null, // 附加数据
  31. * 'variables_data' => array|null, // 模板变量数据
  32. * 'sender_id' => int, // 发送者ID
  33. * 'sender_type' => string, // 发送者类型:system,user
  34. * 'receiver_id' => int|null, // 接收者ID,为空表示群发
  35. * 'receivers' => array|null, // 群发接收者ID数组
  36. * 'allow_reply' => bool, // 是否允许回复
  37. * 'parent_id' => int|null, // 父消息ID(回复时使用)
  38. * ]
  39. * @return AppMessage
  40. */
  41. public function send(array $data): AppMessage
  42. {
  43. try {
  44. DB::beginTransaction();
  45. // 如果提供了模板ID或代码,则获取模板
  46. if (!empty($data['template_id']) || !empty($data['template_code'])) {
  47. $template = $this->getTemplate($data['template_id'] ?? null, $data['template_code'] ?? null);
  48. if ($template) {
  49. $data = $this->applyTemplate($template, $data);
  50. }
  51. }
  52. // 创建消息
  53. $message = new AppMessage();
  54. $message->type = $data['type'] ?? 'user';
  55. $message->template_id = $data['template_id'] ?? null;
  56. $message->template_code = $data['template_code'] ?? null;
  57. $message->title = $data['title'];
  58. $message->content = $data['content'];
  59. $message->content_type = $data['content_type'] ?? 'text';
  60. $message->data = $data['data'] ?? null;
  61. $message->variables_data = $data['variables_data'] ?? null;
  62. $message->sender_id = $data['sender_id'];
  63. $message->sender_type = $data['sender_type'];
  64. $message->receiver_id = $data['receiver_id'] ?? null;
  65. $message->parent_id = $data['parent_id'] ?? null;
  66. $message->allow_reply = $data['allow_reply'] ?? true;
  67. $message->save();
  68. // 处理群发
  69. if (empty($message->receiver_id) && !empty($data['receivers'])) {
  70. $this->sendToRecipients($message, $data['receivers']);
  71. }
  72. DB::commit();
  73. return $message;
  74. } catch (\Exception $e) {
  75. DB::rollBack();
  76. throw $e;
  77. }
  78. }
  79. /**
  80. * 批量发送消息
  81. *
  82. * @param array $messages 消息数组
  83. * @return Collection
  84. */
  85. public function sendBatch(array $messages): Collection
  86. {
  87. $results = collect();
  88. foreach ($messages as $data) {
  89. $results->push($this->send($data));
  90. }
  91. return $results;
  92. }
  93. /**
  94. * 获取模板
  95. *
  96. * @param int|null $id 模板ID
  97. * @param string|null $code 模板代码
  98. * @return AppMessageTemplate|null
  99. */
  100. public function getTemplate(?int $id = null, ?string $code = null): ?AppMessageTemplate
  101. {
  102. if ($id) {
  103. return AppMessageTemplate::find($id);
  104. }
  105. if ($code) {
  106. return AppMessageTemplate::where('code', $code)->first();
  107. }
  108. return null;
  109. }
  110. /**
  111. * 应用模板
  112. *
  113. * @param AppMessageTemplate $template 模板
  114. * @param array $data 数据
  115. * @return array
  116. */
  117. protected function applyTemplate(AppMessageTemplate $template, array $data): array
  118. {
  119. $variables = $data['variables_data'] ?? [];
  120. $content = $template->content;
  121. // 替换模板变量
  122. if (!empty($variables) && !empty($template->variables)) {
  123. foreach ($template->variables as $key => $info) {
  124. $value = $variables[$key] ?? '';
  125. $content = str_replace('{{'.$key.'}}', $value, $content);
  126. }
  127. }
  128. return array_merge($data, [
  129. 'type' => $template->type,
  130. 'title' => $template->title,
  131. 'content' => $content,
  132. 'content_type' => $template->content_type,
  133. 'allow_reply' => $template->allow_reply,
  134. ]);
  135. }
  136. /**
  137. * 发送给多个接收者
  138. *
  139. * @param AppMessage $message 消息
  140. * @param array $receiverIds 接收者ID数组
  141. * @return void
  142. */
  143. protected function sendToRecipients(AppMessage $message, array $receiverIds): void
  144. {
  145. $now = Carbon::now();
  146. $recipients = array_map(function ($userId) use ($message, $now) {
  147. return [
  148. 'message_id' => $message->id,
  149. 'user_id' => $userId,
  150. 'created_at' => $now,
  151. 'updated_at' => $now,
  152. ];
  153. }, array_unique($receiverIds));
  154. AppMessageRecipient::insert($recipients);
  155. }
  156. /**
  157. * 标记消息为已读
  158. *
  159. * @param int $messageId 消息ID
  160. * @param int $userId 用户ID
  161. * @return bool
  162. */
  163. public function markAsRead(int $messageId, int $userId): bool
  164. {
  165. $now = Carbon::now();
  166. // 更新直接接收的消息
  167. $message = AppMessage::where('id', $messageId)
  168. ->where('receiver_id', $userId)
  169. ->first();
  170. if ($message) {
  171. $message->update([
  172. 'is_read' => true,
  173. 'read_at' => $now
  174. ]);
  175. return true;
  176. }
  177. // 更新群发消息接收记录
  178. $recipient = AppMessageRecipient::where('message_id', $messageId)
  179. ->where('user_id', $userId)
  180. ->first();
  181. if ($recipient) {
  182. $recipient->update([
  183. 'is_read' => true,
  184. 'read_at' => $now
  185. ]);
  186. return true;
  187. }
  188. return false;
  189. }
  190. /**
  191. * 获取用户未读消息数量
  192. *
  193. * @param int $userId 用户ID
  194. * @return int
  195. */
  196. public function getUnreadCount(int $userId): int
  197. {
  198. // 直接接收的未读消息
  199. $directCount = AppMessage::where('receiver_id', $userId)
  200. ->where('is_read', false)
  201. ->where('status', 1)
  202. ->count();
  203. // 群发消息的未读数量
  204. $recipientCount = AppMessageRecipient::where('user_id', $userId)
  205. ->where('is_read', false)
  206. ->where('status', 1)
  207. ->count();
  208. return $directCount + $recipientCount;
  209. }
  210. /**
  211. * 获取用户的消息列表
  212. *
  213. * @param int $userId 用户ID
  214. * @param array $filters 过滤条件
  215. * @param int $perPage 每页数量
  216. * @return \Illuminate\Pagination\LengthAwarePaginator
  217. */
  218. public function getUserMessages(int $userId, array $filters = [], int $perPage = 15)
  219. {
  220. $query = AppMessage::where(function ($query) use ($userId) {
  221. $query->where('receiver_id', $userId)
  222. ->orWhereExists(function ($query) use ($userId) {
  223. $query->from('app_message_recipients')
  224. ->whereColumn('app_message_recipients.message_id', 'app_messages.id')
  225. ->where('app_message_recipients.user_id', $userId)
  226. ->where('app_message_recipients.status', 1);
  227. });
  228. })->where('status', 1);
  229. // 应用过滤条件
  230. if (!empty($filters['type'])) {
  231. $query->where('type', $filters['type']);
  232. }
  233. if (!empty($filters['content_type'])) {
  234. $query->where('content_type', $filters['content_type']);
  235. }
  236. if (isset($filters['is_read'])) {
  237. $query->where('is_read', $filters['is_read']);
  238. }
  239. return $query->orderBy('created_at', 'desc')->paginate($perPage);
  240. }
  241. /**
  242. * 创建消息模板
  243. *
  244. * @param array $data 模板数据,包含以下字段:
  245. * - name: string 模板名称
  246. * - code: string 模板代码
  247. * - type: string 模板类型
  248. * - title: string 消息标题
  249. * - content: string 消息内容
  250. * - variables: array 模板变量
  251. * - status: bool 模板状态
  252. * @return AppMessageTemplate 返回创建的模板实例
  253. */
  254. public function createTemplate(array $data): AppMessageTemplate
  255. {
  256. return AppMessageTemplate::create($data);
  257. }
  258. /**
  259. * 更新消息模板
  260. *
  261. * @param int $id 模板ID
  262. * @param array $data 更新的模板数据,字段同createTemplate方法
  263. * @return bool 更新成功返回true,失败返回false
  264. */
  265. public function updateTemplate(int $id, array $data): bool
  266. {
  267. $template = $this->getTemplate($id);
  268. if (!$template) {
  269. return false;
  270. }
  271. return $template->update($data);
  272. }
  273. /**
  274. * 删除消息模板
  275. *
  276. * @param int $id 模板ID
  277. * @return bool 删除成功返回true,失败返回false
  278. */
  279. public function deleteTemplate(int $id): bool
  280. {
  281. $template = $this->getTemplate($id);
  282. if (!$template) {
  283. return false;
  284. }
  285. return $template->delete();
  286. }
  287. }