重构分析架构,彻底解决重复历史记录问题

This commit is contained in:
patdelphi
2025-08-19 13:58:26 +08:00
parent 75aedeb7ca
commit 3730d66185
16 changed files with 294 additions and 136 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -39,34 +39,12 @@ router.post('/bazi', authenticate, asyncHandler(async (req, res) => {
}
try {
// 执行八字分析
// 执行八字分析(纯分析,不存储历史记录)
const analysisResult = await baziAnalyzer.performFullBaziAnalysis(birth_data);
// 保存到数据库
const db = getDB();
const insertReading = db.prepare(`
INSERT INTO numerology_readings (
user_id, reading_type, name, birth_date, birth_time, birth_place, gender,
input_data, analysis, status
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`);
const result = insertReading.run(
req.user.id,
'bazi',
birth_data.name,
birth_data.birth_date,
birth_data.birth_time || null,
birth_data.birth_place || null,
birth_data.gender || null,
JSON.stringify(birth_data),
JSON.stringify(analysisResult),
'completed'
);
// 只返回分析结果,不存储历史记录
res.json({
data: {
record_id: result.lastInsertRowid,
analysis: analysisResult
}
});
@@ -86,38 +64,16 @@ router.post('/yijing', authenticate, asyncHandler(async (req, res) => {
}
try {
// 执行易经分析
// 执行易经分析(纯分析,不存储历史记录)
const analysisResult = yijingAnalyzer.performYijingAnalysis({
question: question,
user_id: user_id || req.user.id,
divination_method: divination_method || 'time'
});
// 保存到数据库
const db = getDB();
const insertReading = db.prepare(`
INSERT INTO numerology_readings (
user_id, reading_type, name, birth_date, birth_time, birth_place, gender,
input_data, analysis, status
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`);
const result = insertReading.run(
req.user.id,
'yijing',
'易经占卜用户', // 易经占卜不需要真实姓名
null, // 不需要出生日期
null, // 不需要出生时间
null, // 不需要出生地点
null, // 不需要性别
JSON.stringify({ question, divination_method }),
JSON.stringify(analysisResult),
'completed'
);
// 只返回分析结果,不存储历史记录
res.json({
data: {
record_id: result.lastInsertRowid,
analysis: analysisResult
}
});
@@ -152,34 +108,12 @@ router.post('/ziwei', authenticate, asyncHandler(async (req, res) => {
}
try {
// 执行紫微斗数分析
// 执行紫微斗数分析(纯分析,不存储历史记录)
const analysisResult = ziweiAnalyzer.performRealZiweiAnalysis(birth_data);
// 保存到数据库
const db = getDB();
const insertReading = db.prepare(`
INSERT INTO numerology_readings (
user_id, reading_type, name, birth_date, birth_time, birth_place, gender,
input_data, analysis, status
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`);
const result = insertReading.run(
req.user.id,
'ziwei',
birth_data.name,
birth_data.birth_date,
birth_data.birth_time || null,
birth_data.birth_place || null,
birth_data.gender || null,
JSON.stringify(birth_data),
JSON.stringify(analysisResult),
'completed'
);
// 只返回分析结果,不存储历史记录
res.json({
data: {
record_id: result.lastInsertRowid,
analysis: analysisResult
}
});
@@ -189,6 +123,79 @@ router.post('/ziwei', authenticate, asyncHandler(async (req, res) => {
}
}));
// 历史记录存储接口
router.post('/save-history', authenticate, asyncHandler(async (req, res) => {
const { analysis_type, analysis_data, input_data } = req.body;
// 输入验证
if (!analysis_type || !analysis_data) {
throw new AppError('缺少必要参数:分析类型和分析数据', 400, 'MISSING_REQUIRED_DATA');
}
// 验证分析类型
const validTypes = ['bazi', 'ziwei', 'yijing'];
if (!validTypes.includes(analysis_type)) {
throw new AppError('无效的分析类型', 400, 'INVALID_ANALYSIS_TYPE');
}
try {
const db = getDB();
// 根据分析类型准备不同的数据
let name, birth_date, birth_time, birth_place, gender;
if (analysis_type === 'yijing') {
// 易经占卜:获取用户档案信息
const getUserProfile = db.prepare('SELECT full_name FROM user_profiles WHERE user_id = ?');
const userProfile = getUserProfile.get(req.user.id);
name = userProfile?.full_name || '易经占卜用户';
birth_date = null;
birth_time = null;
birth_place = null;
gender = null;
} else {
// 八字和紫微:从输入数据中获取
name = input_data?.name || '用户';
birth_date = input_data?.birth_date || null;
birth_time = input_data?.birth_time || null;
birth_place = input_data?.birth_place || null;
gender = input_data?.gender || null;
}
// 插入历史记录
const insertReading = db.prepare(`
INSERT INTO numerology_readings (
user_id, reading_type, name, birth_date, birth_time, birth_place, gender,
input_data, analysis, status, created_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`);
const result = insertReading.run(
req.user.id,
analysis_type,
name,
birth_date,
birth_time,
birth_place,
gender,
JSON.stringify(input_data || {}),
JSON.stringify(analysis_data),
'completed',
new Date().toISOString()
);
res.json({
data: {
record_id: result.lastInsertRowid,
message: '历史记录保存成功'
}
});
} catch (error) {
console.error('保存历史记录错误:', error);
throw new AppError('保存历史记录失败', 500, 'SAVE_HISTORY_ERROR');
}
}));
// 综合分析接口(可选)
router.post('/comprehensive', authenticate, asyncHandler(async (req, res) => {
const { birth_data, include_types } = req.body;

View File

@@ -79,7 +79,7 @@ class BaziAnalyzer {
return {
analysis_type: 'bazi',
analysis_date: new Date().toISOString().split('T')[0],
analysis_date: new Date().toISOString(),
basic_info: {
personal_data: {
name: personalizedName,

View File

@@ -58,7 +58,7 @@ class YijingAnalyzer {
return {
analysis_type: 'yijing',
analysis_date: currentTime.toISOString().split('T')[0],
analysis_date: currentTime.toISOString(),
basic_info: {
divination_data: {
question: question,

View File

@@ -67,7 +67,7 @@ class ZiweiAnalyzer {
return {
analysis_type: 'ziwei',
analysis_date: new Date().toISOString().split('T')[0],
analysis_date: new Date().toISOString(),
basic_info: {
personal_data: {
name: personName,

View File

@@ -14,6 +14,7 @@ interface AnalysisResultDisplayProps {
question?: string;
userId?: string;
divinationMethod?: string;
preAnalysisData?: any; // 预先分析的数据,用于历史记录
}
const AnalysisResultDisplay: React.FC<AnalysisResultDisplayProps> = ({
@@ -22,7 +23,8 @@ const AnalysisResultDisplay: React.FC<AnalysisResultDisplayProps> = ({
birthDate,
question,
userId,
divinationMethod
divinationMethod,
preAnalysisData
}) => {
// 安全地获取数据的辅助函数
const safeGet = (obj: any, path: string, defaultValue: any = '暂无数据') => {
@@ -58,7 +60,7 @@ const AnalysisResultDisplay: React.FC<AnalysisResultDisplayProps> = ({
const renderBaziAnalysis = () => {
// 如果有 birthDate使用新的 CompleteBaziAnalysis 组件
if (birthDate) {
return <CompleteBaziAnalysis birthDate={birthDate} />;
return <CompleteBaziAnalysis birthDate={birthDate} analysisData={preAnalysisData} />;
}
// 如果有分析结果但没有 birthDate尝试从结果中提取出生信息
if (analysisResult && analysisResult.data) {
@@ -70,21 +72,22 @@ const AnalysisResultDisplay: React.FC<AnalysisResultDisplayProps> = ({
name: basicInfo.personal_data.name || '',
gender: basicInfo.personal_data.gender === '男性' ? 'male' : 'female'
};
return <CompleteBaziAnalysis birthDate={extractedBirthDate} />;
return <CompleteBaziAnalysis birthDate={extractedBirthDate} analysisData={preAnalysisData} />;
}
}
// 回退到旧的组件(向后兼容)
if (birthDate) {
return <BaziAnalysisDisplay birthDate={birthDate} />;
}
return <div className="text-center text-red-600 p-8"></div>;
// 如果没有足够的数据,返回错误提示
return (
<div className="bg-white rounded-lg p-6 shadow-lg">
<p className="text-gray-500 text-center"></p>
</div>
);
};
// 渲染紫微斗数分析
const renderZiweiAnalysis = () => {
// 如果有 birthDate使用新的 CompleteZiweiAnalysis 组件
if (birthDate) {
return <CompleteZiweiAnalysis birthDate={birthDate} />;
return <CompleteZiweiAnalysis birthDate={birthDate} analysisData={preAnalysisData} />;
}
// 如果有分析结果但没有 birthDate尝试从结果中提取出生信息
if (analysisResult && analysisResult.data) {
@@ -96,7 +99,7 @@ const AnalysisResultDisplay: React.FC<AnalysisResultDisplayProps> = ({
name: basicInfo.personal_data.name || '',
gender: basicInfo.personal_data.gender === '男性' ? 'male' : 'female'
};
return <CompleteZiweiAnalysis birthDate={extractedBirthDate} />;
return <CompleteZiweiAnalysis birthDate={extractedBirthDate} analysisData={preAnalysisData} />;
}
}
@@ -439,13 +442,14 @@ const AnalysisResultDisplay: React.FC<AnalysisResultDisplayProps> = ({
question={question}
userId={userId}
divinationMethod={divinationMethod}
analysisData={preAnalysisData}
/>
);
}
// 对于紫微斗数,如果有 birthDate 参数,直接返回 CompleteZiweiAnalysis 组件(不添加额外容器)
if (analysisType === 'ziwei' && birthDate) {
return <CompleteZiweiAnalysis birthDate={birthDate} />;
return <CompleteZiweiAnalysis birthDate={birthDate} analysisData={preAnalysisData} />;
}
// 如果没有分析结果数据

View File

@@ -11,12 +11,13 @@ interface CompleteBaziAnalysisProps {
name?: string;
gender?: string;
};
analysisData?: any; // 可选的预先分析的数据
}
const CompleteBaziAnalysis: React.FC<CompleteBaziAnalysisProps> = ({ birthDate }) => {
const [isLoading, setIsLoading] = useState(true);
const CompleteBaziAnalysis: React.FC<CompleteBaziAnalysisProps> = ({ birthDate, analysisData: propAnalysisData }) => {
const [isLoading, setIsLoading] = useState(!propAnalysisData);
const [error, setError] = useState<string | null>(null);
const [analysisData, setAnalysisData] = useState<any>(null);
const [analysisData, setAnalysisData] = useState<any>(propAnalysisData || null);
// 五行颜色配置
const elementColors: { [key: string]: string } = {
@@ -52,6 +53,13 @@ const CompleteBaziAnalysis: React.FC<CompleteBaziAnalysisProps> = ({ birthDate }
};
useEffect(() => {
// 如果已经有分析数据,直接使用
if (propAnalysisData) {
setAnalysisData(propAnalysisData);
setIsLoading(false);
return;
}
const fetchAnalysisData = async () => {
try {
setIsLoading(true);
@@ -84,10 +92,10 @@ const CompleteBaziAnalysis: React.FC<CompleteBaziAnalysisProps> = ({ birthDate }
}
};
if (birthDate?.date) {
if (birthDate?.date && !propAnalysisData) {
fetchAnalysisData();
}
}, [birthDate]);
}, [birthDate?.date, birthDate?.time, birthDate?.name, birthDate?.gender, propAnalysisData]);
// 渲染加载状态
if (isLoading) {
@@ -284,7 +292,7 @@ const CompleteBaziAnalysis: React.FC<CompleteBaziAnalysisProps> = ({ birthDate }
</div>
<div className="flex items-center space-x-2">
<User className="h-5 w-5" />
<span>{analysisData.basic_info?.personal_data?.gender}</span>
<span>{analysisData.basic_info?.personal_data?.gender === 'male' ? '男性' : analysisData.basic_info?.personal_data?.gender === 'female' ? '女性' : analysisData.basic_info?.personal_data?.gender}</span>
</div>
</div>
</CardHeader>
@@ -759,7 +767,7 @@ const CompleteBaziAnalysis: React.FC<CompleteBaziAnalysisProps> = ({ birthDate }
<CardContent className="text-center py-8">
<div className="text-red-800">
<p className="text-lg font-bold mb-2"></p>
<p className="text-sm">{analysisData.analysis_date}</p>
<p className="text-sm">{analysisData.analysis_date ? new Date(analysisData.analysis_date).toLocaleString('zh-CN') : new Date().toLocaleString('zh-CN')}</p>
<p className="text-xs mt-4 text-red-600">
</p>

View File

@@ -4,19 +4,21 @@ import { Card, CardContent, CardHeader, CardTitle } from './ui/Card';
import { localApi } from '../lib/localApi';
interface CompleteYijingAnalysisProps {
question: string;
question?: string;
userId?: string;
divinationMethod?: string;
analysisData?: any; // 可选的预先分析的数据
}
const CompleteYijingAnalysis: React.FC<CompleteYijingAnalysisProps> = ({
question,
userId = 'user123',
divinationMethod = 'time'
divinationMethod = 'time',
analysisData: propAnalysisData
}) => {
const [isLoading, setIsLoading] = useState(true);
const [isLoading, setIsLoading] = useState(!propAnalysisData);
const [error, setError] = useState<string | null>(null);
const [analysisData, setAnalysisData] = useState<any>(null);
const [analysisData, setAnalysisData] = useState<any>(propAnalysisData || null);
// 卦象颜色配置
const hexagramColors: { [key: string]: string } = {
@@ -49,6 +51,13 @@ const CompleteYijingAnalysis: React.FC<CompleteYijingAnalysisProps> = ({
};
useEffect(() => {
// 如果已经有分析数据,直接使用
if (propAnalysisData) {
setAnalysisData(propAnalysisData);
setIsLoading(false);
return;
}
const fetchAnalysisData = async () => {
try {
setIsLoading(true);
@@ -80,10 +89,10 @@ const CompleteYijingAnalysis: React.FC<CompleteYijingAnalysisProps> = ({
}
};
if (question) {
if (question && !propAnalysisData) {
fetchAnalysisData();
}
}, [question, userId, divinationMethod]);
}, [question, userId, divinationMethod, propAnalysisData]);
// 渲染加载状态
if (isLoading) {
@@ -263,11 +272,7 @@ const CompleteYijingAnalysis: React.FC<CompleteYijingAnalysisProps> = ({
<span></span>
<Hexagon className="h-8 w-8" />
</CardTitle>
<div className="flex justify-center space-x-6 mt-4 text-red-700">
<div className="flex items-center space-x-2">
<Calendar className="h-5 w-5" />
<span>{analysisData.analysis_date}</span>
</div>
<div className="flex justify-center mt-4 text-red-700">
<div className="flex items-center space-x-2">
<Clock className="h-5 w-5" />
<span>{new Date(analysisData.basic_info.divination_data.divination_time).toLocaleString('zh-CN')}</span>

View File

@@ -11,12 +11,13 @@ interface CompleteZiweiAnalysisProps {
name?: string;
gender?: string;
};
analysisData?: any; // 可选的预先分析的数据
}
const CompleteZiweiAnalysis: React.FC<CompleteZiweiAnalysisProps> = ({ birthDate }) => {
const [isLoading, setIsLoading] = useState(true);
const CompleteZiweiAnalysis: React.FC<CompleteZiweiAnalysisProps> = ({ birthDate, analysisData: propAnalysisData }) => {
const [isLoading, setIsLoading] = useState(!propAnalysisData);
const [error, setError] = useState<string | null>(null);
const [analysisData, setAnalysisData] = useState<any>(null);
const [analysisData, setAnalysisData] = useState<any>(propAnalysisData || null);
// 四化飞星详细解释
const sihuaExplanations = {
@@ -251,6 +252,13 @@ const CompleteZiweiAnalysis: React.FC<CompleteZiweiAnalysisProps> = ({ birthDate
};
useEffect(() => {
// 如果已经有分析数据,直接使用
if (propAnalysisData) {
setAnalysisData(propAnalysisData);
setIsLoading(false);
return;
}
const fetchAnalysisData = async () => {
try {
setIsLoading(true);
@@ -283,10 +291,10 @@ const CompleteZiweiAnalysis: React.FC<CompleteZiweiAnalysisProps> = ({ birthDate
}
};
if (birthDate?.date) {
if (birthDate?.date && !propAnalysisData) {
fetchAnalysisData();
}
}, [birthDate]);
}, [birthDate?.date, birthDate?.time, birthDate?.name, birthDate?.gender, propAnalysisData]);
// 渲染加载状态
if (isLoading) {
@@ -467,7 +475,7 @@ const CompleteZiweiAnalysis: React.FC<CompleteZiweiAnalysisProps> = ({ birthDate
</div>
<div className="flex items-center space-x-2">
<User className="h-5 w-5" />
<span>{analysisData.basic_info?.personal_data?.gender}</span>
<span>{analysisData.basic_info?.personal_data?.gender === 'male' ? '男性' : analysisData.basic_info?.personal_data?.gender === 'female' ? '女性' : analysisData.basic_info?.personal_data?.gender}</span>
</div>
</div>
</CardHeader>
@@ -1122,7 +1130,7 @@ const CompleteZiweiAnalysis: React.FC<CompleteZiweiAnalysisProps> = ({ birthDate
</p>
<div className="mt-4 text-xs text-gray-500">
{new Date().toLocaleString('zh-CN')}
{analysisData.analysis_date ? new Date(analysisData.analysis_date).toLocaleString('zh-CN') : new Date().toLocaleString('zh-CN')}
</div>
</CardContent>
</Card>

View File

@@ -23,6 +23,7 @@ interface AuthResponse {
class LocalApiClient {
private token: string | null = null;
private pendingRequests: Map<string, Promise<any>> = new Map();
constructor() {
// 从localStorage恢复token
@@ -176,30 +177,60 @@ class LocalApiClient {
},
};
// 生成请求唯一键
private generateRequestKey(endpoint: string, data: any): string {
return `${endpoint}:${JSON.stringify(data)}`;
}
// 带去重的请求方法
private async requestWithDeduplication<T>(
endpoint: string,
options: RequestInit,
data: any
): Promise<ApiResponse<T>> {
const requestKey = this.generateRequestKey(endpoint, data);
// 如果已有相同请求在进行中返回该请求的Promise
if (this.pendingRequests.has(requestKey)) {
return this.pendingRequests.get(requestKey);
}
// 创建新请求
const requestPromise = this.request<T>(endpoint, options).finally(() => {
// 请求完成后清除缓存
this.pendingRequests.delete(requestKey);
});
// 缓存请求Promise
this.pendingRequests.set(requestKey, requestPromise);
return requestPromise;
}
// 分析相关方法
analysis = {
// 八字分析
bazi: async (birthData: any): Promise<ApiResponse<{ record_id: number; analysis: any }>> => {
return this.request<{ record_id: number; analysis: any }>('/analysis/bazi', {
return this.requestWithDeduplication<{ record_id: number; analysis: any }>('/analysis/bazi', {
method: 'POST',
body: JSON.stringify({ birth_data: birthData }),
});
}, birthData);
},
// 紫微斗数分析
ziwei: async (birthData: any): Promise<ApiResponse<{ record_id: number; analysis: any }>> => {
return this.request<{ record_id: number; analysis: any }>('/analysis/ziwei', {
return this.requestWithDeduplication<{ record_id: number; analysis: any }>('/analysis/ziwei', {
method: 'POST',
body: JSON.stringify({ birth_data: birthData }),
});
}, birthData);
},
// 易经分析
yijing: async (yijingData: any): Promise<ApiResponse<{ record_id: number; analysis: any }>> => {
return this.request<{ record_id: number; analysis: any }>('/analysis/yijing', {
return this.requestWithDeduplication<{ record_id: number; analysis: any }>('/analysis/yijing', {
method: 'POST',
body: JSON.stringify(yijingData),
});
}, yijingData);
},
// 综合分析
@@ -222,6 +253,18 @@ class LocalApiClient {
body: JSON.stringify({ birth_data: birthData, analysis_type: analysisType }),
});
},
// 保存历史记录
saveHistory: async (analysisType: string, analysisData: any, inputData?: any): Promise<ApiResponse<{ record_id: number; message: string }>> => {
return this.request<{ record_id: number; message: string }>('/analysis/save-history', {
method: 'POST',
body: JSON.stringify({
analysis_type: analysisType,
analysis_data: analysisData,
input_data: inputData
}),
});
},
};
// 历史记录相关方法

View File

@@ -5,9 +5,7 @@ import './index.css'
import App from './App.tsx'
createRoot(document.getElementById('root')!).render(
<StrictMode>
<ErrorBoundary>
<App />
</ErrorBoundary>
</StrictMode>,
<ErrorBoundary>
<App />
</ErrorBoundary>,
)

View File

@@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useMemo } from 'react';
import { useAuth } from '../contexts/AuthContext';
import { localApi } from '../lib/localApi';
import { Button } from '../components/ui/Button';
@@ -22,15 +22,33 @@ const AnalysisPage: React.FC = () => {
birth_time: '',
gender: 'male' as 'male' | 'female',
birth_place: '',
question: ''
question: '财运'
});
const [loading, setLoading] = useState(false);
const [analysisResult, setAnalysisResult] = useState<any>(null);
// 使用useMemo缓存birthDate对象避免重复渲染导致useEffect重复执行
const memoizedBirthDate = useMemo(() => {
if (analysisType === 'bazi' || analysisType === 'ziwei') {
return {
date: formData.birth_date,
time: formData.birth_time,
name: formData.name,
gender: formData.gender
};
}
return undefined;
}, [analysisType, formData.birth_date, formData.birth_time, formData.name, formData.gender]);
useEffect(() => {
loadProfile();
}, [user]);
// 切换分析类型时清空分析结果
useEffect(() => {
setAnalysisResult(null);
}, [analysisType]);
const loadProfile = async () => {
if (!user) return;
@@ -45,7 +63,7 @@ const AnalysisPage: React.FC = () => {
birth_time: data.birth_time || '',
gender: data.gender || 'male',
birth_place: data.birth_location || '',
question: ''
question: '财运'
});
}
} catch (error) {
@@ -113,11 +131,33 @@ const AnalysisPage: React.FC = () => {
throw new Error(data.error.message);
}
// 后端返回格式: { data: { record_id, analysis } }
// 后端返回格式: { data: { analysis } }
const analysisData = data.analysis;
setAnalysisResult({
type: analysisType,
data: data.analysis
data: analysisData
});
// 分析完成后,保存历史记录
try {
const inputData = analysisType === 'yijing' ?
{ question: formData.question, divination_method: 'time' } :
{
name: formData.name,
birth_date: formData.birth_date,
birth_time: formData.birth_time,
birth_place: formData.birth_place,
gender: formData.gender
};
await localApi.analysis.saveHistory(analysisType, analysisData, inputData);
console.log('历史记录保存成功');
} catch (historyError: any) {
console.error('保存历史记录失败:', historyError);
// 历史记录保存失败不影响分析结果显示
}
toast.success('分析完成!');
} catch (error: any) {
console.error('分析失败:', error);
@@ -250,7 +290,16 @@ const AnalysisPage: React.FC = () => {
type="date"
label="出生日期 *"
value={formData.birth_date}
onChange={(e) => setFormData(prev => ({ ...prev, birth_date: e.target.value }))}
onChange={(e) => {
const value = e.target.value;
// 验证日期格式YYYY-MM-DD确保年份是4位数字
if (value && !/^\d{4}-\d{2}-\d{2}$/.test(value)) {
return; // 不更新状态,保持原值
}
setFormData(prev => ({ ...prev, birth_date: value }));
}}
min="1900-01-01"
max="2100-12-31"
required
/>
<Calendar className="absolute right-3 top-8 h-4 w-4 text-gray-400 pointer-events-none" />
@@ -307,10 +356,7 @@ const AnalysisPage: React.FC = () => {
<AnalysisResultDisplay
analysisResult={analysisResult}
analysisType={analysisType}
birthDate={(analysisType === 'bazi' || analysisType === 'ziwei') ? {
date: formData.birth_date,
time: formData.birth_time
} : undefined}
birthDate={memoizedBirthDate}
question={analysisType === 'yijing' ? formData.question : undefined}
userId={user?.id}
divinationMethod="time"

View File

@@ -15,6 +15,29 @@ const HistoryPage: React.FC = () => {
const [selectedReading, setSelectedReading] = useState<NumerologyReading | null>(null);
const [viewingResult, setViewingResult] = useState(false);
// 安全地从input_data中获取值的辅助函数
const getInputDataValue = (inputData: string | any, key: string, defaultValue: any = null) => {
try {
if (!inputData) return defaultValue;
// 如果已经是对象,直接返回
if (typeof inputData === 'object') {
return inputData[key] || defaultValue;
}
// 如果是字符串尝试解析JSON
if (typeof inputData === 'string') {
const parsed = JSON.parse(inputData);
return parsed[key] || defaultValue;
}
return defaultValue;
} catch (error) {
console.warn('解析input_data失败:', error);
return defaultValue;
}
};
useEffect(() => {
loadHistory();
}, [user]);
@@ -144,6 +167,18 @@ const HistoryPage: React.FC = () => {
<AnalysisResultDisplay
analysisResult={selectedReading.analysis}
analysisType={selectedReading.reading_type as 'bazi' | 'ziwei' | 'yijing'}
birthDate={selectedReading.reading_type !== 'yijing' ? {
date: selectedReading.birth_date || '',
time: selectedReading.birth_time || '12:00',
name: selectedReading.name || '',
gender: selectedReading.gender || 'male'
} : undefined}
question={selectedReading.reading_type === 'yijing' ?
getInputDataValue(selectedReading.input_data, 'question', '综合运势如何?') : undefined}
userId={selectedReading.user_id?.toString()}
divinationMethod={selectedReading.reading_type === 'yijing' ?
getInputDataValue(selectedReading.input_data, 'divination_method', 'time') : undefined}
preAnalysisData={selectedReading.analysis}
/>
</div>
);
@@ -197,16 +232,20 @@ const HistoryPage: React.FC = () => {
</div>
<div>
<h3 className="font-medium text-gray-900">
{reading.name || '未知姓名'} {getAnalysisTypeName(reading.reading_type)}
{reading.name || '未知姓名'} - {getAnalysisTypeName(reading.reading_type)}
</h3>
<div className="flex items-center space-x-4 text-sm text-gray-600 mt-1">
<div className="flex items-center space-x-1">
<Calendar className="h-3 w-3" />
<span>{new Date(reading.created_at).toLocaleDateString('zh-CN')}</span>
<span>{new Date(reading.created_at).toLocaleString('zh-CN')}</span>
</div>
<div className="flex items-center space-x-1">
<User className="h-3 w-3" />
<span>{reading.birth_date}</span>
<span>
{reading.reading_type === 'yijing'
? `问题:${getInputDataValue(reading.input_data, 'question', '综合运势').substring(0, 20)}${getInputDataValue(reading.input_data, 'question', '').length > 20 ? '...' : ''}`
: reading.birth_date}
</span>
</div>
</div>
</div>

View File

@@ -38,7 +38,7 @@ export interface NumerologyReading {
birth_time?: string;
gender: string;
birth_place?: string;
input_data: any;
input_data: string | any; // JSON字符串或已解析的对象
analysis: {
bazi?: { bazi_analysis: any };
ziwei?: { ziwei_analysis: any };