healthapp
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

8.3 KiB

02-数据库和模型设计

目标

实现数据库连接模块和所有数据模型定义,支持多数据库切换。


前置要求

  • 项目结构已初始化
  • 依赖已安装

实施步骤

步骤 1:创建配置加载模块

创建 server/internal/config/config.go

package config

import (
	"github.com/spf13/viper"
)

type Config struct {
	Server   ServerConfig
	Database DatabaseConfig
	JWT      JWTConfig
	AI       AIConfig
}

type ServerConfig struct {
	Port int
	Mode string
}

type DatabaseConfig struct {
	Driver   string
	SQLite   SQLiteConfig
	Postgres PostgresConfig
	MySQL    MySQLConfig
}

type SQLiteConfig struct {
	Path string
}

type PostgresConfig struct {
	Host     string
	Port     int
	User     string
	Password string
	DBName   string
}

type MySQLConfig struct {
	Host     string
	Port     int
	User     string
	Password string
	DBName   string
}

type JWTConfig struct {
	Secret      string
	ExpireHours int `mapstructure:"expire_hours"`
}

type AIConfig struct {
	Provider string
	APIKey   string `mapstructure:"api_key"`
	BaseURL  string `mapstructure:"base_url"`
}

var AppConfig *Config

func LoadConfig(path string) error {
	viper.SetConfigFile(path)
	if err := viper.ReadInConfig(); err != nil {
		return err
	}
	AppConfig = &Config{}
	return viper.Unmarshal(AppConfig)
}

步骤 2:创建数据库连接模块

创建 server/internal/database/database.go

package database

import (
	"fmt"

	"health-ai/internal/config"

	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
)

var DB *gorm.DB

func InitDatabase(cfg *config.DatabaseConfig) error {
	var err error

	switch cfg.Driver {
	case "sqlite":
		DB, err = gorm.Open(sqlite.Open(cfg.SQLite.Path), &gorm.Config{})
	case "postgres":
		// TODO: 添加 PostgreSQL 支持
		// dsn := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable",
		//     cfg.Postgres.Host, cfg.Postgres.Port, cfg.Postgres.User, cfg.Postgres.Password, cfg.Postgres.DBName)
		// DB, err = gorm.Open(postgres.Open(dsn), &gorm.Config{})
		return fmt.Errorf("postgres driver not implemented yet")
	case "mysql":
		// TODO: 添加 MySQL 支持
		return fmt.Errorf("mysql driver not implemented yet")
	default:
		return fmt.Errorf("unsupported database driver: %s", cfg.Driver)
	}

	return err
}

func AutoMigrate(models ...interface{}) error {
	return DB.AutoMigrate(models...)
}

步骤 3:创建数据模型

创建 server/internal/model/user.go

package model

import (
	"time"

	"gorm.io/gorm"
)

// User 用户表
type User struct {
	gorm.Model
	Phone           string `gorm:"uniqueIndex;size:20"`
	Email           string `gorm:"uniqueIndex;size:100"`
	PasswordHash    string `gorm:"size:255"`
	Nickname        string `gorm:"size:50"`
	Avatar          string `gorm:"size:255"`
	SurveyCompleted bool   `gorm:"default:false"`
}

// HealthProfile 健康档案
type HealthProfile struct {
	gorm.Model
	UserID        uint   `gorm:"uniqueIndex"`
	Name          string `gorm:"size:50"`
	BirthDate     *time.Time
	Gender        string  `gorm:"size:10"` // male, female
	Height        float64 // cm
	Weight        float64 // kg
	BMI           float64
	BloodType     string `gorm:"size:10"` // A, B, AB, O
	Occupation    string `gorm:"size:50"`
	MaritalStatus string `gorm:"size:20"` // single, married, divorced
	Region        string `gorm:"size:100"`
}

// LifestyleInfo 生活习惯
type LifestyleInfo struct {
	gorm.Model
	UserID              uint   `gorm:"uniqueIndex"`
	SleepTime           string `gorm:"size:10"` // HH:MM
	WakeTime            string `gorm:"size:10"`
	SleepQuality        string `gorm:"size:20"` // good, normal, poor
	MealRegularity      string `gorm:"size:20"` // regular, irregular
	DietPreference      string `gorm:"size:50"` // 偏好
	DailyWaterML        int    // 每日饮水量 ml
	ExerciseFrequency   string `gorm:"size:20"` // never, sometimes, often, daily
	ExerciseType        string `gorm:"size:100"`
	ExerciseDurationMin int    // 每次运动时长
	IsSmoker            bool
	AlcoholFrequency    string `gorm:"size:20"` // never, sometimes, often
}

创建 server/internal/model/health.go

package model

import "gorm.io/gorm"

// MedicalHistory 既往病史
type MedicalHistory struct {
	gorm.Model
	HealthProfileID uint
	DiseaseName     string `gorm:"size:100"`
	DiseaseType     string `gorm:"size:50"` // chronic, surgery, other
	DiagnosedDate   string `gorm:"size:20"`
	Status          string `gorm:"size:20"` // cured, treating, controlled
	Notes           string `gorm:"type:text"`
}

// FamilyHistory 家族病史
type FamilyHistory struct {
	gorm.Model
	HealthProfileID uint
	Relation        string `gorm:"size:20"` // father, mother, grandparent
	DiseaseName     string `gorm:"size:100"`
	Notes           string `gorm:"type:text"`
}

// AllergyRecord 过敏记录
type AllergyRecord struct {
	gorm.Model
	HealthProfileID uint
	AllergyType     string `gorm:"size:20"` // drug, food, other
	Allergen        string `gorm:"size:100"`
	Severity        string `gorm:"size:20"` // mild, moderate, severe
	ReactionDesc    string `gorm:"type:text"`
}

创建 server/internal/model/constitution.go

package model

import (
	"time"

	"gorm.io/gorm"
)

// ConstitutionAssessment 体质测评记录
type ConstitutionAssessment struct {
	gorm.Model
	UserID                 uint
	AssessedAt             time.Time
	Scores                 string `gorm:"type:text"` // JSON: 各体质得分
	PrimaryConstitution    string `gorm:"size:20"`   // 主要体质
	SecondaryConstitutions string `gorm:"type:text"` // JSON: 次要体质
	Recommendations        string `gorm:"type:text"` // JSON: 调养建议
}

// AssessmentAnswer 问卷答案
type AssessmentAnswer struct {
	gorm.Model
	AssessmentID uint
	QuestionID   uint
	Score        int // 1-5
}

// QuestionBank 问卷题库
type QuestionBank struct {
	gorm.Model
	ConstitutionType string `gorm:"size:20"` // 体质类型
	QuestionText     string `gorm:"type:text"`
	Options          string `gorm:"type:text"` // JSON: 选项
	OrderNum         int
}

创建 server/internal/model/conversation.go

package model

import "gorm.io/gorm"

// Conversation 对话
type Conversation struct {
	gorm.Model
	UserID   uint
	Title    string `gorm:"size:200"`
	Messages []Message
}

// Message 消息
type Message struct {
	gorm.Model
	ConversationID uint
	Role           string `gorm:"size:20"` // user, assistant, system
	Content        string `gorm:"type:text"`
}

步骤 4:创建模型聚合文件

创建 server/internal/model/models.go

package model

// AllModels 返回所有需要迁移的模型
func AllModels() []interface{} {
	return []interface{}{
		&User{},
		&HealthProfile{},
		&LifestyleInfo{},
		&MedicalHistory{},
		&FamilyHistory{},
		&AllergyRecord{},
		&ConstitutionAssessment{},
		&AssessmentAnswer{},
		&QuestionBank{},
		&Conversation{},
		&Message{},
	}
}

步骤 5:更新主程序

更新 server/cmd/server/main.go

package main

import (
	"log"

	"health-ai/internal/config"
	"health-ai/internal/database"
	"health-ai/internal/model"
)

func main() {
	// 加载配置
	if err := config.LoadConfig("config.yaml"); err != nil {
		log.Fatalf("Failed to load config: %v", err)
	}
	log.Println("Config loaded")

	// 初始化数据库
	if err := database.InitDatabase(&config.AppConfig.Database); err != nil {
		log.Fatalf("Failed to init database: %v", err)
	}
	log.Println("Database connected")

	// 自动迁移
	if err := database.AutoMigrate(model.AllModels()...); err != nil {
		log.Fatalf("Failed to migrate: %v", err)
	}
	log.Println("Database migrated")

	log.Println("Health AI Server Ready!")
}

需要创建的文件清单

文件路径 说明
internal/config/config.go 配置加载
internal/database/database.go 数据库连接
internal/model/user.go 用户相关模型
internal/model/health.go 健康相关模型
internal/model/constitution.go 体质相关模型
internal/model/conversation.go 对话相关模型
internal/model/models.go 模型聚合

验收标准

  • 配置文件可正常加载
  • SQLite 数据库文件自动创建
  • 所有表自动迁移成功
  • data/health.db 文件生成

预计耗时

20-30 分钟


下一步

完成后进入 02-后端开发/03-用户认证模块.md