398 lines
12 KiB
JavaScript
398 lines
12 KiB
JavaScript
/**
|
||
* 宠物体重管理工具类
|
||
* 负责体重数据的存储、分析和AI建议生成
|
||
*/
|
||
|
||
class WeightManager {
|
||
constructor() {
|
||
this.storageKey = 'pet_weight_records'
|
||
}
|
||
|
||
/**
|
||
* 获取宠物的体重记录
|
||
* @param {string} petId 宠物ID
|
||
* @returns {Array} 体重记录数组
|
||
*/
|
||
getWeightRecords(petId) {
|
||
try {
|
||
const allRecords = uni.getStorageSync(this.storageKey) || {}
|
||
let records = allRecords[petId] || []
|
||
|
||
// 如果没有数据,初始化一些测试数据
|
||
if (records.length === 0) {
|
||
records = this.initializeTestData(petId)
|
||
allRecords[petId] = records
|
||
uni.setStorageSync(this.storageKey, allRecords)
|
||
}
|
||
|
||
return records
|
||
} catch (error) {
|
||
console.error('获取体重记录失败:', error)
|
||
return this.initializeTestData(petId)
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 初始化测试数据
|
||
* @param {string} petId 宠物ID
|
||
* @returns {Array} 测试数据数组
|
||
*/
|
||
initializeTestData(petId) {
|
||
const now = new Date()
|
||
const testData = []
|
||
|
||
// 生成过去3个月的体重数据,模拟真实的体重变化
|
||
const baseWeight = 3.8 // 基础体重
|
||
const growthRate = 0.015 // 每周增长率
|
||
|
||
for (let i = 90; i >= 0; i -= 3) { // 每3天一个记录点
|
||
const date = new Date(now.getTime() - i * 24 * 60 * 60 * 1000)
|
||
const weeksPassed = (90 - i) / 7
|
||
|
||
// 模拟自然的体重增长,加入一些随机波动
|
||
let weight = baseWeight + (weeksPassed * growthRate * baseWeight)
|
||
weight += (Math.random() - 0.5) * 0.1 // 添加±0.05kg的随机波动
|
||
weight = Math.round(weight * 10) / 10 // 保留一位小数
|
||
|
||
testData.push({
|
||
id: Date.now() + i,
|
||
weight: weight,
|
||
date: date.toISOString(),
|
||
note: i === 0 ? '最新记录' : '自动生成',
|
||
timestamp: date.getTime()
|
||
})
|
||
}
|
||
|
||
return testData
|
||
}
|
||
|
||
/**
|
||
* 添加体重记录
|
||
* @param {string} petId 宠物ID
|
||
* @param {Object} record 体重记录
|
||
*/
|
||
addWeightRecord(petId, record) {
|
||
try {
|
||
const allRecords = uni.getStorageSync(this.storageKey) || {}
|
||
if (!allRecords[petId]) {
|
||
allRecords[petId] = []
|
||
}
|
||
|
||
const newRecord = {
|
||
id: Date.now(),
|
||
weight: record.weight,
|
||
date: record.date || new Date().toISOString(),
|
||
note: record.note || '',
|
||
timestamp: Date.now()
|
||
}
|
||
|
||
allRecords[petId].push(newRecord)
|
||
allRecords[petId].sort((a, b) => new Date(a.date) - new Date(b.date))
|
||
|
||
uni.setStorageSync(this.storageKey, allRecords)
|
||
return newRecord
|
||
} catch (error) {
|
||
console.error('添加体重记录失败:', error)
|
||
return null
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取当前体重
|
||
* @param {string} petId 宠物ID
|
||
* @returns {number} 当前体重
|
||
*/
|
||
getCurrentWeight(petId) {
|
||
const records = this.getWeightRecords(petId)
|
||
if (records.length === 0) return 0
|
||
return records[records.length - 1].weight
|
||
}
|
||
|
||
/**
|
||
* 计算体重变化
|
||
* @param {string} petId 宠物ID
|
||
* @param {number} days 对比天数
|
||
* @returns {Object} 变化数据
|
||
*/
|
||
calculateWeightChange(petId, days) {
|
||
const records = this.getWeightRecords(petId)
|
||
if (records.length < 2) {
|
||
return {
|
||
change: 0,
|
||
percent: 0,
|
||
trend: 'stable'
|
||
}
|
||
}
|
||
|
||
const now = new Date()
|
||
const compareDate = new Date(now.getTime() - days * 24 * 60 * 60 * 1000)
|
||
|
||
const currentWeight = this.getCurrentWeight(petId)
|
||
const compareRecord = records.find(record =>
|
||
new Date(record.date) >= compareDate
|
||
) || records[0]
|
||
|
||
const change = currentWeight - compareRecord.weight
|
||
const percent = compareRecord.weight > 0 ? (change / compareRecord.weight) * 100 : 0
|
||
|
||
let trend = 'stable'
|
||
if (Math.abs(percent) > 1) {
|
||
trend = change > 0 ? 'increase' : 'decrease'
|
||
}
|
||
|
||
return {
|
||
change: Number(change.toFixed(1)),
|
||
percent: Number(percent.toFixed(1)),
|
||
trend: trend,
|
||
previousWeight: compareRecord.weight
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 生成图表数据
|
||
* @param {string} petId 宠物ID
|
||
* @param {string} timeRange 时间范围
|
||
* @returns {Object} 图表数据
|
||
*/
|
||
generateChartData(petId, timeRange) {
|
||
const records = this.getWeightRecords(petId)
|
||
if (records.length === 0) {
|
||
return {
|
||
categories: [],
|
||
series: [{ name: "体重", data: [] }]
|
||
}
|
||
}
|
||
|
||
let filteredRecords = []
|
||
const now = new Date()
|
||
|
||
switch (timeRange) {
|
||
case 'week':
|
||
const weekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000)
|
||
filteredRecords = records.filter(record => new Date(record.date) >= weekAgo)
|
||
break
|
||
case 'month':
|
||
const monthAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000)
|
||
filteredRecords = records.filter(record => new Date(record.date) >= monthAgo)
|
||
break
|
||
case 'year':
|
||
const yearAgo = new Date(now.getTime() - 365 * 24 * 60 * 60 * 1000)
|
||
filteredRecords = records.filter(record => new Date(record.date) >= yearAgo)
|
||
break
|
||
default:
|
||
filteredRecords = records
|
||
}
|
||
|
||
// 如果数据点太少,补充一些模拟数据
|
||
if (filteredRecords.length === 0) {
|
||
filteredRecords = this.generateMockData(timeRange)
|
||
}
|
||
|
||
const categories = filteredRecords.map(record => {
|
||
const date = new Date(record.date)
|
||
if (timeRange === 'year' || timeRange === 'all') {
|
||
return `${date.getMonth() + 1}月`
|
||
} else {
|
||
return `${date.getMonth() + 1}/${date.getDate()}`
|
||
}
|
||
})
|
||
|
||
const data = filteredRecords.map(record => record.weight)
|
||
|
||
return {
|
||
categories: categories,
|
||
series: [{ name: "体重", data: data }]
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 生成模拟数据(用于演示)
|
||
* @param {string} timeRange 时间范围
|
||
* @returns {Array} 模拟数据
|
||
*/
|
||
generateMockData(timeRange) {
|
||
const now = new Date()
|
||
const mockData = []
|
||
const currentWeight = 4.2
|
||
|
||
switch (timeRange) {
|
||
case 'week':
|
||
// 近一周数据:每天一个点
|
||
for (let i = 6; i >= 0; i--) {
|
||
const date = new Date(now.getTime() - i * 24 * 60 * 60 * 1000)
|
||
const weight = currentWeight - (i * 0.02) + (Math.random() - 0.5) * 0.05
|
||
mockData.push({
|
||
weight: Math.round(weight * 10) / 10,
|
||
date: date.toISOString()
|
||
})
|
||
}
|
||
break
|
||
case 'month':
|
||
// 近一月数据:每周一个点
|
||
for (let i = 4; i >= 0; i--) {
|
||
const date = new Date(now.getTime() - i * 7 * 24 * 60 * 60 * 1000)
|
||
const weight = currentWeight - (i * 0.08) + (Math.random() - 0.5) * 0.1
|
||
mockData.push({
|
||
weight: Math.round(weight * 10) / 10,
|
||
date: date.toISOString()
|
||
})
|
||
}
|
||
break
|
||
case 'year':
|
||
// 近一年数据:每两个月一个点
|
||
for (let i = 5; i >= 0; i--) {
|
||
const date = new Date(now.getTime() - i * 60 * 24 * 60 * 60 * 1000)
|
||
const weight = currentWeight - (i * 0.2) + (Math.random() - 0.5) * 0.15
|
||
mockData.push({
|
||
weight: Math.round(weight * 10) / 10,
|
||
date: date.toISOString()
|
||
})
|
||
}
|
||
break
|
||
default:
|
||
// 全部历史:从小猫到现在
|
||
const milestones = [
|
||
{ months: 18, weight: 1.2 },
|
||
{ months: 15, weight: 1.8 },
|
||
{ months: 12, weight: 2.5 },
|
||
{ months: 9, weight: 3.0 },
|
||
{ months: 6, weight: 3.4 },
|
||
{ months: 3, weight: 3.8 },
|
||
{ months: 0, weight: 4.2 }
|
||
]
|
||
|
||
milestones.forEach(milestone => {
|
||
const date = new Date(now.getTime() - milestone.months * 30 * 24 * 60 * 60 * 1000)
|
||
mockData.push({
|
||
weight: milestone.weight,
|
||
date: date.toISOString()
|
||
})
|
||
})
|
||
}
|
||
|
||
return mockData.sort((a, b) => new Date(a.date) - new Date(b.date))
|
||
}
|
||
|
||
/**
|
||
* 生成AI健康分析
|
||
* @param {string} petId 宠物ID
|
||
* @param {Object} petInfo 宠物信息
|
||
* @returns {Object} AI分析结果
|
||
*/
|
||
generateAIAnalysis(petId, petInfo) {
|
||
const currentWeight = this.getCurrentWeight(petId)
|
||
const weeklyChange = this.calculateWeightChange(petId, 7)
|
||
const monthlyChange = this.calculateWeightChange(petId, 30)
|
||
|
||
// 根据品种、年龄、性别判断健康范围
|
||
const healthRange = this.getHealthyWeightRange(petInfo)
|
||
const isHealthy = currentWeight >= healthRange.min && currentWeight <= healthRange.max
|
||
|
||
// 健康评估
|
||
let healthAssessment = ''
|
||
if (isHealthy) {
|
||
healthAssessment = `根据${petInfo.name}的品种(${petInfo.breed})、年龄(${petInfo.age}岁)和性别(${petInfo.gender}),当前体重${currentWeight}kg处于健康范围内(${healthRange.min}-${healthRange.max}kg)。`
|
||
} else if (currentWeight < healthRange.min) {
|
||
healthAssessment = `当前体重${currentWeight}kg低于健康范围(${healthRange.min}-${healthRange.max}kg),建议增加营养摄入。`
|
||
} else {
|
||
healthAssessment = `当前体重${currentWeight}kg超出健康范围(${healthRange.min}-${healthRange.max}kg),建议控制饮食并增加运动。`
|
||
}
|
||
|
||
// 趋势分析
|
||
let trendAnalysis = ''
|
||
if (weeklyChange.trend === 'stable') {
|
||
trendAnalysis = '近期体重保持稳定,这是一个良好的状态。'
|
||
} else if (weeklyChange.trend === 'increase') {
|
||
if (weeklyChange.percent > 5) {
|
||
trendAnalysis = `近期体重上升较快(${weeklyChange.percent}%),需要关注饮食控制。`
|
||
} else {
|
||
trendAnalysis = `近期体重呈适度上升趋势(${weeklyChange.percent}%),增长速度适中。`
|
||
}
|
||
} else {
|
||
if (weeklyChange.percent < -5) {
|
||
trendAnalysis = `近期体重下降较快(${weeklyChange.percent}%),建议检查健康状况。`
|
||
} else {
|
||
trendAnalysis = `近期体重呈下降趋势(${weeklyChange.percent}%),请注意营养补充。`
|
||
}
|
||
}
|
||
|
||
// 建议
|
||
let recommendations = this.generateRecommendations(currentWeight, healthRange, weeklyChange)
|
||
|
||
// 医疗建议
|
||
let medicalAdvice = ''
|
||
if (Math.abs(weeklyChange.percent) > 10 || !isHealthy) {
|
||
medicalAdvice = '建议咨询兽医,进行专业的健康检查和营养指导。'
|
||
}
|
||
|
||
return {
|
||
healthAssessment,
|
||
trendAnalysis,
|
||
recommendations,
|
||
medicalAdvice
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取健康体重范围
|
||
* @param {Object} petInfo 宠物信息
|
||
* @returns {Object} 健康体重范围
|
||
*/
|
||
getHealthyWeightRange(petInfo) {
|
||
// 简化的品种体重范围映射
|
||
const breedRanges = {
|
||
'橘猫': { min: 3.5, max: 5.5 },
|
||
'英短': { min: 3.0, max: 5.0 },
|
||
'美短': { min: 3.5, max: 5.5 },
|
||
'布偶': { min: 4.0, max: 7.0 },
|
||
'波斯': { min: 3.0, max: 5.5 },
|
||
'暹罗': { min: 2.5, max: 4.5 }
|
||
}
|
||
|
||
const baseRange = breedRanges[petInfo.breed] || { min: 3.0, max: 6.0 }
|
||
|
||
// 根据性别调整(公猫通常比母猫重一些)
|
||
if (petInfo.gender === '公') {
|
||
baseRange.min += 0.5
|
||
baseRange.max += 0.5
|
||
}
|
||
|
||
return baseRange
|
||
}
|
||
|
||
/**
|
||
* 生成个性化建议
|
||
* @param {number} currentWeight 当前体重
|
||
* @param {Object} healthRange 健康范围
|
||
* @param {Object} weeklyChange 周变化
|
||
* @returns {string} 建议内容
|
||
*/
|
||
generateRecommendations(currentWeight, healthRange, weeklyChange) {
|
||
let recommendations = []
|
||
|
||
if (currentWeight < healthRange.min) {
|
||
recommendations.push('增加高质量蛋白质摄入,如优质猫粮、煮熟的鸡胸肉')
|
||
recommendations.push('少量多餐,每日3-4次定时喂食')
|
||
recommendations.push('确保充足的饮水')
|
||
} else if (currentWeight > healthRange.max) {
|
||
recommendations.push('控制食物分量,减少高热量零食')
|
||
recommendations.push('增加运动量,每日逗猫棒游戏20-30分钟')
|
||
recommendations.push('选择低脂肪、高纤维的减肥猫粮')
|
||
} else {
|
||
recommendations.push('保持当前的饮食习惯,每日定时定量喂食')
|
||
recommendations.push('适量运动,如逗猫棒游戏15-20分钟')
|
||
recommendations.push('定期监测体重变化')
|
||
}
|
||
|
||
if (weeklyChange.trend === 'increase' && weeklyChange.percent > 3) {
|
||
recommendations.push('近期体重增长较快,建议减少零食摄入')
|
||
} else if (weeklyChange.trend === 'decrease' && weeklyChange.percent < -3) {
|
||
recommendations.push('近期体重下降,注意观察食欲和精神状态')
|
||
}
|
||
|
||
return recommendations.join(';') + '。'
|
||
}
|
||
}
|
||
|
||
export default new WeightManager()
|