UserExportDataJobs.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. <?php
  2. namespace App\Jobs\OpenWork\External;
  3. use Illuminate\Bus\Queueable;
  4. use Illuminate\Contracts\Queue\ShouldBeUnique;
  5. use Illuminate\Contracts\Queue\ShouldQueue;
  6. use Illuminate\Foundation\Bus\Dispatchable;
  7. use Illuminate\Queue\InteractsWithQueue;
  8. use Illuminate\Queue\SerializesModels;
  9. use App\Servers\DB\DbService;
  10. use App\Facades\Servers\Logs\Log;
  11. use Illuminate\Support\Facades\Cache;
  12. use App\Models\OpenWork\External\FollowTag as ExternalFollowTag;
  13. use App\Models\OpenWork\External\CustomExternal as CustomExternalModel;
  14. use App\Models\OpenWork\External\Custom as CustomModel;
  15. use App\Models\OpenWork\Group\Chat as ChatModel;
  16. use App\Models\OpenWork\DownloadTask as TaskModel;
  17. use App\Servers\Aliyun\Oss;
  18. /**
  19. * 客户信息导出存储写入
  20. * @author 唐远望
  21. * @version 1.0
  22. * @date 2025-11-24
  23. */
  24. class UserExportDataJobs implements ShouldQueue
  25. {
  26. use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
  27. protected $message_data;
  28. /**
  29. * Create a new job instance.
  30. *
  31. * @return void
  32. */
  33. public function __construct(array $message_data)
  34. {
  35. $this->message_data = $message_data;
  36. }
  37. public function getCorpId()
  38. {
  39. return $this->message_data['corpid'] ?? null;
  40. }
  41. /**
  42. * Execute the job.
  43. *
  44. * @return void
  45. */
  46. public function handle()
  47. {
  48. try {
  49. $corpid = $this->message_data['corpid'];
  50. (new DbService())->getConnectionNameByCorpId($corpid);
  51. //处理分块数据
  52. $chunk = $this->message_data['chunk'];
  53. $tagGroup = $this->message_data['tagGroup'];
  54. $totle_page = $this->message_data['totle_page'];
  55. $now_page = $this->message_data['now_page'];
  56. Log::info('job_error', 'UserExportDataJobs查询处理进度', ['totle_page' => $totle_page, 'now_page' => $now_page]);
  57. $user_data = $this->handleData($chunk, $tagGroup, $corpid);
  58. //处理数据
  59. $key_name = 'UserExportDataJobs_' . $corpid;
  60. $fileName = $this->message_data['file_name']; // 文件名
  61. $companyId = $this->message_data['companyId']; // 公司ID
  62. $taskId = $this->message_data['taskId']; // 任务ID
  63. $user_data_info = Cache::get($key_name);
  64. $user_new_data = !empty($user_data_info) ? array_merge($user_data_info, $user_data): $user_data;
  65. Cache::put($key_name, $user_new_data, 60 * 60 * 24);
  66. if ($now_page == $totle_page) {
  67. // 设置表头
  68. $headers = ['企微ID', '客户名称', '客户备注', '客户描述', '客户状态', '企业名称', '所属客服', '添加时间', '手机号', '系统客户ID', 'SAAS关联手机号', '群聊名称', '入群时间', '退群时间'];
  69. // 如果有标签组
  70. if ($tagGroup) {
  71. foreach (array_keys($tagGroup) as $col => $value) {
  72. // 设置表头
  73. $headers[] = '标签组(' . $tagGroup[$value] . ')';
  74. }
  75. }
  76. // 路径写入
  77. $path = public_path('uploads/exports/' . $companyId);
  78. // 判断目录是否存在
  79. if (!is_dir($path)) mkdir($path, 0777, true);
  80. // 获取上传文件
  81. $ossUrl = $this->toDown($path, $fileName, $user_new_data, $headers);
  82. $TaskModel = new TaskModel();
  83. $TaskModel->edit($taskId, ['status' => 1, 'url' => $ossUrl, 'file_dir_name' => $fileName]);
  84. //删除缓存$key_name
  85. Cache::forget($key_name);
  86. }
  87. return true;
  88. // 成功处理...
  89. } catch (\Exception $e) {
  90. $taskId = $this->message_data['taskId']; // 任务ID
  91. $corpid = $this->message_data['corpid'];
  92. //查询下载任务
  93. (new TaskModel)->query()->where(['id' => $taskId, 'corpid' => $corpid])->update(['status' => 2, 'update_time' => time()]);
  94. // 失败处理...
  95. Log::info('job_error', 'UserExportDataJobs任务队列处理失败', ['data' => $this->message_data, 'error' => $e->getMessage()]);
  96. }
  97. }
  98. /**
  99. * 处理分块数据
  100. * @param $data
  101. * @param $key_name
  102. */
  103. public function handleData($chunk, $tagGroup, $corpid)
  104. {
  105. $ExternalFollowTag = new ExternalFollowTag();
  106. $CustomExternalModel = new CustomExternalModel();
  107. $CustomModel = new CustomModel();
  108. $ChatModel = new ChatModel();
  109. $list = [];
  110. // 查询客户的标签
  111. $tagNames = $ExternalFollowTag->query()
  112. ->join('openwork_external_tag', 'openwork_external_tag.tag_id', '=', 'openwork_external_follow_tag.tag_id')
  113. ->whereIn('openwork_external_follow_tag.external_userid', array_column($chunk, 'external_userid'))
  114. ->get([
  115. 'openwork_external_follow_tag.external_userid',
  116. 'openwork_external_follow_tag.follow_userid',
  117. 'openwork_external_tag.tag_id',
  118. 'openwork_external_tag.tag_name',
  119. 'openwork_external_tag.group_id'
  120. ])
  121. ->toArray();
  122. //查询绑定SAAS客户ID
  123. $custom_external_userid = [];
  124. $custom_external_data = $CustomExternalModel->whereIn('external_userid', array_column($chunk, 'external_userid'))->get(['external_userid', 'custom_uid'])->toarray();
  125. if (!empty($custom_external_data)) {
  126. foreach ($custom_external_data as $key => $value) {
  127. $custom_external_userid[$value['external_userid']]['custom_uids'][] = $value['custom_uid'];
  128. unset($value);
  129. }
  130. foreach ($custom_external_userid as $key => $value) {
  131. $phone_list = $CustomModel->whereIn('uid', $value['custom_uids'])->pluck('phone')->implode(',');
  132. $custom_external_userid[$key]['phone_list'] = $phone_list;
  133. unset($value);
  134. }
  135. }
  136. //查询加入的群聊
  137. $chat_list = [];
  138. $chat_group_list = $ChatModel->join('openwork_group_member', function ($join) {
  139. $join->on('openwork_group.chat_id', '=', 'openwork_group_member.chat_id');
  140. })->select([
  141. 'openwork_group.corpid',
  142. 'openwork_group_member.userid',
  143. 'openwork_group.id',
  144. 'openwork_group.chat_id',
  145. 'openwork_group.name',
  146. 'openwork_group.status',
  147. 'openwork_group_member.join_time',
  148. 'openwork_group_member.quit_time',
  149. ])->where(['openwork_group.corpid' => $corpid])->whereIn('openwork_group_member.userid', array_column($chunk, 'external_userid'))->get('id')->toarray();
  150. if (!empty($chat_group_list)) {
  151. foreach ($chat_group_list as $key => $value) {
  152. $join_time = $value['join_time'] ? date('Y-m-d H:i:s', $value['join_time']) : '';
  153. $quit_time = $value['quit_time'] ? date('Y-m-d H:i:s', $value['quit_time']) : '未退群';
  154. $group_name = $value['name'] == '' ? '未命名群聊' : $value['name'];
  155. $chat_list[$value['userid']][] = $group_name . '&' . $join_time . '&' . $quit_time;
  156. unset($value);
  157. }
  158. }
  159. // 处理数据
  160. $keyTags = [];
  161. // 循环处理标签数据
  162. foreach ($tagNames as $value) {
  163. // 处理标签
  164. $keyTags[$value['external_userid']][$value['follow_userid']][$value['group_id']][$value['tag_id']] = $value['tag_name'];
  165. }
  166. // 循环数据
  167. foreach ($chunk as $value) {
  168. // 手机号
  169. $value['status'] = $value['status'] ? '已流失' : '未流失';
  170. $value['createtime'] = $value['createtime'] ? date('Y-m-d H:i:s', $value['createtime']) : '';
  171. $value['custom_uid'] = empty($value['custom_uid']) ? '' : $value['custom_uid'];
  172. $value['corp_name'] = empty($value['corp_name']) ? ($value['remark_corp_name'] ? $value['remark_corp_name'] : '') : $value['corp_name'];
  173. $value['phone'] = empty($value['phone']) ? ($value['remark_mobiles'] ? $value['remark_mobiles'] : '') : $value['phone'];
  174. $value['phone_list'] = array_key_exists($value['external_userid'], $custom_external_userid) ? $custom_external_userid[$value['external_userid']]['phone_list'] : '';
  175. if (array_key_exists($value['external_userid'], $chat_list)) {
  176. $chat_list_info = $chat_list[$value['external_userid']];
  177. foreach ($chat_list_info as $key => $chat_list_info_value) {
  178. $chat_info = explode('&', $chat_list_info_value);
  179. $value['chat_name'] = $chat_info[0];
  180. $value['join_time'] = $chat_info[1];
  181. $value['quit_time'] = $chat_info[2];
  182. // 循环标签数据
  183. foreach ($tagGroup as $groupId => $groupName) {
  184. // 获取分组标签
  185. $value[$groupId] = isset($keyTags[$value['external_userid']][$value['follow_userid']][$groupId]) ? implode(',', $keyTags[$value['external_userid']][$value['follow_userid']][$groupId]) : '';
  186. }
  187. // 去除字段
  188. unset($value['remark_corp_name'], $value['remark_mobiles']);
  189. // 修改数据
  190. $list[] = $value;
  191. }
  192. } else {
  193. $value['chat_name'] = '';
  194. $value['join_time'] = '';
  195. $value['quit_time'] = '';
  196. // 循环标签数据
  197. foreach ($tagGroup as $groupId => $groupName) {
  198. // 获取分组标签
  199. $value[$groupId] = isset($keyTags[$value['external_userid']][$value['follow_userid']][$groupId]) ? implode(',', $keyTags[$value['external_userid']][$value['follow_userid']][$groupId]) : '';
  200. }
  201. // 去除字段
  202. unset($value['remark_corp_name'], $value['remark_mobiles']);
  203. // 修改数据
  204. $list[] = $value;
  205. }
  206. }
  207. return $list;
  208. }
  209. /**
  210. * 去下载 上传oss
  211. */
  212. private function toDown($path, $fileName, &$data, $header)
  213. {
  214. // xlsx文件保存路径
  215. $excel = new \Vtiful\Kernel\Excel(['path' => $path]);
  216. $filePath = $excel->filename($fileName, 'sheet1')->constMemory($fileName, 'sheet1')->header($header)->data($data)->output();
  217. $Oss = new Oss();
  218. $res = $Oss->uploadFile($fileName, $filePath);
  219. if ($res) @unlink($filePath);
  220. return $res;
  221. }
  222. public function failed(\Throwable $exception)
  223. {
  224. Log::info('job_error', 'UserExportDataJobs任务完全失败', ['data' => $this->message_data, 'error' => $exception->getMessage()]);
  225. }
  226. }