portal
- Online demo account: guest, password: 123456
- The source address
This chapter content
- Data query interface
Ant Design Pro
Access to theLinux
Under theNginx
The deployment ofJenkins
Continuous integration/deployment
Data query
Create a new dTO directory in modules/stock, create a new stock.dto.ts file, and define the following types:
import { Exclude } from 'class-transformer';
// Stock list type
export class StockDto {
readonly code: string;
readonly name: string;
readonly market: string;
readonly price: string;
readonly peTtm: string;
readonly peTtmAvg: string;
readonly peTtmRate: string;
readonly peTtmMid: string;
@Exclude(a)readonly sourceData: object | null;
us: any;
}
// List query parameter types
export class StockQueryDto {
readonly pageSize: number;
readonly pageIndex: number;
readonly keywords: string;
readonly orderBy: number;
}
export class UserStockDto {
readonly code: string;
}
// Paging data type
export class PageListModel<T> {
totalNum: number; / / the total number of
pageSize: number; / / page number
pageIndex: number; // Current page number
list: T[];
}
Copy the code
Modules /stock
$ nest g s modules/stock
Copy the code
Query method demonstrates TypeOrm QueryBuilder and execution of native SQL statement writing (native SQL recommended to use parameterized query to prevent SQL injection, here only to demonstrate), code as follows:
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, getRepository, getConnection, getManager } from 'typeorm';
import { plainToClass } from 'class-transformer';
import { StockDto, StockQueryDto, PageListModel } from './dto/stock.dto';
import { Stock } from '.. /.. /entities/Stock';
import { UserStock } from '.. /.. /entities/UserStock';
@Injectable(a)export class StockService {
constructor(
@InjectRepository(Stock)
private readonly stStockRepository: Repository<Stock>,
) {}
/** * Select * from the list where you want to query the stock@param param
* @returns* /
async getStockList(
uid: number.param: StockQueryDto,
): Promise<PageListModel<StockDto>> {
const pageIndex = param.pageIndex ? param.pageIndex : 1;
const pageSize = param.pageSize ? param.pageSize : 10;
const orderBy = param.orderBy ? param.orderBy : 0;
let orderByMap: any = {
'st.code': 'DESC'};switch (orderBy) {
case 1:
orderByMap = {
'st.peTtmRate': 'ASC'};break;
case 2:
orderByMap = {
'st.peTtmRate': 'DESC'};break;
default:
break;
}
let keyWordsWhere = 'st.is_delete = 0';
if (param.keywords) {
keyWordsWhere += ' and (st.code like :code or st.name like :name)';
}
const { totalNum } = await getRepository(Stock)
.createQueryBuilder('st')
.select('COUNT(1)'.'totalNum')
.where(keyWordsWhere, {
code: The '%' + param.keywords + The '%'.name: The '%' + param.keywords + The '%',
})
.getRawOne();
const stockList = await getRepository(Stock)
.createQueryBuilder('st')
.select([
'st.id'.'st.code'.'st.name'.'st.pe'.'st.peTtm'.'st.peTtmAvg'.'st.peTtmMid'.'st.peTtmRate'.'st.updateDt',
])
.where(keyWordsWhere, {
code: The '%' + param.keywords + The '%'.name: The '%' + param.keywords + The '%',
})
.orderBy(orderByMap)
.skip((pageIndex - 1) * pageSize)
.take(pageSize)
.getMany();
// Convert type, otherwise we need to add us attribute to Stock
const stocks = plainToClass(StockDto, stockList);
for (let i = 0; i < stocks.length; i++) {
// Query whether the relational table exists
const us = await getConnection()
.createQueryBuilder()
.select(['us.uid'])
.from(UserStock, 'us')
.where('us.uid = :uid and us.code = :code ', {
uid,
code: stocks[i].code,
})
.getOne();
if (us) {
stocks[i].us = us;
} else {
stocks[i].us = null; }}return {
list: stocks,
totalNum,
pageIndex,
pageSize,
};
}
/** * Add optional *@param uid
* @param code
* @returns* /
async addUserStock(uid: number.code: string) :Promise<boolean> {
let result = false;
// Determine whether the relationship already exists
const userStock = await getConnection()
.createQueryBuilder()
.select(['ut.id'])
.from(UserStock, 'ut')
.where('ut.uid = :uid and ut.code = :code ', {
uid,
code,
})
.getOne();
if(! userStock) {const ut = await getConnection()
.createQueryBuilder()
.insert()
.into(UserStock)
.values([{ uid, code }])
.execute();
if (ut) {
result = true; }}return result;
}
/** * Select a list of native SQL statements to execute *@param uid
* @param param
* @returns* /
async getUserStocts(
uid: number.param: StockQueryDto,
): Promise<PageListModel<UserStock>> {
const pageIndex = param.pageIndex ? param.pageIndex : 1;
const pageSize = param.pageSize ? param.pageSize : 10;
const orderBy = param.orderBy ? param.orderBy : 0;
let orderByStr = `order by s.code desc `;
switch (orderBy) {
case 1:
orderByStr = `order by s.pe_ttm_rate asc `;
break;
case 2:
orderByStr = `order by s.pe_ttm_rate desc `;
break;
default:
break;
}
let keyWordsWhere = `ut.uid=${uid} and s.is_delete=0`;
if (param.keywords) {
keyWordsWhere += ` and (s.code like '%${param.keywords}%' or s.name like '%${param.keywords}` % ');
}
const limit = ` limit ${pageSize} offset ${(pageIndex - 1) * pageSize}`;
const manager = getManager();
const total = await manager.query(
`select count(1) as totalNum
from user_stock ut inner join stock s on ut.code=s.code where ${keyWordsWhere}`,);const stockList = await manager.query(
`select ut.uid,ut.code,s.name,
s.pe_ttm as peTtm,
s.pe_ttm_avg as peTtmAvg,
s.pe_ttm_mid as peTtmMid,
s.pe_ttm_rate as peTtmRate,
s.update_dt as updateDt
from user_stock ut inner join stock s on ut.code=s.code where ${keyWordsWhere} ${orderByStr} ${limit}`,);return {
list: stockList,
totalNum: total.length > 0 ? Number(total[0].totalNum) : 0,
pageIndex,
pageSize,
};
}
/** * Delete optional *@param uid
* @param code
* @returns* /
async deleteUserStock(uid: number.code: string) :Promise<boolean> {
let result = false;
const res = await getConnection()
.createQueryBuilder()
.delete()
.from(UserStock)
.where('uid = :uid', { uid })
.andWhere('code = :code', { code })
.execute();
if (res.affected > 0) {
result = true;
}
returnresult; }}Copy the code
Create stock. Controller. Ts:
$ nest g co modules/stock
Copy the code
Modify the stock.controller.ts template code to note the use of UseGuards, scoped to all routes:
import { Controller, Post, Body, UseGuards } from '@nestjs/common';
import { StockService } from './stock.service';
import { StockQueryDto } from './dto/stock.dto';
import { AuthGuard } from '@nestjs/passport';
import { RolesGuard } from '.. /auth/guards/role.guard';
import { User } from '.. /.. /libs/decorators/user.decorator';
import { ProfileInfo } from '.. /.. /libs/decorators/profileInfo';
import { UserStockDto } from './dto/stock.dto';
@Controller('stock')
@UseGuards(AuthGuard('jwt'))
export class StockController {
constructor(private readonly userService: StockService) {}
@UseGuards(RolesGuard)
@Post('list')
async getStockList(@Body() parmas: StockQueryDto, @User() user: ProfileInfo) {
return this.userService.getStockList(user.uid, parmas);
}
@UseGuards(RolesGuard)
@Post('add-choice')
async addChoice(@Body() parmas: UserStockDto, @User() user: ProfileInfo) {
return this.userService.addUserStock(user.uid, parmas.code);
}
@UseGuards(RolesGuard)
@Post('user-list')
async getUserStocts(
@Body() parmas: StockQueryDto,
@User() user: ProfileInfo,
) {
return this.userService.getUserStocts(user.uid, parmas);
}
@UseGuards(RolesGuard)
@Post('delete-choice')
async deleteChoice(@Body() parmas: UserStockDto, @User() user: ProfileInfo) {
return this.userService.deleteUserStock(user.uid, parmas.code); }}Copy the code
To run the program, visit http://localhost:3000/stock/list POST (note with Authorization) parameters are as follows:
{
"pageSize":10."pageIndex":1."keywords":"China"."orderBy":1
}
Copy the code
Return result:
Ant Design Pro
If the API is ready, install Ant Design Pro. For details, see Ant Design Pro
Note the following when the front-end page is initialized in config/config.ts:
- With the proxy, the service is deployed to the server and the proxy configuration needs to correspond to the front end
- The front-end project is deployed in a non-root directory of the server
base
andpublicPath
configuration mock
Open and close, etc
Ant Design Pro uses @umijs/plugin-request as the request library by default, but the request library itself has stopped updating and is not flexible in obtaining Response header attributes, so it overwrites the request class based on AXIos. The HTTP code and business code are classified. For other details, please refer to the source code directly.
Linux
Under theNginx
The deployment of
Service deployment can be based on docker packaging as an image or by building node+ Nginx. The second method is used this time.
Server environment:
$cat /etc/redhat-release CentOS Linux release 7.4.1708 (Core) $node -v v14.16.0 $NPM -v 6.14.11 $pm2 -v 4.5.4Copy the code
Note the following when deploying the Node service:
-
The local build/dist will not run if it is placed directly on the server. Dependencies are not packaged into the output package. It is recommended to install dependencies on the server and package them.
-
Because of the single-thread mechanism of node event loop, pM2 can be used to realize multiple processes.
-
Nginx agent Settings need to be consistent with Ant Design Pro, so that no code changes are required for local development or online deployment.
For front-end deployment, if the directory is not the root directory, see Non-root directory
Jenkins
Continuous integration/deployment
Jenkins installation, please refer to Jenkins installation. Jenkins mainly uses single item construction or pipeline construction. Pipeline construction can be understood as simple and crude definition of multiple multi-step single items through Jenkinsfile, and manual confirmation process can be set between each item to decide whether to proceed to the next step. For more details, please refer to Jenkins documentation. This time, the single-item construction method can meet the requirements. The main implementation process can be divided into the following steps:
- Manually or through the code repository
webhooks
Trigger a build Jenkins
Pulls the specified repository code through a pre-configured repository access token- Execute build commands in a single build using a pre-configured execution environment
- Post-build operations
General network environment code repository is not recommended to choose Github, pull speed will make people doubt life, this code cloud managed code for example reference document code cloud document, according to the above key steps to extract the relevant key configuration:
- The installation
gitee
Plug-ins: System configuration -> Plug-in management -> Searchgitee
–> Click Install
- Global configuration
gitee
Parameters: System Configuration -> System Configuration -> Foundgitee
Configuration -> Enter related configurations
- Click The Certificate token “Add” -> select “Gitee API Token” -> Enter the private token on the code cloud and add the ID for subsequent use;
- The installation
node
Plug-ins: System configuration -> Plug-in management -> Searchnode
–> Click Install - configuration
node
Environment: system configuration – > global tool configuration – > NodeJs — — > click install > enter the alias – > select to install version – > save
- Create a single build: New Task -> Select Freestyle Project -> OK
- General Settings
- Source code management: selection
git
- Add source access credentials
- Trigger the configuration. Select it as required
- Configure build target branch and
webhooks
Secret key (to be entered in the code cloud)
- Binding configuration, select the previous global configuration
node
version
- Build script:
Jenkins
It has its own workspace where the pulled code and the output of executing the build are stored. The following example shows the build environment and the release environment in the same virtual machine, directlycp
File to the destination path and restartpm2
Can. If it is not the same machine, you can use the SSH command to send the file to the target machine.
- Save, manually or submit the code to trigger the test, and check the console log to see why the error occurred.