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.
13 KiB
13 KiB
04-健康调查模块
目标
实现新用户健康调查功能,包括基础信息、生活习惯、病史、过敏史等信息的提交和管理。
前置要求
- 用户认证模块已完成
- 数据模型已定义
实施步骤
步骤 1:创建健康档案 Repository
创建 server/internal/repository/impl/health.go:
package impl
import (
"health-ai/internal/database"
"health-ai/internal/model"
)
type HealthRepository struct{}
func NewHealthRepository() *HealthRepository {
return &HealthRepository{}
}
// HealthProfile
func (r *HealthRepository) CreateProfile(profile *model.HealthProfile) error {
return database.DB.Create(profile).Error
}
func (r *HealthRepository) GetProfileByUserID(userID uint) (*model.HealthProfile, error) {
var profile model.HealthProfile
err := database.DB.Where("user_id = ?", userID).First(&profile).Error
return &profile, err
}
func (r *HealthRepository) UpdateProfile(profile *model.HealthProfile) error {
return database.DB.Save(profile).Error
}
// LifestyleInfo
func (r *HealthRepository) CreateLifestyle(lifestyle *model.LifestyleInfo) error {
return database.DB.Create(lifestyle).Error
}
func (r *HealthRepository) GetLifestyleByUserID(userID uint) (*model.LifestyleInfo, error) {
var lifestyle model.LifestyleInfo
err := database.DB.Where("user_id = ?", userID).First(&lifestyle).Error
return &lifestyle, err
}
func (r *HealthRepository) UpdateLifestyle(lifestyle *model.LifestyleInfo) error {
return database.DB.Save(lifestyle).Error
}
// MedicalHistory
func (r *HealthRepository) CreateMedicalHistory(history *model.MedicalHistory) error {
return database.DB.Create(history).Error
}
func (r *HealthRepository) GetMedicalHistories(profileID uint) ([]model.MedicalHistory, error) {
var histories []model.MedicalHistory
err := database.DB.Where("health_profile_id = ?", profileID).Find(&histories).Error
return histories, err
}
func (r *HealthRepository) DeleteMedicalHistory(id uint) error {
return database.DB.Delete(&model.MedicalHistory{}, id).Error
}
// FamilyHistory
func (r *HealthRepository) CreateFamilyHistory(history *model.FamilyHistory) error {
return database.DB.Create(history).Error
}
func (r *HealthRepository) GetFamilyHistories(profileID uint) ([]model.FamilyHistory, error) {
var histories []model.FamilyHistory
err := database.DB.Where("health_profile_id = ?", profileID).Find(&histories).Error
return histories, err
}
// AllergyRecord
func (r *HealthRepository) CreateAllergyRecord(record *model.AllergyRecord) error {
return database.DB.Create(record).Error
}
func (r *HealthRepository) GetAllergyRecords(profileID uint) ([]model.AllergyRecord, error) {
var records []model.AllergyRecord
err := database.DB.Where("health_profile_id = ?", profileID).Find(&records).Error
return records, err
}
步骤 2:创建健康调查 Service
创建 server/internal/service/survey.go:
package service
import (
"health-ai/internal/model"
"health-ai/internal/repository/impl"
)
type SurveyService struct {
healthRepo *impl.HealthRepository
userRepo *impl.UserRepositoryImpl
}
func NewSurveyService() *SurveyService {
return &SurveyService{
healthRepo: impl.NewHealthRepository(),
userRepo: impl.NewUserRepository(),
}
}
// 基础信息请求
type BasicInfoRequest struct {
Name string `json:"name" binding:"required"`
BirthDate string `json:"birth_date"`
Gender string `json:"gender" binding:"required,oneof=male female"`
Height float64 `json:"height" binding:"required"`
Weight float64 `json:"weight" binding:"required"`
BloodType string `json:"blood_type"`
Occupation string `json:"occupation"`
MaritalStatus string `json:"marital_status"`
Region string `json:"region"`
}
// 生活习惯请求
type LifestyleRequest struct {
SleepTime string `json:"sleep_time"`
WakeTime string `json:"wake_time"`
SleepQuality string `json:"sleep_quality"`
MealRegularity string `json:"meal_regularity"`
DietPreference string `json:"diet_preference"`
DailyWaterML int `json:"daily_water_ml"`
ExerciseFrequency string `json:"exercise_frequency"`
ExerciseType string `json:"exercise_type"`
ExerciseDurationMin int `json:"exercise_duration_min"`
IsSmoker bool `json:"is_smoker"`
AlcoholFrequency string `json:"alcohol_frequency"`
}
// 病史请求
type MedicalHistoryRequest struct {
DiseaseName string `json:"disease_name" binding:"required"`
DiseaseType string `json:"disease_type"`
DiagnosedDate string `json:"diagnosed_date"`
Status string `json:"status"`
Notes string `json:"notes"`
}
// 家族病史请求
type FamilyHistoryRequest struct {
Relation string `json:"relation" binding:"required"`
DiseaseName string `json:"disease_name" binding:"required"`
Notes string `json:"notes"`
}
// 过敏记录请求
type AllergyRequest struct {
AllergyType string `json:"allergy_type" binding:"required"`
Allergen string `json:"allergen" binding:"required"`
Severity string `json:"severity"`
ReactionDesc string `json:"reaction_desc"`
}
// 获取调查状态
func (s *SurveyService) GetStatus(userID uint) (map[string]bool, error) {
status := map[string]bool{
"basic_info": false,
"lifestyle": false,
"medical_history": false,
"family_history": false,
"allergy": false,
}
profile, err := s.healthRepo.GetProfileByUserID(userID)
if err == nil && profile.ID > 0 {
status["basic_info"] = true
histories, _ := s.healthRepo.GetMedicalHistories(profile.ID)
status["medical_history"] = len(histories) >= 0 // 可以为空
familyHistories, _ := s.healthRepo.GetFamilyHistories(profile.ID)
status["family_history"] = len(familyHistories) >= 0
allergies, _ := s.healthRepo.GetAllergyRecords(profile.ID)
status["allergy"] = len(allergies) >= 0
}
lifestyle, err := s.healthRepo.GetLifestyleByUserID(userID)
if err == nil && lifestyle.ID > 0 {
status["lifestyle"] = true
}
return status, nil
}
// 提交基础信息
func (s *SurveyService) SubmitBasicInfo(userID uint, req *BasicInfoRequest) error {
// 计算 BMI
heightM := req.Height / 100
bmi := req.Weight / (heightM * heightM)
profile := &model.HealthProfile{
UserID: userID,
Name: req.Name,
Gender: req.Gender,
Height: req.Height,
Weight: req.Weight,
BMI: bmi,
BloodType: req.BloodType,
Occupation: req.Occupation,
MaritalStatus: req.MaritalStatus,
Region: req.Region,
}
// 检查是否已存在
existing, _ := s.healthRepo.GetProfileByUserID(userID)
if existing.ID > 0 {
profile.ID = existing.ID
return s.healthRepo.UpdateProfile(profile)
}
return s.healthRepo.CreateProfile(profile)
}
// 提交生活习惯
func (s *SurveyService) SubmitLifestyle(userID uint, req *LifestyleRequest) error {
lifestyle := &model.LifestyleInfo{
UserID: userID,
SleepTime: req.SleepTime,
WakeTime: req.WakeTime,
SleepQuality: req.SleepQuality,
MealRegularity: req.MealRegularity,
DietPreference: req.DietPreference,
DailyWaterML: req.DailyWaterML,
ExerciseFrequency: req.ExerciseFrequency,
ExerciseType: req.ExerciseType,
ExerciseDurationMin: req.ExerciseDurationMin,
IsSmoker: req.IsSmoker,
AlcoholFrequency: req.AlcoholFrequency,
}
existing, _ := s.healthRepo.GetLifestyleByUserID(userID)
if existing.ID > 0 {
lifestyle.ID = existing.ID
return s.healthRepo.UpdateLifestyle(lifestyle)
}
return s.healthRepo.CreateLifestyle(lifestyle)
}
// 提交病史
func (s *SurveyService) SubmitMedicalHistory(userID uint, req *MedicalHistoryRequest) error {
profile, err := s.healthRepo.GetProfileByUserID(userID)
if err != nil {
return err
}
history := &model.MedicalHistory{
HealthProfileID: profile.ID,
DiseaseName: req.DiseaseName,
DiseaseType: req.DiseaseType,
DiagnosedDate: req.DiagnosedDate,
Status: req.Status,
Notes: req.Notes,
}
return s.healthRepo.CreateMedicalHistory(history)
}
// 提交家族病史
func (s *SurveyService) SubmitFamilyHistory(userID uint, req *FamilyHistoryRequest) error {
profile, err := s.healthRepo.GetProfileByUserID(userID)
if err != nil {
return err
}
history := &model.FamilyHistory{
HealthProfileID: profile.ID,
Relation: req.Relation,
DiseaseName: req.DiseaseName,
Notes: req.Notes,
}
return s.healthRepo.CreateFamilyHistory(history)
}
// 提交过敏信息
func (s *SurveyService) SubmitAllergy(userID uint, req *AllergyRequest) error {
profile, err := s.healthRepo.GetProfileByUserID(userID)
if err != nil {
return err
}
record := &model.AllergyRecord{
HealthProfileID: profile.ID,
AllergyType: req.AllergyType,
Allergen: req.Allergen,
Severity: req.Severity,
ReactionDesc: req.ReactionDesc,
}
return s.healthRepo.CreateAllergyRecord(record)
}
// 标记调查完成
func (s *SurveyService) MarkSurveyCompleted(userID uint) error {
user, err := s.userRepo.GetByID(userID)
if err != nil {
return err
}
user.SurveyCompleted = true
return s.userRepo.Update(user)
}
步骤 3:创建健康调查 Handler
创建 server/internal/api/handler/survey.go:
package handler
import (
"health-ai/internal/api/middleware"
"health-ai/internal/service"
"health-ai/pkg/response"
"github.com/gin-gonic/gin"
)
type SurveyHandler struct {
surveyService *service.SurveyService
}
func NewSurveyHandler() *SurveyHandler {
return &SurveyHandler{
surveyService: service.NewSurveyService(),
}
}
func (h *SurveyHandler) GetStatus(c *gin.Context) {
userID := middleware.GetUserID(c)
status, err := h.surveyService.GetStatus(userID)
if err != nil {
response.Error(c, 500, err.Error())
return
}
response.Success(c, status)
}
func (h *SurveyHandler) SubmitBasicInfo(c *gin.Context) {
userID := middleware.GetUserID(c)
var req service.BasicInfoRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.Error(c, 400, "参数错误: "+err.Error())
return
}
if err := h.surveyService.SubmitBasicInfo(userID, &req); err != nil {
response.Error(c, 500, err.Error())
return
}
response.Success(c, nil)
}
func (h *SurveyHandler) SubmitLifestyle(c *gin.Context) {
userID := middleware.GetUserID(c)
var req service.LifestyleRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.Error(c, 400, "参数错误: "+err.Error())
return
}
if err := h.surveyService.SubmitLifestyle(userID, &req); err != nil {
response.Error(c, 500, err.Error())
return
}
response.Success(c, nil)
}
func (h *SurveyHandler) SubmitMedicalHistory(c *gin.Context) {
userID := middleware.GetUserID(c)
var req service.MedicalHistoryRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.Error(c, 400, "参数错误: "+err.Error())
return
}
if err := h.surveyService.SubmitMedicalHistory(userID, &req); err != nil {
response.Error(c, 500, err.Error())
return
}
response.Success(c, nil)
}
func (h *SurveyHandler) SubmitFamilyHistory(c *gin.Context) {
userID := middleware.GetUserID(c)
var req service.FamilyHistoryRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.Error(c, 400, "参数错误: "+err.Error())
return
}
if err := h.surveyService.SubmitFamilyHistory(userID, &req); err != nil {
response.Error(c, 500, err.Error())
return
}
response.Success(c, nil)
}
func (h *SurveyHandler) SubmitAllergy(c *gin.Context) {
userID := middleware.GetUserID(c)
var req service.AllergyRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.Error(c, 400, "参数错误: "+err.Error())
return
}
if err := h.surveyService.SubmitAllergy(userID, &req); err != nil {
response.Error(c, 500, err.Error())
return
}
response.Success(c, nil)
}
步骤 4:更新路由配置
在 router.go 中添加调查路由:
// 健康调查路由(需要登录)
surveyHandler := handler.NewSurveyHandler()
surveyGroup := apiGroup.Group("/survey")
surveyGroup.Use(middleware.AuthRequired())
{
surveyGroup.GET("/status", surveyHandler.GetStatus)
surveyGroup.POST("/basic-info", surveyHandler.SubmitBasicInfo)
surveyGroup.POST("/lifestyle", surveyHandler.SubmitLifestyle)
surveyGroup.POST("/medical-history", surveyHandler.SubmitMedicalHistory)
surveyGroup.POST("/family-history", surveyHandler.SubmitFamilyHistory)
surveyGroup.POST("/allergy", surveyHandler.SubmitAllergy)
}
API 接口说明
GET /api/survey/status
获取调查完成状态(需认证)
POST /api/survey/basic-info
提交基础信息
POST /api/survey/lifestyle
提交生活习惯
POST /api/survey/medical-history
提交病史(可多次提交)
POST /api/survey/family-history
提交家族病史(可多次提交)
POST /api/survey/allergy
提交过敏信息(可多次提交)
需要创建的文件清单
| 文件路径 | 说明 |
|---|---|
internal/repository/impl/health.go |
健康 Repository |
internal/service/survey.go |
调查 Service |
internal/api/handler/survey.go |
调查 Handler |
验收标准
- 获取调查状态接口正常
- 基础信息提交成功,BMI 自动计算
- 生活习惯提交成功
- 病史、家族史、过敏信息可多次添加
- 所有接口需要认证
预计耗时
30-40 分钟
下一步
完成后进入 02-后端开发/05-体质辨识模块.md