pet/utils/weightManager.js

398 lines
12 KiB
JavaScript
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.

/**
* 宠物体重管理工具类
* 负责体重数据的存储、分析和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()