refactor: data analyze.
This commit is contained in:
@ -1,6 +1,7 @@
|
|||||||
package controllers
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
"gorm.io/gorm/clause"
|
"gorm.io/gorm/clause"
|
||||||
@ -57,13 +58,10 @@ func (tc *TrainingController) CreateTrainingRecord(c *gin.Context) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
err := tc.heartRateAnalyze(tx, record)
|
||||||
//// 保存腰带关联关系
|
if err != nil {
|
||||||
//if len(record.Belts) > 0 {
|
return err
|
||||||
// if err := tx.Model(&record).Association("Belts").Replace(record.Belts); err != nil {
|
}
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
@ -79,6 +77,45 @@ func (tc *TrainingController) CreateTrainingRecord(c *gin.Context) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tc *TrainingController) heartRateAnalyze(tx *gorm.DB, record models.TrainRecord) error {
|
||||||
|
startTime := record.StartTime
|
||||||
|
|
||||||
|
// 获取所有唯一的beltID
|
||||||
|
var beltIDs []uint
|
||||||
|
tx.Model(&models.HeartRate{}).Where("train_id = ?", record.TrainId).
|
||||||
|
Select("DISTINCT belt_id").Pluck("belt_id", &beltIDs)
|
||||||
|
|
||||||
|
// 对每个belt计算
|
||||||
|
for _, bid := range beltIDs {
|
||||||
|
// 计算平均心率
|
||||||
|
ranges := getTimeRanges(startTime)
|
||||||
|
averages, err := calculateAverages(tx, record.TrainId, bid, ranges)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 曲线拟合
|
||||||
|
x := []float64{2, 4, 6}
|
||||||
|
y := []float64{averages["2min"], averages["4min"], averages["6min"]}
|
||||||
|
a, _ := quadraticFit(x, y)
|
||||||
|
|
||||||
|
// 存储结果
|
||||||
|
analysis := models.BeltAnalysis{
|
||||||
|
TrainID: record.TrainId,
|
||||||
|
RunType: record.RunType,
|
||||||
|
BeltID: bid,
|
||||||
|
Avg2min: averages["2min"],
|
||||||
|
Avg4min: averages["4min"],
|
||||||
|
Avg6min: averages["6min"],
|
||||||
|
CurveParamA: a,
|
||||||
|
}
|
||||||
|
if err := tx.Create(&analysis).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func ReceiveTrainingData(c *gin.Context) {
|
func ReceiveTrainingData(c *gin.Context) {
|
||||||
var data models.TrainingData
|
var data models.TrainingData
|
||||||
|
|
||||||
@ -101,3 +138,48 @@ func ReceiveTrainingData(c *gin.Context) {
|
|||||||
"id": data.ID,
|
"id": data.ID,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func calculateAverages(tx *gorm.DB, trainID uint, beltID uint, ranges map[string]TimeRange) (map[string]float64, error) {
|
||||||
|
averages := make(map[string]float64)
|
||||||
|
for key, tr := range ranges {
|
||||||
|
var avg float64
|
||||||
|
// 使用GORM Raw SQL提高效率[6,10](@ref)
|
||||||
|
err := tx.Raw(`
|
||||||
|
SELECT AVG(value)
|
||||||
|
FROM heart_rates
|
||||||
|
WHERE train_id = ?
|
||||||
|
AND belt_id = ?
|
||||||
|
AND time BETWEEN ? AND ?`,
|
||||||
|
trainID, beltID, tr.Start, tr.End,
|
||||||
|
).Scan(&avg).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
averages[key] = avg
|
||||||
|
}
|
||||||
|
return averages, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func quadraticFit(x []float64, y []float64) (float64, error) {
|
||||||
|
// 使用三点计算y=ax²+b的a值(x=[2,4,6]对应分钟)
|
||||||
|
if len(x) != 3 || len(y) != 3 {
|
||||||
|
return 0, errors.New("需要三个点")
|
||||||
|
}
|
||||||
|
// 构造方程组矩阵(简化计算)
|
||||||
|
a := (y[2] - 2*y[1] + y[0]) / (x[2]*x[2] - 2*x[1]*x[1] + x[0]*x[0])
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type TimeRange struct {
|
||||||
|
Start int64 // 毫秒时间戳起点
|
||||||
|
End int64 // 毫秒时间戳终点
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTimeRanges(startTime int64) map[string]TimeRange {
|
||||||
|
// 计算相对于训练开始时间的窗口
|
||||||
|
return map[string]TimeRange{
|
||||||
|
"2min": {Start: startTime + 120000, End: startTime + 240000}, // 第2分钟(120-240秒)
|
||||||
|
"4min": {Start: startTime + 240000, End: startTime + 360000},
|
||||||
|
"6min": {Start: startTime + 360000, End: startTime + 480000},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
2
main.go
2
main.go
@ -15,7 +15,7 @@ func main() {
|
|||||||
|
|
||||||
config.DB.Debug()
|
config.DB.Debug()
|
||||||
// 自动迁移模型
|
// 自动迁移模型
|
||||||
config.DB.AutoMigrate(&models.TrainRecord{}, &models.TrainingData{}, &models.Belt{}, &models.HeartRate{})
|
config.DB.AutoMigrate(&models.TrainRecord{}, &models.TrainingData{}, &models.Belt{}, &models.HeartRate{}, &models.BeltAnalysis{})
|
||||||
|
|
||||||
// 启动服务
|
// 启动服务
|
||||||
r := routes.SetupRouter()
|
r := routes.SetupRouter()
|
||||||
|
|||||||
@ -18,6 +18,20 @@ type Belt struct {
|
|||||||
Name string `gorm:"size:100" json:"name"`
|
Name string `gorm:"size:100" json:"name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 分析结果存储实体
|
||||||
|
type BeltAnalysis struct {
|
||||||
|
gorm.Model
|
||||||
|
TrainID uint `gorm:"index;not null"` // 关联训练记录
|
||||||
|
BeltID uint `gorm:"index;not null"` // 腰带唯一标识
|
||||||
|
RunType string `gorm:"size:100" json:"RunType"`
|
||||||
|
Avg2min float64 `gorm:"type:double precision"` // 第2分钟平均心率
|
||||||
|
Avg4min float64 `gorm:"type:double precision"` // 第4分钟平均心率
|
||||||
|
Avg6min float64 `gorm:"type:double precision"` // 第6分钟平均心率
|
||||||
|
CurveParamA float64 `gorm:"type:double precision"` // 拟合参数a值
|
||||||
|
}
|
||||||
|
|
||||||
|
// 中间计算结构(无需持久化)
|
||||||
|
|
||||||
// 对应Flutter的HeartRate结构
|
// 对应Flutter的HeartRate结构
|
||||||
type HeartRate struct {
|
type HeartRate struct {
|
||||||
gorm.Model
|
gorm.Model
|
||||||
@ -37,6 +51,7 @@ type TrainRecord struct {
|
|||||||
StartTime int64 `gorm:"type:bigint" json:"time"` // 开始时间戳
|
StartTime int64 `gorm:"type:bigint" json:"time"` // 开始时间戳
|
||||||
EndTime int64 `gorm:"type:bigint" json:"endTime"` // 结束时间戳[3](@ref)
|
EndTime int64 `gorm:"type:bigint" json:"endTime"` // 结束时间戳[3](@ref)
|
||||||
Name string `gorm:"size:100" json:"name"`
|
Name string `gorm:"size:100" json:"name"`
|
||||||
|
RunType string `gorm:"size:100" json:"RunType"`
|
||||||
MaxHeartRate int `gorm:"type:int" json:"maxHeartRate"`
|
MaxHeartRate int `gorm:"type:int" json:"maxHeartRate"`
|
||||||
Duration int `gorm:"type:int" json:"duration"` // 持续时间(秒)
|
Duration int `gorm:"type:int" json:"duration"` // 持续时间(秒)
|
||||||
PeopleNum int `gorm:"type:int" json:"peopleNum"`
|
PeopleNum int `gorm:"type:int" json:"peopleNum"`
|
||||||
|
|||||||
Reference in New Issue
Block a user