xiongzhu il y a 2 ans
Parent
commit
7420873fec

+ 2 - 2
src/membership/membership.service.ts

@@ -80,11 +80,11 @@ export class MembershipService {
         }
     }
 
-    async getMembership(userId: number) {
+    async getMembership(userId: number, autoTrial: boolean = true) {
         const membership = await this.memberShipRepository.findOneBy({
             userId: userId
         })
-        if (!membership) {
+        if (!membership && autoTrial) {
             return await this.trial(userId)
         }
         return membership

+ 17 - 0
src/paper/entities/ref-search-record.entity.ts

@@ -0,0 +1,17 @@
+import { Column, CreateDateColumn, Entity, PrimaryGeneratedColumn } from "typeorm";
+
+@Entity()
+export class RefSearchRecord {
+
+    @PrimaryGeneratedColumn()
+    id: number
+
+    @CreateDateColumn()
+    createdAt: Date
+
+    @Column()
+    userId: number
+
+    @Column()
+    title: string
+}

+ 0 - 5
src/paper/paper.controller.ts

@@ -46,9 +46,4 @@ export class PaperController {
     async genChapters(@Body() body) {
         return await this.paperService.genChapters(body.id, body.description)
     }
-
-    @Get('/searchReferences')
-    async searchReferences(@Query('title') title) {
-        return await this.paperService.searchReferences(title)
-    }
 }

+ 5 - 2
src/paper/paper.module.ts

@@ -4,10 +4,13 @@ import { PaperService } from './paper.service'
 import { TypeOrmModule } from '@nestjs/typeorm'
 import { PaperOrder } from './entities/paper-order.entity'
 import { PaperGenResult } from './entities/paper-gen-result.entity'
+import { RefSearchRecord } from './entities/ref-search-record.entity'
+import { MembershipModule } from 'src/membership/membership.module'
+import { RefController } from './ref.controller'
 
 @Module({
-    imports: [TypeOrmModule.forFeature([PaperOrder, PaperGenResult])],
-    controllers: [PaperController],
+    imports: [TypeOrmModule.forFeature([PaperOrder, PaperGenResult, RefSearchRecord]), MembershipModule],
+    controllers: [PaperController, RefController],
     providers: [PaperService]
 })
 export class PaperModule {}

+ 126 - 59
src/paper/paper.service.ts

@@ -1,7 +1,7 @@
-import { Injectable, InternalServerErrorException, Logger, OnModuleInit } from '@nestjs/common'
+import { HttpException, Injectable, InternalServerErrorException, Logger, OnModuleInit } from '@nestjs/common'
 import { InjectRepository } from '@nestjs/typeorm'
 import { PaperOrder, PaperOrderStatus } from './entities/paper-order.entity'
-import { Repository, Like } from 'typeorm'
+import { Repository, Like, Between } from 'typeorm'
 import { PaperGenResult } from './entities/paper-gen-result.entity'
 import { Pagination, paginate } from 'nestjs-typeorm-paginate'
 import { PageRequest } from 'src/common/dto/page-request'
@@ -14,6 +14,10 @@ import { HumanMessage } from 'langchain/schema'
 import axios from 'axios'
 import * as qs from 'qs'
 import { load } from 'cheerio'
+import { parse } from 'date-fns'
+import { RefSearchRecord } from './entities/ref-search-record.entity'
+import { MembershipService } from 'src/membership/membership.service'
+import { startOfDay, endOfDay } from 'date-fns'
 
 @Injectable()
 export class PaperService implements OnModuleInit {
@@ -21,7 +25,10 @@ export class PaperService implements OnModuleInit {
         @InjectRepository(PaperOrder)
         private readonly paperOrderRepository: Repository<PaperOrder>,
         @InjectRepository(PaperGenResult)
-        private readonly paperGenResultRepository: Repository<PaperGenResult>
+        private readonly paperGenResultRepository: Repository<PaperGenResult>,
+        @InjectRepository(RefSearchRecord)
+        private readonly refSearchRecordRepository: Repository<RefSearchRecord>,
+        private readonly membershipService: MembershipService
     ) {}
 
     async onModuleInit() {
@@ -122,15 +129,24 @@ export class PaperService implements OnModuleInit {
         }
     }
 
-    async searchReferences(title: string) {
-        // const { llm } = createLLM('gpt-3.5-turbo')
-        // const { content } = await llm.call([
-        //     new HumanMessage(
-        //         `您需要从陈述或问题中提取关键词,并返回由逗号分隔的一系列关键词。例如,如果问题是“什么是人工智能?”,则关键词可能是“人工智能,AI,人工智能,机器学习,深度学习”。\n\n问题:${title}\n关键词:`
-        //     )
-        // ])
-        // const keywords = content.replace(/[,,]/g, ' ')
-        const keywords = title
+    async searchReferences(userId: number, title: string) {
+        let record = await this.refSearchRecordRepository.findOneBy({
+            userId,
+            createdAt: Between(startOfDay(new Date()), endOfDay(new Date()))
+        })
+        if (record) {
+            const member = await this.membershipService.getMembership(userId, false)
+            if (!member || member.isExpired) {
+                throw new InternalServerErrorException('USAGE_LIMIT_EXCEEDED')
+            }
+        }
+
+        const { llm } = createLLM('gpt-3.5-turbo')
+        const { content } = await llm.call([
+            new HumanMessage(`你要从我给你的论文标题中提取2-3个由逗号分隔的主要关键词。\n\n标题:${title}\n关键词:`)
+        ])
+        const keywords = content.replace(/,/g, ',').split(',')
+        // const keywords = title
         const http = axios.create({
             headers: {
                 Host: 'kns.cnki.net',
@@ -151,55 +167,106 @@ export class PaperService implements OnModuleInit {
                 Cookie: 'Ecp_notFirstLogin=8TNR2E; KNS3COOKIE=1705297590.529.38190.813537|b25e41a932fd162af3b8c5cff4059fc3; Ecp_ClientId=m240115134600532586; Ecp_IpLoginFail=240115180.111.242.2; SID_sug=128008; SID_kns_new=kns25128005; Ecp_ClientIp=180.111.242.2; SID_restapi=018105; LID=WEEvREcwSlJHSldTTEYyTE5mZ3VrVmtob3VPdjBpS29QOWl1NG1pRVJqWT0=$9A4hF_YAuvQ5obgVAqNKPCYcEjKensW4IQMovwHtwkF4VYPoHbKxJw!!; Ecp_session=1; Ecp_showrealname=1; Ecp_LoginStuts={"IsAutoLogin":false,"UserName":"15077886171","ShowName":"15077886171","UserType":"jf","BUserName":"","BShowName":"","BUserType":"","r":"8TNR2E","Members":[]}; Ecp_loginuserjf=15077886171; c_m_LinID=LinID=WEEvREcwSlJHSldTTEYyTE5mZ3VrVmtob3VPdjBpS29QOWl1NG1pRVJqWT0=$9A4hF_YAuvQ5obgVAqNKPCYcEjKensW4IQMovwHtwkF4VYPoHbKxJw!!&ot=02%2F14%2F2024%2013%3A52%3A01; c_m_expire=2024-02-14%2013%3A52%3A01; dblang=both'
             }
         })
-        const res = await http.post(
-            'https://kns.cnki.net/kns8s/brief/grid',
-
-            qs.stringify({
-                boolSearch: true,
-                QueryJson: `{"Platform":"","Resource":"CROSSDB","Classid":"WD0FTY92","Products":"","QNode":{"QGroup":[{"Key":"Subject","Title":"","Logic":0,"Items":[{"Field":"SU","Value":"${keywords}","Operator":"TOPRANK","Logic":0}],"ChildItems":[]}]},"ExScope":1,"SearchType":2,"Rlang":"CHINESE","KuaKuCode":"YSTT4HG0,LSTPFY1C,JUP3MUPD,MPMFIG1A,EMRPGLPA,WQ0UVIAA,BLZOG7CK,PWFIRAGL,NN3FJMUV,NLBO1Z6R"}`,
-                pageNum: 1,
-                pageSize: 20,
-                dstyle: 'listmode',
-                boolSortSearch: false,
-                productStr:
-                    'YSTT4HG0,LSTPFY1C,RMJLXHZ3,JQIRZIYA,JUP3MUPD,1UR4K4HZ,BPBAFJ5S,R79MZMCB,MPMFIG1A,EMRPGLPA,J708GVCE,ML4DRIDX,WQ0UVIAA,NB3BWEHK,XVLO76FD,HR1YT1Z9,BLZOG7CK,PWFIRAGL,NN3FJMUV,NLBO1Z6R,',
-                aside: `主题:${keywords}`,
-                searchFrom: '资源范围:总库',
-                CurPage: 1
-            })
-        )
-        const $ = load(res.data)
-        const trs = $('tr')
-        return (
+
+        let en = []
+        let zh = []
+        for (let kw of keywords) {
+            const res = await http.post(
+                'https://kns.cnki.net/kns8s/brief/grid',
+                qs.stringify({
+                    boolSearch: true,
+                    QueryJson: `{"Platform":"","Resource":"CROSSDB","Classid":"WD0FTY92","Products":"","QNode":{"QGroup":[{"Key":"Subject","Title":"","Logic":0,"Items":[{"Field":"SU","Value":"${kw}","Operator":"TOPRANK","Logic":0}],"ChildItems":[]}]},"ExScope":1,"SearchType":2,"Rlang":"CHINESE","KuaKuCode":"YSTT4HG0,LSTPFY1C,JUP3MUPD,MPMFIG1A,EMRPGLPA,WQ0UVIAA,BLZOG7CK,PWFIRAGL,NN3FJMUV,NLBO1Z6R"}`,
+                    pageNum: 1,
+                    pageSize: 20,
+                    dstyle: 'listmode',
+                    boolSortSearch: false,
+                    productStr:
+                        'YSTT4HG0,LSTPFY1C,RMJLXHZ3,JQIRZIYA,JUP3MUPD,1UR4K4HZ,BPBAFJ5S,R79MZMCB,MPMFIG1A,EMRPGLPA,J708GVCE,ML4DRIDX,WQ0UVIAA,NB3BWEHK,XVLO76FD,HR1YT1Z9,BLZOG7CK,PWFIRAGL,NN3FJMUV,NLBO1Z6R,',
+                    aside: `主题:${kw}`,
+                    searchFrom: '资源范围:总库',
+                    CurPage: 1
+                })
+            )
+            zh = zh.concat(this.parseRefHtml(res.data))
+
+            const res1 = await http.post(
+                'https://kns.cnki.net/kns8s/brief/grid',
+                qs.stringify({
+                    boolSearch: true,
+                    QueryJson: `{"Platform":"","Resource":"CROSSDB","Classid":"WD0FTY92","Products":"","QNode":{"QGroup":[{"Key":"Subject","Title":"","Logic":0,"Items":[{"Field":"SU","Value":"${kw}","Operator":"TOPRANK","Logic":0}],"ChildItems":[]},{"Key":"SCDBGroup","Title":"","Logic":0,"Items":[],"ChildItems":[{"Key":"LG","Title":"","Logic":0,"Items":[{"Key":"EN","Title":"英语","Logic":1,"Field":"LG","Operator":"DEFAULT","Value":"EN","Value2":"","Name":"LG","ExtendType":0}],"ChildItems":[]}]}]},"ExScope":1,"SearchType":6,"Rlang":"FOREIGN","KuaKuCode":"YSTT4HG0,LSTPFY1C,JUP3MUPD,MPMFIG1A,WQ0UVIAA,BLZOG7CK,EMRPGLPA,PWFIRAGL,NLBO1Z6R,NN3FJMUV","View":""}`,
+                    pageNum: 1,
+                    pageSize: 20,
+                    dstyle: 'listmode',
+                    boolSortSearch: false,
+                    productStr:
+                        'YSTT4HG0,LSTPFY1C,RMJLXHZ3,JQIRZIYA,JUP3MUPD,1UR4K4HZ,BPBAFJ5S,R79MZMCB,MPMFIG1A,WQ0UVIAA,NB3BWEHK,XVLO76FD,HR1YT1Z9,BLZOG7CK,EMRPGLPA,J708GVCE,ML4DRIDX,PWFIRAGL,NLBO1Z6R,NN3FJMUV,',
+                    aside: `主题:${kw}`,
+                    searchFrom: '资源范围:总库',
+                    CurPage: 1
+                })
+            )
+            en = en.concat(this.parseRefHtml(res1.data))
+        }
+        zh = this.sortDeduplication(zh.slice(0, 20))
+        en = this.sortDeduplication(en.slice(0, 10))
+
+        async function getRef(arr) {
             await Promise.all(
-                trs.map((i, tr) => {
-                    return new Promise((resolve, reject) => {
-                        let $$ = load(tr)
-                        const filename = $$('td.seq .cbItem').attr('value')
-                        const name = $$('td.name a').text()
-                        if (filename) {
-                            http
-                                .post(
-                                    'https://kns.cnki.net/dm8/API/GetExport',
-                                    qs.stringify({
-                                        filename,
-                                        displaymode: 'GBTREFER,MLA,APA',
-                                        uniplatform: 'NZKPT'
-                                    })
-                                )
-                                .then((res) => {
-                                    resolve({
-                                        filename,
-                                        name,
-                                        reference: res.data.data
-                                    })
-                                })
-                        } else {
-                            resolve(null)
-                        }
-                    })
+                arr.map((item) => {
+                    return http
+                        .post(
+                            'https://kns.cnki.net/dm8/API/GetExport',
+                            qs.stringify({
+                                filename: item.filename,
+                                displaymode: 'GBTREFER,MLA,APA',
+                                uniplatform: 'NZKPT'
+                            })
+                        )
+                        .then((res) => {
+                            item.ref = res.data.data.map((i) => i.value[0].replace(/^\[\d+\]/, ''))
+                        })
                 })
             )
-        ).filter((i) => !!i)
+        }
+        await getRef(zh)
+        await getRef(en)
+        await this.refSearchRecordRepository.save({
+            userId,
+            title
+        })
+        return { zh, en }
+    }
+
+    parseRefHtml(html: any) {
+        const $ = load(html)
+        const trs = $('tr')
+        const res = []
+        trs.each((i, tr) => {
+            let $$ = load(tr)
+            const filename = $$('td.seq .cbItem').attr('value')
+            if (filename) {
+                const name = $$('td.name a').text()
+                const date = $$('td.date').text()
+                res.push({
+                    filename,
+                    name,
+                    date
+                })
+            }
+        })
+        return res
+    }
+
+    sortDeduplication(arr: any[]) {
+        let res = []
+        arr.forEach((item) => {
+            if (!res.find((i) => i.filename === item.filename)) {
+                res.push(item)
+            }
+        })
+        res.sort((a, b) => {
+            return parse(b.date, 'yyyy-MM-dd', new Date()).getTime() - parse(a.date, 'yyyy-MM-dd', new Date()).getTime()
+        })
+        return res
     }
 }

+ 12 - 0
src/paper/ref.controller.ts

@@ -0,0 +1,12 @@
+import { Controller, Get, Query, Req } from '@nestjs/common'
+import { PaperService } from './paper.service'
+
+@Controller('ref')
+export class RefController {
+    constructor(private readonly paperService: PaperService) {}
+
+    @Get('/search')
+    async searchReferences(@Query('title') title, @Req() req) {
+        return await this.paperService.searchReferences(req.user.id, title)
+    }
+}

+ 13 - 0
src/sys-config/sys-config.service.ts

@@ -24,6 +24,19 @@ export class SysConfigService implements OnModuleInit {
                     remark: '系统预设'
                 })
             }
+
+            if (
+                !(await sysConfigRepository.findOneBy({
+                    name: 'paper_qr'
+                }))
+            ) {
+                sysConfigRepository.save({
+                    name: 'paper_qr',
+                    type: 'string',
+                    value: '',
+                    remark: '论文老师二维码'
+                })
+            }
         })()
     }
     async onModuleInit() {