🏗️ refactor error handling in controllers to utilize BusinessError and enhance response structure

This commit is contained in:
刘嘉伟 2025-02-25 23:01:57 +08:00
parent fb2846c0b5
commit efd68fb453
14 changed files with 183 additions and 71 deletions

View File

@ -6,8 +6,9 @@ import FrpcProcessService from "../service/FrpcProcessService";
import SystemService from "../service/SystemService";
import moment from "moment";
import ResponseUtils from "../utils/ResponseUtils";
import { dialog } from "electron";
import { BrowserWindow, dialog } from "electron";
import Logger from "../core/Logger";
import BeanFactory from "../core/BeanFactory";
class ConfigController extends BaseController {
private readonly _serverService: ServerService;
@ -33,7 +34,7 @@ class ConfigController extends BaseController {
})
.catch((err: Error) => {
Logger.error("ConfigController.saveConfig", err);
req.event.reply(req.channel, ResponseUtils.fail(err.message));
req.event.reply(req.channel, ResponseUtils.fail(err));
});
}
@ -45,7 +46,7 @@ class ConfigController extends BaseController {
})
.catch((err: Error) => {
Logger.error("ConfigController.getServerConfig", err);
req.event.reply(req.channel, ResponseUtils.fail(err.message));
req.event.reply(req.channel, ResponseUtils.fail(err));
});
}
@ -57,7 +58,7 @@ class ConfigController extends BaseController {
})
.catch((err: Error) => {
Logger.error("ConfigController.openAppData", err);
req.event.reply(req.channel, ResponseUtils.fail(err.message));
req.event.reply(req.channel, ResponseUtils.fail(err));
});
}
@ -91,7 +92,7 @@ class ConfigController extends BaseController {
})
.catch((err: Error) => {
Logger.error("ConfigController.resetAllConfig", err);
req.event.reply(req.channel, ResponseUtils.fail(err.message));
req.event.reply(req.channel, ResponseUtils.fail(err));
});
}
@ -126,20 +127,48 @@ class ConfigController extends BaseController {
})
.catch((err: Error) => {
Logger.error("ConfigController.exportConfig", err);
req.event.reply(req.channel, ResponseUtils.fail(err.message));
req.event.reply(req.channel, ResponseUtils.fail(err));
});
}
importTomlConfig(req: ControllerParam) {
this._serverService
.importTomlConfig()
.then(() => {
req.event.reply(req.channel, ResponseUtils.success());
const win: BrowserWindow = BeanFactory.getBean("win");
dialog
.showOpenDialog(win, {
properties: ["openFile"],
filters: [{ name: "Frpc Toml ConfigFile", extensions: ["toml"] }]
})
.catch((err: Error) => {
Logger.error("ConfigController.importTomlConfig", err);
req.event.reply(req.channel, ResponseUtils.fail(err.message));
.then(result => {
if (result.canceled) {
req.event.reply(
req.channel,
ResponseUtils.success({
canceled: true,
path: ""
})
);
} else {
req.event.reply(
req.channel,
ResponseUtils.success({
canceled: false,
path: ""
})
);
}
});
// if (result.canceled) {
// } else {
// }
// this._serverService
// .importTomlConfig()
// .then(() => {
// req.event.reply(req.channel, ResponseUtils.success());
// })
// .catch((err: Error) => {
// Logger.error("ConfigController.importTomlConfig", err);
// req.event.reply(req.channel, ResponseUtils.fail(err));
// });
}
}

View File

@ -19,7 +19,7 @@ class LaunchController extends BaseController {
})
.catch((err: Error) => {
Logger.error("LaunchController.launch", err);
req.event.reply(req.channel, ResponseUtils.fail(err.message));
req.event.reply(req.channel, ResponseUtils.fail(err));
});
}
@ -31,6 +31,7 @@ class LaunchController extends BaseController {
})
.catch(err => {
Logger.error("LaunchController.terminate", err);
req.event.reply(req.channel, ResponseUtils.fail(err));
});
}

View File

@ -19,7 +19,7 @@ class LogController extends BaseController {
})
.catch((err: Error) => {
Logger.error("LogController.getFrpLogContent", err);
req.event.reply(req.channel, ResponseUtils.fail(err.message));
req.event.reply(req.channel, ResponseUtils.fail(err));
});
}
@ -37,12 +37,12 @@ class LogController extends BaseController {
if (data) {
ResponseUtils.success();
} else {
ResponseUtils.fail();
// ResponseUtils.fail();
}
})
.catch((err: Error) => {
Logger.error("LogController.openFrpcLogFile", err);
req.event.reply(req.channel, ResponseUtils.fail(err.message));
req.event.reply(req.channel, ResponseUtils.fail(err));
});
}
}

View File

@ -22,7 +22,7 @@ class ProxyController extends BaseController {
})
.catch((err: Error) => {
Logger.error("ProxyController.createProxy", err);
req.event.reply(req.channel, ResponseUtils.fail(err.message));
req.event.reply(req.channel, ResponseUtils.fail(err));
});
}
@ -34,7 +34,7 @@ class ProxyController extends BaseController {
})
.catch((err: Error) => {
Logger.error("ProxyController.modifyProxy", err);
req.event.reply(req.channel, ResponseUtils.fail(err.message));
req.event.reply(req.channel, ResponseUtils.fail(err));
});
}
@ -46,7 +46,7 @@ class ProxyController extends BaseController {
})
.catch((err: Error) => {
Logger.error("ProxyController.getAllProxies", err);
req.event.reply(req.channel, ResponseUtils.fail(err.message));
req.event.reply(req.channel, ResponseUtils.fail(err));
});
}
@ -58,7 +58,7 @@ class ProxyController extends BaseController {
})
.catch((err: Error) => {
Logger.error("ProxyController.deleteProxy", err);
req.event.reply(req.channel, ResponseUtils.fail(err.message));
req.event.reply(req.channel, ResponseUtils.fail(err));
});
}
@ -70,7 +70,7 @@ class ProxyController extends BaseController {
})
.catch((err: Error) => {
Logger.error("ProxyController.modifyProxyStatus", err);
req.event.reply(req.channel, ResponseUtils.fail(err.message));
req.event.reply(req.channel, ResponseUtils.fail(err));
});
}
@ -82,7 +82,7 @@ class ProxyController extends BaseController {
})
.catch((err: Error) => {
Logger.error("ProxyController.getLocalPorts", err);
req.event.reply(req.channel, ResponseUtils.fail(err.message));
req.event.reply(req.channel, ResponseUtils.fail(err));
});
}
}

View File

@ -20,7 +20,7 @@ class SystemController {
})
.catch((err: Error) => {
Logger.error("SystemController.openUrl", err);
req.event.reply(req.channel, ResponseUtils.fail(err.message));
req.event.reply(req.channel, ResponseUtils.fail(err));
});
}
@ -32,7 +32,7 @@ class SystemController {
})
.catch((err: Error) => {
Logger.error("SystemController.relaunchApp", err);
req.event.reply(req.channel, ResponseUtils.fail(err.message));
req.event.reply(req.channel, ResponseUtils.fail(err));
});
}
@ -44,14 +44,15 @@ class SystemController {
})
.catch((err: Error) => {
Logger.error("SystemController.openAppData", err);
req.event.reply(req.channel, ResponseUtils.fail(err.message));
req.event.reply(req.channel, ResponseUtils.fail(err));
});
}
selectLocalFile(req: ControllerParam) {
const { name, extensions } = req.args;
if (!extensions || extensions.length === 0) {
req.event.reply(req.channel, ResponseUtils.fail("可选择扩展名不能为空"));
return;
// req.event.reply(req.channel, ResponseUtils.fail("可选择扩展名不能为空"));
}
const win: BrowserWindow = BeanFactory.getBean("win");
dialog
@ -75,7 +76,7 @@ class SystemController {
})
.catch((err: Error) => {
Logger.error("SystemController.selectLocalFile", err);
req.event.reply(req.channel, ResponseUtils.fail(err.message));
req.event.reply(req.channel, ResponseUtils.fail(err));
});
}
}

View File

@ -36,7 +36,7 @@ class VersionController extends BaseController {
})
.catch((err: Error) => {
Logger.error("VersionController.getDownloadedVersions", err);
req.event.reply(req.channel, ResponseUtils.fail(err.message));
req.event.reply(req.channel, ResponseUtils.fail(err));
});
}
@ -64,7 +64,7 @@ class VersionController extends BaseController {
})
.catch((err: Error) => {
Logger.error("VersionController.downloadFrpVersion", err);
req.event.reply(req.channel, ResponseUtils.fail(err.message));
req.event.reply(req.channel, ResponseUtils.fail(err));
});
}
@ -76,7 +76,7 @@ class VersionController extends BaseController {
})
.catch((err: Error) => {
Logger.error("VersionController.deleteDownloadedVersion", err);
req.event.reply(req.channel, ResponseUtils.fail(err.message));
req.event.reply(req.channel, ResponseUtils.fail(err));
});
}
@ -88,7 +88,7 @@ class VersionController extends BaseController {
})
.catch((err: Error) => {
Logger.error("VersionController.importLocalFrpcVersion", err);
req.event.reply(req.channel, ResponseUtils.fail(err.message));
req.event.reply(req.channel, ResponseUtils.fail(err));
});
}
}

View File

@ -0,0 +1,28 @@
enum ResponseCode {
SUCCESS = "A1000:successful.",
INTERNAL_ERROR = "B1000:internal error.",
NOT_CONFIG = "B1001:未配置"
}
class BusinessError extends Error {
private readonly _bizCode: string;
// constructor(bizCode: string, message: string) {
// super(message);
// this.bizCode = bizCode;
// this.name = "BusinessError";
// }
constructor(bizErrorEnum: ResponseCode) {
const [bizCode, message] = bizErrorEnum.split(":");
super(message);
this._bizCode = bizCode;
this.name = "BusinessError";
}
get bizCode(): string {
return this._bizCode;
}
}
export { BusinessError, ResponseCode };

View File

@ -6,7 +6,7 @@ import { app, BrowserWindow, Notification } from "electron";
import treeKill from "tree-kill";
import BeanFactory from "../core/BeanFactory";
import ResponseUtils from "../utils/ResponseUtils";
import Logger from "../core/Logger";
import { BusinessError, ResponseCode } from "../core/BusinessError";
class FrpcProcessService {
private readonly _serverService: ServerService;
@ -37,7 +37,7 @@ class FrpcProcessService {
}
const config = await this._serverService.getServerConfig();
if (!config) {
throw new Error("请先进行配置");
throw new BusinessError(ResponseCode.NOT_CONFIG);
}
const version = await this._versionDao.findByGithubReleaseId(
config.frpcVersion

View File

@ -83,13 +83,13 @@ class ServerService extends BaseService<OpenSourceFrpcDesktopServer> {
]
});
if (result.canceled) {
}else {
const filePath = result.filePaths[0];
const fileExtension = path.extname(filePath);
if (fileExtension === GlobalConstant.TOML_EXT) {
const tomlData = fs.readFileSync(filePath, "utf-8");
const sourceConfig = TOML.parse(tomlData);
console.log('1');
// todo Persistent
} else {
throw new Error(`导入失败,暂不支持 ${fileExtension} 格式文件`);
}

View File

@ -1,16 +1,35 @@
import { BusinessError, ResponseCode } from "../core/BusinessError";
class ResponseUtils {
public static success<T>(data?: any, message?: string) {
const [bizCode, message2] = ResponseCode.SUCCESS.split(":");
const resp: ApiResponse<T> = {
success: true,
bizCode: bizCode,
data: data,
message: message || "successful."
message: message || message2
};
return resp;
}
public static fail(message?: string) {
// public static fail(bizCode?: string, message?: string) {
// const resp: ApiResponse<any> = {
// success: false,
// bizCode: bizCode,
// data: null,
// message: message || "internal error."
// };
// return resp;
// }
public static fail(err: Error) {
let bizCode = "";
let message = "";
if (err instanceof BusinessError) {
bizCode = (err as BusinessError).bizCode;
message = (err as BusinessError).message;
}
const resp: ApiResponse<any> = {
success: false,
bizCode: bizCode,
data: null,
message: message || "internal error."
};

View File

@ -1,4 +1,5 @@
import { ipcRenderer } from "electron";
import { ElMessage } from "element-plus";
export const send = (router: IpcRouter, params?: any) => {
ipcRenderer.send(router.path, params);
@ -21,12 +22,25 @@ export const send = (router: IpcRouter, params?: any) => {
// });
// };
export const on = (router: IpcRouter, listerHandler: (data: any) => void) => {
export const on = (
router: IpcRouter,
listerHandler: (data: any) => void,
errHandler?: (bizCode: string, message: string) => void
) => {
ipcRenderer.on(`${router.path}:hook`, (event, args: ApiResponse<any>) => {
const { success, data, message } = args;
if (success) {
const { bizCode, data, message } = args;
if (bizCode === "A1000") {
listerHandler(data);
} else {
if (errHandler) {
errHandler(bizCode, message);
} else {
// ElMessageBox.alert(message,"出错了");
ElMessage({
message: message,
type: "error"
});
}
// reject(new Error(message));
}
});
@ -38,8 +52,8 @@ export const onListener = (
) => {
// return new Promise((resolve, reject) => {
ipcRenderer.on(`${listener.channel}`, (event, args: ApiResponse<any>) => {
const { success, data, message } = args;
if (success) {
const { bizCode, data, message } = args;
if (bizCode === "A1000") {
listerHandler(data);
}
});

View File

@ -380,20 +380,23 @@ onMounted(() => {
});
on(ipcRouters.SERVER.importTomlConfig, data => {
//
confetti({
zIndex: 12002,
particleCount: 200,
spread: 70,
origin: { y: 0.6 }
});
ElMessageBox.alert("🎉 恭喜你,导入成功 请重启软件", `提示`, {
closeOnClickModal: false,
showClose: false,
confirmButtonText: "立即重启"
}).then(() => {
send(ipcRouters.SYSTEM.relaunchApp);
});
const { canceled, path } = data;
if (!canceled) {
//
confetti({
zIndex: 12002,
particleCount: 200,
spread: 70,
origin: { y: 0.6 }
});
ElMessageBox.alert("🎉 恭喜你,导入成功 请重启软件", `提示`, {
closeOnClickModal: false,
showClose: false,
confirmButtonText: "立即重启"
}).then(() => {
send(ipcRouters.SYSTEM.relaunchApp);
});
}
});
on(ipcRouters.SERVER.exportConfig, data => {

View File

@ -2,9 +2,11 @@
import { defineComponent, onMounted, onUnmounted, ref } from "vue";
import Breadcrumb from "@/layout/compoenets/Breadcrumb.vue";
import { useDebounceFn } from "@vueuse/core";
import { on, onListener, removeRouterListeners2, send } from "@/utils/ipcUtils";
import { ipcRouters, listeners } from "../../../electron/core/IpcRouter";
import { on, removeRouterListeners, send } from "@/utils/ipcUtils";
import { ipcRouters } from "../../../electron/core/IpcRouter";
import { useFrpcProcessStore } from "@/store/frpcProcess";
import { ElMessageBox } from "element-plus";
import router from "@/router";
defineComponent({
name: "Home"
@ -12,7 +14,6 @@ defineComponent({
const frpcProcessStore = useFrpcProcessStore();
// const running = ref(false);
const loading = ref(false);
@ -34,14 +35,28 @@ const handleButtonClick = useDebounceFn(() => {
}, 300);
onMounted(() => {
on(ipcRouters.LAUNCH.launch, () => {
// send(ipcRouters.LAUNCH.getStatus);
frpcProcessStore.refreshRunning();
loading.value = false;
});
on(
ipcRouters.LAUNCH.launch,
() => {
// send(ipcRouters.LAUNCH.getStatus);
frpcProcessStore.refreshRunning();
loading.value = false;
},
(bizCode: string, message: string) => {
if (bizCode === "B1001") {
ElMessageBox.alert("请先前往设置页面,修改配置后再启动", "提示", {
showCancelButton: true,
cancelButtonText: "取消",
confirmButtonText: "去设置"
}).then(() => {
router.replace({
name: "Config"
});
});
}
loading.value = false;
}
);
on(ipcRouters.LAUNCH.terminate, () => {
// send(ipcRouters.LAUNCH.getStatus);
@ -66,6 +81,8 @@ onMounted(() => {
onUnmounted(() => {
// ipcRenderer.removeAllListeners("Home.frpc.start.error.hook");
// removeRouterListeners2(listeners.watchFrpcProcess);
removeRouterListeners(ipcRouters.LAUNCH.launch);
removeRouterListeners(ipcRouters.LAUNCH.terminate);
});
</script>

2
types/core.d.ts vendored
View File

@ -1,5 +1,5 @@
interface ApiResponse<T> {
success: boolean;
bizCode: string;
data: T;
message: string;
}