diff --git a/app/controller/DailyController.php b/app/controller/DailyController.php new file mode 100644 index 0000000..61cc700 --- /dev/null +++ b/app/controller/DailyController.php @@ -0,0 +1,100 @@ +input('page', 1); + $perPage = $request->input('per_page', 10); + + //id + $user_id = $request->user['id']; + if(Util::isEmpty($user_id)){ + return Util::fail([],'user_id'.trans('not_null')); + } + + $where = []; + $where['user_id'] = $user_id; + + // 执行分页查询 + $data = Daily::with('user')->where($where) + ->orderBy('id', 'desc') + ->paginate($perPage, ['*'], 'page', $page); + + return Util::success($data,trans('success')); + } + + /** + * 日报列表 + * @return \support\Response + */ + public function detail(Request $request) + { + //日报id + $id = Util::getParam($request,'id'); + if(Util::isEmpty($id)){ + return Util::fail([],'id'.trans('not_null')); + } + + $where = []; + $where['id'] = $id; + + // 执行分页查询 + $data = Daily::with('user')->where($where) + ->first(); + + $data['self'] = 0; + + return Util::success($data,trans('success')); + } + + /** + * 日报编辑 + * @param Request $request + * @return \support\Response + */ + public function save(Request $request) + { + $id = Util::getParam($request, 'id'); + $text = Util::getParam($request, 'text'); + + if (empty($id)) { + return Util::fail([], '日报id' . trans('not_null')); + } + if (Util::isEmpty($text)) { + return Util::fail([], 'text' . trans('not_null')); + } + + $data = [ + 'text' => $text, + 'remark' => Util::getParam($request, 'remark', '') + ]; + + $res = Daily::where(['id' => $id])->update($data); + + if ($res) { + return Util::success($res,trans('save_success')); + } else { + return Util::fail($res,trans('save_fail')); + } + } + +} \ No newline at end of file diff --git a/app/controller/FeishuController.php b/app/controller/FeishuController.php index 94bea1a..319a5de 100644 --- a/app/controller/FeishuController.php +++ b/app/controller/FeishuController.php @@ -141,17 +141,18 @@ class FeishuController /** - * @param $receiveId openid - * @param $title 推送标题 - * @param $content 推送内容 - * @param $href 详情链接 + * @param $receiveId //openid + * @param $title //推送标题 + * @param $content //推送内容 + * @param $href //详情链接 + * @param $msgType = 'post'; //卡片消息:interactive,富文本消息:post * @return \support\Response */ - public static function send($config='xinyouhe',$receiveId='ou_e33bb3017ada6c4781398a68e55a4696',$title='ceshi',$content='测试',$href='http://219.157.255.213:8085/pages/index/mine') + public static function send($config='xinyouhe',$receiveId='ou_e33bb3017ada6c4781398a68e55a4696',$content = '' , $msgType = 'post') { // 获取凭证 $certificate = config('feishu.'.$config); - Log::info('feishu send', [$certificate,'config'=>$config,'receiveId' => $receiveId, 'title' => $title, 'content' => $content, 'href' => $href]); + Log::info('feishu send', [$certificate,'config'=>$config,'receiveId' => $receiveId, 'content' => $content]); // 飞书服务 $class = new \app\service\FeishuService($certificate['appId'], $certificate['appSecret'], $certificate['state']); @@ -160,7 +161,7 @@ class FeishuController // $receiveId = 'ou_a3b97543acbf1b28d978356bb08f85dd'; // 富文本 类型 - $content = "{ + /*$content = "{ \"zh_cn\":{ \"title\":\"$title\", \"content\":[ @@ -179,7 +180,7 @@ class FeishuController }] ] } - }"; + }";*/ // 卡片 类型 /*$content = "{ @@ -212,13 +213,88 @@ class FeishuController } }";*/ // $msgType = 'interactive'; - $msgType = 'post'; //卡片消息:interactive,富文本消息:post +// $msgType = 'post'; //卡片消息:interactive,富文本消息:post $receiveIdtype = 'open_id'; $result = $class->sendMessages($receiveId, $content, $msgType, $receiveIdtype); return json($result); } + /** + * 富文本消息 + * @param $title + * @param $content + * @param $href + * @return string + */ + public static function postMsg($title,$content,$href) + { + // 富文本 类型 + $content = "{ + \"zh_cn\":{ + \"title\":\"$title\", + \"content\":[ + [{ + \"tag\":\"text\", + \"text\":\"$content\", + \"style\":[\"bold\"] + }], + [{ + \"tag\":\"hr\" + }], + [{ + \"tag\":\"a\", + \"href\":\"$href\", + \"text\":\"查看详情\" + }] + ] + } + }"; + return $content; + } + + /** + * 卡片消息 + * @param $title + * @param $content + * @param $href + * @return string + */ + public static function interactiveMsg($title,$content,$href) + { + // 富文本 类型 + $content = "{ + \"config\":{ + \"wide_screen_mode\":true + }, + \"elements\":[{ + \"tag\":\"div\", + \"text\":{ + \"content\":\"$content \", + \"tag\":\"lark_md\" + }},{ + \"actions\":[{ + \"tag\":\"button\", + \"text\":{ + \"content\":\"立即查看\", + \"tag\":\"plain_text\" + }, + \"type\":\"primary\", + \"url\":\"$href\" + }], + \"tag\":\"action\" + }], + \"header\":{ + \"template\":\"turquoise\", + \"title\":{ + \"content\":\"📚$title\", + \"tag\":\"plain_text\" + } + } + }"; + return $content; + } + public function show() { return '
查看详情 - 示例页面
'; diff --git a/app/middleware/DataTracker.php b/app/middleware/DataTracker.php new file mode 100644 index 0000000..5d06b4d --- /dev/null +++ b/app/middleware/DataTracker.php @@ -0,0 +1,66 @@ +path() != 'user/login'){ + return $request; + } + $this->logRequestData($request); + + $response = $next($request); + + // 后置处理 +// $this->logResponseData($response); + + return $response; + } + + protected function logRequestData($request) + { + $trackData = [ + 'app_id' => 'crm/api', + 'user_id' => $request->user['id'], + 'ip' => $request->getRealIp(), + 'method' => $request->method(), + 'path' => $request->path(), + 'params' => json_encode($request->all()), + 'header' => json_encode($request->header()), + 'uuid' => $this->generateTraceId(), + 'user_agent' => $request->header('user-agent'), + 'referer' => $request->header('referer'), + ]; + $requestTrack = RequestTracks::create($trackData); + } + + protected function sendToQueue($data) + { + // 使用Redis队列 + try { + redis()->lpush('track_queue', json_encode($data)); + } catch (\Exception $e) { + // 失败降级处理 + file_put_contents( + runtime_path('logs/track_fallback.log'), + json_encode($data) . PHP_EOL, + FILE_APPEND + ); + } + } + + protected function generateTraceId() + { + return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x', + mt_rand(0, 0xffff), mt_rand(0, 0xffff), + mt_rand(0, 0xffff), + mt_rand(0, 0x0fff) | 0x4000, + mt_rand(0, 0x3fff) | 0x8000, + mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff) + ); + } +} \ No newline at end of file diff --git a/app/model/Daily.php b/app/model/Daily.php new file mode 100644 index 0000000..2ce3b46 --- /dev/null +++ b/app/model/Daily.php @@ -0,0 +1,61 @@ + 'datetime:Y-m-d H:i:s', // 指定序列化格式 + 'created_at' => 'datetime:Y-m-d H:i:s', // 指定序列化格式 + ]; + + /** + * 重新格式化时间 + * @param DateTimeInterface $date + * @return string + */ + protected function serializeDate(DateTimeInterface $date): string + { + return $date->format(Carbon::parse($date)->toDateTimeString()); + } + + + // 定义反向多对一关联 + public function user() + { + return $this->belongsTo(User::class, 'user_id', 'id'); + } +} \ No newline at end of file diff --git a/app/model/RequestTracks.php b/app/model/RequestTracks.php new file mode 100644 index 0000000..6313304 --- /dev/null +++ b/app/model/RequestTracks.php @@ -0,0 +1,57 @@ + 'datetime:Y-m-d H:i:s', // 指定序列化格式 + ]; + + /** + * 重新格式化时间 + * @param DateTimeInterface $date + * @return string + */ + protected function serializeDate(DateTimeInterface $date): string + { + return $date->format(Carbon::parse($date)->toDateTimeString()); + } + + // 禁用 updated_at 的自动更新 + public function setUpdatedAt($value) + { + return $this; // 不做任何操作 + } +} \ No newline at end of file diff --git a/app/process/Task.php b/app/process/Task.php index 97ae0fe..926a7b6 100644 --- a/app/process/Task.php +++ b/app/process/Task.php @@ -6,6 +6,7 @@ use app\controller\FeishuController; use app\model\Customer; use app\model\CustomerHis; use app\model\FeishuUser; +use app\model\RequestTracks; use app\model\User; use support\Log; use Webman\App; @@ -65,7 +66,8 @@ class Task $item->warn = 1; $item->update(); Log::info('send',['open_id'=>$item['open_id'],'url'=>$url]); - FeishuController::send($feishuUser->sign, $feishuUser->open_id, '跟进提醒', '您有新的跟进提醒', $url); + $content = FeishuController::postMsg( '跟进提醒', '您有新的跟进提醒', $url); + FeishuController::send($feishuUser->sign, $feishuUser->open_id, $content, 'post'); } } @@ -73,7 +75,7 @@ class Task }); - // 每小时执行一次 + //回收站客户转向公海 每小时执行一次 new Crontab('0 10 * * * *', function(){ echo date('Y-m-d H:i:s') . "\n"; Log::info('send high seas start',[time()]); @@ -95,6 +97,56 @@ class Task Log::info('send high seas end',[time()]); }); + //总结日报, 每天执行一次 + new Crontab('0 30 17 * * *', function () { + echo date('Y-m-d H:i:s') . "\n"; + Log::info('start daily', [time()]); + + $where = [ + ['created_at', '<=', time() - (3600 * 24)], + ]; + $requestTracks = RequestTracks::select('*, COUNT(*) as count') + ->where($where) + ->whereIn('path', ['customer/claim', 'customer/save', 'follow/save', 'customer/collect']) + ->groupBy('user_id','path') + ->orderBy('user_id', 'asc') + ->get(); + + $statistics = []; + $users = []; + $daily = ''; + foreach ($requestTracks as $item) { + $statistics[$item->user_id][$item->path] = $item->count; + $users[] = $item->user_id; + } + + foreach ($users as $userId) { + $daily .= date('Y-m-d') . "\n"; + $daily .= '领取客户'.$statistics[$userId]['customer/claim'].'位\n'; + $daily .= '创建或修改客户'.$statistics[$userId]['customer/save'].'位\n'; + $daily .= '添加跟进'.$statistics[$userId]['follow/save'].'次\n'; + $daily .= '询盘跟单客户'.$statistics[$userId]['customer/collect'].'位\n'; + $daily .= '请及时查看,30分钟后将向主管发送'; + + $feishuUser = FeishuUser::where(['user_id' => $userId])->first(); + if ($feishuUser) { + // 获取凭证 + $certificate = config('feishu.'.$feishuUser->sign); + // 使用parse_url解析URL + $parsedUrl = parse_url($certificate['rUrl']); + // 获取域名和端口 + $host = $parsedUrl['host']; + $port = isset($parsedUrl['port']) ? ':' . $parsedUrl['port'] : ''; + $url = 'http://'. $host .$port. '/pages/customer/detile?id='; + $content = FeishuController::postMsg( '跟进提醒', $daily, $url); + FeishuController::send($feishuUser->sign, $feishuUser->open_id, $content, 'post'); + } + + } + + Log::info('send high seas end', [time()]); + }); + } } \ No newline at end of file diff --git a/config/const.php b/config/const.php index 12ba463..cfcfe6e 100644 --- a/config/const.php +++ b/config/const.php @@ -2,4 +2,9 @@ return [ 'rpc_domain' => getenv('RPC_DOMAIN'), + 'event_tracking' => [ + 'follow/save', + 'customer/claim', + 'customer/save', + ], ]; \ No newline at end of file diff --git a/config/middleware.php b/config/middleware.php index 8296416..9d8c0e5 100644 --- a/config/middleware.php +++ b/config/middleware.php @@ -17,5 +17,12 @@ return [ '' => [ app\middleware\CrossDomain::class, // 全局跨域中间件 app\middleware\AuthMiddleware::class, // 鉴权中间件 +// app\middleware\DataTracker::class, //埋点 + ], + + // 路由中间件 + 'route' => [ +// 'track' => app\middleware\DataTracker::class, ] + ]; \ No newline at end of file