🏗️ 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 SystemService from "../service/SystemService";
import moment from "moment"; import moment from "moment";
import ResponseUtils from "../utils/ResponseUtils"; import ResponseUtils from "../utils/ResponseUtils";
import { dialog } from "electron"; import { BrowserWindow, dialog } from "electron";
import Logger from "../core/Logger"; import Logger from "../core/Logger";
import BeanFactory from "../core/BeanFactory";
class ConfigController extends BaseController { class ConfigController extends BaseController {
private readonly _serverService: ServerService; private readonly _serverService: ServerService;
@ -33,7 +34,7 @@ class ConfigController extends BaseController {
}) })
.catch((err: Error) => { .catch((err: Error) => {
Logger.error("ConfigController.saveConfig", err); 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) => { .catch((err: Error) => {
Logger.error("ConfigController.getServerConfig", err); 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) => { .catch((err: Error) => {
Logger.error("ConfigController.openAppData", err); 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) => { .catch((err: Error) => {
Logger.error("ConfigController.resetAllConfig", err); 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) => { .catch((err: Error) => {
Logger.error("ConfigController.exportConfig", err); 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) { importTomlConfig(req: ControllerParam) {
this._serverService const win: BrowserWindow = BeanFactory.getBean("win");
.importTomlConfig() dialog
.then(() => { .showOpenDialog(win, {
req.event.reply(req.channel, ResponseUtils.success()); properties: ["openFile"],
filters: [{ name: "Frpc Toml ConfigFile", extensions: ["toml"] }]
}) })
.catch((err: Error) => { .then(result => {
Logger.error("ConfigController.importTomlConfig", err); if (result.canceled) {
req.event.reply(req.channel, ResponseUtils.fail(err.message)); 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) => { .catch((err: Error) => {
Logger.error("LaunchController.launch", err); 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 => { .catch(err => {
Logger.error("LaunchController.terminate", 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) => { .catch((err: Error) => {
Logger.error("LogController.getFrpLogContent", err); 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) { if (data) {
ResponseUtils.success(); ResponseUtils.success();
} else { } else {
ResponseUtils.fail(); // ResponseUtils.fail();
} }
}) })
.catch((err: Error) => { .catch((err: Error) => {
Logger.error("LogController.openFrpcLogFile", err); 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) => { .catch((err: Error) => {
Logger.error("ProxyController.createProxy", err); 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) => { .catch((err: Error) => {
Logger.error("ProxyController.modifyProxy", err); 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) => { .catch((err: Error) => {
Logger.error("ProxyController.getAllProxies", err); 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) => { .catch((err: Error) => {
Logger.error("ProxyController.deleteProxy", err); 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) => { .catch((err: Error) => {
Logger.error("ProxyController.modifyProxyStatus", err); 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) => { .catch((err: Error) => {
Logger.error("ProxyController.getLocalPorts", err); 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) => { .catch((err: Error) => {
Logger.error("SystemController.openUrl", err); 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) => { .catch((err: Error) => {
Logger.error("SystemController.relaunchApp", err); 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) => { .catch((err: Error) => {
Logger.error("SystemController.openAppData", err); 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) { selectLocalFile(req: ControllerParam) {
const { name, extensions } = req.args; const { name, extensions } = req.args;
if (!extensions || extensions.length === 0) { 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"); const win: BrowserWindow = BeanFactory.getBean("win");
dialog dialog
@ -75,7 +76,7 @@ class SystemController {
}) })
.catch((err: Error) => { .catch((err: Error) => {
Logger.error("SystemController.selectLocalFile", err); 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) => { .catch((err: Error) => {
Logger.error("VersionController.getDownloadedVersions", err); 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) => { .catch((err: Error) => {
Logger.error("VersionController.downloadFrpVersion", err); 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) => { .catch((err: Error) => {
Logger.error("VersionController.deleteDownloadedVersion", err); 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) => { .catch((err: Error) => {
Logger.error("VersionController.importLocalFrpcVersion", err); 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 treeKill from "tree-kill";
import BeanFactory from "../core/BeanFactory"; import BeanFactory from "../core/BeanFactory";
import ResponseUtils from "../utils/ResponseUtils"; import ResponseUtils from "../utils/ResponseUtils";
import Logger from "../core/Logger"; import { BusinessError, ResponseCode } from "../core/BusinessError";
class FrpcProcessService { class FrpcProcessService {
private readonly _serverService: ServerService; private readonly _serverService: ServerService;
@ -37,7 +37,7 @@ class FrpcProcessService {
} }
const config = await this._serverService.getServerConfig(); const config = await this._serverService.getServerConfig();
if (!config) { if (!config) {
throw new Error("请先进行配置"); throw new BusinessError(ResponseCode.NOT_CONFIG);
} }
const version = await this._versionDao.findByGithubReleaseId( const version = await this._versionDao.findByGithubReleaseId(
config.frpcVersion config.frpcVersion

View File

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

View File

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

View File

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

View File

@ -380,6 +380,8 @@ onMounted(() => {
}); });
on(ipcRouters.SERVER.importTomlConfig, data => { on(ipcRouters.SERVER.importTomlConfig, data => {
const { canceled, path } = data;
if (!canceled) {
// //
confetti({ confetti({
zIndex: 12002, zIndex: 12002,
@ -394,6 +396,7 @@ onMounted(() => {
}).then(() => { }).then(() => {
send(ipcRouters.SYSTEM.relaunchApp); send(ipcRouters.SYSTEM.relaunchApp);
}); });
}
}); });
on(ipcRouters.SERVER.exportConfig, data => { on(ipcRouters.SERVER.exportConfig, data => {

View File

@ -2,9 +2,11 @@
import { defineComponent, onMounted, onUnmounted, ref } from "vue"; import { defineComponent, onMounted, onUnmounted, ref } from "vue";
import Breadcrumb from "@/layout/compoenets/Breadcrumb.vue"; import Breadcrumb from "@/layout/compoenets/Breadcrumb.vue";
import { useDebounceFn } from "@vueuse/core"; import { useDebounceFn } from "@vueuse/core";
import { on, onListener, removeRouterListeners2, send } from "@/utils/ipcUtils"; import { on, removeRouterListeners, send } from "@/utils/ipcUtils";
import { ipcRouters, listeners } from "../../../electron/core/IpcRouter"; import { ipcRouters } from "../../../electron/core/IpcRouter";
import { useFrpcProcessStore } from "@/store/frpcProcess"; import { useFrpcProcessStore } from "@/store/frpcProcess";
import { ElMessageBox } from "element-plus";
import router from "@/router";
defineComponent({ defineComponent({
name: "Home" name: "Home"
@ -12,7 +14,6 @@ defineComponent({
const frpcProcessStore = useFrpcProcessStore(); const frpcProcessStore = useFrpcProcessStore();
// const running = ref(false); // const running = ref(false);
const loading = ref(false); const loading = ref(false);
@ -34,14 +35,28 @@ const handleButtonClick = useDebounceFn(() => {
}, 300); }, 300);
onMounted(() => { onMounted(() => {
on(
ipcRouters.LAUNCH.launch,
() => {
on(ipcRouters.LAUNCH.launch, () => {
// send(ipcRouters.LAUNCH.getStatus); // send(ipcRouters.LAUNCH.getStatus);
frpcProcessStore.refreshRunning(); frpcProcessStore.refreshRunning();
loading.value = false; 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, () => { on(ipcRouters.LAUNCH.terminate, () => {
// send(ipcRouters.LAUNCH.getStatus); // send(ipcRouters.LAUNCH.getStatus);
@ -66,6 +81,8 @@ onMounted(() => {
onUnmounted(() => { onUnmounted(() => {
// ipcRenderer.removeAllListeners("Home.frpc.start.error.hook"); // ipcRenderer.removeAllListeners("Home.frpc.start.error.hook");
// removeRouterListeners2(listeners.watchFrpcProcess); // removeRouterListeners2(listeners.watchFrpcProcess);
removeRouterListeners(ipcRouters.LAUNCH.launch);
removeRouterListeners(ipcRouters.LAUNCH.terminate);
}); });
</script> </script>

2
types/core.d.ts vendored
View File

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