Files
smart-cropx-ui/src/app/(app)/ai-crop-model/support/dashboard/components/aiDecisionDashboardReducer.tsx
peng 68d9d97142 refactor: 优化认证系统和组件类型安全性
- 新增 safeLocalStorage 工具函数增强本地存储安全性
- 简化认证流程,重构用户数据结构和 token 管理
- 修复多个模块的 TypeScript 类型错误和导入问题
- 优化状态管理,重构各模块 store 结构
- 清理冗余代码,移除未使用的组件和函数
- 改进错误处理和边界情况处理
- 更新配置文件以支持最新的类型检查

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-13 14:52:52 +08:00

444 lines
12 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* filekorolheader: AI决策看板状态管理 - 决策数据状态管理核心
* 功能:决策数据管理、统计计算、状态更新、本地存储同步
* 路径:/ai-crop-model/support/dashboard/components/aiDecisionDashboardReducer
* 规范遵循crop-x/docs/开发项目规范.md使用useReducer状态管理模式
*/
import { safeLocalStorage } from '@/utils/storage';
// 决策类型
export type DecisionType = 'irrigation' | 'fertilizer' | 'pesticide' | 'harvest' | 'soil' | 'weather';
// 决策状态
export type DecisionStatus = 'generated' | 'executing' | 'completed' | 'expired';
// 决策优先级
export type DecisionPriority = 'urgent' | 'high' | 'medium' | 'low';
// 决策记录
export interface DecisionRecord {
id: string;
type: DecisionType;
title: string;
description: string;
status: DecisionStatus;
priority: DecisionPriority;
fieldId: string;
fieldName: string;
fieldArea: number;
cropType: string;
confidence: number;
createdAt: string;
updatedAt: string;
dueDate: string;
location: {
lat: number;
lng: number;
};
modelVersion?: string;
ruleCount?: number;
executedAt?: string;
executedBy?: string;
completedAt?: string;
}
// 地块决策信息
export interface FieldDecisionInfo {
fieldId: string;
fieldName: string;
location: {
lat: number;
lng: number;
};
area: number;
cropType: string;
decisions: DecisionRecord[];
urgentCount: number;
generatedCount: number;
executingCount: number;
completedCount: number;
}
// 统计数据
export interface DecisionStats {
total: number;
generated: number;
executing: number;
completed: number;
urgent: number;
avgConfidence: number;
}
// 趋势数据
export interface TrendData {
date: string;
generated: number;
completed: number;
}
// 状态接口
export interface AIDecisionDashboardState {
decisions: DecisionRecord[];
fieldDecisions: FieldDecisionInfo[];
stats: DecisionStats;
trendData: TrendData[];
latestDecisions: DecisionRecord[];
lastUpdated: string;
}
// Action类型
export type AIDecisionDashboardAction =
| { type: 'SET_DECISIONS'; payload: DecisionRecord[] }
| { type: 'ADD_DECISION'; payload: DecisionRecord }
| { type: 'UPDATE_DECISION'; payload: { id: string; updates: Partial<DecisionRecord> } }
| { type: 'DELETE_DECISION'; payload: string }
| { type: 'REFRESH_DATA' }
| { type: 'LOAD_FROM_STORAGE'; payload: Partial<AIDecisionDashboardState> };
// 初始决策数据
const initialDecisions: DecisionRecord[] = [
{
id: 'dec_001',
type: 'irrigation',
title: '1号大棚番茄开花期灌溉',
description: '土壤湿度35%低于最佳湿度建议立即灌溉120升',
status: 'generated',
priority: 'urgent',
fieldId: 'field_001',
fieldName: '1号大棚',
fieldArea: 2.5,
cropType: '番茄',
confidence: 0.89,
createdAt: '2024-10-23 14:30',
updatedAt: '2024-10-23 14:30',
dueDate: '2024-10-23 18:00',
location: { lat: 39.9042, lng: 116.4074 },
modelVersion: 'v2.1.3',
ruleCount: 2,
},
{
id: 'dec_002',
type: 'pesticide',
title: '2号大棚早疫病防治',
description: '检测到早疫病轻度感染,建议使用生物防治剂',
status: 'executing',
priority: 'high',
fieldId: 'field_002',
fieldName: '2号大棚',
fieldArea: 3.0,
cropType: '番茄',
confidence: 0.87,
createdAt: '2024-10-23 10:15',
updatedAt: '2024-10-23 11:00',
dueDate: '2024-10-23 17:00',
executedAt: '2024-10-23 11:00',
executedBy: '王五',
location: { lat: 39.9142, lng: 116.4174 },
modelVersion: 'v3.2.1',
ruleCount: 1,
},
{
id: 'dec_003',
type: 'fertilizer',
title: '3号地块小麦追肥',
description: '结果期营养需求增加,建议施用复合肥',
status: 'executing',
priority: 'medium',
fieldId: 'field_003',
fieldName: '3号地块',
fieldArea: 5.0,
cropType: '小麦',
confidence: 0.92,
createdAt: '2024-10-23 09:30',
updatedAt: '2024-10-23 10:00',
dueDate: '2024-10-24 12:00',
executedAt: '2024-10-23 10:00',
executedBy: '李四',
location: { lat: 39.8942, lng: 116.3974 },
modelVersion: 'v2.0.5',
ruleCount: 3,
},
{
id: 'dec_004',
type: 'soil',
title: '4号地块土壤改良',
description: 'pH值偏低建议施用石灰调节土壤酸碱度',
status: 'completed',
priority: 'low',
fieldId: 'field_004',
fieldName: '4号地块',
fieldArea: 4.2,
cropType: '玉米',
confidence: 0.85,
createdAt: '2024-10-22 15:00',
updatedAt: '2024-10-22 16:30',
dueDate: '2024-10-25 12:00',
executedAt: '2024-10-22 15:30',
executedBy: '张三',
completedAt: '2024-10-22 16:30',
location: { lat: 39.8842, lng: 116.3874 },
modelVersion: 'v1.8.2',
ruleCount: 1,
},
{
id: 'dec_005',
type: 'weather',
title: '5号大棚温度调控',
description: '预计晚间温度降至12℃建议提前加温',
status: 'generated',
priority: 'high',
fieldId: 'field_005',
fieldName: '5号大棚',
fieldArea: 2.8,
cropType: '黄瓜',
confidence: 0.91,
createdAt: '2024-10-23 16:00',
updatedAt: '2024-10-23 16:00',
dueDate: '2024-10-23 20:00',
location: { lat: 39.9242, lng: 116.4274 },
modelVersion: 'v2.3.1',
ruleCount: 2,
},
{
id: 'dec_006',
type: 'harvest',
title: '6号地块水稻收获',
description: '水稻已达成熟期建议3天内完成收获',
status: 'generated',
priority: 'urgent',
fieldId: 'field_006',
fieldName: '6号地块',
fieldArea: 8.0,
cropType: '水稻',
confidence: 0.94,
createdAt: '2024-10-23 08:00',
updatedAt: '2024-10-23 08:00',
dueDate: '2024-10-26 18:00',
location: { lat: 39.9342, lng: 116.3874 },
modelVersion: 'v2.5.0',
ruleCount: 3,
},
{
id: 'dec_007',
type: 'irrigation',
title: '7号大棚茄子补水',
description: '连续3天无降雨土壤湿度偏低',
status: 'completed',
priority: 'medium',
fieldId: 'field_007',
fieldName: '7号大棚',
fieldArea: 2.2,
cropType: '茄子',
confidence: 0.86,
createdAt: '2024-10-22 18:00',
updatedAt: '2024-10-23 09:00',
dueDate: '2024-10-23 12:00',
executedAt: '2024-10-22 19:00',
executedBy: '赵六',
completedAt: '2024-10-23 09:00',
location: { lat: 39.8742, lng: 116.4174 },
modelVersion: 'v2.1.3',
ruleCount: 2,
},
];
// 初始趋势数据
const initialTrendData: TrendData[] = [
{ date: '10-17', generated: 8, completed: 5 },
{ date: '10-18', generated: 12, completed: 8 },
{ date: '10-19', generated: 10, completed: 9 },
{ date: '10-20', generated: 15, completed: 11 },
{ date: '10-21', generated: 13, completed: 10 },
{ date: '10-22', generated: 11, completed: 9 },
{ date: '10-23', generated: 14, completed: 7 },
];
// 计算统计数据
const calculateStats = (decisions: DecisionRecord[]): DecisionStats => {
return {
total: decisions.length,
generated: decisions.filter(d => d.status === 'generated').length,
executing: decisions.filter(d => d.status === 'executing').length,
completed: decisions.filter(d => d.status === 'completed').length,
urgent: decisions.filter(d => d.priority === 'urgent').length,
avgConfidence: decisions.reduce((sum, d) => sum + d.confidence, 0) / decisions.length,
};
};
// 计算地块决策信息
const calculateFieldDecisions = (decisions: DecisionRecord[]): FieldDecisionInfo[] => {
const fieldDecisionMap = new Map<string, FieldDecisionInfo>();
decisions.forEach(decision => {
if (!fieldDecisionMap.has(decision.fieldId)) {
fieldDecisionMap.set(decision.fieldId, {
fieldId: decision.fieldId,
fieldName: decision.fieldName,
location: decision.location,
area: decision.fieldArea,
cropType: decision.cropType,
decisions: [],
urgentCount: 0,
generatedCount: 0,
executingCount: 0,
completedCount: 0,
});
}
const fieldInfo = fieldDecisionMap.get(decision.fieldId)!;
fieldInfo.decisions.push(decision);
if (decision.priority === 'urgent') fieldInfo.urgentCount++;
if (decision.status === 'generated') fieldInfo.generatedCount++;
if (decision.status === 'executing') fieldInfo.executingCount++;
if (decision.status === 'completed') fieldInfo.completedCount++;
});
return Array.from(fieldDecisionMap.values());
};
// 计算最新决策
const calculateLatestDecisions = (decisions: DecisionRecord[]): DecisionRecord[] => {
return [...decisions]
.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime())
.slice(0, 5);
};
// 保存到本地存储
const saveToStorage = (state: AIDecisionDashboardState) => {
safeLocalStorage.setItem('ai-decision-dashboard', JSON.stringify({
decisions: state.decisions,
lastUpdated: state.lastUpdated,
}));
};
// 从本地存储加载
const loadFromStorage = () => {
const stored = safeLocalStorage.getItem('ai-decision-dashboard');
if (stored) {
try {
const data = JSON.parse(stored);
return {
decisions: data.decisions || initialDecisions,
lastUpdated: data.lastUpdated || new Date().toISOString(),
};
} catch (error) {
console.warn('Failed to parse stored data:', error);
}
}
return null;
};
// 计算派生状态
const calculateDerivedState = (decisions: DecisionRecord[]) => {
const stats = calculateStats(decisions);
const fieldDecisions = calculateFieldDecisions(decisions);
const latestDecisions = calculateLatestDecisions(decisions);
return {
stats,
fieldDecisions,
latestDecisions,
trendData: initialTrendData,
};
};
// 初始状态
export const initialState: AIDecisionDashboardState = (() => {
const stored = loadFromStorage();
const decisions = stored?.decisions || initialDecisions;
const derivedState = calculateDerivedState(decisions);
return {
decisions,
...derivedState,
lastUpdated: stored?.lastUpdated || new Date().toISOString(),
};
})();
// Reducer
export function AIDecisionDashboardReducer(
state: AIDecisionDashboardState,
action: AIDecisionDashboardAction
): AIDecisionDashboardState {
switch (action.type) {
case 'SET_DECISIONS': {
const derivedState = calculateDerivedState(action.payload);
const newState = {
...state,
decisions: action.payload,
...derivedState,
lastUpdated: new Date().toISOString(),
};
saveToStorage(newState);
return newState;
}
case 'ADD_DECISION': {
const newDecisions = [...state.decisions, action.payload];
const derivedState = calculateDerivedState(newDecisions);
const newState = {
...state,
decisions: newDecisions,
...derivedState,
lastUpdated: new Date().toISOString(),
};
saveToStorage(newState);
return newState;
}
case 'UPDATE_DECISION': {
const newDecisions = state.decisions.map(decision =>
decision.id === action.payload.id
? { ...decision, ...action.payload.updates }
: decision
);
const derivedState = calculateDerivedState(newDecisions);
const newState = {
...state,
decisions: newDecisions,
...derivedState,
lastUpdated: new Date().toISOString(),
};
saveToStorage(newState);
return newState;
}
case 'DELETE_DECISION': {
const newDecisions = state.decisions.filter(decision => decision.id !== action.payload);
const derivedState = calculateDerivedState(newDecisions);
const newState = {
...state,
decisions: newDecisions,
...derivedState,
lastUpdated: new Date().toISOString(),
};
saveToStorage(newState);
return newState;
}
case 'REFRESH_DATA': {
const derivedState = calculateDerivedState(state.decisions);
const newState = {
...state,
...derivedState,
lastUpdated: new Date().toISOString(),
};
saveToStorage(newState);
return newState;
}
case 'LOAD_FROM_STORAGE': {
const decisions = action.payload.decisions || state.decisions;
const derivedState = calculateDerivedState(decisions);
return {
...state,
decisions,
...derivedState,
lastUpdated: action.payload.lastUpdated || state.lastUpdated,
};
}
default:
return state;
}
}