From b6803ed01c29e507531e60aa9305bfcdd7763534 Mon Sep 17 00:00:00 2001 From: MiaoWoo Date: Tue, 30 Jun 2020 16:02:51 +0800 Subject: [PATCH] feat: update plugins Signed-off-by: MiaoWoo --- packages/plugins/src/MiaoSpring.ts | 75 +++++++++++++----- packages/plugins/src/MiaoWeb.ts | 39 +++++++++ packages/plugins/src/SearchRanking.ts | 109 +++++++++++++++++--------- 3 files changed, 166 insertions(+), 57 deletions(-) create mode 100644 packages/plugins/src/MiaoWeb.ts diff --git a/packages/plugins/src/MiaoSpring.ts b/packages/plugins/src/MiaoSpring.ts index 10d91b60..4e431dbd 100644 --- a/packages/plugins/src/MiaoSpring.ts +++ b/packages/plugins/src/MiaoSpring.ts @@ -2,10 +2,10 @@ /// import { constants, database, plugin, web } from "@ccms/api" -import { inject, ContainerInstance, Container, JSClass } from "@ccms/container" +import { inject, ContainerInstance, Container, JSClass, postConstruct } from "@ccms/container" import { JSPlugin, interfaces, cmd } from "@ccms/plugin" import { DataBase, DataBaseManager } from '@ccms/database' -import { Server, Context, RequestHandler } from '@ccms/web' +import { Server, Context, RequestHandler, Controller, Get, Post, Param, Body } from '@ccms/web' import * as fs from '@ccms/common/dist/fs' import * as reflect from '@ccms/common/dist/reflect' @@ -65,7 +65,7 @@ export class MiaoSpring extends interfaces.Plugin { this.webServer.registryInterceptor({ name: 'StaticHandle', preHandle: (ctx: Context) => { - let type = ctx.header['Accept'] || '' + let type = ctx.headers['Accept'] || '' if (type == '*/*' || type.includes('text/css') || type.includes('text/html')) { let filePath = fs.concat(root, 'wwwroot', (ctx.request.getRequestURI() == '/' ? 'index.html' : ctx.request.getRequestURI())) let fileType = 'text/html;charset=utf-8' @@ -73,33 +73,25 @@ export class MiaoSpring extends interfaces.Plugin { if (fs.exists(filePath)) { if (filePath.endsWith('.js')) { fileType = 'application/javascript' } if (filePath.endsWith('.css')) { fileType = 'text/css' } - // @ts-ignore return this.ResponseEntity.ok().header('Content-Type', fileType).body(base.read(filePath)) } } } }) - this.webServer.registryMapping('/api/eval', (ctx: Context) => { + this.registryMapping('/api/eval', (ctx: Context) => { try { return { status: 200, data: this.runCode(ctx.body + ''), msg: '代码执行成功!' } } catch (error) { return { status: 500, data: console.stack(error, false), msg: '代码执行异常!' } } }) - this.webServer.registryMapping('/api/plugin/list', () => { - return { status: 200, data: [...this.pluginManager.getPlugins().values()].map((plugin) => plugin.description), msg: '插件列表获取成功!' } - }) - this.webServer.registryMapping('/api/plugin/update', (ctx: Context) => { - if (!ctx.params.name) { return { status: 400, msg: '插件名称不得为空!' } } - - }) } private registryPages() { - this.webServer.registryMapping('/api/page/list', () => { + this.registryMapping('/api/page/list', () => { return { status: 0, data: { rows: this.mainDatabase.query('SELECT `id`, `type`, `name`, `content` FROM `pages` WHERE `deleted` = 0') } } }) - this.webServer.registryMapping('/api/page/get', (ctx: Context) => { + this.registryMapping('/api/page/get', (ctx: Context) => { let name = decodeURIComponent(`${ctx.params.name}`) let varable = undefined if (!name) { return { status: 400, msg: '名称不能为空!' } } @@ -119,20 +111,20 @@ export class MiaoSpring extends interfaces.Plugin { } return { status: 0, data: JSON.parse(content) } }) - this.webServer.registryMapping('/api/page/add', (ctx: Context) => { + this.registryMapping('/api/page/add', (ctx: Context) => { let body = ctx.body if (typeof body.content !== "string") { body.content = JSON.stringify(body.content) } this.mainDatabase.update("INSERT INTO `pages`(`type`, `name`, `content`) VALUES (?, ?, ?)", body.type || 1, body.name, body.content) return { status: 0, msg: `${body.name} 新增成功!` } }) - this.webServer.registryMapping('/api/page/update', (ctx: Context) => { + this.registryMapping('/api/page/update', (ctx: Context) => { if (!ctx.params.id) { return { status: 400, msg: 'ID 不能为空!' } } const body = ctx.body if (typeof body.content !== "string") { body.content = JSON.stringify(body.content) } this.mainDatabase.update("UPDATE `pages` SET `name` = ?, `content` = ? WHERE id = ?", body.name, body.content, ctx.params.id) return { status: 0, msg: `${body.name} 更新成功!` } }) - this.webServer.registryMapping('/api/page/delete', (ctx: Context) => { + this.registryMapping('/api/page/delete', (ctx: Context) => { if (!ctx.params.name) { return { status: 400, msg: '页面 名称 不能为空!' } } this.mainDatabase.update("UPDATE `pages` SET `name` = CONCAT(name, '_deleted'), deleted = 1 WHERE name = ?", ctx.params.name) return { status: 0, msg: `${ctx.params.name} 删除成功!` } @@ -142,17 +134,17 @@ export class MiaoSpring extends interfaces.Plugin { private configTable = "config" private registryDatabase() { - this.webServer.registryMapping('/api/config/list', (ctx: Context) => { + this.registryMapping('/api/config/list', (ctx: Context) => { return { status: 0, data: this.mainDatabase.query('SELECT id, name, label, url, driver, username, password FROM `' + this.configTable + '` WHERE deleted = 0 AND type = ?', ctx.params.type || 0) } }) - this.webServer.registryMapping('/api/config/get', (ctx: Context) => { + this.registryMapping('/api/config/get', (ctx: Context) => { let name = ctx.params.name if (!name) { return { status: 400, msg: '名称不能为空!' } } let result = this.mainDatabase.query('SELECT id, name, label, url, driver, username, password FROM `' + this.configTable + '` WHERE `name` = ?', name) if (!result.length) { return { status: 404, msg: `配置 ${name} 不存在!` } } return { status: 0, data: result[0] } }) - this.webServer.registryMapping('/api/config/add', (ctx: Context) => { + this.registryMapping('/api/config/add', (ctx: Context) => { let body = ctx.body if (!body.name) { return { status: 400, msg: '名称不能为空!' } } this.mainDatabase.update( @@ -161,7 +153,7 @@ export class MiaoSpring extends interfaces.Plugin { ) return { status: 0, msg: `配置 ${body.name} 新增成功!` } }) - this.webServer.registryMapping('/api/config/update', (ctx: Context) => { + this.registryMapping('/api/config/update', (ctx: Context) => { if (!ctx.params.id) { return { status: 400, msg: 'ID 不能为空!' } } let body = ctx.body this.mainDatabase.update( @@ -197,3 +189,44 @@ return eval(${JSON.stringify(code)});`) Object.keys(this.mappings).forEach((r) => this.webServer.unregistryMapping(r)) } } + +class Plugin { + id: number + name: string + source: string +} + +@Controller() +class PluginController { + @inject(plugin.PluginManager) + private pluginManager: plugin.PluginManager + @inject(database.DataBaseManager) + private databaseManager: DataBaseManager + + private mainDB: DataBase + + @postConstruct() + initialize() { + this.mainDB = this.databaseManager.getMainDatabase() + } + + @Get() + list(@Param('install') install: boolean) { + if (install) { + return { status: 200, data: [...this.pluginManager.getPlugins().values()].map((plugin) => plugin.description), msg: '插件列表获取成功!' } + } else { + return { status: 200, data: this.mainDB.query("SELECT name FROM plugins WHERE deleted = 0") } + } + } + @Post() + deploy(@Body() info: Plugin) { + let plugin = this.mainDB.query("SELECT name FROM plugins WHERE name = ?", info.name) + if (plugin.length == 0) { + this.mainDB.update("INSERT INTO `plugins`(`name`, `source`) VALUES (?, ?)", info.name, info.source) + return { status: 200, msg: `插件 ${info.name} 新增成功!` } + } else { + this.mainDB.update("UPDATE `plugins` SET `source` = ? WHERE id = ?", info.source, plugin[0].id) + return { status: 200, msg: `插件 ${info.name} 更新成功!` } + } + } +} diff --git a/packages/plugins/src/MiaoWeb.ts b/packages/plugins/src/MiaoWeb.ts new file mode 100644 index 00000000..663af525 --- /dev/null +++ b/packages/plugins/src/MiaoWeb.ts @@ -0,0 +1,39 @@ +import { constants, web } from "@ccms/api" +import { inject } from "@ccms/container" +import { plugin, interfaces } from "@ccms/plugin" +import { Controller, Get, Server, Body, Post, Cookie, Header, Param } from "@ccms/web" + +@plugin({ name: 'MiaoWeb', version: '1.0.0', author: 'MiaoWoo', servers: [constants.ServerType.Spring], source: __filename }) +export class MiaoWeb extends interfaces.Plugin { + @inject(web.Server) + private webServer: Server + + enable() { + this.webServer.registryController(TestController) + } + + disable() { + this.webServer.unregistryController(TestController) + } +} + +@Controller() +export class TestController { + @Get() + public get() { + return 'test' + } + @Post() + public post(@Body() body: any, + @Cookie('test') testCookie, + @Header('Accept') testHeader, + @Param("name") param) { + return { + body, + testCookie, + testHeader, + param + } + } +} + \ No newline at end of file diff --git a/packages/plugins/src/SearchRanking.ts b/packages/plugins/src/SearchRanking.ts index 49517be7..3bd0b11d 100644 --- a/packages/plugins/src/SearchRanking.ts +++ b/packages/plugins/src/SearchRanking.ts @@ -1,8 +1,16 @@ +/// + import { constants, plugin as pluginApi, amqp, server, web } from '@ccms/api' import { plugin, interfaces, cmd } from '@ccms/plugin' import { AmqpAdmin, ConnectionFactoryAdapter, AmqpManager } from '@ccms/amqp' -import { inject, Autowired } from '@ccms/container' -import { Server } from '@ccms/web' +import { inject, Autowired, ContainerInstance, Container } from '@ccms/container' +import { Server, Controller, Get, Param, METADATA_KEY } from '@ccms/web' + +const SearchRankingAmqpAdmin = Symbol('SearchRanking-AmqpAdmin') + +const EXCHANGE_NAME = `search.ranking.${process.env.SPRING_CLOUD_CONFIG_PROFILE || 'dev'}` +const QUEUE_NAME = `search.ranking.${process.env.SPRING_CLOUD_CONFIG_PROFILE || 'dev'}` +const ROUTER_KEY = `search.ranking.${process.env.SPRING_CLOUD_CONFIG_PROFILE || 'dev'}` @plugin({ name: SearchRanking.name, version: SearchRanking.version, author: SearchRanking.author, servers: SearchRanking.servers, source: __filename }) export class SearchRanking extends interfaces.Plugin { @@ -10,6 +18,8 @@ export class SearchRanking extends interfaces.Plugin { public static author = 'MiaoWoo' public static servers = [constants.ServerType.Spring] + @inject(ContainerInstance) + private container: Container @inject(pluginApi.PluginManager) private pluginManager: pluginApi.PluginManager @inject(amqp.Manager) @@ -20,17 +30,11 @@ export class SearchRanking extends interfaces.Plugin { private webServer: Server @Autowired() - private mongoTemplate: any - @Autowired() - private redisTemplate: any + private redisTemplate: org.springframework.data.redis.core.RedisTemplate private amqpAdmin: AmqpAdmin private listener: org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer - private readonly exchangeName = 'search.ranking' - private readonly queueName = 'search.ranking' - private readonly routerKey = 'search.ranking' - load() { let connection = new ConnectionFactoryAdapter({ url: 'amqp://rabbitmq.c.sixi.com:5672', @@ -42,33 +46,26 @@ export class SearchRanking extends interfaces.Plugin { } enable() { - this.webServer.registryMapping('/api/search', (ctx) => { - if (!ctx.params.keyword) { return { status: 400, msg: '查询关键词不得为空!' } } - let keyword = ctx.params.keyword + '' - let type = ctx.params.type == 'sale' ? 'sale' : 'normal' - let time = parseInt(ctx.params.time + '') || 60 * 60 * 24 * 15 - return this.cacheAndSearch(keyword, type as any, time * 1000) - }) - this.webServer.registryMapping('/api/search/ranking', (ctx) => { - if (!ctx.params.keyword || !ctx.params.shopName) { return { status: 400, msg: '查询关键词不得为空!' } } - let keyword = ctx.params.keyword + '' - let shopName = ctx.params.shopName + '' - let type = ctx.params.type == 'sale' ? 'sale' : 'normal' - let time = parseInt(ctx.params.time + '') || 60 * 60 * 24 * 15 - return this.sendSearchRankingCmd(keyword, shopName, type as any, time * 1000) - }) - this.amqpAdmin.declareQueueAndBindExchange(this.queueName, this.exchangeName, this.routerKey) - this.amqpAdmin.declareBinding(this.queueName, 'client.topic.exchange', `cmd_res.${this.routerKey}`) - this.listener = this.amqpAdmin.createContainer(this.queueName, (content, message, channel) => { + this.amqpAdmin.declareQueueAndBindExchange(QUEUE_NAME, EXCHANGE_NAME, ROUTER_KEY) + this.amqpAdmin.declareBinding(QUEUE_NAME, 'client.topic.exchange', `cmd_res.${ROUTER_KEY}`) + this.listener = this.amqpAdmin.createContainer(QUEUE_NAME, (content, _message, _channel) => { let searchResult = JSON.parse(content) + if (searchResult.type == "company") { + this.logger.info(content) + return + } this.redisTemplate.opsForValue().set(searchResult.reqData.cacheKey, searchResult) this.logger.sender(this.Server.getConsoleSender(), `§6查询任务完成! §b关键词: §r${searchResult.reqData.keywords}`) }) + this.container.bind(SearchRankingAmqpAdmin).toConstantValue(this.amqpAdmin) this.listener.start() + this.webServer.registryController(SearchRankingController) } disable() { + this.container.unbind(SearchRankingAmqpAdmin) this.listener.stop() + this.webServer.unregistryController(SearchRankingController) } @cmd() @@ -90,14 +87,55 @@ export class SearchRanking extends interfaces.Plugin { this.pluginManager.reload(this) } - cmdsend(sender: any, ...args: string[]) { - } - cmdsearch(sender: any, keyword: string, type: "sale" | "normal" = "sale", time: number) { - this.logger.sender(sender, this.cacheAndSearch(keyword, type, time)?.msg) + this.logger.sender(sender, this.container.getNamed(METADATA_KEY.Controller, SearchRankingController.name).cacheAndSearch(keyword, type, time)?.msg) + } +} + +@Controller({ path: '/api', name: SearchRankingController.name }) +class SearchRankingController { + @Autowired() + private redisTemplate: org.springframework.data.redis.core.RedisTemplate + + @inject(SearchRankingAmqpAdmin) + private amqpAdmin: AmqpAdmin + + @Get() + public search( + @Param({ name: 'keyword', require: true, message: '查询关键词不得为空!' }) keyword: string, + @Param("type") type: string = 'normal', + @Param('time') time: number = 60 * 60 * 24 * 15 + ) { + return this.cacheAndSearch(keyword, type as any, time * 1000) } - private cacheAndSearch(keyword: string, type: string = "sale", time: number = 15 * 24 * 60 * 60 * 1000) { + @Get("/search/ranking") + public searchRanking( + @Param({ name: 'keyword', require: true, message: '查询关键词不得为空!' }) keyword: string, + @Param("shopName") shopName: string = 'normal', + @Param("type") type: string = 'normal', + @Param('time') time: number = 60 * 60 * 24 * 15 + ) { + return this.sendSearchRankingCmd(keyword, shopName, type as any, time * 1000) + } + + @Get('/search/company') + public searchCompany( + @Param('url') url: string + ) { + this.amqpAdmin.getTemplate().convertAndSend('client.topic.exchange', 'cmd_req', { + cmd: 'scout._1688CompanyInfo', + data: { + type: 'company', + url: url, + cacheKey: 'SearchRanking:company' + }, + resRouteSuffix: ROUTER_KEY, + target: {} + }) + } + + public cacheAndSearch(keyword: string, type: string = "sale", time: number = 15 * 24 * 60 * 60 * 1000) { let cacheKey = this.getCacheKey(keyword, type) if (this.redisTemplate.hasKey(cacheKey)) { let lastSearchTime = this.redisTemplate.opsForValue().get(cacheKey) @@ -143,7 +181,7 @@ export class SearchRanking extends interfaces.Plugin { } : {}, cacheKey: this.getResultCacheKey(keywords, type, dateCache) }, - resRouteSuffix: this.routerKey, + resRouteSuffix: ROUTER_KEY, target: {} }) } @@ -159,11 +197,10 @@ export class SearchRanking extends interfaces.Plugin { cacheKey: this.getResultCacheKey(keywords, type, cacheTime), cacheTime }, - resRouteSuffix: this.routerKey, + resRouteSuffix: ROUTER_KEY, target: {} }) } - /** * 获得关键词+类型的上次查询时间 * @param keywords 关键词 @@ -175,4 +212,4 @@ export class SearchRanking extends interfaces.Plugin { private getResultCacheKey(keywords: string, type: string, date: number) { return `SearchRanking:${keywords}:${type}:${date}` } -} +} \ No newline at end of file