refactor: trim unused smoke API fields
This commit is contained in:
@@ -44,51 +44,20 @@ class Smoke extends BaseController
|
|||||||
$homeSummary = $this->smokeService->getHomeSummary($uid, $now);
|
$homeSummary = $this->smokeService->getHomeSummary($uid, $now);
|
||||||
$motivation = $this->smokeService->motivation($uid, $profileView['profile'], $now);
|
$motivation = $this->smokeService->motivation($uid, $profileView['profile'], $now);
|
||||||
|
|
||||||
$adviceDate = Support::dateOnly($now->modify('-1 day'));
|
|
||||||
$adviceCard = [
|
|
||||||
'title' => '智能控烟建议',
|
|
||||||
'date' => $adviceDate->format(Support::DATE_LAYOUT),
|
|
||||||
'message' => '',
|
|
||||||
'model' => '',
|
|
||||||
'status' => 'empty',
|
|
||||||
];
|
|
||||||
|
|
||||||
try {
|
|
||||||
$advice = $this->smokeAiService->getOrGenerateAdvice($user, $adviceDate, 'v2');
|
|
||||||
$adviceCard['message'] = (string) ($advice['advice'] ?? '');
|
|
||||||
$adviceCard['model'] = (string) ($advice['model'] ?? '');
|
|
||||||
$adviceCard['status'] = 'available';
|
|
||||||
} catch (\RuntimeException $e) {
|
|
||||||
if ($e->getCode() === 403) {
|
|
||||||
$adviceCard['status'] = 'locked';
|
|
||||||
} elseif ($e->getCode() === 400) {
|
|
||||||
$adviceCard['status'] = 'no_data';
|
|
||||||
} else {
|
|
||||||
$adviceCard['status'] = 'unavailable';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$timer = [
|
$timer = [
|
||||||
'label' => '距上次抽烟',
|
|
||||||
'last_smoke_at' => (string) ($homeSummary['last_smoke_at'] ?? ''),
|
|
||||||
'seconds_since_last' => (int) ($homeSummary['seconds_since_last'] ?? -1),
|
'seconds_since_last' => (int) ($homeSummary['seconds_since_last'] ?? -1),
|
||||||
'next_suggested_at' => (string) ($defaultSuggestion['next_smoke_at'] ?? ''),
|
'next_suggested_at' => (string) ($defaultSuggestion['next_smoke_at'] ?? ''),
|
||||||
'next_suggested_clock' => Support::formatClock((string) ($defaultSuggestion['next_smoke_at'] ?? '')),
|
'next_suggested_clock' => Support::formatClock((string) ($defaultSuggestion['next_smoke_at'] ?? '')),
|
||||||
'not_before_at' => (string) ($defaultSuggestion['next_smoke_at'] ?? ''),
|
|
||||||
'suggestion_source' => 'default',
|
'suggestion_source' => 'default',
|
||||||
'suggestion_algorithm' => (string) ($defaultSuggestion['algorithm'] ?? ''),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
$cachedAiNext = $this->smokeAiService->getCachedNextSmoke($user, $planDate, 'v1');
|
$cachedAiNext = $this->smokeAiService->getCachedNextSmoke($user, $planDate, 'v1');
|
||||||
if ($cachedAiNext) {
|
if ($cachedAiNext) {
|
||||||
$timer['suggestion_source'] = 'ai';
|
$timer['suggestion_source'] = 'ai';
|
||||||
$timer['suggestion_algorithm'] = 'ai_next_smoke_v1';
|
|
||||||
$timer['next_suggested_at'] = (string) ($cachedAiNext['suggested_at'] ?? '');
|
$timer['next_suggested_at'] = (string) ($cachedAiNext['suggested_at'] ?? '');
|
||||||
$timer['next_suggested_clock'] = Support::formatClock((string) ($cachedAiNext['suggested_at'] ?? ''));
|
$timer['next_suggested_clock'] = Support::formatClock((string) ($cachedAiNext['suggested_at'] ?? ''));
|
||||||
$timer['not_before_at'] = (string) ($cachedAiNext['not_before_at'] ?? '');
|
|
||||||
$timer['ai_time_nodes'] = $cachedAiNext['time_nodes'] ?? [];
|
$timer['ai_time_nodes'] = $cachedAiNext['time_nodes'] ?? [];
|
||||||
$timer['ai_advice'] = (string) ($cachedAiNext['advice'] ?? '');
|
$timer['ai_advice'] = (string) ($cachedAiNext['advice'] ?? '');
|
||||||
$timer['ai_model'] = (string) ($cachedAiNext['model'] ?? '');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$dailySummaryRecord = $this->smokeAiService->getCachedByType($uid, SmokeAiService::TYPE_DAILY_SUMMARY, $planDate, 'v1');
|
$dailySummaryRecord = $this->smokeAiService->getCachedByType($uid, SmokeAiService::TYPE_DAILY_SUMMARY, $planDate, 'v1');
|
||||||
@@ -103,14 +72,6 @@ class Smoke extends BaseController
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Response::success([
|
return Response::success([
|
||||||
'greeting' => $this->buildGreeting((string) ($user['nickname'] ?? ''), (string) ($user['avatar_url'] ?? ''), $now),
|
|
||||||
'profile' => $profileView,
|
|
||||||
'advice_card' => $adviceCard,
|
|
||||||
'campaign_card' => [
|
|
||||||
'title' => '绿色生活,从戒烟开始',
|
|
||||||
'subtitle' => 'BRAND CAMPAIGN',
|
|
||||||
'badge' => '广告',
|
|
||||||
],
|
|
||||||
'timer' => $timer,
|
'timer' => $timer,
|
||||||
'summary' => [
|
'summary' => [
|
||||||
'today_count' => (int) ($homeSummary['today_count'] ?? 0),
|
'today_count' => (int) ($homeSummary['today_count'] ?? 0),
|
||||||
@@ -118,18 +79,9 @@ class Smoke extends BaseController
|
|||||||
'resisted_count' => (int) ($homeSummary['resisted_count'] ?? 0),
|
'resisted_count' => (int) ($homeSummary['resisted_count'] ?? 0),
|
||||||
'reduced_from_yesterday' => (int) ($homeSummary['reduced_from_yesterday'] ?? 0),
|
'reduced_from_yesterday' => (int) ($homeSummary['reduced_from_yesterday'] ?? 0),
|
||||||
'exceeded_yesterday' => (bool) ($homeSummary['exceeded_yesterday'] ?? false),
|
'exceeded_yesterday' => (bool) ($homeSummary['exceeded_yesterday'] ?? false),
|
||||||
'profile_completed' => (bool) ($profileView['is_completed'] ?? false),
|
|
||||||
],
|
],
|
||||||
'daily_summary' => $dailySummary,
|
'daily_summary' => $dailySummary,
|
||||||
'motivation' => $motivation,
|
'motivation' => $motivation,
|
||||||
'quick_actions' => [
|
|
||||||
['type' => 'log_smoke', 'title' => '记录抽烟', 'primary' => false],
|
|
||||||
['type' => 'resist', 'title' => '想抽忍住了', 'primary' => true],
|
|
||||||
],
|
|
||||||
'data_sources' => [
|
|
||||||
'ai_advice_date' => $adviceDate->format(Support::DATE_LAYOUT),
|
|
||||||
'plan_date' => $planDate->format(Support::DATE_LAYOUT),
|
|
||||||
],
|
|
||||||
]);
|
]);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return Response::error($e->getMessage(), $e->getCode() ?: 500);
|
return Response::error($e->getMessage(), $e->getCode() ?: 500);
|
||||||
@@ -163,24 +115,6 @@ class Smoke extends BaseController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createResistedLog()
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
return Response::success($this->smokeService->createLog($this->getCurrentSmtUserId(), $this->request->post(), true));
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
return Response::error($e->getMessage(), $e->getCode() ?: 500);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function readLog(int $id)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
return Response::success($this->smokeService->getLog($this->getCurrentSmtUserId(), $id));
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
return Response::error($e->getMessage(), $e->getCode() ?: 500);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function logs()
|
public function logs()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
@@ -190,15 +124,6 @@ class Smoke extends BaseController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function latestLogs()
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
return Response::success($this->smokeService->latestLogs($this->getCurrentSmtUserId(), (int) $this->request->get('limit', 20)));
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
return Response::error($e->getMessage(), $e->getCode() ?: 500);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function updateLog(int $id)
|
public function updateLog(int $id)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
@@ -217,15 +142,6 @@ class Smoke extends BaseController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function dashboard()
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
return Response::success($this->smokeService->dashboard($this->getCurrentSmtUserId(), $this->request->get()));
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
return Response::error($e->getMessage(), $e->getCode() ?: 500);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function stats()
|
public function stats()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
@@ -236,17 +152,7 @@ class Smoke extends BaseController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function motivation()
|
public function aiNextSmokeTime()
|
||||||
{
|
|
||||||
try {
|
|
||||||
$uid = $this->getCurrentSmtUserId();
|
|
||||||
return Response::success($this->smokeService->motivation($uid, $this->smokeService->getProfile($uid)));
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
return Response::error($e->getMessage(), $e->getCode() ?: 500);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function nextSmokeTime()
|
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$user = $this->getCurrentSmtUser();
|
$user = $this->getCurrentSmtUser();
|
||||||
@@ -264,18 +170,11 @@ class Smoke extends BaseController
|
|||||||
|
|
||||||
$profileView = $this->smokeService->getProfileView($uid);
|
$profileView = $this->smokeService->getProfileView($uid);
|
||||||
$defaultSuggestion = $this->smokeService->getDefaultNextSuggestion($uid, $now, $planDate, $profileView);
|
$defaultSuggestion = $this->smokeService->getDefaultNextSuggestion($uid, $now, $planDate, $profileView);
|
||||||
$homeSummary = $this->smokeService->getHomeSummary($uid, $now);
|
|
||||||
|
|
||||||
$response = [
|
$response = [
|
||||||
'source' => 'default',
|
'source' => 'default',
|
||||||
'not_before_at' => (string) ($defaultSuggestion['next_smoke_at'] ?? ''),
|
|
||||||
'suggested_at' => (string) ($defaultSuggestion['next_smoke_at'] ?? ''),
|
'suggested_at' => (string) ($defaultSuggestion['next_smoke_at'] ?? ''),
|
||||||
'last_smoke_at' => (string) ($homeSummary['last_smoke_at'] ?? ''),
|
'time_nodes' => [],
|
||||||
'today_count' => (int) ($homeSummary['today_count'] ?? 0),
|
'advice' => '',
|
||||||
'resisted_count' => (int) ($homeSummary['resisted_count'] ?? 0),
|
|
||||||
'reduced_from_yesterday' => (int) ($homeSummary['reduced_from_yesterday'] ?? 0),
|
|
||||||
'exceeded_yesterday' => (bool) ($homeSummary['exceeded_yesterday'] ?? false),
|
|
||||||
'default' => $defaultSuggestion,
|
|
||||||
];
|
];
|
||||||
|
|
||||||
if ($mode !== 'default') {
|
if ($mode !== 'default') {
|
||||||
@@ -285,11 +184,9 @@ class Smoke extends BaseController
|
|||||||
|
|
||||||
if ($aiSuggestion) {
|
if ($aiSuggestion) {
|
||||||
$response['source'] = 'ai';
|
$response['source'] = 'ai';
|
||||||
$response['not_before_at'] = (string) ($aiSuggestion['not_before_at'] ?? '');
|
|
||||||
$response['suggested_at'] = (string) ($aiSuggestion['suggested_at'] ?? '');
|
$response['suggested_at'] = (string) ($aiSuggestion['suggested_at'] ?? '');
|
||||||
$response['time_nodes'] = $aiSuggestion['time_nodes'] ?? [];
|
$response['time_nodes'] = $aiSuggestion['time_nodes'] ?? [];
|
||||||
$response['advice'] = (string) ($aiSuggestion['advice'] ?? '');
|
$response['advice'] = (string) ($aiSuggestion['advice'] ?? '');
|
||||||
$response['ai'] = $aiSuggestion;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -299,30 +196,6 @@ class Smoke extends BaseController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function aiNextSmokeTime()
|
|
||||||
{
|
|
||||||
return $this->nextSmokeTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function aiAdvice()
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
$user = $this->getCurrentSmtUser();
|
|
||||||
$date = !empty($this->request->get('date'))
|
|
||||||
? Support::parseDate((string) $this->request->get('date'), 'date')
|
|
||||||
: Support::dateOnly(Support::now()->modify('-1 day'));
|
|
||||||
$record = $this->smokeAiService->getOrGenerateAdvice($user, $date, 'v2');
|
|
||||||
|
|
||||||
return Response::success([
|
|
||||||
'date' => (string) ($record['date'] ?? $date->format(Support::DATE_LAYOUT)),
|
|
||||||
'advice' => (string) ($record['advice'] ?? ''),
|
|
||||||
'model' => (string) ($record['model'] ?? ''),
|
|
||||||
]);
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
return Response::error($e->getMessage(), $e->getCode() ?: 500);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function unlockAiAdvice()
|
public function unlockAiAdvice()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
@@ -372,15 +245,6 @@ class Smoke extends BaseController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function revokeShare(string $token)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
return Response::success($this->smokeService->revokeShare($this->getCurrentSmtUserId(), $token));
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
return Response::error($e->getMessage(), $e->getCode() ?: 500);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function generateQuitPlan()
|
public function generateQuitPlan()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
@@ -458,27 +322,4 @@ class Smoke extends BaseController
|
|||||||
return Support::parseDate($value, 'date');
|
return Support::parseDate($value, 'date');
|
||||||
}
|
}
|
||||||
|
|
||||||
private function buildGreeting(string $nickname, string $avatarUrl, \DateTimeImmutable $now): array
|
|
||||||
{
|
|
||||||
$nickname = trim($nickname) !== '' ? trim($nickname) : '朋友';
|
|
||||||
$hour = (int) $now->format('H');
|
|
||||||
|
|
||||||
if ($hour >= 5 && $hour < 11) {
|
|
||||||
[$timeOfDay, $title, $subtitle] = ['morning', '早安', '今天也是清爽的一天'];
|
|
||||||
} elseif ($hour >= 11 && $hour < 14) {
|
|
||||||
[$timeOfDay, $title, $subtitle] = ['noon', '午安', '补充水分和能量'];
|
|
||||||
} elseif ($hour >= 14 && $hour < 19) {
|
|
||||||
[$timeOfDay, $title, $subtitle] = ['afternoon', '下午好', '把烟瘾留在昨天'];
|
|
||||||
} else {
|
|
||||||
[$timeOfDay, $title, $subtitle] = ['evening', '晚上好', '今晚早点休息'];
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
'title' => $title . ',' . $nickname,
|
|
||||||
'subtitle' => $subtitle,
|
|
||||||
'nickname' => $nickname,
|
|
||||||
'time_of_day' => $timeOfDay,
|
|
||||||
'avatar_url' => $avatarUrl,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,25 +23,17 @@ Route::group('v1', function () {
|
|||||||
Route::get('smoke/home', [Smoke::class, 'home']);
|
Route::get('smoke/home', [Smoke::class, 'home']);
|
||||||
Route::get('smoke/profile', [Smoke::class, 'profile']);
|
Route::get('smoke/profile', [Smoke::class, 'profile']);
|
||||||
Route::post('smoke/profile', [Smoke::class, 'saveProfile']);
|
Route::post('smoke/profile', [Smoke::class, 'saveProfile']);
|
||||||
Route::get('smoke/next_smoke_time', [Smoke::class, 'nextSmokeTime']);
|
|
||||||
Route::get('smoke/dashboard', [Smoke::class, 'dashboard']);
|
|
||||||
Route::get('smoke/stats', [Smoke::class, 'stats']);
|
Route::get('smoke/stats', [Smoke::class, 'stats']);
|
||||||
Route::post('smoke/logs', [Smoke::class, 'createLog']);
|
Route::post('smoke/logs', [Smoke::class, 'createLog']);
|
||||||
Route::post('smoke/logs/resisted', [Smoke::class, 'createResistedLog']);
|
|
||||||
Route::get('smoke/logs', [Smoke::class, 'logs']);
|
Route::get('smoke/logs', [Smoke::class, 'logs']);
|
||||||
Route::get('smoke/logs/latest', [Smoke::class, 'latestLogs']);
|
|
||||||
Route::get('smoke/logs/:id', [Smoke::class, 'readLog']);
|
|
||||||
Route::post('smoke/logs/:id', [Smoke::class, 'updateLog']);
|
Route::post('smoke/logs/:id', [Smoke::class, 'updateLog']);
|
||||||
Route::delete('smoke/logs/:id', [Smoke::class, 'deleteLog']);
|
Route::delete('smoke/logs/:id', [Smoke::class, 'deleteLog']);
|
||||||
Route::get('smoke/motivation', [Smoke::class, 'motivation']);
|
|
||||||
|
|
||||||
Route::get('smoke/ai/advice', [Smoke::class, 'aiAdvice']);
|
|
||||||
Route::post('smoke/ai/advice_unlocks', [Smoke::class, 'unlockAiAdvice']);
|
Route::post('smoke/ai/advice_unlocks', [Smoke::class, 'unlockAiAdvice']);
|
||||||
Route::get('smoke/ai/next_smoke_time', [Smoke::class, 'aiNextSmokeTime']);
|
Route::get('smoke/ai/next_smoke_time', [Smoke::class, 'aiNextSmokeTime']);
|
||||||
Route::get('smoke/ai/daily_summary', [Smoke::class, 'aiDailySummary']);
|
Route::get('smoke/ai/daily_summary', [Smoke::class, 'aiDailySummary']);
|
||||||
|
|
||||||
Route::post('smoke/share', [Smoke::class, 'createShare']);
|
Route::post('smoke/share', [Smoke::class, 'createShare']);
|
||||||
Route::post('smoke/share/:token/revoke', [Smoke::class, 'revokeShare']);
|
|
||||||
|
|
||||||
Route::post('smoke/quit-plan/generate', [Smoke::class, 'generateQuitPlan']);
|
Route::post('smoke/quit-plan/generate', [Smoke::class, 'generateQuitPlan']);
|
||||||
Route::get('smoke/quit-plan', [Smoke::class, 'quitPlan']);
|
Route::get('smoke/quit-plan', [Smoke::class, 'quitPlan']);
|
||||||
|
|||||||
@@ -117,11 +117,8 @@ class SmokeService
|
|||||||
$smokeTime = Support::dateOnly();
|
$smokeTime = Support::dateOnly();
|
||||||
}
|
}
|
||||||
|
|
||||||
$level = $resisted ? 0 : max(0, (int) ($data['level'] ?? 1));
|
|
||||||
$num = $resisted ? 0 : max(0, (int) ($data['num'] ?? 1));
|
$num = $resisted ? 0 : max(0, (int) ($data['num'] ?? 1));
|
||||||
if (!$resisted && $num === 0) {
|
$level = ($resisted || $num === 0) ? 0 : max(0, (int) ($data['level'] ?? 1));
|
||||||
throw new \RuntimeException('num=0 请使用 /smoke/logs/resisted', 400);
|
|
||||||
}
|
|
||||||
|
|
||||||
$insertId = Db::connect('mysql')->name('fa_smoke_log')->insertGetId([
|
$insertId = Db::connect('mysql')->name('fa_smoke_log')->insertGetId([
|
||||||
'uid' => $userId,
|
'uid' => $userId,
|
||||||
@@ -195,23 +192,6 @@ class SmokeService
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function latestLogs(int $userId, int $limit = 20): array
|
|
||||||
{
|
|
||||||
$limit = min(100, max(1, $limit));
|
|
||||||
$rows = Db::connect('mysql')->name('fa_smoke_log')
|
|
||||||
->where('uid', $userId)
|
|
||||||
->whereRaw('(deletetime IS NULL OR deletetime = 0)')
|
|
||||||
->orderRaw('COALESCE(smoke_at, FROM_UNIXTIME(createtime), smoke_time) DESC')
|
|
||||||
->order('id', 'desc')
|
|
||||||
->limit($limit)
|
|
||||||
->select()
|
|
||||||
->toArray();
|
|
||||||
|
|
||||||
return ['items' => array_map(static function ($row) {
|
|
||||||
return Support::formatLog($row);
|
|
||||||
}, $rows)];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function updateLog(int $userId, int $id, array $data): array
|
public function updateLog(int $userId, int $id, array $data): array
|
||||||
{
|
{
|
||||||
$this->getLog($userId, $id);
|
$this->getLog($userId, $id);
|
||||||
@@ -267,61 +247,6 @@ class SmokeService
|
|||||||
return ['deleted' => true];
|
return ['deleted' => true];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function dashboard(int $userId, array $params = []): array
|
|
||||||
{
|
|
||||||
$now = Support::now();
|
|
||||||
[$defaultStart, $defaultEnd] = Support::weekRange($now);
|
|
||||||
$start = !empty($params['start']) ? Support::parseDate((string) $params['start'], 'start') : $defaultStart;
|
|
||||||
$end = !empty($params['end']) ? Support::parseDate((string) $params['end'], 'end') : (!empty($params['start']) ? $start->modify('+6 day') : $defaultEnd);
|
|
||||||
if ($end < $start) {
|
|
||||||
throw new \RuntimeException('end 不能早于 start', 400);
|
|
||||||
}
|
|
||||||
|
|
||||||
$rows = Db::connect('mysql')->name('fa_smoke_log')
|
|
||||||
->field('smoke_time, SUM(num) AS total')
|
|
||||||
->where('uid', $userId)
|
|
||||||
->whereBetween('smoke_time', [$start->format(Support::DATE_LAYOUT), $end->format(Support::DATE_LAYOUT)])
|
|
||||||
->whereRaw('(deletetime IS NULL OR deletetime = 0)')
|
|
||||||
->group('smoke_time')
|
|
||||||
->select()
|
|
||||||
->toArray();
|
|
||||||
|
|
||||||
$counts = [];
|
|
||||||
foreach ($rows as $row) {
|
|
||||||
$counts[(string) $row['smoke_time']] = (int) ($row['total'] ?? 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
$today = Support::dateOnly($now);
|
|
||||||
$todayKey = $today->format(Support::DATE_LAYOUT);
|
|
||||||
$todayCount = (int) Db::connect('mysql')->name('fa_smoke_log')
|
|
||||||
->where('uid', $userId)
|
|
||||||
->where('smoke_time', $todayKey)
|
|
||||||
->whereRaw('(deletetime IS NULL OR deletetime = 0)')
|
|
||||||
->sum('num');
|
|
||||||
|
|
||||||
$lastSmoke = $this->findLastActualSmoke($userId);
|
|
||||||
$minutesSinceLast = null;
|
|
||||||
if ($lastSmoke) {
|
|
||||||
$minutesSinceLast = max(0, (int) floor(($now->getTimestamp() - $lastSmoke->getTimestamp()) / 60));
|
|
||||||
}
|
|
||||||
|
|
||||||
$weekly = [];
|
|
||||||
for ($cursor = $start; $cursor <= $end; $cursor = $cursor->add(new DateInterval('P1D'))) {
|
|
||||||
$key = $cursor->format(Support::DATE_LAYOUT);
|
|
||||||
$weekly[] = [
|
|
||||||
'date' => $key,
|
|
||||||
'count' => (int) ($counts[$key] ?? 0),
|
|
||||||
'is_today' => $key === $todayKey,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
'today_count' => $todayCount,
|
|
||||||
'minutes_since_last' => $minutesSinceLast,
|
|
||||||
'weekly' => $weekly,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getHomeSummary(int $userId, ?DateTimeImmutable $asOf = null): array
|
public function getHomeSummary(int $userId, ?DateTimeImmutable $asOf = null): array
|
||||||
{
|
{
|
||||||
$asOf = $asOf ?: Support::now();
|
$asOf = $asOf ?: Support::now();
|
||||||
@@ -622,23 +547,6 @@ class SmokeService
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function revokeShare(int $userId, string $token): array
|
|
||||||
{
|
|
||||||
$share = SmokeShare::where('share_token', trim($token))->whereNull('deleted_at')->find();
|
|
||||||
if (!$share) {
|
|
||||||
throw new \RuntimeException('分享不存在', 404);
|
|
||||||
}
|
|
||||||
if ((int) $share->uid !== $userId) {
|
|
||||||
throw new \RuntimeException('无权限操作该分享', 403);
|
|
||||||
}
|
|
||||||
|
|
||||||
$share->revoked_at = Support::now()->format(Support::DATETIME_LAYOUT);
|
|
||||||
$share->updated_at = Support::now()->format(Support::DATETIME_LAYOUT);
|
|
||||||
$share->save();
|
|
||||||
|
|
||||||
return ['revoked' => true];
|
|
||||||
}
|
|
||||||
|
|
||||||
private function formatProfileRow(array $row): array
|
private function formatProfileRow(array $row): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
|
|||||||
+2
-7
@@ -10,22 +10,17 @@
|
|||||||
- 更新用户资料:`PUT|POST /smt/v1/auth/profile`
|
- 更新用户资料:`PUT|POST /smt/v1/auth/profile`
|
||||||
- 戒烟资料:`GET|POST /smt/v1/smoke/profile`
|
- 戒烟资料:`GET|POST /smt/v1/smoke/profile`
|
||||||
- 抽烟记录:`/smt/v1/smoke/logs*`
|
- 抽烟记录:`/smt/v1/smoke/logs*`
|
||||||
- 首页、看板、统计、激励语:
|
- 首页与统计:
|
||||||
- `GET /smt/v1/smoke/home`
|
- `GET /smt/v1/smoke/home`
|
||||||
- `GET /smt/v1/smoke/dashboard`
|
|
||||||
- `GET /smt/v1/smoke/stats`
|
- `GET /smt/v1/smoke/stats`
|
||||||
- `GET /smt/v1/smoke/motivation`
|
|
||||||
- 下次抽烟建议:
|
- 下次抽烟建议:
|
||||||
- `GET /smt/v1/smoke/next_smoke_time`
|
|
||||||
- `GET /smt/v1/smoke/ai/next_smoke_time`
|
- `GET /smt/v1/smoke/ai/next_smoke_time`
|
||||||
- AI 建议与每日总结:
|
- AI 解锁与每日总结:
|
||||||
- `GET /smt/v1/smoke/ai/advice`
|
|
||||||
- `POST /smt/v1/smoke/ai/advice_unlocks`
|
- `POST /smt/v1/smoke/ai/advice_unlocks`
|
||||||
- `GET /smt/v1/smoke/ai/daily_summary`
|
- `GET /smt/v1/smoke/ai/daily_summary`
|
||||||
- 分享:
|
- 分享:
|
||||||
- `POST /smt/v1/smoke/share`
|
- `POST /smt/v1/smoke/share`
|
||||||
- `GET /smt/v1/smoke/share/:token`
|
- `GET /smt/v1/smoke/share/:token`
|
||||||
- `POST /smt/v1/smoke/share/:token/revoke`
|
|
||||||
- 戒烟计划:
|
- 戒烟计划:
|
||||||
- `POST /smt/v1/smoke/quit-plan/generate`
|
- `POST /smt/v1/smoke/quit-plan/generate`
|
||||||
- `GET /smt/v1/smoke/quit-plan`
|
- `GET /smt/v1/smoke/quit-plan`
|
||||||
|
|||||||
@@ -33,25 +33,17 @@ Route::group('api/v1', function () {
|
|||||||
Route::get('smoke/home', [Smoke::class, 'home']);
|
Route::get('smoke/home', [Smoke::class, 'home']);
|
||||||
Route::get('smoke/profile', [Smoke::class, 'profile']);
|
Route::get('smoke/profile', [Smoke::class, 'profile']);
|
||||||
Route::post('smoke/profile', [Smoke::class, 'saveProfile']);
|
Route::post('smoke/profile', [Smoke::class, 'saveProfile']);
|
||||||
Route::get('smoke/next_smoke_time', [Smoke::class, 'nextSmokeTime']);
|
|
||||||
Route::get('smoke/dashboard', [Smoke::class, 'dashboard']);
|
|
||||||
Route::get('smoke/stats', [Smoke::class, 'stats']);
|
Route::get('smoke/stats', [Smoke::class, 'stats']);
|
||||||
Route::post('smoke/logs', [Smoke::class, 'createLog']);
|
Route::post('smoke/logs', [Smoke::class, 'createLog']);
|
||||||
Route::post('smoke/logs/resisted', [Smoke::class, 'createResistedLog']);
|
|
||||||
Route::get('smoke/logs', [Smoke::class, 'logs']);
|
Route::get('smoke/logs', [Smoke::class, 'logs']);
|
||||||
Route::get('smoke/logs/latest', [Smoke::class, 'latestLogs']);
|
|
||||||
Route::get('smoke/logs/:id', [Smoke::class, 'readLog']);
|
|
||||||
Route::post('smoke/logs/:id', [Smoke::class, 'updateLog']);
|
Route::post('smoke/logs/:id', [Smoke::class, 'updateLog']);
|
||||||
Route::delete('smoke/logs/:id', [Smoke::class, 'deleteLog']);
|
Route::delete('smoke/logs/:id', [Smoke::class, 'deleteLog']);
|
||||||
Route::get('smoke/motivation', [Smoke::class, 'motivation']);
|
|
||||||
|
|
||||||
Route::get('smoke/ai/advice', [Smoke::class, 'aiAdvice']);
|
|
||||||
Route::post('smoke/ai/advice_unlocks', [Smoke::class, 'unlockAiAdvice']);
|
Route::post('smoke/ai/advice_unlocks', [Smoke::class, 'unlockAiAdvice']);
|
||||||
Route::get('smoke/ai/next_smoke_time', [Smoke::class, 'aiNextSmokeTime']);
|
Route::get('smoke/ai/next_smoke_time', [Smoke::class, 'aiNextSmokeTime']);
|
||||||
Route::get('smoke/ai/daily_summary', [Smoke::class, 'aiDailySummary']);
|
Route::get('smoke/ai/daily_summary', [Smoke::class, 'aiDailySummary']);
|
||||||
|
|
||||||
Route::post('smoke/share', [Smoke::class, 'createShare']);
|
Route::post('smoke/share', [Smoke::class, 'createShare']);
|
||||||
Route::post('smoke/share/:token/revoke', [Smoke::class, 'revokeShare']);
|
|
||||||
|
|
||||||
Route::post('smoke/quit-plan/generate', [Smoke::class, 'generateQuitPlan']);
|
Route::post('smoke/quit-plan/generate', [Smoke::class, 'generateQuitPlan']);
|
||||||
Route::get('smoke/quit-plan', [Smoke::class, 'quitPlan']);
|
Route::get('smoke/quit-plan', [Smoke::class, 'quitPlan']);
|
||||||
|
|||||||
Reference in New Issue
Block a user