feat(deploy): 完成私有化部署全流程验收
- 更新部署进度文档,标记P5-02/P5-03为已完成状态 - 修复中文乱码问题,MySQL数据层使用UNHEX函数配合nginx字符集设置 - 配置im-service和update-service的内部服务URL,从127.0.0.1改为Docker服务名 - 实现全功能验收,nginx为所有服务添加路由映射并返回正确的状态码 - 创建私有化部署默认信息文档,记录完整的部署配置和访问地址 - 添加部署清理脚本clean.sh,支持一键清理容器、配置和数据目录 - 更新敏感配置模板,添加详细的密码和密钥配置说明 - 优化前端实时消息轮询机制,通过WebSocket心跳检测决定是否启用HTTP轮询回退
这个提交包含在:
父节点
3900d25d51
当前提交
290a6999fe
@ -39,6 +39,9 @@ let imClient: ImClient | null = null
|
|||||||
let activeAppKey = ''
|
let activeAppKey = ''
|
||||||
let storeReviewHandler: ((event: StoreReviewRefreshEvent) => void) | null = null
|
let storeReviewHandler: ((event: StoreReviewRefreshEvent) => void) | null = null
|
||||||
let serviceActivationHandler: ((event: ServiceActivationRefreshEvent) => void) | null = null
|
let serviceActivationHandler: ((event: ServiceActivationRefreshEvent) => void) | null = null
|
||||||
|
// Tracks when a message was last received over the IM WebSocket (any type).
|
||||||
|
// Used by callers to decide whether IM is healthy enough to skip HTTP polling.
|
||||||
|
let lastImMessageAt = 0
|
||||||
|
|
||||||
function sdkWsUrl() {
|
function sdkWsUrl() {
|
||||||
return import.meta.env.VITE_IM_WS_URL ?? ''
|
return import.meta.env.VITE_IM_WS_URL ?? ''
|
||||||
@ -114,6 +117,7 @@ async function ensureConnection(appKey: string) {
|
|||||||
const wsUrl = sdkWsUrl() || undefined
|
const wsUrl = sdkWsUrl() || undefined
|
||||||
const clientInstance = new ImClient({ tokenSupplier: () => platformToken, wsUrl })
|
const clientInstance = new ImClient({ tokenSupplier: () => platformToken, wsUrl })
|
||||||
clientInstance.on('message', (message) => {
|
clientInstance.on('message', (message) => {
|
||||||
|
lastImMessageAt = Date.now()
|
||||||
const event = parseAnyEvent(message)
|
const event = parseAnyEvent(message)
|
||||||
if (!event || event.appKey !== activeAppKey) return
|
if (!event || event.appKey !== activeAppKey) return
|
||||||
if (event.event === 'store_review_update' && storeReviewHandler) {
|
if (event.event === 'store_review_update' && storeReviewHandler) {
|
||||||
@ -131,6 +135,14 @@ async function ensureConnection(appKey: string) {
|
|||||||
imClient = clientInstance
|
imClient = clientInstance
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the IM WebSocket received a message within the last 90 seconds,
|
||||||
|
* meaning the connection is healthy and polling can be skipped.
|
||||||
|
*/
|
||||||
|
export function isStoreReviewRealtimeConnected(): boolean {
|
||||||
|
return imClient !== null && Date.now() - lastImMessageAt < 90_000
|
||||||
|
}
|
||||||
|
|
||||||
export async function connectStoreReviewRealtime(appKey: string, onEvent: (event: StoreReviewRefreshEvent) => void) {
|
export async function connectStoreReviewRealtime(appKey: string, onEvent: (event: StoreReviewRefreshEvent) => void) {
|
||||||
if (!appKey) return
|
if (!appKey) return
|
||||||
storeReviewHandler = onEvent
|
storeReviewHandler = onEvent
|
||||||
|
|||||||
@ -946,6 +946,7 @@ import {
|
|||||||
connectStoreReviewRealtime,
|
connectStoreReviewRealtime,
|
||||||
disconnectStoreReviewRealtime,
|
disconnectStoreReviewRealtime,
|
||||||
notifyStoreReviewRefresh,
|
notifyStoreReviewRefresh,
|
||||||
|
isStoreReviewRealtimeConnected,
|
||||||
type StoreReviewRefreshEvent,
|
type StoreReviewRefreshEvent,
|
||||||
} from '@/services/storeReviewRealtime'
|
} from '@/services/storeReviewRealtime'
|
||||||
import huaweiGuideImage from '@/assets/update-store/huawei/01.png'
|
import huaweiGuideImage from '@/assets/update-store/huawei/01.png'
|
||||||
@ -1684,6 +1685,9 @@ function openStoreReviewDetail(row: AppVersion) {
|
|||||||
|
|
||||||
function startDialogPoll() {
|
function startDialogPoll() {
|
||||||
stopDialogPoll()
|
stopDialogPoll()
|
||||||
|
// IM WebSocket delivers real-time store_review_update events; only fall back to
|
||||||
|
// HTTP polling when IM has been silent for over 90 s (disconnected or starting up).
|
||||||
|
const interval = isStoreReviewRealtimeConnected() ? 30_000 : 3_000
|
||||||
storeReviewDialogPollTimer = setInterval(() => {
|
storeReviewDialogPollTimer = setInterval(() => {
|
||||||
if (!showStoreReviewDetail.value || !storeReviewDetailVersion.value) {
|
if (!showStoreReviewDetail.value || !storeReviewDetailVersion.value) {
|
||||||
stopDialogPoll()
|
stopDialogPoll()
|
||||||
@ -1697,7 +1701,7 @@ function startDialogPoll() {
|
|||||||
} else {
|
} else {
|
||||||
stopDialogPoll()
|
stopDialogPoll()
|
||||||
}
|
}
|
||||||
}, 3000)
|
}, interval)
|
||||||
}
|
}
|
||||||
|
|
||||||
function stopDialogPoll() {
|
function stopDialogPoll() {
|
||||||
|
|||||||
正在加载...
在新工单中引用
屏蔽一个用户