update pay ui

This commit is contained in:
2025-12-13 16:30:50 +08:00
parent ed9d49da01
commit 4ccd43f025
17 changed files with 67 additions and 43 deletions

View File

@@ -62,7 +62,7 @@ server {
# ============================================
# CORS 配置(允许 CDN 域名访问)
# ============================================
set $cors_origin '*';
set $cors_origin 'https://valuefrontier.cn';
# 如果需要限制来源,取消下面注释
# set $cors_origin '';
@@ -78,13 +78,20 @@ server {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' $cors_origin always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Origin, Content-Type, Accept, Authorization, X-Requested-With' always;
add_header 'Access-Control-Allow-Headers' 'Origin, Content-Type, Accept, Authorization, X-Requested-With, Cookie' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Max-Age' 86400;
add_header 'Content-Length' 0;
return 204;
}
# 隐藏后端返回的 CORS 头(避免重复)
proxy_hide_header 'Access-Control-Allow-Origin';
proxy_hide_header 'Access-Control-Allow-Credentials';
proxy_hide_header 'Access-Control-Allow-Methods';
proxy_hide_header 'Access-Control-Allow-Headers';
proxy_hide_header 'Access-Control-Expose-Headers';
proxy_pass http://127.0.0.1:5001;
proxy_http_version 1.1;
proxy_set_header Host $host;
@@ -93,7 +100,7 @@ server {
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Connection "";
# CORS 头
# 统一添加 CORS 头
add_header 'Access-Control-Allow-Origin' $cors_origin always;
add_header 'Access-Control-Allow-Credentials' 'true' always;

View File

@@ -37,6 +37,7 @@ import VerificationCodeInput from './VerificationCodeInput';
import WechatRegister from './WechatRegister';
import { setCurrentUser } from '../../mocks/data/users';
import { logger } from '../../utils/logger';
import { getApiBase } from '../../utils/apiConfig';
import { useAuthEvents } from '../../hooks/useAuthEvents';
// 统一配置对象
@@ -186,7 +187,7 @@ export default function AuthFormContent() {
purpose: config.api.purpose
};
const response = await fetch('/api/auth/send-verification-code', {
const response = await fetch(`${getApiBase()}/api/auth/send-verification-code`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
@@ -300,7 +301,7 @@ export default function AuthFormContent() {
};
// 调用API根据模式选择不同的endpoint
const response = await fetch('/api/auth/login-with-code', {
const response = await fetch(`${getApiBase()}/api/auth/login-with-code`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',

View File

@@ -20,6 +20,7 @@ import CitationMark from '@components/Citation/CitationMark';
import CitedContent from '@components/Citation/CitedContent';
import { processCitationData } from '@utils/citationUtils';
import { logger } from '@utils/logger';
import { getApiBase } from '@utils/apiConfig';
import './InvestmentCalendar.css';
const { TabPane } = Tabs;
@@ -153,7 +154,7 @@ const InvestmentCalendar = () => {
const code = codes[i];
const originalCode = normalizedStocks[i].code; // 使用归一化后的代码作为key
try {
const response = await fetch(`/api/market/trade/${code}?days=1`);
const response = await fetch(`${getApiBase()}/api/market/trade/${code}?days=1`);
if (response.ok) {
const data = await response.json();
if (data.success && data.data && data.data.length > 0) {

View File

@@ -53,6 +53,7 @@ import {
FaChevronDown,
FaChevronUp,
} from 'react-icons/fa';
import { getApiBase } from '../../utils/apiConfig';
export default function SubscriptionContent() {
// Auth context
@@ -129,7 +130,7 @@ export default function SubscriptionContent() {
const fetchSubscriptionPlans = async () => {
try {
logger.debug('SubscriptionContent', '正在获取订阅套餐');
const response = await fetch('/api/subscription/plans');
const response = await fetch(`${getApiBase()}/api/subscription/plans`);
if (response.ok) {
const data = await response.json();
@@ -175,7 +176,7 @@ export default function SubscriptionContent() {
validPromoCode
});
const response = await fetch('/api/subscription/calculate-price', {
const response = await fetch(`${getApiBase()}/api/subscription/calculate-price`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
@@ -307,7 +308,7 @@ export default function SubscriptionContent() {
orderId: null // Will be set after order creation
});
const response = await fetch('/api/payment/create-order', {
const response = await fetch(`${getApiBase()}/api/payment/create-order`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
@@ -379,7 +380,7 @@ export default function SubscriptionContent() {
const checkInterval = setInterval(async () => {
try {
const response = await fetch(`/api/payment/order/${orderId}/status`, {
const response = await fetch(`${getApiBase()}/api/payment/order/${orderId}/status`, {
credentials: 'include'
});
@@ -456,7 +457,7 @@ export default function SubscriptionContent() {
setForceUpdating(true);
try {
const response = await fetch(`/api/payment/order/${paymentOrder.id}/force-update`, {
const response = await fetch(`${getApiBase()}/api/payment/order/${paymentOrder.id}/force-update`, {
method: 'POST',
credentials: 'include'
});
@@ -517,7 +518,7 @@ export default function SubscriptionContent() {
setCheckingPayment(true);
try {
const response = await fetch(`/api/payment/order/${paymentOrder.id}/status`, {
const response = await fetch(`${getApiBase()}/api/payment/order/${paymentOrder.id}/status`, {
credentials: 'include'
});

View File

@@ -44,6 +44,7 @@ import { logger } from '../../utils/logger';
import { useAuth } from '../../contexts/AuthContext';
import { useSubscriptionEvents } from '../../hooks/useSubscriptionEvents';
import { subscriptionConfig, themeColors } from '../../views/Pages/Account/subscription-content';
import { getApiBase } from '../../utils/apiConfig';
// 计费周期选择器组件 - 移动端垂直布局(年付在上),桌面端水平布局
interface CycleSelectorProps {
@@ -207,8 +208,8 @@ export default function SubscriptionContentNew() {
// 优先使用 sessionStorage 中的 orderId否则使用 order_no 查询
const orderId = sessionStorage.getItem('alipay_order_id');
const statusUrl = orderId
? `/api/payment/alipay/order/${orderId}/status`
: `/api/payment/alipay/order-by-no/${orderNo}/status`;
? `${getApiBase()}/api/payment/alipay/order/${orderId}/status`
: `${getApiBase()}/api/payment/alipay/order-by-no/${orderNo}/status`;
const response = await fetch(statusUrl, {
credentials: 'include',
@@ -261,7 +262,7 @@ export default function SubscriptionContentNew() {
const fetchSubscriptionPlans = async () => {
try {
logger.debug('SubscriptionContentNew', '正在获取订阅套餐');
const response = await fetch('/api/subscription/plans');
const response = await fetch(`${getApiBase()}/api/subscription/plans`);
if (response.ok) {
const data = await response.json();
@@ -324,7 +325,7 @@ export default function SubscriptionContentNew() {
? promoCodeValue.trim()
: null;
const response = await fetch('/api/subscription/calculate-price', {
const response = await fetch(`${getApiBase()}/api/subscription/calculate-price`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
@@ -430,7 +431,7 @@ export default function SubscriptionContentNew() {
// 检查是否为免费升级(剩余价值足够抵扣新套餐价格)
if (price === 0 && priceInfo?.is_upgrade) {
const response = await fetch('/api/subscription/free-upgrade', {
const response = await fetch(`${getApiBase()}/api/subscription/free-upgrade`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
@@ -479,8 +480,8 @@ export default function SubscriptionContentNew() {
// 根据支付方式选择不同的 API
const apiUrl = paymentMethod === 'alipay'
? '/api/payment/alipay/create-order'
: '/api/payment/create-order';
? `${getApiBase()}/api/payment/alipay/create-order`
: `${getApiBase()}/api/payment/create-order`;
// 检测是否为移动端设备
const userAgent = navigator.userAgent;
@@ -615,8 +616,8 @@ export default function SubscriptionContentNew() {
const startAutoPaymentCheck = (orderId: string, method: 'wechat' | 'alipay' = 'wechat') => {
// 根据支付方式选择不同的状态查询 API
const statusApiUrl = method === 'alipay'
? `/api/payment/alipay/order/${orderId}/status`
: `/api/payment/order/${orderId}/status`;
? `${getApiBase()}/api/payment/alipay/order/${orderId}/status`
: `${getApiBase()}/api/payment/order/${orderId}/status`;
const checkInterval = setInterval(async () => {
try {
@@ -666,8 +667,8 @@ export default function SubscriptionContentNew() {
// 根据订单的支付方式选择不同的查询 API
const orderPaymentMethod = (paymentOrder as any).payment_method || paymentMethod;
const statusApiUrl = orderPaymentMethod === 'alipay'
? `/api/payment/alipay/order/${(paymentOrder as any).id}/status`
: `/api/payment/order/${(paymentOrder as any).id}/status`;
? `${getApiBase()}/api/payment/alipay/order/${(paymentOrder as any).id}/status`
: `${getApiBase()}/api/payment/order/${(paymentOrder as any).id}/status`;
const response = await fetch(statusApiUrl, {
credentials: 'include',

View File

@@ -4,6 +4,7 @@ import { useNavigate } from 'react-router-dom';
import { useToast } from '@chakra-ui/react';
import { logger } from '@utils/logger';
import { performanceMonitor } from '@utils/performanceMonitor';
import { getApiBase } from '@utils/apiConfig';
import { useNotification } from '@contexts/NotificationContext';
// ⚡ PostHog 延迟加载:移除同步导入,首屏减少 ~180KB
// import { identifyUser, resetUser, trackEvent } from '@lib/posthog';
@@ -119,7 +120,7 @@ export const AuthProvider = ({ children }) => {
controller.abort(new Error('Session check timeout after 5 seconds'));
}, 5000); // 5秒超时
const response = await fetch(`/api/auth/session`, {
const response = await fetch(`${getApiBase()}/api/auth/session`, {
method: 'GET',
credentials: 'include',
headers: {
@@ -251,7 +252,7 @@ export const AuthProvider = ({ children }) => {
loginType
});
const response = await fetch(`/api/auth/login`, {
const response = await fetch(`${getApiBase()}/api/auth/login`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
@@ -315,7 +316,7 @@ export const AuthProvider = ({ children }) => {
try {
setIsLoading(true);
const response = await fetch(`/api/auth/register/phone`, {
const response = await fetch(`${getApiBase()}/api/auth/register/phone`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
@@ -370,7 +371,7 @@ export const AuthProvider = ({ children }) => {
// 发送手机验证码
const sendSmsCode = async (phone) => {
try {
const response = await fetch(`/api/auth/send-sms-code`, {
const response = await fetch(`${getApiBase()}/api/auth/send-sms-code`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
@@ -400,7 +401,7 @@ export const AuthProvider = ({ children }) => {
const logout = async () => {
try {
// 调用后端登出API
await fetch(`/api/auth/logout`, {
await fetch(`${getApiBase()}/api/auth/logout`, {
method: 'POST',
credentials: 'include'
});

View File

@@ -3,6 +3,7 @@
import { useState, useEffect, useCallback, useRef } from 'react';
import { logger } from '../utils/logger';
import { getApiBase } from '../utils/apiConfig';
// 交易日数据会从后端获取,这里只做时间判断
const TRADING_SESSIONS = [
@@ -29,7 +30,7 @@ const isInTradingSession = () => {
*/
const fetchIndexRealtime = async (indexCode) => {
try {
const response = await fetch(`/api/index/${indexCode}/realtime`);
const response = await fetch(`${getApiBase()}/api/index/${indexCode}/realtime`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}

View File

@@ -3,6 +3,7 @@ import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import { eventService } from '../../services/eventService';
import { logger } from '../../utils/logger';
import { localCacheManager, CACHE_EXPIRY_STRATEGY } from '../../utils/CacheManager';
import { getApiBase } from '../../utils/apiConfig';
// ==================== 常量定义 ====================
@@ -284,7 +285,7 @@ export const toggleEventFollow = createAsyncThunk(
logger.debug('CommunityData', '切换事件关注状态', { eventId });
// 调用 API自动切换关注状态后端根据当前状态决定关注/取消关注)
const response = await fetch(`/api/events/${eventId}/follow`, {
const response = await fetch(`${getApiBase()}/api/events/${eventId}/follow`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include'

View File

@@ -63,7 +63,7 @@ if (typeof document !== 'undefined') {
*/
const fetchIndexKline = async (indexCode) => {
try {
const response = await fetch(`/api/index/${indexCode}/kline?type=daily`);
const response = await fetch(`${getApiBase()}/api/index/${indexCode}/kline?type=daily`);
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
const data = await response.json();
return data;

View File

@@ -37,6 +37,7 @@ import ReactECharts from 'echarts-for-react';
import { eventService } from '../../../services/eventService';
import CitedContent from '../../../components/Citation/CitedContent';
import { logger } from '../../../utils/logger';
import { getApiBase } from '../../../utils/apiConfig';
import { PROFESSIONAL_COLORS } from '../../../constants/professionalTheme';
// 节点样式配置 - 完全复刻Flask版本
@@ -522,7 +523,7 @@ const TransmissionChainAnalysis = ({ eventId }) => {
// 获取节点详情 - 完全复刻Flask版本API调用
async function getChainNodeDetail(nodeId) {
try {
const response = await fetch(`/api/events/${eventId}/chain-node/${nodeId}`);
const response = await fetch(`${getApiBase()}/api/events/${eventId}/chain-node/${nodeId}`);
const result = await response.json();
if (result.success) {
return result.data;

View File

@@ -3,10 +3,11 @@
*/
import type { Exchange } from '../types';
import { getApiBase } from '@utils/apiConfig';
/**
* 获取 WebSocket 配置
* - 生产环境 (HTTPS): 通过 Nginx 代理使用 wss://
* - 生产环境 (HTTPS): 通过 API 服务器 Nginx 代理使用 wss://
* - 开发环境 (HTTP): 直连 ws://
*/
const getWsConfig = (): Record<Exchange, string> => {
@@ -19,13 +20,15 @@ const getWsConfig = (): Record<Exchange, string> => {
}
const isHttps = window.location.protocol === 'https:';
const host = window.location.host;
if (isHttps) {
// 生产环境:通过 Nginx 代理
// 生产环境:通过 API 服务器的 Nginx 代理
// CDN 不支持 WebSocket需要连接到 api.valuefrontier.cn
const apiBase = getApiBase();
const apiHost = apiBase.replace(/^https?:\/\//, '');
return {
SSE: `wss://${host}/ws/sse`, // 上交所 - Nginx 代理
SZSE: `wss://${host}/ws/szse`, // 深交所 - Nginx 代理
SSE: `wss://${apiHost}/ws/sse`, // 上交所 - 通过 API 服务器代理
SZSE: `wss://${apiHost}/ws/szse`, // 深交所 - 通过 API 服务器代理
};
}

View File

@@ -14,6 +14,7 @@
import { useState, useEffect, useRef, useCallback } from 'react';
import { logger } from '@utils/logger';
import { getApiBase } from '@utils/apiConfig';
import { WS_CONFIG, HEARTBEAT_INTERVAL, RECONNECT_INTERVAL } from './constants';
import { getExchange, normalizeCode, calcChangePct } from './utils';
import type {
@@ -337,7 +338,7 @@ const fetchInitialQuotes = async (
if (codes.length === 0) return {};
try {
const response = await fetch('/api/flex-screen/quotes', {
const response = await fetch(`${getApiBase()}/api/flex-screen/quotes`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ codes, include_order_book: includeOrderBook }),

View File

@@ -62,6 +62,7 @@ import { useRealtimeQuote } from './hooks';
import { getFullCode } from './hooks/utils';
import QuoteTile from './components/QuoteTile';
import { logger } from '@utils/logger';
import { getApiBase } from '@utils/apiConfig';
import type { WatchlistItem, ConnectionStatus } from './types';
// 本地存储 key
@@ -179,7 +180,7 @@ const FlexScreen: React.FC = () => {
setIsSearching(true);
try {
const response = await fetch(`/api/stocks/search?q=${encodeURIComponent(query)}&limit=10`);
const response = await fetch(`${getApiBase()}/api/stocks/search?q=${encodeURIComponent(query)}&limit=10`);
const data: SearchApiResponse = await response.json();
if (data.success) {

View File

@@ -46,6 +46,7 @@ import {
} from 'lucide-react';
import { useNavigate } from 'react-router-dom';
import axios from 'axios';
import { getApiBase } from '@utils/apiConfig';
import { colors, glassEffect } from '../../../theme/glassTheme';
import {
ALERT_TYPE_CONFIG,
@@ -408,7 +409,7 @@ const AlertDetailDrawer = ({ isOpen, onClose, alertData }) => {
setLoadingConcepts(prev => ({ ...prev, [conceptId]: true }));
try {
const response = await axios.get(`/api/concept/${encodeURIComponent(conceptId)}/stocks`);
const response = await axios.get(`${getApiBase()}/api/concept/${encodeURIComponent(conceptId)}/stocks`);
if (response.data?.success && response.data?.data?.stocks) {
setConceptStocks(prev => ({
...prev,

View File

@@ -46,6 +46,7 @@ import {
} from 'lucide-react';
import { useNavigate } from 'react-router-dom';
import axios from 'axios';
import { getApiBase } from '@utils/apiConfig';
import {
ALERT_TYPE_CONFIG,
METRIC_CONFIG,
@@ -692,7 +693,7 @@ const ConceptAlertList = ({
});
try {
const response = await axios.get(`/api/concept/${encodeURIComponent(conceptId)}/stocks`);
const response = await axios.get(`${getApiBase()}/api/concept/${encodeURIComponent(conceptId)}/stocks`);
if (response.data?.success && response.data?.data?.stocks) {
setConceptStocks(prev => ({
...prev,

View File

@@ -4,6 +4,7 @@
*/
import { useState, useEffect, useCallback, useRef } from 'react';
import { logger } from '@utils/logger';
import { getApiBase } from '@utils/apiConfig';
/**
* @param {Date|null} selectedDate - 选中的交易日期
@@ -40,7 +41,7 @@ export const useHotspotData = (selectedDate) => {
const dateParam = selectedDate
? `?date=${dateStr}`
: '';
const response = await fetch(`/api/market/hotspot-overview${dateParam}`);
const response = await fetch(`${getApiBase()}/api/market/hotspot-overview${dateParam}`);
const result = await response.json();
if (result.success) {

View File

@@ -1,5 +1,6 @@
import React, { useState, useEffect, useCallback, useRef } from 'react';
import { useNavigate } from 'react-router-dom';
import { getApiBase } from '@utils/apiConfig';
import {
Box,
Container,
@@ -146,7 +147,7 @@ const StockOverview = () => {
setIsSearching(true);
try {
logger.debug('StockOverview', '开始搜索股票', { query });
const response = await fetch(`/api/stocks/search?q=${encodeURIComponent(query)}&limit=10`);
const response = await fetch(`${getApiBase()}/api/stocks/search?q=${encodeURIComponent(query)}&limit=10`);
const data = await response.json();
logger.debug('StockOverview', 'API返回数据', {
status: response.status,