massagerModel = new Massager(); $this->colletModel = new Collect(); $this->commentModel = new Comment(); $this->orderModel = new Order(); $this->workModel = new Work(); } /** * 合并助教数据代码 * @param $paginate * @param $user_id * @return array */ private function mergeMassager($paginate, $user_id) { $items = $paginate->items(); $collet_relation = []; if ($user_id) { $collet_relation = $this->colletModel->fetchByUserIdAndMassagerIds($user_id, array_map(function ($data) { return $data['id']; }, $items)); $collet_relation = array_reduce($collet_relation, function ($p, $cur) { $p[$cur['massager_id']] = 1; return $p; }, []); } $services = (new \app\admin\model\Service())->where([ "duration_minute" => 120, "type" => \E_SERVICE_TYPE::App, ])->select(); $services = array_reduce($services, function ($p, $cur) { $p[$cur["level"]] = $cur["real_price"]; return $p; }, []); foreach ($items as &$item) { $recently_work_date = $this->workDateRange(array_map(function ($data) { return [strtotime($data["service_start_date"]), strtotime($data["service_end_date"])]; }, $item["nearly_two_days_orders"]), true); $item['recently_work_date'] = $recently_work_date; $item['is_can_collet'] = !isset($collet_relation[$item['id']]); $item["price"] = isset($services[$item['level']]) ? $services[$item['level']] : 260; unset($item['nearly_two_days_orders']); } return [ array_map(function ($data) { return Massager::fmtMassager($data); }, $items), $paginate->total() ]; } /** * 判断是否能够选择 * @param $in_time * @param $ranges * @return bool */ private function checkCanOption($in_time, $ranges) { foreach ($ranges as $range) { if ($in_time >= $range[0] && $in_time <= $range[1]) { return false; } } return true; } /** * 获取可助教工作的时间段 * @param array $order_date_range * @param bool $recently * @return array|false|string|null */ private function workDateRange(array $order_date_range, $recently = true) { $now_time = time(); $range = []; $max_time = strtotime(date("Y-m-d 23:59:59")); $acc_time = $now_time; $recently_work_date = null; while (true) { if ($acc_time === $now_time) { $y_m_d = date("Y-m-d", $acc_time); $now_minute = date("i", $acc_time); if ($now_minute < 30) { $range["$y_m_d"] = ["date" => $y_m_d, "range" => [["value" => date("H:30", $acc_time), "can_option" => false]]]; } } $acc_time += (60 * 60); $y_m_d = date("Y-m-d", $acc_time); $y_m_d_h = date("Y-m-d H:00:00", $acc_time); $v_1 = [ "value" => date("H:00", strtotime($y_m_d_h)), "can_option" => (($acc_time - (60 * 60)) === $now_time) ? false : $this->checkCanOption(strtotime($y_m_d_h), $order_date_range) ]; $v_2 = [ "value" => date("H:30", $acc_time), "can_option" => $this->checkCanOption($acc_time + (30 * 60), $order_date_range) ]; if ($recently) { if ($v_1["can_option"]) { $recently_work_date = "$y_m_d_h"; break; } if ($v_2["can_option"]) { $recently_work_date = date("Y-m-d H:30:00", $acc_time); break; } } if (isset($range["$y_m_d"])) { array_push($range["$y_m_d"]["range"], $v_1, $v_2); } else { $range["$y_m_d"] = [ "date" => $y_m_d, "range" => [$v_1, $v_2] ]; } if ($acc_time >= $max_time) break; } if ($recently) { return $recently_work_date; } $format_range = []; $keys = array_keys($range); for ($i = 0; $i <= count($keys) - 1; $i++) { $key = $keys[$i]; $format_range[$i] = $range[$key]; } return $format_range; } /** * 获取App助教 * @param null $user_id * @param array $params * @param $gender * @return array */ public function fetchAppMassager($user_id, array $params, $gender) { $paginate = $this->massagerModel->fetchAppMassager( $params['city_code'], $params['lng_lat'], isset($params['search_text']) && mb_strlen($params['search_text']) > 0 ? $params['search_text'] : null, isset($params['distance']) && is_numeric($params["distance"]) ? $params['distance'] : 300, isset($params['service_status']) && is_numeric($params['service_status']) ? $params['service_status'] : null, $gender, isset($params['free_travel']) && is_numeric($params['free_travel']) ? $params['free_travel'] : null, isset($params['hot']) && is_numeric($params['hot']) ? $params['hot'] : null, isset($params['service_id']) && is_numeric($params['service_id']) ? $params['service_id'] : null, $params['sort'] ?? null, $params['page'] ?? 1, $params['size'] ?? 10, $user_id, isset($params['level']) && is_numeric($params['level']) ? $params['level'] : null ); return $this->mergeMassager($paginate, $user_id); } /** * 获取球房助教 * @param array $params * @param $gender * @param null $user_id * @return array */ public function fetchStoreMassager(array $params, $gender, $user_id = null) { $paginate = $this->massagerModel->fetchStoreMassager( $params['store_id'], $params['service_id'] ?? null, $params['search_text'] ?? null, $params['hot'] ?? null, $gender, $params['page'] ?? 1, $params['size'] ?? 10 ); return $this->mergeMassager($paginate, $user_id); } /** * 获取助教上班的的时间 * @param $id * @return |null */ public function getWorkDateRange($id) { $massager = $this->massagerModel->getMassager($id, ["store", "nearlyTwoDaysOrders"]); if (!$massager) return $this->ok([]); $order_date_range = array_map(function ($data) { return [strtotime($data["service_start_date"]), strtotime($data["service_end_date"])]; }, $massager["nearlyTwoDaysOrders"]); $no_work_ranges = (new WorkPeriod())->where("massager_id", $id)->where("end_time", ">", time())->select(); $ranges_ = array_map(function ($data) { return [$data["start_time"], $data["end_time"]]; }, $no_work_ranges); $range = $this->workDateRange(array_merge($order_date_range, $ranges_), false); return $this->ok([ "id" => $massager->id, "range" => $range ]); } /** * 获取单个助教详情 * @param $massager_id * @param null $user_id * @return |null */ public function get($massager_id, $user_id = null, $is_add_clock = null) { $massager = $this->massagerModel->fetchHiddenAndNormal($massager_id); if (null === $massager) return null; $massager['is_can_collet'] = true; if ($user_id) { $relation = $this->colletModel->getByUserIdAndMassagerId($user_id, $massager_id); if ($relation) $massager['is_can_collet'] = false; } $serviceModel = new Service(); $query = $serviceModel ->where([ "level" => $massager["level"], "type" => \E_SERVICE_TYPE::App ]); if ($is_add_clock != 1) { $query->where("is_add_clock", 0); } $exist = null; $items = collection($query ->order('sort', 'desc') ->order('real_price', 'ASC')->select())->toArray(); $membership_discount_rate = config("site.membership_discount_rate") / 100; $available_services = []; foreach ($items as &$item) { $item["membership_discount_price"] = fixed2Float($item["real_price"] * $membership_discount_rate); if ($item["id"] != 276) { $available_services[] = $item; } else { if ($massager["store_id"] == $exist && $exist > 0) { $available_services[] = $item; } } } $massager["services"] = $available_services; return Massager::fmtMassager($massager); } /** * @param $massager_id * @param $page * @param $size * @return array * @throws \think\exception\DbException */ public function fetchMassagerComment($massager_id, $page, $size) { $paginate = $this->commentModel->fetchByMassagerId($massager_id, $page, $size); $items = $paginate->items(); return [ array_map(function ($data) { $nickname = "匿名用户"; $avatar = null; if ($data['user'] && 0 == $data['is_anonymity']) { $nickname = $data['user']['nickname']; $avatar = $data['user']['avatar']; } $data['user_nickname'] = $nickname; $data['user_avatar'] = $avatar; unset($data['user']); return $data; }, $items), $paginate->total() ]; } public function getMassagerTentativeNowLevelConfiguration($m_id, $city_code, $intraday = null) { $configuration = [ [0, 50], [300, 60], [500, 65], [700, 70] ]; $level_index = 1; $profit_rate = 50; $next_configuration = 288; $sumPerformanceByNowMonth = $this->orderModel->sumPerformanceByIntraday($m_id, $city_code, $intraday); if ($sumPerformanceByNowMonth > $configuration[0][0]) { for ($index = 0; $index < count($configuration); $index++) { $item = $configuration[$index]; if ($sumPerformanceByNowMonth > $item[0]) { $level_index += 1; $profit_rate = $item[1]; $next_configuration = isset($configuration[$index + 1]) ? $configuration[$index + 1][0] : null; } } $level_index -= 1; } return [ "level" => $level_index, "profit_rate" => $profit_rate, "next_configuration" => $next_configuration, "sum_performance_by_now_month" => $sumPerformanceByNowMonth, ]; } public function getMassagerNowLevelConfiguration($m_id, $city_code, $y = null, $m = null) { $configuration = array_map(function ($data) { $performance = explode("|", $data["月业绩/元"]); $duration = explode("|", $data["在岗时长/小时"]); $reorder = explode("|", $data["加钟比例(%)"]); $praise = explode("|", $data["好评率(%)"]); $chargeback = explode("|", $data["退单率(%)"]); return [ [ "condition" => isset($performance[0]) && is_numeric($performance[0]) ? (float)$performance[0] : 6500, "rate" => isset($performance[1]) && is_numeric($performance[1]) ? (float)$performance[1] : 55 ], [ "condition" => isset($duration[0]) && is_numeric($duration[0]) ? (float)$duration[0] : 100, "rate" => isset($duration[1]) && is_numeric($duration[1]) ? (float)$duration[1] : 1 ], [ "condition" => isset($reorder[0]) && is_numeric($reorder[0]) ? (float)$reorder[0] : 30, "rate" => isset($reorder[1]) && is_numeric($reorder[1]) ? (float)$reorder[1] : 1 ], [ "condition" => isset($praise[0]) && is_numeric($praise[0]) ? (float)$praise[0] : 30, "rate" => isset($praise[1]) && is_numeric($praise[1]) ? (float)$praise[1] : 1 ], [ "condition" => isset($chargeback[0]) && is_numeric($chargeback[0]) ? (float)$chargeback[0] : 30, "rate" => isset($chargeback[1]) && is_numeric($chargeback[1]) ? (float)$chargeback[1] : -1 ] ]; }, [ config("site.massager_level_1"), config("site.massager_level_2"), config("site.massager_level_3"), config("site.massager_level_4"), config("site.massager_level_5"), ]); $sumPerformanceByNowMonth = $this->orderModel->sumPerformanceByNowMonth($m_id, $city_code, $y, $m); $sumDurationByNowMonth = $this->workModel->sumByNowMonth($m_id, $y, $m); $reorderRateByNowMonth = $this->orderModel->reorderRateByNowMonth($m_id, $city_code, $y, $m); $praiseRateByNowMonth = $this->orderModel->countByPraiseRate($m_id, $city_code, $y, $m); $chargebackRateByNowMonth = $this->orderModel->chargebackRate($m_id, $city_code, $y, $m); $profit_rate = config("site.massager_basics_deposit_rate") ?? 50; $level_index = 0; $next_configuration = null; for ($i = 0; $i < count($configuration); $i++) { $now_configuration = $configuration[$i]; if ($sumPerformanceByNowMonth >= $now_configuration[0]["condition"]) { $level_index += 1; $next_configuration = isset($configuration[$i + 1]) ? $configuration[$i + 1] : null; $profit_rate = $now_configuration[0]["rate"]; $profit_rate += ($sumDurationByNowMonth >= $now_configuration[1]["condition"] ? $now_configuration[1]["rate"] : 0); $profit_rate += ($reorderRateByNowMonth >= $now_configuration[2]["condition"] ? $now_configuration[2]["rate"] : 0); $profit_rate += ($praiseRateByNowMonth >= $now_configuration[3]["condition"] ? $now_configuration[3]["rate"] : 0); $profit_rate += ($chargebackRateByNowMonth >= $now_configuration[4]["condition"] ? $now_configuration[4]["rate"] : 0); } else { break; } } return [ "level" => $level_index, "profit_rate" => $profit_rate, "next_configuration" => $next_configuration, "sum_performance_by_now_month" => $sumPerformanceByNowMonth, ]; } public function getProfitRate($m_id, $city_code, $y = null, $m = null, $intraday = null): float { $massager = $this->massagerModel->findById($m_id); if (!$massager) return 0; if ($massager["fixed_profit_rate"] > 0) return $massager["fixed_profit_rate"]; // DOTO: 临时替换 $level_info = $this->getMassagerTentativeNowLevelConfiguration($m_id, $city_code, $intraday); // $level_info = $this->getMassagerNowLevelConfiguration($m_id, $city_code, $y, $m); return $level_info["profit_rate"]; } public function interiorScoreLevel($score) { $config = config("site.massager_review_level"); $keys = array_keys($config); $level_desc = "不及格"; foreach ($keys as $key) { if ($score >= (float)$key) { $level_desc = $config[$key]; } else { break; } } return $level_desc; } public function updateInteriorScore($m_id, $year, $month) { $config = config("site.massager_review_score"); $fmt_config = [ "duration" => explode("|", $config["时长/天"] ?? "6|2"), "praise" => explode("|", $config["评论/好评"] ?? "1|2"), "negative" => explode("|", $config["评论/差评"] ?? "1|-10"), "performance" => explode("|", $config["业绩/天"] ?? "500|2"), "reorder" => explode("|", $config["项目加钟"] ?? "1|2") ]; $byDuration = $this->interiorScoreByDuration($m_id, $year, $month, $fmt_config["duration"]); $byPraise = $this->interiorScoreByPraise($m_id, $year, $month, $fmt_config["praise"]); $byNegative = $this->interiorScoreByNegative($m_id, $year, $month, $fmt_config["negative"]); $byPerformance = $this->interiorScoreByPerformance($m_id, $year, $month, $fmt_config["performance"]); $byReorder = $this->interiorScoreByReorder($m_id, $year, $month, $fmt_config["reorder"]); $total_score = fixed2Float( $byDuration["score"] + $byPraise["score"] + $byNegative["score"] + $byPerformance["score"] + $byReorder["score"] ); if (date("Y") == $year && date("m") == $month) { $this->massagerModel->update([ "interior_score" => $total_score ], ["id" => $m_id]); } return [ "total_interior_score" => $total_score, "level_desc" => $this->interiorScoreLevel($total_score), "items" => [ "byDuration" => $byDuration, "byPraise" => $byPraise, "byNegative" => $byNegative, "byPerformance" => $byPerformance, "byReorder" => $byReorder ] ]; } public function interiorScoreByDuration($m_id, $year, $month, $config) { $last_day = date("t", strtotime("$year-$month-01 00:00:00")); $starttime = strtotime("$year-$month-01 00:00:00"); $endtime = strtotime("$year-$month-$last_day 23:59:59"); $works = (new Work()) ->where("massager_id", $m_id) ->where("clock_in_time", ">=", $starttime) ->where("clock_off_time", "<=", $endtime) ->select(); $clocks_times = []; foreach ($works as $work) { $times = splittingDate($work["clock_in_time"], $work["clock_off_time"]); $clocks_times = array_merge($clocks_times, $times); } $clocks_times = array_merge(array_unique($clocks_times)); $every_days = []; $DAY_TIME = 24 * 60 * 60; $start_time_copy = $starttime; while ($start_time_copy <= $endtime) { array_push($every_days, date("Y-m-d", $start_time_copy)); $start_time_copy += $DAY_TIME; } $score = 0; $intersections = []; foreach ($every_days as $now_day) { $now_day_times = splittingDate(strtotime("$now_day 00:00:00"), strtotime("$now_day 23:59:59")); $intersection = array_intersect($now_day_times, $clocks_times); $meet = count($intersection) >= (fixed2Float((($config[0] ?? 6) * 60) / 10) + 1); if ($meet) { $intersections[] = [ "date" => $now_day, "count" => count($intersection), ]; $score += $config[1] ?? 2; } } return [ "score" => fixed2Float($score), "details" => $intersections ]; } public function interiorScoreByPraise($m_id, $year, $month, $config) { $negative = $this->commentModel->fetchPraiseCommentByMassager($m_id, $year, $month); $count = count($negative); $item_score = $config[1] / $config[0]; return ["score" => fixed2Float($item_score * $count), "details" => $negative]; } public function interiorScoreByNegative($m_id, $year, $month, $config) { $negative = $this->commentModel->fetchNegativeCommentByMassager($m_id, $year, $month); $count = count($negative); $item_score = $config[1] / $config[0]; return ["score" => fixed2Float($item_score * $count), "details" => $negative]; } public function interiorScoreByPerformance($m_id, $year, $month, $config) { $all_orders = $this->orderModel->fetchByMassager($m_id, $year, $month); $fmt = []; foreach ($all_orders as $order) { $key = date("Y-m-d", $order["pay_time"]); if (isset($fmt[$key])) { $fmt[$key] += $order["total_real_amount"]; } else { $fmt[$key] = $order["total_real_amount"]; } } $score = 0; $details = []; foreach ($fmt as $key => $value) { if ($value >= $config[0]) { $details[] = [ "date" => $key, "count" => $value, ]; $score += $config[1]; } } return ["score" => fixed2Float($score), "details" => $details]; } public function interiorScoreByReorder($m_id, $year, $month, $config) { $all_orders = $this->orderModel->fetchReorderByMassager($m_id, $year, $month); $count = count($all_orders); $item_score = $config[1] / $config[0]; return ["score" => fixed2Float($item_score * $count), "details" => $all_orders]; } public function fetchSelfDynamics($m_id, $page = 1, $size = 10) { $paginate = (new Dynamic()) ->where("massager_id", $m_id) ->with(["comments", "likes"]) ->page($page) ->paginate($size); return [ $paginate->items(), $paginate->total() ]; } public function fetchFansNumberAndPraiseNumber($m_id) { return [ "fansNumber" => Collect::where("massager_id", $m_id)->count(), "praiseNumber" => Like::where("massager_id", $m_id)->count() ]; } }