Electron + Vue3开源跨平台壁纸工具实战(六)子进程服务

124 阅读6分钟

fbw_social_preview.png

系列

Electron + Vue3开源跨平台壁纸工具实战(一)

Electron + Vue3开源跨平台壁纸工具实战(二)本地运行

Electron + Vue3开源跨平台壁纸工具实战(三)主进程

Electron + Vue3开源跨平台壁纸工具实战(四)主进程-数据管理(1)

Electron + Vue3开源跨平台壁纸工具实战(五)主进程-数据管理(2)

Electron + Vue3开源跨平台壁纸工具实战(六)子进程服务

Electron + Vue3开源跨平台壁纸工具实战(七)进程通信

Electron + Vue3开源跨平台壁纸工具实战(八)主进程-核心功能

Electron + Vue3开源跨平台壁纸工具实战(九)子进程服务(2)

Electron + Vue3开源跨平台壁纸工具实战(十)渲染进程

源码

省流点我进Github获取源码,欢迎fork、star、PR

子进程服务

目录结构


├── main/                       # Electron主进程
│   └── child_server/           # 子进程服务
│   │   ├── file_server/        # 文件处理服务
│   │   │   └── index.mjs       # 子进程服务入口
│   │   └── h5_server/          # H5后台服务
│   │   │   ├── api/            # Koa路由接口
│   │   │   ├── socket/         # Socket服务端
│   │   │   ├── server.mjs      # Koa服务
│   │   │   └── index.mjs       # 子进程服务入口
│   │   ├── ChildServer.mjs     # 子进程服务基类
│   │   └── index.mjs           # 子进程入口

子服务入口

通过ChildServer来 fork 子进程服务,这里用到了electron-vite 中的 ?modulePath`

这里导出的是两个服务的创建函数,用于在主进程中创建

// main/child_server/index.mjs

import ChildServer from './ChildServer.mjs'
import fileServerPath from './file_server/index.mjs?modulePath'
import h5ServerPath from './h5_server/index.mjs?modulePath'

// 文件处理子进程服务
export const createFileServer = () => new ChildServer('fileServer', fileServerPath)
// h5服务子进程服务
export const createH5Server = () => new ChildServer('h5Server', h5ServerPath)

ChildServer主要功能:

  • 统一子进程服务管理、数据通信
// main/child_server/ChildServer.mjs

import { utilityProcess, MessageChannelMain } from 'electron'

export default class ChildServer {
  #serverName
  #serverPath
  #child
  #port2

  constructor(serverName, serverPath) {
    global.logger.info(
      `ChildServer INIT:: serverName => ${serverName}, serverPath => ${serverPath}`
    )
    this.#serverName = serverName
    this.#serverPath = serverPath
    this.#child = null
    this.#port2 = null
  }

  start({ options, onMessage = () => {} } = {}) {
    const { port1, port2 } = new MessageChannelMain()
    this.#child = utilityProcess.fork(this.#serverPath, options)
    global.logger.info(`ChildServer START:: serverName => ${this.#serverName}`)
    this.#port2 = port2

    this.#child.on('exit', () => {
      global.logger.info(`ChildServer EXIT:: serverName => ${this.#serverName}`)
      this.#child = null
      this.#port2 = null
    })

    this.#port2.on('message', onMessage)

    this.#port2.start()
    // 初始消息
    this.#child.postMessage(
      {
        serverName: this.#serverName,
        event: 'SERVER_FORKED'
      },
      [port1]
    )
    // 服务启动消息
    this.postMessage({
      serverName: this.#serverName,
      event: 'SERVER_START'
    })
  }

  stop(callback) {
    global.logger.info(`ChildServer STOP:: serverName => ${this.#serverName}`)
    if (!this.#child) {
      global.logger.info(
        `ChildServer STOP FAILED:: SERVER NOT START, serverName => ${this.#serverName}`
      )
      return
    }
    const isSuccess = this.#child?.kill()
    typeof callback === 'function' && callback(isSuccess)
  }

  restart({ params, onMessage = () => {}, stopCallback = () => {} } = {}) {
    this.stop(stopCallback)
    this.start({ params, onMessage })
  }

  postMessage(data) {
    this.#port2?.postMessage(data)
  }
}

文件服务子进程

服务入口

file_server主要功能:

  • 监听主进程刷新目录REFRESH_DIRECTORY事件
  • 监听主进程处理图片质量HANDLE_IMAGE_QUALITY事件

这里将比较耗时的刷新、计算任务放入子进程处理,主进程只进行任务管理、数据入库操作,防止主进程被耗时任务卡死

// main/child_server/file_server/index.mjs

/**
 * 文件服务子进程
 * */
import { readDirRecursive, calculateImageByPath } from '../../utils/utils.mjs'

process.parentPort.on('message', (e) => {
  const [port] = e.ports

  const handleLogger = (type = 'info') => {
    return (data) => {
      if (!data) {
        return
      }
      const postData = {
        event: 'SERVER_LOG',
        level: type,
        msg: ''
      }
      if (typeof data === 'string') {
        postData.msg = data
      } else if (typeof data === 'object') {
        postData.msg = JSON.stringify(data)
      }
      port.postMessage(postData)
    }
  }
  const logger = {
    info: handleLogger('info'),
    warn: handleLogger('warn'),
    error: handleLogger('error')
  }
  // 监听消息
  port.on('message', async (e) => {
    const { data } = e

    // 分批处理大量文件
    const processBatch = async (files, batchSize = 1000) => {
      const results = []
      for (let i = 0; i < files.length; i += batchSize) {
        const batch = files.slice(i, i + batchSize)
        results.push(...batch)

        // 允许事件循环处理其他任务
        await new Promise((resolve) => setTimeout(resolve, 0))
      }
      return results
    }

    if (data.event === 'SERVER_START') {
      port.postMessage({
        event: 'SERVER_START::SUCCESS'
      })
    } else if (data.event === 'REFRESH_DIRECTORY') {
      const readDirTime = {
        start: Date.now(),
        end: Date.now()
      }
      try {
        // 获取现有文件列表(如果有)
        const existingFiles = data.existingFiles || []

        const fileMap = new Map()

        // 并行处理多个目录
        const dirPromises = data.folderPaths.map((folderPath) =>
          readDirRecursive(data.resourceName, folderPath, data.allowedFileExt, existingFiles)
        )
        // 等待所有目录处理完成
        const results = await Promise.all(dirPromises)

        // 合并结果
        for (const fileList of results) {
          if (fileList && fileList.length) {
            for (const item of fileList) {
              fileMap.set(item.filePath, item)
            }
          }
        }

        readDirTime.end = Date.now()

        // 添加统计信息
        const stats = {
          newFiles: fileMap.size,
          modifiedFiles: 0,
          totalProcessed: fileMap.size
        }

        // 对于大量文件,使用批量处理
        if (fileMap.size > 5000) {
          // 先发送一个处理中的消息
          port.postMessage({
            event: 'REFRESH_DIRECTORY::PROCESSING',
            isManual: data.isManual,
            resourceName: data.resourceName,
            totalFiles: fileMap.size,
            refreshDirStartTime: data.refreshDirStartTime,
            readDirTime
          })

          // 分批处理文件
          const batchedList = await processBatch([...fileMap.values()], 1000)

          // 发送最终结果
          port.postMessage({
            event: 'REFRESH_DIRECTORY::SUCCESS',
            isManual: data.isManual,
            resourceName: data.resourceName,
            list: batchedList,
            stats,
            refreshDirStartTime: data.refreshDirStartTime,
            readDirTime
          })
        } else {
          // 对于少量文件,直接发送
          port.postMessage({
            event: 'REFRESH_DIRECTORY::SUCCESS',
            isManual: data.isManual,
            resourceName: data.resourceName,
            list: [...fileMap.values()],
            stats,
            refreshDirStartTime: data.refreshDirStartTime,
            readDirTime
          })
        }
      } catch (err) {
        logger.error(`[FileServer] ERROR => 刷新资源目录失败: ${err}`)
        readDirTime.end = Date.now()
        port.postMessage({
          event: 'REFRESH_DIRECTORY::FAIL',
          isManual: data.isManual,
          resourceName: data.resourceName,
          list: [],
          refreshDirStartTime: data.refreshDirStartTime,
          readDirTime
        })
      }
    } else if (data.event === 'HANDLE_IMAGE_QUALITY') {
      try {
        const { list } = data
        const ret = []
        for (let i = 0; i < list.length; i++) {
          const imgData = await calculateImageByPath(list[i].filePath)
          ret.push({
            id: list[i].id,
            ...imgData
          })
        }
        port.postMessage({
          event: 'HANDLE_IMAGE_QUALITY::SUCCESS',
          resourceName: data.resourceName,
          list: ret
        })
      } catch (err) {
        logger.error(`[FileServer] ERROR => 处理图片质量失败: ${err}`)
        port.postMessage({
          event: 'HANDLE_IMAGE_QUALITY::FAIL',
          resourceName: data.resourceName,
          list: []
        })
      }
    }
  })

  port.start()
})

H5服务子进程

服务入口

服务入口主要功能:

  • 启动H5服务
  • 设置数据广播同步
// main/child_server/h5_server/index.mjs

/**
 * h5服务子进程
 * */
import DatabaseManager from '../../store/DatabaseManager.mjs'
import SettingManager from '../../store/SettingManager.mjs'
import ResourcesManager from '../../store/ResourcesManager.mjs'
import FileManager from '../../store/FileManager.mjs'
import server from './server.mjs'

process.parentPort.on('message', (e) => {
  const [port] = e.ports

  const handleLogger = (type = 'info') => {
    return (data) => {
      if (!data) {
        return
      }
      const postData = {
        event: 'SERVER_LOG',
        level: type,
        msg: ''
      }
      if (typeof data === 'string') {
        postData.msg = data
      } else if (typeof data === 'object') {
        postData.msg = JSON.stringify(data)
      }
      port.postMessage(postData)
    }
  }
  const logger = {
    info: handleLogger('info'),
    warn: handleLogger('warn'),
    error: handleLogger('error')
  }
  const postMessage = (data) => {
    if (!data) {
      return
    }
    port.postMessage(data)
  }
  let dbManager
  let settingManager
  let resourcesManager
  let fileManager
  let ioServer
  // 监听消息
  port.on('message', async (e) => {
    try {
      const { data } = e
      // 启动h5服务
      if (data.event === 'SERVER_START') {
        // 初始化数据库管理器
        dbManager = DatabaseManager.getInstance(logger)
        await dbManager.waitForInitialization()

        // 初始化各种管理器并等待它们初始化完成
        settingManager = SettingManager.getInstance(logger, dbManager)
        await settingManager.waitForInitialization()

        fileManager = FileManager.getInstance(logger, dbManager, settingManager)
        resourcesManager = ResourcesManager.getInstance(
          logger,
          dbManager,
          settingManager,
          fileManager
        )
        const serverRes = await server({
          dbManager,
          settingManager,
          resourcesManager,
          fileManager,
          logger,
          postMessage,
          onStartSuccess: (url) => {
            port.postMessage({
              event: 'SERVER_START::SUCCESS',
              url
            })
          },
          onStartFail: (data) => {
            port.postMessage({
              event: 'SERVER_START::FAIL',
              ...data
            })
          }
        })
        ioServer = serverRes.ioServer
      } else if (data.event === 'APP_SETTING_UPDATED') {
        await settingManager.getSettingData()
        // 广播设置更新给所有客户端
        ioServer?.emit('settingUpdated', {
          success: true,
          data: settingManager.settingData
        })
      }
    } catch (err) {
      logger.error(`[H5Server] ERROR => ${err}`)
    }
  })

  port.start()
})

H5服务

server主要功能:

  • 使用Koa创建本机IPhttp\https的服务,这里使用了自签名证书主要是为了H5前端可以控制屏幕常亮
  • 使用@koa/router创建接口服务,用于处理图片加载请求
  • 使用@koa-static创建静态资源服务,用于支撑H5页面渲染,注意在打包前后资源目录路径不同
  • 使用socket.io创建Socket服务,用于数据查询、设置数据更新、多端数据同步
// main/child_server/h5_server/server.mjs

import Koa from 'koa'
import KoaRouter from '@koa/router'
import staticServe from 'koa-static'
import { bodyParser } from '@koa/bodyparser'
import { Server } from 'socket.io'
import http from 'http'
import https from 'https' // 添加https模块
import fs from 'fs' // 添加fs模块用于读取证书文件
import path from 'path'
import { fileURLToPath } from 'url'
import { getLocalIP, findAvailablePort, generateSelfSignedCert } from '../../utils/utils.mjs' // 添加证书生成函数
import useApi from './api/index.mjs'
import { t } from '../../../i18n/server.js'
import setupSocketIO from './socket/index.mjs'

// 创建 Koa 服务器
export default async ({
  port = 8888,
  host = '0.0.0.0',
  useHttps = true, // 添加HTTPS选项
  dbManager,
  settingManager,
  resourcesManager,
  fileManager,
  logger = () => {},
  postMessage = () => {},
  onStartSuccess = () => {},
  onStartFail = () => {}
} = {}) => {
  let httpServer
  let ioServer
  try {
    const __dirname = path.dirname(fileURLToPath(import.meta.url))
    const isProduction = process.env.NODE_ENV === 'production'
    port = await findAvailablePort(port)
    host = getLocalIP()

    // 创建 Koa 服务器
    const app = new Koa()

    // 创建服务器 (HTTP 或 HTTPS)
    if (useHttps) {
      // 证书路径
      const certPath = process.env.FBW_CERTS_PATH
      let sslOptions

      // 检查证书文件是否存在
      try {
        sslOptions = {
          key: fs.readFileSync(path.join(certPath, 'private.key')),
          cert: fs.readFileSync(path.join(certPath, 'certificate.crt'))
        }
      } catch (err) {
        // 证书不存在,生成自签名证书
        logger.info('[H5Server] INFO => 未找到SSL证书,正在生成自签名证书...')

        // 确保证书目录存在
        if (!fs.existsSync(certPath)) {
          fs.mkdirSync(certPath, { recursive: true })
        }

        // 生成自签名证书
        const { key, cert } = generateSelfSignedCert(host)

        // 保存证书
        fs.writeFileSync(path.join(certPath, 'private.key'), key)
        fs.writeFileSync(path.join(certPath, 'certificate.crt'), cert)

        sslOptions = { key, cert }
      }

      // 创建HTTPS服务器
      httpServer = https.createServer(sslOptions, app.callback())
      logger.info('[H5Server] INFO => 已创建HTTPS服务器')
    } else {
      // 创建HTTP服务器
      httpServer = http.createServer(app.callback())
    }

    // 创建 Socket.IO 服务器
    ioServer = new Server(httpServer, {
      cors: {
        origin: '*',
        methods: ['GET', 'POST']
      },
      // 添加性能优化配置
      transports: ['websocket', 'polling'], // 优先使用websocket
      pingTimeout: 30000,
      pingInterval: 25000,
      upgradeTimeout: 10000,
      maxHttpBufferSize: 1e6 // 1MB
    })

    // 包装 postMessage 函数,确保它能正常工作
    const safePostMessage = (data) => {
      try {
        return postMessage(data)
      } catch (err) {
        logger.error(`[H5Server] ERROR => postMessage 错误: ${err}`)
        return false
      }
    }

    // 使用中间件方式挂载方法
    app.use(async (ctx, next) => {
      // 绑定方法到 ctx 对象
      ctx.t = t
      ctx.logger = logger
      ctx.postMessage = safePostMessage
      ctx.ioServer = ioServer // 将 Socket.IO 实例添加到上下文

      await next()
    })

    // 解析请求体
    app.use(bodyParser())
    const staticPath = isProduction
      ? path.resolve(process.env.FBW_RESOURCES_PATH, './h5')
      : path.resolve(__dirname, '../h5')
    logger.info(`[H5Server] INFO => H5静态资源路径: ${staticPath}`)
    // 提供静态资源服务
    app.use(
      staticServe(staticPath, {
        // 添加静态资源服务配置,提高性能
        maxage: 86400000, // 缓存一天
        gzip: true, // 启用gzip压缩
        br: true, // 启用brotli压缩(如果可用)
        setHeaders: (res) => {
          res.setHeader('Access-Control-Allow-Origin', '*')
          res.setHeader('Cache-Control', 'public, max-age=86400')
        }
      })
    )

    // 创建路由 - 仅保留 getImage 接口
    const router = new KoaRouter()

    // 注册 http 接口
    useApi(router)

    // 注册路由中间件
    app.use(router.routes()).use(router.allowedMethods())
    try {
      // 设置 Socket.IO - 等待初始化完成
      await setupSocketIO(ioServer, {
        t,
        dbManager,
        settingManager,
        resourcesManager,
        fileManager,
        logger,
        postMessage: safePostMessage
      })
    } catch (err) {
      typeof onStartFail === 'function' &&
        onStartFail({
          msg: `Socket.IO 初始化失败: ${err}`
        })
    }

    logger.info('[H5Server] INFO => Socket.IO 初始化完成,准备启动服务器')

    // 添加性能优化中间件
    app.use(async (ctx, next) => {
      // 设置缓存控制头
      ctx.set('Cache-Control', 'public, max-age=86400')
      ctx.set('X-Content-Type-Options', 'nosniff')
      await next()
    })

    // 启动服务器
    httpServer.listen(port, host, () => {
      const protocol = useHttps ? 'https' : 'http'
      const serverUrl = `${protocol}://${host}:${port}`
      typeof onStartSuccess === 'function' && onStartSuccess(serverUrl)
    })

    // 处理端口被占用的情况
    httpServer.on('error', (err) => {
      if (err.code === 'EADDRINUSE') {
        logger.error(`[H5Server] ERROR => 端口 ${port} 已被占用,尝试使用其他端口...`)
        // 关闭当前服务器
        httpServer.close()

        // 尝试使用新端口重新启动
        findAvailablePort(port + 1)
          .then((newPort) => {
            logger.info(`[H5Server] INFO => 尝试使用新端口: ${newPort}`)
            httpServer.listen(newPort, host)
          })
          .catch((err) => {
            typeof onStartFail === 'function' &&
              onStartFail({
                msg: `查找可用端口失败: ${err}`
              })
          })
      } else {
        typeof onStartFail === 'function' &&
          onStartFail({
            msg: `服务器错误: ${err}`
          })
      }
    })
  } catch (err) {
    typeof onStartFail === 'function' &&
      onStartFail({
        msg: `服务器启动失败: ${err}`
      })
  }
  return {
    httpServer,
    ioServer
  }
}

API

使用@koa/router创建接口服务,用于处理图片加载请求

// main/child_server/h5_server/api/index.mjs

import { getImage } from './images.mjs'

const useApi = (router) => {
  // 图片相关接口
  router.get('/api/images/get', getImage)
}

export default useApi

这里复用了与主进程中的handleFileRsponse方法,实现统一的图片请求逻辑

// main/child_server/h5_server/api/images.mjs

import { handleFileRsponse } from '../../../utils/file.mjs'

export const getImage = async (ctx) => {
  const { filePath, w, h } = ctx.request.query

  const res = await handleFileRsponse({ filePath, w, h })
  ctx.set(res.headers)
  ctx.status = res.status
  ctx.body = res.data
}

Socket

Socket主功能:

  • 监听H5前端请求消息,调用各个管理类进行数据操作
// main/child_server/h5_server/socket/index.mjs

export default async function setupSocketIO(
  ioServer,
  { t, dbManager, settingManager, resourcesManager, fileManager, logger, postMessage }
) {
  // 添加一个安全调用回调的辅助函数
  const safeCallback = (callback, response) => {
    if (typeof callback === 'function') {
      try {
        callback(response)
      } catch (err) {
        logger.error('[H5Server] ERROR => 回调函数执行错误:', err)
      }
    } else {
      logger.error('[H5Server] ERROR => 回调函数不是一个函数')
    }
  }

  ioServer.on('connection', (socket) => {
    // 获取设置
    socket.on('getSettingData', async (params, callback) => {
      try {
        const res = await settingManager.getSettingData()
        safeCallback(callback, res)
      } catch (err) {
        logger.error(`[H5Server] ERROR => 获取设置错误: ${err}`)
        safeCallback(callback, {
          success: false,
          data: null,
          msg: t('messages.operationFail')
        })
      }
    })

    // 更新设置
    socket.on('h5UpdateSettingData', async (data, callback) => {
      try {
        const res = await settingManager.updateSettingData(data)

        if (res.success && res.data) {
          // 向主进程发送设置更新消息
          postMessage({
            event: 'H5_SETTING_UPDATED',
            data: res.data
          })

          // 广播设置更新给所有客户端
          ioServer.emit('settingUpdated', res)

          safeCallback(callback, {
            success: true,
            data: res.data,
            msg: t('messages.operationSuccess')
          })
        } else {
          safeCallback(callback, {
            success: false,
            data: null,
            msg: t('messages.operationFail')
          })
        }
      } catch (err) {
        logger.error(`[H5Server] ERROR => 更新设置错误: ${err}`)
        safeCallback(callback, {
          success: false,
          data: null,
          msg: t('messages.operationFail')
        })
      }
    })

    // 获取资源数据
    socket.on('getResourceMap', async (params, callback) => {
      try {
        const res = await dbManager.getResourceMap()
        safeCallback(callback, res)
      } catch (err) {
        logger.error(`[H5Server] ERROR => 获取资源数据错误: ${err}`)
        safeCallback(callback, {
          success: false,
          data: null,
          msg: t('messages.operationFail')
        })
      }
    })

    // 搜索图片
    socket.on('searchImages', async (params, callback) => {
      try {
        const res = await resourcesManager.searchImages(params)
        safeCallback(callback, res)
      } catch (err) {
        logger.error(`[H5Server] ERROR => 搜索图片错误: ${err}`)
        safeCallback(callback, {
          success: false,
          data: null,
          msg: t('messages.operationFail')
        })
      }
    })

    // 切换收藏状态
    socket.on('toggleFavorite', async (id, callback) => {
      try {
        if (!id) {
          safeCallback(callback, {
            success: false,
            msg: t('messages.operationFail')
          })
          return
        }

        // 检查是否已经收藏
        const isFavorite = await resourcesManager.checkFavorite(id)

        if (isFavorite) {
          // 如果已经收藏,则取消收藏
          const res = await resourcesManager.removeFavorites(id)
          safeCallback(callback, {
            success: res.success,
            msg: res.success ? t('messages.operationSuccess') : t('messages.operationFail')
          })
        } else {
          // 如果没有收藏,则添加收藏
          const res = await resourcesManager.addToFavorites(id)
          safeCallback(callback, {
            success: res.success,
            msg: res.success ? t('messages.operationSuccess') : t('messages.operationFail')
          })
        }
      } catch (err) {
        logger.error(`[H5Server] ERROR => 切换收藏状态错误: ${err}`)
        safeCallback(callback, {
          success: false,
          msg: t('messages.operationFail')
        })
      }
    })

    // 加入收藏
    socket.on('addToFavorites', async (id, callback) => {
      try {
        if (!id) {
          safeCallback(callback, {
            success: false,
            msg: t('messages.operationFail')
          })
          return
        }
        const res = await resourcesManager.addToFavorites(id)
        safeCallback(callback, {
          success: res.success,
          msg: res.success ? t('messages.operationSuccess') : t('messages.operationFail')
        })
      } catch (err) {
        logger.error(`[H5Server] ERROR => 加入收藏错误: ${err}`)
        safeCallback(callback, {
          success: false,
          msg: t('messages.operationFail')
        })
      }
    })
    // 更新收藏数量
    socket.on('updateFavoriteCount', async ({ id, count }, callback) => {
      try {
        if (!id) {
          safeCallback(callback, {
            success: false,
            msg: t('messages.operationFail')
          })
          return
        }
        const res = await resourcesManager.updateFavoriteCount(id, count)
        safeCallback(callback, {
          success: res.success,
          msg: res.success ? t('messages.operationSuccess') : t('messages.operationFail')
        })
      } catch (err) {
        logger.error(`[H5Server] ERROR => 更新收藏数量错误: ${err}`)
        safeCallback(callback, {
          success: false,
          msg: t('messages.operationFail')
        })
      }
    })
    // 取消收藏
    socket.on('removeFavorites', async (id, callback) => {
      try {
        if (!id) {
          safeCallback(callback, {
            success: false,
            msg: t('messages.operationFail')
          })
          return
        }
        const res = await resourcesManager.removeFavorites(id)
        safeCallback(callback, {
          success: res.success,
          msg: res.success ? t('messages.operationSuccess') : t('messages.operationFail')
        })
      } catch (err) {
        logger.error(`[H5Server] ERROR => 取消收藏错误: ${err}`)
        safeCallback(callback, {
          success: false,
          msg: t('messages.operationFail')
        })
      }
    })
    // 删除图片
    socket.on('deleteImage', async (item, callback) => {
      try {
        if (!item) {
          safeCallback(callback, {
            success: false,
            msg: t('messages.operationFail')
          })
          return
        }
        const res = await fileManager.deleteFile(item)
        safeCallback(callback, {
          success: res.success,
          msg: res.success ? t('messages.operationSuccess') : t('messages.operationFail')
        })
      } catch (err) {
        logger.error(`[H5Server] ERROR => 删除图片错误: ${err}`)
        safeCallback(callback, {
          success: false,
          msg: t('messages.operationFail')
        })
      }
    })

    // 断开连接
    socket.on('disconnect', () => {
      logger.info(`[H5Server] INFO => 客户端断开连接: ${socket.id}`)
    })
  })

  logger.info('[H5Server] INFO => Socket.IO 管理器初始化完成')
}