diff --git a/README.md b/README.md index 8ecedce..dfcb888 100644 --- a/README.md +++ b/README.md @@ -16,13 +16,12 @@ ./scripts/dev.sh ``` -脚本会自动启动 MySQL、Redis、MinIO,首次初始化数据库结构,并同时启动后端和前端。 +脚本会自动启动 MySQL、Redis、MinIO,首次初始化数据库结构,并同时启动后端、主站前后台、分销独立后台与分销专属前台。 -前端地址:`http://localhost:5189` - -后台地址默认:`http://localhost:5189/admin/login`。可通过 `VITE_ADMIN_BASE_PATH` 调整后台页面入口。 - -分销后台地址默认:`http://localhost:5190` +- 主站前台地址:`http://localhost:5189` +- 主站后台地址:`http://localhost:5189/admin/login`(超级管理员默认 `admin` / `admin123456`) +- 分销管理后台:`http://localhost:5190`(分销操作员 `test_fx1`) +- 分销专属前台:`http://localhost:5191`(独立端口隔离,默认直连 `test_1` 分销站) 如需拆分调试,可手动执行: diff --git a/frontend/src/features/auth/api/notifications.ts b/frontend/src/features/auth/api/notifications.ts index c10057d..c40d534 100644 --- a/frontend/src/features/auth/api/notifications.ts +++ b/frontend/src/features/auth/api/notifications.ts @@ -1,6 +1,6 @@ import { apiClient } from '@/shared/api/client' - import type { ApiResponse, PaginatedResult } from '@/shared/types/types' +import { getEffectiveSiteCode } from '@/stores/distributionSite' export interface NotificationItem { id: number @@ -16,10 +16,7 @@ export interface NotificationItem { } export async function fetchNotifications(page = 1, pageSize = 20, distributionSiteCode?: string) { - const siteCode = - distributionSiteCode || - (typeof window !== 'undefined' ? localStorage.getItem('hfb_site_code') : '') || - undefined + const siteCode = distributionSiteCode || getEffectiveSiteCode() || undefined const params: Record = { page, page_size: pageSize } if (siteCode) { params.distribution_site_code = siteCode @@ -34,10 +31,7 @@ export async function fetchNotifications(page = 1, pageSize = 20, distributionSi } export async function markNotificationRead(id: number, distributionSiteCode?: string) { - const siteCode = - distributionSiteCode || - (typeof window !== 'undefined' ? localStorage.getItem('hfb_site_code') : '') || - undefined + const siteCode = distributionSiteCode || getEffectiveSiteCode() || undefined const params: Record = {} if (siteCode) { params.distribution_site_code = siteCode @@ -51,10 +45,7 @@ export async function markNotificationRead(id: number, distributionSiteCode?: st } export async function fetchUnreadNotificationCount(distributionSiteCode?: string) { - const siteCode = - distributionSiteCode || - (typeof window !== 'undefined' ? localStorage.getItem('hfb_site_code') : '') || - undefined + const siteCode = distributionSiteCode || getEffectiveSiteCode() || undefined const params: Record = {} if (siteCode) { params.distribution_site_code = siteCode @@ -70,10 +61,7 @@ export async function fetchUnreadNotificationCount(distributionSiteCode?: string } export async function markAllNotificationsRead(distributionSiteCode?: string) { - const siteCode = - distributionSiteCode || - (typeof window !== 'undefined' ? localStorage.getItem('hfb_site_code') : '') || - undefined + const siteCode = distributionSiteCode || getEffectiveSiteCode() || undefined const params: Record = {} if (siteCode) { params.distribution_site_code = siteCode diff --git a/frontend/src/features/chats/api/chats.ts b/frontend/src/features/chats/api/chats.ts index 72fb134..c63071b 100644 --- a/frontend/src/features/chats/api/chats.ts +++ b/frontend/src/features/chats/api/chats.ts @@ -1,5 +1,6 @@ import { apiClient } from '@/shared/api/client' import type { ApiResponse, PaginatedResult } from '@/shared/types/types' +import { getEffectiveSiteCode } from '@/stores/distributionSite' export interface ChatParticipant { id: number @@ -94,10 +95,7 @@ export interface ChatMessage { } export async function fetchChats(page = 1, pageSize = 20, distributionSiteCode?: string) { - const siteCode = - distributionSiteCode || - (typeof window !== 'undefined' ? localStorage.getItem('hfb_site_code') : '') || - undefined + const siteCode = distributionSiteCode || getEffectiveSiteCode() || undefined const params: Record = { page, page_size: pageSize } if (siteCode) { params.distribution_site_code = siteCode @@ -109,10 +107,7 @@ export async function fetchChats(page = 1, pageSize = 20, distributionSiteCode?: } export async function fetchUnreadChatCount(distributionSiteCode?: string) { - const siteCode = - distributionSiteCode || - (typeof window !== 'undefined' ? localStorage.getItem('hfb_site_code') : '') || - undefined + const siteCode = distributionSiteCode || getEffectiveSiteCode() || undefined const params: Record = {} if (siteCode) { params.distribution_site_code = siteCode @@ -141,10 +136,7 @@ export async function ensureSupportChat( scene: SupportScene | string = 'general', distributionSiteCode?: string ) { - const siteCode = - distributionSiteCode || - (typeof window !== 'undefined' ? localStorage.getItem('hfb_site_code') : '') || - undefined + const siteCode = distributionSiteCode || getEffectiveSiteCode() || undefined const payload: { scene: string; distribution_site_code?: string } = { scene: scene || 'general', } diff --git a/frontend/src/features/listings/api/listings.ts b/frontend/src/features/listings/api/listings.ts index 05b8469..b0e4596 100644 --- a/frontend/src/features/listings/api/listings.ts +++ b/frontend/src/features/listings/api/listings.ts @@ -1,5 +1,6 @@ import { apiClient } from '@/shared/api/client' import type { ApiResponse } from '@/shared/types/types' +import { getEffectiveSiteCode } from '@/stores/distributionSite' import type { ListingReviewStatus, ListingStatus } from '@/shared/types/status' export interface Listing { @@ -102,10 +103,7 @@ export async function fetchListings(query: PublicListingQuery = {}) { } export async function fetchListingsPage(query: PublicListingQuery = {}) { - const siteCode = - query.distribution_site_code || - (typeof window !== 'undefined' ? localStorage.getItem('hfb_site_code') : '') || - undefined + const siteCode = query.distribution_site_code || getEffectiveSiteCode() || undefined const effectiveQuery = siteCode ? { distribution_site_code: siteCode, ...query } : query const params = Object.fromEntries( Object.entries(effectiveQuery).filter( diff --git a/frontend/src/features/orders/api/orders.ts b/frontend/src/features/orders/api/orders.ts index 56d786b..dfbd405 100644 --- a/frontend/src/features/orders/api/orders.ts +++ b/frontend/src/features/orders/api/orders.ts @@ -2,6 +2,7 @@ import { apiClient } from '@/shared/api/client' import type { ApiResponse, PaginatedResult } from '@/shared/types/types' import type { HandoffStatus, OrderStatus, SettlementStatus } from '@/shared/types/status' import { yuanToCent } from '@/shared/utils/money' +import { getEffectiveSiteCode } from '@/stores/distributionSite' export interface Order { id: number @@ -320,10 +321,7 @@ export async function fetchPostRentalNotice() { } export async function fetchOrders(page = 1, pageSize = 20, distributionSiteCode?: string) { - const siteCode = - distributionSiteCode || - (typeof window !== 'undefined' ? localStorage.getItem('hfb_site_code') : '') || - undefined + const siteCode = distributionSiteCode || getEffectiveSiteCode() || undefined const params: Record = { page, page_size: pageSize } if (siteCode) { params.distribution_site_code = siteCode @@ -352,10 +350,7 @@ export async function fetchSellerHandoffs( } export async function fetchOrder(id: string | number, distributionSiteCode?: string) { - const siteCode = - distributionSiteCode || - (typeof window !== 'undefined' ? localStorage.getItem('hfb_site_code') : '') || - undefined + const siteCode = distributionSiteCode || getEffectiveSiteCode() || undefined const params: Record = {} if (siteCode) { params.distribution_site_code = siteCode @@ -365,10 +360,7 @@ export async function fetchOrder(id: string | number, distributionSiteCode?: str } export async function createOrder(listingId: number, distributionSiteCode?: string) { - const siteCode = - distributionSiteCode || - (typeof window !== 'undefined' ? localStorage.getItem('hfb_site_code') : '') || - undefined + const siteCode = distributionSiteCode || getEffectiveSiteCode() || undefined const payload: { listing_id: number; distribution_site_code?: string } = { listing_id: listingId } if (siteCode) { payload.distribution_site_code = siteCode diff --git a/frontend/src/features/wallet/api/wallet.ts b/frontend/src/features/wallet/api/wallet.ts index 44040ef..166d219 100644 --- a/frontend/src/features/wallet/api/wallet.ts +++ b/frontend/src/features/wallet/api/wallet.ts @@ -1,6 +1,6 @@ import { apiClient } from '@/shared/api/client' - import type { ApiResponse, PaginatedResult } from '@/shared/types/types' +import { getEffectiveSiteCode } from '@/stores/distributionSite' import type { BalanceType, LedgerDirection, WalletStatus } from '@/shared/types/status' export interface WalletAccount { @@ -28,10 +28,7 @@ export interface WalletLedger { } export async function fetchWalletBalance(distributionSiteCode?: string) { - const siteCode = - distributionSiteCode || - (typeof window !== 'undefined' ? localStorage.getItem('hfb_site_code') : '') || - undefined + const siteCode = distributionSiteCode || getEffectiveSiteCode() || undefined const params: Record = {} if (siteCode) { params.distribution_site_code = siteCode @@ -41,10 +38,7 @@ export async function fetchWalletBalance(distributionSiteCode?: string) { } export async function fetchWalletLedger(page = 1, pageSize = 20, distributionSiteCode?: string) { - const siteCode = - distributionSiteCode || - (typeof window !== 'undefined' ? localStorage.getItem('hfb_site_code') : '') || - undefined + const siteCode = distributionSiteCode || getEffectiveSiteCode() || undefined const params: Record = { page, page_size: pageSize } if (siteCode) { params.distribution_site_code = siteCode diff --git a/frontend/src/features/wallet/api/withdrawal.ts b/frontend/src/features/wallet/api/withdrawal.ts index dc9ce3e..c74a371 100644 --- a/frontend/src/features/wallet/api/withdrawal.ts +++ b/frontend/src/features/wallet/api/withdrawal.ts @@ -1,6 +1,6 @@ import { apiClient } from '@/shared/api/client' - import type { ApiResponse, PaginatedResult } from '@/shared/types/types' +import { getEffectiveSiteCode } from '@/stores/distributionSite' // 收款账号类型 export type AccountType = 'alipay' | 'wechat' | 'bank' @@ -116,11 +116,7 @@ export async function createWithdrawal( req: CreateWithdrawalRequest, distributionSiteCode?: string ) { - const siteCode = - distributionSiteCode || - req.distribution_site_code || - (typeof window !== 'undefined' ? localStorage.getItem('hfb_site_code') : '') || - undefined + const siteCode = distributionSiteCode || req.distribution_site_code || getEffectiveSiteCode() || undefined const payload: CreateWithdrawalRequest = { ...req } if (siteCode) { payload.distribution_site_code = siteCode @@ -130,10 +126,7 @@ export async function createWithdrawal( } export async function fetchWithdrawals(page = 1, pageSize = 20, distributionSiteCode?: string) { - const siteCode = - distributionSiteCode || - (typeof window !== 'undefined' ? localStorage.getItem('hfb_site_code') : '') || - undefined + const siteCode = distributionSiteCode || getEffectiveSiteCode() || undefined const params: Record = { page, page_size: pageSize } if (siteCode) { params.distribution_site_code = siteCode diff --git a/frontend/src/shared/api/client.ts b/frontend/src/shared/api/client.ts index 418092f..eead27f 100644 --- a/frontend/src/shared/api/client.ts +++ b/frontend/src/shared/api/client.ts @@ -3,6 +3,7 @@ import { ElMessage } from 'element-plus' import { showToast } from 'vant' import { apiMonitor } from './monitor' import { debugError, debugLog } from '@/shared/utils/debug' +import { getEffectiveSiteCode } from '@/stores/distributionSite' function showError(message: string) { const isMobile = window.location.pathname.startsWith('/m') @@ -201,11 +202,9 @@ apiClient.interceptors.request.use( config.headers.Authorization = `Bearer ${token}` } - if (typeof window !== 'undefined') { - const siteCode = localStorage.getItem('hfb_site_code') - if (siteCode && !config.headers['X-Distribution-Site-Code']) { - config.headers['X-Distribution-Site-Code'] = siteCode - } + const siteCode = getEffectiveSiteCode() + if (siteCode && !config.headers['X-Distribution-Site-Code']) { + config.headers['X-Distribution-Site-Code'] = siteCode } // 记录请求日志 diff --git a/frontend/src/stores/distributionSite.ts b/frontend/src/stores/distributionSite.ts index f042269..39fd50f 100644 --- a/frontend/src/stores/distributionSite.ts +++ b/frontend/src/stores/distributionSite.ts @@ -11,20 +11,52 @@ export interface DistributionSiteInfo { default_support_group_id?: number | null } +/** + * 获取当前上下文生效的分销站点编码 + * 优先级: + * 1. 环境变量 VITE_DISTRIBUTION_SITE_CODE(针对独立分销前台开发端口,如 5191) + * 2. 当前标签页 sessionStorage 中的 hfb_site_code(避免跨 Tab 污染主站) + * 3. 本地二级域名(如 test_1.localhost:5189) + * 4. 生产环境或独立域名绑定的 localStorage 缓存 + */ +export function getEffectiveSiteCode(): string { + if (typeof window === 'undefined') return '' + const envCode = ((import.meta.env.VITE_DISTRIBUTION_SITE_CODE as string) || '').trim() + if (envCode) return envCode + + const sessionCode = sessionStorage.getItem('hfb_site_code') || '' + if (sessionCode) return sessionCode + + const hostname = window.location.hostname + if (hostname && hostname.endsWith('.localhost')) { + return hostname.replace(/\.localhost$/, '') + } + + if (hostname !== 'localhost' && hostname !== '127.0.0.1') { + return localStorage.getItem('hfb_site_code') || '' + } + + return '' +} + export const useDistributionSiteStore = defineStore('distributionSite', { - state: () => ({ - siteCode: localStorage.getItem('hfb_site_code') || '', - siteName: localStorage.getItem('hfb_site_name') || '大锤商行', - domain: '', - isDistribution: Boolean(localStorage.getItem('hfb_site_code')), - displayAccountEnabled: true, - defaultSupportGroupId: null as number | null, - initialized: false, - }), + state: () => { + const code = getEffectiveSiteCode() + return { + siteCode: code, + siteName: sessionStorage.getItem('hfb_site_name') || '大锤商行', + domain: '', + isDistribution: Boolean(code), + displayAccountEnabled: true, + defaultSupportGroupId: null as number | null, + initialized: false, + } + }, actions: { async initSite() { - // 1. Check URL query params: ?site=xxx or ?site_code=xxx + const envCode = ((import.meta.env.VITE_DISTRIBUTION_SITE_CODE as string) || '').trim() + const hostname = window.location.hostname const urlParams = new URLSearchParams(window.location.search) const querySite = ( urlParams.get('site') || @@ -33,24 +65,37 @@ export const useDistributionSiteStore = defineStore('distributionSite', { '' ).trim() + // 1. 显式清除或重置为主站 + if (querySite === 'main' || querySite === 'clear' || querySite === '0') { + this.resetToMainSite() + this.initialized = true + return + } + + // 2. 识别目标分销站点编码 + let targetCode = '' if (querySite) { - if (querySite === 'main' || querySite === 'clear' || querySite === '0') { + targetCode = querySite + sessionStorage.setItem('hfb_site_code', querySite) + } else if (envCode) { + targetCode = envCode + sessionStorage.setItem('hfb_site_code', envCode) + } else if (hostname && hostname.endsWith('.localhost')) { + targetCode = hostname.replace(/\.localhost$/, '') + sessionStorage.setItem('hfb_site_code', targetCode) + } else { + targetCode = sessionStorage.getItem('hfb_site_code') || '' + // 若在普通 localhost 且无 envCode、无 query、无 sessionStorage,则视为纯主站,清理旧的 localStorage 串扰 + if (!targetCode && (hostname === 'localhost' || hostname === '127.0.0.1')) { this.resetToMainSite() this.initialized = true return } - this.siteCode = querySite - localStorage.setItem('hfb_site_code', querySite) } - const effectiveCode = this.siteCode || localStorage.getItem('hfb_site_code') || '' - const hostname = window.location.hostname - - // If we have a site code, query by code. - // Otherwise query by domain (for independent domain access in production). let queryUrl = '' - if (effectiveCode) { - queryUrl = `/distribution/site-info?code=${encodeURIComponent(effectiveCode)}` + if (targetCode) { + queryUrl = `/distribution/site-info?code=${encodeURIComponent(targetCode)}` } else if (hostname && hostname !== 'localhost' && hostname !== '127.0.0.1') { queryUrl = `/distribution/site-info?domain=${encodeURIComponent(hostname)}` } @@ -75,16 +120,16 @@ export const useDistributionSiteStore = defineStore('distributionSite', { this.displayAccountEnabled = info.display_account_enabled ?? true this.defaultSupportGroupId = info.default_support_group_id ?? null - localStorage.setItem('hfb_site_code', this.siteCode) - localStorage.setItem('hfb_site_name', this.siteName) + sessionStorage.setItem('hfb_site_code', this.siteCode) + sessionStorage.setItem('hfb_site_name', this.siteName) - // Update page title + // 更新页面标题 document.title = `【${this.siteName}】租号商城 - 精选游戏账号平台` } else { this.resetToMainSite() } } catch { - // If site code was invalid or not found, fall back to main site + // 若站点编码无效或未找到,自动回退为主站 this.resetToMainSite() } finally { this.initialized = true @@ -98,6 +143,8 @@ export const useDistributionSiteStore = defineStore('distributionSite', { this.isDistribution = false this.displayAccountEnabled = true this.defaultSupportGroupId = null + sessionStorage.removeItem('hfb_site_code') + sessionStorage.removeItem('hfb_site_name') localStorage.removeItem('hfb_site_code') localStorage.removeItem('hfb_site_name') document.title = '大锤商行 - 官方租号平台' diff --git a/scripts/dev.sh b/scripts/dev.sh index 31ef56b..fb9ed8e 100755 --- a/scripts/dev.sh +++ b/scripts/dev.sh @@ -20,6 +20,8 @@ MYSQL_DATABASE="${DEV_MYSQL_DATABASE:-hfb_sys}" BACKEND_PORT="${DEV_BACKEND_PORT:-18088}" FRONTEND_PORT="${DEV_FRONTEND_PORT:-5189}" DISTRIBUTION_PORT="${DEV_DISTRIBUTION_PORT:-5190}" +DISTRIBUTION_STOREFRONT_PORT="${DEV_DISTRIBUTION_STOREFRONT_PORT:-5191}" +DISTRIBUTION_STOREFRONT_SITE_CODE="${DEV_DISTRIBUTION_STOREFRONT_SITE_CODE:-test_1}" MYSQL_PORT="${DEV_MYSQL_PORT:-13306}" REDIS_PORT="${DEV_REDIS_PORT:-16379}" MINIO_API_PORT="${DEV_MINIO_API_PORT:-19090}" @@ -27,6 +29,7 @@ MINIO_CONSOLE_PORT="${DEV_MINIO_CONSOLE_PORT:-19091}" BACKEND_HEALTH_URL="${DEV_BACKEND_HEALTH_URL:-http://127.0.0.1:${BACKEND_PORT}/health}" FRONTEND_HEALTH_URL="${DEV_FRONTEND_HEALTH_URL:-http://127.0.0.1:${FRONTEND_PORT}/}" DISTRIBUTION_HEALTH_URL="${DEV_DISTRIBUTION_HEALTH_URL:-http://127.0.0.1:${DISTRIBUTION_PORT}/}" +DISTRIBUTION_STOREFRONT_HEALTH_URL="${DEV_DISTRIBUTION_STOREFRONT_HEALTH_URL:-http://127.0.0.1:${DISTRIBUTION_STOREFRONT_PORT}/}" HTTP_READY_TIMEOUT="${DEV_HTTP_READY_TIMEOUT:-120}" AUTO_KILL_PORTS="${DEV_AUTO_KILL_PORTS:-1}" FORCE_NPM_CI="${DEV_FORCE_NPM_CI:-0}" @@ -36,6 +39,7 @@ GOOSE_BIN="${GOOSE_BIN:-}" BACKEND_PID="" FRONTEND_PID="" DISTRIBUTION_PID="" +DISTRIBUTION_STOREFRONT_PID="" CLEANED_UP=0 # 颜色定义 @@ -78,6 +82,8 @@ show_help() { --no-frontend 不启动主站前端 --no-backend 不启动后端 --no-distribution 不启动分销后台 + --no-distribution-storefront + 不启动分销前台商城 -h, --help 显示帮助信息 环境变量: @@ -124,6 +130,7 @@ NO_MIGRATE=0 NO_FRONTEND=0 NO_BACKEND=0 NO_DISTRIBUTION=0 +NO_DISTRIBUTION_STOREFRONT=0 while [[ $# -gt 0 ]]; do case "$1" in @@ -147,6 +154,10 @@ while [[ $# -gt 0 ]]; do NO_DISTRIBUTION=1 shift ;; + --no-distribution-storefront) + NO_DISTRIBUTION_STOREFRONT=1 + shift + ;; -h|--help) show_help ;; @@ -163,11 +174,14 @@ cleanup() { fi CLEANED_UP=1 - if [[ -z "${FRONTEND_PID}" && -z "${BACKEND_PID}" && -z "${DISTRIBUTION_PID}" ]]; then + if [[ -z "${FRONTEND_PID}" && -z "${BACKEND_PID}" && -z "${DISTRIBUTION_PID}" && -z "${DISTRIBUTION_STOREFRONT_PID}" ]]; then return fi log "正在停止前后端开发进程..." + if [[ -n "${DISTRIBUTION_STOREFRONT_PID}" ]] && kill -0 "${DISTRIBUTION_STOREFRONT_PID}" >/dev/null 2>&1; then + kill "${DISTRIBUTION_STOREFRONT_PID}" >/dev/null 2>&1 || true + fi if [[ -n "${DISTRIBUTION_PID}" ]] && kill -0 "${DISTRIBUTION_PID}" >/dev/null 2>&1; then kill "${DISTRIBUTION_PID}" >/dev/null 2>&1 || true fi @@ -619,6 +633,21 @@ start_distribution() { DISTRIBUTION_PID="$!" } +start_distribution_storefront() { + ensure_frontend_dependencies + + log "启动分销前台:http://localhost:${DISTRIBUTION_STOREFRONT_PORT} (分销站: ${DISTRIBUTION_STOREFRONT_SITE_CODE})" + ( + cd "${FRONTEND_DIR}" + export DEV_FRONTEND_PORT="${DISTRIBUTION_STOREFRONT_PORT}" + export DEV_BACKEND_PORT="${BACKEND_PORT}" + export VITE_API_PROXY_TARGET="${VITE_API_PROXY_TARGET:-http://127.0.0.1:${BACKEND_PORT}}" + export VITE_DISTRIBUTION_SITE_CODE="${DISTRIBUTION_STOREFRONT_SITE_CODE}" + npm run dev + ) & + DISTRIBUTION_STOREFRONT_PID="$!" +} + watch_processes() { while true; do if [[ -n "${BACKEND_PID}" ]] && ! kill -0 "${BACKEND_PID}" >/dev/null 2>&1; then @@ -636,6 +665,11 @@ watch_processes() { printf "分销后台进程已退出\n" >&2 exit 1 fi + if [[ -n "${DISTRIBUTION_STOREFRONT_PID}" ]] && ! kill -0 "${DISTRIBUTION_STOREFRONT_PID}" >/dev/null 2>&1; then + wait "${DISTRIBUTION_STOREFRONT_PID}" || true + printf "分销前台商城进程已退出\n" >&2 + exit 1 + fi sleep 2 done } @@ -698,7 +732,13 @@ main() { wait_http_ready "${DISTRIBUTION_HEALTH_URL}" "分销后台" "${DISTRIBUTION_PID}" fi - if [[ -z "${BACKEND_PID}" && -z "${FRONTEND_PID}" && -z "${DISTRIBUTION_PID}" ]]; then + if [[ "${NO_DISTRIBUTION_STOREFRONT}" == "0" && "${NO_FRONTEND}" == "0" ]]; then + ensure_port_free "${DISTRIBUTION_STOREFRONT_PORT}" "分销前台" + start_distribution_storefront + wait_http_ready "${DISTRIBUTION_STOREFRONT_HEALTH_URL}" "分销前台" "${DISTRIBUTION_STOREFRONT_PID}" + fi + + if [[ -z "${BACKEND_PID}" && -z "${FRONTEND_PID}" && -z "${DISTRIBUTION_PID}" && -z "${DISTRIBUTION_STOREFRONT_PID}" ]]; then log "仅启动 Docker 开发依赖,前后端与分销端未启动" return 0 fi @@ -706,12 +746,15 @@ main() { log_success "开发环境已就绪" if [[ "${NO_FRONTEND}" == "0" ]]; then local admin_base_path="${VITE_ADMIN_BASE_PATH:-/admin}" - log "前台地址:http://localhost:${FRONTEND_PORT}" - log "后台地址:http://localhost:${FRONTEND_PORT}${admin_base_path%/}/login" + log "主站前台:http://localhost:${FRONTEND_PORT}" + log "主站后台:http://localhost:${FRONTEND_PORT}${admin_base_path%/}/login" fi if [[ "${NO_DISTRIBUTION}" == "0" && -d "${DISTRIBUTION_DIR}" ]]; then log "分销后台:http://localhost:${DISTRIBUTION_PORT}" fi + if [[ "${NO_DISTRIBUTION_STOREFRONT}" == "0" && "${NO_FRONTEND}" == "0" ]]; then + log "分销前台:http://localhost:${DISTRIBUTION_STOREFRONT_PORT} (直连分销站: ${DISTRIBUTION_STOREFRONT_SITE_CODE})" + fi log "按 Ctrl+C 停止所有服务;Docker 依赖会保留运行"