update pay function
This commit is contained in:
274
src/services/predictionMarketService.api.js
Normal file
274
src/services/predictionMarketService.api.js
Normal file
@@ -0,0 +1,274 @@
|
|||||||
|
/**
|
||||||
|
* 预测市场服务 - API 版本
|
||||||
|
* 调用真实的后端 API,数据存储到 MySQL 数据库
|
||||||
|
*/
|
||||||
|
|
||||||
|
import axios from 'axios';
|
||||||
|
import { getApiBase } from '@utils/apiConfig';
|
||||||
|
|
||||||
|
const api = axios.create({
|
||||||
|
baseURL: getApiBase(),
|
||||||
|
timeout: 10000,
|
||||||
|
withCredentials: true, // 携带 Cookie(session)
|
||||||
|
});
|
||||||
|
|
||||||
|
// ==================== 积分系统 API ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户积分账户
|
||||||
|
*/
|
||||||
|
export const getUserAccount = async () => {
|
||||||
|
try {
|
||||||
|
const response = await api.get('/api/prediction/credit/account');
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取积分账户失败:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 领取每日奖励(100积分)
|
||||||
|
*/
|
||||||
|
export const claimDailyBonus = async () => {
|
||||||
|
try {
|
||||||
|
const response = await api.post('/api/prediction/credit/daily-bonus');
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('领取每日奖励失败:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================== 预测话题 API ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建预测话题
|
||||||
|
* @param {Object} topicData - { title, description, category, deadline }
|
||||||
|
*/
|
||||||
|
export const createTopic = async (topicData) => {
|
||||||
|
try {
|
||||||
|
const response = await api.post('/api/prediction/topics', topicData);
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('创建预测话题失败:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取预测话题列表
|
||||||
|
* @param {Object} params - { status, category, sort_by, page, per_page }
|
||||||
|
*/
|
||||||
|
export const getTopics = async (params = {}) => {
|
||||||
|
try {
|
||||||
|
const response = await api.get('/api/prediction/topics', { params });
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取话题列表失败:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取预测话题详情
|
||||||
|
* @param {number} topicId
|
||||||
|
*/
|
||||||
|
export const getTopicDetail = async (topicId) => {
|
||||||
|
try {
|
||||||
|
const response = await api.get(`/api/prediction/topics/${topicId}`);
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取话题详情失败:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 结算预测话题(仅创建者可操作)
|
||||||
|
* @param {number} topicId
|
||||||
|
* @param {string} result - 'yes' | 'no' | 'draw'
|
||||||
|
*/
|
||||||
|
export const settleTopic = async (topicId, result) => {
|
||||||
|
try {
|
||||||
|
const response = await api.post(`/api/prediction/topics/${topicId}/settle`, { result });
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('结算话题失败:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================== 交易 API ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 买入预测份额
|
||||||
|
* @param {Object} tradeData - { topic_id, direction, shares }
|
||||||
|
*/
|
||||||
|
export const buyShares = async (tradeData) => {
|
||||||
|
try {
|
||||||
|
const response = await api.post('/api/prediction/trade/buy', tradeData);
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('买入份额失败:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户持仓列表
|
||||||
|
*/
|
||||||
|
export const getUserPositions = async () => {
|
||||||
|
try {
|
||||||
|
const response = await api.get('/api/prediction/positions');
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取持仓列表失败:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================== 评论 API ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发表话题评论
|
||||||
|
* @param {number} topicId
|
||||||
|
* @param {Object} commentData - { content, parent_id }
|
||||||
|
*/
|
||||||
|
export const createComment = async (topicId, commentData) => {
|
||||||
|
try {
|
||||||
|
const response = await api.post(`/api/prediction/topics/${topicId}/comments`, commentData);
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('发表评论失败:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取话题评论列表
|
||||||
|
* @param {number} topicId
|
||||||
|
* @param {Object} params - { page, per_page }
|
||||||
|
*/
|
||||||
|
export const getComments = async (topicId, params = {}) => {
|
||||||
|
try {
|
||||||
|
const response = await api.get(`/api/prediction/topics/${topicId}/comments`, { params });
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取评论列表失败:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 点赞/取消点赞评论
|
||||||
|
* @param {number} commentId
|
||||||
|
*/
|
||||||
|
export const likeComment = async (commentId) => {
|
||||||
|
try {
|
||||||
|
const response = await api.post(`/api/prediction/comments/${commentId}/like`);
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('点赞评论失败:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================== 工具函数(价格计算保留在前端,用于实时预览)====================
|
||||||
|
|
||||||
|
export const MARKET_CONFIG = {
|
||||||
|
MAX_SEATS_PER_SIDE: 5,
|
||||||
|
TAX_RATE: 0.02,
|
||||||
|
MIN_PRICE: 50,
|
||||||
|
MAX_PRICE: 950,
|
||||||
|
BASE_PRICE: 500,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算当前价格(简化版AMM)
|
||||||
|
* @param {number} yesShares - Yes方总份额
|
||||||
|
* @param {number} noShares - No方总份额
|
||||||
|
* @returns {Object} {yes: price, no: price}
|
||||||
|
*/
|
||||||
|
export const calculatePrice = (yesShares, noShares) => {
|
||||||
|
const totalShares = yesShares + noShares;
|
||||||
|
|
||||||
|
if (totalShares === 0) {
|
||||||
|
return {
|
||||||
|
yes: MARKET_CONFIG.BASE_PRICE,
|
||||||
|
no: MARKET_CONFIG.BASE_PRICE,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const yesProb = yesShares / totalShares;
|
||||||
|
const noProb = noShares / totalShares;
|
||||||
|
|
||||||
|
let yesPrice = yesProb * 1000;
|
||||||
|
let noPrice = noProb * 1000;
|
||||||
|
|
||||||
|
yesPrice = Math.max(MARKET_CONFIG.MIN_PRICE, Math.min(MARKET_CONFIG.MAX_PRICE, yesPrice));
|
||||||
|
noPrice = Math.max(MARKET_CONFIG.MIN_PRICE, Math.min(MARKET_CONFIG.MAX_PRICE, noPrice));
|
||||||
|
|
||||||
|
return { yes: Math.round(yesPrice), no: Math.round(noPrice) };
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算交易税
|
||||||
|
* @param {number} amount - 交易金额
|
||||||
|
* @returns {number} 税费
|
||||||
|
*/
|
||||||
|
export const calculateTax = (amount) => {
|
||||||
|
return Math.floor(amount * MARKET_CONFIG.TAX_RATE);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算买入成本(用于前端预览)
|
||||||
|
* @param {number} currentShares - 当前方总份额
|
||||||
|
* @param {number} otherShares - 对手方总份额
|
||||||
|
* @param {number} buyAmount - 买入数量
|
||||||
|
* @returns {Object} { amount, tax, total }
|
||||||
|
*/
|
||||||
|
export const calculateBuyCost = (currentShares, otherShares, buyAmount) => {
|
||||||
|
const currentPrice = calculatePrice(currentShares, otherShares);
|
||||||
|
const afterShares = currentShares + buyAmount;
|
||||||
|
const afterPrice = calculatePrice(afterShares, otherShares);
|
||||||
|
|
||||||
|
const avgPrice = (currentPrice.yes + afterPrice.yes) / 2;
|
||||||
|
const amount = avgPrice * buyAmount;
|
||||||
|
const tax = calculateTax(amount);
|
||||||
|
const total = amount + tax;
|
||||||
|
|
||||||
|
return {
|
||||||
|
amount: Math.round(amount),
|
||||||
|
tax: Math.round(tax),
|
||||||
|
total: Math.round(total),
|
||||||
|
avgPrice: Math.round(avgPrice),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
// 积分系统
|
||||||
|
getUserAccount,
|
||||||
|
claimDailyBonus,
|
||||||
|
|
||||||
|
// 话题管理
|
||||||
|
createTopic,
|
||||||
|
getTopics,
|
||||||
|
getTopicDetail,
|
||||||
|
settleTopic,
|
||||||
|
|
||||||
|
// 交易
|
||||||
|
buyShares,
|
||||||
|
getUserPositions,
|
||||||
|
|
||||||
|
// 评论
|
||||||
|
createComment,
|
||||||
|
getComments,
|
||||||
|
likeComment,
|
||||||
|
|
||||||
|
// 工具函数
|
||||||
|
calculatePrice,
|
||||||
|
calculateTax,
|
||||||
|
calculateBuyCost,
|
||||||
|
MARKET_CONFIG,
|
||||||
|
};
|
||||||
@@ -36,8 +36,7 @@ import {
|
|||||||
import { useParams, useNavigate } from 'react-router-dom';
|
import { useParams, useNavigate } from 'react-router-dom';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { forumColors } from '@theme/forumTheme';
|
import { forumColors } from '@theme/forumTheme';
|
||||||
import { getTopic } from '@services/predictionMarketService';
|
import { getTopicDetail, getUserAccount } from '@services/predictionMarketService.api';
|
||||||
import { getUserAccount } from '@services/creditSystemService';
|
|
||||||
import { useAuth } from '@contexts/AuthContext';
|
import { useAuth } from '@contexts/AuthContext';
|
||||||
import TradeModal from './components/TradeModal';
|
import TradeModal from './components/TradeModal';
|
||||||
|
|
||||||
@@ -63,13 +62,24 @@ const PredictionTopicDetail = () => {
|
|||||||
|
|
||||||
// 加载话题数据
|
// 加载话题数据
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const loadTopic = () => {
|
const loadTopic = async () => {
|
||||||
const topicData = getTopic(topicId);
|
try {
|
||||||
if (topicData) {
|
const response = await getTopicDetail(topicId);
|
||||||
setTopic(topicData);
|
if (response.success) {
|
||||||
} else {
|
setTopic(response.data);
|
||||||
|
} else {
|
||||||
|
toast({
|
||||||
|
title: '话题不存在',
|
||||||
|
status: 'error',
|
||||||
|
duration: 3000,
|
||||||
|
});
|
||||||
|
navigate('/value-forum');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取话题详情失败:', error);
|
||||||
toast({
|
toast({
|
||||||
title: '话题不存在',
|
title: '加载失败',
|
||||||
|
description: error.message,
|
||||||
status: 'error',
|
status: 'error',
|
||||||
duration: 3000,
|
duration: 3000,
|
||||||
});
|
});
|
||||||
@@ -77,12 +87,21 @@ const PredictionTopicDetail = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
loadTopic();
|
const loadAccount = async () => {
|
||||||
|
if (!user) return;
|
||||||
|
try {
|
||||||
|
const response = await getUserAccount();
|
||||||
|
if (response.success) {
|
||||||
|
setUserAccount(response.data);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取账户失败:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (user) {
|
loadTopic();
|
||||||
setUserAccount(getUserAccount(user.id));
|
loadAccount();
|
||||||
}
|
}, [topicId, user, toast, navigate]);
|
||||||
}, [topicId, user]);
|
|
||||||
|
|
||||||
// 打开交易弹窗
|
// 打开交易弹窗
|
||||||
const handleOpenTrade = (mode) => {
|
const handleOpenTrade = (mode) => {
|
||||||
@@ -100,10 +119,21 @@ const PredictionTopicDetail = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 交易成功回调
|
// 交易成功回调
|
||||||
const handleTradeSuccess = () => {
|
const handleTradeSuccess = async () => {
|
||||||
// 刷新话题数据
|
// 刷新话题数据
|
||||||
setTopic(getTopic(topicId));
|
try {
|
||||||
setUserAccount(getUserAccount(user.id));
|
const topicResponse = await getTopicDetail(topicId);
|
||||||
|
if (topicResponse.success) {
|
||||||
|
setTopic(topicResponse.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
const accountResponse = await getUserAccount();
|
||||||
|
if (accountResponse.success) {
|
||||||
|
setUserAccount(accountResponse.data);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('刷新数据失败:', error);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!topic) {
|
if (!topic) {
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ import {
|
|||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { Zap, Calendar, DollarSign } from 'lucide-react';
|
import { Zap, Calendar, DollarSign } from 'lucide-react';
|
||||||
import { forumColors } from '@theme/forumTheme';
|
import { forumColors } from '@theme/forumTheme';
|
||||||
import { createTopic } from '@services/predictionMarketService';
|
import { createTopic, getUserAccount } from '@services/predictionMarketService.api';
|
||||||
import { getUserAccount, CREDIT_CONFIG } from '@services/creditSystemService';
|
import { CREDIT_CONFIG } from '@services/creditSystemService';
|
||||||
import { useAuth } from '@contexts/AuthContext';
|
import { useAuth } from '@contexts/AuthContext';
|
||||||
|
|
||||||
const CreatePredictionModal = ({ isOpen, onClose, onTopicCreated }) => {
|
const CreatePredictionModal = ({ isOpen, onClose, onTopicCreated }) => {
|
||||||
@@ -46,9 +46,23 @@ const CreatePredictionModal = ({ isOpen, onClose, onTopicCreated }) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
|
const [userAccount, setUserAccount] = useState(null);
|
||||||
|
|
||||||
// 获取用户余额
|
// 异步获取用户余额
|
||||||
const userAccount = user ? getUserAccount(user.id) : null;
|
useEffect(() => {
|
||||||
|
const fetchAccount = async () => {
|
||||||
|
if (!user || !isOpen) return;
|
||||||
|
try {
|
||||||
|
const response = await getUserAccount();
|
||||||
|
if (response.success) {
|
||||||
|
setUserAccount(response.data);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取账户失败:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
fetchAccount();
|
||||||
|
}, [user, isOpen]);
|
||||||
|
|
||||||
// 处理表单变化
|
// 处理表单变化
|
||||||
const handleChange = (field, value) => {
|
const handleChange = (field, value) => {
|
||||||
@@ -80,7 +94,7 @@ const CreatePredictionModal = ({ isOpen, onClose, onTopicCreated }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 检查余额
|
// 检查余额
|
||||||
if (userAccount.balance < CREDIT_CONFIG.CREATE_TOPIC_COST) {
|
if (!userAccount || userAccount.balance < CREDIT_CONFIG.CREATE_TOPIC_COST) {
|
||||||
toast({
|
toast({
|
||||||
title: '积分不足',
|
title: '积分不足',
|
||||||
description: `创建话题需要${CREDIT_CONFIG.CREATE_TOPIC_COST}积分`,
|
description: `创建话题需要${CREDIT_CONFIG.CREATE_TOPIC_COST}积分`,
|
||||||
@@ -94,42 +108,43 @@ const CreatePredictionModal = ({ isOpen, onClose, onTopicCreated }) => {
|
|||||||
const deadline = new Date();
|
const deadline = new Date();
|
||||||
deadline.setDate(deadline.getDate() + parseInt(formData.deadline_days));
|
deadline.setDate(deadline.getDate() + parseInt(formData.deadline_days));
|
||||||
|
|
||||||
const settlement_date = new Date(deadline);
|
// 调用 API 创建话题
|
||||||
settlement_date.setDate(settlement_date.getDate() + 1);
|
const response = await createTopic({
|
||||||
|
|
||||||
// 创建话题
|
|
||||||
const newTopic = createTopic({
|
|
||||||
author_id: user.id,
|
|
||||||
author_name: user.name || user.username,
|
|
||||||
author_avatar: user.avatar,
|
|
||||||
title: formData.title,
|
title: formData.title,
|
||||||
description: formData.description,
|
description: formData.description,
|
||||||
category: formData.category,
|
category: formData.category,
|
||||||
deadline: deadline.toISOString(),
|
deadline: deadline.toISOString(),
|
||||||
settlement_date: settlement_date.toISOString(),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
toast({
|
if (response.success) {
|
||||||
title: '创建成功!',
|
toast({
|
||||||
description: `话题已发布,扣除${CREDIT_CONFIG.CREATE_TOPIC_COST}积分`,
|
title: '创建成功!',
|
||||||
status: 'success',
|
description: `话题已发布,剩余 ${response.data.new_balance} 积分`,
|
||||||
duration: 3000,
|
status: 'success',
|
||||||
});
|
duration: 3000,
|
||||||
|
});
|
||||||
|
|
||||||
// 重置表单
|
// 重置表单
|
||||||
setFormData({
|
setFormData({
|
||||||
title: '',
|
title: '',
|
||||||
description: '',
|
description: '',
|
||||||
category: 'stock',
|
category: 'stock',
|
||||||
deadline_days: 7,
|
deadline_days: 7,
|
||||||
});
|
});
|
||||||
|
|
||||||
// 通知父组件
|
// 通知父组件
|
||||||
if (onTopicCreated) {
|
if (onTopicCreated) {
|
||||||
onTopicCreated(newTopic);
|
onTopicCreated(response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
onClose();
|
||||||
|
|
||||||
|
// 刷新账户数据
|
||||||
|
const accountResponse = await getUserAccount();
|
||||||
|
if (accountResponse.success) {
|
||||||
|
setUserAccount(accountResponse.data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onClose();
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('创建话题失败:', error);
|
console.error('创建话题失败:', error);
|
||||||
toast({
|
toast({
|
||||||
|
|||||||
@@ -33,14 +33,13 @@ import { TrendingUp, TrendingDown, DollarSign, AlertCircle, Zap } from 'lucide-r
|
|||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { forumColors } from '@theme/forumTheme';
|
import { forumColors } from '@theme/forumTheme';
|
||||||
import {
|
import {
|
||||||
buyPosition,
|
buyShares,
|
||||||
sellPosition,
|
getUserAccount,
|
||||||
calculateBuyCost,
|
calculateBuyCost,
|
||||||
calculateSellRevenue,
|
|
||||||
calculateTax,
|
calculateTax,
|
||||||
getTopic,
|
MARKET_CONFIG,
|
||||||
} from '@services/predictionMarketService';
|
} from '@services/predictionMarketService.api';
|
||||||
import { getUserAccount, CREDIT_CONFIG } from '@services/creditSystemService';
|
import { CREDIT_CONFIG } from '@services/creditSystemService';
|
||||||
import { useAuth } from '@contexts/AuthContext';
|
import { useAuth } from '@contexts/AuthContext';
|
||||||
|
|
||||||
const MotionBox = motion(Box);
|
const MotionBox = motion(Box);
|
||||||
@@ -53,9 +52,23 @@ const TradeModal = ({ isOpen, onClose, topic, mode = 'buy', onTradeSuccess }) =>
|
|||||||
const [selectedOption, setSelectedOption] = useState('yes');
|
const [selectedOption, setSelectedOption] = useState('yes');
|
||||||
const [shares, setShares] = useState(1);
|
const [shares, setShares] = useState(1);
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
|
const [userAccount, setUserAccount] = useState(null);
|
||||||
|
|
||||||
// 获取用户账户
|
// 异步获取用户账户
|
||||||
const userAccount = user ? getUserAccount(user.id) : null;
|
useEffect(() => {
|
||||||
|
const fetchAccount = async () => {
|
||||||
|
if (!user || !isOpen) return;
|
||||||
|
try {
|
||||||
|
const response = await getUserAccount();
|
||||||
|
if (response.success) {
|
||||||
|
setUserAccount(response.data);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取账户失败:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
fetchAccount();
|
||||||
|
}, [user, isOpen]);
|
||||||
|
|
||||||
// 重置状态
|
// 重置状态
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -79,15 +92,22 @@ const TradeModal = ({ isOpen, onClose, topic, mode = 'buy', onTradeSuccess }) =>
|
|||||||
let avgPrice = 0;
|
let avgPrice = 0;
|
||||||
|
|
||||||
if (mode === 'buy') {
|
if (mode === 'buy') {
|
||||||
cost = calculateBuyCost(selectedSide.total_shares, otherSide.total_shares, shares);
|
const costData = calculateBuyCost(
|
||||||
tax = calculateTax(cost);
|
selectedOption === 'yes' ? selectedSide.total_shares : otherSide.total_shares,
|
||||||
totalCost = cost + tax;
|
selectedOption === 'yes' ? otherSide.total_shares : selectedSide.total_shares,
|
||||||
avgPrice = cost / shares;
|
shares
|
||||||
|
);
|
||||||
|
cost = costData.amount;
|
||||||
|
tax = costData.tax;
|
||||||
|
totalCost = costData.total;
|
||||||
|
avgPrice = costData.avgPrice;
|
||||||
} else {
|
} else {
|
||||||
cost = calculateSellRevenue(selectedSide.total_shares, otherSide.total_shares, shares);
|
// 卖出功能暂未实现,使用简化计算
|
||||||
|
const currentPrice = selectedSide.current_price || MARKET_CONFIG.BASE_PRICE;
|
||||||
|
cost = currentPrice * shares;
|
||||||
tax = calculateTax(cost);
|
tax = calculateTax(cost);
|
||||||
totalCost = cost - tax;
|
totalCost = cost - tax;
|
||||||
avgPrice = cost / shares;
|
avgPrice = currentPrice;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取用户在该方向的持仓
|
// 获取用户在该方向的持仓
|
||||||
@@ -128,43 +148,49 @@ const TradeModal = ({ isOpen, onClose, topic, mode = 'buy', onTradeSuccess }) =>
|
|||||||
try {
|
try {
|
||||||
setIsSubmitting(true);
|
setIsSubmitting(true);
|
||||||
|
|
||||||
let result;
|
|
||||||
if (mode === 'buy') {
|
if (mode === 'buy') {
|
||||||
result = buyPosition({
|
// 调用买入 API
|
||||||
user_id: user.id,
|
const response = await buyShares({
|
||||||
user_name: user.name || user.username,
|
|
||||||
user_avatar: user.avatar,
|
|
||||||
topic_id: topic.id,
|
topic_id: topic.id,
|
||||||
option_id: selectedOption,
|
direction: selectedOption,
|
||||||
shares,
|
shares,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
toast({
|
||||||
|
title: '购买成功!',
|
||||||
|
description: `花费${totalCost}积分,剩余 ${response.data.new_balance} 积分`,
|
||||||
|
status: 'success',
|
||||||
|
duration: 3000,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 刷新账户数据
|
||||||
|
const accountResponse = await getUserAccount();
|
||||||
|
if (accountResponse.success) {
|
||||||
|
setUserAccount(accountResponse.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 通知父组件刷新
|
||||||
|
if (onTradeSuccess) {
|
||||||
|
onTradeSuccess(response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
onClose();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
result = sellPosition({
|
// 卖出功能暂未实现
|
||||||
user_id: user.id,
|
toast({
|
||||||
topic_id: topic.id,
|
title: '功能暂未开放',
|
||||||
option_id: selectedOption,
|
description: '卖出功能正在开发中,敬请期待',
|
||||||
shares,
|
status: 'warning',
|
||||||
|
duration: 3000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
toast({
|
|
||||||
title: mode === 'buy' ? '购买成功!' : '卖出成功!',
|
|
||||||
description: mode === 'buy' ? `花费${totalCost}积分` : `获得${totalCost}积分`,
|
|
||||||
status: 'success',
|
|
||||||
duration: 3000,
|
|
||||||
});
|
|
||||||
|
|
||||||
// 通知父组件刷新
|
|
||||||
if (onTradeSuccess) {
|
|
||||||
onTradeSuccess(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
onClose();
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('交易失败:', error);
|
console.error('交易失败:', error);
|
||||||
toast({
|
toast({
|
||||||
title: '交易失败',
|
title: '交易失败',
|
||||||
description: error.message,
|
description: error.response?.data?.message || error.message,
|
||||||
status: 'error',
|
status: 'error',
|
||||||
duration: 3000,
|
duration: 3000,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ import { Search, PenSquare, TrendingUp, Clock, Heart, Zap, HelpCircle } from 'lu
|
|||||||
import { motion, AnimatePresence } from 'framer-motion';
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
import { forumColors } from '@theme/forumTheme';
|
import { forumColors } from '@theme/forumTheme';
|
||||||
import { getPosts, searchPosts } from '@services/elasticsearchService';
|
import { getPosts, searchPosts } from '@services/elasticsearchService';
|
||||||
import { getTopics } from '@services/predictionMarketService';
|
import { getTopics } from '@services/predictionMarketService.api';
|
||||||
import PostCard from './components/PostCard';
|
import PostCard from './components/PostCard';
|
||||||
import PredictionTopicCard from './components/PredictionTopicCard';
|
import PredictionTopicCard from './components/PredictionTopicCard';
|
||||||
import CreatePostModal from './components/CreatePostModal';
|
import CreatePostModal from './components/CreatePostModal';
|
||||||
@@ -93,11 +93,13 @@ const ValueForum = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 获取预测话题列表
|
// 获取预测话题列表
|
||||||
const fetchPredictionTopics = () => {
|
const fetchPredictionTopics = async () => {
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const topics = getTopics({ status: 'active', sortBy });
|
const response = await getTopics({ status: 'active', sort_by: sortBy });
|
||||||
setPredictionTopics(topics);
|
if (response.success) {
|
||||||
|
setPredictionTopics(response.data);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取预测话题失败:', error);
|
console.error('获取预测话题失败:', error);
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
Reference in New Issue
Block a user