door manager

This commit is contained in:
Schneider Roland 2022-05-04 19:41:18 +02:00
parent fb39d6599e
commit 946799a598
20 changed files with 8872 additions and 220 deletions

View File

@ -8,7 +8,6 @@ use backend\models\CitySearch;
use yii\web\Controller; use yii\web\Controller;
use yii\web\NotFoundHttpException; use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter; use yii\filters\VerbFilter;
use yii\base\Object;
use yii\db\Query; use yii\db\Query;
use yii\helpers\Json; use yii\helpers\Json;

View File

@ -8,7 +8,6 @@ use backend\models\CitySearch;
use yii\web\Controller; use yii\web\Controller;
use yii\web\NotFoundHttpException; use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter; use yii\filters\VerbFilter;
use yii\base\Object;
use yii\db\Query; use yii\db\Query;
use yii\helpers\Json; use yii\helpers\Json;

View File

@ -12,7 +12,6 @@ use yii\filters\AccessControl;
use yii\web\Controller; use yii\web\Controller;
use yii\web\NotFoundHttpException; use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter; use yii\filters\VerbFilter;
use yii\base\Object;
use backend\models\CustomerUpdate; use backend\models\CustomerUpdate;
use backend\models\CustomerNewsLetterModel; use backend\models\CustomerNewsLetterModel;
use yii\db\Query; use yii\db\Query;

View File

@ -9,7 +9,6 @@ use backend\models\UserCreate;
use yii\web\Controller; use yii\web\Controller;
use yii\web\NotFoundHttpException; use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter; use yii\filters\VerbFilter;
use yii\base\Object;
use backend\models\UserUpdate; use backend\models\UserUpdate;
use common\models\Account; use common\models\Account;
use common\models\UserAccountAssignment; use common\models\UserAccountAssignment;

View File

@ -18,6 +18,12 @@ use yii\i18n\Formatter;
class DateUtil class DateUtil
{ {
public static function fromUnixTimeStamp($timestamp){
$dt = DateUtil::utcDate();
$dt->setTimestamp($timestamp);
return $dt;
}
/** /**
* Get UTC today @00:00:00 . * Get UTC today @00:00:00 .
* Helper method to generate date for mysql * Helper method to generate date for mysql
@ -27,11 +33,47 @@ class DateUtil
*/ */
public static function todayStart(){ public static function todayStart(){
$d2 = new DateTime(); $d2 = new DateTime();
return DateUtil::utcDate($d2);
}
/**
* @param $date \DateTime optional. The date to set as utc date. If not set, new DateTime() will be used
* @return DateTime the datetime object with time reset and utc timezone
*/
public static function utcDate($date = null){
$d2 = isset($date ) ? $date : new DateTime();
$d2 = DateUtil::withTimeZoneUTC($d2);
return DateUtil::resetTime($d2);
}
/**
* @param $date \DateTime optional. If not set,defaults to : new DateTime() .
* @return DateTime
*/
public static function utcDateTime($date = null){
$d2 = isset($date ) ? $date : new DateTime();
return DateUtil::withTimeZoneUTC($d2);
}
/**
* @param $date \DateTime
* @return DateTime
*/
public static function withTimeZoneUTC( $date = null){
$d2 = isset($date ) ? $date : new DateTime();
$d2->setTimezone( new DateTimeZone('UTC') ); $d2->setTimezone( new DateTimeZone('UTC') );
$d2->setTime(0, 0);
return $d2; return $d2;
} }
/**
* @param $dateTime \DateTime
* @return \DateTime
*/
public static function resetTime($dateTime){
$dateTime->setTime(0, 0);
return $dateTime;
}
/** /**
* Get UTC t @00:00:00 . * Get UTC t @00:00:00 .
* Helper method to generate date for mysql * Helper method to generate date for mysql
@ -39,16 +81,12 @@ class DateUtil
* @return DateTime * @return DateTime
* @throws Exception * @throws Exception
*/ */
public static function tomorrowStart( ){ public static function tomorrowStart( $date = null){
$d2 = new DateTime(); $d2 = isset($date) ? $date : new DateTime();
$d2->add(new DateInterval('P1D')); $d2->add(new DateInterval('P1D'));
$d2->setTimezone( new DateTimeZone('UTC') ); return DateUtil::utcDate($d2);
$d2->setTime(0, 0);
return $d2;
} }
public static function addMonth($timestamp, $monthCount = 1) public static function addMonth($timestamp, $monthCount = 1)
{ {
@ -99,7 +137,7 @@ class DateUtil
* @return string * @return string
* @throws InvalidConfigException * @throws InvalidConfigException
*/ */
public static function formatUtc($dateTimeObject) public static function formatDateTimeUtc($dateTimeObject)
{ {
$formatter = new Formatter; $formatter = new Formatter;
$formatter->datetimeFormat = 'php:Y-m-d H:i:s'; $formatter->datetimeFormat = 'php:Y-m-d H:i:s';
@ -127,6 +165,10 @@ class DateUtil
return $date; return $date;
} }
public static function parseDateTime($dateTimeString){
return DateTime::createFromFormat('Y-m-d H:i:s', $dateTimeString, new DateTimeZone( 'UTC'));
}
/** /**
* @param integer $weekDay Numeric representation of the day of the week. @See https://www.php.net/manual/en/function.date.php * @param integer $weekDay Numeric representation of the day of the week. @See https://www.php.net/manual/en/function.date.php
* @return string * @return string

View File

@ -0,0 +1,352 @@
<?php
namespace common\manager;
use common\components\DateUtil;
use common\components\Helper;
use common\models\Card;
use common\models\CardEventRegistrationForm;
use common\models\Customer;
use common\models\DoorLog;
use common\models\DoorLogForTest;
use common\models\Event;
use common\models\EventRegistration;
use common\models\Log;
use common\models\MobileDevice;
use common\models\Ticket;
use customerapi\models\available\EventInterval;
use customerapi\models\registrations\EventRegistrationAvailable;
use customerapi\models\details\EventRegistrationView;
use Exception;
use Yii;
use yii\base\BaseObject;
use yii\db\ActiveRecord;
use yii\db\Query;
use yii\web\BadRequestHttpException;
use yii\web\NotFoundHttpException;
use yii\web\ServerErrorHttpException;
/**
* Created by IntelliJ IDEA.
* User: rocho
* Date: 2018.12.17.
* Time: 6:12
*/
class DoorManager extends BaseObject
{
/**
* @param $cardNumber
* @param $device
* @param $direction
* @param $createdAt number unix timestamp , override createdAt for doorLogs
* @param $date number unix timestamp, override date for validation check 'now'
* @return void
* @throws BadRequestHttpException
* @throws \yii\base\InvalidConfigException
*/
public function move($cardNumber, $device, $direction, $createdAt = null, $date = null )
{
if ( isset($createdAt)){
$createdAt = DateUtil::parseDateTime($createdAt);
}else{
$createdAt = DateUtil::utcDateTime();
}
$createdAtStr = DateUtil::formatDateTimeUtc($createdAt);
if ( isset($date)){
$date = DateUtil::parseDateTime($date);
}else{
$date = DateUtil::utcDate();
}
$dateStr = DateUtil::formatDateUtc($date);
$doorLog = new DoorLog();
$doorLog->direction = $direction;
$doorLog->source_app = $device;
$doorLog->created_at = $createdAtStr;
/**
* emergency- no card needed
*/
if ($direction == DoorLog::$DIRECTION_ALL_EMERGENCY) {
$doorLog->save(false);
Log::log(
[
'type' => Log::$TYPE_INFO,
'message' => 'Ajtó nyitás: vészhelyzet',
'id_door_log' => $doorLog->id_door_log
]
);
\Yii::$app->response->statusCode = 204;
return;
}
$card = Card::readCard(Helper::fixAsciiChars($cardNumber));
/*
* in any other cases, the door needs a card
*/
if (!isset($card)) {
throw new BadRequestHttpException('Card not found with number: ' . $cardNumber);
}
$doorLog->id_card = $card->id_card;
$doorLog->card_flag = $card->flag;
/**
* if the card type is employee, neither customer nor ticket is needed.
* Free to enter/leave
*/
if ($card->type == Card::TYPE_EMPLOYEE) {
$doorLog->save(false);
\Yii::$app->response->statusCode = 204;
return;
}
//otherwise ticket is required
$activeTickets = Ticket::readActive($card, clone $date);
\Yii::error('active ticket count:' . count($activeTickets));
$ticket = null;
if (isset($activeTickets) && count($activeTickets) > 0) {
$ticket = $activeTickets[0];
}
$doorLog->id_ticket_current = $ticket->id_ticket;
if (!isset($ticket)) {
throw new BadRequestHttpException("No active ticket found for:" . $card->number);
}
\Yii::error("active ticket: " . $ticket->id_ticket);
// customer is also required
$customer = $card->customer;
if (!isset($customer)) {
throw new BadRequestHttpException("Customer not found for:" . $card->number);
}
$doorLog->id_customer = $customer->id_customer;
// save the door log
$doorLog->save(false);
// if direction is in
if ($direction == DoorLog::$DIRECTION_IN || $direction == DoorLog::$DIRECTION_IN_WITHOUT_MOVE) {
$countDoorLogsForTicketSince = $this->getCountDoorLogsForTicketSince($ticket->id_ticket, DateUtil::utcDate( clone $date));
// if the current event is the first door log today
if ($countDoorLogsForTicketSince == 1) {
// increase the ticket usage count with 1
$usageCount = $ticket->usage_count;
$ticket->usage_count += 1;
$ticket->save(false);
Log::log(
[
'type' => Log::$TYPE_TICKET_USAGE_FIRST,
'message' => 'Bérlet használat(előtte: ' . $usageCount . ' -> utána: ' . $ticket->usage_count,
'id_ticket' => $ticket->id_ticket,
'id_door_log' => $doorLog->id_door_log
]
);
} else {
// we have already a door log for today, other than this
// Now we split the day into 3hour intervalls, starting with the createdAt value of the first event.
// If the actual event happens in an interval, in which still now doorlog event happend, we increase
// the usage count with 1
// 3 óránként 1-et levonunk
$startOfDay = DateUtil::utcDate(clone $date);
$startOfTomorrow = DateUtil::tomorrowStart(clone $date);
$allDoorLogToday = DoorLog::find()
->andWhere(['>=', 'door_log.created_at', DateUtil::formatDateUtc($startOfDay)])
->andWhere(['<', 'door_log.created_at', DateUtil::formatDateUtc($startOfTomorrow)])
->andWhere(['id_ticket_current' => $ticket->id_ticket])
->andWhere(['in', 'direction', [DoorLog::$DIRECTION_IN_WITHOUT_MOVE, DoorLog::$DIRECTION_IN]])
->orderBy(['door_log.created_at' => SORT_ASC])
->all();
\Yii::error("new door log: ".$doorLog->id_door_log.";".$doorLog->created_at.";".$doorLog->type);
foreach ($allDoorLogToday as $log){
\Yii::error("all log: ".$log->id_door_log.";".$log->created_at.";".$log->type);
}
$firstInToday = $allDoorLogToday[0];
if (isset($firstInToday)) {
$firstEntryDateTimeToday = DateUtil::parseDateTime($firstInToday->created_at);
$interval = \DateInterval::createFromDateString('3 hours');
$daterange = new \DatePeriod($firstEntryDateTimeToday, $interval ,$startOfTomorrow);
$intervals = [];
$intervalStart = null;
foreach($daterange as $intervalEnd){
if ( isset($intervalStart)){
$intervals[] = $this->createTicketUsageInterval($intervalStart,$intervalEnd,$allDoorLogToday,$doorLog);
}
$intervalStart = clone $intervalEnd;
}
if ( $intervalStart < $startOfTomorrow ){
$intervals[] = $this->createTicketUsageInterval($intervalStart,$startOfTomorrow,$allDoorLogToday,$doorLog);
}
$activeInterval = $this->getActiveInterval($intervals,$createdAt);
if ( !isset($activeInterval)){
throw new ServerErrorHttpException("Active Interval not found");
}
$logCountInActiveInterval = count($activeInterval['logs']);
if ( $logCountInActiveInterval == 1){
$ticket->usage_count = $ticket->usage_count+1;
$ticket->save(false);
}
}
// select min(created_at) + INTERVAL (3 * FLOOR( ( ( HOUR( TIMEDIFF( min(created_at) , now() ) ) /3 ) ) ) ) hour as last_date
// into @p_from
// from door_log
// where created_at > CURDATE() and id_customer is not null and id_ticket_current = NEW.id_ticket_current and ( direction = 7 or direction = 3);
// select count(*) into @p_count_all_2 from door_log where created_at >= @p_from and id_ticket_current = New.id_ticket_current and ( direction = 7 or direction = 3);
// INSERT INTO devlog ( msg) values(CONCAT( 'Bel<65>p<EFBFBD>sek sz<73>ma az aktu<74>lis 3 <20>r<EFBFBD>s intervalumban: ', @p_count_all_2) );
// IF @p_count_all_2 = 1
// THEN
// INSERT INTO devlog ( msg) values( 'Az aktu<74>lis intervallumban ez az els? bel<65>p<EFBFBD>s, usage_count n<>vel<65>se' );
//
// select usage_count, max_usage_count into @p_usage_count ,@p_max_usage_count from ticket where id_ticket = NEW.id_ticket_current;
//
// update ticket set usage_count = usage_count +1 where id_ticket = New.id_ticket_current;
//
// INSERT INTO log (type,message, app, id_ticket, id_door_log,created_at, updated_at)
// values(
// 40, concat('B<>rlet haszn<7A>lat/egy nap tobbszori (elotte: ',@p_usage_count, ' > utana: ' , @p_usage_count +1 , ' max: ', @p_max_usage_count, ')' ), ' trigger_inc_ticket',New.id_ticket_current, New.id_door_log,now(),now());
// END IF;
}
//
//
//
\Yii::error("finished");
}
}
function getActiveInterval($intervals,$date){
foreach ($intervals as $interval ){
$start = $interval['start'];
$end = $interval['end'];
if ( $start <= $date && $date < $end ){
return $interval;
}
}
return null;
}
function createTicketUsageInterval($start,$end, $allLogs, $actualDoorLog){
$result = ['start' => $start, 'end' => $end , 'logs' =>[] ];
foreach ($allLogs as $log){
$createdAt = DateUtil::parseDateTime($log->created_at);
if ( $createdAt >= $start && $createdAt < $end){
$result['logs'][] = $log;
}
}
return $result;
}
function getCountDoorLogsForTicketSince($idTicket, $since)
{
\Yii::error("getting door log count for today");
return DoorLog::find()
->innerJoinWith('card')
->andWhere(['card.id_ticket_current' => $idTicket])
->andWhere(['in', 'door_log.direction', [DoorLog::$DIRECTION_IN, DoorLog::$DIRECTION_IN_WITHOUT_MOVE]])
->andWhere(['>=', 'door_log.created_at', DateUtil::formatDateUtc($since)])
->count();
}
public function readActive($cardNumber)
{
$card = Card::readCard($cardNumber);
return Ticket::readActive($card);
}
public function resetLogs($cardNumber)
{
$card = Card::readCard($cardNumber);
DoorLog::deleteAll(
['id_card' => $card->id_card]
);
}
public function getLogs($cardNumber)
{
return DoorLog::findAll(
['id_card' => $cardNumber]
);
}
public function getInfo($cardNumber)
{
$card = Card::readCard($cardNumber);
return [
'card' => $card,
'customer' => $card->customer,
'tickets' => Ticket::readActive($card),
'doorLogs' => DoorLog::findAll(
['id_card' => $card->id_card]
),
'lastDoorLog' => DoorLog::find()->orderBy(['id_door_log' => SORT_DESC])->limit(1)->one(),
'doorLogCount' => DoorLog::find()->count()
];
}
//
// public function createDoorLog($direction, $idCard, $idTicket, $createdAt)
// {
// $doorLog = new DoorLog();
// $doorLog->id_card = $idCard;
// $doorLog->direction = $direction;
// $doorLog->id_ticket_current = $idTicket;
//
// $doorLog->save(false);
// // update the created at flag
// \Yii::$app->db->createCommand('update door_log set created_at = :created_at where id_door_log = :id ')
// ->bindValue("created_at", $createdAt)
// ->bindValue("id", $doorLog->id_door_log)
// ->execute();
// }
public function createLog()
{
\Yii::error("Post create log:". \Yii::$app->request->method);
if (\Yii::$app->request->isPost) {
$log = new DoorLogForTest();
if ($log->load(\Yii::$app->request->post(), "")) {
if ($log->validate()) {
\Yii::error("Door log saving:".$log->created_at);
$log->save(false);
return $log;
}else{
throw new BadRequestHttpException(print_r($log->getErrors(),true));
}
} else {
\Yii::error("validated" . print_r($log->errors, true));
throw new BadRequestHttpException();
}
}
throw new BadRequestHttpException('Not a Post');
}
}

View File

@ -28,6 +28,22 @@ class DoorLog extends \yii\db\ActiveRecord
public static $SOURCE_APP_FORGO_VILLA = "forgo_villa"; public static $SOURCE_APP_FORGO_VILLA = "forgo_villa";
public static $SOURCE_APP_FITNESS_ADMIN = "fitness_admin"; public static $SOURCE_APP_FITNESS_ADMIN = "fitness_admin";
public static $DIRECTION_OUT_MANUAL_READ_KEY_ASSIGNED = -2; // "Kézi olvasás/Kulcs ki",
public static $DIRECTION_IN_MANUAL_READ_KEY_UNASSIGNED = -1; // "Kézi olvasás/Kulcs vissza",
public static $DIRECTION_ALL_MANUAL_READ = 0; // "Kézi olvasás",
public static $DIRECTION_OUT_WITHOUT_MOVE = 1; // "KI olvastatás mozgás nélkül",
public static $DIRECTION_IN_WITHOUT_MOVE = 3; // "BE olvastatás mozgás nélkül",
public static $DIRECTION_OUT_ = 5; // "KI mozgás",
public static $DIRECTION_IN = 7; // "BE mozgás",
public static $DIRECTION_OUT_ERROR_KEY_ASSIGNED = 9; // "KI olvastatás, van érvényes öltöző kulcs (nem enged)",
public static $DIRECTION_IN_ERROR_KEY_MISSING = 11; // "BE olvastatás, nincs érvényes öltöző kulcs (nem enged)",
public static $DIRECTION_OUT_NO_TICKET = 17; // "Bérlet érvényességi időn kívüli KI olvastatás (nem enged)",
public static $DIRECTION_IN_NO_TICKET = 19; // "Bérlet érvényességi időn kívüli BE olvastatás (nem enged)",
public static $DIRECTION_ALL_EMERGENCY = 128; // "Vésznyitás",
public static $DIRECTION_ALL_CARD_BLOCKED = 256; // "Kártya tiltva -> információ mező",
/** /**
* @inheritdoc * @inheritdoc
*/ */
@ -41,7 +57,12 @@ class DoorLog extends \yii\db\ActiveRecord
return ArrayHelper::merge([ return ArrayHelper::merge([
[ [
'class' => TimestampBehavior::className(), 'class' => TimestampBehavior::className(),
'value' => function(){ return date('Y-m-d H:i:s' ); }, 'value' => function ($event) {
if ( isset($event->sender->created_at) ){
return $event->sender->created_at;
}
return date('Y-m-d H:i:s');
},
'updatedAtAttribute' => false, 'updatedAtAttribute' => false,
] ]
], parent::behaviors()); ], parent::behaviors());
@ -76,40 +97,50 @@ class DoorLog extends \yii\db\ActiveRecord
]; ];
} }
public function getCustomer(){ public function getCustomer()
{
return $this->hasOne(Customer::className(), ["id_customer" => "id_customer"]); return $this->hasOne(Customer::className(), ["id_customer" => "id_customer"]);
} }
public function getCustomerName(){ public function getCustomerName()
{
$result = ""; $result = "";
if (isset($this->customer)) { if (isset($this->customer)) {
$result = $this->customer->name; $result = $this->customer->name;
} }
return $result; return $result;
} }
public function getCard(){
public function getCard()
{
return $this->hasOne(Card::className(), ["id_card" => "id_card"]); return $this->hasOne(Card::className(), ["id_card" => "id_card"]);
} }
public function getCardNumber(){ public function getCardNumber()
{
$result = ""; $result = "";
if (isset($this->card)) { if (isset($this->card)) {
$result = $this->card->number; $result = $this->card->number;
} }
return $result; return $result;
} }
public function getKey(){
public function getKey()
{
return $this->hasOne(Key::className(), ["id_key" => "id_key"]); return $this->hasOne(Key::className(), ["id_key" => "id_key"]);
} }
public function getKeyNumber(){ public function getKeyNumber()
{
$result = ""; $result = "";
if (isset($this->key)) { if (isset($this->key)) {
$result = $this->key->number; $result = $this->key->number;
} }
return $result; return $result;
} }
public function getDirectionName(){
public function getDirectionName()
{
$result = ""; $result = "";
if ($this->source_app == 'forgo_villa') { if ($this->source_app == 'forgo_villa') {
if (isset($this->direction)) { if (isset($this->direction)) {
@ -122,7 +153,8 @@ class DoorLog extends \yii\db\ActiveRecord
} }
public static function getSourceAppName($source_app){ public static function getSourceAppName($source_app)
{
$result = ""; $result = "";
switch ($source_app) { switch ($source_app) {
case self::$SOURCE_APP_FITNESS_ADMIN : case self::$SOURCE_APP_FITNESS_ADMIN :
@ -135,7 +167,8 @@ class DoorLog extends \yii\db\ActiveRecord
return $result; return $result;
} }
public static function getDirectionTypes( ){ public static function getDirectionTypes()
{
return [ return [
-2 => "Kézi olvasás/Kulcs ki", -2 => "Kézi olvasás/Kulcs ki",
-1 => "Kézi olvasás/Kulcs vissza", -1 => "Kézi olvasás/Kulcs vissza",
@ -162,7 +195,8 @@ class DoorLog extends \yii\db\ActiveRecord
]; ];
} }
public static function getCardFlagTexts( ){ public static function getCardFlagTexts()
{
return [ return [
0 => "Kártya érvényes bérlettel", 0 => "Kártya érvényes bérlettel",
1 => "Nincs érvényes bérlet", 1 => "Nincs érvényes bérlet",
@ -172,7 +206,8 @@ class DoorLog extends \yii\db\ActiveRecord
]; ];
} }
public static function mkDoorLog($direction,$card,$customer = null,$key = null){ public static function mkDoorLog($direction, $card, $customer = null, $key = null)
{
if (!Helper::isKeyToggleDoorLogEnabled()) { if (!Helper::isKeyToggleDoorLogEnabled()) {
return; return;

View File

@ -0,0 +1,49 @@
<?php
namespace common\models;
use Yii;
use common\components\Helper;
use yii\behaviors\TimestampBehavior;
use yii\helpers\ArrayHelper;
/**
* This is the model class for table "door_log".
*
* @property integer $id_door_log
* @property integer $id_card
* @property integer $id_customer
* @property integer $id_key
* @property integer $direction
* @property integer $type
* @property integer $id_account
* @property string $created_at
* @property string $source_app
* @property integer id_ticket_current
* @property integer card_flag
* @property integer flag_out
*/
class DoorLogForTest extends DoorLog
{
public function behaviors()
{
return [];
}
/**
* @inheritdoc
*/
public function rules()
{
return [
[
['id_card', 'id_customer', 'id_key', 'direction', 'type', 'id_ticket_current'], 'integer'
],
[['created_at'], 'string'],
[['created_at'], 'required'],
];
}
}

138
doc/trigger.sql Normal file
View File

@ -0,0 +1,138 @@
CREATE TRIGGER trigger_inc_ticket_usage_count AFTER INSERT ON `door_log` FOR EACH ROW
begin
DECLARE p_count_all Integer;
DECLARE p_count_all_2 Integer;
DECLARE p_from DATETIME;
DECLARE p_usage_count Integer;
DECLARE p_max_usage_count Integer;
DECLARE p_mo_ticket_id Integer;
DECLARE p_mo_ticket_max_usage_count Integer;
DECLARE p_allow_multiple_enter boolean;
DECLARE p_allow_enter boolean;
delete from devlog;
IF NEW.id_customer is not null and NEW.id_card is not null
THEN
IF (NEW.direction = 7 or New.direction = 3 ) and NEW.id_ticket_current is not null
then
INSERT INTO devlog ( msg) values('belepes feldoglozas indit');
select count(*) into @p_count_all from door_log where created_at >= CURDATE() and id_ticket_current = New.id_ticket_current and ( direction = 7 or direction = 3);
INSERT INTO devlog ( msg) values( concat( 'count all' ,@p_count_all ) );
IF @p_count_all = 1
THEN
select usage_count, max_usage_count into @p_usage_count ,@p_max_usage_count from ticket where id_ticket = NEW.id_ticket_current;
update ticket set usage_count = usage_count +1 where id_ticket = NEW.id_ticket_current;
INSERT INTO log (type,message, app, id_ticket, id_door_log,created_at, updated_at)
values(
30, concat('B<EFBFBD>rlet haszn<7A>lat (elotte: ',@p_usage_count, ' > utana: ' , @p_usage_count +1 , ' max: ', @p_max_usage_count, ')' ), ' trigger_inc_ticket',New.id_ticket_current, New.id_door_log,now(),now());
else
-- deltaElsoBelépés a napi első door log és 'most' között eltelt órák
-- HOUR( TIMEDIFF( min(created_at) , now() ) )
-- hány darab 3 órás intervallum telt el
-- floor : 2.75 -> 2
-- FLOOR( ( ( HOUR( TIMEDIFF( min(created_at) , now() ) ) /3 ) ) )
-- a napi első belépés után kiszámoljuk az aktuális n-edik 3órás intervallum kezdetét
select min(created_at) + INTERVAL (3 * FLOOR( ( ( HOUR( TIMEDIFF( min(created_at) , now() ) ) /3 ) ) ) ) hour as last_date
into @p_from
from door_log
where created_at > CURDATE() and id_customer is not null and id_ticket_current = NEW.id_ticket_current and ( direction = 7 or direction = 3);
select count(*) into @p_count_all_2 from door_log where created_at >= @p_from and id_ticket_current = New.id_ticket_current and ( direction = 7 or direction = 3);
INSERT INTO devlog ( msg) values(CONCAT( 'Bel<EFBFBD>p<EFBFBD>sek sz<73>ma az aktu<74>lis 3 <20>r<EFBFBD>s intervalumban: ', @p_count_all_2) );
IF @p_count_all_2 = 1
THEN
INSERT INTO devlog ( msg) values( 'Az aktu<74>lis intervallumban ez az els? bel<65>p<EFBFBD>s, usage_count n<>vel<65>se' );
select usage_count, max_usage_count into @p_usage_count ,@p_max_usage_count from ticket where id_ticket = NEW.id_ticket_current;
update ticket set usage_count = usage_count +1 where id_ticket = New.id_ticket_current;
INSERT INTO log (type,message, app, id_ticket, id_door_log,created_at, updated_at)
values(
40, concat('B<EFBFBD>rlet haszn<7A>lat/egy nap tobbszori (elotte: ',@p_usage_count, ' > utana: ' , @p_usage_count +1 , ' max: ', @p_max_usage_count, ')' ), ' trigger_inc_ticket',New.id_ticket_current, New.id_door_log,now(),now());
END IF;
END IF;
End IF;
IF NEW.direction = 5 or New.direction = 1
then
INSERT INTO devlog ( msg) values('Kil<EFBFBD>p<EFBFBD>s van folyamatban, kil<69>p<EFBFBD>sek sz<73>m<EFBFBD>nak be<62>ll<6C>t<EFBFBD>sa');
update ticket set count_move_out = usage_count where id_ticket = NEW.id_ticket_current;
END IF;
INSERT INTO devlog ( msg) values( 'K<EFBFBD>rtya valid<69>ci<63> m<>dos<6F>t<EFBFBD>sa' );
UPDATE card as c1
left JOIN ( select ticket.id_card as id_card , max(ticket.id_ticket) as id_ticket
from ticket
where ticket.start <= CURDATE()
and ticket.end >= curdate()
and ticket.status = 10
and ticket.count_move_out < ticket.max_usage_count
and ticket.id_card = New.id_card
group by id_card
order by id_card desc ) as t
on t.id_card = c1.id_card
SET c1.validity = case when t.id_card is null then ( c1.validity | 1 << 0 ) else ( c1.validity & ~(1 << 0) ) end
, c1.flag = case when t.id_card is null then ( c1.flag | 1 << 0 ) else ( c1.flag & ~(1 << 0) ) end
, c1.id_ticket_current = case when t.id_ticket is null then null else t.id_ticket end
WHERE c1.type <> 50 and c1.id_card = New.id_card;
IF NEW.direction = 5 or New.direction = 1
then
select max(ticket.id_ticket) into @p_mo_ticket_id
from ticket
where ticket.start <= CURDATE()
and ticket.end >= curdate()
and ticket.status = 10
and ticket.count_move_out < ticket.max_usage_count
and ticket.id_card = New.id_card
group by id_card
order by id_card desc;
set @p_allow_enter = true;
update card set
flag_out = ( flag_out | 1 << 1 ) ,
flag = case when @p_allow_enter then ( flag & ~(1 << 1) ) else ( flag | 1 << 1 ) end
WHERE type <> 50 and id_card = New.id_card;
END IF;
IF (NEW.direction = 7 or New.direction = 3 ) and NEW.id_ticket_current is not null
THEN
update card set
flag_out = ( flag_out & ~(1 << 1 ) ) ,
flag = ( flag | 1 << 1 )
WHERE type <> 50 and id_card = New.id_card;
END IF;
END IF;
END

20
docker/start2.sh Normal file
View File

@ -0,0 +1,20 @@
#!/usr/bin/env bash
PROJECT_DIR=$(pwd)/..
PORT=86
HOST=localhost
docker container stop fitness-web || true
docker run \
-v ${PROJECT_DIR}:/var/www/html/fitness_web \
--rm \
-d \
-p ${PORT}:80 \
--name fitness-web \
--hostname test.fintess_web.hu \
--link mariadb1:mariadb1 \
-e XDEBUG_CONFIG="idekey=PHPSTORM" \
rocho02.ddns.net/admin/cutlergyor
echo "started http://"${HOST}:${PORT}

View File

@ -9,7 +9,6 @@ use frontend\models\AccountSelect;
use yii\web\Controller; use yii\web\Controller;
use yii\web\NotFoundHttpException; use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter; use yii\filters\VerbFilter;
use yii\base\Object;
use common\models\Log; use common\models\Log;
use common\models\User; use common\models\User;

View File

@ -13,7 +13,7 @@ use yii\helpers\Url;
<?php <?php
/** @var \common\models\Ticket $ticket */ /** @var \common\models\Ticket $ticket */
$ticket = null; $ticket = null;
if (count($model->tickets) > 0) { if ( isset($model->tickets) && count($model->tickets) > 0) {
$ticket = $model->tickets[0]; $ticket = $model->tickets[0];
} }

View File

@ -24,7 +24,7 @@ $this->params['breadcrumbs'][] = $this->title;
$dt->sub( new DateInterval( 'P2D') ); $dt->sub( new DateInterval( 'P2D') );
echo \common\components\DateUtil::formatUtc($dt); echo \common\components\DateUtil::formatDateTimeUtc($dt);
echo " - "; echo " - ";
@ -33,7 +33,7 @@ $this->params['breadcrumbs'][] = $this->title;
$ticketType->time_unit_count = 3; $ticketType->time_unit_count = 3;
$dt2 = \common\components\Helper::getTicketExpirationDate($dt, $ticketType); $dt2 = \common\components\Helper::getTicketExpirationDate($dt, $ticketType);
echo \common\components\DateUtil::formatUtc($dt2); echo \common\components\DateUtil::formatDateTimeUtc($dt2);
// $dt2 = \common\components\Helper::getTicketExpirationDate($dt, $ticketType); // $dt2 = \common\components\Helper::getTicketExpirationDate($dt, $ticketType);
// echo \common\components\DateUtil::formatUtc($dt2); // echo \common\components\DateUtil::formatUtc($dt2);

View File

@ -48,6 +48,7 @@ class LoginController extends RestController
->issuedAt($time)// Configures the time that the token was issue (iat claim) ->issuedAt($time)// Configures the time that the token was issue (iat claim)
->expiresAt($time + $validFor)// Configures the expiration time of the token (exp claim) ->expiresAt($time + $validFor)// Configures the expiration time of the token (exp claim)
->withClaim('uid', $form->getMobileDevice()->getId())// Configures a new claim, called "uid" ->withClaim('uid', $form->getMobileDevice()->getId())// Configures a new claim, called "uid"
->withClaim('cardId', $form->getMobileDevice()->card->number)// Configures a new claim, called "uid"
->getToken($signer, $key); // Retrieves the generated token ->getToken($signer, $key); // Retrieves the generated token
return $this->asJson([ return $this->asJson([

View File

@ -0,0 +1,49 @@
<?php /** @noinspection PhpUnused */
namespace rest\controllers;
use common\manager\DoorManager;
use rest\models\DoorMoveForm;
use Yii;
use yii\web\BadRequestHttpException;
class DoorController extends RestController
{
public function actionMove()
{
$formModel = new DoorMoveForm();
if ($formModel->load(Yii::$app->request->post(),"") ) {
if ( !$formModel->validate()){
$keys = array_keys($formModel->getFirstErrors());
throw new BadRequestHttpException("Invalid move request:". $formModel->getFirstErrors()[$keys[0]]);
}
$doorManager = new DoorManager();
$doorManager->move($formModel->cardNumber,$formModel->device,$formModel->direction, $formModel->createdAt, $formModel->date);
}
return $formModel;
}
public function actionReset($cardNumber){
$doorManager = new DoorManager();
$doorManager->resetLogs($cardNumber);
}
public function actionInfo($cardNumber){
$doorManager = new DoorManager();
return $doorManager->getInfo($cardNumber);
}
public function actionCreateDoorLog(){
$doorManager = new DoorManager();
return $doorManager->createLog();
}
public function actionPing(){
\Yii::$app->response->statusCode = 204;
echo "hello";
}
}

View File

@ -0,0 +1,24 @@
<?php
namespace rest\models;
use yii\base\Model;
class DoorMoveForm extends Model
{
public $cardNumber;
public $device;
public $direction;
public $test;
public $createdAt;
public $date;
public function rules( ) {
return [
[ ['cardNumber', 'device', 'direction' ], 'required'],
[ ['createdAt', 'date' ], 'string']
];
}
}

7715
test/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

20
test/package.json Normal file
View File

@ -0,0 +1,20 @@
{
"name": "rest-test",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "jest",
"test:watch": "jest --watch"
},
"author": "",
"license": "ISC",
"devDependencies": {
"jest": "^27.5.1",
"supertest": "^6.2.2"
},
"dependencies": {
"axios": "^0.27.0",
"node-fetch": "^3.2.3"
}
}

212
test/src/rest/move.test.js Normal file
View File

@ -0,0 +1,212 @@
// const fetch = require('node-fetch');
// const {test, expect} = require('jest')
const axios = require('axios');
const url = 'http://localhost:86/fitness_web/rest/web/index.php?r=';
const auth = "Basic ZG9vcl9zeXN0ZW06ZG9vcnN5c3RlbTE=";
const TEST_CARD_NUMBER = "10WMVXMZ";
const TEST_AUTH_HEADER = {
'Authorization': auth
};
function fail(reason = "fail was called in a test.") {
throw new Error(reason);
}
function secondsSinceEpoch(d){
return Math.floor( d.getTime() / 1000 );
}
global.fail = fail;
function formatDateTime(date){
let s = date.getFullYear()
+ "-"
+ (date.getMonth() + 1+"").padStart(2,"0")
+ "-"
+ (date.getDate() +"").padStart(2,"0")
+ " "
+ (date.getHours()+"").padStart(2,"0")
+ ":"
+ (date.getMinutes()+"").padStart(2,"0")
+ ":"
+ "00";
console.info(s);
return s;
}
// This test fails because 1 !== 2
test('Testing to see if Jest works', () => {
expect(1).toBe(1)
})
class Client {
async reset($cardNumber) {
return await axios.get(url + 'door/reset&cardNumber=' + $cardNumber, {
headers: {...TEST_AUTH_HEADER}
});
}
async getInfo($cardNumber) {
return await axios.get(url + 'door/info&cardNumber=' + $cardNumber, {
headers: {...TEST_AUTH_HEADER}
});
}
async createDoorLog(doorlog) {
return await axios.post(url + 'door/create-door-log', doorlog,{
headers: {...TEST_AUTH_HEADER},
});
}
async move(data, headers) {
return await axios.get(url + 'door/move', {
headers: {
...TEST_AUTH_HEADER,
...headers
},
data: {
direction: 3,
device: 'QRCODE',
cardNumber: '10WMVXMZ',
...data
}
});
}
async moveWithInfo(data, headers) {
const before = await this.getInfo(data.cardNumber);
const response = await this.move(data, headers);
const after = await this.getInfo(data.cardNumber);
return {
before,
response,
after
}
}
}
test('Emergency open', async () => {
try {
const client = new Client();
await client.reset(TEST_CARD_NUMBER)
let before = await client.getInfo(TEST_CARD_NUMBER);
let response = await client.move({direction: 128})
expect(response.status).toBe(204);
let after = await client.getInfo(TEST_CARD_NUMBER);
// console.info("before:after", before.data.doorLogCount, after.data.doorLogCount);
expect(after.data.doorLogs.length).toBe(0);
expect(after.data.doorLogCount).toBe("" + (parseInt(before.data.doorLogCount) + 1));
expect(after.data.lastDoorLog?.direction).toBe(128);
} catch (e) {
throw e;
}
})
test('Error if card not exists', async () => {
const client = new Client();
try {
await client.move({cardNumber: 'notexists', direction: 3});
} catch (e) {
expect(e?.response?.status).toBe(400);
}
expect.assertions(1);
})
test('Allow card type employee', async () => {
const client = new Client();
let createdAt = new Date();
createdAt.setHours(18);
const info = await client.moveWithInfo({cardNumber: 'employee', direction: 3, createdAt: secondsSinceEpoch(createdAt)});
// console.info("employee ready");
const lastLog = info.after.data.lastDoorLog;
expect(lastLog.id_card).not.toBeNull();
expect(lastLog.direction).toBe(3);
})
test('Normal Ticket: usage count will be increased on entry', async () => {
const client = new Client();
client.reset(TEST_CARD_NUMBER);
const info = await client.moveWithInfo({cardNumber: TEST_CARD_NUMBER, direction: 3});
expect(info?.response?.status).toBe(200);
const ticketBefore = info?.before.data?.tickets[0];
const ticketAfter = info?.after.data?.tickets[0];
expect(ticketBefore.usage_count + 1).toBe(ticketAfter.usage_count);
const info2 = await client.moveWithInfo({cardNumber: TEST_CARD_NUMBER, direction: 3});
expect(info?.response?.status).toBe(200);
const ticketAfter2 = info2?.after.data?.tickets[0];
expect(ticketAfter.usage_count).toBe(ticketAfter2.usage_count);
})
test('Normal Ticket: increase door log after 3 hours', async () => {
const client = new Client();
client.reset(TEST_CARD_NUMBER);
let info = await client.getInfo(TEST_CARD_NUMBER);
// First log on this day
let date = new Date();
date.setHours(1);
date.setMinutes(0,0,0);
await client.createDoorLog({
'created_at' : formatDateTime( date ),
'id_card': info.data.card.id_card,
'id_customer': info.data.customer.id_customer,
'id_ticket_current' : info.data.tickets[0].id_ticket,
'direction' : 3
});
// second door in @ 2:00 am. Less then 3 hours since first door log, so no
// usage_count must be consumed
let createdAt = new Date();
createdAt.setHours(2);
createdAt.setMinutes(0,0,0);
info = await client.moveWithInfo({
cardNumber: TEST_CARD_NUMBER,
direction: 3,
createdAt: formatDateTime(createdAt)
});
let ticketBefore = info?.before.data?.tickets[0];
let ticketAfter = info?.after.data?.tickets[0];
expect(ticketBefore.usage_count).toBe(ticketAfter.usage_count);
// @4:00 3 hours already passed, we need consume usage count
createdAt = new Date();
createdAt.setHours(4);
createdAt.setMinutes(0,0,0);
info = await client.moveWithInfo({
cardNumber: TEST_CARD_NUMBER,
direction: 3,
createdAt: formatDateTime(createdAt)
});
ticketBefore = info?.before.data?.tickets[0];
ticketAfter = info?.after.data?.tickets[0];
expect(ticketBefore.usage_count+1).toBe(ticketAfter.usage_count);
// @6:00 2 hours passed since 4, we don't need consume usage count
createdAt = new Date();
createdAt.setHours(5);
createdAt.setMinutes(0,0,0);
info = await client.moveWithInfo({
cardNumber: TEST_CARD_NUMBER,
direction: 3,
createdAt: formatDateTime(createdAt)
});
ticketBefore = info?.before.data?.tickets[0];
ticketAfter = info?.after.data?.tickets[0];
expect(ticketBefore.usage_count).toBe(ticketAfter.usage_count);
// @20:15 Need to consume usage count
createdAt = new Date();
createdAt.setHours(20);
createdAt.setMinutes(15,0,0);
info = await client.moveWithInfo({
cardNumber: TEST_CARD_NUMBER,
direction: 3,
createdAt: formatDateTime(createdAt)
});
ticketBefore = info?.before.data?.tickets[0];
ticketAfter = info?.after.data?.tickets[0];
expect(ticketBefore.usage_count+1).toBe(ticketAfter.usage_count);
},10000)