本次提交引入了一系列重要功能,核心是实现了财务分析模块的动态配置,并对配置和报告页面的用户界面进行了改进。 主要变更: - **动态配置:** - 后端实现了 `ConfigManager` 服务,用于动态管理 `analysis-config.json` 和 `config.json`。 - 添加了用于读取和更新配置的 API 端点。 - 开发了前端 `/config` 页面,允许用户实时查看和修改分析配置。 - **后端增强:** - 更新了 `AnalysisClient` 和 `CompanyProfileClient` 以使用新的配置系统。 - 重构了财务数据相关的路由。 - **前端改进:** - 新增了可复用的 `Checkbox` UI 组件。 - 使用更直观和用户友好的界面重新设计了配置页面。 - 改进了财务报告页面的布局和数据展示。 - **文档与杂务:** - 更新了设计和需求文档以反映新功能。 - 更新了前后端依赖。 - 修改了开发脚本 `dev.sh`。
114 lines
3.5 KiB
TypeScript
114 lines
3.5 KiB
TypeScript
import useSWR from 'swr';
|
||
import { useConfigStore } from '@/stores/useConfigStore';
|
||
import { BatchFinancialDataResponse, FinancialConfigResponse, AnalysisConfigResponse } from '@/types';
|
||
|
||
const fetcher = async (url: string) => {
|
||
const res = await fetch(url);
|
||
const contentType = res.headers.get('Content-Type') || '';
|
||
const text = await res.text();
|
||
|
||
// 尝试解析JSON
|
||
const tryParseJson = () => {
|
||
try { return JSON.parse(text); } catch { return null; }
|
||
};
|
||
|
||
const data = contentType.includes('application/json') ? tryParseJson() : tryParseJson();
|
||
|
||
if (!res.ok) {
|
||
// 后端可能返回纯文本错误,统一抛出可读错误
|
||
const message = data && data.detail ? data.detail : (text || `Request failed: ${res.status}`);
|
||
throw new Error(message);
|
||
}
|
||
|
||
if (data === null) {
|
||
throw new Error('无效的服务器响应(非JSON)');
|
||
}
|
||
|
||
return data;
|
||
};
|
||
|
||
export function useConfig() {
|
||
const { setConfig, setError } = useConfigStore();
|
||
const { data, error, isLoading } = useSWR('/api/config', fetcher, {
|
||
onSuccess: (data) => setConfig(data),
|
||
onError: (err) => setError(err.message),
|
||
});
|
||
|
||
return { data, error, isLoading };
|
||
}
|
||
|
||
export async function updateConfig(newConfig: any) {
|
||
const res = await fetch('/api/config', {
|
||
method: 'PUT',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify(newConfig),
|
||
});
|
||
if (!res.ok) throw new Error(await res.text());
|
||
return res.json();
|
||
}
|
||
|
||
export async function testConfig(type: string, data: any) {
|
||
const res = await fetch('/api/config/test', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ config_type: type, config_data: data }),
|
||
});
|
||
if (!res.ok) throw new Error(await res.text());
|
||
return res.json();
|
||
}
|
||
|
||
export function useFinancialConfig() {
|
||
return useSWR<FinancialConfigResponse>('/api/financials/config', fetcher);
|
||
}
|
||
|
||
export function useChinaFinancials(ts_code?: string, years: number = 10) {
|
||
return useSWR<BatchFinancialDataResponse>(
|
||
ts_code ? `/api/financials/china/${encodeURIComponent(ts_code)}?years=${encodeURIComponent(String(years))}` : null,
|
||
fetcher,
|
||
{
|
||
revalidateOnFocus: false, // 不在窗口聚焦时重新验证
|
||
revalidateOnReconnect: false, // 不在网络重连时重新验证
|
||
dedupingInterval: 300000, // 5分钟内去重
|
||
errorRetryCount: 1, // 错误重试1次
|
||
}
|
||
);
|
||
}
|
||
|
||
export function useAnalysisConfig() {
|
||
return useSWR<AnalysisConfigResponse>('/api/financials/analysis-config', fetcher);
|
||
}
|
||
|
||
export async function updateAnalysisConfig(config: AnalysisConfigResponse) {
|
||
const res = await fetch('/api/financials/analysis-config', {
|
||
method: 'PUT',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify(config),
|
||
});
|
||
if (!res.ok) throw new Error(await res.text());
|
||
return res.json();
|
||
}
|
||
|
||
export async function generateFullAnalysis(tsCode: string, companyName: string) {
|
||
const url = `/api/financials/china/${encodeURIComponent(tsCode)}/analysis?company_name=${encodeURIComponent(companyName)}`;
|
||
const res = await fetch(url, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
});
|
||
|
||
const text = await res.text();
|
||
if (!res.ok) {
|
||
try {
|
||
const errorJson = JSON.parse(text);
|
||
throw new Error(errorJson.detail || text);
|
||
} catch {
|
||
throw new Error(text || `Request failed: ${res.status}`);
|
||
}
|
||
}
|
||
|
||
try {
|
||
return JSON.parse(text);
|
||
} catch {
|
||
throw new Error('Invalid JSON response from server.');
|
||
}
|
||
}
|