15 Commits

  1. 75
      app/Listeners/BusinessUpdateListener.php
  2. 1
      app/Models/Business.php
  3. 2
      app/Models/User.php
  4. 1
      app/Providers/EventServiceProvider.php
  5. 8
      app/Providers/RouteServiceProvider.php
  6. 7
      bootstrap/app.php
  7. 69
      composer.lock
  8. 1
      config/app.php
  9. 18
      config/cors.php
  10. 34
      config/cors.php.bcp
  11. 40
      config/cors.php.laravel
  12. 14
      config/cors.php.lumen
  13. 2
      database/migrations/2020_08_18_085017_fingerprints.php
  14. 13
      database/seeds/BusinessSeeder.php
  15. 2
      database/seeds/UserSeeder.php
  16. 26
      docker-compose.yml
  17. 4
      resources/lang/fa/notification.php

75
app/Listeners/BusinessUpdateListener.php

@ -5,12 +5,22 @@ namespace App\Listeners;
use App\Models\User;
use App\Models\Business;
use Illuminate\Support\Arr;
<<<<<<< Updated upstream
use App\Channels\FcmChannel;
use App\Events\BusinessUpdate;
use App\Events\BusinessUserCreate;
use App\Notifications\DBNotification;
use App\Notifications\FcmNotification;
use App\Notifications\MailNotification;
use Illuminate\Queue\InteractsWithQueue;
=======
use App\Events\BusinessUpdate;
use App\Events\BusinessUserCreate;
use App\Notifications\DBNotification;
use App\Notifications\MailNotification;
use Illuminate\Queue\InteractsWithQueue;
>>>>>>> Stashed changes
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Support\Facades\Notification;
@ -19,6 +29,70 @@ class BusinessUpdateListener
public function handle(BusinessUpdate $event)
{
$payload = $event->message;
<<<<<<< Updated upstream
$business = Business::findOrFail($payload->business)->load('owners');
$this->checkWalletIsRunningLow($business);
$this->checkWalletIsEmptyOrNegative($business);
$this->accountHasBeenSuspended($business);
}
public function checkWalletIsRunningLow(Business $business): void
{
$moving_average_days = 7;
$predict_ahead_hours = 24;
$hours = $moving_average_days * 24;
$business->load([
'cost' => fn ($query) => $query->where('created_at', '>=', now('Asia/Tehran')->subHours($hours)),
]);
// The wallet is running out
// Calculate the average consumption of the N past days
$average_cost = $business->cost->sum('cost') / $hours;
// Average hourly consumption multiplied by the next 24 hours
// If the account does not charge as much as in the next 24 hours, notified.
$message = ['body' => __('notification.business.wallet_almost_empty')];
if (($business->wallet / $predict_ahead_hours) < $average_cost) {
Notification::send($business->owners, new MailNotification($message));
Notification::send($business->owners, new DBNotification($message));
}
}
public function checkWalletIsEmptyOrNegative(Business $business): void
{
$message = ['body' => __('notification.business.wallet_empty')];
if ($business->wallet <= 0) {
Notification::send($business->owners, new MailNotification($message));
Notification::send($business->owners, new DBNotification($message));
}
}
public function accountHasBeenSuspended(Business $business): void
{
if ($business->wallet > 0) {
return;
}
$recent_payments_number = 10;
$negativity_threshold = 20;
$business->load([
'transactions' => fn ($query) => $query->where('succeeded', '=', true)
->orderBy('created_at')
->take($recent_payments_number),
]);
// Your account has been blocked.
// What is the average of the last 10 payments?
$average_payment = $business->transactions->average('amount');
$threshold = $average_payment / 100 * $negativity_threshold;
$message = ['body' => __('notification.business.suspended')];
if ($business->wallet < $threshold) {
Notification::send($business->owners, new MailNotification($message));
Notification::send($business->owners, new DBNotification($message));
=======
$wallet = $payload?->data?->diff?->wallet;
$owners = Business::findOrFail($payload->business)->owners;
@ -27,6 +101,7 @@ class BusinessUpdateListener
if ($wallet < 0) {
Notification::send($owners, new MailNotification($message));
Notification::send($owners, new DBNotification($message));
>>>>>>> Stashed changes
}
}
}

1
app/Models/Business.php

@ -43,6 +43,7 @@ class Business extends Model implements HasMedia
protected $casts = [
'has_avatar' => 'boolean',
'calculated_at' => 'datetime',
'wallet' => 'integer',
];
public function getValueOf(?string $key)

2
app/Models/User.php

@ -5,6 +5,8 @@ namespace App\Models;
use App\Models\File;
use App\Models\Model;
use App\Models\SoftDeletes;
use App\Models\ReportableRelation;
use Illuminate\Notifications\Notifiable;
use Illuminate\Validation\Rule;
use Illuminate\Http\UploadedFile;
use Spatie\MediaLibrary\HasMedia;

1
app/Providers/EventServiceProvider.php

@ -45,6 +45,7 @@ class EventServiceProvider extends ServiceProvider
ProjectUserCreate::class => [
ProjectUserCreateNotif::class,
],
BusinessUpdate::class => [
BusinessUpdateListener::class,
],

8
app/Providers/RouteServiceProvider.php

@ -38,14 +38,14 @@ class RouteServiceProvider extends ServiceProvider
$this->configureRateLimiting();
$this->routes(function () {
Route::prefix('api')
Route::prefix('user/v1')
->middleware('api')
->namespace($this->namespace)
->group(base_path('routes/api.php'));
Route::middleware('web')
->namespace($this->namespace)
->group(base_path('routes/web.php'));
// Route::middleware('web')
// ->namespace($this->namespace)
// ->group(base_path('routes/web.php'));
});
}

7
bootstrap/app.php

@ -52,11 +52,4 @@ $app->singleton(
|
*/
$app->router->group([
'namespace' => 'App\Http\Controllers',
'prefix' => '/user/v1/'
], function ($router) {
require __DIR__.'/../routes/api.php';
});
return $app;

69
composer.lock

@ -119,16 +119,16 @@
},
{
"name": "aws/aws-sdk-php",
"version": "3.173.23",
"version": "3.173.25",
"source": {
"type": "git",
"url": "https://github.com/aws/aws-sdk-php.git",
"reference": "259fa1a47a46f81e66b1ea328ed961a58fa061c2"
"reference": "69e4653acf8cd855e9010ec4e0e0a7d074c77ee1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/259fa1a47a46f81e66b1ea328ed961a58fa061c2",
"reference": "259fa1a47a46f81e66b1ea328ed961a58fa061c2",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/69e4653acf8cd855e9010ec4e0e0a7d074c77ee1",
"reference": "69e4653acf8cd855e9010ec4e0e0a7d074c77ee1",
"shasum": ""
},
"require": {
@ -203,9 +203,9 @@
"support": {
"forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
"issues": "https://github.com/aws/aws-sdk-php/issues",
"source": "https://github.com/aws/aws-sdk-php/tree/3.173.23"
"source": "https://github.com/aws/aws-sdk-php/tree/3.173.25"
},
"time": "2021-03-05T19:20:35+00:00"
"time": "2021-03-09T19:14:56+00:00"
},
{
"name": "beberlei/assert",
@ -1313,16 +1313,16 @@
},
{
"name": "laravel/framework",
"version": "v8.31.0",
"version": "v8.32.1",
"source": {
"type": "git",
"url": "https://github.com/laravel/framework.git",
"reference": "2aa5c2488d25178ebc097052c7897a0e463ddc35"
"reference": "7c37b64f8153c16b6406f5c28cf37828ebbe8846"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/framework/zipball/2aa5c2488d25178ebc097052c7897a0e463ddc35",
"reference": "2aa5c2488d25178ebc097052c7897a0e463ddc35",
"url": "https://api.github.com/repos/laravel/framework/zipball/7c37b64f8153c16b6406f5c28cf37828ebbe8846",
"reference": "7c37b64f8153c16b6406f5c28cf37828ebbe8846",
"shasum": ""
},
"require": {
@ -1477,7 +1477,7 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
"time": "2021-03-04T15:22:36+00:00"
"time": "2021-03-09T15:37:45+00:00"
},
{
"name": "laravel/helpers",
@ -4094,20 +4094,20 @@
},
{
"name": "swiftmailer/swiftmailer",
"version": "v6.2.6",
"version": "v6.2.7",
"source": {
"type": "git",
"url": "https://github.com/swiftmailer/swiftmailer.git",
"reference": "d2791ff0b73247cdc2096b14f5580aba40c12bff"
"reference": "15f7faf8508e04471f666633addacf54c0ab5933"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/d2791ff0b73247cdc2096b14f5580aba40c12bff",
"reference": "d2791ff0b73247cdc2096b14f5580aba40c12bff",
"url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/15f7faf8508e04471f666633addacf54c0ab5933",
"reference": "15f7faf8508e04471f666633addacf54c0ab5933",
"shasum": ""
},
"require": {
"egulias/email-validator": "^2.0",
"egulias/email-validator": "^2.0|^3.1",
"php": ">=7.0.0",
"symfony/polyfill-iconv": "^1.0",
"symfony/polyfill-intl-idn": "^1.10",
@ -4153,7 +4153,7 @@
],
"support": {
"issues": "https://github.com/swiftmailer/swiftmailer/issues",
"source": "https://github.com/swiftmailer/swiftmailer/tree/v6.2.6"
"source": "https://github.com/swiftmailer/swiftmailer/tree/v6.2.7"
},
"funding": [
{
@ -4165,7 +4165,7 @@
"type": "tidelift"
}
],
"time": "2021-03-05T12:08:49+00:00"
"time": "2021-03-09T12:30:35+00:00"
},
{
"name": "symfony/console",
@ -6627,30 +6627,35 @@
},
{
"name": "webmozart/assert",
"version": "1.9.1",
"version": "1.10.0",
"source": {
"type": "git",
"url": "https://github.com/webmozarts/assert.git",
"reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389"
"reference": "6964c76c7804814a842473e0c8fd15bab0f18e25"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/webmozarts/assert/zipball/bafc69caeb4d49c39fd0779086c03a3738cbb389",
"reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389",
"url": "https://api.github.com/repos/webmozarts/assert/zipball/6964c76c7804814a842473e0c8fd15bab0f18e25",
"reference": "6964c76c7804814a842473e0c8fd15bab0f18e25",
"shasum": ""
},
"require": {
"php": "^5.3.3 || ^7.0 || ^8.0",
"php": "^7.2 || ^8.0",
"symfony/polyfill-ctype": "^1.8"
},
"conflict": {
"phpstan/phpstan": "<0.12.20",
"vimeo/psalm": "<3.9.1"
"vimeo/psalm": "<4.6.1 || 4.6.2"
},
"require-dev": {
"phpunit/phpunit": "^4.8.36 || ^7.5.13"
"phpunit/phpunit": "^8.5.13"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.10-dev"
}
},
"autoload": {
"psr-4": {
"Webmozart\\Assert\\": "src/"
@ -6674,9 +6679,9 @@
],
"support": {
"issues": "https://github.com/webmozarts/assert/issues",
"source": "https://github.com/webmozarts/assert/tree/1.9.1"
"source": "https://github.com/webmozarts/assert/tree/1.10.0"
},
"time": "2020-07-08T17:02:28+00:00"
"time": "2021-03-09T10:59:23+00:00"
}
],
"packages-dev": [
@ -7183,16 +7188,16 @@
},
{
"name": "laravel/sail",
"version": "v1.4.6",
"version": "v1.4.7",
"source": {
"type": "git",
"url": "https://github.com/laravel/sail.git",
"reference": "59ee7e2b2efeb644eabea719186db91d11666733"
"reference": "c57927e1e3919a317034ff706c684f01f15c10d8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/sail/zipball/59ee7e2b2efeb644eabea719186db91d11666733",
"reference": "59ee7e2b2efeb644eabea719186db91d11666733",
"url": "https://api.github.com/repos/laravel/sail/zipball/c57927e1e3919a317034ff706c684f01f15c10d8",
"reference": "c57927e1e3919a317034ff706c684f01f15c10d8",
"shasum": ""
},
"require": {
@ -7239,7 +7244,7 @@
"issues": "https://github.com/laravel/sail/issues",
"source": "https://github.com/laravel/sail"
},
"time": "2021-03-03T15:22:44+00:00"
"time": "2021-03-09T19:15:10+00:00"
},
{
"name": "mockery/mockery",

1
config/app.php

@ -65,6 +65,7 @@ return [
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
App\Utilities\Zarinpal\Laravel\ZarinpalServiceProvider::class,
Fruitcake\Cors\CorsServiceProvider::class
],
'aliases' => [

18
config/cors.php

@ -1,27 +1,13 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Cross-Origin Resource Sharing (CORS) Configuration
|--------------------------------------------------------------------------
|
| Here you may configure your settings for cross-origin resource sharing
| or "CORS". This determines what cross-origin operations may execute
| in web browsers. You are free to adjust these settings as needed.
|
| To learn more: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
|
*/
'paths' => ['api/*', 'sanctum/csrf-cookie'],
'paths' => ['user/v1/*'],
'allowed_methods' => ['*'],
'allowed_origins' => ['*'],
'allowed_origins_patterns' => ['Content-Type', 'X-Requested-With'],
'allowedOriginsPatterns' => ['Content-Type', 'X-Requested-With'],
'allowed_headers' => ['*'],

34
config/cors.php.bcp

@ -0,0 +1,34 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Cross-Origin Resource Sharing (CORS) Configuration
|--------------------------------------------------------------------------
|
| Here you may configure your settings for cross-origin resource sharing
| or "CORS". This determines what cross-origin operations may execute
| in web browsers. You are free to adjust these settings as needed.
|
| To learn more: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
|
*/
'paths' => ['*', 'sanctum/csrf-cookie'],
'allowed_methods' => ['*'],
'allowed_origins' => ['*'],
'allowed_origins_patterns' => ['*'],
'allowed_headers' => ['*'],
'exposed_headers' => [],
'max_age' => 0,
'supports_credentials' => false,
];

40
config/cors.php.laravel

@ -1,59 +1,19 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Laravel CORS Options
|--------------------------------------------------------------------------
|
| The allowed_methods and allowed_headers options are case-insensitive.
|
| You don't need to provide both allowed_origins and allowed_origins_patterns.
| If one of the strings passed matches, it is considered a valid origin.
|
| If ['*'] is provided to allowed_methods, allowed_origins or allowed_headers
| all methods / origins / headers are allowed.
|
*/
/*
* You can enable CORS for 1 or multiple paths.
* Example: ['api/*']
*/
'paths' => ['/*'],
/*
* Matches the request method. `['*']` allows all methods.
*/
'allowed_methods' => ['*'],
/*
* Matches the request origin. `['*']` allows all origins. Wildcards can be used, eg `*.mydomain.com`
*/
'allowed_origins' => ['*'],
/*
* Patterns that can be used with `preg_match` to match the origin.
*/
'allowedOriginsPatterns' => ['Content-Type', 'X-Requested-With'],
/*
* Sets the Access-Control-Allow-Headers response header. `['*']` allows all headers.
*/
'allowed_headers' => ['*'],
/*
* Sets the Access-Control-Expose-Headers response header with these headers.
*/
'exposed_headers' => [],
/*
* Sets the Access-Control-Max-Age response header when > 0.
*/
'max_age' => 0,
/*
* Sets the Access-Control-Allow-Credentials header.
*/
'supports_credentials' => false,
];

14
config/cors.php.lumen

@ -1,20 +1,6 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Cross-Origin Resource Sharing (CORS) Configuration
|--------------------------------------------------------------------------
|
| Here you may configure your settings for cross-origin resource sharing
| or "CORS". This determines what cross-origin operations may execute
| in web browsers. You are free to adjust these settings as needed.
|
| To learn more: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
|
*/
'paths' => ['api/*', 'sanctum/csrf-cookie'],
'allowed_methods' => ['*'],

2
database/migrations/2020_08_18_085017_fingerprints.php

@ -22,7 +22,7 @@ class Fingerprints extends Migration
$table->decimal('latitude', 10, 4);
$table->decimal('longitude', 11, 4);
$table->char('token', 60)->unique();
$table->text('fcm_token')->unique()->nullable();
$table->text('fcm_token')->nullable();
$table->timestamps();
});
}

13
database/seeds/BusinessSeeder.php

@ -72,6 +72,19 @@ class BusinessSeeder extends Seeder
Project::insert($projects);
DB::table('project_user')->insert($project_user);
DB::table('systems')->insert($systems);
// make sure user with id 1 is owner in one business
$user_businesses = DB::table('business_user')->where('user_id', 1)->get();
if ($user_businesses->max('level') < enum('roles.manager.id')){
$firstRecord = $user_businesses->first();
DB::table('business_user')
->where([
'business_id' => $firstRecord->business_id,
'user_id' => $firstRecord->user_id,
'level' => $firstRecord->level,
]) ->update(['level' => enum('roles.manager.id')]);
}
/**
* Every business has one or more owners, which might be the owner of other business as well
*/

2
database/seeds/UserSeeder.php

@ -15,6 +15,6 @@ class UserSeeder extends Seeder
Fingerprint::insert(
factory(Fingerprint::class, 5000)->raw()
);
User::where('id', 1)->update(['mobile' => '09123456789']);
User::where('id', 1)->update(['email' => 'masouddarvishiv56@gmail.com']);
}
}

26
docker-compose.yml

@ -12,7 +12,7 @@ services:
RABBITMQ_DEFAULT_PASS: "root"
RABBITMQ_DEFAULT_VHOST: "/"
networks:
- sail
- hi-user
volumes:
- ./definitions.json:/etc/rabbitmq/definitions.json
depends_on:
@ -26,7 +26,7 @@ services:
MINIO_ACCESS_KEY: root
MINIO_SECRET_KEY: minioroot
networks:
- sail
- hi-user
depends_on:
- laravel.test
commander:
@ -42,9 +42,7 @@ services:
depends_on:
- redis
networks:
- sail
depends_on:
- redis
- hi-user
redis:
image: redis:latest
ports:
@ -53,7 +51,7 @@ services:
volumes:
- redis-data:/data
networks:
- sail
- hi-user
laravel.test:
build:
context: ./vendor/laravel/sail/runtimes/8.0
@ -63,13 +61,15 @@ services:
image: sail-8.0/app
ports:
- '${APP_PORT:-80}:80'
container_name: "liwo-user-app"
environment:
WWWUSER: '${WWWUSER}'
LARAVEL_SAIL: 1
volumes:
- '.:/var/www/html'
networks:
- sail
- hi-user
- hi
depends_on:
- mysql
mysql:
@ -85,7 +85,7 @@ services:
volumes:
- 'sailmysql:/var/lib/mysql'
networks:
- sail
- hi-user
healthcheck:
test: ["CMD", "mysqladmin", "ping"]
pma:
@ -99,7 +99,7 @@ services:
depends_on:
- mysql
networks:
- sail
- hi-user
mailhog:
image: mailhog/mailhog
logging:
@ -108,10 +108,12 @@ services:
- 1025:1025 # smtp server
- 8025:8025 # web ui
networks:
- sail
- hi-user
networks:
sail:
driver: bridge
hi-user:
external: false
hi:
external: true
volumes:
redis-data:
driver: local

4
resources/lang/fa/notification.php

@ -80,7 +80,9 @@ return [
'business' => [
'update' => 'کاربر :user به کسب و کار :business اضافه شد.',
'update_wallet' => 'پول رو میدی یا پولت کنم',
'wallet_almost_empty' => 'کیف پول تقریبا خالی است.',
'wallet_empty' => 'کیف پول خالی است.',
'suspended' => 'حساب مسدود شد.',
],
];
Loading…
Cancel
Save