add
This commit is contained in:
parent
8c87e39c6a
commit
88b66c50bf
|
@ -1,4 +1,19 @@
|
||||||
import { Controller, Headers, Post, Body, UseGuards, Get, Req, HttpException, HttpStatus, BadRequestException, InternalServerErrorException, NotFoundException, UnauthorizedException, Logger } from '@nestjs/common';
|
import {
|
||||||
|
Controller,
|
||||||
|
Headers,
|
||||||
|
Post,
|
||||||
|
Body,
|
||||||
|
UseGuards,
|
||||||
|
Get,
|
||||||
|
Req,
|
||||||
|
HttpException,
|
||||||
|
HttpStatus,
|
||||||
|
BadRequestException,
|
||||||
|
InternalServerErrorException,
|
||||||
|
NotFoundException,
|
||||||
|
UnauthorizedException,
|
||||||
|
Logger,
|
||||||
|
} from '@nestjs/common';
|
||||||
import { AuthService } from './auth.service';
|
import { AuthService } from './auth.service';
|
||||||
import { AuthSchema, JwtPayload } from '@nice/common';
|
import { AuthSchema, JwtPayload } from '@nice/common';
|
||||||
import { AuthGuard } from './auth.guard';
|
import { AuthGuard } from './auth.guard';
|
||||||
|
@ -7,8 +22,8 @@ import { z } from 'zod';
|
||||||
import { FileValidationErrorType } from './types';
|
import { FileValidationErrorType } from './types';
|
||||||
@Controller('auth')
|
@Controller('auth')
|
||||||
export class AuthController {
|
export class AuthController {
|
||||||
private logger = new Logger(AuthController.name)
|
private logger = new Logger(AuthController.name);
|
||||||
constructor(private readonly authService: AuthService) { }
|
constructor(private readonly authService: AuthService) {}
|
||||||
@Get('file')
|
@Get('file')
|
||||||
async authFileRequset(
|
async authFileRequset(
|
||||||
@Headers('x-original-uri') originalUri: string,
|
@Headers('x-original-uri') originalUri: string,
|
||||||
|
@ -18,7 +33,6 @@ export class AuthController {
|
||||||
@Headers('host') host: string,
|
@Headers('host') host: string,
|
||||||
@Headers('authorization') authorization: string,
|
@Headers('authorization') authorization: string,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const fileRequest = {
|
const fileRequest = {
|
||||||
originalUri,
|
originalUri,
|
||||||
|
@ -26,10 +40,11 @@ export class AuthController {
|
||||||
method,
|
method,
|
||||||
queryParams,
|
queryParams,
|
||||||
host,
|
host,
|
||||||
authorization
|
authorization,
|
||||||
};
|
};
|
||||||
|
|
||||||
const authResult = await this.authService.validateFileRequest(fileRequest);
|
const authResult =
|
||||||
|
await this.authService.validateFileRequest(fileRequest);
|
||||||
if (!authResult.isValid) {
|
if (!authResult.isValid) {
|
||||||
// 使用枚举类型进行错误处理
|
// 使用枚举类型进行错误处理
|
||||||
switch (authResult.error) {
|
switch (authResult.error) {
|
||||||
|
@ -41,7 +56,9 @@ export class AuthController {
|
||||||
case FileValidationErrorType.INVALID_TOKEN:
|
case FileValidationErrorType.INVALID_TOKEN:
|
||||||
throw new UnauthorizedException(authResult.error);
|
throw new UnauthorizedException(authResult.error);
|
||||||
default:
|
default:
|
||||||
throw new InternalServerErrorException(authResult.error || FileValidationErrorType.UNKNOWN_ERROR);
|
throw new InternalServerErrorException(
|
||||||
|
authResult.error || FileValidationErrorType.UNKNOWN_ERROR,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
|
@ -51,17 +68,20 @@ export class AuthController {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
this.logger.verbose(`File request auth failed from ${realIp} reason:${error.message}`)
|
this.logger.verbose(
|
||||||
|
`File request auth failed from ${realIp} reason:${error.message}`,
|
||||||
|
);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@UseGuards(AuthGuard)
|
@UseGuards(AuthGuard)
|
||||||
@Get('user-profile')
|
@Get('user-profile')
|
||||||
async getUserProfile(@Req() request: Request) {
|
async getUserProfile(@Req() request: Request) {
|
||||||
|
|
||||||
const payload: JwtPayload = (request as any).user;
|
const payload: JwtPayload = (request as any).user;
|
||||||
const { staff } = await UserProfileService.instance.getUserProfileById(payload.sub);
|
const { staff } = await UserProfileService.instance.getUserProfileById(
|
||||||
return staff
|
payload.sub,
|
||||||
|
);
|
||||||
|
return staff;
|
||||||
}
|
}
|
||||||
@Post('login')
|
@Post('login')
|
||||||
async login(@Body() body: z.infer<typeof AuthSchema.signInRequset>) {
|
async login(@Body() body: z.infer<typeof AuthSchema.signInRequset>) {
|
||||||
|
|
|
@ -1,238 +1,307 @@
|
||||||
import { Logger } from "@nestjs/common";
|
import { Logger } from '@nestjs/common';
|
||||||
import { UserProfile, db, RowModelRequest } from "@nice/common";
|
import { UserProfile, db, RowModelRequest } from '@nice/common';
|
||||||
import { LogicalCondition, OperatorType, SQLBuilder } from './sql-builder';
|
import { LogicalCondition, OperatorType, SQLBuilder } from './sql-builder';
|
||||||
export interface GetRowOptions {
|
export interface GetRowOptions {
|
||||||
id?: string;
|
id?: string;
|
||||||
ids?: string[];
|
ids?: string[];
|
||||||
extraCondition?: LogicalCondition;
|
extraCondition?: LogicalCondition;
|
||||||
staff?: UserProfile;
|
staff?: UserProfile;
|
||||||
}
|
}
|
||||||
export abstract class RowModelService {
|
export abstract class RowModelService {
|
||||||
private keywords: Set<string> = new Set([
|
private keywords: Set<string> = new Set([
|
||||||
'SELECT', 'FROM', 'WHERE', 'ORDER', 'BY', 'GROUP', 'JOIN', 'AND', 'OR'
|
'SELECT',
|
||||||
// 添加更多需要引号的关键词
|
'FROM',
|
||||||
]);
|
'WHERE',
|
||||||
protected logger = new Logger(this.tableName);
|
'ORDER',
|
||||||
protected constructor(protected tableName: string) { }
|
'BY',
|
||||||
protected async getRowDto(row: any, staff?: UserProfile): Promise<any> {
|
'GROUP',
|
||||||
return row;
|
'JOIN',
|
||||||
|
'AND',
|
||||||
|
'OR',
|
||||||
|
// 添加更多需要引号的关键词
|
||||||
|
]);
|
||||||
|
protected logger = new Logger(this.tableName);
|
||||||
|
protected constructor(protected tableName: string) {}
|
||||||
|
protected async getRowDto(row: any, staff?: UserProfile): Promise<any> {
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
protected async getRowsSqlWrapper(
|
||||||
|
sql: string,
|
||||||
|
request?: RowModelRequest,
|
||||||
|
staff?: UserProfile,
|
||||||
|
) {
|
||||||
|
if (request) return SQLBuilder.join([sql, this.getLimitSql(request)]);
|
||||||
|
return sql;
|
||||||
|
}
|
||||||
|
protected getLimitSql(request: RowModelRequest) {
|
||||||
|
return SQLBuilder.limit(
|
||||||
|
request.endRow - request.startRow,
|
||||||
|
request.startRow,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
abstract createJoinSql(request?: RowModelRequest): string[];
|
||||||
|
async getRows(request: RowModelRequest, staff?: UserProfile) {
|
||||||
|
try {
|
||||||
|
let SQL = SQLBuilder.join([
|
||||||
|
SQLBuilder.select(this.getRowSelectCols(request)),
|
||||||
|
SQLBuilder.from(this.tableName),
|
||||||
|
SQLBuilder.join(this.createJoinSql(request)),
|
||||||
|
SQLBuilder.where(this.createGetRowsFilters(request, staff)),
|
||||||
|
SQLBuilder.groupBy(this.getGroupByColumns(request)),
|
||||||
|
SQLBuilder.orderBy(this.getOrderByColumns(request)),
|
||||||
|
]);
|
||||||
|
SQL = await this.getRowsSqlWrapper(SQL, request, staff);
|
||||||
|
|
||||||
|
this.logger.debug('getrows', SQL);
|
||||||
|
|
||||||
|
const results: any[] = (await db?.$queryRawUnsafe(SQL)) || [];
|
||||||
|
|
||||||
|
const rowDataDto = await Promise.all(
|
||||||
|
results.map((row) => this.getRowDto(row, staff)),
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
rowCount: this.getRowCount(request, rowDataDto) || 0,
|
||||||
|
rowData: rowDataDto,
|
||||||
|
};
|
||||||
|
} catch (error: any) {
|
||||||
|
this.logger.error('Error executing getRows:', error);
|
||||||
}
|
}
|
||||||
protected async getRowsSqlWrapper(sql: string, request?: RowModelRequest, staff?: UserProfile) {
|
}
|
||||||
if (request)
|
getRowCount(request: RowModelRequest, results: any[]) {
|
||||||
return SQLBuilder.join([sql, this.getLimitSql(request)])
|
if (results === null || results === undefined || results.length === 0) {
|
||||||
return sql
|
return null;
|
||||||
}
|
}
|
||||||
protected getLimitSql(request: RowModelRequest) {
|
const currentLastRow = request.startRow + results.length;
|
||||||
return SQLBuilder.limit(request.endRow - request.startRow, request.startRow)
|
return currentLastRow <= request.endRow ? currentLastRow : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getRowById(options: GetRowOptions): Promise<any> {
|
||||||
|
const {
|
||||||
|
id,
|
||||||
|
extraCondition = {
|
||||||
|
field: `${this.tableName}.deleted_at`,
|
||||||
|
op: 'blank',
|
||||||
|
type: 'date',
|
||||||
|
},
|
||||||
|
staff,
|
||||||
|
} = options;
|
||||||
|
return this.getSingleRow(
|
||||||
|
{ AND: [this.createGetByIdFilter(id!), extraCondition] },
|
||||||
|
staff,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getRowByIds(options: GetRowOptions): Promise<any[]> {
|
||||||
|
const {
|
||||||
|
ids,
|
||||||
|
extraCondition = {
|
||||||
|
field: `${this.tableName}.deleted_at`,
|
||||||
|
op: 'blank',
|
||||||
|
type: 'date',
|
||||||
|
},
|
||||||
|
staff,
|
||||||
|
} = options;
|
||||||
|
return this.getMultipleRows(
|
||||||
|
{ AND: [this.createGetByIdsFilter(ids!), extraCondition] },
|
||||||
|
staff,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected createGetRowsFilters(
|
||||||
|
request: RowModelRequest,
|
||||||
|
staff?: UserProfile,
|
||||||
|
): LogicalCondition {
|
||||||
|
let groupConditions: LogicalCondition[] = [];
|
||||||
|
if (this.isDoingTreeGroup(request)) {
|
||||||
|
groupConditions = [
|
||||||
|
{
|
||||||
|
field: 'parent_id',
|
||||||
|
op: 'equals' as OperatorType,
|
||||||
|
value: request.groupKeys[request.groupKeys.length - 1],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
groupConditions = request?.groupKeys?.map((key, index) => ({
|
||||||
|
field: request.rowGroupCols[index].field,
|
||||||
|
op: 'equals' as OperatorType,
|
||||||
|
value: key,
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
abstract createJoinSql(request?: RowModelRequest): string[];
|
|
||||||
async getRows(request: RowModelRequest, staff?: UserProfile) {
|
|
||||||
try {
|
|
||||||
|
|
||||||
let SQL = SQLBuilder.join([
|
const condition: LogicalCondition = {
|
||||||
SQLBuilder.select(this.getRowSelectCols(request)),
|
AND: [
|
||||||
SQLBuilder.from(this.tableName),
|
...groupConditions,
|
||||||
SQLBuilder.join(this.createJoinSql(request)),
|
...this.buildFilterConditions(request.filterModel),
|
||||||
SQLBuilder.where(this.createGetRowsFilters(request, staff)),
|
],
|
||||||
SQLBuilder.groupBy(this.getGroupByColumns(request)),
|
};
|
||||||
SQLBuilder.orderBy(this.getOrderByColumns(request)),
|
|
||||||
]);
|
|
||||||
SQL = await this.getRowsSqlWrapper(SQL, request, staff)
|
|
||||||
|
|
||||||
this.logger.debug('getrows', SQL)
|
return condition;
|
||||||
|
}
|
||||||
|
private buildFilterConditions(filterModel: any): LogicalCondition[] {
|
||||||
|
return filterModel
|
||||||
|
? Object.entries(filterModel)?.map(([key, item]) =>
|
||||||
|
SQLBuilder.createFilterSql(
|
||||||
|
key === 'ag-Grid-AutoColumn' ? 'name' : key,
|
||||||
|
item,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: [];
|
||||||
|
}
|
||||||
|
|
||||||
const results: any[] = await db?.$queryRawUnsafe(SQL) || [];
|
getRowSelectCols(request: RowModelRequest): string[] {
|
||||||
|
return this.isDoingGroup(request)
|
||||||
|
? this.createGroupingRowSelect(request)
|
||||||
|
: this.createUnGroupingRowSelect(request);
|
||||||
|
}
|
||||||
|
protected createUnGroupingRowSelect(request?: RowModelRequest): string[] {
|
||||||
|
return ['*'];
|
||||||
|
}
|
||||||
|
protected createAggSqlForWrapper(request: RowModelRequest) {
|
||||||
|
const { rowGroupCols, valueCols, groupKeys } = request;
|
||||||
|
return valueCols.map(
|
||||||
|
(valueCol) =>
|
||||||
|
`${valueCol.aggFunc}(${valueCol.field.replace('.', '_')}) AS ${valueCol.field.split('.').join('_')}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
protected createGroupingRowSelect(
|
||||||
|
request: RowModelRequest,
|
||||||
|
wrapperSql: boolean = false,
|
||||||
|
): string[] {
|
||||||
|
const { rowGroupCols, valueCols, groupKeys } = request;
|
||||||
|
const colsToSelect: string[] = [];
|
||||||
|
|
||||||
let rowDataDto = await Promise.all(results.map(row => this.getRowDto(row, staff)))
|
const rowGroupCol = rowGroupCols[groupKeys!.length];
|
||||||
return { rowCount: this.getRowCount(request, rowDataDto) || 0, rowData: rowDataDto };
|
if (rowGroupCol) {
|
||||||
} catch (error: any) {
|
colsToSelect.push(
|
||||||
this.logger.error('Error executing getRows:', error);
|
`${rowGroupCol.field} AS ${rowGroupCol.field.replace('.', '_')}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
colsToSelect.push(
|
||||||
|
...valueCols.map(
|
||||||
|
(valueCol) =>
|
||||||
|
`${wrapperSql ? '' : valueCol.aggFunc}(${valueCol.field}) AS ${valueCol.field.replace('.', '_')}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return colsToSelect;
|
||||||
|
}
|
||||||
|
|
||||||
|
getGroupByColumns(request: RowModelRequest): string[] {
|
||||||
|
return this.isDoingGroup(request)
|
||||||
|
? [request.rowGroupCols[request.groupKeys!.length]?.field]
|
||||||
|
: [];
|
||||||
|
}
|
||||||
|
|
||||||
|
getOrderByColumns(request: RowModelRequest): string[] {
|
||||||
|
const { sortModel, rowGroupCols, groupKeys } = request;
|
||||||
|
const grouping = this.isDoingGroup(request);
|
||||||
|
const sortParts: string[] = [];
|
||||||
|
|
||||||
|
if (sortModel) {
|
||||||
|
const groupColIds = rowGroupCols
|
||||||
|
.map((groupCol) => groupCol.id)
|
||||||
|
.slice(0, groupKeys.length + 1);
|
||||||
|
sortModel.forEach((item) => {
|
||||||
|
if (
|
||||||
|
!grouping ||
|
||||||
|
(groupColIds.indexOf(item.colId) >= 0 &&
|
||||||
|
rowGroupCols[groupKeys.length].field === item.colId)
|
||||||
|
) {
|
||||||
|
const colId = this.keywords.has(item.colId.toUpperCase())
|
||||||
|
? `"${item.colId}"`
|
||||||
|
: item.colId;
|
||||||
|
sortParts.push(`${colId} ${item.sort}`);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
getRowCount(request: RowModelRequest, results: any[]) {
|
|
||||||
if (results === null || results === undefined || results.length === 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const currentLastRow = request.startRow + results.length;
|
|
||||||
return currentLastRow <= request.endRow ? currentLastRow : -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getRowById(options: GetRowOptions): Promise<any> {
|
return sortParts;
|
||||||
const { id, extraCondition = {
|
}
|
||||||
field: `${this.tableName}.deleted_at`,
|
isDoingGroup(requset: RowModelRequest): boolean {
|
||||||
op: "blank",
|
return requset.rowGroupCols.length > requset.groupKeys.length;
|
||||||
type: "date"
|
}
|
||||||
}, staff } = options;
|
isDoingTreeGroup(requset: RowModelRequest): boolean {
|
||||||
return this.getSingleRow({ AND: [this.createGetByIdFilter(id!), extraCondition] }, staff);
|
return requset.rowGroupCols.length === 0 && requset.groupKeys.length > 0;
|
||||||
}
|
}
|
||||||
|
private async getSingleRow(
|
||||||
|
condition: LogicalCondition,
|
||||||
|
staff?: UserProfile,
|
||||||
|
): Promise<any> {
|
||||||
|
const results = await this.getRowsWithFilters(condition, staff);
|
||||||
|
return results[0];
|
||||||
|
}
|
||||||
|
private async getMultipleRows(
|
||||||
|
condition: LogicalCondition,
|
||||||
|
staff?: UserProfile,
|
||||||
|
): Promise<any[]> {
|
||||||
|
return this.getRowsWithFilters(condition, staff);
|
||||||
|
}
|
||||||
|
|
||||||
async getRowByIds(options: GetRowOptions): Promise<any[]> {
|
private async getRowsWithFilters(
|
||||||
const { ids, extraCondition = {
|
condition: LogicalCondition,
|
||||||
field: `${this.tableName}.deleted_at`,
|
staff?: UserProfile,
|
||||||
op: "blank",
|
): Promise<any[]> {
|
||||||
type: "date"
|
try {
|
||||||
}, staff } = options;
|
const SQL = SQLBuilder.join([
|
||||||
return this.getMultipleRows({ AND: [this.createGetByIdsFilter(ids!), extraCondition] }, staff);
|
SQLBuilder.select(this.createUnGroupingRowSelect()),
|
||||||
}
|
SQLBuilder.from(this.tableName),
|
||||||
|
SQLBuilder.join(this.createJoinSql()),
|
||||||
|
SQLBuilder.where(condition),
|
||||||
|
]);
|
||||||
|
|
||||||
protected createGetRowsFilters(request: RowModelRequest, staff?: UserProfile): LogicalCondition {
|
// this.logger.debug(SQL)
|
||||||
let groupConditions: LogicalCondition[] = []
|
const results: any[] = await db.$queryRawUnsafe(SQL);
|
||||||
if (this.isDoingTreeGroup(request)) {
|
|
||||||
groupConditions = [
|
|
||||||
{
|
|
||||||
field: 'parent_id',
|
|
||||||
op: "equals" as OperatorType,
|
|
||||||
value: request.groupKeys[request.groupKeys.length - 1]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
} else {
|
|
||||||
groupConditions = request?.groupKeys?.map((key, index) => ({
|
|
||||||
field: request.rowGroupCols[index].field,
|
|
||||||
op: "equals" as OperatorType,
|
|
||||||
value: key
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
const condition: LogicalCondition = {
|
const rowDataDto = await Promise.all(
|
||||||
AND: [...groupConditions, ...this.buildFilterConditions(request.filterModel)]
|
results.map((item) => this.getRowDto(item, staff)),
|
||||||
}
|
);
|
||||||
|
|
||||||
return condition;
|
// rowDataDto = getUniqueItems(rowDataDto, "id")
|
||||||
}
|
return rowDataDto;
|
||||||
private buildFilterConditions(filterModel: any): LogicalCondition[] {
|
} catch (error) {
|
||||||
return filterModel
|
this.logger.error('Error executing query:', error);
|
||||||
? Object.entries(filterModel)?.map(([key, item]) => SQLBuilder.createFilterSql(key === 'ag-Grid-AutoColumn' ? 'name' : key, item))
|
throw error;
|
||||||
: [];
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
getRowSelectCols(request: RowModelRequest): string[] {
|
async getAggValues(request: RowModelRequest) {
|
||||||
return this.isDoingGroup(request)
|
try {
|
||||||
? this.createGroupingRowSelect(request)
|
const SQL = SQLBuilder.join([
|
||||||
: this.createUnGroupingRowSelect(request);
|
SQLBuilder.select(this.buildAggSelect(request.valueCols)),
|
||||||
|
SQLBuilder.from(this.tableName),
|
||||||
|
SQLBuilder.join(this.createJoinSql(request)),
|
||||||
|
SQLBuilder.where(this.createGetRowsFilters(request)),
|
||||||
|
SQLBuilder.groupBy(this.buildAggGroupBy()),
|
||||||
|
]);
|
||||||
|
const result: any[] = await db.$queryRawUnsafe(SQL);
|
||||||
|
return result[0];
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error('Error executing query:', error);
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
protected createUnGroupingRowSelect(request?: RowModelRequest): string[] {
|
}
|
||||||
return ['*'];
|
protected buildAggGroupBy(): string[] {
|
||||||
}
|
return [];
|
||||||
protected createAggSqlForWrapper(request: RowModelRequest) {
|
}
|
||||||
const { rowGroupCols, valueCols, groupKeys } = request;
|
protected buildAggSelect(valueCols: any[]): string[] {
|
||||||
return valueCols.map(valueCol =>
|
return valueCols.map(
|
||||||
`${valueCol.aggFunc}(${valueCol.field.replace('.', '_')}) AS ${valueCol.field.split('.').join('_')}`
|
(valueCol) =>
|
||||||
);
|
`${valueCol.aggFunc}(${valueCol.field}) AS ${valueCol.field.replace('.', '_')}`,
|
||||||
}
|
);
|
||||||
protected createGroupingRowSelect(request: RowModelRequest, wrapperSql: boolean = false): string[] {
|
}
|
||||||
const { rowGroupCols, valueCols, groupKeys } = request;
|
|
||||||
const colsToSelect: string[] = [];
|
|
||||||
|
|
||||||
const rowGroupCol = rowGroupCols[groupKeys!.length];
|
private createGetByIdFilter(id: string): LogicalCondition {
|
||||||
if (rowGroupCol) {
|
return {
|
||||||
colsToSelect.push(`${rowGroupCol.field} AS ${rowGroupCol.field.replace('.', '_')}`);
|
field: `${this.tableName}.id`,
|
||||||
}
|
value: id,
|
||||||
colsToSelect.push(...valueCols.map(valueCol =>
|
op: 'equals',
|
||||||
`${wrapperSql ? "" : valueCol.aggFunc}(${valueCol.field}) AS ${valueCol.field.replace('.', '_')}`
|
};
|
||||||
));
|
}
|
||||||
|
private createGetByIdsFilter(ids: string[]): LogicalCondition {
|
||||||
return colsToSelect;
|
return {
|
||||||
}
|
field: `${this.tableName}.id`,
|
||||||
|
value: ids,
|
||||||
getGroupByColumns(request: RowModelRequest): string[] {
|
op: 'in',
|
||||||
return this.isDoingGroup(request)
|
};
|
||||||
? [request.rowGroupCols[request.groupKeys!.length]?.field]
|
}
|
||||||
: [];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
getOrderByColumns(request: RowModelRequest): string[] {
|
|
||||||
const { sortModel, rowGroupCols, groupKeys } = request;
|
|
||||||
const grouping = this.isDoingGroup(request);
|
|
||||||
const sortParts: string[] = [];
|
|
||||||
|
|
||||||
if (sortModel) {
|
|
||||||
const groupColIds = rowGroupCols.map(groupCol => groupCol.id).slice(0, groupKeys.length + 1);
|
|
||||||
sortModel.forEach(item => {
|
|
||||||
if (!grouping || (groupColIds.indexOf(item.colId) >= 0 && rowGroupCols[groupKeys.length].field === item.colId)) {
|
|
||||||
const colId = this.keywords.has(item.colId.toUpperCase()) ? `"${item.colId}"` : item.colId;
|
|
||||||
sortParts.push(`${colId} ${item.sort}`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return sortParts;
|
|
||||||
}
|
|
||||||
isDoingGroup(requset: RowModelRequest): boolean {
|
|
||||||
return requset.rowGroupCols.length > requset.groupKeys.length;
|
|
||||||
}
|
|
||||||
isDoingTreeGroup(requset: RowModelRequest): boolean {
|
|
||||||
return requset.rowGroupCols.length === 0 && requset.groupKeys.length > 0;
|
|
||||||
}
|
|
||||||
private async getSingleRow(condition: LogicalCondition, staff?: UserProfile): Promise<any> {
|
|
||||||
const results = await this.getRowsWithFilters(condition, staff)
|
|
||||||
return results[0]
|
|
||||||
}
|
|
||||||
private async getMultipleRows(condition: LogicalCondition, staff?: UserProfile): Promise<any[]> {
|
|
||||||
return this.getRowsWithFilters(condition, staff);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async getRowsWithFilters(condition: LogicalCondition, staff?: UserProfile): Promise<any[]> {
|
|
||||||
try {
|
|
||||||
let SQL = SQLBuilder.join([
|
|
||||||
SQLBuilder.select(this.createUnGroupingRowSelect()),
|
|
||||||
SQLBuilder.from(this.tableName),
|
|
||||||
SQLBuilder.join(this.createJoinSql()),
|
|
||||||
SQLBuilder.where(condition)
|
|
||||||
]);
|
|
||||||
|
|
||||||
// this.logger.debug(SQL)
|
|
||||||
const results: any[] = await db.$queryRawUnsafe(SQL);
|
|
||||||
|
|
||||||
let rowDataDto = await Promise.all(results.map(item => this.getRowDto(item, staff)));
|
|
||||||
|
|
||||||
// rowDataDto = getUniqueItems(rowDataDto, "id")
|
|
||||||
return rowDataDto
|
|
||||||
} catch (error) {
|
|
||||||
this.logger.error('Error executing query:', error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getAggValues(request: RowModelRequest) {
|
|
||||||
try {
|
|
||||||
const SQL = SQLBuilder.join([
|
|
||||||
SQLBuilder.select(this.buildAggSelect(request.valueCols)),
|
|
||||||
SQLBuilder.from(this.tableName),
|
|
||||||
SQLBuilder.join(this.createJoinSql(request)),
|
|
||||||
SQLBuilder.where(this.createGetRowsFilters(request)),
|
|
||||||
SQLBuilder.groupBy(this.buildAggGroupBy())
|
|
||||||
]);
|
|
||||||
const result: any[] = await db.$queryRawUnsafe(SQL);
|
|
||||||
return result[0];
|
|
||||||
} catch (error) {
|
|
||||||
this.logger.error('Error executing query:', error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
protected buildAggGroupBy(): string[] {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
protected buildAggSelect(valueCols: any[]): string[] {
|
|
||||||
return valueCols.map(valueCol =>
|
|
||||||
`${valueCol.aggFunc}(${valueCol.field}) AS ${valueCol.field.replace('.', '_')}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private createGetByIdFilter(id: string): LogicalCondition {
|
|
||||||
return {
|
|
||||||
field: `${this.tableName}.id`,
|
|
||||||
value: id,
|
|
||||||
op: "equals"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private createGetByIdsFilter(ids: string[]): LogicalCondition {
|
|
||||||
return {
|
|
||||||
field: `${this.tableName}.id`,
|
|
||||||
value: ids,
|
|
||||||
op: "in"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,46 +1,49 @@
|
||||||
import { db, EnrollmentStatus, PostType } from "@nice/common";
|
import { db, EnrollmentStatus, PostType } from '@nice/common';
|
||||||
|
|
||||||
// 更新课程评价统计
|
// 更新课程评价统计
|
||||||
export async function updateCourseReviewStats(courseId: string) {
|
export async function updateCourseReviewStats(courseId: string) {
|
||||||
const reviews = await db.post.findMany({
|
const reviews = await db.post.findMany({
|
||||||
where: {
|
where: {
|
||||||
courseId,
|
courseId,
|
||||||
type: PostType.COURSE_REVIEW,
|
type: PostType.COURSE_REVIEW,
|
||||||
deletedAt: null
|
deletedAt: null,
|
||||||
},
|
},
|
||||||
select: { rating: true }
|
select: { rating: true },
|
||||||
});
|
});
|
||||||
const numberOfReviews = reviews.length;
|
const numberOfReviews = reviews.length;
|
||||||
const averageRating = numberOfReviews > 0
|
const averageRating =
|
||||||
? reviews.reduce((sum, review) => sum + review.rating, 0) / numberOfReviews
|
numberOfReviews > 0
|
||||||
: 0;
|
? reviews.reduce((sum, review) => sum + review.rating, 0) /
|
||||||
|
numberOfReviews
|
||||||
|
: 0;
|
||||||
|
|
||||||
return db.course.update({
|
return db.course.update({
|
||||||
where: { id: courseId },
|
where: { id: courseId },
|
||||||
data: { numberOfReviews, averageRating }
|
data: {
|
||||||
});
|
// numberOfReviews,
|
||||||
|
//averageRating,
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新课程注册统计
|
// 更新课程注册统计
|
||||||
export async function updateCourseEnrollmentStats(courseId: string) {
|
export async function updateCourseEnrollmentStats(courseId: string) {
|
||||||
const completedEnrollments = await db.enrollment.count({
|
const completedEnrollments = await db.enrollment.count({
|
||||||
where: {
|
where: {
|
||||||
courseId,
|
courseId,
|
||||||
status: EnrollmentStatus.COMPLETED
|
status: EnrollmentStatus.COMPLETED,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
const totalEnrollments = await db.enrollment.count({
|
const totalEnrollments = await db.enrollment.count({
|
||||||
where: { courseId }
|
where: { courseId },
|
||||||
});
|
});
|
||||||
const completionRate = totalEnrollments > 0
|
const completionRate =
|
||||||
? (completedEnrollments / totalEnrollments) * 100
|
totalEnrollments > 0 ? (completedEnrollments / totalEnrollments) * 100 : 0;
|
||||||
: 0;
|
return db.course.update({
|
||||||
return db.course.update({
|
where: { id: courseId },
|
||||||
where: { id: courseId },
|
data: {
|
||||||
data: {
|
// numberOfStudents: totalEnrollments,
|
||||||
numberOfStudents: totalEnrollments,
|
// completionRate,
|
||||||
completionRate
|
},
|
||||||
}
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue