Browse Source

Merge branch 'main' into gterdem/docker-compose

pull/104/head
Galip Tolga Erdem 4 years ago
parent
commit
0af5ace773
  1. 31
      apps/angular/angular.json
  2. 3
      apps/angular/package.json
  3. 1
      apps/angular/projects/catalog/tsconfig.lib.json
  4. 1
      apps/angular/projects/catalog/tsconfig.lib.prod.json
  5. 24
      apps/angular/projects/ordering/README.md
  6. 7
      apps/angular/projects/ordering/config/ng-package.json
  7. 2
      apps/angular/projects/ordering/config/src/enums/index.ts
  8. 6
      apps/angular/projects/ordering/config/src/enums/policy-names.ts
  9. 3
      apps/angular/projects/ordering/config/src/enums/route-names.ts
  10. 12
      apps/angular/projects/ordering/config/src/ordering-config.module.ts
  11. 1
      apps/angular/projects/ordering/config/src/providers/index.ts
  12. 23
      apps/angular/projects/ordering/config/src/providers/route.provider.ts
  13. 3
      apps/angular/projects/ordering/config/src/public-api.ts
  14. 44
      apps/angular/projects/ordering/karma.conf.js
  15. 7
      apps/angular/projects/ordering/ng-package.json
  16. 11
      apps/angular/projects/ordering/package.json
  17. 3
      apps/angular/projects/ordering/src/lib/index.ts
  18. 5
      apps/angular/projects/ordering/src/lib/order-view-model.ts
  19. 17
      apps/angular/projects/ordering/src/lib/proxy/README.md
  20. 2377
      apps/angular/projects/ordering/src/lib/proxy/generate-proxy.json
  21. 2
      apps/angular/projects/ordering/src/lib/proxy/index.ts
  22. 3
      apps/angular/projects/ordering/src/lib/proxy/orders/index.ts
  23. 58
      apps/angular/projects/ordering/src/lib/proxy/orders/models.ts
  24. 10
      apps/angular/projects/ordering/src/lib/proxy/orders/order-status.enum.ts
  25. 74
      apps/angular/projects/ordering/src/lib/proxy/orders/order.service.ts
  26. 8
      apps/angular/projects/ordering/src/lib/to-order-view-model.ts
  27. 13
      apps/angular/projects/ordering/src/ordering-routing.module.ts
  28. 9
      apps/angular/projects/ordering/src/ordering.module.ts
  29. 1
      apps/angular/projects/ordering/src/pages/index.ts
  30. 3
      apps/angular/projects/ordering/src/pages/orders/index.ts
  31. 20
      apps/angular/projects/ordering/src/pages/orders/order-detail/order-detail-item/order-detail-item.component.ts
  32. 78
      apps/angular/projects/ordering/src/pages/orders/order-detail/order-detail.component.html
  33. 17
      apps/angular/projects/ordering/src/pages/orders/order-detail/order-detail.component.ts
  34. 11
      apps/angular/projects/ordering/src/pages/orders/orders-routing.module.ts
  35. 87
      apps/angular/projects/ordering/src/pages/orders/orders.component.html
  36. 25
      apps/angular/projects/ordering/src/pages/orders/orders.component.spec.ts
  37. 83
      apps/angular/projects/ordering/src/pages/orders/orders.component.ts
  38. 16
      apps/angular/projects/ordering/src/pages/orders/orders.module.ts
  39. 7
      apps/angular/projects/ordering/src/public-api.ts
  40. 28
      apps/angular/projects/ordering/src/test.ts
  41. 19
      apps/angular/projects/ordering/tsconfig.lib.json
  42. 9
      apps/angular/projects/ordering/tsconfig.lib.prod.json
  43. 17
      apps/angular/projects/ordering/tsconfig.spec.json
  44. 4
      apps/angular/src/app/app-routing.module.ts
  45. 4
      apps/angular/src/app/app.module.ts
  46. 10
      apps/angular/src/environments/environment.prod.ts
  47. 13
      apps/angular/src/environments/environment.ts
  48. 5
      apps/angular/src/environments/my-environment.ts
  49. 8
      apps/angular/tsconfig.json
  50. 2
      apps/public-web/src/EShopOnAbp.PublicWeb/Components/UserOrders/Default.cshtml
  51. 28
      apps/public-web/src/EShopOnAbp.PublicWeb/EShopOnAbpPublicWebModule.cs
  52. 19
      apps/public-web/src/EShopOnAbp.PublicWeb/Pages/Index.cshtml
  53. 43
      gateways/web-public/src/EShopOnAbp.WebPublicGateway/EShopOnAbpWebPublicGatewayModule.cs
  54. 1
      gateways/web-public/src/EShopOnAbp.WebPublicGateway/appsettings.json
  55. 100
      gateways/web-public/src/EShopOnAbp.WebPublicGateway/ocelot.json
  56. 100
      gateways/web-public/src/EShopOnAbp.WebPublicGateway/yarp.json
  57. 29
      gateways/web/src/EShopOnAbp.WebGateway/EShopOnAbpWebGatewayModule.cs
  58. 90
      gateways/web/src/EShopOnAbp.WebGateway/ocelot.json
  59. 86
      gateways/web/src/EShopOnAbp.WebGateway/yarp.json
  60. 4
      services/administration/src/EShopOnAbp.AdministrationService.Application.Contracts/AdministrationServiceApplicationContractsModule.cs
  61. 2
      services/administration/src/EShopOnAbp.AdministrationService.Application.Contracts/EShopOnAbp.AdministrationService.Application.Contracts.csproj
  62. 18
      services/basket/src/EShopOnAbp.BasketService/BasketServiceModule.cs
  63. 12
      services/catalog/src/EShopOnAbp.CatalogService.HttpApi.Host/EShopOnAbp.CatalogService.HttpApi.Host.csproj
  64. 0
      services/catalog/src/EShopOnAbp.CatalogService.HttpApi.Host/wwwroot/product-images/asus.jpg
  65. 0
      services/catalog/src/EShopOnAbp.CatalogService.HttpApi.Host/wwwroot/product-images/beats.jpg
  66. 0
      services/catalog/src/EShopOnAbp.CatalogService.HttpApi.Host/wwwroot/product-images/bluecat.jpg
  67. 0
      services/catalog/src/EShopOnAbp.CatalogService.HttpApi.Host/wwwroot/product-images/lego.jpg
  68. 0
      services/catalog/src/EShopOnAbp.CatalogService.HttpApi.Host/wwwroot/product-images/nikon.jpg
  69. 0
      services/catalog/src/EShopOnAbp.CatalogService.HttpApi.Host/wwwroot/product-images/noimage.jpg
  70. 0
      services/catalog/src/EShopOnAbp.CatalogService.HttpApi.Host/wwwroot/product-images/oki.jpg
  71. 0
      services/catalog/src/EShopOnAbp.CatalogService.HttpApi.Host/wwwroot/product-images/playstation.jpg
  72. 0
      services/catalog/src/EShopOnAbp.CatalogService.HttpApi.Host/wwwroot/product-images/rampage.jpg
  73. 0
      services/catalog/src/EShopOnAbp.CatalogService.HttpApi.Host/wwwroot/product-images/sunny.jpg
  74. 30
      services/identity/src/EShopOnAbp.IdentityService.HttpApi.Host/DbMigrations/IdentityServerDataSeeder.cs
  75. 7
      services/ordering/src/EShopOnAbp.OrderingService.Application.Contracts/Orders/GetOrdersInput.cs
  76. 6
      services/ordering/src/EShopOnAbp.OrderingService.Application.Contracts/Orders/IOrderAppService.cs
  77. 6
      services/ordering/src/EShopOnAbp.OrderingService.Application.Contracts/Permissions/OrderingServicePermissionDefinitionProvider.cs
  78. 9
      services/ordering/src/EShopOnAbp.OrderingService.Application.Contracts/Permissions/OrderingServicePermissions.cs
  79. 41
      services/ordering/src/EShopOnAbp.OrderingService.Application/Orders/OrderAppService.cs
  80. 35
      services/ordering/src/EShopOnAbp.OrderingService.Domain.Shared/Localization/OrderingService/en.json
  81. 35
      services/ordering/src/EShopOnAbp.OrderingService.Domain.Shared/Localization/OrderingService/tr.json
  82. 8
      services/ordering/src/EShopOnAbp.OrderingService.Domain.Shared/Localization/OrderingService/zh-Hans.json
  83. 17
      services/ordering/src/EShopOnAbp.OrderingService.Domain.Shared/Orders/OrderCancelledEto.cs
  84. 5
      services/ordering/src/EShopOnAbp.OrderingService.Domain/Orders/IOrderRepository.cs
  85. 16
      services/ordering/src/EShopOnAbp.OrderingService.Domain/Orders/Order.cs
  86. 25
      services/ordering/src/EShopOnAbp.OrderingService.Domain/Orders/OrderManager.cs
  87. 12
      services/ordering/src/EShopOnAbp.OrderingService.EntityFrameworkCore/Orders/EfCoreOrderRepository.cs
  88. 87
      services/ordering/src/EShopOnAbp.OrderingService.HttpApi.Client/ClientProxies/OrderClientProxy.Generated.cs
  89. 172
      services/ordering/src/EShopOnAbp.OrderingService.HttpApi.Client/ClientProxies/ordering-generate-proxy.json
  90. 2
      services/ordering/src/EShopOnAbp.OrderingService.HttpApi.Host/appsettings.json
  91. 33
      services/ordering/test/EShopOnAbp.OrderingService.Application.Tests/Orders/OrderApplication_Tests.cs
  92. 5
      services/payment/src/EShopOnAbp.PaymentService.Domain/EShopOnAbp.PaymentService.Domain.csproj
  93. 26
      services/payment/src/EShopOnAbp.PaymentService.Domain/EventHandlers/OrderCancelledEventHandler.cs
  94. 7
      shared/EShopOnAbp.Shared.Hosting.Gateways/EShopOnAbp.Shared.Hosting.Gateways.csproj
  95. 12
      shared/EShopOnAbp.Shared.Hosting.Gateways/EShopOnAbpSharedHostingGatewaysModule.cs
  96. 6
      shared/EShopOnAbp.Shared.Hosting.Gateways/GatewayHostBuilderExtensions.cs
  97. 20
      shared/EShopOnAbp.Shared.Hosting.Gateways/OcelotConfiguration.cs
  98. 52
      shared/EShopOnAbp.Shared.Hosting.Gateways/YarpSwaggerUIBuilderExtensions.cs

31
apps/angular/angular.json

@ -187,6 +187,37 @@
}
}
}
},
"ordering": {
"projectType": "library",
"root": "projects/ordering",
"sourceRoot": "projects/ordering/src",
"prefix": "lib",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:ng-packagr",
"options": {
"project": "projects/ordering/ng-package.json"
},
"configurations": {
"production": {
"tsConfig": "projects/ordering/tsconfig.lib.prod.json"
},
"development": {
"tsConfig": "projects/ordering/tsconfig.lib.json"
}
},
"defaultConfiguration": "production"
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "projects/ordering/src/test.ts",
"tsConfig": "projects/ordering/tsconfig.spec.json",
"karmaConfig": "projects/ordering/karma.conf.js"
}
}
}
}
},
"defaultProject": "EShopOnAbp",

3
apps/angular/package.json

@ -52,9 +52,10 @@
"karma-jasmine": "~4.0.0",
"karma-jasmine-html-reporter": "^1.5.0",
"ng-packagr": "^12.2.2",
"prettier": "^2.6.0",
"protractor": "~7.0.0",
"ts-node": "~8.3.0",
"tslint": "~6.1.0",
"typescript": "~4.3.5"
}
}
}

1
apps/angular/projects/catalog/tsconfig.lib.json

@ -1,4 +1,3 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "../../tsconfig.json",
"compilerOptions": {

1
apps/angular/projects/catalog/tsconfig.lib.prod.json

@ -1,4 +1,3 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "./tsconfig.lib.json",
"compilerOptions": {

24
apps/angular/projects/ordering/README.md

@ -0,0 +1,24 @@
# Ordering
This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 12.2.0.
## Code scaffolding
Run `ng generate component component-name --project ordering` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project ordering`.
> Note: Don't forget to add `--project ordering` or else it will be added to the default project in your `angular.json` file.
## Build
Run `ng build ordering` to build the project. The build artifacts will be stored in the `dist/` directory.
## Publishing
After building your library with `ng build ordering`, go to the dist folder `cd dist/ordering` and run `npm publish`.
## Running unit tests
Run `ng test ordering` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.

7
apps/angular/projects/ordering/config/ng-package.json

@ -0,0 +1,7 @@
{
"$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../dist/ordering/config",
"lib": {
"entryFile": "src/public-api.ts"
}
}

2
apps/angular/projects/ordering/config/src/enums/index.ts

@ -0,0 +1,2 @@
export * from './policy-names';
export * from './route-names';

6
apps/angular/projects/ordering/config/src/enums/policy-names.ts

@ -0,0 +1,6 @@
export const enum eOrderingPolicyNames {
ordering = 'OrderingService.Orders',
detail = 'OrderingService.Orders',
setAsShipped = 'OrderingService.SetAsShipped',
setAsCancelled = 'OrderingService.SetAsCancelled',
}

3
apps/angular/projects/ordering/config/src/enums/route-names.ts

@ -0,0 +1,3 @@
export const enum eOrderingRouteNames {
ordering = 'OrderingService::Menu:OrderManagement'
}

12
apps/angular/projects/ordering/config/src/ordering-config.module.ts

@ -0,0 +1,12 @@
import { ModuleWithProviders, NgModule } from '@angular/core';
import { ORDERING_ROUTE_PROVIDERS } from './providers/route.provider';
@NgModule()
export class OrderingConfigModule {
static forRoot(): ModuleWithProviders<OrderingConfigModule> {
return {
ngModule: OrderingConfigModule,
providers: [ORDERING_ROUTE_PROVIDERS],
};
}
}

1
apps/angular/projects/ordering/config/src/providers/index.ts

@ -0,0 +1 @@
export * from './route.provider';

23
apps/angular/projects/ordering/config/src/providers/route.provider.ts

@ -0,0 +1,23 @@
import { eLayoutType, RoutesService } from '@abp/ng.core';
import { APP_INITIALIZER } from '@angular/core';
import { eOrderingPolicyNames } from '../enums/policy-names';
import { eOrderingRouteNames } from '../enums/route-names';
export const ORDERING_ROUTE_PROVIDERS = [
{ provide: APP_INITIALIZER, useFactory: configureRoutes, deps: [RoutesService], multi: true },
];
export function configureRoutes(routesService: RoutesService) {
return () => {
routesService.add([
{
path: '/ordering',
name: eOrderingRouteNames.ordering,
layout: eLayoutType.application,
requiredPolicy: eOrderingPolicyNames.ordering,
parentName: null,
iconClass: 'bi bi-collection-fill',
},
]);
};
}

3
apps/angular/projects/ordering/config/src/public-api.ts

@ -0,0 +1,3 @@
export * from './enums';
export * from './providers';
export * from './ordering-config.module';

44
apps/angular/projects/ordering/karma.conf.js

@ -0,0 +1,44 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
jasmine: {
// you can add configuration options for Jasmine here
// the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
// for example, you can disable the random execution with `random: false`
// or set a specific seed with `seed: 4321`
},
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
jasmineHtmlReporter: {
suppressAll: true // removes the duplicated traces
},
coverageReporter: {
dir: require('path').join(__dirname, '../../coverage/ordering'),
subdir: '.',
reporters: [
{ type: 'html' },
{ type: 'text-summary' }
]
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
restartOnFileChange: true
});
};

7
apps/angular/projects/ordering/ng-package.json

@ -0,0 +1,7 @@
{
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../dist/ordering",
"lib": {
"entryFile": "src/public-api.ts"
}
}

11
apps/angular/projects/ordering/package.json

@ -0,0 +1,11 @@
{
"name": "@eshoponabp/ordering",
"version": "0.0.1",
"peerDependencies": {
"@angular/common": "^12.2.0",
"@angular/core": "^12.2.0"
},
"dependencies": {
"tslib": "^2.3.0"
}
}

3
apps/angular/projects/ordering/src/lib/index.ts

@ -0,0 +1,3 @@
export * from './order-view-model';
export * from './proxy';
export * from './to-order-view-model';

5
apps/angular/projects/ordering/src/lib/order-view-model.ts

@ -0,0 +1,5 @@
import { OrderDto } from './proxy/orders';
export interface OrderViewModel extends OrderDto {
orderTotal: number;
}

17
apps/angular/projects/ordering/src/lib/proxy/README.md

@ -0,0 +1,17 @@
# Proxy Generation Output
This directory includes the output of the latest proxy generation.
The files and folders in it will be overwritten when proxy generation is run again.
Therefore, please do not place your own content in this folder.
In addition, `generate-proxy.json` works like a lock file.
It includes information used by the proxy generator, so please do not delete or modify it.
Finally, the name of the files and folders should not be changed for two reasons:
- Proxy generator will keep creating them at those paths and you will have multiple copies of the same content.
- ABP Suite generates files which include imports from this folder.
> **Important Notice:** If you are building a module and are planning to publish to npm,
> some of the generated proxies are likely to be exported from public-api.ts file. In such a case,
> please make sure you export files directly and not from barrel exports. In other words,
> do not include index.ts exports in your public-api.ts exports.

2377
apps/angular/projects/ordering/src/lib/proxy/generate-proxy.json

File diff suppressed because it is too large

2
apps/angular/projects/ordering/src/lib/proxy/index.ts

@ -0,0 +1,2 @@
import * as Orders from './orders';
export { Orders };

3
apps/angular/projects/ordering/src/lib/proxy/orders/index.ts

@ -0,0 +1,3 @@
export * from './models';
export * from './order-status.enum';
export * from './order.service';

58
apps/angular/projects/ordering/src/lib/proxy/orders/models.ts

@ -0,0 +1,58 @@
import type { EntityDto } from '@abp/ng.core';
import type { OrderStatus } from './order-status.enum';
export interface BuyerDto extends EntityDto<string> {
name?: string;
email?: string;
}
export interface GetMyOrdersInput {
filter?: string;
}
export interface GetOrdersInput {
filter?: string;
}
export interface OrderAddressDto {
description?: string;
street: string;
city: string;
country: string;
zipCode: string;
}
export interface OrderCreateDto {
paymentMethod?: string;
address: OrderAddressDto;
products: OrderItemCreateDto[];
}
export interface OrderDto extends EntityDto<string> {
orderDate?: string;
orderNo: number;
orderStatus: OrderStatus;
paymentMethod?: string;
buyer: BuyerDto;
address: OrderAddressDto;
items: OrderItemDto[];
}
export interface OrderItemCreateDto {
productId?: string;
productCode?: string;
productName?: string;
pictureUrl?: string;
unitPrice: number;
discount: number;
units: number;
}
export interface OrderItemDto extends EntityDto<string> {
productId?: string;
productName?: string;
pictureUrl?: string;
unitPrice: number;
discount: number;
units: number;
}

10
apps/angular/projects/ordering/src/lib/proxy/orders/order-status.enum.ts

@ -0,0 +1,10 @@
import { mapEnumToOptions } from '@abp/ng.core';
export enum OrderStatus {
Placed = 0,
Paid = 1,
Shipped = 2,
Cancelled = 3,
}
export const orderStatusOptions = mapEnumToOptions(OrderStatus);

74
apps/angular/projects/ordering/src/lib/proxy/orders/order.service.ts

@ -0,0 +1,74 @@
import type { GetMyOrdersInput, GetOrdersInput, OrderCreateDto, OrderDto } from './models';
import { RestService } from '@abp/ng.core';
import type { PagedAndSortedResultRequestDto, PagedResultDto } from '@abp/ng.core';
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class OrderService {
apiName = 'Ordering';
create = (input: OrderCreateDto) =>
this.restService.request<any, OrderDto>({
method: 'POST',
url: '/api/ordering/order',
body: input,
},
{ apiName: this.apiName });
get = (id: string) =>
this.restService.request<any, OrderDto>({
method: 'GET',
url: `/api/ordering/order/${id}`,
},
{ apiName: this.apiName });
getByOrderNo = (orderNo: number) =>
this.restService.request<any, OrderDto>({
method: 'GET',
url: '/api/ordering/order/by-order-no',
params: { orderNo },
},
{ apiName: this.apiName });
getListPaged = (input: PagedAndSortedResultRequestDto) =>
this.restService.request<any, PagedResultDto<OrderDto>>({
method: 'GET',
url: '/api/ordering/order/paged',
params: { sorting: input.sorting, skipCount: input.skipCount, maxResultCount: input.maxResultCount },
},
{ apiName: this.apiName });
getMyOrders = (input: GetMyOrdersInput) =>
this.restService.request<any, OrderDto[]>({
method: 'GET',
url: '/api/ordering/order/my-orders',
params: { filter: input.filter },
},
{ apiName: this.apiName });
getOrders = (input: GetOrdersInput) =>
this.restService.request<any, OrderDto[]>({
method: 'GET',
url: '/api/ordering/order/orders',
params: { filter: input.filter },
},
{ apiName: this.apiName });
setAsCancelled = (id: string) =>
this.restService.request<any, void>({
method: 'POST',
url: `/api/ordering/order/${id}/set-as-cancelled`,
},
{ apiName: this.apiName });
setAsShipped = (id: string) =>
this.restService.request<any, void>({
method: 'POST',
url: `/api/ordering/order/${id}/set-as-shipped`,
},
{ apiName: this.apiName });
constructor(private restService: RestService) {}
}

8
apps/angular/projects/ordering/src/lib/to-order-view-model.ts

@ -0,0 +1,8 @@
import { OrderDto } from './proxy/orders';
import { OrderViewModel } from './order-view-model';
const mapItem = (x: OrderDto): OrderViewModel => {
const orderTotal = x.items?.reduce((acc, curr) => ( acc + (curr.unitPrice * curr.units)), 0) || 0;
return {...x, orderTotal};
};
export const toOrderViewModel = (orders: OrderDto[]) => orders.map(mapItem);

13
apps/angular/projects/ordering/src/ordering-routing.module.ts

@ -0,0 +1,13 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
const routes: Routes = [
{ path: '', redirectTo: 'orders', pathMatch: 'full' },
{ path: 'orders', loadChildren: () => import('./pages/orders/orders.module').then(m => m.OrdersModule) },
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class OrderingRoutingModule {}

9
apps/angular/projects/ordering/src/ordering.module.ts

@ -0,0 +1,9 @@
import { NgModule } from '@angular/core';
import { OrderingRoutingModule } from './ordering-routing.module';
@NgModule({
declarations: [],
imports: [OrderingRoutingModule],
exports: [],
})
export class OrderingModule {}

1
apps/angular/projects/ordering/src/pages/index.ts

@ -0,0 +1 @@
export * from './orders';

3
apps/angular/projects/ordering/src/pages/orders/index.ts

@ -0,0 +1,3 @@
export * from './orders-routing.module';
export * from './orders.component';
export * from './orders.module';

20
apps/angular/projects/ordering/src/pages/orders/order-detail/order-detail-item/order-detail-item.component.ts

@ -0,0 +1,20 @@
import { Component, Input } from '@angular/core';
@Component({
selector: 'lib-order-detail-item',
template: `
<div class="row">
<div class="col-3">
{{ label | abpLocalization }}
</div>
<b class="col-9">
<ng-content></ng-content>
</b>
</div>
`,
styles: [],
})
export class OrderDetailItemComponent {
@Input()
label = '';
}

78
apps/angular/projects/ordering/src/pages/orders/order-detail/order-detail.component.html

@ -0,0 +1,78 @@
<abp-modal [visible]="visible" (visibleChange)="visibleChange.emit($event)" [options]="modalOption">
<ng-template #abpHeader>
<h3>{{ 'OrderingService::ModalTitle' }}</h3>
</ng-template>
<ng-template #abpBody>
<div *ngIf="order">
<div class="container">
<lib-order-detail-item label="OrderingService::DisplayName:OrderNo"
>#{{ order.orderNo }}</lib-order-detail-item
>
<lib-order-detail-item label="OrderingService::DisplayName:OrderStatus">{{
order.orderStatus
}}</lib-order-detail-item>
<lib-order-detail-item label="OrderingService::DisplayName:BuyerName">{{
order.buyer.name
}}</lib-order-detail-item>
<lib-order-detail-item label="OrderingService::DisplayName:BuyerEmail">{{
order.buyer.email
}}</lib-order-detail-item>
<lib-order-detail-item label="OrderingService::DisplayName:Address">
<span *ngIf="order.address as address">
{{ address.description }} <br />
{{ address.street }} <br />
{{ address.zipCode }} <br />
{{ address.city }} / {{ address.country }} <br />
</span>
</lib-order-detail-item>
<lib-order-detail-item label="OrderingService::DisplayName:PaymentMethod">{{
order.paymentMethod
}}</lib-order-detail-item>
<lib-order-detail-item label="OrderingService::DisplayName:Total">{{
order.orderTotal | currency
}}</lib-order-detail-item>
</div>
<br /><br /><br />
<ngx-datatable [rows]="order.items" default>
<!-- TODO: localize column headers -->
<ngx-datatable-column
[name]="'OrderingService::DisplayName:ProductId'"
prop="productId"
></ngx-datatable-column>
<ngx-datatable-column [name]="'OrderingService::DisplayName:PictureUrl'" prop="pictureUrl">
<ng-template let-value="value" ngx-datatable-cell-template>
<img [src]="mediaServerUrl + '/product-images/' + value" width="80" alt="" />
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column
[name]="'OrderingService::DisplayName:ProductName'"
prop="productName"
></ngx-datatable-column>
<ngx-datatable-column [name]="'OrderingService::DisplayName:UnitPrice'" prop="unitPrice">
<ng-template let-value="value" ngx-datatable-cell-template>
{{ value | currency }}
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column
[name]="'OrderingService::DisplayName:Units'"
prop="units"
></ngx-datatable-column>
<ngx-datatable-column [name]="'OrderingService::DisplayName:Discount'" prop="discount">
<ng-template let-value="value" ngx-datatable-cell-template> {{ value }} % </ng-template>
</ngx-datatable-column>
<ngx-datatable-column [name]="'OrderingService::DisplayName:TotalPrice'">
<ng-template let-row="row" ngx-datatable-cell-template>
{{ row.units * row.unitPrice | currency }}
</ng-template>
</ngx-datatable-column>
</ngx-datatable>
</div>
</ng-template>
<ng-template #abpFooter>
<button type="button" class="btn btn-secondary" abpClose>{{ 'AbpUi::Close' }}</button>
</ng-template>
</abp-modal>

17
apps/angular/projects/ordering/src/pages/orders/order-detail/order-detail.component.ts

@ -0,0 +1,17 @@
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { OrderViewModel } from '../../../lib/order-view-model';
import { environment } from '../../../../../../src/environments/environment';
@Component({
selector: 'lib-order-detail',
templateUrl: './order-detail.component.html',
})
export class OrderDetailComponent {
modalOption = { size: 'xl' };
@Input()
visible: boolean;
@Input()
order: OrderViewModel | undefined;
mediaServerUrl = environment.mediaServerUrl;
@Output() readonly visibleChange = new EventEmitter<boolean>();
}

11
apps/angular/projects/ordering/src/pages/orders/orders-routing.module.ts

@ -0,0 +1,11 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { OrdersComponent } from './orders.component';
const routes: Routes = [{ path: '', component: OrdersComponent }];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class OrdersRoutingModule { }

87
apps/angular/projects/ordering/src/pages/orders/orders.component.html

@ -0,0 +1,87 @@
<div class="card">
<div class="card-header">
<div class="row">
<div class="col col-md-6">
<h5 class="card-title">{{ 'AbpOrdering::Orders' | abpLocalization }}</h5>
</div>
</div>
</div>
<div class="card-body">
<ngx-datatable [rows]="items" [count]="count" [list]="list" default>
<!-- TODO: localize column headers -->
<ngx-datatable-column name="" [sortable]="false" prop="id">
<ng-template let-row="row" ngx-datatable-cell-template>
<div ngbDropdown container="body" class="d-inline-block">
<button
class="btn btn-primary btn-sm dropdown-toggle"
data-toggle="dropdown"
aria-haspopup="true"
ngbDropdownToggle
>
<i class="mr-1 fa fa-cog"></i>{{ 'AbpUi::Actions' | abpLocalization }}
</button>
<div ngbDropdownMenu>
<button
class="dropdown-item"
(click)="openModal(row)"
*abpPermission="permissions.detail"
>
{{ 'AbpUi::Detail' | abpLocalization }}
</button>
<button
class="dropdown-item"
(click)="setAsShipped(row)"
*abpPermission="permissions.setAsShipped"
>
{{ 'OrderingService::Permission:Orders.SetAsShipped' | abpLocalization }}
</button>
<!-- Todo: Add permissions -->
<button
class="dropdown-item"
*abpPermission="permissions.setAsCancelled"
(click)="setAsCancelled(row)"
>
{{ 'OrderingService::Permission:Orders.SetAsCancelled' | abpLocalization }}
</button>
</div>
</div>
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column
[name]="'OrderingService::DisplayName:OrderNo' | abpLocalization"
prop="orderNo"
></ngx-datatable-column>
<ngx-datatable-column
[name]="'OrderingService::DisplayName:OrderDate' | abpLocalization"
prop="orderDate"
>
<ng-template let-value="value" ngx-datatable-cell-template>
<span>{{ value | date }}</span>
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column
[name]="'OrderingService::DisplayName:OrderTotal' | abpLocalization"
prop="orderTotal"
>
<ng-template let-value="value" ngx-datatable-cell-template>
<span>{{ value | currency }}</span>
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column
[name]="'OrderingService::DisplayName:OrderStatus' | abpLocalization"
prop="orderStatus"
>
<ng-template let-value="value" ngx-datatable-cell-template>
{{ 'OrderingService::Enum:OrderStatus:' + value | abpLocalization }}
</ng-template>
</ngx-datatable-column>
</ngx-datatable>
</div>
</div>
<lib-order-detail
[visible]="isModalVisible"
(visibleChange)="closeModal($event)"
[order]="selectedOrder"
>
</lib-order-detail>

25
apps/angular/projects/ordering/src/pages/orders/orders.component.spec.ts

@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { OrdersComponent } from './orders.component';
describe('OrdersComponent', () => {
let component: OrdersComponent;
let fixture: ComponentFixture<OrdersComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ OrdersComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(OrdersComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

83
apps/angular/projects/ordering/src/pages/orders/orders.component.ts

@ -0,0 +1,83 @@
import { Component, OnInit } from '@angular/core';
import { OrderService } from '../../lib/proxy/orders';
import { OrderViewModel, toOrderViewModel } from '../../lib';
import { Confirmation, ConfirmationService } from '@abp/ng.theme.shared';
import { ListService } from '@abp/ng.core';
import { eOrderingPolicyNames } from '@eshoponabp/ordering/config';
@Component({
selector: 'lib-orders',
templateUrl: './orders.component.html',
providers: [ListService],
})
export class OrdersComponent implements OnInit {
constructor(
private service: OrderService,
public list: ListService,
private confirmationService: ConfirmationService
) {}
selectedOrder: OrderViewModel | undefined;
isModalVisible = false;
items: OrderViewModel[];
count = 0;
permissions = {
detail: eOrderingPolicyNames.ordering,
setAsShipped: eOrderingPolicyNames.setAsShipped,
setAsCancelled: eOrderingPolicyNames.setAsCancelled,
};
ngOnInit(): void {
const ordersStreamCreator = query => this.service.getListPaged(query);
this.list.hookToQuery(ordersStreamCreator).subscribe(response => {
this.items = toOrderViewModel(response.items);
this.count = response.totalCount;
});
}
openModal(order: OrderViewModel) {
if (!order) {
return;
}
this.selectedOrder = order;
this.isModalVisible = true;
}
closeModal(isVisible: boolean) {
if (isVisible) {
return;
}
this.selectedOrder = null;
this.isModalVisible = false;
}
setAsShipped(row: OrderViewModel) {
this.confirmationService
.warn('AbpOrdering::WillSetAsShipped', { key: '::AreYouSure', defaultValue: 'Are you sure?' })
.subscribe(status => {
if (status !== Confirmation.Status.confirm) {
return;
}
this.service.setAsShipped(row.id).subscribe(() => {
this.list.get();
});
});
}
setAsCancelled(row: OrderViewModel) {
this.confirmationService
.warn('AbpOrdering::WillSetAsCancelled', {
key: '::AreYouSure',
defaultValue: 'Are you sure?',
})
.subscribe(status => {
if (status !== Confirmation.Status.confirm) {
return;
}
this.service.setAsCancelled(row.id).subscribe(() => {
this.list.get();
});
});
}
}

16
apps/angular/projects/ordering/src/pages/orders/orders.module.ts

@ -0,0 +1,16 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { OrdersRoutingModule } from './orders-routing.module';
import { OrdersComponent } from './orders.component';
import { ThemeSharedModule } from '@abp/ng.theme.shared';
import { CoreModule } from '@abp/ng.core';
import { OrderDetailComponent } from './order-detail/order-detail.component';
import { OrderDetailItemComponent } from './order-detail/order-detail-item/order-detail-item.component';
import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap';
@NgModule({
declarations: [OrdersComponent, OrderDetailComponent, OrderDetailItemComponent],
imports: [CommonModule, NgbDropdownModule, OrdersRoutingModule, ThemeSharedModule, CoreModule],
})
export class OrdersModule {}

7
apps/angular/projects/ordering/src/public-api.ts

@ -0,0 +1,7 @@
/*
* Public API Surface of ordering
*/
export * from './pages/index';
export * from './ordering.module';
export * from './lib/proxy/index';

28
apps/angular/projects/ordering/src/test.ts

@ -0,0 +1,28 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js';
import 'zone.js/testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
declare const require: {
context(path: string, deep?: boolean, filter?: RegExp): {
keys(): string[];
<T>(id: string): T;
};
};
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting(),
{ teardown: { destroyAfterEach: true }},
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);

19
apps/angular/projects/ordering/tsconfig.lib.json

@ -0,0 +1,19 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../out-tsc/lib",
"target": "es2015",
"declaration": true,
"declarationMap": true,
"inlineSources": true,
"types": [],
"lib": [
"dom",
"es2018"
]
},
"exclude": [
"src/test.ts",
"**/*.spec.ts"
]
}

9
apps/angular/projects/ordering/tsconfig.lib.prod.json

@ -0,0 +1,9 @@
{
"extends": "./tsconfig.lib.json",
"compilerOptions": {
"declarationMap": false
},
"angularCompilerOptions": {
"compilationMode": "partial"
}
}

17
apps/angular/projects/ordering/tsconfig.spec.json

@ -0,0 +1,17 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../out-tsc/spec",
"types": [
"jasmine"
]
},
"files": [
"src/test.ts"
],
"include": [
"**/*.spec.ts",
"**/*.d.ts"
]
}

4
apps/angular/src/app/app-routing.module.ts

@ -29,6 +29,10 @@ const routes: Routes = [
path: 'catalog',
loadChildren: () => import('@eshoponabp/catalog').then(m => m.CatalogModule),
},
{
path: 'ordering',
loadChildren: () => import('@eshoponabp/ordering').then(m => m.OrderingModule),
},
];
@NgModule({

4
apps/angular/src/app/app.module.ts

@ -16,6 +16,7 @@ import { environment } from '../environments/environment';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { APP_ROUTE_PROVIDER } from './route.provider';
import { OrderingConfigModule } from '@eshoponabp/ordering/config';
@NgModule({
imports: [
@ -34,7 +35,8 @@ import { APP_ROUTE_PROVIDER } from './route.provider';
ThemeLeptonXModule.forRoot(),
SideMenuLayoutModule.forRoot(),
AccountLayoutModule.forRoot(),
CatalogConfigModule.forRoot()
CatalogConfigModule.forRoot(),
OrderingConfigModule.forRoot()
],
declarations: [AppComponent],
providers: [APP_ROUTE_PROVIDER],

10
apps/angular/src/environments/environment.prod.ts

@ -1,4 +1,4 @@
import { Environment } from '@abp/ng.core';
import { MyEnvironment } from './my-environment';
const baseUrl = 'http://localhost:4200';
@ -22,8 +22,8 @@ export const environment = {
rootNamespace: 'EShopOnAbp',
},
},
remoteEnv:{
url: "/getEnvConfig",
mergeStrategy:'deepmerge'
remoteEnv: {
url: '/getEnvConfig',
mergeStrategy: 'deepmerge'
}
} as Environment;
} as MyEnvironment;

13
apps/angular/src/environments/environment.ts

@ -1,4 +1,4 @@
import { Environment } from '@abp/ng.core';
import { MyEnvironment } from './my-environment';
const baseUrl = 'http://localhost:4200';
@ -13,7 +13,7 @@ export const environment = {
redirectUri: baseUrl,
clientId: 'Web',
//responseType: 'code',
scope: 'offline_access openid profile email phone AccountService IdentityService AdministrationService CatalogService',
scope: 'offline_access openid profile email phone AccountService IdentityService AdministrationService CatalogService OrderingService',
//requireHttps: true,
},
apis: {
@ -25,5 +25,12 @@ export const environment = {
url: 'https://localhost:44354',
rootNamespace: 'EShopOnAbp.CatalogService',
},
Ordering: {
url: "https://localhost:44356",
rootNamespace: 'EShopOnAbp.OrderingService',
}
},
} as Environment;
mediaServerUrl:'https://localhost:44335'
} as MyEnvironment;

5
apps/angular/src/environments/my-environment.ts

@ -0,0 +1,5 @@
import { Environment } from '@abp/ng.core';
export interface MyEnvironment extends Environment {
mediaServerUrl?: string;
}

8
apps/angular/tsconfig.json

@ -22,6 +22,14 @@
"node_modules/@eshoponabp/catalog",
"projects/catalog/src/public-api.ts",
],
"@eshoponabp/ordering/config": [
"node_modules/@eshoponabp/ordering/config",
"projects/ordering/config/src/public-api.ts",
],
"@eshoponabp/ordering": [
"node_modules/@eshoponabp/ordering",
"projects/ordering/src/public-api.ts",
],
}
},
"angularCompilerOptions": {

2
apps/public-web/src/EShopOnAbp.PublicWeb/Components/UserOrders/Default.cshtml

@ -13,7 +13,7 @@
</abp-row>
@foreach (var order in Model.UserOrders)
{
var orderTotalString = order.Items.Sum(q => q.UnitPrice).ToString("C", new CultureInfo("en-US"));
var orderTotalString = order.Items.Sum(q => q.UnitPrice * q.Units).ToString("C", new CultureInfo("en-US"));
string addressString = $"{order.Address.Street} {order.Address.ZipCode} \n {order.Address.City}/{order.Address.Country}";
<div class="card">
<div class="card-header">

28
apps/public-web/src/EShopOnAbp.PublicWeb/EShopOnAbpPublicWebModule.cs

@ -1,25 +1,32 @@
using EShopOnAbp.BasketService;
using EShopOnAbp.CatalogService;
using EShopOnAbp.Localization;
using EShopOnAbp.OrderingService;
using EShopOnAbp.PaymentService;
using EShopOnAbp.PaymentService.PaymentMethods;
using EShopOnAbp.PublicWeb.AnonymousUser;
using EShopOnAbp.PublicWeb.Components.Toolbar.Cart;
using EShopOnAbp.PublicWeb.Menus;
using EShopOnAbp.PublicWeb.PaymentMethods;
using EShopOnAbp.Shared.Hosting.AspNetCore;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using StackExchange.Redis;
using System;
using System.Net.Http.Headers;
using EShopOnAbp.OrderingService;
using Volo.Abp;
using Volo.Abp.Account;
using Volo.Abp.AspNetCore.Authentication.OpenIdConnect;
using Volo.Abp.AspNetCore.Mvc.Client;
using Volo.Abp.AspNetCore.Mvc.Localization;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Toolbars;
using Volo.Abp.AspNetCore.SignalR;
@ -27,20 +34,14 @@ using Volo.Abp.AutoMapper;
using Volo.Abp.Caching;
using Volo.Abp.Caching.StackExchangeRedis;
using Volo.Abp.EventBus.RabbitMq;
using Volo.Abp.Http.Client;
using Volo.Abp.Http.Client.IdentityModel.Web;
using Volo.Abp.Modularity;
using Volo.Abp.MultiTenancy;
using Volo.Abp.UI.Navigation;
using Volo.Abp.UI.Navigation.Urls;
using Yarp.ReverseProxy.Transforms;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Bundling;
using EShopOnAbp.PublicWeb.Components.Toolbar.Cart;
using EShopOnAbp.PublicWeb.PaymentMethods;
using EShopOnAbp.PaymentService.PaymentMethods;
using EShopOnAbp.PublicWeb.AnonymousUser;
using Microsoft.Extensions.Configuration;
using Volo.Abp.VirtualFileSystem;
using Yarp.ReverseProxy.Transforms;
namespace EShopOnAbp.PublicWeb;
@ -77,7 +78,6 @@ public class EShopOnAbpPublicWebModule : AbpModule
public override void ConfigureServices(ServiceConfigurationContext context)
{
Microsoft.IdentityModel.Logging.IdentityModelEventSource.ShowPII = true;
var hostingEnvironment = context.Services.GetHostingEnvironment();
var configuration = context.Services.GetConfiguration();
ConfigureBasketHttpClient(context);
@ -93,6 +93,12 @@ public class EShopOnAbpPublicWebModule : AbpModule
);
});
context.Services.Configure<AbpRemoteServiceOptions>(options =>
{
options.RemoteServices.Default =
new RemoteServiceConfiguration(configuration["RemoteServices:Default:BaseUrl"]);
});
Configure<AbpMultiTenancyOptions>(options => { options.IsEnabled = true; });
Configure<AbpDistributedCacheOptions>(options => { options.KeyPrefix = "EShopOnAbp:"; });
@ -167,7 +173,7 @@ public class EShopOnAbpPublicWebModule : AbpModule
context.Services.AddStaticHttpClientProxies(
typeof(BasketServiceContractsModule).Assembly, remoteServiceConfigurationName: BasketServiceConstants.RemoteServiceName
);
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<EShopOnAbpPublicWebModule>();

19
apps/public-web/src/EShopOnAbp.PublicWeb/Pages/Index.cshtml

@ -2,8 +2,11 @@
@using EShopOnAbp.Localization
@using Microsoft.Extensions.Localization
@using System.Globalization
@using Microsoft.Extensions.Options
@using Volo.Abp.Http.Client
@model EShopOnAbp.PublicWeb.Pages.IndexModel
@inject IStringLocalizer<EShopOnAbpResource> L
@inject IOptions<AbpRemoteServiceOptions> options
@{
const int productsColumnSize = 4; // TODO: update abp-column helper to plain html for proper row and columns css
@ -11,12 +14,12 @@
}
@section styles{
<abp-style src="/Pages/index.css"/>
<abp-style src="/Pages/index.css" />
}
@section scripts {
<abp-script src="/client-proxies/basket-proxy.js"/>
<abp-script src="/Pages/index.js"/>
<abp-script src="/client-proxies/basket-proxy.js" />
<abp-script src="/Pages/index.js" />
}
@if (Model.HasRemoteServiceError)
@ -28,7 +31,7 @@
<div class="col-lg-12">
<h3 class="pt-5 pb-4 text-center">@pageHeader</h3>
<div class="product-list">
@for (int i = 0; i < Math.Ceiling(Model.Products.Count / (double) productsColumnSize); i++)
@for (int i = 0; i < Math.Ceiling(Model.Products.Count / (double)productsColumnSize); i++)
{
<abp-row>
@for (int j = 0; j < productsColumnSize; j++)
@ -38,7 +41,7 @@
{
var product = Model.Products[(i * productsColumnSize) + j];
var productPrice = product.Price.ToString("C", new CultureInfo("en-US"));
string productImage = !product.ImageName.IsNullOrEmpty() ? $"/product-images/{product.ImageName}" : "/product-images/@product.ImageName";
string productImage = !product.ImageName.IsNullOrEmpty() ? $"{options.Value.RemoteServices.Default.BaseUrl}product-images/{product.ImageName}" : $"{options.Value.RemoteServices.Default.BaseUrl}product-images/@product.ImageName";
string buyText = L["Index:AddToBasket"];
<div class="product-list-item" data-product-id="@product.Id.ToString()">
<div class="col">
@ -47,10 +50,10 @@
<em class="d-block text-muted">@product.Code - @L["StockCount"]: @product.StockCount</em>
</div>
</div>
<img src="@productImage"/>
<img src="@productImage" />
<div class="row mt-5 ">
@if(product.StockCount > 0)
{
@if (product.StockCount > 0)
{
<abp-button class="add-basket-button" button-type="Warning" text="@buyText" data-product-id="@product.Id.ToString()"></abp-button>
}
else

43
gateways/web-public/src/EShopOnAbp.WebPublicGateway/EShopOnAbpWebPublicGatewayModule.cs

@ -1,12 +1,10 @@
using EShopOnAbp.Shared.Hosting.Gateways;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Ocelot.Middleware;
using System.Collections.Generic;
using System.Linq;
using EShopOnAbp.Shared.Hosting.AspNetCore;
using Microsoft.AspNetCore.Http;
using Volo.Abp;
using Volo.Abp.Modularity;
using Microsoft.AspNetCore.Rewrite;
@ -38,6 +36,9 @@ public class EShopOnAbpWebPublicGatewayModule : AbpModule
},
apiTitle: "WebPublic Gateway"
);
// context.Services.AddReverseProxy()
// .LoadFromConfig(configuration.GetSection("ReverseProxy"));
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
@ -52,35 +53,17 @@ public class EShopOnAbpWebPublicGatewayModule : AbpModule
app.UseCorrelationId();
app.UseAbpSerilogEnrichers();
app.UseSwagger();
app.UseSwaggerUI(options =>
{
var configuration = context.ServiceProvider.GetRequiredService<IConfiguration>();
var routes = configuration.GetSection("Routes").Get<List<OcelotConfiguration>>();
var routedServices = routes
.GroupBy(t => t.ServiceKey)
.Select(r => r.First())
.Distinct();
foreach (var config in routedServices)
{
var url =
$"{config.DownstreamScheme}://{config.DownstreamHostAndPorts.FirstOrDefault()?.Host}:{config.DownstreamHostAndPorts.FirstOrDefault()?.Port}";
if (!env.IsDevelopment())
{
url = $"https://{config.DownstreamHostAndPorts.FirstOrDefault()?.Host}";
}
options.SwaggerEndpoint($"{url}/swagger/v1/swagger.json", $"{config.ServiceKey} API");
options.OAuthClientId(configuration["AuthServer:SwaggerClientId"]);
options.OAuthClientSecret(configuration["AuthServer:SwaggerClientSecret"]);
}
});
app.ConfigureSwaggerUIWithYarp(context);
app.UseRewriter(new RewriteOptions()
// Regex for "", "/" and "" (whitespace)
.AddRedirect("^(|\\|\\s+)$", "/swagger"));
app.UseOcelot().Wait();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("", ctx => ctx.Response.WriteAsync("YAG"));
endpoints.MapReverseProxy();
});
}
}

1
gateways/web-public/src/EShopOnAbp.WebPublicGateway/appsettings.json

@ -17,7 +17,6 @@
}
},
"AllowedHosts": "*",
"Redis": {
"Configuration": "localhost:6379"
},

100
gateways/web-public/src/EShopOnAbp.WebPublicGateway/ocelot.json

@ -1,100 +0,0 @@
{
"GlobalConfiguration": {
"BaseUrl": "https://localhost:44373"
},
"Routes": [
{
"ServiceKey": "Account Service",
"DownstreamPathTemplate": "/api/account/{everything}",
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 44330
}
],
"UpstreamPathTemplate": "/api/account/{everything}",
"UpstreamHttpMethod": [ "Put", "Delete", "Get", "Post" ]
},
{
"ServiceKey": "Administration Service",
"DownstreamPathTemplate": "/api/abp/{everything}",
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 44353
}
],
"UpstreamPathTemplate": "/api/abp/{everything}",
"UpstreamHttpMethod": [ "Put", "Delete", "Get", "Post" ],
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 3,
"DurationOfBreak": 3000,
"TimeoutValue": 2500
}
},
{
"ServiceKey": "Catalog Service",
"DownstreamPathTemplate": "/api/catalog/{everything}",
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 44354
}
],
"UpstreamPathTemplate": "/api/catalog/{everything}",
"UpstreamHttpMethod": [ "Put", "Delete", "Get", "Post" ],
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 3,
"DurationOfBreak": 3000,
"TimeoutValue": 2500
}
},
{
"ServiceKey": "Basket Service",
"DownstreamPathTemplate": "/api/basket/{everything}",
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 44355
}
],
"UpstreamPathTemplate": "/api/basket/{everything}",
"UpstreamHttpMethod": [ "Put", "Delete", "Get", "Post" ],
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 3,
"DurationOfBreak": 3000,
"TimeoutValue": 2500
}
},
{
"ServiceKey": "Ordering Service",
"DownstreamPathTemplate": "/api/ordering/{everything}",
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 44356
}
],
"UpstreamPathTemplate": "/api/ordering/{everything}",
"UpstreamHttpMethod": [ "Put", "Delete", "Get", "Post" ]
},
{
"ServiceKey": "Payment Service",
"DownstreamPathTemplate": "/api/payment/{everything}",
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 44357
}
],
"UpstreamPathTemplate": "/api/payment/{everything}",
"UpstreamHttpMethod": [ "Put", "Delete", "Get", "Post" ]
}
]
}

100
gateways/web-public/src/EShopOnAbp.WebPublicGateway/yarp.json

@ -0,0 +1,100 @@
{
"ReverseProxy": {
"Routes": {
"Account Service": {
"ClusterId": "account-cluster",
"Match": {
"Path": "/api/account/{**everything}"
}
},
"Administration Service": {
"ClusterId": "administration-cluster",
"Match": {
"Path": "/api/abp/{**everything}"
}
},
"Catalog Service": {
"ClusterId": "catalog-cluster",
"Match": {
"Path": "/api/catalog/{**everything}"
}
},
"Basket Service": {
"ClusterId": "basket-cluster",
"Match": {
"Path": "/api/basket/{**everything}"
}
},
"Ordering Service": {
"ClusterId": "ordering-cluster",
"Match": {
"Path": "/api/ordering/{**everything}"
}
},
"Payment Service": {
"ClusterId": "payment-cluster",
"Match": {
"Path": "/api/payment/{**everything}"
}
},
"product-picture-route": {
"ClusterId": "product-picture-cluster",
"Match": {
"Path": "/product-images/{**everything}",
"Methods" : [ "GET" ]
}
}
},
"Clusters": {
"account-cluster": {
"Destinations": {
"destination1": {
"Address": "https://localhost:44330"
}
}
},
"administration-cluster": {
"Destinations": {
"destination1": {
"Address": "https://localhost:44353"
}
}
},
"catalog-cluster": {
"Destinations": {
"destination1": {
"Address": "https://localhost:44354"
}
}
},
"product-picture-cluster": {
"Destinations": {
"destination1": {
"Address": "https://localhost:44354"
}
}
},
"basket-cluster": {
"Destinations": {
"destination1": {
"Address": "https://localhost:44355"
}
}
},
"ordering-cluster": {
"Destinations": {
"destination1": {
"Address": "https://localhost:44356"
}
}
},
"payment-cluster": {
"Destinations": {
"destination1": {
"Address": "https://localhost:44357"
}
}
}
}
}
}

29
gateways/web/src/EShopOnAbp.WebGateway/EShopOnAbpWebGatewayModule.cs

@ -4,7 +4,6 @@ using Microsoft.AspNetCore.Cors;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Ocelot.Middleware;
using System;
using System.Collections.Generic;
using System.Linq;
@ -71,39 +70,15 @@ public class EShopOnAbpWebGatewayModule : AbpModule
{
app.UseDeveloperExceptionPage();
}
app.UseCorrelationId();
app.UseAbpSerilogEnrichers();
app.UseCors();
app.UseSwagger();
app.UseSwaggerUI(options =>
{
var configuration = context.ServiceProvider.GetRequiredService<IConfiguration>();
var routes = configuration.GetSection("Routes").Get<List<OcelotConfiguration>>();
var routedServices = routes
.GroupBy(t => t.ServiceKey)
.Select(r => r.First())
.Distinct();
foreach (var config in routedServices)
{
var url =
$"{config.DownstreamScheme}://{config.DownstreamHostAndPorts.FirstOrDefault()?.Host}:{config.DownstreamHostAndPorts.FirstOrDefault()?.Port}";
if (!env.IsDevelopment())
{
url = $"https://{config.DownstreamHostAndPorts.FirstOrDefault()?.Host}";
}
options.SwaggerEndpoint($"{url}/swagger/v1/swagger.json", $"{config.ServiceKey} API");
options.OAuthClientId(configuration["AuthServer:SwaggerClientId"]);
options.OAuthClientSecret(configuration["AuthServer:SwaggerClientSecret"]);
}
});
app.ConfigureSwaggerUIWithYarp(context);
app.UseRewriter(new RewriteOptions()
// Regex for "", "/" and "" (whitespace)
.AddRedirect("^(|\\|\\s+)$", "/swagger"));
app.UseOcelot().Wait();
// app.UseOcelot().Wait();
}
}

90
gateways/web/src/EShopOnAbp.WebGateway/ocelot.json

@ -1,90 +0,0 @@
{
"GlobalConfiguration": {
"BaseUrl": "https://localhost:44372"
},
"Routes": [
{
"ServiceKey": "Identity Service",
"DownstreamPathTemplate": "/api/identity/{everything}",
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 44351
}
],
"UpstreamPathTemplate": "/api/identity/{everything}",
"UpstreamHttpMethod": [ "Put", "Delete", "Get", "Post" ]
},
{
"ServiceKey": "Account Service",
"DownstreamPathTemplate": "/api/account/{everything}",
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 44330
}
],
"UpstreamPathTemplate": "/api/account/{everything}",
"UpstreamHttpMethod": [ "Put", "Delete", "Get", "Post" ]
},
{
"ServiceKey": "Administration Service",
"DownstreamPathTemplate": "/api/abp/{everything}",
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 44353
}
],
"UpstreamPathTemplate": "/api/abp/{everything}",
"UpstreamHttpMethod": [ "Put", "Delete", "Get", "Post" ],
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 3,
"DurationOfBreak": 5000,
"TimeoutValue": 2500
}
},
{
"ServiceKey": "Administration Service",
"DownstreamPathTemplate": "/api/feature-management/{everything}",
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 44353
}
],
"UpstreamPathTemplate": "/api/feature-management/{everything}",
"UpstreamHttpMethod": [ "Put", "Delete", "Get", "Post" ]
},
{
"ServiceKey": "Administration Service",
"DownstreamPathTemplate": "/api/permission-management/{everything}",
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 44353
}
],
"UpstreamPathTemplate": "/api/permission-management/{everything}",
"UpstreamHttpMethod": [ "Put", "Delete", "Get", "Post" ]
},
{
"ServiceKey": "Administration Service",
"DownstreamPathTemplate": "/api/setting-management/{everything}",
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 44353
}
],
"UpstreamPathTemplate": "/api/setting-management/{everything}",
"UpstreamHttpMethod": [ "Put", "Delete", "Get", "Post" ]
}
]
}

86
gateways/web/src/EShopOnAbp.WebGateway/yarp.json

@ -0,0 +1,86 @@
{
"ReverseProxy": {
"Routes": {
"Account Service": {
"ClusterId": "account-cluster",
"Match": {
"Path": "/api/account/{**everything}"
}
},
"Identity Service": {
"ClusterId": "identity-cluster",
"Match": {
"Path": "/api/identity/{**everything}"
}
},
"Administration Service": {
"ClusterId": "administration-cluster",
"Match": {
"Path": "/api/abp/{**everything}"
}
},
"feature-management-route": {
"ClusterId": "feature-management-cluster",
"Match": {
"Path": "/api/feature-management/{**everything}"
}
},
"permission-management-route": {
"ClusterId": "permission-management-cluster",
"Match": {
"Path": "/api/permission-management/{**everything}"
}
},
"setting-management-route": {
"ClusterId": "setting-management-cluster",
"Match": {
"Path": "/api/setting-management/{**everything}"
}
}
},
"Clusters": {
"account-cluster": {
"Destinations": {
"destination1": {
"Address": "https://localhost:44330"
}
}
},
"identity-cluster": {
"Destinations": {
"destination1": {
"Address": "https://localhost:44351"
}
}
},
"administration-cluster": {
"Destinations": {
"destination1": {
"Address": "https://localhost:44353"
}
}
},
"feature-management-cluster": {
"Destinations": {
"destination1": {
"Address": "https://localhost:44353"
}
}
},
"permission-management-cluster": {
"Destinations": {
"destination1": {
"Address": "https://localhost:44353"
}
}
},
"setting-management-cluster": {
"Destinations": {
"destination1": {
"Address": "https://localhost:44353"
}
}
}
}
}
}

4
services/administration/src/EShopOnAbp.AdministrationService.Application.Contracts/AdministrationServiceApplicationContractsModule.cs

@ -2,6 +2,7 @@
using Volo.Abp.PermissionManagement;
using Volo.Abp.SettingManagement;
using EShopOnAbp.CatalogService;
using EShopOnAbp.OrderingService;
namespace EShopOnAbp.AdministrationService
{
@ -9,7 +10,8 @@ namespace EShopOnAbp.AdministrationService
typeof(CatalogServiceApplicationContractsModule),
typeof(AdministrationServiceDomainSharedModule),
typeof(AbpPermissionManagementApplicationContractsModule),
typeof(AbpSettingManagementApplicationContractsModule)
typeof(AbpSettingManagementApplicationContractsModule),
typeof(OrderingServiceApplicationContractsModule)
)]
public class AdministrationServiceApplicationContractsModule : AbpModule
{

2
services/administration/src/EShopOnAbp.AdministrationService.Application.Contracts/EShopOnAbp.AdministrationService.Application.Contracts.csproj

@ -8,6 +8,8 @@
<ItemGroup>
<ProjectReference Include="..\EShopOnAbp.AdministrationService.Domain.Shared\EShopOnAbp.AdministrationService.Domain.Shared.csproj" />
<ProjectReference Include="..\..\..\catalog\src\EShopOnAbp.CatalogService.Application.Contracts\EShopOnAbp.CatalogService.Application.Contracts.csproj" />
<ProjectReference Include="..\..\..\ordering\src\EShopOnAbp.OrderingService.Application.Contracts\EShopOnAbp.OrderingService.Application.Contracts.csproj" />
</ItemGroup>
<ItemGroup>

18
services/basket/src/EShopOnAbp.BasketService/BasketServiceModule.cs

@ -28,18 +28,18 @@ namespace EShopOnAbp.BasketService;
typeof(BasketServiceContractsModule),
typeof(EShopOnAbpSharedHostingMicroservicesModule)
)]
public class BasketServiceModule : AbpModule
public class BasketServiceModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
var hostingEnvironment = context.Services.GetHostingEnvironment();
Microsoft.IdentityModel.Logging.IdentityModelEventSource.ShowPII = hostingEnvironment.IsDevelopment();
var configuration = context.Services.GetConfiguration();
ConfigureAutoMapper();
ConfigureAspNetCoreRouting(context);
ConfigureAspNetCoreRouting();
ConfigureGrpc(context);
ConfigureDistributedCache();
ConfigureVirtualFileSystem();
@ -48,7 +48,7 @@ public class BasketServiceModule : AbpModule
ConfigureAutoApiControllers();
}
private void ConfigureAspNetCoreRouting(ServiceConfigurationContext context)
private void ConfigureAspNetCoreRouting()
{
Configure<AbpAspNetCoreMvcOptions>(options =>
{
@ -91,7 +91,7 @@ public class BasketServiceModule : AbpModule
app.UseUnitOfWork();
app.UseConfiguredEndpoints();
}
private void ConfigureSwagger(ServiceConfigurationContext context, IConfiguration configuration)
{
SwaggerConfigurationHelper.ConfigureWithAuth(
@ -105,7 +105,7 @@ public class BasketServiceModule : AbpModule
apiTitle: "Basket Service API"
);
}
private void ConfigureAuthentication(ServiceConfigurationContext context, IConfiguration configuration)
{
JwtBearerConfigurationHelper.Configure(context, "BasketService");
@ -128,7 +128,7 @@ public class BasketServiceModule : AbpModule
.AllowCredentials();
});
});
Configure<AbpAntiForgeryOptions>(options => { options.AutoValidate = false; });
}
@ -142,12 +142,12 @@ public class BasketServiceModule : AbpModule
private void ConfigureGrpc(ServiceConfigurationContext context)
{
context.Services.AddGrpcClient<ProductPublic.ProductPublicClient>((services, options) =>
context.Services.AddGrpcClient<ProductPublic.ProductPublicClient>((services, options) =>
{
var remoteServiceOptions = services.GetRequiredService<IOptionsMonitor<AbpRemoteServiceOptions>>().CurrentValue;
var catalogServiceConfiguration = remoteServiceOptions.RemoteServices.GetConfigurationOrDefault("Catalog");
var catalogGrpcUrl = catalogServiceConfiguration.GetOrDefault("GrpcUrl");
options.Address = new Uri(catalogGrpcUrl);
});
}

12
services/catalog/src/EShopOnAbp.CatalogService.HttpApi.Host/EShopOnAbp.CatalogService.HttpApi.Host.csproj

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Project Sdk="Microsoft.NET.Sdk.Web">
<Import Project="..\..\..\..\common.props" />
@ -30,4 +30,14 @@
<None Remove="Logs\**" />
</ItemGroup>
<ItemGroup>
<None Remove="wwwroot\product-images\*.jpg" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="wwwroot\product-images\*.jpg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</EmbeddedResource>
</ItemGroup>
</Project>

0
apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/product-images/asus.jpg → services/catalog/src/EShopOnAbp.CatalogService.HttpApi.Host/wwwroot/product-images/asus.jpg

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 33 KiB

0
apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/product-images/beats.jpg → services/catalog/src/EShopOnAbp.CatalogService.HttpApi.Host/wwwroot/product-images/beats.jpg

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

0
apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/product-images/bluecat.jpg → services/catalog/src/EShopOnAbp.CatalogService.HttpApi.Host/wwwroot/product-images/bluecat.jpg

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

0
apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/product-images/lego.jpg → services/catalog/src/EShopOnAbp.CatalogService.HttpApi.Host/wwwroot/product-images/lego.jpg

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 94 KiB

0
apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/product-images/nikon.jpg → services/catalog/src/EShopOnAbp.CatalogService.HttpApi.Host/wwwroot/product-images/nikon.jpg

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

0
apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/product-images/noimage.jpg → services/catalog/src/EShopOnAbp.CatalogService.HttpApi.Host/wwwroot/product-images/noimage.jpg

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

0
apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/product-images/oki.jpg → services/catalog/src/EShopOnAbp.CatalogService.HttpApi.Host/wwwroot/product-images/oki.jpg

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

0
apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/product-images/playstation.jpg → services/catalog/src/EShopOnAbp.CatalogService.HttpApi.Host/wwwroot/product-images/playstation.jpg

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

0
apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/product-images/rampage.jpg → services/catalog/src/EShopOnAbp.CatalogService.HttpApi.Host/wwwroot/product-images/rampage.jpg

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

0
apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/product-images/sunny.jpg → services/catalog/src/EShopOnAbp.CatalogService.HttpApi.Host/wwwroot/product-images/sunny.jpg

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 66 KiB

30
services/identity/src/EShopOnAbp.IdentityService.HttpApi.Host/DbMigrations/IdentityServerDataSeeder.cs

@ -108,9 +108,13 @@ public class IdentityServerDataSeeder : IDataSeedContributor, ITransientDependen
await CreateWebGatewaySwaggerClientAsync("WebGateway",
new[]
{
"AccountService", "IdentityService", "AdministrationService",
"CatalogService", "BasketService",
"PaymentService", "OrderingService"
"AccountService",
"IdentityService",
"AdministrationService",
"CatalogService",
"BasketService",
"PaymentService",
"OrderingService"
});
}
@ -125,7 +129,7 @@ public class IdentityServerDataSeeder : IDataSeedContributor, ITransientDependen
"phone",
"address"
};
scopes ??= new[] {name};
scopes ??= new[] { name };
// Swagger Client
var swaggerClientId = $"{name}_Swagger";
@ -144,7 +148,7 @@ public class IdentityServerDataSeeder : IDataSeedContributor, ITransientDependen
await CreateClientAsync(
name: swaggerClientId,
scopes: commonScopes.Union(scopes),
grantTypes: new[] {"authorization_code"},
grantTypes: new[] { "authorization_code" },
secret: "1q2w3e*".Sha256(),
requireClientSecret: false,
redirectUris: new List<string>
@ -245,12 +249,12 @@ public class IdentityServerDataSeeder : IDataSeedContributor, ITransientDependen
"PaymentService",
"OrderingService"
}),
grantTypes: new[] {"hybrid"},
grantTypes: new[] { "hybrid" },
secret: "1q2w3e*".Sha256(),
redirectUris: new List<string>{ $"{publicWebClientRootUrl}signin-oidc" },
redirectUris: new List<string> { $"{publicWebClientRootUrl}signin-oidc" },
postLogoutRedirectUri: $"{publicWebClientRootUrl}signout-callback-oidc",
frontChannelLogoutUri: $"{publicWebClientRootUrl}Account/FrontChannelLogout",
corsOrigins: new[] {publicWebClientRootUrl.RemovePostFix("/")}
corsOrigins: new[] { publicWebClientRootUrl.RemovePostFix("/") }
);
//Angular Client
@ -266,13 +270,13 @@ public class IdentityServerDataSeeder : IDataSeedContributor, ITransientDependen
"CatalogService",
"OrderingService"
}),
grantTypes: new[] {"authorization_code", "LinkLogin", "password"},
grantTypes: new[] { "authorization_code", "LinkLogin", "password" },
secret: "1q2w3e*".Sha256(),
requirePkce: true,
requireClientSecret: false,
redirectUris: new List<string>{ $"{angularClientRootUrl}" },
redirectUris: new List<string> { $"{angularClientRootUrl}" },
postLogoutRedirectUri: $"{angularClientRootUrl}",
corsOrigins: new[] {angularClientRootUrl}
corsOrigins: new[] { angularClientRootUrl }
);
//Administration Service Client
@ -282,9 +286,9 @@ public class IdentityServerDataSeeder : IDataSeedContributor, ITransientDependen
{
"IdentityService"
}),
grantTypes: new[] {"client_credentials"},
grantTypes: new[] { "client_credentials" },
secret: "1q2w3e*".Sha256(),
permissions: new[] {IdentityPermissions.Users.Default}
permissions: new[] { IdentityPermissions.Users.Default }
);
}

7
services/ordering/src/EShopOnAbp.OrderingService.Application.Contracts/Orders/GetOrdersInput.cs

@ -0,0 +1,7 @@
namespace EShopOnAbp.OrderingService.Orders
{
public class GetOrdersInput
{
public string Filter { get; set; }
}
}

6
services/ordering/src/EShopOnAbp.OrderingService.Application.Contracts/Orders/IOrderAppService.cs

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
namespace EShopOnAbp.OrderingService.Orders;
@ -10,5 +11,10 @@ public interface IOrderAppService : IApplicationService
Task<OrderDto> CreateAsync(OrderCreateDto input);
Task<OrderDto> GetAsync(Guid id);
Task<List<OrderDto>> GetMyOrdersAsync(GetMyOrdersInput input);
Task<List<OrderDto>> GetOrdersAsync(GetOrdersInput input);
Task<OrderDto> GetByOrderNoAsync(int orderNo);
Task SetAsCancelledAsync(Guid id);
Task SetAsShippedAsync(Guid id);
Task<PagedResultDto<OrderDto>> GetListPagedAsync(PagedAndSortedResultRequestDto input);
}

6
services/ordering/src/EShopOnAbp.OrderingService.Application.Contracts/Permissions/OrderingServicePermissionDefinitionProvider.cs

@ -8,7 +8,11 @@ namespace EShopOnAbp.OrderingService.Permissions
{
public override void Define(IPermissionDefinitionContext context)
{
var myGroup = context.AddGroup(OrderingServicePermissions.GroupName, L("Permission:OrderingService"));
var orderManagmentGroup = context.AddGroup(OrderingServicePermissions.GroupName, L("Permission:OrderingService"));
var oders = orderManagmentGroup.AddPermission(OrderingServicePermissions.Orders.Default, L("Permission:Orders"));
oders.AddChild(OrderingServicePermissions.Orders.SetAsCancelled, L("Permission:Orders.SetAsCancelled"));
oders.AddChild(OrderingServicePermissions.Orders.SetAsShipped, L("Permission:Orders.SetAsShipped"));
}
private static LocalizableString L(string name)

9
services/ordering/src/EShopOnAbp.OrderingService.Application.Contracts/Permissions/OrderingServicePermissions.cs

@ -2,10 +2,17 @@
namespace EShopOnAbp.OrderingService.Permissions
{
public class OrderingServicePermissions
public static class OrderingServicePermissions
{
public const string GroupName = "OrderingService";
public static class Orders
{
public const string Default = GroupName + ".Orders";
public const string SetAsCancelled = GroupName + ".SetAsCancelled";
public const string SetAsShipped = GroupName + ".SetAsShipped";
}
public static string[] GetAll()
{
return ReflectionHelper.GetPublicConstantsRecursively(typeof(OrderingServicePermissions));

41
services/ordering/src/EShopOnAbp.OrderingService.Application/Orders/OrderAppService.cs

@ -1,15 +1,19 @@
using EShopOnAbp.OrderingService.Localization;
using EShopOnAbp.OrderingService.Orders.Specifications;
using EShopOnAbp.OrderingService.Permissions;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Specifications;
using Volo.Abp.Users;
namespace EShopOnAbp.OrderingService.Orders;
[Authorize(OrderingServicePermissions.Orders.Default)]
public class OrderAppService : ApplicationService, IOrderAppService
{
private readonly OrderManager _orderManager;
@ -31,15 +35,33 @@ public class OrderAppService : ApplicationService, IOrderAppService
return CreateOrderDtoMapping(order);
}
[AllowAnonymous]
public async Task<List<OrderDto>> GetMyOrdersAsync(GetMyOrdersInput input)
{
ISpecification<Order> specification = SpecificationFactory.Create(input.Filter);
var orders = await _orderRepository.GetOrdersByUserId(CurrentUser.GetId(), specification, true);
return CreateOrderDtoMapping(orders);
}
public async Task<List<OrderDto>> GetOrdersAsync(GetOrdersInput input)
{
ISpecification<Order> specification = SpecificationFactory.Create(input.Filter);
var orders = await _orderRepository.GetOrdersAsync(specification, true);
return CreateOrderDtoMapping(orders);
}
public async Task<PagedResultDto<OrderDto>> GetListPagedAsync(PagedAndSortedResultRequestDto input)
{
var orders = await _orderRepository.GetPagedListAsync(input.SkipCount, input.MaxResultCount, input.Sorting ?? "OrderDate", true);
var totalCount = await _orderRepository.GetCountAsync();
return new PagedResultDto<OrderDto>(
totalCount,
CreateOrderDtoMapping(orders)
);
}
[AllowAnonymous]
public async Task<OrderDto> GetByOrderNoAsync(int orderNo)
{
var order = await _orderRepository.GetByOrderNoAsync(orderNo);
@ -47,6 +69,22 @@ public class OrderAppService : ApplicationService, IOrderAppService
return CreateOrderDtoMapping(order);
}
[Authorize(OrderingServicePermissions.Orders.SetAsCancelled)]
public async Task SetAsCancelledAsync(Guid id)
{
await _orderManager.CancelOrderAsync(id);
}
[Authorize(OrderingServicePermissions.Orders.SetAsShipped)]
public async Task SetAsShippedAsync(Guid id)
{
var order = await _orderRepository.GetAsync(id);
order.SetOrderAsShipped();
await _orderRepository.UpdateAsync(order);
}
[AllowAnonymous]
public async Task<OrderDto> CreateAsync(OrderCreateDto input)
{
var orderItems = GetProductListTuple(input.Products);
@ -68,6 +106,7 @@ public class OrderAppService : ApplicationService, IOrderAppService
return CreateOrderDtoMapping(placedOrder);
}
private List<(Guid productId, string productName, string productCode, decimal unitPrice, decimal discount, string
pictureUrl, int units
)> GetProductListTuple(List<OrderItemCreateDto> products)

35
services/ordering/src/EShopOnAbp.OrderingService.Domain.Shared/Localization/OrderingService/en.json

@ -1,12 +1,43 @@
{
"culture": "en",
"texts": {
"MyAccount": "My account",
"MyAccount": "My account",
"SamplePageMessage": "A sample page for the OrderingService module",
"Ordering:00001": "Possible values for Order status: {OrderStatus}",
"Ordering:00002": "Invalid number of units",
"Ordering:00003": "Invalid discount",
"Ordering:00004": "The total of order item is lower than applied discount",
"Ordering:01001": "Order with {0} id could not be found!"
"Ordering:01001": "Order with {0} id could not be found!",
"Permission:Orders": "Orders",
"Permission:OrderingService": "Ordering Service",
"Menu:Orders": "Orders",
"Menu:OrderManagement": "Order Management",
"ModalTitle":"Order Detail",
"DisplayName:OrderNo": "Order No",
"DisplayName:OrderDate": "Order Date",
"DisplayName:OrderTotal": "Order Total",
"DisplayName:OrderStatus": "Order Status",
"DisplayName:ProductId": "Product Id",
"DisplayName:ProductName": "Product Name",
"DisplayName:PictureUrl": "Picture",
"DisplayName:Units": "Unit",
"DisplayName:UnitPrice": "Unit Price",
"DisplayName:Discount": "Discount",
"DisplayName:TotalPrice": "Total Price",
"DisplayName:BuyerName": "Buyer Name",
"DisplayName:BuyerEmail": "Buyer E-mail",
"DisplayName:Address": "Address",
"DisplayName:PaymentMethod": "Payment Method",
"DisplayName:Total": "Total",
"Permission:Orders.SetAsCancelled": "Set As Cancelled",
"Permission:Orders.SetAsShipped": "Set As Shipped",
"Enum:OrderStatus:0":"Placed",
"Enum:OrderStatus:1":"Paid",
"Enum:OrderStatus:2":"Shipped",
"Enum:OrderStatus:3":"Cancelled"
}
}

35
services/ordering/src/EShopOnAbp.OrderingService.Domain.Shared/Localization/OrderingService/tr.json

@ -1,12 +1,43 @@
{
"culture": "tr",
"texts": {
"MyAccount": "Hesabım",
"MyAccount": "Hesabım",
"SamplePageMessage": "OrderingService modulünden örnek bir sayfa",
"Ordering:00001": "Muhtemel Sipariş durum değerleri: {OrderStatus}",
"Ordering:00002": "Ürün adedi negatif olamaz",
"Ordering:00003": "Geçersiz indirim",
"Ordering:00004": "Ürünlerin toplam bedeli, uygulanan indirimden daha az olamaz",
"Ordering:01001": "{0} ID numaralı ürün bulunamadı!"
"Ordering:01001": "{0} ID numaralı ürün bulunamadı!",
"Permission:Orders": "Siparişler",
"Permission:OrderingService": "Sipariş Servisi",
"Menu:Orders": "Siparişler",
"Menu:OrderManagement": "Sipariş Yönetimi",
"ModalTitle": "Sipariş Detayı",
"DisplayName:OrderNo": "Sipariş Numarası",
"DisplayName:OrderDate": "Sipariş Tarihi",
"DisplayName:OrderTotal": "Sipariş Toplamı",
"DisplayName:OrderStatus": "Sipariş Durumu",
"DisplayName:ProductId": "Ürün Anahtarı",
"DisplayName:ProductName": "Ürün Adı",
"DisplayName:PictureUrl": "Resim",
"DisplayName:Units": "Adet",
"DisplayName:UnitPrice": "Adet Fiyatı",
"DisplayName:Discount": "İndirim",
"DisplayName:TotalPrice": "Toplam Fiyat",
"DisplayName:BuyerName": "Alan Kişi",
"DisplayName:BuyerEmail": "Alan Kişi Mail",
"DisplayName:Address": "Adres",
"DisplayName:PaymentMethod": "Ödeme Şekli",
"DisplayName:Total": "Toplam",
"Permission:Orders.SetAsCancelled": "İptal Edildi Olarak İşaretle",
"Permission:Orders.SetAsShipped": "Sevk Edildi Olarak İşaretle",
"Enum:OrderStatus:0": "Oluşturuldu",
"Enum:OrderStatus:1": "Ödendi",
"Enum:OrderStatus:2": "Sevk Edildi",
"Enum:OrderStatus:3": "İptal Edildi"
}
}

8
services/ordering/src/EShopOnAbp.OrderingService.Domain.Shared/Localization/OrderingService/zh-Hans.json

@ -8,6 +8,12 @@
"Ordering:00002": "无效的单位数量",
"Ordering:00003": "无效的折扣",
"Ordering:00004": "订单项的总额低于申请的折扣",
"Ordering:01001": "无法找到 id 为 {0} 的订单!"
"Ordering:01001": "无法找到 id 为 {0} 的订单!",
"Permission:Orders": "命令",
"Permission:OrderingService": "订购服务",
"Menu:Orders": "命令",
"Menu:OrderManagement": "订单管理",
"Permission:Orders.SetAsCancelled": "设置为取消",
"Permission:Orders.SetAsShipped": "设置为发货"
}
}

17
services/ordering/src/EShopOnAbp.OrderingService.Domain.Shared/Orders/OrderCancelledEto.cs

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using Volo.Abp.Domain.Entities.Events.Distributed;
using Volo.Abp.EventBus;
namespace EShopOnAbp.OrderingService.Orders;
[EventName("EShopOnAbp.Order.Cancelled")]
public class OrderCancelledEto : EtoBase
{
public Guid PaymentRequestId { get; set; }
public Guid OrderId { get; set; }
public int OrderNo { get; set; }
public DateTime OrderDate { get; set; }
public BuyerEto Buyer { get; set; }
public List<OrderItemEto> Items { get; set; } = new();
}

5
services/ordering/src/EShopOnAbp.OrderingService.Domain/Orders/IOrderRepository.cs

@ -15,6 +15,11 @@ public interface IOrderRepository : IRepository<Order, Guid>
bool includeDetails = true,
CancellationToken cancellationToken = default);
Task<List<Order>> GetOrdersAsync(
ISpecification<Order> spec,
bool includeDetails = true,
CancellationToken cancellationToken = default);
Task<Order> GetByOrderNoAsync(int orderNo,
bool includeDetails = true,
CancellationToken cancellationToken = default);

16
services/ordering/src/EShopOnAbp.OrderingService.Domain/Orders/Order.cs

@ -57,6 +57,12 @@ public class Order : AggregateRoot<Guid>
return this;
}
public Order SetOrderCancelled()
{
OrderStatus = OrderStatus.Cancelled;
return this;
}
public Order AddOrderItem(Guid id, Guid productId, string productName, string productCode, decimal unitPrice,
decimal discount, string pictureUrl, int units = 1)
{
@ -85,4 +91,14 @@ public class Order : AggregateRoot<Guid>
{
return OrderItems.Sum(o => o.Units * o.UnitPrice);
}
public Order SetOrderAsShipped()
{
if (OrderStatus == OrderStatus.Cancelled)
{
return this;
}
OrderStatus = OrderStatus.Shipped;
return this;
}
}

25
services/ordering/src/EShopOnAbp.OrderingService.Domain/Orders/OrderManager.cs

@ -91,6 +91,31 @@ public class OrderManager : DomainService
return await _orderRepository.UpdateAsync(order, autoSave: true);
}
public async Task<Order> CancelOrderAsync(Guid orderId)
{
var order = await _orderRepository.GetAsync(orderId);
if (order == null)
{
throw new BusinessException(OrderingServiceErrorCodes.OrderWithIdNotFound)
.WithData("OrderId", orderId);
}
order.SetOrderCancelled();
// Publish order cancelled event
await _distributedEventBus.PublishAsync(new OrderCancelledEto
{
PaymentRequestId = order.PaymentRequestId.GetValueOrDefault(),
OrderId = order.Id,
OrderDate = order.OrderDate,
OrderNo = order.OrderNo,
Buyer = GetBuyerEto(order.Buyer),
Items = GetProductItemEtoList(order.OrderItems)
});
return await _orderRepository.UpdateAsync(order, autoSave: true);
}
private BuyerEto GetBuyerEto(Buyer buyer)
{
return new BuyerEto

12
services/ordering/src/EShopOnAbp.OrderingService.EntityFrameworkCore/Orders/EfCoreOrderRepository.cs

@ -34,6 +34,18 @@ public class EfCoreOrderRepository : EfCoreRepository<OrderingServiceDbContext,
.ToListAsync(GetCancellationToken(cancellationToken));
}
public async Task<List<Order>> GetOrdersAsync(
ISpecification<Order> spec,
bool includeDetails = false,
CancellationToken cancellationToken = default)
{
return await (await GetDbSetAsync())
.IncludeDetails(includeDetails)
.Where(spec.ToExpression())
.OrderByDescending(o => o.OrderDate)
.ToListAsync(GetCancellationToken(cancellationToken));
}
public async Task<Order> GetByOrderNoAsync(
int orderNo,
bool includeDetails = true,

87
services/ordering/src/EShopOnAbp.OrderingService.HttpApi.Client/ClientProxies/OrderClientProxy.Generated.cs

@ -10,42 +10,73 @@ using EShopOnAbp.OrderingService.Orders;
using System.Collections.Generic;
// ReSharper disable once CheckNamespace
namespace EShopOnAbp.OrderingService.Orders.ClientProxies
namespace EShopOnAbp.OrderingService.Orders.ClientProxies;
[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(IOrderAppService), typeof(OrderClientProxy))]
public partial class OrderClientProxy : ClientProxyBase<IOrderAppService>, IOrderAppService
{
[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(IOrderAppService), typeof(OrderClientProxy))]
public partial class OrderClientProxy : ClientProxyBase<IOrderAppService>, IOrderAppService
public virtual async Task<OrderDto> GetAsync(Guid id)
{
return await RequestAsync<OrderDto>(nameof(GetAsync), new ClientProxyRequestTypeValue
{
{ typeof(Guid), id }
});
}
public virtual async Task<List<OrderDto>> GetMyOrdersAsync(GetMyOrdersInput input)
{
public virtual async Task<OrderDto> GetAsync(Guid id)
return await RequestAsync<List<OrderDto>>(nameof(GetMyOrdersAsync), new ClientProxyRequestTypeValue
{
return await RequestAsync<OrderDto>(nameof(GetAsync), new ClientProxyRequestTypeValue
{
{ typeof(Guid), id }
});
}
{ typeof(GetMyOrdersInput), input }
});
}
public virtual async Task<List<OrderDto>> GetMyOrdersAsync(GetMyOrdersInput input)
public virtual async Task<List<OrderDto>> GetOrdersAsync(GetOrdersInput input)
{
return await RequestAsync<List<OrderDto>>(nameof(GetOrdersAsync), new ClientProxyRequestTypeValue
{
return await RequestAsync<List<OrderDto>>(nameof(GetMyOrdersAsync), new ClientProxyRequestTypeValue
{
{ typeof(GetMyOrdersInput), input }
});
}
{ typeof(GetOrdersInput), input }
});
}
public virtual async Task<OrderDto> GetByOrderNoAsync(int orderNo)
public virtual async Task<PagedResultDto<OrderDto>> GetListPagedAsync(PagedAndSortedResultRequestDto input)
{
return await RequestAsync<PagedResultDto<OrderDto>>(nameof(GetListPagedAsync), new ClientProxyRequestTypeValue
{
return await RequestAsync<OrderDto>(nameof(GetByOrderNoAsync), new ClientProxyRequestTypeValue
{
{ typeof(int), orderNo }
});
}
{ typeof(PagedAndSortedResultRequestDto), input }
});
}
public virtual async Task<OrderDto> CreateAsync(OrderCreateDto input)
public virtual async Task<OrderDto> GetByOrderNoAsync(int orderNo)
{
return await RequestAsync<OrderDto>(nameof(GetByOrderNoAsync), new ClientProxyRequestTypeValue
{
{ typeof(int), orderNo }
});
}
public virtual async Task SetAsCancelledAsync(Guid id)
{
await RequestAsync(nameof(SetAsCancelledAsync), new ClientProxyRequestTypeValue
{
{ typeof(Guid), id }
});
}
public virtual async Task SetAsShippedAsync(Guid id)
{
await RequestAsync(nameof(SetAsShippedAsync), new ClientProxyRequestTypeValue
{
{ typeof(Guid), id }
});
}
public virtual async Task<OrderDto> CreateAsync(OrderCreateDto input)
{
return await RequestAsync<OrderDto>(nameof(CreateAsync), new ClientProxyRequestTypeValue
{
return await RequestAsync<OrderDto>(nameof(CreateAsync), new ClientProxyRequestTypeValue
{
{ typeof(OrderCreateDto), input }
});
}
{ typeof(OrderCreateDto), input }
});
}
}

172
services/ordering/src/EShopOnAbp.OrderingService.HttpApi.Client/ClientProxies/ordering-generate-proxy.json

@ -97,6 +97,104 @@
"allowAnonymous": null,
"implementFrom": "EShopOnAbp.OrderingService.Orders.IOrderAppService"
},
"GetOrdersAsyncByInput": {
"uniqueName": "GetOrdersAsyncByInput",
"name": "GetOrdersAsync",
"httpMethod": "GET",
"url": "api/ordering/order/orders",
"supportedVersions": [],
"parametersOnMethod": [
{
"name": "input",
"typeAsString": "EShopOnAbp.OrderingService.Orders.GetOrdersInput, EShopOnAbp.OrderingService.Application.Contracts",
"type": "EShopOnAbp.OrderingService.Orders.GetOrdersInput",
"typeSimple": "EShopOnAbp.OrderingService.Orders.GetOrdersInput",
"isOptional": false,
"defaultValue": null
}
],
"parameters": [
{
"nameOnMethod": "input",
"name": "Filter",
"jsonName": null,
"type": "System.String",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null,
"constraintTypes": null,
"bindingSourceId": "ModelBinding",
"descriptorName": "input"
}
],
"returnValue": {
"type": "System.Collections.Generic.List<EShopOnAbp.OrderingService.Orders.OrderDto>",
"typeSimple": "[EShopOnAbp.OrderingService.Orders.OrderDto]"
},
"allowAnonymous": false,
"implementFrom": "EShopOnAbp.OrderingService.Orders.IOrderAppService"
},
"GetListPagedAsyncByInput": {
"uniqueName": "GetListPagedAsyncByInput",
"name": "GetListPagedAsync",
"httpMethod": "GET",
"url": "api/ordering/order/paged",
"supportedVersions": [],
"parametersOnMethod": [
{
"name": "input",
"typeAsString": "Volo.Abp.Application.Dtos.PagedAndSortedResultRequestDto, Volo.Abp.Ddd.Application.Contracts",
"type": "Volo.Abp.Application.Dtos.PagedAndSortedResultRequestDto",
"typeSimple": "Volo.Abp.Application.Dtos.PagedAndSortedResultRequestDto",
"isOptional": false,
"defaultValue": null
}
],
"parameters": [
{
"nameOnMethod": "input",
"name": "Sorting",
"jsonName": null,
"type": "System.String",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null,
"constraintTypes": null,
"bindingSourceId": "ModelBinding",
"descriptorName": "input"
},
{
"nameOnMethod": "input",
"name": "SkipCount",
"jsonName": null,
"type": "System.Int32",
"typeSimple": "number",
"isOptional": false,
"defaultValue": null,
"constraintTypes": null,
"bindingSourceId": "ModelBinding",
"descriptorName": "input"
},
{
"nameOnMethod": "input",
"name": "MaxResultCount",
"jsonName": null,
"type": "System.Int32",
"typeSimple": "number",
"isOptional": false,
"defaultValue": null,
"constraintTypes": null,
"bindingSourceId": "ModelBinding",
"descriptorName": "input"
}
],
"returnValue": {
"type": "Volo.Abp.Application.Dtos.PagedResultDto<EShopOnAbp.OrderingService.Orders.OrderDto>",
"typeSimple": "Volo.Abp.Application.Dtos.PagedResultDto<EShopOnAbp.OrderingService.Orders.OrderDto>"
},
"allowAnonymous": false,
"implementFrom": "EShopOnAbp.OrderingService.Orders.IOrderAppService"
},
"GetByOrderNoAsyncByOrderNo": {
"uniqueName": "GetByOrderNoAsyncByOrderNo",
"name": "GetByOrderNoAsync",
@ -134,6 +232,80 @@
"allowAnonymous": null,
"implementFrom": "EShopOnAbp.OrderingService.Orders.IOrderAppService"
},
"SetAsCancelledAsyncById": {
"uniqueName": "SetAsCancelledAsyncById",
"name": "SetAsCancelledAsync",
"httpMethod": "POST",
"url": "api/ordering/order/{id}/set-as-cancelled",
"supportedVersions": [],
"parametersOnMethod": [
{
"name": "id",
"typeAsString": "System.Guid, System.Private.CoreLib",
"type": "System.Guid",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null
}
],
"parameters": [
{
"nameOnMethod": "id",
"name": "id",
"jsonName": null,
"type": "System.Guid",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null,
"constraintTypes": [],
"bindingSourceId": "Path",
"descriptorName": ""
}
],
"returnValue": {
"type": "System.Void",
"typeSimple": "System.Void"
},
"allowAnonymous": false,
"implementFrom": "EShopOnAbp.OrderingService.Orders.IOrderAppService"
},
"SetAsShippedAsyncById": {
"uniqueName": "SetAsShippedAsyncById",
"name": "SetAsShippedAsync",
"httpMethod": "POST",
"url": "api/ordering/order/{id}/set-as-shipped",
"supportedVersions": [],
"parametersOnMethod": [
{
"name": "id",
"typeAsString": "System.Guid, System.Private.CoreLib",
"type": "System.Guid",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null
}
],
"parameters": [
{
"nameOnMethod": "id",
"name": "id",
"jsonName": null,
"type": "System.Guid",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null,
"constraintTypes": [],
"bindingSourceId": "Path",
"descriptorName": ""
}
],
"returnValue": {
"type": "System.Void",
"typeSimple": "System.Void"
},
"allowAnonymous": false,
"implementFrom": "EShopOnAbp.OrderingService.Orders.IOrderAppService"
},
"CreateAsyncByInput": {
"uniqueName": "CreateAsyncByInput",
"name": "CreateAsync",

2
services/ordering/src/EShopOnAbp.OrderingService.HttpApi.Host/appsettings.json

@ -1,7 +1,7 @@
{
"App": {
"SelfUrl": "https://localhost:44356",
"CorsOrigins": "https://localhost:44372,https://localhost:44373"
"CorsOrigins": "https://localhost:44372,https://localhost:44373,https://localhost:4200"
},
"AuthServer": {
"Authority": "https://localhost:44330",

33
services/ordering/test/EShopOnAbp.OrderingService.Application.Tests/Orders/OrderApplication_Tests.cs

@ -1,15 +1,15 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using NSubstitute;
using Shouldly;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Users;
using Xunit;
namespace EShopOnAbp.OrderingService.Orders;
public class OrderApplication_Tests:OrderingServiceApplicationTestBase
public class OrderApplication_Tests : OrderingServiceApplicationTestBase
{
private readonly IOrderAppService _orderAppService;
private readonly TestData _testData;
@ -38,8 +38,13 @@ public class OrderApplication_Tests:OrderingServiceApplicationTestBase
{
new OrderItemCreateDto()
{
Discount = 0, Units = 2, PictureUrl = "", ProductCode = "Test-001", ProductId = Guid.NewGuid(),
ProductName = "Test product", UnitPrice = 150
Discount = 0,
Units = 2,
PictureUrl = string.Empty,
ProductCode = "Test-001",
ProductId = Guid.NewGuid(),
ProductName = "Test product",
UnitPrice = 150
}
};
@ -48,14 +53,24 @@ public class OrderApplication_Tests:OrderingServiceApplicationTestBase
PaymentMethod = "paypal",
Address = new OrderAddressDto()
{
City = "Test City", Country = "Test Country", Description = "No Description", Street = "Test Street",
City = "Test City",
Country = "Test Country",
Description = "No Description",
Street = "Test Street",
ZipCode = "Test ZipCode"
},
Products = orderItems
});
// Get Order by OrderNo;
var myOrder = await _orderAppService.GetByOrderNoAsync(placedOrder.OrderNo);
myOrder.ShouldNotBeNull();
// Get all orders
var orders = await _orderAppService.GetOrdersAsync(new GetOrdersInput()
{
Filter = string.Empty,
});
orders.ShouldNotBeNull();
}
}

5
services/payment/src/EShopOnAbp.PaymentService.Domain/EShopOnAbp.PaymentService.Domain.csproj

@ -12,4 +12,9 @@
<ProjectReference Include="..\EShopOnAbp.PaymentService.Domain.Shared\EShopOnAbp.PaymentService.Domain.Shared.csproj" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\ordering\src\EShopOnAbp.OrderingService.Domain.Shared\EShopOnAbp.OrderingService.Domain.Shared.csproj" />
<ProjectReference Include="..\..\..\..\shared\EShopOnAbp.Shared.Hosting\EShopOnAbp.Shared.Hosting.csproj" />
</ItemGroup>
</Project>

26
services/payment/src/EShopOnAbp.PaymentService.Domain/EventHandlers/OrderCancelledEventHandler.cs

@ -0,0 +1,26 @@
using EShopOnAbp.OrderingService.Orders;
using EShopOnAbp.PaymentService.PaymentRequests;
using Serilog;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus.Distributed;
namespace EShopOnAbp.PaymentService.EventHandlers
{
public class OrderCancelledEventHandler : IDistributedEventHandler<OrderCancelledEto>, ITransientDependency
{
private readonly IPaymentRequestRepository _paymentRepository;
public OrderCancelledEventHandler(IPaymentRequestRepository paymenRepository)
{
_paymentRepository = paymenRepository;
}
public async Task HandleEventAsync(OrderCancelledEto eventData)
{
var payment = await _paymentRepository.GetAsync(eventData.PaymentRequestId);
Log.Information($"Cancelled the order: {payment.OrderId} payment:{payment.Id}");
}
}
}

7
shared/EShopOnAbp.Shared.Hosting.Gateways/EShopOnAbp.Shared.Hosting.Gateways.csproj

@ -5,12 +5,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Ocelot" Version="18.0.0" />
<PackageReference Include="Ocelot.Provider.Polly" Version="18.0.0" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy" Version="5.1.2" />
<PackageReference Include="Yarp.ReverseProxy" Version="1.0.0" />
</ItemGroup>
<ItemGroup>

12
shared/EShopOnAbp.Shared.Hosting.Gateways/EShopOnAbpSharedHostingGatewaysModule.cs

@ -1,24 +1,20 @@
using EShopOnAbp.Shared.Hosting.AspNetCore;
using Microsoft.Extensions.DependencyInjection;
using Ocelot.DependencyInjection;
using Ocelot.Provider.Polly;
using Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy;
using Volo.Abp.Modularity;
namespace EShopOnAbp.Shared.Hosting.Gateways
{
[DependsOn(
typeof(EShopOnAbpSharedHostingAspNetCoreModule),
typeof(AbpAspNetCoreMvcUiMultiTenancyModule)
typeof(EShopOnAbpSharedHostingAspNetCoreModule)
)]
public class EShopOnAbpSharedHostingGatewaysModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
context.Services.AddOcelot(configuration)
.AddPolly();
context.Services.AddReverseProxy()
.LoadFromConfig(configuration.GetSection("ReverseProxy"));
}
}
}

6
shared/EShopOnAbp.Shared.Hosting.Gateways/GatewayHostBuilderExtensions.cs

@ -4,18 +4,18 @@ namespace Microsoft.Extensions.Hosting;
public static class AbpHostingHostBuilderExtensions
{
public const string AppOcelotJsonPath = "ocelot.json";
public const string AppYarpJsonPath = "yarp.json";
public static IHostBuilder AddOcelotJson(
this IHostBuilder hostBuilder,
bool optional = true,
bool reloadOnChange = true,
string path = AppOcelotJsonPath)
string path = AppYarpJsonPath)
{
return hostBuilder.ConfigureAppConfiguration((_, builder) =>
{
builder.AddJsonFile(
path: AppOcelotJsonPath,
path: AppYarpJsonPath,
optional: optional,
reloadOnChange: reloadOnChange
);

20
shared/EShopOnAbp.Shared.Hosting.Gateways/OcelotConfiguration.cs

@ -1,20 +0,0 @@
using System.Collections.Generic;
namespace EShopOnAbp.Shared.Hosting.Gateways
{
public class OcelotConfiguration
{
public string ServiceKey { get; set; }
public string DownstreamPathTemplate { get; set; }
public string DownstreamScheme { get; set; }
public string UpstreamPathTemplate { get; set; }
public List<string> UpstreamHttpMethod { get; set; }
public List<HostAndPort> DownstreamHostAndPorts { get; set; }
}
public class HostAndPort
{
public string Host { get; set; }
public int Port { get; set; }
}
}

52
shared/EShopOnAbp.Shared.Hosting.Gateways/YarpSwaggerUIBuilderExtensions.cs

@ -0,0 +1,52 @@
using System.Linq;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Volo.Abp;
using Yarp.ReverseProxy.Configuration;
namespace EShopOnAbp.Shared.Hosting.Gateways;
public static class YarpSwaggerUIBuilderExtensions
{
public static IApplicationBuilder ConfigureSwaggerUIWithYarp(this IApplicationBuilder app,
ApplicationInitializationContext context)
{
app.UseSwagger();
app.UseSwaggerUI(options =>
{
var configuration = context.ServiceProvider.GetRequiredService<IConfiguration>();
var logger = context.ServiceProvider.GetRequiredService<ILogger<ApplicationInitializationContext>>();
var proxyConfigProvider = context.ServiceProvider.GetRequiredService<IProxyConfigProvider>();
var yarpConfig = proxyConfigProvider.GetConfig();
var routedClusters = yarpConfig.Clusters
.SelectMany(t => t.Destinations,
(clusterId, destination) => new {clusterId.ClusterId, destination.Value});
var groupedClusters = routedClusters
.GroupBy(q => q.Value.Address)
.Select(t => t.First())
.Distinct()
.ToList();
foreach (var clusterGroup in groupedClusters)
{
var routeConfig = yarpConfig.Routes.FirstOrDefault(q =>
q.ClusterId == clusterGroup.ClusterId);
if (routeConfig == null)
{
logger.LogWarning($"Swagger UI: Couldn't find route configuration for {clusterGroup.ClusterId}...");
continue;
}
options.SwaggerEndpoint($"{clusterGroup.Value.Address}/swagger/v1/swagger.json", $"{routeConfig.RouteId} API");
options.OAuthClientId(configuration["AuthServer:SwaggerClientId"]);
options.OAuthClientSecret(configuration["AuthServer:SwaggerClientSecret"]);
}
});
return app;
}
}
Loading…
Cancel
Save