update cache, table cache mothly

This commit is contained in:
JOSEPH LE 2024-07-01 10:51:46 +07:00
parent 728e967049
commit b9fa0f8c3e
9 changed files with 280 additions and 61 deletions

View File

@ -2,7 +2,9 @@
namespace Modules\Admin\app\Http\Controllers;
use App\Helper\Cache\CurrentMonthTimekeeping;
use App\Http\Controllers\Controller;
use App\Traits\AnalyzeData;
use App\Traits\HasFilterRequest;
use App\Traits\HasOrderByRequest;
use App\Traits\HasSearchRequest;
@ -10,6 +12,7 @@ use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Modules\Admin\app\Models\Admin;
use Modules\Admin\app\Models\MonthlyTimekeeping;
use Modules\Admin\app\Models\Tracking;
use function PHPSTORM_META\type;
@ -19,59 +22,39 @@ class TimekeepingController extends Controller
use HasOrderByRequest;
use HasFilterRequest;
use HasSearchRequest;
use AnalyzeData;
public function get(Request $request)
{
// dd($request->date);
$now = Carbon::create($request->year, $request->month);
// $now = Carbon::create(2024, 5, 30); // Nếu muốn khởi tạo với một ngày cụ thể
$daysInMonth = $now->daysInMonth;
// return response()->json(['status'=> true, 'data'=>$request->all()]);
// Lấy ngày đầu tháng
$startOfMonth = $now->startOfMonth()->toDateString();
// Lấy ngày cuối tháng
$endOfMonth = $now->endOfMonth()->toDateString();
$admins = Admin::all();
$history = DB::table('tracking')->select('*')
->whereBetween('tracking.created_at', [$startOfMonth, $endOfMonth])->orderBy('tracking.created_at', 'asc')->get();
$history = collect($history);
$result = [];
foreach ($admins as $admin) {
$user_data = [];
for ($i = 1; $i <= $daysInMonth; $i++) {
// Tạo ngày cụ thể trong tháng
$date = Carbon::create($now->year, $now->month, $i)->setTimezone(env('TIME_ZONE'))->format('Y-m-d');
// Kiểm tra xem có mục nào trong $history có created_at trùng với $date
$hasEntry = $history->filter(function ($entry) use ($date, $admin) {
// echo($hasEntry);
return Carbon::parse($entry->created_at)->setTimezone(env('TIME_ZONE'))->format('Y-m-d') === $date && $entry->user_id == $admin->id;
});
// echo($hasEntry);
if (count($hasEntry) > 0) {
$values = array_values($hasEntry->toArray());
$last_checkin = null;
$total = 0;
foreach ($values as $value) {
$createdAt = Carbon::parse($value->created_at)->setTimezone(env('TIME_ZONE'));
if ($value->status == 'check out' && $last_checkin != null) {
$lastCheckInTime = Carbon::parse($last_checkin)->setTimezone(env('TIME_ZONE'));
// Tính thời gian làm việc bằng hiệu của thời gian check out và check in
$workingTime = $createdAt->diffInSeconds($lastCheckInTime);
$total += $workingTime;
}
if ($value->status == 'check in') {
$last_checkin = $createdAt;
}
}
$user_data[] = ['values'=>array_values($hasEntry->toArray()), 'total' => $total, 'day' => $i];
$currentDate = Carbon::now();
$currentMonth = $currentDate->month;
$currentYear = $currentDate->year;
$data = MonthlyTimekeeping::where('month', '=', $request->month)->where('year', '=', $request->year)->first();
if ($currentMonth == (int)$request->month && $currentYear == (int)$request->year) {
$cacheData = CurrentMonthTimekeeping::getCacheCurrentMonthTimekeeping();
if ($cacheData) {
$cacheData->data = json_decode($cacheData->data, true);
return response()->json(['status' => true, 'data' => $cacheData->data, 'working_days'=> $cacheData->working_days, 'message' => 'Get from cache']);
} else {
$result = $this->analyzeCurrentMonthTimeKeepingData($currentMonth, $currentYear);
if ($data) {
$data->update(['data' => json_encode($result)]);
return response()->json(['status' => true, 'data' => $result, 'message' => 'Get from analyzeCurrentMonthTimeKeepingData + update record']);
}
MonthlyTimekeeping::create(['month' => $currentMonth, 'year' => $currentYear, 'working_days' => $currentDate->daysInMonth, 'data' => json_encode($result)]);
return response()->json(['status' => true, 'data' => $result, 'message' => 'Get from analyzeCurrentMonthTimeKeepingData + create record']);
}
} else {
if ($data) {
$data['data'] = json_decode($data['data']);
return response()->json(['status' => true, 'data' => $data['data'], 'working_days'=> $data['working_days'], 'message' => 'Get from DB']);
} else {
$result = $this->analyzeCurrentMonthTimeKeepingData($request->month, $request->year);
MonthlyTimekeeping::create(['month' => $request->month, 'year' => $request->year, 'working_days' => Carbon::create((int)$request->year, (int)$request->month)->daysInMonth, 'data' => json_encode($result)]);
return response()->json(['status' => true, 'data' => $result, 'message' => 'Get from analyzeCurrentMonthTimeKeepingData']);
}
$result[] = ['user' => $admin, 'history' => $user_data];
}
return response()->json(['status'=> true, 'data'=>$result]);
return response()->json(['status' => false, 'mewssage' => 'Get data failed!']);
}
public function addWorkingTimeForMultipleUser(Request $request)
@ -81,7 +64,7 @@ class TimekeepingController extends Controller
$month = $request->month;
$day = $request->day;
$type = $request->type;
foreach($user_ids as $id){
foreach ($user_ids as $id) {
$user = Admin::find($id);
$date = Carbon::create($year, $month, $day)->setTimezone(env('TIME_ZONE'));
$start = $date->copy()->setTime(7, 31, 11);
@ -91,7 +74,7 @@ class TimekeepingController extends Controller
'name' => $user->name,
'user_id' => $user->id,
'status' => 'check in',
'time_string' => $start ->format('Y-m-d H:i:s'),
'time_string' => $start->format('Y-m-d H:i:s'),
'created_at' => $start->setTimezone('UTC')
],
[
@ -104,6 +87,18 @@ class TimekeepingController extends Controller
]);
}
return response()->json(['status'=> true, 'message'=> 'Add successfully']);
return response()->json(['status' => true, 'message' => 'Add successfully']);
}
public function saveWorkingDays(Request $request)
{
$data = MonthlyTimekeeping::where('month', '=', $request->month)->where('year', '=', $request->year)->first();
if($data){
$data->update(['working_days'=>$request->working_days]);
$this->createOrUpdateRecordForCurrentMonth($request->month, $request->year);
return response()->json(['status' => true, 'message' => 'Update successful']);
}
return response()->json(['status' => false, 'message' => 'Update failed']);
}
}

View File

@ -2,7 +2,9 @@
namespace Modules\Admin\app\Http\Controllers;
use App\Helper\Cache\CurrentMonthTimekeeping;
use App\Http\Controllers\Controller;
use App\Traits\AnalyzeData;
use App\Traits\HasFilterRequest;
use App\Traits\HasOrderByRequest;
use App\Traits\HasSearchRequest;
@ -11,6 +13,7 @@ use DateTime;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Modules\Admin\app\Models\Admin;
use Modules\Admin\app\Models\MonthlyTimekeeping;
use Modules\Admin\app\Models\Tracking;
class TrackingController extends Controller
@ -18,7 +21,8 @@ class TrackingController extends Controller
use HasOrderByRequest;
use HasFilterRequest;
use HasSearchRequest;
use AnalyzeData;
private $CHECK_IN = 'check in';
private $CHECK_OUT = 'check out';
@ -96,6 +100,7 @@ class TrackingController extends Controller
$payload['status'] = $this->CHECK_IN;
}
$tracking = Tracking::create($payload);
$this->createOrUpdateRecordForCurrentMonth($payload['created_at']->month, $payload['created_at']->year);
return response()->json([
'data' => $tracking,
'check_status' => $payload['status'],
@ -109,6 +114,7 @@ class TrackingController extends Controller
}
} else {
$tracking = Tracking::create($payload);
$this->createOrUpdateRecordForCurrentMonth($payload['created_at']->month, $payload['created_at']->year);
return response()->json([
'data' => $tracking,
'check_status' => $this->CHECK_IN,
@ -142,6 +148,7 @@ class TrackingController extends Controller
}
if ($tracking) {
$tracking->update($payload);
$this->createOrUpdateRecordForCurrentMonth($payload['created_at']->month, $payload['created_at']->year);
}
return response()->json([
'data' => $tracking,
@ -174,6 +181,8 @@ class TrackingController extends Controller
$tracking->image = $path;
$tracking->save();
$date = Carbon::create(new DateTime())->setTimezone(env('TIME_ZONE'));
$this->createOrUpdateRecordForCurrentMonth($date->month, $date->year);
}
return response()->json([
'data' => $tracking,
@ -186,6 +195,8 @@ class TrackingController extends Controller
$id = $request->get('id');
Tracking::destroy($id);
$date = Carbon::create(new DateTime())->setTimezone(env('TIME_ZONE'));
$this->createOrUpdateRecordForCurrentMonth($date->month, $date->year);
return response()->json([
'status' => true
]);

View File

@ -0,0 +1,20 @@
<?php
namespace Modules\Admin\app\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Event;
use App\Traits\HasCacheModel;
class MonthlyTimekeeping extends Model
{
use HasFactory;
use HasCacheModel;
public function __construct()
{
$this->table = 'monthly_timekeeping';
$this->guarded = [];
}
}

View File

@ -107,6 +107,7 @@ Route::middleware('api')
], function () {
Route::get('/', [TimekeepingController::class, 'get'])->middleware('check.permission:admin.hr.staff');
Route::post('/addMutilple', [TimekeepingController::class, 'addWorkingTimeForMultipleUser'])->middleware('check.permission:admin.hr');
Route::post('/update-working-days', [TimekeepingController::class, 'saveWorkingDays'])->middleware('check.permission:admin.hr');
});
Route::group([

View File

@ -0,0 +1,44 @@
<?php
namespace App\Helper\Cache;
use Modules\Admin\app\Models\MonthlyTimekeeping;
use Illuminate\Support\Facades\Cache;
use App\Helper\Constant\CacheConstant;
use Carbon\Carbon;
class CurrentMonthTimekeeping
{
public static $globals = 'current-month-timekeeping-data';
public static $key = 'current-month-timekeeping-data';
public static function getCacheCurrentMonthTimekeeping()
{
$currentDate = Carbon::now();
$currentMonth = $currentDate->month;
$currentYear = $currentDate->year;
$monthData = Cache::get(self::$key, null);
if (isset($GLOBALS[self::$globals]) && $monthData != null)
return $GLOBALS[self::$globals];
if ($monthData == null) {
$monthData = MonthlyTimekeeping::where('month', '=', $currentMonth)->where('year', '=', $currentYear)->first();
if ($monthData) {
Cache::put(self::$key, json_encode($monthData), (60 * CacheConstant::$expired));
$monthData = json_encode($monthData);
} else {
$monthData = null;
}
}
if ($monthData == null) {
$GLOBALS[self::$globals] = null;
} else {
$GLOBALS[self::$globals] = json_decode($monthData);
}
return $GLOBALS[self::$globals];
}
public static function cleanCacheCurrentMonthTimekeeping()
{
Cache::forget(self::$key);
}
}

View File

@ -0,0 +1,90 @@
<?php
namespace App\Traits;
use App\Helper\Cache\CurrentMonthTimekeeping;
use Carbon\Carbon;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\DB;
use Modules\Admin\app\Models\Admin;
use Modules\Admin\app\Models\MonthlyTimekeeping;
trait AnalyzeData
{
/**
* Return a JSON response.
*
* @param mixed $data
* @param int $status
* @param array $headers
* @param int $options
* @return array $result
*/
public function analyzeCurrentMonthTimeKeepingData($month, $year)
{
// dd((int)$month, (int)$year);
$now = Carbon::create((int)$year, (int)$month);
// $now = Carbon::create(2024, 5, 30); // Nếu muốn khởi tạo với một ngày cụ thể
$daysInMonth = $now->daysInMonth;
// Lấy ngày đầu tháng
$startOfMonth = $now->startOfMonth()->toDateString();
// Lấy ngày cuối tháng
$endOfMonth = $now->endOfMonth()->toDateString();
$admins = Admin::all();
$history = DB::table('tracking')->select('*')
->whereBetween('tracking.created_at', [$startOfMonth, $endOfMonth])->orderBy('tracking.created_at', 'asc')->get();
$history = collect($history);
$result = [];
foreach ($admins as $admin) {
$user_data = [];
for ($i = 1; $i <= $daysInMonth; $i++) {
// Tạo ngày cụ thể trong tháng
$date = Carbon::create($now->year, $now->month, $i)->setTimezone(env('TIME_ZONE'))->format('Y-m-d');
// Kiểm tra xem có mục nào trong $history có created_at trùng với $date
$hasEntry = $history->filter(function ($entry) use ($date, $admin) {
// echo($hasEntry);
return Carbon::parse($entry->created_at)->setTimezone(env('TIME_ZONE'))->format('Y-m-d') === $date && $entry->user_id == $admin->id;
});
// echo($hasEntry);
if (count($hasEntry) > 0) {
$values = array_values($hasEntry->toArray());
$last_checkin = null;
$total = 0;
foreach ($values as $value) {
$createdAt = Carbon::parse($value->created_at)->setTimezone(env('TIME_ZONE'));
if ($value->status == 'check out' && $last_checkin != null) {
$lastCheckInTime = Carbon::parse($last_checkin)->setTimezone(env('TIME_ZONE'));
// Tính thời gian làm việc bằng hiệu của thời gian check out và check in
$workingTime = $createdAt->diffInSeconds($lastCheckInTime);
$total += $workingTime;
}
if ($value->status == 'check in') {
$last_checkin = $createdAt;
}
}
$user_data[] = ['values' => array_values($hasEntry->toArray()), 'total' => $total, 'day' => $i];
}
}
$result[] = ['user' => $admin, 'history' => $user_data];
}
return $result;
}
public function createOrUpdateRecordForCurrentMonth($month, $year)
{
$data = MonthlyTimekeeping::where('month', '=', $month)->where('year', '=', $year)->first();
if ($data) {
$result = $this->analyzeCurrentMonthTimeKeepingData($month, $year);
$data->update(['data' => json_encode($result)]);
} else {
$result = $this->analyzeCurrentMonthTimeKeepingData($month, $year);
MonthlyTimekeeping::create(['month' => $month, 'year' => $year, 'working_days' => Carbon::create((int)$year, (int)$month)->daysInMonth, 'data' => json_encode($result)]);
}
CurrentMonthTimekeeping::cleanCacheCurrentMonthTimekeeping();
return;
}
}

View File

@ -0,0 +1,31 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('monthly_timekeeping', function (Blueprint $table) {
$table->id();
$table->integer('month');
$table->integer('year');
$table->integer('working_days');
$table->json('data');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('monthly_timekeeping');
}
};

View File

@ -66,4 +66,5 @@ export const getAllUserWorklogs = API_URL + 'v1/admin/jira/worklogs'
//Timekeeping
export const getTheTimesheet = API_URL + 'v1/admin/timekeeping'
export const updateMultipleUserWorkingTime = API_URL + 'v1/admin/timekeeping/addMutilple'
export const updateMultipleUserWorkingTime = API_URL + 'v1/admin/timekeeping/addMutilple'
export const updateWorkingDays = API_URL + 'v1/admin/timekeeping/update-working-days'

View File

@ -1,11 +1,12 @@
import { getTheTimesheet, updateMultipleUserWorkingTime } from '@/api/Admin'
import { getTheTimesheet, updateMultipleUserWorkingTime, updateWorkingDays } from '@/api/Admin'
import { update } from '@/rtk/helpers/CRUD'
import { get } from '@/rtk/helpers/apiService'
import { Box, Image, Menu, Select, Table, Text, TextInput, Tooltip } from '@mantine/core'
import { Box, Button, Image, Menu, Select, Table, Text, TextInput, Tooltip } from '@mantine/core'
import { notifications } from '@mantine/notifications'
import { IconCheck, IconExclamationMark, IconX } from '@tabler/icons-react'
import { useEffect, useState } from 'react'
import classes from './Timekeeping.module.css'
import moment from 'moment'
interface User {
id: number
@ -67,7 +68,7 @@ const Timekeeping = () => {
Array.from({ length: getDaysInMonth() }, (_, index) => index + 1),
)
localStorage.getItem('workingdays') ? setWorkingDays(parseFloat(localStorage.getItem('workingdays')!)) : setWorkingDays(getDaysInMonth())
setWorkingDays(res.working_days ?? 30)
}
} catch (error: any) {
console.log(error)
@ -115,6 +116,18 @@ const Timekeeping = () => {
}
}
const handleUpdateWorkingDays = async()=>{
try {
await update(updateWorkingDays, {
working_days: workingDays,
year: date.year,
month: date.month
}, getTimeSheet)
} catch (error) {
console.log(error)
}
}
useEffect(() => {
getTimeSheet()
}, [date])
@ -136,7 +149,14 @@ const Timekeeping = () => {
size="xs"
label="Month"
data={Array.from({ length: 12 }, (_, index) =>
(1 + index).toString(),
{
return {
value: (1 + index).toString(),
label:(1 + index).toString(),
disabled: (1 + index) > parseInt(date.month)
}
}
)}
onChange={(e) => {
setDate({ ...date, month: e! })
@ -148,19 +168,25 @@ const Timekeeping = () => {
size="xs"
ml={'sm'}
label="Year"
data={Array.from({ length: 20 }, (_, index) =>
(2023 + 1 + index).toString(),
data={Array.from({ length: 10 }, (_, index) =>
{
return {
value: (parseInt(moment(Date.now()).format('YYYY')) - 3 + index).toString(),
label:(parseInt(moment(Date.now()).format('YYYY')) - 3 + index).toString(),
disabled: (parseInt(moment(Date.now()).format('YYYY')) - 3 + index) > parseInt(date.year)
}
}
)}
onChange={(e) => {
setDate({ ...date, year: e! })
}}
></Select>
</Box>
<Box>
<Box display={'flex'} style={{alignItems:'end'}}>
<TextInput type='number' size='xs' label="Working days" w={"20%"} value={workingDays} onChange={(e)=>{
setWorkingDays(parseFloat(e.target.value))
localStorage.setItem('workingdays', e.target.value)
}}/>
<Tooltip label="Save working days"><Button size='xs' ml={'sm'} onClick={()=>handleUpdateWorkingDays()}>Save</Button></Tooltip>
</Box>
</Box>
<Box w="70%" pl={200} style={{display:'flex', alignItems:'end', justifyContent:"space-evenly"}}>