| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288 |
- 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, 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'
- import { CreatePaperOrderDto } from './dto/create-order.dto'
- import { genPaper } from './paper-gen'
- import { genGeneralPaper1 } from './paper-gen/general'
- import { createLLM } from './paper-gen/llm'
- import { genChapters } from './paper-gen/chapter'
- 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'
- import { MemberType } from 'src/membership/entities/membership.entity'
- import { PaperOrderAttachment } from './entities/paper-order-attachment.entity'
- @Injectable()
- export class PaperService implements OnModuleInit {
- constructor(
- @InjectRepository(PaperOrder)
- private readonly paperOrderRepository: Repository<PaperOrder>,
- @InjectRepository(PaperGenResult)
- private readonly paperGenResultRepository: Repository<PaperGenResult>,
- @InjectRepository(RefSearchRecord)
- private readonly refSearchRecordRepository: Repository<RefSearchRecord>,
- private readonly membershipService: MembershipService,
- @InjectRepository(PaperOrderAttachment)
- private readonly paperOrderAttachmentRepository: Repository<PaperOrderAttachment>
- ) {}
- async onModuleInit() {
- for (const order of await this.paperOrderRepository.findBy({
- status: PaperOrderStatus.Generating
- })) {
- if (
- (await this.paperGenResultRepository.countBy({
- orderId: order.id
- })) === 0
- ) {
- order.status = PaperOrderStatus.Pending
- } else {
- order.status = PaperOrderStatus.Complete
- }
- await this.paperOrderRepository.save(order)
- }
- }
- async findAllOrders(req, pageReq: PageRequest<PaperOrder>) {
- ;(pageReq as any).search.where = (pageReq as any).search.where || {}
- let where = (pageReq as any).search.where
- if (!req.user.roles.includes('paperGen')) {
- where.userId = req.user.id
- }
- if (where.title) {
- where.title = Like(`%${where.title}%`)
- }
- return await paginate<PaperOrder>(this.paperOrderRepository, pageReq.page, pageReq.search)
- }
- async findOrderById(id: number): Promise<PaperOrder> {
- return await this.paperOrderRepository.findOneByOrFail({
- id
- })
- }
- async findAllResults(req: PageRequest<PaperGenResult>) {
- return await paginate<PaperGenResult>(this.paperGenResultRepository, req.page, req.search)
- }
- async createOrder(dto: CreatePaperOrderDto) {
- return await this.paperOrderRepository.save(dto)
- }
- async updateOrder(id: number, data) {
- return await this.paperOrderRepository.update(id, data)
- }
- async gen(orderId: number) {
- const order = await this.findOrderById(orderId)
- if (order.status === PaperOrderStatus.Generating) {
- throw new InternalServerErrorException('正在生成中,请稍后再试')
- }
- order.status = PaperOrderStatus.Generating
- await this.paperOrderRepository.save(order)
- this.genPaper(order)
- }
- async genChapters(id: number, description: string) {
- const order = await this.findOrderById(id)
- const tools = createLLM()
- const chapters = await genChapters(tools, order.major, order.title, description)
- order.chapters = chapters
- order.description = description
- await this.paperOrderRepository.save(order)
- return chapters
- }
- async genPaper(order: PaperOrder) {
- try {
- let content, duration, tokenUsage, fileUrl
- if (/计算机/.test(order.major)) {
- ;({ content, duration, tokenUsage, fileUrl } = await genPaper(order.title, order.description))
- } else {
- ;({ content, duration, tokenUsage, fileUrl } = await genGeneralPaper1(
- order.major,
- order.title,
- order.description,
- order.chapters
- ))
- }
- await this.paperGenResultRepository.save({
- orderId: order.id,
- content,
- duration,
- tokenUsage,
- fileUrl
- })
- order.status = PaperOrderStatus.Complete
- await this.paperOrderRepository.save(order)
- } catch (error) {
- Logger.error('生成论文失败', error.stack)
- order.status = PaperOrderStatus.Pending
- await this.paperOrderRepository.save(order)
- }
- }
- 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 || member.memberType === MemberType.Trial) {
- throw new InternalServerErrorException('USAGE_LIMIT_EXCEEDED')
- }
- }
- // const { llm } = createLLM()
- // 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',
- 'sec-ch-ua': '"Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"',
- 'X-Requested-With': 'XMLHttpRequest',
- 'sec-ch-ua-mobile': '?0',
- 'User-Agent':
- 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
- 'sec-ch-ua-platform': 'macOS',
- Origin: 'https://kns.cnki.net',
- 'Sec-Fetch-Site': 'same-origin',
- 'Sec-Fetch-Mode': 'cors',
- 'Sec-Fetch-Dest': 'empty',
- Referer:
- 'https://kns.cnki.net/kns8s/defaultresult/index?crossids=YSTT4HG0%2CLSTPFY1C%2CJUP3MUPD%2CMPMFIG1A%2CWQ0UVIAA%2CBLZOG7CK%2CEMRPGLPA%2CPWFIRAGL%2CNLBO1Z6R%2CNN3FJMUV&korder=SU&kw=%E9%98%BF%E6%96%AF%E9%A1%BF',
- 'Accept-Encoding': 'gzip, deflate, br',
- 'Accept-Language': 'zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7,und;q=0.6',
- 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'
- }
- })
- 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(
- 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+\]/, ''))
- })
- })
- )
- }
- 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
- }
- async attachments(req: PageRequest<PaperOrderAttachment>) {
- return await paginate<PaperOrderAttachment>(this.paperOrderAttachmentRepository, req.page, req.search)
- }
- async createAttachment(dto: PaperOrderAttachment) {
- return await this.paperOrderAttachmentRepository.save(dto)
- }
- async deleteAttachment(id: number) {
- return await this.paperOrderAttachmentRepository.delete(id)
- }
- }
|