all(); $list = $this->model->with('user')->where(function ($query) use ($all) { if (isset($all['filter']) && !empty($all['filter'])) { foreach ($all['filter'] as $condition) { $key = $condition['key'] ?? null; $op = $condition['op'] ?? null; $value = $condition['value'] ?? null; if (!isset($key) || !isset($op) || !isset($value)) { continue; } // 等于 if ($op == 'eq') { $query->where($key, $value); } // 不等于 if ($op == 'neq') { $query->where($key, '!=', $value); } // 大于 if ($op == 'gt') { $query->where($key, '>', $value); } // 大于等于 if ($op == 'egt') { $query->where($key, '>=', $value); } // 小于 if ($op == 'lt') { $query->where($key, '<', $value); } // 小于等于 if ($op == 'elt') { $query->where($key, '<=', $value); } // 模糊搜索 if ($op == 'like') { $query->where($key, 'like', '%' . $value . '%'); } // 否定模糊搜索 if ($op == 'notlike') { $query->where($key, 'not like', '%' . $value . '%'); } // 范围搜索 if ($op == 'range') { list($from, $to) = explode(',', $value); if (empty($from) || empty($to)) { continue; } $query->whereBetween($key, [$from, $to]); } } } })->orderBy($all['sort_name'] ?? 'id', $all['sort_type'] ?? 'desc'); if (isset($all['is_export']) && !empty($all['is_export'])) { $list = $list->limit(5000)->get()->toArray(); return Excel::download(new CommonExport($list, $all['export_fields'] ?? ''), $all['file_name'] ?? '' . date('YmdHis') . '.xlsx'); } else { // 输出 $list = $list->paginate($all['page_size'] ?? 20); } return $this->success($list); } /** * @OA\Get( * path="/api/admin/supply-demand/show", * tags={"供需信息管理"}, * summary="详情", * description="", * @OA\Parameter(name="id", in="query", @OA\Schema(type="string"), required=true, description="id"), * @OA\Parameter(name="show_relation", in="query", @OA\Schema(type="string"), required=false, description="需要输出的关联关系数组,填写输出指定数据"), * @OA\Parameter(name="token", in="query", @OA\Schema(type="string"), required=true, description="token"), * @OA\Response( * response="200", * description="暂无" * ) * ) */ public function show() { $all = \request()->all(); $messages = [ 'id.required' => 'Id必填', ]; $validator = Validator::make($all, [ 'id' => 'required' ], $messages); if ($validator->fails()) { return $this->fail([ResponseCode::ERROR_PARAMETER, implode(',', $validator->errors()->all())]); } $detail = $this->model->with('user')->find($all['id']); return $this->success($detail); } /** * @OA\Post( * path="/api/admin/supply-demand/save", * tags={"供需信息管理"}, * summary="更新或新增", * @OA\Parameter(name="id", in="query", @OA\Schema(type="integer"), required=false, description="需求供应表ID(存在则更新,不存在则新增)"), * @OA\Parameter(name="title", in="query", @OA\Schema(type="string"), required=false, description="标题"), * @OA\Parameter(name="supply_demand_type_id", in="query", @OA\Schema(type="integer"), required=false, description="分类ID"), * @OA\Parameter(name="content", in="query", @OA\Schema(type="string"), required=false, description="内容"), * @OA\Parameter(name="tag", in="query", @OA\Schema(type="string"), required=false, description="标签"), * @OA\Parameter(name="wechat", in="query", @OA\Schema(type="string"), required=false, description="微信号"), * @OA\Parameter(name="mobile", in="query", @OA\Schema(type="string"), required=false, description="电话"), * @OA\Parameter(name="email", in="query", @OA\Schema(type="string"), required=false, description="邮箱"), * @OA\Parameter(name="status", in="query", @OA\Schema(type="integer"), required=false, description="状态0待审核1通过2拒绝3退回修改4永久隐藏"), * @OA\Response( * response=200, * description="操作成功" * ) * ) */ public function save() { return parent::save(); } /** * @OA\Get( * path="/api/admin/supply-demand/destroy", * tags={"供需信息管理"}, * summary="删除", * description="", * @OA\Parameter(name="id", in="query", @OA\Schema(type="string"), required=true, description="id"), * @OA\Parameter(name="token", in="query", @OA\Schema(type="string"), required=true, description="token"), * @OA\Response( * response="200", * description="暂无" * ) * ) */ public function destroy() { return parent::destroy(); } /** * @OA\Get( * path="/api/admin/supply-demand/chart", * tags={"供需信息管理"}, * summary="交互统计", * description="", * @OA\Parameter(name="start_date", in="query", @OA\Schema(type="string"), required=true, description="开始日期"), * @OA\Parameter(name="end_date", in="query", @OA\Schema(type="string"), required=true, description="结束日期"), * @OA\Parameter(name="type", in="query", @OA\Schema(type="string"), required=true, description="type类型"), * @OA\Parameter(name="page_size", in="query", @OA\Schema(type="string"), required=false, description="每页显示的条数"), * @OA\Parameter(name="page", in="query", @OA\Schema(type="string"), required=false, description="页码"), * @OA\Parameter(name="token", in="query", @OA\Schema(type="string"), required=true, description="token"), * @OA\Response( * response="200", * description="暂无" * ) * ) */ public function chart() { $start = \Illuminate\Support\Carbon::parse(request('start_date', date('Y-m-d')))->startOfDay(); $end = \Illuminate\Support\Carbon::parse(request('end_date', date('Y-m-d')))->endOfDay(); if ($start->gt($end)) { return $this->fail([ResponseCode::ERROR_PARAMETER, '开始日期不能晚于结束日期']); } $type = request('type'); $days = $start->diffInDays($end->copy()->startOfDay()) + 1; $prevEnd = $start->copy()->subDay()->endOfDay(); $prevStart = $start->copy()->subDays($days); $supplyQuery = function () use ($type) { return SupplyDemand::when($type, function ($q) use ($type) { $q->where('type', $type); }); }; // 发布数按发布时间,私信与双向交互按消息发生时间统计。 $messageQuery = function ($from, $to) use ($type) { return Message::whereBetween('created_at', [$from, $to]) ->whereColumn('user_id', '<>', 'to_user_id') ->whereHas('supplyDemand', function ($q) use ($type) { if ($type) $q->where('type', $type); }); }; $supplyDemandCount = $supplyQuery()->whereBetween('created_at', [$start, $end])->count(); $prevSupplyDemandCount = $supplyQuery()->whereBetween('created_at', [$prevStart, $prevEnd])->count(); $messageCount = $messageQuery($start, $end)->count(); $prevMessageCount = $messageQuery($prevStart, $prevEnd)->count(); // 同一供需、同一会话中双方均有发言,计一次。 $interactionCount = $messageQuery($start, $end)->whereNotNull('dialogue_id') ->select('supply_demand_id', 'dialogue_id')->groupBy('supply_demand_id', 'dialogue_id') ->havingRaw('COUNT(DISTINCT user_id) >= 2')->get()->count(); $prevInteractionCount = $messageQuery($prevStart, $prevEnd)->whereNotNull('dialogue_id') ->select('supply_demand_id', 'dialogue_id')->groupBy('supply_demand_id', 'dialogue_id') ->havingRaw('COUNT(DISTINCT user_id) >= 2')->get()->count(); $supplyDemandGrowthRate = $this->calculateGrowthRate($supplyDemandCount, $prevSupplyDemandCount); $messageGrowthRate = $this->calculateGrowthRate($messageCount, $prevMessageCount); $interactionGrowthRate = $this->calculateGrowthRate($interactionCount, $prevInteractionCount); // 包含本期发布的信息,以及历史发布但本期有咨询的信息。 $list = $supplyQuery()->with(['user', 'messages' => function ($q) use ($start, $end) { $q->whereBetween('created_at', [$start, $end]) ->whereColumn('user_id', '<>', 'to_user_id') ->with(['dialogue.user', 'dialogue.toUser'])->orderBy('created_at', 'desc'); }])->where(function ($q) use ($start, $end) { $q->whereBetween('created_at', [$start, $end])->orWhereHas('messages', function ($m) use ($start, $end) { $m->whereBetween('created_at', [$start, $end])->whereColumn('user_id', '<>', 'to_user_id'); }); })->orderBy('id', 'desc')->paginate(request('page_size', 20)); foreach ($list as $item) { $dialogues = $item->messages->pluck('dialogue')->filter()->unique('id')->values(); $item->setRelation('dialogues', $dialogues); $item->unsetRelation('messages'); } return $this->success([ 'list' => $list, 'supply_demand_count' => $supplyDemandCount, 'prev_supply_demand_count' => $prevSupplyDemandCount, 'supply_demand_growth_rate' => $supplyDemandGrowthRate, 'message_count' => $messageCount, 'prev_message_count' => $prevMessageCount, 'message_growth_rate' => $messageGrowthRate, 'interaction_count' => $interactionCount, 'prev_interaction_count' => $prevInteractionCount, 'interaction_growth_rate' => $interactionGrowthRate, ]); } /** * 计算增长率 * @param int $current 当前数值 * @param int $previous 上期数值 * @return array 包含增长率和增长状态 */ private function calculateGrowthRate($current, $previous) { if ($previous == 0) { if ($current > 0) { return [ 'rate' => 100, 'status' => 'increase', 'display' => '+100%' ]; } else { return [ 'rate' => 0, 'status' => 'stable', 'display' => '0%' ]; } } $rate = round(($current - $previous) / $previous * 100, 2); if ($rate > 0) { $status = 'increase'; $display = '+' . $rate . '%'; } elseif ($rate < 0) { $status = 'decrease'; $display = $rate . '%'; } else { $status = 'stable'; $display = '0%'; } return [ 'rate' => $rate, 'status' => $status, 'display' => $display ]; } /** * @OA\Get( * path="/api/admin/supply-demand/message-list", * tags={"供需信息管理"}, * summary="消息列表", * description="", * @OA\Parameter(name="to_user_id", in="query", @OA\Schema(type="string"), required=true, description="接收人"), * @OA\Parameter(name="user_id", in="query", @OA\Schema(type="string"), required=true, description="发起人"), * @OA\Parameter(name="page_size", in="query", @OA\Schema(type="string"), required=false, description="每页显示的条数"), * @OA\Parameter(name="page", in="query", @OA\Schema(type="string"), required=false, description="页码"), * @OA\Parameter(name="token", in="query", @OA\Schema(type="string"), required=true, description="token"), * @OA\Response( * response="200", * description="暂无" * ) * ) */ public function messageList() { $all = \request()->all(); $messages = [ 'to_user_id.required' => '接收人必填', 'user_id.required' => '接收人必填', ]; $validator = Validator::make($all, [ 'user_id' => 'required', 'to_user_id' => 'required' ], $messages); if ($validator->fails()) { return $this->fail([ResponseCode::ERROR_PARAMETER, implode(',', $validator->errors()->all())]); } // 获取会话id $dialogue = Dialogue::where(function ($query) use ($all) { $query->where('user_id', $all['user_id'])->where('to_user_id', $all['to_user_id']); })->orWhere(function ($query) use ($all) { $query->where('user_id', $all['to_user_id'])->where('to_user_id', $all['user_id']); })->first(); if (empty($dialogue)) { return $this->fail([ResponseCode::ERROR_BUSINESS, '会话不存在']); } $message = Message::with([ 'user' => function ($query) { $query->select('id', 'nickname', 'name', 'headimgurl', 'username'); }, 'toUser' => function ($query) { $query->select('id', 'nickname', 'name', 'headimgurl', 'username'); } ])->where(function ($query) use ($dialogue) { $query->where('dialogue_id', $dialogue->id); if (request('supply_demand_id')) { $query->where('supply_demand_id', request('supply_demand_id')); } })->orderBy($all['sort_name'] ?? 'id', $all['sort_type'] ?? 'desc') ->paginate($all['page_size'] ?? 20); return $this->success(compact('message')); } }