diff --git a/packages/database/package.json b/packages/database/package.json index a0e707c4..718fbe1c 100644 --- a/packages/database/package.json +++ b/packages/database/package.json @@ -1,6 +1,6 @@ { "name": "@ccms/database", - "version": "0.6.7", + "version": "0.7.0", "description": "MiaoScript database package", "keywords": [ "miaoscript", diff --git a/packages/database/src/constants.ts b/packages/database/src/constants.ts new file mode 100644 index 00000000..a6423b97 --- /dev/null +++ b/packages/database/src/constants.ts @@ -0,0 +1,3 @@ +export const METADATA_KEY = { + +} \ No newline at end of file diff --git a/packages/database/src/database.ts b/packages/database/src/database.ts index 48032d51..08950279 100644 --- a/packages/database/src/database.ts +++ b/packages/database/src/database.ts @@ -1,9 +1,33 @@ /// +import { Model } from './model' + const HikariDataSource = Java.type('com.zaxxer.hikari.HikariDataSource') const HikariConfig = Java.type('com.zaxxer.hikari.HikariConfig') const JdbcTemplate = Java.type('org.springframework.jdbc.core.JdbcTemplate') +/** + * 数据库配置 + */ +export interface DataBaseConfig { + /** + * 数据库连接串 + */ + url: string | javax.sql.DataSource + /** + * 数据库驱动 + */ + driverClassName?: string + /** + * 用户名 + */ + username?: string + /** + * 密码 + */ + password?: string +} + /** * 数据库封装类 */ @@ -11,24 +35,27 @@ export class DataBase { private dataSource: javax.sql.DataSource private jdbcTemplate: org.springframework.jdbc.core.JdbcTemplate - constructor(url: string | javax.sql.DataSource, username?: string, password?: string) { - if (!url) { throw new Error('DataBase url can\'t be null!') } - this.createDataSource(url, username, password) + constructor(dbConfig: DataBaseConfig) { + if (!dbConfig.url) { throw new Error('DataBase url can\'t be null!') } + this.createDataSource(dbConfig) this.initialize() } - private createDataSource(url: string | javax.sql.DataSource, username?: string, password?: string) { - if (typeof url === "string") { - if (!username || !password) { + private createDataSource(dbConfig: DataBaseConfig) { + if (typeof dbConfig.url === "string") { + if (!dbConfig.username || !dbConfig.password) { throw new Error('DataBase username or password can\'t be null!') } let config = new HikariConfig() - config.setUsername(username) - config.setPassword(password) - config.setJdbcUrl(url) + if (dbConfig.driverClassName) { + config.setDriverClassName(dbConfig.driverClassName) + } + config.setUsername(dbConfig.username) + config.setPassword(dbConfig.password) + config.setJdbcUrl(dbConfig.url) this.dataSource = new HikariDataSource(config) } else { - this.dataSource = url + this.dataSource = dbConfig.url } } @@ -41,7 +68,21 @@ export class DataBase { * @param sql SQL语句 */ query(sql: string, ...args: any[]): Array { - return this.jdbcTemplate.queryForList(sql, args) + let startTime = Date.now() + let result = Java.from(this.jdbcTemplate.queryForList(sql, args)) + console.debug(java.lang.String.format(`\n[DB] query \nSQL : ${sql.replace(/\?/ig, '%s')} \nCOST : ${Date.now() - startTime}ms`, args)) + return result + } + + /** + * 执行SQL更新 + * @param sql SQL语句 + */ + update(sql: string, ...args: any[]): number { + let startTime = Date.now() + let result = this.jdbcTemplate.update(sql, args) + console.debug(java.lang.String.format(`\n[DB] update \nSQL : ${sql.replace(/\?/ig, '%s')} \nCOST : ${Date.now() - startTime}ms`, args)) + return result } close() { diff --git a/packages/database/src/decorators.ts b/packages/database/src/decorators.ts new file mode 100644 index 00000000..34d518a9 --- /dev/null +++ b/packages/database/src/decorators.ts @@ -0,0 +1,7 @@ +import 'reflect-metadata' + +export function id() { + return (target: Object, propertyKey: string | symbol) => void { + + } +} \ No newline at end of file diff --git a/packages/database/src/manager.ts b/packages/database/src/manager.ts index 84cfb07c..dad42984 100644 --- a/packages/database/src/manager.ts +++ b/packages/database/src/manager.ts @@ -1,11 +1,9 @@ import { plugin, database } from '@ccms/api' -import { provideSingleton, inject, postConstruct, Container, ContainerInstance } from '@ccms/container' -import { DataBase } from './database' +import { provideSingleton, inject, postConstruct } from '@ccms/container' +import { DataBase, DataBaseConfig } from './database' @provideSingleton(database.DataBaseManager) export class DataBaseManager { - @inject(ContainerInstance) - private container: Container @inject(plugin.PluginInstance) private instance: any @@ -18,20 +16,30 @@ export class DataBaseManager { try { this.beanFactory = this.instance.getAutowireCapableBeanFactory() let mainDatasource = this.beanFactory.getBean(Packages.javax.sql.DataSource.class) - this.mainDatabase = new DataBase(mainDatasource) + this.mainDatabase = new DataBase({ url: mainDatasource }) } catch (error) { console.ex(error) } } + /** + * 获得主数据库 + * Get MainDatabase + */ getMainDatabase() { return this.mainDatabase } - createDatabase(name: string, url: string, username: string, password: string) { + /** + * 创建数据库 + * Create A Database Instance + * @param name 数据库名称 用于代码 database Name use at code + * @param config 数据库配置 + */ + createDatabase(name: string, config: DataBaseConfig) { Java.synchronized(() => { if (this.databases[name]) return this.databases[name] - return this.databases[name] = new DataBase(url, username, password) + return this.databases[name] = new DataBase(config) }, this.databases)() } diff --git a/packages/database/src/model.ts b/packages/database/src/model.ts new file mode 100644 index 00000000..e659d13c --- /dev/null +++ b/packages/database/src/model.ts @@ -0,0 +1,11 @@ +import { DataBase } from "./database" + +export class Model { + constructor(private database: DataBase) { + + } + queryForList(): Array { + + return [] + } +} \ No newline at end of file diff --git a/packages/spring/package.json b/packages/spring/package.json index 4cad36ad..797426c0 100644 --- a/packages/spring/package.json +++ b/packages/spring/package.json @@ -26,6 +26,7 @@ "dependencies": { "@ccms/api": "^0.7.0", "@ccms/common": "^0.7.0", - "@ccms/container": "^0.7.0" + "@ccms/container": "^0.7.0", + "@ccms/database": "^0.7.0" } } diff --git a/packages/spring/src/console.ts b/packages/spring/src/console.ts index 3fbc1a49..5d835aba 100644 --- a/packages/spring/src/console.ts +++ b/packages/spring/src/console.ts @@ -28,13 +28,16 @@ function mcColor2ANSI(str: string) { for (const regex in regexMap) { str = str.replace(regexMap[regex], `\u001b[${regex}m`) } - return str; + return str } export class SpringConsole extends MiaoScriptConsole { error(...args: any[]) { this.logger.error(args.join(' ')) } + warn(...args: any[]) { + this.logger.warn(args.join(' ')) + } sender(sender: any, ...args: any[]) { sender = sender || { sendMessage: (message: string) => console.console(message) @@ -46,7 +49,7 @@ export class SpringConsole extends MiaoScriptConsole { if (Object.prototype.toString.call(args[0]) === '[object Array]') { args[0].forEach(line => sender.sendMessage(this.prefix + line)) } else { - sender.sendMessage(this.prefix + args.join(' ')); + sender.sendMessage(this.prefix + args.join(' ')) } } console(...args: string[]): void { diff --git a/packages/spring/src/index.ts b/packages/spring/src/index.ts index 8f8df628..1e0ec712 100644 --- a/packages/spring/src/index.ts +++ b/packages/spring/src/index.ts @@ -1,13 +1,29 @@ import { server } from '@ccms/api' -import { Container } from '@ccms/container' -import '@ccms/database' +import { ioc, Container, reduceMetadata } from '@ccms/container' -import { SpringConsole } from './console'; -import './event'; -import './server'; -import './command'; -import './task'; +import { SpringConsole } from './console' +import './event' +import './server' +import './command' +import './task' + +const toString = {}.toString export default function SpringImpl(container: Container) { + try { + require('@ccms/web') + require('@ccms/amqp') + require('@ccms/database') + require('./internal/scanner/mysql-scanner') + } catch (error) { + console.ex(error) + } + const beanFactory = base.getInstance().getAutowireCapableBeanFactory() container.bind(server.Console).toConstantValue(SpringConsole) + container.bind(ioc.Autowired).toDynamicValue((ctx) => { + var metadata: any = reduceMetadata(ctx) + if (toString.call(metadata.named) === "[object Symbol]") { return container.get(metadata.named) } + if (toString.call(metadata.named) === '[object jdk.internal.dynalink.beans.StaticClass]') { metadata.named = metadata.named.class } + return beanFactory.getBean(metadata.named) + }) } diff --git a/packages/spring/src/internal/command.ts b/packages/spring/src/internal/command.ts index 21d75401..1b9a2bca 100644 --- a/packages/spring/src/internal/command.ts +++ b/packages/spring/src/internal/command.ts @@ -34,12 +34,32 @@ export class CommandMap { } let exists = this.commands[command] if (exists) { - return exists.executor(sender, '', command, Java.to(args)) + try { + return exists.executor(sender, '', command, Java.to(args)) + } catch (error) { + console.ex(error) + } + return true } else { sender.sendMessage && sender.sendMessage(`Unknown command. Type "/help" for help.`) return false } } + + tabComplate(sender: any, input: string, index?: number): string[] { + if (index == 0) { return Object.keys(this.commands) } + let [command, ...args] = input.split(' ') + let exists = this.commands[command] + if (exists && exists.tabCompleter) { + try { + if (args.length !== index) { args.push('') } + return exists.tabCompleter(sender, '', command, Java.to(args)) + } catch (error) { + console.ex(error) + } + } + return [] + } } export class SpringCommand { diff --git a/packages/spring/src/internal/scanner/mysql-scanner.ts b/packages/spring/src/internal/scanner/mysql-scanner.ts new file mode 100644 index 00000000..3c4cf467 --- /dev/null +++ b/packages/spring/src/internal/scanner/mysql-scanner.ts @@ -0,0 +1,32 @@ +import { plugin, database } from '@ccms/api' +import { DataBaseManager } from '@ccms/database' +import { provideSingleton, inject } from '@ccms/container' + +import * as fs from '@ccms/common/dist/fs' + +@provideSingleton(plugin.PluginScanner) +export class MySQLScanner implements plugin.PluginScanner { + type: string = "mysql" + + private target: string + + @inject(database.DataBaseManager) + private databaseManager: DataBaseManager + + scan(target: any): string[] { + this.target = target + let plugins = this.databaseManager.getMainDatabase().query<{ name: string }>(`SELECT name FROM ${this.target} WHERE LENGTH(source) != 0 AND deleted = 0`) + return plugins.map(p => `mysql:${p.name}`) + } + load(target: any) { + if (typeof target !== "string" || !target.startsWith('mysql:')) { return } + let name = target.split("mysql:")[1] + if (!name) { console.warn(`[PluginScanner][mysql] plugin name can't be null!`); return } + var plugin: any = this.databaseManager.getMainDatabase().query<{ source: string }>(`SELECT source FROM ${this.target} WHERE name = ? AND deleted = 0`, name) + if (plugin.length == 0) { console.warn(`[PluginScanner][mysql] plugin ${target} not found at mysql database...`); return } + let temp = fs.concat(root, 'mysql-plugin-cache', target, `${plugin[0]}.js`) + base.save(temp, plugin[0].source) + //@ts-ignore + return require(temp, { cache: false }) + } +} diff --git a/packages/spring/src/server.ts b/packages/spring/src/server.ts index 98ec5742..1560ad06 100644 --- a/packages/spring/src/server.ts +++ b/packages/spring/src/server.ts @@ -21,6 +21,7 @@ export class SpringServer implements server.Server { } getConsoleSender() { return { + name: 'CONSOLE', sendMessage: (message: string) => console.console(message) } } @@ -50,6 +51,6 @@ export class SpringServer implements server.Server { throw new Error("Method not implemented.") } tabComplete(sender: any, input: string, index?: number) { - throw new Error("Method not implemented.") + return this.commandMap.tabComplate(sender, input, index) } } diff --git a/packages/spring/src/task.ts b/packages/spring/src/task.ts index 2289bdde..5c2c7cf5 100644 --- a/packages/spring/src/task.ts +++ b/packages/spring/src/task.ts @@ -1,29 +1,30 @@ import { task, plugin } from '@ccms/api' import { inject, provideSingleton } from '@ccms/container' +import thread_pool from '@ccms/common/dist/thread-pool' +const AtomicInteger = Java.type("java.util.concurrent.atomic.AtomicInteger") const AtomicBoolean = Java.type("java.util.concurrent.atomic.AtomicBoolean") const Thread = Java.type('java.lang.Thread') -const ThreadPoolExecutor = Java.type('java.util.concurrent.ThreadPoolExecutor') -const ThreadPoolTaskExecutor = Java.type('org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor') -let executor: any -let tasks: { [key: number]: task.Cancelable } = {} -let taskId = 0 +const taskId = new AtomicInteger(0) +const tasks: { [key: number]: task.Cancelable } = {} +const executor = thread_pool.create({ + groupName: '@ccms/spring' +}) @provideSingleton(task.TaskManager) export class SpringTaskManager implements task.TaskManager { @inject(plugin.PluginInstance) private pluginInstance: any + private innerTaskId: any + private innerTasks: { [s: string]: task.Cancelable } + private innerExecutor: java.util.concurrent.ThreadPoolExecutor + constructor() { - executor = new ThreadPoolTaskExecutor() - executor.setCorePoolSize(10) - executor.setMaxPoolSize(100) - executor.setQueueCapacity(500) - executor.setKeepAliveSeconds(60) - executor.setThreadNamePrefix("@ccms/spring-") - executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()) - executor.initialize() + this.innerTaskId = taskId + this.innerTasks = tasks + this.innerExecutor = executor } create(func: Function): task.Task { @@ -34,13 +35,13 @@ export class SpringTaskManager implements task.TaskManager { return func() } disable() { - Object.values(tasks).forEach((task) => task?.cancel()) - executor.shutdown(); + Object.values(this.innerTasks).forEach((task) => task?.cancel()) + this.innerExecutor.shutdown() } } export class SpringTask extends task.Task { - public id = taskId++ + public id = taskId.incrementAndGet() private running = new AtomicBoolean(true) run(...args: any[]) { @@ -78,7 +79,7 @@ export class SpringTask extends task.Task { submit(...args: any[]): task.Cancelable { tasks[this.id] = this - executor.execute(() => this.run(...args)) + executor.execute((() => this.run(...args)) as any) return { cancel: () => { return this.cancel() diff --git a/packages/web/src/decorators/index.ts b/packages/web/src/decorators/index.ts index 6f5b7cc0..eb3112aa 100644 --- a/packages/web/src/decorators/index.ts +++ b/packages/web/src/decorators/index.ts @@ -3,11 +3,6 @@ export const Controller = () => { return } } -export const Header = () => { - return (target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor): MethodDecorator => { - return - } -} export const Post = () => { return (target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor): MethodDecorator => { return @@ -18,6 +13,11 @@ export const Get = () => { return } } +export const Header = () => { + return (target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor): MethodDecorator => { + return + } +} export const Param = () => { return (target: Object, propertyKey: string | symbol, parameterIndex: number): ParameterDecorator => { return diff --git a/packages/web/src/server.ts b/packages/web/src/server.ts index ade18dfe..2407b126 100644 --- a/packages/web/src/server.ts +++ b/packages/web/src/server.ts @@ -156,9 +156,10 @@ export class Server { } console.debug(` ===================== MiaoSpring ===================== -Request URL : ${ctx.url} -Response Body : ${JSON.stringify(Java.asJSONCompatible(ctx.result))} -Handle Time : ${Date.now() - startTime}ms +Request Method : ${ctx.request.getMethod()} +Request URL : ${ctx.url} +Response Body : ${JSON.stringify(Java.asJSONCompatible(ctx.result))} +Handle Time : ${Date.now() - startTime}ms ======================================================`) return ctx.result }