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 { AuthSchema, JwtPayload } from '@nice/common';
|
||||
import { AuthGuard } from './auth.guard';
|
||||
|
@ -7,7 +22,7 @@ import { z } from 'zod';
|
|||
import { FileValidationErrorType } from './types';
|
||||
@Controller('auth')
|
||||
export class AuthController {
|
||||
private logger = new Logger(AuthController.name)
|
||||
private logger = new Logger(AuthController.name);
|
||||
constructor(private readonly authService: AuthService) {}
|
||||
@Get('file')
|
||||
async authFileRequset(
|
||||
|
@ -18,7 +33,6 @@ export class AuthController {
|
|||
@Headers('host') host: string,
|
||||
@Headers('authorization') authorization: string,
|
||||
) {
|
||||
|
||||
try {
|
||||
const fileRequest = {
|
||||
originalUri,
|
||||
|
@ -26,10 +40,11 @@ export class AuthController {
|
|||
method,
|
||||
queryParams,
|
||||
host,
|
||||
authorization
|
||||
authorization,
|
||||
};
|
||||
|
||||
const authResult = await this.authService.validateFileRequest(fileRequest);
|
||||
const authResult =
|
||||
await this.authService.validateFileRequest(fileRequest);
|
||||
if (!authResult.isValid) {
|
||||
// 使用枚举类型进行错误处理
|
||||
switch (authResult.error) {
|
||||
|
@ -41,7 +56,9 @@ export class AuthController {
|
|||
case FileValidationErrorType.INVALID_TOKEN:
|
||||
throw new UnauthorizedException(authResult.error);
|
||||
default:
|
||||
throw new InternalServerErrorException(authResult.error || FileValidationErrorType.UNKNOWN_ERROR);
|
||||
throw new InternalServerErrorException(
|
||||
authResult.error || FileValidationErrorType.UNKNOWN_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
return {
|
||||
|
@ -51,17 +68,20 @@ export class AuthController {
|
|||
},
|
||||
};
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
@UseGuards(AuthGuard)
|
||||
@Get('user-profile')
|
||||
async getUserProfile(@Req() request: Request) {
|
||||
|
||||
const payload: JwtPayload = (request as any).user;
|
||||
const { staff } = await UserProfileService.instance.getUserProfileById(payload.sub);
|
||||
return staff
|
||||
const { staff } = await UserProfileService.instance.getUserProfileById(
|
||||
payload.sub,
|
||||
);
|
||||
return staff;
|
||||
}
|
||||
@Post('login')
|
||||
async login(@Body() body: z.infer<typeof AuthSchema.signInRequset>) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Logger } from "@nestjs/common";
|
||||
import { UserProfile, db, RowModelRequest } from "@nice/common";
|
||||
import { Logger } from '@nestjs/common';
|
||||
import { UserProfile, db, RowModelRequest } from '@nice/common';
|
||||
import { LogicalCondition, OperatorType, SQLBuilder } from './sql-builder';
|
||||
export interface GetRowOptions {
|
||||
id?: string;
|
||||
|
@ -9,7 +9,15 @@ export interface GetRowOptions {
|
|||
}
|
||||
export abstract class RowModelService {
|
||||
private keywords: Set<string> = new Set([
|
||||
'SELECT', 'FROM', 'WHERE', 'ORDER', 'BY', 'GROUP', 'JOIN', 'AND', 'OR'
|
||||
'SELECT',
|
||||
'FROM',
|
||||
'WHERE',
|
||||
'ORDER',
|
||||
'BY',
|
||||
'GROUP',
|
||||
'JOIN',
|
||||
'AND',
|
||||
'OR',
|
||||
// 添加更多需要引号的关键词
|
||||
]);
|
||||
protected logger = new Logger(this.tableName);
|
||||
|
@ -17,18 +25,23 @@ export abstract class RowModelService {
|
|||
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 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)
|
||||
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),
|
||||
|
@ -37,17 +50,21 @@ export abstract class RowModelService {
|
|||
SQLBuilder.groupBy(this.getGroupByColumns(request)),
|
||||
SQLBuilder.orderBy(this.getOrderByColumns(request)),
|
||||
]);
|
||||
SQL = await this.getRowsSqlWrapper(SQL, request, staff)
|
||||
SQL = await this.getRowsSqlWrapper(SQL, request, staff);
|
||||
|
||||
this.logger.debug('getrows', SQL)
|
||||
this.logger.debug('getrows', SQL);
|
||||
|
||||
const results: any[] = await db?.$queryRawUnsafe(SQL) || [];
|
||||
const results: any[] = (await db?.$queryRawUnsafe(SQL)) || [];
|
||||
|
||||
let rowDataDto = await Promise.all(results.map(row => this.getRowDto(row, staff)))
|
||||
return { rowCount: this.getRowCount(request, rowDataDto) || 0, rowData: rowDataDto };
|
||||
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);
|
||||
|
||||
}
|
||||
}
|
||||
getRowCount(request: RowModelRequest, results: any[]) {
|
||||
|
@ -59,50 +76,75 @@ export abstract class RowModelService {
|
|||
}
|
||||
|
||||
async getRowById(options: GetRowOptions): Promise<any> {
|
||||
const { id, extraCondition = {
|
||||
const {
|
||||
id,
|
||||
extraCondition = {
|
||||
field: `${this.tableName}.deleted_at`,
|
||||
op: "blank",
|
||||
type: "date"
|
||||
}, staff } = options;
|
||||
return this.getSingleRow({ AND: [this.createGetByIdFilter(id!), extraCondition] }, staff);
|
||||
op: 'blank',
|
||||
type: 'date',
|
||||
},
|
||||
staff,
|
||||
} = options;
|
||||
return this.getSingleRow(
|
||||
{ AND: [this.createGetByIdFilter(id!), extraCondition] },
|
||||
staff,
|
||||
);
|
||||
}
|
||||
|
||||
async getRowByIds(options: GetRowOptions): Promise<any[]> {
|
||||
const { ids, extraCondition = {
|
||||
const {
|
||||
ids,
|
||||
extraCondition = {
|
||||
field: `${this.tableName}.deleted_at`,
|
||||
op: "blank",
|
||||
type: "date"
|
||||
}, staff } = options;
|
||||
return this.getMultipleRows({ AND: [this.createGetByIdsFilter(ids!), extraCondition] }, staff);
|
||||
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[] = []
|
||||
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]
|
||||
}
|
||||
]
|
||||
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
|
||||
}))
|
||||
op: 'equals' as OperatorType,
|
||||
value: key,
|
||||
}));
|
||||
}
|
||||
|
||||
const condition: LogicalCondition = {
|
||||
AND: [...groupConditions, ...this.buildFilterConditions(request.filterModel)]
|
||||
}
|
||||
AND: [
|
||||
...groupConditions,
|
||||
...this.buildFilterConditions(request.filterModel),
|
||||
],
|
||||
};
|
||||
|
||||
return condition;
|
||||
}
|
||||
private buildFilterConditions(filterModel: any): LogicalCondition[] {
|
||||
return filterModel
|
||||
? Object.entries(filterModel)?.map(([key, item]) => SQLBuilder.createFilterSql(key === 'ag-Grid-AutoColumn' ? 'name' : key, item))
|
||||
? Object.entries(filterModel)?.map(([key, item]) =>
|
||||
SQLBuilder.createFilterSql(
|
||||
key === 'ag-Grid-AutoColumn' ? 'name' : key,
|
||||
item,
|
||||
),
|
||||
)
|
||||
: [];
|
||||
}
|
||||
|
||||
|
@ -116,21 +158,30 @@ export abstract class RowModelService {
|
|||
}
|
||||
protected createAggSqlForWrapper(request: RowModelRequest) {
|
||||
const { rowGroupCols, valueCols, groupKeys } = request;
|
||||
return valueCols.map(valueCol =>
|
||||
`${valueCol.aggFunc}(${valueCol.field.replace('.', '_')}) AS ${valueCol.field.split('.').join('_')}`
|
||||
return valueCols.map(
|
||||
(valueCol) =>
|
||||
`${valueCol.aggFunc}(${valueCol.field.replace('.', '_')}) AS ${valueCol.field.split('.').join('_')}`,
|
||||
);
|
||||
}
|
||||
protected createGroupingRowSelect(request: RowModelRequest, wrapperSql: boolean = false): string[] {
|
||||
protected createGroupingRowSelect(
|
||||
request: RowModelRequest,
|
||||
wrapperSql: boolean = false,
|
||||
): string[] {
|
||||
const { rowGroupCols, valueCols, groupKeys } = request;
|
||||
const colsToSelect: string[] = [];
|
||||
|
||||
const rowGroupCol = rowGroupCols[groupKeys!.length];
|
||||
if (rowGroupCol) {
|
||||
colsToSelect.push(`${rowGroupCol.field} AS ${rowGroupCol.field.replace('.', '_')}`);
|
||||
colsToSelect.push(
|
||||
`${rowGroupCol.field} AS ${rowGroupCol.field.replace('.', '_')}`,
|
||||
);
|
||||
}
|
||||
colsToSelect.push(...valueCols.map(valueCol =>
|
||||
`${wrapperSql ? "" : valueCol.aggFunc}(${valueCol.field}) AS ${valueCol.field.replace('.', '_')}`
|
||||
));
|
||||
colsToSelect.push(
|
||||
...valueCols.map(
|
||||
(valueCol) =>
|
||||
`${wrapperSql ? '' : valueCol.aggFunc}(${valueCol.field}) AS ${valueCol.field.replace('.', '_')}`,
|
||||
),
|
||||
);
|
||||
|
||||
return colsToSelect;
|
||||
}
|
||||
|
@ -141,17 +192,24 @@ export abstract class RowModelService {
|
|||
: [];
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
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}`);
|
||||
}
|
||||
});
|
||||
|
@ -165,30 +223,41 @@ export abstract class RowModelService {
|
|||
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 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[]> {
|
||||
private async getMultipleRows(
|
||||
condition: LogicalCondition,
|
||||
staff?: UserProfile,
|
||||
): Promise<any[]> {
|
||||
return this.getRowsWithFilters(condition, staff);
|
||||
}
|
||||
|
||||
private async getRowsWithFilters(condition: LogicalCondition, staff?: UserProfile): Promise<any[]> {
|
||||
private async getRowsWithFilters(
|
||||
condition: LogicalCondition,
|
||||
staff?: UserProfile,
|
||||
): Promise<any[]> {
|
||||
try {
|
||||
let SQL = SQLBuilder.join([
|
||||
const SQL = SQLBuilder.join([
|
||||
SQLBuilder.select(this.createUnGroupingRowSelect()),
|
||||
SQLBuilder.from(this.tableName),
|
||||
SQLBuilder.join(this.createJoinSql()),
|
||||
SQLBuilder.where(condition)
|
||||
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)));
|
||||
const rowDataDto = await Promise.all(
|
||||
results.map((item) => this.getRowDto(item, staff)),
|
||||
);
|
||||
|
||||
// rowDataDto = getUniqueItems(rowDataDto, "id")
|
||||
return rowDataDto
|
||||
return rowDataDto;
|
||||
} catch (error) {
|
||||
this.logger.error('Error executing query:', error);
|
||||
throw error;
|
||||
|
@ -202,7 +271,7 @@ export abstract class RowModelService {
|
|||
SQLBuilder.from(this.tableName),
|
||||
SQLBuilder.join(this.createJoinSql(request)),
|
||||
SQLBuilder.where(this.createGetRowsFilters(request)),
|
||||
SQLBuilder.groupBy(this.buildAggGroupBy())
|
||||
SQLBuilder.groupBy(this.buildAggGroupBy()),
|
||||
]);
|
||||
const result: any[] = await db.$queryRawUnsafe(SQL);
|
||||
return result[0];
|
||||
|
@ -215,8 +284,9 @@ export abstract class RowModelService {
|
|||
return [];
|
||||
}
|
||||
protected buildAggSelect(valueCols: any[]): string[] {
|
||||
return valueCols.map(valueCol =>
|
||||
`${valueCol.aggFunc}(${valueCol.field}) AS ${valueCol.field.replace('.', '_')}`
|
||||
return valueCols.map(
|
||||
(valueCol) =>
|
||||
`${valueCol.aggFunc}(${valueCol.field}) AS ${valueCol.field.replace('.', '_')}`,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -224,15 +294,14 @@ export abstract class RowModelService {
|
|||
return {
|
||||
field: `${this.tableName}.id`,
|
||||
value: id,
|
||||
op: "equals"
|
||||
}
|
||||
op: 'equals',
|
||||
};
|
||||
}
|
||||
private createGetByIdsFilter(ids: string[]): LogicalCondition {
|
||||
return {
|
||||
field: `${this.tableName}.id`,
|
||||
value: ids,
|
||||
op: "in"
|
||||
op: 'in',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { db, EnrollmentStatus, PostType } from "@nice/common";
|
||||
import { db, EnrollmentStatus, PostType } from '@nice/common';
|
||||
|
||||
// 更新课程评价统计
|
||||
export async function updateCourseReviewStats(courseId: string) {
|
||||
|
@ -6,18 +6,23 @@ export async function updateCourseReviewStats(courseId: string) {
|
|||
where: {
|
||||
courseId,
|
||||
type: PostType.COURSE_REVIEW,
|
||||
deletedAt: null
|
||||
deletedAt: null,
|
||||
},
|
||||
select: { rating: true }
|
||||
select: { rating: true },
|
||||
});
|
||||
const numberOfReviews = reviews.length;
|
||||
const averageRating = numberOfReviews > 0
|
||||
? reviews.reduce((sum, review) => sum + review.rating, 0) / numberOfReviews
|
||||
const averageRating =
|
||||
numberOfReviews > 0
|
||||
? reviews.reduce((sum, review) => sum + review.rating, 0) /
|
||||
numberOfReviews
|
||||
: 0;
|
||||
|
||||
return db.course.update({
|
||||
where: { id: courseId },
|
||||
data: { numberOfReviews, averageRating }
|
||||
data: {
|
||||
// numberOfReviews,
|
||||
//averageRating,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -26,21 +31,19 @@ export async function updateCourseEnrollmentStats(courseId: string) {
|
|||
const completedEnrollments = await db.enrollment.count({
|
||||
where: {
|
||||
courseId,
|
||||
status: EnrollmentStatus.COMPLETED
|
||||
}
|
||||
status: EnrollmentStatus.COMPLETED,
|
||||
},
|
||||
});
|
||||
const totalEnrollments = await db.enrollment.count({
|
||||
where: { courseId }
|
||||
where: { courseId },
|
||||
});
|
||||
const completionRate = totalEnrollments > 0
|
||||
? (completedEnrollments / totalEnrollments) * 100
|
||||
: 0;
|
||||
const completionRate =
|
||||
totalEnrollments > 0 ? (completedEnrollments / totalEnrollments) * 100 : 0;
|
||||
return db.course.update({
|
||||
where: { id: courseId },
|
||||
data: {
|
||||
numberOfStudents: totalEnrollments,
|
||||
completionRate
|
||||
}
|
||||
// numberOfStudents: totalEnrollments,
|
||||
// completionRate,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue