wangqifan 2 лет назад
Родитель
Сommit
871439b3b7

+ 77 - 0
src/api-users/api-user.controller.ts

@@ -0,0 +1,77 @@
+import {
+    Controller,
+    Put,
+    Get,
+    Body,
+    Param,
+    HttpStatus,
+    NotFoundException,
+    Delete,
+    BadRequestException,
+    Req,
+    Post
+} from '@nestjs/common'
+import { HasRoles } from '../auth/roles.decorator'
+import { Role } from '../model/role.enum'
+import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'
+import { PageRequest } from '../common/dto/page-request'
+import { ApiUser } from './entities/api-user.entity'
+import { ApiUserDto } from './dto/api-user.dto'
+import { ApiUserService } from './api-user.service'
+
+@ApiTags('apiUser')
+@Controller('/apiUser')
+@ApiBearerAuth()
+export class ApiUserController {
+
+    constructor(private readonly apiUserService: ApiUserService) { }
+
+
+    @Post()
+    public async list(@Body() page: PageRequest<ApiUser>) {
+
+        return await this.apiUserService.findAll(page)
+    }
+
+    @Put()
+    @HasRoles(Role.Admin)
+    public async create(@Body() userId: number) {
+        return await this.apiUserService.create(userId)
+    }
+
+    @Get('/get/:id')
+    public async get(@Param('id') id: string) {
+        const chatRole = await this.apiUserService.findById(Number(id))
+        return chatRole
+    }
+
+    @Put('/:id')
+    @HasRoles(Role.Admin)
+    public async update(@Param('id') id: string, @Body() apiUser: ApiUser) {
+        try {
+            await this.apiUserService.update(Number(id), apiUser)
+
+            return {
+                message: 'apiUser Updated successfully!',
+                status: HttpStatus.OK
+            }
+        } catch (err) {
+            throw new BadRequestException(err, 'Error: apiuser not updated!')
+        }
+    }
+
+    @Delete('/:id')
+    @HasRoles(Role.Admin)
+    public async delete(@Param('id') id: number) {
+        try {
+            await this.apiUserService.delete(id)
+            return {
+                message: 'apiuser Deleted successfully!',
+                status: HttpStatus.OK
+            }
+        } catch (err) {
+            throw new BadRequestException(err, 'Error: apiuser not deleted!')
+        }
+    }
+
+}

+ 15 - 0
src/api-users/api-user.module.ts

@@ -0,0 +1,15 @@
+import { Module, forwardRef } from '@nestjs/common'
+import { TypeOrmModule } from '@nestjs/typeorm'
+import { ApiUser } from './entities/api-user.entity'
+import { ApiUserService } from './api-user.service'
+import { ApiUserController } from './api-user.controller'
+import { UsersModule } from '../users/users.module'
+import { Users } from '../users/entities/users.entity'
+
+@Module({
+    imports: [TypeOrmModule.forFeature([ApiUser,Users])],
+    controllers: [ApiUserController],
+    providers: [ApiUserService],
+    exports: [ApiUserService]
+})
+export class ApiUserModule {}

+ 77 - 0
src/api-users/api-user.service.ts

@@ -0,0 +1,77 @@
+import { Injectable, NotFoundException, BadRequestException } from '@nestjs/common'
+import { In, Repository, UpdateResult } from 'typeorm'
+import { InjectRepository } from '@nestjs/typeorm'
+import { ApiType, ApiUser } from './entities/api-user.entity'
+import { ApiUserDto } from './dto/api-user.dto'
+import { paginate, Pagination } from 'nestjs-typeorm-paginate'
+import { PageRequest } from '../common/dto/page-request'
+import { Role } from 'src/model/role.enum'
+import { Users } from '../users/entities/users.entity'
+
+@Injectable()
+export class ApiUserService {
+    constructor(
+        @InjectRepository(ApiUser)
+        private readonly apiUserRepository: Repository<ApiUser>,
+        @InjectRepository(Users)
+        private readonly userRepository: Repository<Users>
+    ) {}
+
+    async findAll(req: PageRequest<ApiUser>): Promise<Pagination<ApiUser>> {
+        return await paginate<ApiUser>(this.apiUserRepository, req.page, req.search)
+    }
+
+    public async create(userId: number) {
+        let user = await this.findUserById(userId)
+        if (!user.roles.includes(Role.Api)) {
+            throw new BadRequestException('Not api user')
+        }
+        let apiUser = new ApiUser()
+        apiUser.name = user.name
+        apiUser.type = ApiType.Customer
+        apiUser.userId = userId
+        const result = await this.apiUserRepository.save(apiUser)
+        return result
+    }
+
+    public async findById(id: number): Promise<ApiUser> {
+        const apiUser = await this.apiUserRepository.findOneBy({
+            id: +id
+        })
+        return apiUser
+    }
+
+    public async findUserById(id: number): Promise<Users> {
+        const user = await this.userRepository.findOneBy({
+            id: +id
+        })
+        return user
+    }
+
+
+    public async findByCode(code: string): Promise<ApiUser> {
+        const apiUser = await this.apiUserRepository.findOneBy({
+            code: code
+        })
+        return apiUser
+    }
+
+    public async update(id: number, apiUser: ApiUser): Promise<UpdateResult> {
+        try {
+            const apiUsers = await this.apiUserRepository.update(
+                {
+                    id: +id
+                },
+                { ...apiUser }
+            )
+            return apiUsers
+        } catch (err) {
+            throw new BadRequestException('api user not updated')
+        }
+    }
+
+    public async delete(id: number): Promise<void> {
+        const apiUsers = await this.findById(id)
+        await this.apiUserRepository.remove(apiUsers)
+    }
+}

+ 17 - 0
src/api-users/dto/api-user.dto.ts

@@ -0,0 +1,17 @@
+import { IsArray, IsNumber, IsString } from 'class-validator'
+import { Label } from '../../label/entities/label.entity'
+
+export class ApiUserDto {
+    @IsString()
+    name: string
+
+    @IsString()
+    describe: string
+
+    @IsNumber()
+    chatted?: number = 0
+
+    @IsNumber()
+    dynamicNumber?: number = 0
+
+}

+ 24 - 0
src/api-users/entities/api-user.entity.ts

@@ -0,0 +1,24 @@
+import { Model } from 'sequelize'
+import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn } from 'typeorm'
+
+export enum ApiType {
+    Customer = 1,
+    Enterprise = 2
+}
+@Entity()
+export class ApiUser {
+    @PrimaryGeneratedColumn()
+    id: number
+
+    @Column()
+    userId: number
+
+    @Column({ nullable: true })
+    name: string
+
+    @Column({ nullable: true })
+    code: string
+
+    @Column({ type: 'enum', enum: ApiType })
+    type: ApiType
+}

+ 4 - 2
src/app.module.ts

@@ -7,6 +7,7 @@ import * as Yup from 'yup'
 import { DevtoolsModule } from '@nestjs/devtools-integration'
 import { APP_FILTER } from '@nestjs/core'
 import { UsersModule } from './users/users.module'
+import { ApiUserModule } from './api-users/api-user.module'
 import { SmsModule } from './sms/sms.module'
 import { AuthModule } from './auth/auth.module'
 import { FileModule } from './file/file.module'
@@ -24,7 +25,7 @@ import { ChatRoleModule } from './chat-role/chat-role.module'
 import { LabelModule } from './label/label.module'
 import { MomentsModule } from './moments/moments.module'
 import { CommentModule } from './comment/comment.module'
-import { ChatPdfModule } from './chat-pdf/chat-pdf.module';
+import { ChatPdfModule } from './chat-pdf/chat-pdf.module'
 import { LikesModule } from './likes/likes.module'
 @Module({
     imports: [
@@ -104,7 +105,8 @@ import { LikesModule } from './likes/likes.module'
         MomentsModule,
         CommentModule,
         ChatPdfModule,
-        LikesModule
+        LikesModule,
+        ApiUserModule
     ],
     controllers: [],
     providers: [

+ 7 - 0
src/chat-pdf/chat-pdf.controller.ts

@@ -14,6 +14,13 @@ export class ChatPdfController {
         return await this.chatPdfService.upload(file)
     }
 
+    @Public()
+    @Post('apiUpload')
+    @UseInterceptors(FileInterceptor('file'))
+    public async apiUpload(@UploadedFile() file: Express.Multer.File,userId: number) {
+        return await this.chatPdfService.upload(file)
+    }
+
     @Public()
     @Post('ask')
     public async ask(@Body() { q, name }) {

+ 2 - 2
src/chat-pdf/chat-pdf.module.ts

@@ -4,9 +4,9 @@ import { ChatPdfController } from './chat-pdf.controller'
 import { TypeOrmModule } from '@nestjs/typeorm'
 import { ConfigModule } from '@nestjs/config'
 import { SysConfigModule } from '../sys-config/sys-config.module'
-
+import { ApiUserModule } from '../api-users/api-user.module'
 @Module({
-    imports: [ConfigModule,SysConfigModule],
+    imports: [ConfigModule, SysConfigModule, ApiUserModule],
     providers: [ChatPdfService],
     controllers: [ChatPdfController]
 })

+ 70 - 2
src/chat-pdf/chat-pdf.service.ts

@@ -1,4 +1,4 @@
-import { Injectable, InternalServerErrorException, Logger } from '@nestjs/common'
+import { BadRequestException, Injectable, InternalServerErrorException, Logger } from '@nestjs/common'
 import * as PdfParse from '@cyber2024/pdf-parse-fixed'
 import { createHash } from 'crypto'
 import { Tiktoken, get_encoding } from '@dqbd/tiktoken'
@@ -11,6 +11,7 @@ import { ConfigService } from '@nestjs/config'
 import { ChatEmbedding } from './entities/chat-embedding.entity'
 import { VECTOR } from './pgvector'
 import { SysConfigService } from '../sys-config/sys-config.service'
+import { ApiUserService } from '../api-users/api-user.service'
 
 function formatEmbedding(embedding: number[]) {
     return `[${embedding.join(', ')}]`
@@ -21,7 +22,11 @@ export class ChatPdfService {
     private readonly tokenizer: Tiktoken
     private readonly openai: OpenAIApi
     private readonly sequelize: Sequelize
-    constructor(private readonly sysConfigService: SysConfigService, private readonly configService: ConfigService) {
+    constructor(
+        private readonly sysConfigService: SysConfigService,
+        private readonly configService: ConfigService,
+        private readonly apiUserService: ApiUserService
+    ) {
         this.tokenizer = get_encoding('cl100k_base')
         this.openai = new OpenAIApi(
             new Configuration({
@@ -252,6 +257,10 @@ export class ChatPdfService {
     }
 
     async customerAsk(q: string, name: string) {
+        let apiUser = this.apiUserService.findByCode(name)
+        if(!apiUser) {
+            throw new BadRequestException("not a enabled api user")
+        }
         const defSysMsg = (await this.sysConfigService.findByName('customer_system_message'))?.value
         const keywords = await this.getKeywords(q)
         const { embedding: keywordEmbedding } = await this.getEmbedding(keywords)
@@ -289,4 +298,63 @@ export class ChatPdfService {
             throw new InternalServerErrorException(error.message)
         }
     }
+
+    public async apiUpload(file: Express.Multer.File, userId: number) {
+        let apiUser = await this.apiUserService.findById(userId)
+        if (!apiUser) {
+            throw new BadRequestException("Can't find api user")
+        }
+        const { originalname, buffer, mimetype } = file
+        const md5 = this.calculateMD5(buffer)
+        const res = await ChatEmbedding.findAll({
+            where: {
+                name: md5
+            }
+        })
+        if (res.length) {
+            return {
+                name: md5
+            }
+        }
+        const pdf = await PdfParse(buffer)
+        const contents = []
+        let paragraph = ''
+        pdf.text
+            .trim()
+            .split('\n')
+            .forEach((line) => {
+                line = line.trim()
+                paragraph += line
+                if (this.isFullSentence(line)) {
+                    contents.push(paragraph)
+                    paragraph = ''
+                }
+            })
+        if (paragraph) {
+            contents.push(paragraph)
+        }
+
+        const embeddings = await this.createEmbeddings(contents)
+        Logger.log(
+            `create embeddings finished, total token usage: ${embeddings.reduce((acc, cur) => acc + cur.token, 0)}`
+        )
+        let i = 0
+        for (const item of embeddings) {
+            try {
+                await ChatEmbedding.create({
+                    name: md5,
+                    text: item.text,
+                    num: i++,
+                    embedding: formatEmbedding(item.embedding)
+                })
+            } catch (error) {
+                Logger.error(error.message)
+            }
+        }
+        apiUser.code = md5
+        this.apiUserService.update(userId, apiUser)
+        return {
+            name: md5
+        }
+    }
 }

+ 2 - 1
src/users/users.module.ts

@@ -8,9 +8,10 @@ import { HashingService } from '../shared/hashing/hashing.service'
 import { SmsModule } from '../sms/sms.module'
 import { UsersAdminController } from './users.admin.controller'
 import { MembershipModule } from '../membership/membership.module'
+import { ApiUserModule } from '../api-users/api-user.module'
 
 @Module({
-    imports: [SmsModule, TypeOrmModule.forFeature([Users]), MembershipModule],
+    imports: [SmsModule, TypeOrmModule.forFeature([Users]), MembershipModule, ApiUserModule],
     controllers: [UsersController, UsersAdminController],
     providers: [
         {

+ 14 - 6
src/users/users.service.ts

@@ -18,6 +18,7 @@ import { HashingService } from '../shared/hashing/hashing.service'
 import { SmsService } from '../sms/sms.service'
 import * as randomstring from 'randomstring'
 import { MembershipService } from '../membership/membership.service'
+import { ApiUserService } from '../api-users/api-user.service'
 import { paginate, Pagination } from 'nestjs-typeorm-paginate'
 import { Role } from '../model/role.enum'
 import { PageRequest } from '../common/dto/page-request'
@@ -29,8 +30,9 @@ export class UsersService {
         private readonly userRepository: Repository<Users>,
         private readonly hashingService: HashingService,
         private readonly smsService: SmsService,
-        private readonly membershipService: MembershipService
-    ) { }
+        private readonly membershipService: MembershipService,
+        private readonly apiUserService: ApiUserService
+    ) {}
 
     async findAll(req: PageRequest<Users>): Promise<Pagination<Users>> {
         return await paginate<Users>(this.userRepository, req.page, req.search)
@@ -61,11 +63,13 @@ export class UsersService {
     }
 
     async findInfoByIds(userIds: number[]): Promise<Users[]> {
-        const userList = await this.userRepository.createQueryBuilder('users')
+        const userList = await this.userRepository
+            .createQueryBuilder('users')
             .select(['users.id', 'users.name'])
             .where({
-                id: In(userIds),
-            }).getMany()
+                id: In(userIds)
+            })
+            .getMany()
 
         return userList
     }
@@ -112,7 +116,11 @@ export class UsersService {
             if (userDto.password) {
                 userDto.password = await this.hashingService.hash(userDto.password)
             }
-            return await this.userRepository.save(userDto)
+            let user = await this.userRepository.save(userDto)
+            if (userDto.roles.includes(Role.Api)) {
+                this.apiUserService.create(user.id)
+            }
+            return user
         } catch (err) {
             throw new InternalServerErrorException(err.message)
         }