feat: rewrite controller service

This commit is contained in:
gaoshuaixing
2024-12-24 17:40:04 +08:00
parent 34a9a3c704
commit 1d881eb2b6
12 changed files with 1322 additions and 20 deletions

View File

@@ -10,14 +10,27 @@ class CrossController {
* View process service information
*/
info() {
const pids = Cross.getPids();
Log.info('cross pids:', pids);
let num = 1;
pids.forEach(pid => {
let entity = Cross.getProc(pid);
Log.info(`server-${num} name:${entity.name}`);
Log.info(`server-${num} config:`, entity.config);
num++;
})
return 'hello electron-egg';
}
/**
* Get service url
*/
async getUrl(args) {
const { name } = args;
const serverUrl = Cross.getUrl(name);
return serverUrl;
}
/**
@@ -25,21 +38,51 @@ class CrossController {
* By default (modifiable), killing the process will exit the electron application.
*/
async killServer(args) {
const { type, name } = args;
if (type == 'all') {
Cross.killAll();
} else {
Cross.killByName(name);
}
return;
}
/**
* create service
*/
async createServer(args) {
const { program } = args;
if (program == 'go') {
Services.get('cross').createGoServer();
} else if (program == 'java') {
Services.get('cross').createJavaServer();
} else if (program == 'python') {
Services.get('cross').createPythonServer();
}
return;
}
/**
* Access the api for the cross service
*/
async requestApi(args) {
const { name, urlPath, params} = args;
const hc = new HttpClient();
const serverUrl = Cross.getUrl(name);
console.log('Server Url:', serverUrl);
const apiHello = serverUrl + urlPath;
const options = {
method: 'GET',
data: params || {},
dataType: 'json',
timeout: 1000,
};
const result = await hc.request(apiHello, options);
return result.data;
}
}

View File

@@ -1,31 +1,65 @@
'use strict';
const { dialog } = require('electron');
const _ = require('lodash');
const { getMainWindow } = require('ee-core/electron/window');
/**
* 特效 - 功能demo
* effect - demo
* @class
*/
class EffectController {
/**
* 选择文件
* select file
*/
selectFile() {
const filePaths = dialog.showOpenDialogSync({
properties: ['openFile']
});
if (_.isEmpty(filePaths)) {
return null
}
return filePaths[0];
}
/**
* login window
*/
loginWindow(args) {
const { width, height } = args;
const win = getMainWindow();
const size = {
width: width || 400,
height: height || 300
}
win.setSize(size.width, size.height);
win.setResizable(true);
win.center();
win.show();
win.focus();
}
/**
* restore window
*/
restoreWindow(args) {
const { width, height } = args;
const win = getMainWindow();
}
const size = {
width: width || 980,
height: height || 650
}
win.setSize(size.width, size.height);
win.setResizable(true);
win.center();
win.show();
win.focus();
}
}
EffectController.toString = () => '[class EffectController]';

View File

@@ -1,17 +1,277 @@
'use strict';
const dayjs = require('dayjs');
const path = require('path');
const fs = require('fs');
const { exec } = require('child_process');
const { app: electronApp, shell } = require('electron');
const { getExtraResourcesDir } = require('ee-core/ps');
const { logger } = require('ee-core/log');
const { getConfig } = require('ee-core/config');
/**
* example
* framework - demo
* @class
*/
class FrameworkController {
/**
* test
* 所有方法接收两个参数
* @param args 前端传的参数
* @param event - ipc通信时才有值。详情见控制器文档
*/
async test () {
return 'hello electron-egg';
/**
* sqlite数据库操作
*/
async sqlitedbOperation(args) {
const { action, info, delete_name, update_name, update_age, search_age, data_dir } = args;
const data = {
action,
result: null,
all_list: [],
code: 0
};
try {
// test
Services.get('database.sqlitedb').getDataDir();
} catch (err) {
console.log(err);
data.code = -1;
return data;
}
switch (action) {
case 'add' :
data.result = await Services.get('database.sqlitedb').addTestDataSqlite(info);;
break;
case 'del' :
data.result = await Services.get('database.sqlitedb').delTestDataSqlite(delete_name);;
break;
case 'update' :
data.result = await Services.get('database.sqlitedb').updateTestDataSqlite(update_name, update_age);
break;
case 'get' :
data.result = await Services.get('database.sqlitedb').getTestDataSqlite(search_age);
break;
case 'getDataDir' :
data.result = await Services.get('database.sqlitedb').getDataDir();
break;
case 'setDataDir' :
data.result = await Services.get('database.sqlitedb').setCustomDataDir(data_dir);
break;
}
data.all_list = await Services.get('database.sqlitedb').getAllTestDataSqlite();
return data;
}
/**
* 调用其它程序exe、bash等可执行程序
*
*/
openSoftware(softName) {
if (!softName) {
return false;
}
let softwarePath = path.join(getExtraResourcesDir(), softName);
logger.info('[openSoftware] softwarePath:', softwarePath);
// 检查程序是否存在
if (!fs.existsSync(softwarePath)) {
return false;
}
// 命令行字符串 并 执行, start 命令后面的路径要加双引号
let cmdStr = `start "${softwarePath}"`;
exec(cmdStr);
// 方法二
// 推荐使用cross模块
return true;
}
/**
* 检查是否有新版本
*/
checkForUpdater() {
Addon.get('autoUpdater').checkUpdate();
return;
}
/**
* 下载新版本
*/
downloadApp() {
Addon.get('autoUpdater').download();
return;
}
/**
* 检测http服务是否开启
*/
async checkHttpServer() {
const { httpServer } = getConfig;
const url = httpServer.protocol + httpServer.host + ':' + httpServer.port;
const data = {
enable: httpServer.enable,
server: url
}
return data;
}
/**
* [todo] 一个http请求访问此方法
*/
async doHttpRequest() {
const { CoreApp } = EE;
// http方法
const method = CoreApp.request.method;
// http get 参数
let params = CoreApp.request.query;
params = (params instanceof Object) ? params : JSON.parse(JSON.stringify(params));
// http post 参数
const body = CoreApp.request.body;
const httpInfo = {
method,
params,
body
}
Log.info('httpInfo:', httpInfo);
if (!body.id) {
return false;
}
const dir = electronApp.getPath(body.id);
shell.openPath(dir);
return true;
}
/**
* 一个socket io请求访问此方法
*/
async doSocketRequest(args) {
const { id } = args;
if (!id) {
return false;
}
const dir = electronApp.getPath(id);
shell.openPath(dir);
return true;
}
/**
* 异步消息类型
*/
async ipcInvokeMsg(args, event) {
let timeNow = dayjs().format('YYYY-MM-DD HH:mm:ss');
const data = args + ' - ' + timeNow;
return data;
}
/**
* 同步消息类型
*/
async ipcSendSyncMsg(args) {
let timeNow = dayjs().format('YYYY-MM-DD HH:mm:ss');
const data = args + ' - ' + timeNow;
return data;
}
/**
* 双向异步通信
*/
async ipcSendMsg(args, event) {
const { type, content } = args;
const data = await Services.get('framework').bothWayMessage(type, content, event);
return data;
}
/**
* 任务
*/
someJob(args, event) {
let jobId = args.id;
let action = args.action;
let result;
switch (action) {
case 'create':
result = Services.get('framework').doJob(jobId, action, event);
break;
case 'close':
Services.get('framework').doJob(jobId, action, event);
break;
case 'pause':
Services.get('framework').doJob(jobId, action, event);
break;
case 'resume':
Services.get('framework').doJob(jobId, action, event);
break;
default:
}
let data = {
jobId,
action,
result
}
return data;
}
/**
* 创建任务池
*/
async createPool(args, event) {
let num = args.number;
Services.get('framework').doCreatePool(num, event);
// test monitor
Services.get('framework').monitorJob();
return;
}
/**
* 通过进程池执行任务
*/
someJobByPool(args, event) {
let jobId = args.id;
let action = args.action;
let result;
switch (action) {
case 'run':
result = Services.get('framework').doJobByPool(jobId, action, event);
break;
default:
}
let data = {
jobId,
action,
result
}
return data;
}
/**
* 测试接口
*/
hello(args) {
Log.info('hello ', args);
}
}
FrameworkController.toString = () => '[class FrameworkController]';

View File

@@ -0,0 +1,66 @@
'use strict';
const path = require('path');
const { getBaseDir } = require('ee-core/ps');
const { getMainWindow } = require('ee-core/electron/window');
/**
* 硬件设备 - 功能demo
* @class
*/
class HardwareController {
/**
* 获取打印机列表
*/
async getPrinterList () {
//主线程获取打印机列表
const win = getMainWindow();
const list = await win.webContents.getPrintersAsync();
return list;
}
/**
* 打印
*/
print (args, event) {
const { view, deviceName } = args;
let content = null;
if (view.type == 'html') {
content = path.join('file://', getBaseDir(), view.content)
} else {
content = view.content;
}
let opt = {
title: 'printer window',
x: 10,
y: 10,
width: 980,
height: 650
}
const name = 'window-printer';
const printWindow = Addon.get('window').create(name, opt);
printWindow.loadURL(content);
printWindow.webContents.once('did-finish-load', () => {
// 页面完全加载完成后,开始打印
printWindow.webContents.print({
silent: false, // 显示打印对话框
printBackground: true,
deviceName,
}, (success, failureReason) => {
const channel = 'controller.hardware.printStatus';
event.reply(`${channel}`, { success, failureReason });
printWindow.close();
});
});
return true;
}
}
HardwareController.toString = () => '[class HardwareController]';
module.exports = HardwareController;

View File

@@ -1,5 +1,13 @@
'use strict';
const _ = require('lodash');
const path = require('path');
const {
app: electronApp, dialog, shell, Notification,
powerMonitor, screen, nativeTheme
} = require('electron');
const { isProd, getBaseDir } = require('ee-core/ps');
/**
* example
* @class
@@ -7,11 +15,325 @@
class OsController {
/**
* test
* 所有方法接收两个参数
* @param args 前端传的参数
* @param event - ipc通信时才有值。详情见控制器文档
*/
async test () {
return 'hello electron-egg';
/**
* 消息提示对话框
*/
messageShow() {
dialog.showMessageBoxSync({
type: 'info', // "none", "info", "error", "question" 或者 "warning"
title: '自定义标题-message',
message: '自定义消息内容',
detail: '其它的额外信息'
})
return '打开了消息框';
}
/**
* 消息提示与确认对话框
*/
messageShowConfirm() {
const res = dialog.showMessageBoxSync({
type: 'info',
title: '自定义标题-message',
message: '自定义消息内容',
detail: '其它的额外信息',
cancelId: 1, // 用于取消对话框的按钮的索引
defaultId: 0, // 设置默认选中的按钮
buttons: ['确认', '取消'], // 按钮及索引
})
let data = (res === 0) ? '点击确认按钮' : '点击取消按钮';
return data;
}
/**
* 选择目录
*/
selectFolder() {
const filePaths = dialog.showOpenDialogSync({
properties: ['openDirectory', 'createDirectory']
});
if (_.isEmpty(filePaths)) {
return null
}
return filePaths[0];
}
/**
* 打开目录
*/
openDirectory(args) {
const { id } = args;
if (!id) {
return false;
}
let dir = '';
if (path.isAbsolute(id)) {
dir = id;
} else {
dir = electronApp.getPath(id);
}
shell.openPath(dir);
return true;
}
/**
* 选择图片
*/
selectPic() {
const filePaths = dialog.showOpenDialogSync({
title: 'select pic',
properties: ['openFile'],
filters: [
{ name: 'Images', extensions: ['jpg', 'png', 'gif'] },
]
});
if (_.isEmpty(filePaths)) {
return null
}
return filePaths[0];
}
/**
* 加载视图内容
*/
loadViewContent(args) {
const { type, content } = args;
let contentUrl = content;
if (type == 'html') {
contentUrl = path.join('file://', electronApp.getAppPath(), content);
}
Services.get('os').createBrowserView(contentUrl);
return true
}
/**
* 移除视图内容
*/
removeViewContent() {
Services.get('os').removeBrowserView();
return true
}
/**
* 打开新窗口
*/
createWindow(args) {
const { type, content, windowName, windowTitle } = args;
let contentUrl = null;
if (type == 'html') {
contentUrl = path.join('file://', electronApp.getAppPath(), content)
} else if (type == 'web') {
contentUrl = content;
} else if (type == 'vue') {
let addr = 'http://localhost:8080'
if (isProd()) {
const mainServer = Conf.getValue('mainServer');
if (Conf.isFileProtocol(mainServer)) {
addr = mainServer.protocol + path.join(Ps.getHomeDir(), mainServer.indexPath);
} else {
addr = mainServer.protocol + mainServer.host + ':' + mainServer.port;
}
}
contentUrl = addr + content;
} else {
// some
}
console.log('contentUrl: ', contentUrl);
let opt = {
title: windowTitle
}
const win = Addon.get('window').create(windowName, opt);
const winContentsId = win.webContents.id;
// load page
win.loadURL(contentUrl);
return winContentsId;
}
/**
* 获取窗口contents id
*/
getWCid(args) {
// 主窗口的name默认是main其它窗口name开发者自己定义
const name = args;
const id = Addon.get('window').getWCid(name);
return id;
}
/**
* 创建系统通知
*/
sendNotification(args, event) {
const { title, subtitle, body, silent} = args;
if (!Notification.isSupported()) {
return '当前系统不支持通知';
}
let options = {};
if (!_.isEmpty(title)) {
options.title = title;
}
if (!_.isEmpty(subtitle)) {
options.subtitle = subtitle;
}
if (!_.isEmpty(body)) {
options.body = body;
}
if (!_.isEmpty(silent)) {
options.silent = silent;
}
Services.get('os').createNotification(options, event);
return true
}
/**
* 电源监控
*/
initPowerMonitor(args, event) {
const channel = 'controller.os.initPowerMonitor';
powerMonitor.on('on-ac', (e) => {
let data = {
type: 'on-ac',
msg: '接入了电源'
}
event.reply(`${channel}`, data)
});
powerMonitor.on('on-battery', (e) => {
let data = {
type: 'on-battery',
msg: '使用电池中'
}
event.reply(`${channel}`, data)
});
powerMonitor.on('lock-screen', (e) => {
let data = {
type: 'lock-screen',
msg: '锁屏了'
}
event.reply(`${channel}`, data)
});
powerMonitor.on('unlock-screen', (e) => {
let data = {
type: 'unlock-screen',
msg: '解锁了'
}
event.reply(`${channel}`, data)
});
return true
}
/**
* 获取屏幕信息
*/
getScreen(args) {
let data = [];
let res = {};
if (args == 0) {
let res = screen.getCursorScreenPoint();
data = [
{
title: '横坐标',
desc: res.x
},
{
title: '纵坐标',
desc: res.y
},
]
return data;
}
if (args == 1) {
res = screen.getPrimaryDisplay();
}
if (args == 2) {
let resArr = screen.getAllDisplays();
// 数组,只取一个吧
res = resArr[0];
}
// Log.info('[electron] [ipc] [example] [getScreen] res:', res);
data = [
{
title: '分辨率',
desc: res.bounds.width + ' x ' + res.bounds.height
},
{
title: '单色显示器',
desc: res.monochrome ? '是' : '否'
},
{
title: '色深',
desc: res. colorDepth
},
{
title: '色域',
desc: res.colorSpace
},
{
title: 'scaleFactor',
desc: res.scaleFactor
},
{
title: '加速器',
desc: res.accelerometerSupport
},
{
title: '触控',
desc: res.touchSupport == 'unknown' ? '不支持' : '支持'
},
]
return data;
}
/**
* 获取系统主题
*/
getTheme() {
let theme = 'system';
if (nativeTheme.shouldUseHighContrastColors) {
theme = 'light';
} else if (nativeTheme.shouldUseInvertedColorScheme) {
theme = 'dark';
}
return theme;
}
/**
* 设置系统主题
*/
setTheme(args) {
// TODO 好像没有什么明显效果
nativeTheme.themeSource = args;
return args;
}
}
OsController.toString = () => '[class OsController]';

101
electron/service/cross.js Normal file
View File

@@ -0,0 +1,101 @@
'use strict';
const { logger } = require('ee-core/log');
const { getExtraResourcesDir } = require('ee-core/ps');
const path = require("path");
const Is = require('ee-core/utils/is');
/**
* cross
* @class
*/
class CrossService {
/**
* create go service
* In the default configuration, services can be started with applications.
* Developers can turn off the configuration and create it manually.
*/
async createGoServer() {
// method 1: Use the default Settings
//const entity = await Cross.run(serviceName);
// method 2: Use custom configuration
const serviceName = "go";
const opt = {
name: 'goapp',
cmd: path.join(getExtraResourcesDir(), 'goapp'),
directory: getExtraResourcesDir(),
args: ['--port=7073'],
appExit: true,
}
const entity = await Cross.run(serviceName, opt);
logger.info('server name:', entity.name);
logger.info('server config:', entity.config);
logger.info('server url:', entity.getUrl());
return;
}
/**
* create java server
*/
async createJavaServer() {
const serviceName = "java";
const jarPath = path.join(getExtraResourcesDir(), 'java-app.jar');
const opt = {
name: 'javaapp',
cmd: path.join(getExtraResourcesDir(), 'jre1.8.0_201/bin/javaw.exe'),
directory: getExtraResourcesDir(),
args: ['-jar', '-server', '-Xms512M', '-Xmx512M', '-Xss512k', '-Dspring.profiles.active=prod', `-Dserver.port=18080`, `-Dlogging.file.path=${Ps.getLogDir()}`, `${jarPath}`],
appExit: false,
}
if (Is.macOS()) {
// Setup Java program
opt.cmd = path.join(Ps.getExtraResourcesDir(), 'jre1.8.0_201.jre/Contents/Home/bin/java');
}
if (Is.linux()) {
// Setup Java program
}
const entity = await Cross.run(serviceName, opt);
logger.info('server name:', entity.name);
logger.info('server config:', entity.config);
logger.info('server url:', Cross.getUrl(entity.name));
return;
}
/**
* create python service
* In the default configuration, services can be started with applications.
* Developers can turn off the configuration and create it manually.
*/
async createPythonServer() {
// method 1: Use the default Settings
//const entity = await Cross.run(serviceName);
// method 2: Use custom configuration
const serviceName = "python";
const opt = {
name: 'pyapp',
cmd: path.join(getExtraResourcesDir(), 'py', 'pyapp'),
directory: path.join(getExtraResourcesDir(), 'py'),
args: ['--port=7074'],
windowsExtname: true,
appExit: true,
}
const entity = await Cross.run(serviceName, opt);
logger.info('server name:', entity.name);
logger.info('server config:', entity.config);
logger.info('server url:', entity.getUrl());
return;
}
}
CrossService.toString = () => '[class CrossService]';
module.exports = {
CrossService,
crossService: new CrossService()
};

View File

@@ -0,0 +1,163 @@
'use strict';
const Storage = require('ee-core/storage');
const _ = require('lodash');
const path = require('path');
/**
* sqlite数据存储
* @class
*/
class SqlitedbService {
constructor () {
this.sqliteFile = 'sqlite-demo.db';
let sqliteOptions = {
driver: 'sqlite',
default: {
timeout: 6000,
verbose: console.log // 打印sql语法
}
}
this.demoSqliteDB = Storage.connection(this.sqliteFile, sqliteOptions);
}
/*
* 检查并创建表 (sqlite)
*/
async checkAndCreateTableSqlite(tableName = '') {
if (_.isEmpty(tableName)) {
throw new Error(`table name is required`);
}
// 检查表是否存在
const userTable = this.demoSqliteDB.db.prepare('SELECT * FROM sqlite_master WHERE type=? AND name = ?');
const result = userTable.get('table', tableName);
//console.log('result:', result);
if (result) {
return;
}
// 创建表
const create_table_user =
`CREATE TABLE ${tableName}
(
id INTEGER PRIMARY KEY AUTOINCREMENT,
name CHAR(50) NOT NULL,
age INT
);`
this.demoSqliteDB.db.exec(create_table_user);
}
/*
* 增 Test data (sqlite)
*/
async addTestDataSqlite(data) {
//console.log("add data:", data);
let table = 'user';
await this.checkAndCreateTableSqlite(table);
const insert = this.demoSqliteDB.db.prepare(`INSERT INTO ${table} (name, age) VALUES (@name, @age)`);
insert.run(data);
return true;
}
/*
* 删 Test data (sqlite)
*/
async delTestDataSqlite(name = '') {
//console.log("delete name:", name);
let table = 'user';
await this.checkAndCreateTableSqlite(table);
const delUser = this.demoSqliteDB.db.prepare(`DELETE FROM ${table} WHERE name = ?`);
delUser.run(name);
return true;
}
/*
* 改 Test data (sqlite)
*/
async updateTestDataSqlite(name= '', age = 0) {
//console.log("update :", {name, age});
let table = 'user';
await this.checkAndCreateTableSqlite(table);
const updateUser = this.demoSqliteDB.db.prepare(`UPDATE ${table} SET age = ? WHERE name = ?`);
updateUser.run(age, name);
return true;
}
/*
* 查 Test data (sqlite)
*/
async getTestDataSqlite(age = 0) {
//console.log("select :", {age});
let table = 'user';
await this.checkAndCreateTableSqlite(table);
const selectUser = this.demoSqliteDB.db.prepare(`SELECT * FROM ${table} WHERE age = @age`);
const users = selectUser.all({age: age});
//console.log("select users:", users);
return users;
}
/*
* all Test data (sqlite)
*/
async getAllTestDataSqlite() {
//console.log("select all user");
let table = 'user';
await this.checkAndCreateTableSqlite(table);
const selectAllUser = this.demoSqliteDB.db.prepare(`SELECT * FROM ${table} `);
const allUser = selectAllUser.all();
//console.log("select allUser:", allUser);
return allUser;
}
/*
* get data dir (sqlite)
*/
async getDataDir() {
const dir = this.demoSqliteDB.getStorageDir();
return dir;
}
/*
* set custom data dir (sqlite)
*/
async setCustomDataDir(dir) {
if (_.isEmpty(dir)) {
return;
}
// the absolute path of the db file
const dbFile = path.join(dir, this.sqliteFile);
const sqliteOptions = {
driver: 'sqlite',
default: {
timeout: 6000,
verbose: console.log
}
}
this.demoSqliteDB = Storage.connection(dbFile, sqliteOptions);
return;
}
}
SqlitedbService.toString = () => '[class SqlitedbService]';
module.exports = {
SqlitedbService,
sqlitedbService: new SqlitedbService()
};

View File

@@ -0,0 +1,30 @@
'use strict';
const { logger } = require('ee-core/log');
/**
* effect
* @class
*/
class EffectService {
/**
* hello
*/
async hello(args) {
let obj = {
status:'ok',
params: args
}
logger.info('EffectService obj:', obj);
return obj;
}
}
EffectService.toString = () => '[class EffectService]';
module.exports = {
EffectService,
effectService: new EffectService()
};

View File

@@ -1,16 +1,12 @@
'use strict';
const { Service } = require('ee-core');
const { logger } = require('ee-core/log');
/**
* 示例服务service层为单例
* 示例服务
* @class
*/
class ExampleService extends Service {
constructor(ctx) {
super(ctx);
}
class ExampleService {
/**
* test
@@ -21,9 +17,16 @@ class ExampleService extends Service {
params: args
}
logger.info('ExampleService obj:', obj);
//Services.get('framework').test('egg');
return obj;
}
}
ExampleService.toString = () => '[class ExampleService]';
module.exports = ExampleService;
module.exports = {
ExampleService,
exampleService: new ExampleService()
};

View File

@@ -0,0 +1,164 @@
'use strict';
const { logger } = require('ee-core/log');
const { ChildJob, ChildPoolJob } = require('ee-core/jobs');
const Ps = require('ee-core/ps');
/**
* framework
* @class
*/
class FrameworkService {
constructor() {
// 在构造函数中初始化一些变量
this.myTimer = null;
this.myJob = new ChildJob();
this.myJobPool = new ChildPoolJob();
this.taskForJob = {};
}
/**
* test
*/
async test(args) {
let obj = {
status:'ok',
params: args
}
logger.info('FrameworkService obj:', obj);
return obj;
}
/**
* ipc通信(双向)
*/
bothWayMessage(type, content, event) {
// 前端ipc频道 channel
const channel = 'controller.framework.ipcSendMsg';
if (type == 'start') {
// 每隔1秒向前端页面发送消息
// 用定时器模拟
this.myTimer = setInterval(function(e, c, msg) {
let timeNow = Date.now();
let data = msg + ':' + timeNow;
e.reply(`${c}`, data)
}, 1000, event, channel, content)
return '开始了'
} else if (type == 'end') {
clearInterval(this.myTimer);
return '停止了'
} else {
return 'ohther'
}
}
/**
* 执行任务
*/
doJob(jobId, action, event) {
let res = {};
let oneTask;
const channel = 'controller.framework.timerJobProgress';
if (action == 'create') {
// 执行任务及监听进度
let eventName = 'job-timer-progress-' + jobId;
const timerTask = this.myJob.exec('./jobs/example/timer', {jobId});
timerTask.emitter.on(eventName, (data) => {
logger.info('[main-process] timerTask, from TimerJob data:', data);
// 发送数据到渲染进程
event.sender.send(`${channel}`, data)
})
// 执行任务及监听进度 异步
// myjob.execPromise('./jobs/example/timer', {jobId}).then(task => {
// task.emitter.on(eventName, (data) => {
// Log.info('[main-process] timerTask, from TimerJob data:', data);
// // 发送数据到渲染进程
// event.sender.send(`${channel}`, data)
// })
// });
res.pid = timerTask.pid;
this.taskForJob[jobId] = timerTask;
}
if (action == 'close') {
oneTask = this.taskForJob[jobId];
oneTask.kill();
event.sender.send(`${channel}`, {jobId, number:0, pid:0});
}
if (action == 'pause') {
oneTask = this.taskForJob[jobId];
oneTask.callFunc('./jobs/example/timer', 'pause', jobId);
}
if (action == 'resume') {
oneTask = this.taskForJob[jobId];
oneTask.callFunc('./jobs/example/timer', 'resume', jobId, oneTask.pid);
}
return res;
}
/**
* 创建pool
*/
doCreatePool(num, event) {
const channel = 'controller.framework.createPoolNotice';
this.myJobPool.create(num).then(pids => {
event.reply(`${channel}`, pids);
});
}
/**
* 通过进程池执行任务
*/
doJobByPool(jobId, action, event) {
let res = {};
const channel = 'controller.framework.timerJobProgress';
if (action == 'run') {
// 异步-执行任务及监听进度
this.myJobPool.runPromise('./jobs/example/timer', {jobId}).then(task => {
// 监听器名称唯一,否则会出现重复监听。
// 任务完成时,需要移除监听器,防止内存泄漏
let eventName = 'job-timer-progress-' + jobId;
task.emitter.on(eventName, (data) => {
logger.info('[main-process] [ChildPoolJob] timerTask, from TimerJob data:', data);
// 发送数据到渲染进程
event.sender.send(`${channel}`, data)
// 如果收到任务完成的消息,移除监听器
if (data.end) {
task.emitter.removeAllListeners(eventName);
}
});
res.pid = task.pid;
});
}
return res;
}
/**
* 获取正在运行的 job 进程
*/
monitorJob() {
setInterval(() => {
let jobPids = this.myJob.getPids();
let jobPoolPids = this.myJobPool.getPids();
logger.info(`[main-process] [monitorJob] jobPids: ${jobPids}, jobPoolPids: ${jobPoolPids}`);
}, 5000)
}
}
FrameworkService.toString = () => '[class FrameworkService]';
module.exports = {
FrameworkService,
frameworkService: new FrameworkService()
};

View File

@@ -0,0 +1,28 @@
'use strict';
const { logger } = require('ee-core/log');
/**
* job 中使用的 service 不要继承 const { Service } = require('ee-core')
* 因为 Service 中会依赖 electron 的 api 导致错误
* @class
*/
class UserService {
/**
* hello
*/
async hello(args) {
let obj = {
status:'ok',
params: args
}
logger.info('UserService obj:', obj);
return obj;
}
}
UserService.toString = () => '[class UserService]';
module.exports = UserService;

88
electron/service/os.js Normal file
View File

@@ -0,0 +1,88 @@
'use strict';
const { BrowserView, Notification } = require('electron');
const { getMainWindow } = require('ee-core/electron/window');
/**
* os
* @class
*/
class OsService {
constructor() {
this.myBrowserView = null;
this.myNotification = null;
}
/**
* createBrowserView
*/
createBrowserView(contentUrl) {
// electron 实验性功能,慎用
const win = getMainWindow();
this.myBrowserView = new BrowserView();
win.setBrowserView(this.myBrowserView);
this.myBrowserView.setBounds({
x: 300,
y: 170,
width: 650,
height: 400
});
this.myBrowserView.webContents.loadURL(contentUrl);
}
/**
* removeBrowserView
*/
removeBrowserView() {
// one
this.myBrowserView.webContents.loadURL('about:blank')
// two - electron 11 remove destroy()
// this.myBrowserView.webContents.destroy();
// three
// this.myBrowserView.webContents.forcefullyCrashRenderer()
// fore
// this.myBrowserView.webContents.close
}
/**
* createNotification
*/
createNotification(options, event) {
const channel = 'controller.os.sendNotification';
this.myNotification = new Notification(options);
if (options.clickEvent) {
this.myNotification.on('click', (e) => {
let data = {
type: 'click',
msg: '您点击了通知消息'
}
event.reply(`${channel}`, data)
});
}
if (options.closeEvent) {
this.myNotification.on('close', (e) => {
let data = {
type: 'close',
msg: '您关闭了通知消息'
}
event.reply(`${channel}`, data)
});
}
this.myNotification.show();
}
}
OsService.toString = () => '[class OsService]';
module.exports = {
OsService,
osService: new OsService()
};