| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617 |
- <?php
- namespace app\api\service;
- use app\admin\model\dynamic\Dynamic;
- use app\admin\model\dynamic\Like;
- use app\api\model\massager\Massager;
- use app\api\model\massager\Collect;
- use app\api\model\massager\Comment;
- use app\api\model\massager\Work;
- use app\api\model\massager\WorkPeriod;
- use app\api\model\order\Order;
- use app\api\model\service\Service;
- use redis\RedisClient;
- class MassagerService extends BaseService
- {
- private $massagerModel;
- private $colletModel;
- private $commentModel;
- private $orderModel;
- private $workModel;
- public function __construct()
- {
- $this->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_null($is_add_clock) || $is_add_clock != 1) {
- $query->where("is_add_clock", 0);
- }
- $q = ["id", "<>", "276"];
- $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()
- ];
- }
- }
|