Browse Source

Merge branch 'main' into enisn/migration-refactoring

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

13
README.md

@ -45,6 +45,19 @@ You can either run in Visual Studio, or using [Microsoft Tye](https://github.com
yarn start
```
### Certificate Expiration
If the cerficiate is expired you'll see following error:
<!-- Make it smaller with 320px height -->
<img src="docs/images/ssl-error.png" height="320"/>
Generating a new certificate will fix that issue. To generate new one,
- Remove `etc/dev-cert/localhost.pfx`
- Manually execute `create-certificate.ps1` **or** re-run solution with `run-tye.ps1`
## Roadmap
- [x] New blank micro-service solution ✔️

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": {

5
apps/public-web/src/EShopOnAbp.PublicWeb/Components/Basket/Default.cshtml

@ -1,8 +1,11 @@
@using EShopOnAbp.Localization
@using EShopOnAbp.PublicWeb.Components.Purchase
@using Microsoft.Extensions.Localization
@using Microsoft.Extensions.Options
@using Volo.Abp.Http.Client
@model EShopOnAbp.BasketService.Services.BasketDto
@inject IStringLocalizer<EShopOnAbpResource> L
@inject IOptions<AbpRemoteServiceOptions> options
<div>
@if (!Model.Items.Any())
@ -31,7 +34,7 @@
<div class="basket-list flex-container vertical">
@foreach (var item in Model.Items)
{
string imageName = item.ImageName.IsNullOrEmpty() ? "/product-images/noimage.jpg" : $"/product-images/{item.ImageName}";
string imageName = item.ImageName.IsNullOrEmpty() ? $"{options.Value.RemoteServices.Default.BaseUrl}product-images/noimage.jpg" : $"{options.Value.RemoteServices.Default.BaseUrl}product-images/{item.ImageName}";
var price = item.TotalPrice.ToString("0.00");
<div class="card basket-list-item" data-product-id="@item.ProductId">
<div class="card-body">

6
apps/public-web/src/EShopOnAbp.PublicWeb/Components/Toolbar/Cart/Default.cshtml

@ -1,7 +1,11 @@
@using EShopOnAbp.Localization
@using Microsoft.AspNetCore.Mvc.Localization
@using Microsoft.Extensions.Options
@using Volo.Abp.Http.Client
@inject IHtmlLocalizer<EShopOnAbpResource> L
@model EShopOnAbp.BasketService.Services.BasketDto
@inject IOptions<AbpRemoteServiceOptions> options
<div id="shopping-cart" class="nav-link">
@if (Model == null)
@ -34,7 +38,7 @@
{
foreach (var item in Model.Items)
{
string imageName = item.ImageName.IsNullOrEmpty() ? "/product-images/noimage.jpg" : $"/product-images/{item.ImageName}";
string imageName = item.ImageName.IsNullOrEmpty() ? $"{options.Value.RemoteServices.Default.BaseUrl}product-images/noimage.jpg" : $"{options.Value.RemoteServices.Default.BaseUrl}product-images/{item.ImageName}";
var price = item.TotalPrice.ToString("0.00");
<div class="dropdown-item">

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

@ -1,9 +1,11 @@
@using System.Globalization
@using EShopOnAbp.Localization
@using Microsoft.Extensions.Localization
@using Microsoft.Extensions.Options
@using Volo.Abp.Http.Client
@model EShopOnAbp.PublicWeb.Components.UserOrders.UserOrderWidgetViewComponent.UserOrderViewModel
@inject IStringLocalizer<EShopOnAbpResource> L
@inject IOptions<AbpRemoteServiceOptions> options
@if (Model.ServiceError)
{
<div>SERVICE ERROR</div>
@ -13,7 +15,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">
@ -49,7 +51,10 @@
</div>
<abp-row>
<abp-column size="_2">
<img class="img-size" src="/product-images/@item.PictureUrl"/>
@{
string imageUrl = $"{options.Value.RemoteServices.Default.BaseUrl}product-images/{item.PictureUrl}";
<img class="img-size" src="@imageUrl" />
}
</abp-column>
<abp-column size-lg="_4">
<div><strong>@item.ProductName</strong></div>
@ -72,5 +77,5 @@
}
</div>
</div>
<hr/>
<hr />
}

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

BIN
docs/images/ssl-error.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

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);
}

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

@ -6,10 +6,9 @@ namespace EShopOnAbp.OrderingService.Orders;
public class OrderDto : EntityDto<Guid>
{
public DateTime OrderDate { get; set; }
public int OrderNo {get;set;}
public int OrderStatusId { get; set; }
public string OrderStatus { get; set; }
public DateTime OrderDate { get; set; }
public int OrderNo { get; set; }
public OrderStatus OrderStatus { get; set; }
public string PaymentMethod { get; set; }
public BuyerDto Buyer { get; set; }
public OrderAddressDto Address { get; set; } = new();

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));

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

@ -1,15 +1,19 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using EShopOnAbp.OrderingService.Localization;
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)
@ -106,8 +145,7 @@ public class OrderAppService : ApplicationService, IOrderAppService
Id = order.Id,
OrderNo = order.OrderNo,
OrderDate = order.OrderDate,
OrderStatus = order.OrderStatus.Name,
OrderStatusId = order.OrderStatus.Id,
OrderStatus = order.OrderStatus,
PaymentMethod = order.PaymentMethod
};
}

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();
}

9
services/ordering/src/EShopOnAbp.OrderingService.Domain.Shared/Orders/OrderStatus.cs

@ -0,0 +1,9 @@
namespace EShopOnAbp.OrderingService.Orders;
public enum OrderStatus
{
Placed,
Paid,
Shipped,
Cancelled
}

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);

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

@ -1,8 +1,8 @@
using System;
using EShopOnAbp.PaymentService.PaymentRequests;
using JetBrains.Annotations;
using System;
using System.Collections.Generic;
using System.Linq;
using EShopOnAbp.PaymentService.PaymentRequests;
using JetBrains.Annotations;
using Volo.Abp;
using Volo.Abp.Domain.Entities;
@ -10,7 +10,6 @@ namespace EShopOnAbp.OrderingService.Orders;
public class Order : AggregateRoot<Guid>
{
private int _orderStatusId;
public DateTime OrderDate { get; private set; }
public int OrderNo { get; private set; }
public string PaymentMethod { get; private set; }
@ -25,15 +24,14 @@ public class Order : AggregateRoot<Guid>
{
}
internal Order(Guid id, Buyer buyer, Address address, [NotNull]string paymentMethod, Guid? paymentRequestId = null) : base(id)
internal Order(Guid id, Buyer buyer, Address address, [NotNull] string paymentMethod, Guid? paymentRequestId = null) : base(id)
{
_orderStatusId = OrderStatus.Placed.Id;
OrderDate = DateTime.UtcNow;
OrderNo = GenerateOrderNo(id);
Buyer = buyer;
Address = address;
PaymentRequestId = paymentRequestId;
PaymentMethod = Check.NotNullOrEmpty(paymentMethod,nameof(paymentMethod),maxLength:OrderConstants.OrderPaymentMethodNameMaxLength);
PaymentMethod = Check.NotNullOrEmpty(paymentMethod, nameof(paymentMethod), maxLength: OrderConstants.OrderPaymentMethodNameMaxLength);
PaymentStatus = PaymentRequestState.Waiting.ToString(); // From PaymentService.Domain.Shared
OrderItems = new List<OrderItem>();
}
@ -59,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)
{
@ -87,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

48
services/ordering/src/EShopOnAbp.OrderingService.Domain/Orders/OrderStatus.cs

@ -1,48 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Volo.Abp;
namespace EShopOnAbp.OrderingService.Orders;
public class OrderStatus : Enumeration
{
public static OrderStatus Placed = new OrderStatus(1, nameof(Placed).ToLowerInvariant());
public static OrderStatus Paid = new OrderStatus(2, nameof(Paid).ToLowerInvariant());
public static OrderStatus Shipped = new OrderStatus(3, nameof(Shipped).ToLowerInvariant());
public static OrderStatus Cancelled = new OrderStatus(4, nameof(Cancelled).ToLowerInvariant());
public OrderStatus(int id, string name) : base(id, name)
{
}
public static IEnumerable<OrderStatus> List() =>
new[] {Placed, Paid, Shipped, Cancelled};
public static OrderStatus FromName(string name)
{
var state = List()
.SingleOrDefault(s => String.Equals(s.Name, name, StringComparison.CurrentCultureIgnoreCase));
if (state == null)
{
throw new BusinessException(OrderingServiceErrorCodes.OrderingStatusNotFound)
.WithData("OrderStatus", String.Join(",", List().Select(s => s.Name)));
}
return state;
}
public static OrderStatus From(int id)
{
var state = List().SingleOrDefault(s => s.Id == id);
if (state == null)
{
throw new BusinessException(OrderingServiceErrorCodes.OrderingStatusNotFound)
.WithData("OrderStatus", String.Join(",", List().Select(s => s.Name)));
}
return state;
}
}

37
services/ordering/src/EShopOnAbp.OrderingService.EntityFrameworkCore/EntityFrameworkCore/OrderServiceDataSeedContributor.cs

@ -1,37 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using EShopOnAbp.OrderingService.Orders;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
namespace EShopOnAbp.OrderingService.EntityFrameworkCore;
/// <summary>
/// DataSeedContributor for seeding pre-exist order status data.
/// This is a sample for seeding data without DbSet (non-aggregate data), without using repository.
/// </summary>
public class OrderServiceDataSeedContributor : IDataSeedContributor, ITransientDependency
{
private readonly IOrderingServiceDbContext _dbContext;
public OrderServiceDataSeedContributor(IOrderingServiceDbContext dbContext)
{
_dbContext = dbContext;
}
public async Task SeedAsync(DataSeedContext context)
{
await SeedOrderStatusAsync();
}
private async Task SeedOrderStatusAsync()
{
if (!await _dbContext.Set<OrderStatus>().AnyAsync())
{
await _dbContext.Set<OrderStatus>().AddRangeAsync(OrderStatus.List());
await _dbContext.SaveChangesAsync();
}
}
}

41
services/ordering/src/EShopOnAbp.OrderingService.EntityFrameworkCore/EntityFrameworkCore/OrderingServiceDbContext.cs

@ -1,6 +1,7 @@
using System;
using EShopOnAbp.OrderingService.Orders;
using EShopOnAbp.OrderingService.Orders;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using System;
using Volo.Abp.Data;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore.Modeling;
@ -17,17 +18,17 @@ public class OrderingServiceDbContext : AbpDbContext<OrderingServiceDbContext>,
{
}
protected override void OnModelCreating(ModelBuilder builder)
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(builder);
base.OnModelCreating(modelBuilder);
/* Include modules to your migration db context */
builder.ConfigureOrderingService();
modelBuilder.ConfigureOrderingService();
/* Configure your own tables/entities inside here */
builder.Entity<Order>(b =>
modelBuilder.Entity<Order>(b =>
{
b.ToTable(OrderingServiceDbProperties.DbTablePrefix + "Orders", OrderingServiceDbProperties.DbSchema);
b.ConfigureByConvention(); //auto configure for the base class props
@ -38,16 +39,12 @@ public class OrderingServiceDbContext : AbpDbContext<OrderingServiceDbContext>,
b.OwnsOne(o => o.Address, a => { a.WithOwner(); });
b.OwnsOne(o => o.Buyer, a => { a.WithOwner(); });
b.Property<int>("_orderStatusId").UsePropertyAccessMode(PropertyAccessMode.Field)
.HasColumnName("OrderStatusId")
.IsRequired();
b.HasOne(q => q.OrderStatus).WithMany().HasForeignKey("_orderStatusId");
b.Navigation(q => q.OrderItems).UsePropertyAccessMode(PropertyAccessMode.Property);
b.Property(q => q.OrderStatus).HasConversion(new EnumToStringConverter<OrderStatus>());
});
builder.Entity<OrderItem>(b =>
modelBuilder.Entity<OrderItem>(b =>
{
b.ToTable(OrderingServiceDbProperties.DbTablePrefix + "OrderItems",
OrderingServiceDbProperties.DbSchema);
@ -62,23 +59,5 @@ public class OrderingServiceDbContext : AbpDbContext<OrderingServiceDbContext>,
b.Property(q => q.Units).IsRequired();
b.Property(q => q.PictureUrl).IsRequired(false);
});
builder.Entity<OrderStatus>(b =>
{
b.ToTable(OrderingServiceDbProperties.DbTablePrefix + "OrderStatus",
OrderingServiceDbProperties.DbSchema);
b.ConfigureByConvention(); //auto configure for the base class props
b.HasKey(q => q.Id);
b.Property(q => q.Id)
.HasDefaultValue(1)
.ValueGeneratedNever()
.IsRequired();
b.Property(o => o.Name)
.HasMaxLength(OrderConstants.OrderStatusNameMaxLength)
.IsRequired();
});
}
}

182
services/ordering/src/EShopOnAbp.OrderingService.EntityFrameworkCore/Migrations/20220317123922_RemovedOrderStatusTable.Designer.cs

@ -0,0 +1,182 @@
// <auto-generated />
using System;
using EShopOnAbp.OrderingService.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using Volo.Abp.EntityFrameworkCore;
#nullable disable
namespace EShopOnAbp.OrderingService.Migrations
{
[DbContext(typeof(OrderingServiceDbContext))]
[Migration("20220317123922_RemovedOrderStatusTable")]
partial class RemovedOrderStatusTable
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.PostgreSql)
.HasAnnotation("ProductVersion", "6.0.1")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("EShopOnAbp.OrderingService.Orders.Order", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uuid");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasMaxLength(40)
.HasColumnType("character varying(40)")
.HasColumnName("ConcurrencyStamp");
b.Property<string>("ExtraProperties")
.HasColumnType("text")
.HasColumnName("ExtraProperties");
b.Property<DateTime>("OrderDate")
.HasColumnType("timestamp without time zone");
b.Property<int>("OrderNo")
.HasColumnType("integer");
b.Property<string>("OrderStatus")
.IsRequired()
.HasColumnType("text");
b.Property<string>("PaymentMethod")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("character varying(128)");
b.Property<Guid?>("PaymentRequestId")
.HasColumnType("uuid");
b.Property<string>("PaymentStatus")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.HasKey("Id");
b.ToTable("Orders", (string)null);
});
modelBuilder.Entity("EShopOnAbp.OrderingService.Orders.OrderItem", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uuid");
b.Property<decimal>("Discount")
.HasColumnType("numeric");
b.Property<Guid>("OrderId")
.HasColumnType("uuid");
b.Property<string>("PictureUrl")
.HasColumnType("text");
b.Property<string>("ProductCode")
.IsRequired()
.HasColumnType("text");
b.Property<Guid>("ProductId")
.HasColumnType("uuid");
b.Property<string>("ProductName")
.IsRequired()
.HasColumnType("text");
b.Property<decimal>("UnitPrice")
.HasColumnType("numeric");
b.Property<int>("Units")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("OrderId");
b.ToTable("OrderItems", (string)null);
});
modelBuilder.Entity("EShopOnAbp.OrderingService.Orders.Order", b =>
{
b.OwnsOne("EShopOnAbp.OrderingService.Orders.Address", "Address", b1 =>
{
b1.Property<Guid>("OrderId")
.HasColumnType("uuid");
b1.Property<string>("City")
.HasColumnType("text");
b1.Property<string>("Country")
.HasColumnType("text");
b1.Property<string>("Description")
.HasColumnType("text");
b1.Property<string>("Street")
.HasColumnType("text");
b1.Property<string>("ZipCode")
.HasColumnType("text");
b1.HasKey("OrderId");
b1.ToTable("Orders");
b1.WithOwner()
.HasForeignKey("OrderId");
});
b.OwnsOne("EShopOnAbp.OrderingService.Orders.Buyer", "Buyer", b1 =>
{
b1.Property<Guid>("OrderId")
.HasColumnType("uuid");
b1.Property<string>("Email")
.HasColumnType("text");
b1.Property<Guid?>("Id")
.HasColumnType("uuid");
b1.Property<string>("Name")
.HasColumnType("text");
b1.HasKey("OrderId");
b1.ToTable("Orders");
b1.WithOwner()
.HasForeignKey("OrderId");
});
b.Navigation("Address");
b.Navigation("Buyer");
});
modelBuilder.Entity("EShopOnAbp.OrderingService.Orders.OrderItem", b =>
{
b.HasOne("EShopOnAbp.OrderingService.Orders.Order", null)
.WithMany("OrderItems")
.HasForeignKey("OrderId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("EShopOnAbp.OrderingService.Orders.Order", b =>
{
b.Navigation("OrderItems");
});
#pragma warning restore 612, 618
}
}
}

73
services/ordering/src/EShopOnAbp.OrderingService.EntityFrameworkCore/Migrations/20220317123922_RemovedOrderStatusTable.cs

@ -0,0 +1,73 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace EShopOnAbp.OrderingService.Migrations
{
public partial class RemovedOrderStatusTable : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Orders_OrderStatus_OrderStatusId",
table: "Orders");
migrationBuilder.DropTable(
name: "OrderStatus");
migrationBuilder.DropIndex(
name: "IX_Orders_OrderStatusId",
table: "Orders");
migrationBuilder.DropColumn(
name: "OrderStatusId",
table: "Orders");
migrationBuilder.AddColumn<string>(
name: "OrderStatus",
table: "Orders",
type: "text",
nullable: false,
defaultValue: "");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "OrderStatus",
table: "Orders");
migrationBuilder.AddColumn<int>(
name: "OrderStatusId",
table: "Orders",
type: "integer",
nullable: false,
defaultValue: 0);
migrationBuilder.CreateTable(
name: "OrderStatus",
columns: table => new
{
Id = table.Column<int>(type: "integer", nullable: false, defaultValue: 1),
Name = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_OrderStatus", x => x.Id);
});
migrationBuilder.CreateIndex(
name: "IX_Orders_OrderStatusId",
table: "Orders",
column: "OrderStatusId");
migrationBuilder.AddForeignKey(
name: "FK_Orders_OrderStatus_OrderStatusId",
table: "Orders",
column: "OrderStatusId",
principalTable: "OrderStatus",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
}
}

36
services/ordering/src/EShopOnAbp.OrderingService.EntityFrameworkCore/Migrations/OrderingServiceDbContextModelSnapshot.cs

@ -19,7 +19,7 @@ namespace EShopOnAbp.OrderingService.Migrations
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.PostgreSql)
.HasAnnotation("ProductVersion", "6.0.0")
.HasAnnotation("ProductVersion", "6.0.1")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
@ -45,6 +45,10 @@ namespace EShopOnAbp.OrderingService.Migrations
b.Property<int>("OrderNo")
.HasColumnType("integer");
b.Property<string>("OrderStatus")
.IsRequired()
.HasColumnType("text");
b.Property<string>("PaymentMethod")
.IsRequired()
.HasMaxLength(128)
@ -57,14 +61,8 @@ namespace EShopOnAbp.OrderingService.Migrations
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<int>("_orderStatusId")
.HasColumnType("integer")
.HasColumnName("OrderStatusId");
b.HasKey("Id");
b.HasIndex("_orderStatusId");
b.ToTable("Orders", (string)null);
});
@ -106,30 +104,8 @@ namespace EShopOnAbp.OrderingService.Migrations
b.ToTable("OrderItems", (string)null);
});
modelBuilder.Entity("EShopOnAbp.OrderingService.Orders.OrderStatus", b =>
{
b.Property<int>("Id")
.HasColumnType("integer")
.HasDefaultValue(1);
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.HasKey("Id");
b.ToTable("OrderStatus", (string)null);
});
modelBuilder.Entity("EShopOnAbp.OrderingService.Orders.Order", b =>
{
b.HasOne("EShopOnAbp.OrderingService.Orders.OrderStatus", "OrderStatus")
.WithMany()
.HasForeignKey("_orderStatusId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.OwnsOne("EShopOnAbp.OrderingService.Orders.Address", "Address", b1 =>
{
b1.Property<Guid>("OrderId")
@ -183,8 +159,6 @@ namespace EShopOnAbp.OrderingService.Migrations
b.Navigation("Address");
b.Navigation("Buyer");
b.Navigation("OrderStatus");
});
modelBuilder.Entity("EShopOnAbp.OrderingService.Orders.OrderItem", b =>

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

@ -1,11 +1,11 @@
using System;
using EShopOnAbp.OrderingService.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading;
using System.Threading.Tasks;
using EShopOnAbp.OrderingService.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
@ -20,23 +20,27 @@ public class EfCoreOrderRepository : EfCoreRepository<OrderingServiceDbContext,
{
}
public override async Task<Order> InsertAsync(Order entity, bool autoSave = false,
public async Task<List<Order>> GetOrdersByUserId(
Guid userId,
ISpecification<Order> spec,
bool includeDetails = false,
CancellationToken cancellationToken = default)
{
var newEntity = await base.InsertAsync(entity, autoSave, GetCancellationToken(cancellationToken));
await EnsurePropertyLoadedAsync(newEntity, o => o.OrderStatus, GetCancellationToken(cancellationToken));
return newEntity;
return await (await GetDbSetAsync())
.IncludeDetails(includeDetails)
.Where(q => q.Buyer.Id == userId)
.Where(spec.ToExpression())
.OrderByDescending(o => o.OrderDate)
.ToListAsync(GetCancellationToken(cancellationToken));
}
public async Task<List<Order>> GetOrdersByUserId(
Guid userId,
public async Task<List<Order>> GetOrdersAsync(
ISpecification<Order> spec,
bool includeDetails = false,
CancellationToken cancellationToken = default)
{
return await (await GetDbSetAsync())
.IncludeDetails(includeDetails)
.Where(q => q.Buyer.Id == userId)
.Where(spec.ToExpression())
.OrderByDescending(o => o.OrderDate)
.ToListAsync(GetCancellationToken(cancellationToken));

3
services/ordering/src/EShopOnAbp.OrderingService.EntityFrameworkCore/Orders/OrderEfCoreQueryableExtensions.cs

@ -15,7 +15,6 @@ public static class OrderEfCoreQueryableExtensions
return queryable
.Include(x => x.Address)
.Include(x => x.Buyer)
.Include(x => x.OrderItems)
.Include(x => x.OrderStatus);
.Include(x => x.OrderItems);
}
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save