Files
thankcard-system/app/Services/User/UserService.php
T
antv 781a0f8aa7 m
2026-07-21 10:16:06 +07:00

351 lines
12 KiB
PHP

<?php
namespace App\Services\User;
use App\Models\Administration;
use App\Models\User;
use App\Services\User\Contracts\UserServiceInterface;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
class UserService implements UserServiceInterface
{
public function getDashboard(User $user, string $selectedMonth): LengthAwarePaginator
{
$startOfMonth = Carbon::parse($selectedMonth)->startOfMonth();
$endOfMonth = Carbon::parse($selectedMonth)->endOfMonth();
return Administration::with(['senderUser', 'receiverUser'])
->where('msnv', $user->msnv)
->whereBetween('date', [$startOfMonth, $endOfMonth])
->orderBy('date', 'desc')
->paginate(10)
->withQueryString();
}
public function getOtherActiveUsers(User $currentUser): Collection
{
return User::where('status', config('constants.STATUS_ACTIVE'))
->where('msnv', '!=', $currentUser->msnv)
->get();
}
public function sendThankcards(User $sender, string $receiverMsnv, int $amount, ?int $templateId = 1): void
{
if ($sender->flag_send == config('constants.FLAG_SEND_DISABLED')) {
throw new \App\Exceptions\ThankCardException(__('messages.error.no_send_permission'));
}
$receiver = User::where('msnv', $receiverMsnv)->first();
if (!$receiver || $receiver->status != config('constants.STATUS_ACTIVE')) {
throw new \App\Exceptions\ThankCardException(__('messages.error.receiver_invalid'));
}
$startOfMonth = Carbon::now()->startOfMonth();
$endOfMonth = Carbon::now()->endOfMonth();
DB::transaction(function () use ($sender, $receiverMsnv, $amount, $startOfMonth, $endOfMonth, $templateId) {
// Lock sender record to prevent concurrent transaction modifications
$lockedSender = User::where('id', $sender->id)->lockForUpdate()->first();
if ($lockedSender->flag_send == config('constants.FLAG_SEND_DISABLED')) {
throw new \App\Exceptions\ThankCardException(__('messages.error.cannot_send_card'));
}
if ($lockedSender->card < $amount) {
throw new \App\Exceptions\ThankCardException(__('messages.error.cannot_send_card'));
}
// Lock & check total sent cards to the receiver this month
$cardsSentToThisUserThisMonth = Administration::where('msnv', $lockedSender->msnv)
->where('receiver', $receiverMsnv)
->whereBetween('date', [$startOfMonth, $endOfMonth])
->lockForUpdate()
->sum('sent');
if ($cardsSentToThisUserThisMonth + $amount > Administration::MAX_SEND_CARD_PER_MONTH) {
throw new \App\Exceptions\ThankCardException(
__('messages.error.max_send_limit_template', [
'max' => Administration::MAX_SEND_CARD_PER_MONTH,
'sent' => $cardsSentToThisUserThisMonth
])
);
}
Administration::create([
'msnv' => $receiverMsnv,
'received' => $amount,
'sender' => $lockedSender->msnv,
'sent' => 0,
'receiver' => null,
'date' => Carbon::today(),
'template_id' => $templateId,
]);
Administration::create([
'msnv' => $lockedSender->msnv,
'received' => 0,
'sender' => null,
'sent' => $amount,
'receiver' => $receiverMsnv,
'date' => Carbon::today(),
'template_id' => $templateId,
]);
$lockedSender->card -= $amount;
if ($lockedSender->card <= 0) {
$lockedSender->card = 0;
$lockedSender->flag_send = config('constants.FLAG_SEND_DISABLED');
}
$lockedSender->save();
// Synchronize variables back to the original model instance
$sender->card = $lockedSender->card;
$sender->flag_send = $lockedSender->flag_send;
});
}
public function updatePassword(User $user, string $newPassword): void
{
$user->pass = md5($newPassword);
$user->first_login = config('constants.FIRST_LOGIN_FALSE');
$user->save();
}
public function getPersonalStats(User $user, string $selectedMonth): array
{
$startOfMonth = Carbon::parse($selectedMonth)->startOfMonth();
$endOfMonth = Carbon::parse($selectedMonth)->endOfMonth();
$receivedCount = (int) Administration::where('msnv', $user->msnv)
->whereBetween('date', [$startOfMonth, $endOfMonth])
->sum('received');
$sentCount = (int) Administration::where('msnv', $user->msnv)
->whereBetween('date', [$startOfMonth, $endOfMonth])
->sum('sent');
// Calculate current rank (based on received)
$rankings = User::where('status', config('constants.STATUS_ACTIVE'))
->addSelect([
'score' => Administration::selectRaw('COALESCE(SUM(received), 0)')
->whereColumn('msnv', 'user.msnv')
->whereBetween('date', [$startOfMonth, $endOfMonth])
])
->get()
->filter(fn($u) => $u->score > 0)
->sortBy([
['score', 'desc'],
['msnv', 'asc']
])
->values();
$currentRank = 0;
$prevScore = null;
$rank = 1;
$denseRank = 0;
foreach ($rankings as $item) {
$score = (int) $item->score;
if ($score !== $prevScore) {
$denseRank = $rank;
$prevScore = $score;
}
if ($item->msnv == $user->msnv) {
$currentRank = $denseRank;
break;
}
$rank++;
}
return [
'receivedCount' => $receivedCount,
'sentCount' => $sentCount,
'currentRank' => $currentRank
];
}
public function getRankingList(string $type, string $selectedMonth, User $currentUser): array
{
$startOfMonth = Carbon::parse($selectedMonth)->startOfMonth();
$endOfMonth = Carbon::parse($selectedMonth)->endOfMonth();
$column = $type === 'sent' ? 'sent' : 'received';
$allActiveUsers = User::where('status', config('constants.STATUS_ACTIVE'))
->addSelect([
'score' => Administration::selectRaw('COALESCE(SUM(' . $column . '), 0)')
->whereColumn('msnv', 'user.msnv')
->whereBetween('date', [$startOfMonth, $endOfMonth])
])
->get();
$rankedUsers = $allActiveUsers->filter(fn($u) => $u->score > 0)
->sortBy([
['score', 'desc'],
['msnv', 'asc']
])
->values();
$prevScore = null;
$rank = 1;
$denseRank = 0;
$rankers = [];
$currentUserRankItem = null;
foreach ($rankedUsers as $index => $userItem) {
$score = (int) $userItem->score;
if ($score !== $prevScore) {
$denseRank = $rank;
$prevScore = $score;
}
$userItem->rank = $denseRank;
$userItem->score = $score;
if ($userItem->msnv == $currentUser->msnv) {
$currentUserRankItem = $userItem;
}
// Include every ranked user (no hard limit)
$rankers[] = $userItem;
$rank++;
}
if (!$currentUserRankItem) {
$currentUserModel = $allActiveUsers->first(fn($u) => $u->msnv == $currentUser->msnv);
if ($currentUserModel) {
$currentUserRankItem = clone $currentUserModel;
$currentUserRankItem->score = 0;
$currentUserRankItem->rank = 0;
}
}
return [
'rankers' => $rankers,
'currentUserRankItem' => $currentUserRankItem
];
}
// Retrieve paginated list of received ThankCards for a user
public function getReceivedPageData(User $user, string $selectedMonth, int $limit = 12, int $offset = 0): array
{
$start = Carbon::parse($selectedMonth)->startOfMonth();
$end = Carbon::parse($selectedMonth)->endOfMonth();
$statsQuery = Administration::where('msnv', $user->msnv)
->whereBetween('date', [$start, $end])
->where('received', '>', 0);
$totalCards = (int) $statsQuery->sum('received');
$totalSenders = (int) $statsQuery->whereNotNull('sender')->distinct('sender')->count('sender');
$cards = Administration::with('senderUser')
->where('msnv', $user->msnv)
->whereBetween('date', [$start, $end])
->where('received', '>', 0)
->orderBy('date', 'desc')
->skip($offset)
->take($limit)
->get();
$hasMore = Administration::where('msnv', $user->msnv)
->whereBetween('date', [$start, $end])
->where('received', '>', 0)
->skip($offset + $limit)
->take(1)
->exists();
return [
'cards' => $cards,
'hasMore' => $hasMore,
'totalCards' => $totalCards,
'totalSenders' => $totalSenders
];
}
// Retrieve paginated list of sent ThankCards for a user
public function getSentPageData(User $user, string $selectedMonth, int $limit = 12, int $offset = 0): array
{
$start = Carbon::parse($selectedMonth)->startOfMonth();
$end = Carbon::parse($selectedMonth)->endOfMonth();
$statsQuery = Administration::where('msnv', $user->msnv)
->whereBetween('date', [$start, $end])
->where('sent', '>', 0);
$totalCards = (int) $statsQuery->sum('sent');
$totalReceivers = (int) $statsQuery->whereNotNull('receiver')->distinct('receiver')->count('receiver');
$cards = Administration::with('receiverUser')
->where('msnv', $user->msnv)
->whereBetween('date', [$start, $end])
->where('sent', '>', 0)
->orderBy('date', 'desc')
->skip($offset)
->take($limit)
->get();
$hasMore = Administration::where('msnv', $user->msnv)
->whereBetween('date', [$start, $end])
->where('sent', '>', 0)
->skip($offset + $limit)
->take(1)
->exists();
return [
'cards' => $cards,
'hasMore' => $hasMore,
'totalCards' => $totalCards,
'totalReceivers' => $totalReceivers
];
}
// Retrieve limited feed of received ThankCards for activity feed
public function getReceivedFeed(User $user, string $selectedMonth, int $limit = 12, int $offset = 0): array
{
return $this->getReceivedPageData($user, $selectedMonth, $limit, $offset);
}
// Retrieve limited feed of sent ThankCards for activity feed
public function getSentFeed(User $user, string $selectedMonth, int $limit = 12, int $offset = 0): array
{
return $this->getSentPageData($user, $selectedMonth, $limit, $offset);
}
public function updateProfile(User $user, array $data, ?\Illuminate\Http\UploadedFile $avatarFile = null): void
{
if (isset($data['name'])) {
$user->name = $data['name'];
}
if ($avatarFile) {
$path = $avatarFile->store('avatars', 'public');
$user->avatar = 'storage/' . $path;
}
if (!empty($data['new_password'])) {
$user->pass = md5($data['new_password']);
$user->first_login = config('constants.FIRST_LOGIN_FALSE');
}
$user->save();
}
public function getSentCardsToUserThisMonth(User $sender, string $receiverMsnv): int
{
$startOfMonth = Carbon::now()->startOfMonth();
$endOfMonth = Carbon::now()->endOfMonth();
return (int) Administration::where('msnv', $sender->msnv)
->where('receiver', $receiverMsnv)
->whereBetween('date', [$startOfMonth, $endOfMonth])
->sum('sent');
}
}