diff --git a/package.json b/package.json index f240408..e46da48 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "chatflow-admin", - "version": "0.5.1", + "version": "0.5.2", "description": "", "author": "", "private": true, diff --git a/src/app.module.ts b/src/app.module.ts index e3a580c..c178963 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -35,6 +35,9 @@ import { StatisticsModule } from './modules/statistics/statistics.module'; import { OrdersService } from './modules/orders/orders.service'; import { OrdersController } from './modules/orders/orders.controller'; import { OrdersModule } from './modules/orders/orders.module'; +import { ChatbotsService } from './modules/chatbots/chatbots.service'; +import { ChatbotsController } from './modules/chatbots/chatbots.controller'; +import { ChatbotsModule } from './modules/chatbots/chatbots.module'; @Module({ imports: [ @@ -52,6 +55,7 @@ import { OrdersModule } from './modules/orders/orders.module'; KeywordsModule, StatisticsModule, OrdersModule, + ChatbotsModule, ], controllers: [ AppController, @@ -66,6 +70,7 @@ import { OrdersModule } from './modules/orders/orders.module'; KeywordsController, StatisticsController, OrdersController, + ChatbotsController, ], providers: [ AppService, @@ -78,6 +83,7 @@ import { OrdersModule } from './modules/orders/orders.module'; KeywordsService, StatisticsService, OrdersService, + ChatbotsService, ], }) export class AppModule {} diff --git a/src/db/vika-db.ts b/src/db/vika-db.ts index 12e8f7c..39602cc 100644 --- a/src/db/vika-db.ts +++ b/src/db/vika-db.ts @@ -23,6 +23,8 @@ export interface DateBase { stockSheet: string; groupNoticeSheet: string; qaSheet: string; + chatBotSheet: string; + chatBotUserSheet: string; } export class KeyDisplaynameMap { @@ -97,6 +99,8 @@ export class VikaDB { stockSheet: '', groupNoticeSheet: '', qaSheet: '', + chatBotSheet: '', + chatBotUserSheet: '', }; this.dataBaseNames = { ...this.dataBaseIds }; this.config = {}; diff --git a/src/db/vika-orm.ts b/src/db/vika-orm.ts index 22005b4..21627af 100644 --- a/src/db/vika-orm.ts +++ b/src/db/vika-orm.ts @@ -254,6 +254,7 @@ export abstract class BaseEntity { static async findByField( fieldName: string, value: any, + pageSize: number | undefined = 100, ): Promise { const field = this.mappingOptions.fieldMapping[fieldName]; let records: IRecord[] = []; @@ -263,7 +264,7 @@ export abstract class BaseEntity { const query = { filterByFormula: `{${field}}="${value}"`, - pageSize: 1000, + pageSize: pageSize, }; console.info('query:', JSON.stringify(query)); // 分页获取记录,默认返回第一页 @@ -282,10 +283,11 @@ export abstract class BaseEntity { */ static async findByQuery( filterByFormula: string, + pageSize: number | undefined = 100, ): Promise { const query = { filterByFormula, - pageSize: 1000, + pageSize: pageSize, }; console.info('query:', JSON.stringify(query)); // 分页获取记录,默认返回第一页 diff --git a/src/db/vikaModel/ChatBot/fields.json b/src/db/vikaModel/ChatBot/fields.json new file mode 100644 index 0000000..a228020 --- /dev/null +++ b/src/db/vikaModel/ChatBot/fields.json @@ -0,0 +1,87 @@ +{ + "code": 200, + "success": true, + "data": { + "fields": [ + { + "id": "fldA47yx9L3bk", + "name": "机器人ID|id", + "type": "SingleText", + "property": { + "defaultValue": "" + }, + "editable": true, + "isPrimary": true + }, + { + "id": "fldPrcQ5QfhxV", + "name": "昵称|botname", + "type": "SingleText", + "property": {}, + "editable": true + }, + { + "id": "fld6rYKTZ0zQV", + "name": "用户ID|wxid", + "type": "SingleText", + "property": {}, + "editable": true + }, + { + "id": "fldxD8wwu1hGy", + "name": "用户名称|name", + "type": "SingleText", + "property": {}, + "editable": true + }, + { + "id": "fld4P2sX0CHco", + "name": "用户提示词|prompt", + "type": "Text", + "editable": true + }, + { + "id": "fldguMiluobGu", + "name": "配额|quota", + "type": "Number", + "property": { + "precision": 0 + }, + "editable": true + }, + { + "id": "fldUcpFFLyMo7", + "name": "启用状态|state", + "type": "SingleSelect", + "property": { + "options": [ + { + "id": "optMTJ3j2fMr4", + "name": "启用", + "color": { + "name": "deepPurple_0", + "value": "#E5E1FC" + } + }, + { + "id": "opt4bTBzh2Mx6", + "name": "禁用", + "color": { + "name": "indigo_0", + "value": "#DDE7FF" + } + } + ] + }, + "editable": true + }, + { + "id": "fldsG69W4KzJa", + "name": "备注|info", + "type": "Text", + "editable": true + } + ] + }, + "message": "SUCCESS" +} \ No newline at end of file diff --git a/src/db/vikaModel/ChatBot/mod.ts b/src/db/vikaModel/ChatBot/mod.ts new file mode 100644 index 0000000..0f2d9fb --- /dev/null +++ b/src/db/vikaModel/ChatBot/mod.ts @@ -0,0 +1,118 @@ +/* eslint-disable sort-keys */ + +import type { + Sheet, + // Field, +} from '../Model'; + +import { replaceSyncStatus, actionState } from '../actionBar'; + +const name = '智聊|Chatbot'; +const code = 'chatBotSheet'; + +const vikaFields = { + code: 200, + success: true, + data: { + fields: [ + { + id: 'fldA47yx9L3bk', + name: '机器人ID|id', + type: 'SingleText', + property: { + defaultValue: '', + }, + editable: true, + isPrimary: true, + }, + { + id: 'fldPrcQ5QfhxV', + name: '昵称|botname', + type: 'SingleText', + property: {}, + editable: true, + }, + { + id: 'fld6rYKTZ0zQV', + name: '用户ID|wxid', + type: 'SingleText', + property: {}, + editable: true, + }, + { + id: 'fldxD8wwu1hGy', + name: '用户名称|name', + type: 'SingleText', + property: {}, + editable: true, + }, + { + id: 'fld4P2sX0CHco', + name: '用户提示词|prompt', + type: 'Text', + editable: true, + }, + { + id: 'fldguMiluobGu', + name: '配额|quota', + type: 'Number', + property: { + precision: 0, + }, + editable: true, + }, + { + id: 'fldUcpFFLyMo7', + name: '启用状态|state', + type: 'SingleSelect', + property: { + options: [ + { + id: 'optMTJ3j2fMr4', + name: '启用', + color: { + name: 'deepPurple_0', + value: '#E5E1FC', + }, + }, + { + id: 'opt4bTBzh2Mx6', + name: '禁用', + color: { + name: 'indigo_0', + value: '#DDE7FF', + }, + }, + ], + }, + editable: true, + }, + { + id: 'fldsG69W4KzJa', + name: '备注|info', + type: 'Text', + editable: true, + }, + ], + }, + message: 'SUCCESS', +}; + +let fields: any = vikaFields.data.fields; + +if (actionState[code]) { + fields = replaceSyncStatus(fields); +} + +const defaultRecords: any = { + code: 200, + success: true, + data: { total: 0, records: [], pageNum: 1, pageSize: 0 }, + message: 'SUCCESS', +}; + +export const sheet: Sheet = { + fields, + name, + defaultRecords: defaultRecords.data.records, +}; diff --git a/src/db/vikaModel/ChatBot/records.json b/src/db/vikaModel/ChatBot/records.json new file mode 100644 index 0000000..b8708b7 --- /dev/null +++ b/src/db/vikaModel/ChatBot/records.json @@ -0,0 +1 @@ +{"code":200,"success":true,"data":{"total":0,"records":[],"pageNum":1,"pageSize":0},"message":"SUCCESS"} \ No newline at end of file diff --git a/src/db/vikaModel/ChatBotUser/mod.ts b/src/db/vikaModel/ChatBotUser/mod.ts new file mode 100644 index 0000000..f230e9d --- /dev/null +++ b/src/db/vikaModel/ChatBotUser/mod.ts @@ -0,0 +1,118 @@ +/* eslint-disable sort-keys */ + +import type { + Sheet, + // Field, +} from '../Model'; + +import { replaceSyncStatus, actionState } from '../actionBar'; + +const name = '智聊用户|ChatbotUser'; +const code = 'chatBotUserSheet'; + +const vikaFields = { + code: 200, + success: true, + data: { + fields: [ + { + id: 'fldA47yx9L3bk', + name: '机器人ID|id', + type: 'SingleText', + property: { + defaultValue: '', + }, + editable: true, + isPrimary: true, + }, + { + id: 'fldPrcQ5QfhxV', + name: '昵称|botname', + type: 'SingleText', + property: {}, + editable: true, + }, + { + id: 'fld6rYKTZ0zQV', + name: '用户ID|wxid', + type: 'SingleText', + property: {}, + editable: true, + }, + { + id: 'fldxD8wwu1hGy', + name: '用户名称|name', + type: 'SingleText', + property: {}, + editable: true, + }, + { + id: 'fld4P2sX0CHco', + name: '用户提示词|prompt', + type: 'Text', + editable: true, + }, + { + id: 'fldguMiluobGu', + name: '配额|quota', + type: 'Number', + property: { + precision: 0, + }, + editable: true, + }, + { + id: 'fldUcpFFLyMo7', + name: '启用状态|state', + type: 'SingleSelect', + property: { + options: [ + { + id: 'optMTJ3j2fMr4', + name: '启用', + color: { + name: 'deepPurple_0', + value: '#E5E1FC', + }, + }, + { + id: 'opt4bTBzh2Mx6', + name: '禁用', + color: { + name: 'indigo_0', + value: '#DDE7FF', + }, + }, + ], + }, + editable: true, + }, + { + id: 'fldsG69W4KzJa', + name: '备注|info', + type: 'Text', + editable: true, + }, + ], + }, + message: 'SUCCESS', +}; + +let fields: any = vikaFields.data.fields; + +if (actionState[code]) { + fields = replaceSyncStatus(fields); +} + +const defaultRecords: any = { + code: 200, + success: true, + data: { total: 0, records: [], pageNum: 1, pageSize: 0 }, + message: 'SUCCESS', +}; + +export const sheet: Sheet = { + fields, + name, + defaultRecords: defaultRecords.data.records, +}; diff --git a/src/db/vikaModel/actionBar.ts b/src/db/vikaModel/actionBar.ts index 20bdbf5..14374fc 100644 --- a/src/db/vikaModel/actionBar.ts +++ b/src/db/vikaModel/actionBar.ts @@ -20,6 +20,8 @@ export const actionState = { statisticSheet: true, groupNoticeSheet: true, messageSheet: false, + chatBotSheet: false, + chatBotUserSheet: false, }; export function replaceSyncStatus(fields: CustomObject[]): CustomObject[] { diff --git a/src/db/vikaModel/index.ts b/src/db/vikaModel/index.ts index 36d1d12..f192bc7 100644 --- a/src/db/vikaModel/index.ts +++ b/src/db/vikaModel/index.ts @@ -14,6 +14,8 @@ import { sheet as noticeSheet } from './Notice/mod'; import { sheet as whiteListSheet } from './WhiteList/mod'; import { stockSheet } from './Stock/mod'; import { sheet as groupNoticeSheet } from './GroupNotice/mod'; +import { sheet as chatBotSheet } from './ChatBot/mod'; +import { sheet as chatBotUserSheet } from './ChatBotUser/mod'; const sheets: Sheets = { qaSheet, @@ -27,6 +29,8 @@ const sheets: Sheets = { statisticSheet, groupNoticeSheet, messageSheet, + chatBotSheet, + chatBotUserSheet, // stockSheet, // groupSheet, // switchSheet, diff --git a/src/modules/chatbots/chatbots.controller.spec.ts b/src/modules/chatbots/chatbots.controller.spec.ts new file mode 100644 index 0000000..8bea2db --- /dev/null +++ b/src/modules/chatbots/chatbots.controller.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { ChatbotsController } from './chatbots.controller'; + +describe('ChatbotsController', () => { + let controller: ChatbotsController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [ChatbotsController], + }).compile(); + + controller = module.get(ChatbotsController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/src/modules/chatbots/chatbots.controller.ts b/src/modules/chatbots/chatbots.controller.ts new file mode 100644 index 0000000..81a3d7d --- /dev/null +++ b/src/modules/chatbots/chatbots.controller.ts @@ -0,0 +1,81 @@ +import { + Controller, + Get, + Request, + UnauthorizedException, +} from '@nestjs/common'; +import { ChatbotsService, ChatbotUserService } from './chatbots.service.js'; +import { Store } from '../../db/store.js'; + +@Controller('api/v1/chatbot') +export class ChatbotsController { + @Get('list') + async findAll(@Request() req: any): Promise { + const user = req.user; + // console.debug(user); + // console.debug(Store.users); + const db = Store.findUser(user.userId); + if (!db) { + throw new UnauthorizedException(); + } + console.debug(db); + ChatbotsService.setVikaOptions({ + apiKey: db.token, + baseId: db.dataBaseIds.chatBotSheet, // 设置 base ID + }); + const data = await ChatbotsService.findAll(); + const items = data.map((value: any) => { + const fields = value.fields; + fields.recordId = value.recordId; + return fields; + }); + // console.debug(data); + const res: any = { + code: 200, + message: 'success', + data: { + page: 1, + pageSize: 1000, + pageCount: 1, + itemCount: data.length, + list: items, + }, + }; + return res; + } + + @Get('user/list') + async findUserAll(@Request() req: any): Promise { + const user = req.user; + // console.debug(user); + // console.debug(Store.users); + const db = Store.findUser(user.userId); + if (!db) { + throw new UnauthorizedException(); + } + console.debug(db); + ChatbotUserService.setVikaOptions({ + apiKey: db.token, + baseId: db.dataBaseIds.chatBotUserSheet, // 设置 base ID + }); + const data = await ChatbotUserService.findAll(); + const items = data.map((value: any) => { + const fields = value.fields; + fields.recordId = value.recordId; + return fields; + }); + // console.debug(data); + const res: any = { + code: 200, + message: 'success', + data: { + page: 1, + pageSize: 1000, + pageCount: 1, + itemCount: data.length, + list: items, + }, + }; + return res; + } +} diff --git a/src/modules/chatbots/chatbots.module.ts b/src/modules/chatbots/chatbots.module.ts new file mode 100644 index 0000000..63f5932 --- /dev/null +++ b/src/modules/chatbots/chatbots.module.ts @@ -0,0 +1,4 @@ +import { Module } from '@nestjs/common'; + +@Module({}) +export class ChatbotsModule {} diff --git a/src/modules/chatbots/chatbots.service.spec.ts b/src/modules/chatbots/chatbots.service.spec.ts new file mode 100644 index 0000000..42b47a1 --- /dev/null +++ b/src/modules/chatbots/chatbots.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { ChatbotsService } from './chatbots.service'; + +describe('ChatbotsService', () => { + let service: ChatbotsService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ChatbotsService], + }).compile(); + + service = module.get(ChatbotsService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/src/modules/chatbots/chatbots.service.ts b/src/modules/chatbots/chatbots.service.ts new file mode 100644 index 0000000..68768d4 --- /dev/null +++ b/src/modules/chatbots/chatbots.service.ts @@ -0,0 +1,83 @@ +import { Injectable } from '@nestjs/common'; +import { BaseEntity, MappingOptions } from '../../utils/vika-orm'; // 导入 BaseEntity, VikaOptions, 和 MappingOptions 类型/类 + +@Injectable() +export class ChatbotsService extends BaseEntity { + id: string; // 定义名字属性,可选 + name: string; + desc: string; + type: string; + model: string; + prompt: string; + quota: string; + endpoint: string; + key: string; + + // protected static override recordId: string = '' // 定义记录ID,初始为空字符串 + + protected static override mappingOptions: MappingOptions = { + // 定义字段映射选项 + fieldMapping: { + // 字段映射 + id: '机器人ID|id', + name: '昵称|name', + desc: '描述|desc', + type: '类型|type', + model: '模型|model', + prompt: '系统提示词|prompt', + quota: '配额|quota', + endpoint: '接入点|endpoint', + key: '密钥|key', + }, + tableName: '智聊|Chatbot', // 表名 + }; + + protected static override getMappingOptions(): MappingOptions { + // 获取映射选项的方法 + return this.mappingOptions; // 返回当前类的映射选项 + } + + static override setMappingOptions(options: MappingOptions) { + // 设置映射选项的方法 + this.mappingOptions = options; // 更新当前类的映射选项 + } +} + +@Injectable() +export class ChatbotUserService extends BaseEntity { + id: string; // 定义名字属性,可选 + botname: string; + wxid: string; + name: string; + prompt: string; + quota: string; + state: string; + info: string; + // protected static override recordId: string = '' // 定义记录ID,初始为空字符串 + + protected static override mappingOptions: MappingOptions = { + // 定义字段映射选项 + fieldMapping: { + // 字段映射 + id: '机器人ID|id', + botname: '昵称|botname', + wxid: '用户ID|wxid', + name: '用户名称|name', + prompt: '用户提示词|prompt', + quota: '配额|quota', + state: '启用状态|state', + info: '备注|info', + }, + tableName: '智聊用户|ChatbotUser', // 表名 + }; + + protected static override getMappingOptions(): MappingOptions { + // 获取映射选项的方法 + return this.mappingOptions; // 返回当前类的映射选项 + } + + static override setMappingOptions(options: MappingOptions) { + // 设置映射选项的方法 + this.mappingOptions = options; // 更新当前类的映射选项 + } +} diff --git a/src/modules/chats/chats.controller.ts b/src/modules/chats/chats.controller.ts index b0c0a8b..9925a60 100644 --- a/src/modules/chats/chats.controller.ts +++ b/src/modules/chats/chats.controller.ts @@ -245,10 +245,15 @@ export class ChatsController { let res: any = []; if (data.talk_type === '2') { - res = await ChatsService.findByField('roomid', data.receiver_id); + res = await ChatsService.findByField( + 'roomid', + data.receiver_id, + data.limit, + ); } else { res = await ChatsService.findByQuery( `({接收人ID|listenerid}="${data.receiver_id}"&&{好友ID|wxid}="${data.record_id}")||({接收人ID|listenerid}="${data.record_id}"&&{好友ID|wxid}="${data.receiver_id}")`, + data.limit, ); } // console.debug('vika res', res); @@ -266,6 +271,7 @@ export class ChatsController { wxAvatar, file, } = value.fields; + console.debug('消息messagePayload...', messagePayload); if (file) console.debug('文件消息 file', file); const { wxid, listenerid } = value.fields; const user_id = wxid; @@ -379,7 +385,7 @@ export class ChatsController { is_revoke: 0, is_mark: 0, is_read: 0, - content: messagePayload, + content: messagePayload || '', created_at: timeHms, extra: extra, recordId: recordId, diff --git a/src/utils/vika-orm.ts b/src/utils/vika-orm.ts index 22005b4..a1ddc81 100644 --- a/src/utils/vika-orm.ts +++ b/src/utils/vika-orm.ts @@ -254,6 +254,7 @@ export abstract class BaseEntity { static async findByField( fieldName: string, value: any, + pageSize: number | undefined = 100, ): Promise { const field = this.mappingOptions.fieldMapping[fieldName]; let records: IRecord[] = []; @@ -263,7 +264,7 @@ export abstract class BaseEntity { const query = { filterByFormula: `{${field}}="${value}"`, - pageSize: 1000, + pageSize, }; console.info('query:', JSON.stringify(query)); // 分页获取记录,默认返回第一页 @@ -282,10 +283,11 @@ export abstract class BaseEntity { */ static async findByQuery( filterByFormula: string, + pageSize: number | undefined = 100, ): Promise { const query = { filterByFormula, - pageSize: 1000, + pageSize: pageSize, }; console.info('query:', JSON.stringify(query)); // 分页获取记录,默认返回第一页