package service import ( "errors" "fmt" "photowall/internal/model" "regexp" "strings" "gorm.io/gorm" ) type UserService struct { db *gorm.DB } func NewUserService(db *gorm.DB) *UserService { return &UserService{db: db} } // ProfileResp 用户完整资料 type ProfileResp struct { User model.User `json:"user"` Contacts []model.UserContact `json:"contacts"` Education []model.EducationHistory `json:"education"` } func (s *UserService) GetProfile(userID uint) (*ProfileResp, error) { var user model.User if err := s.db.First(&user, userID).Error; err != nil { return nil, errors.New("用户不存在") } var contacts []model.UserContact s.db.Where("user_id = ?", userID).Order("type, is_primary desc, id").Find(&contacts) // 融合基本资料与联系方式:若 contacts 中缺少基本资料中的 phone/email/address,补齐展示 hasPhone := false hasEmail := false hasAddress := false for _, c := range contacts { if c.Type == model.ContactPhone { hasPhone = true } if c.Type == model.ContactEmail { hasEmail = true } if c.Type == model.ContactAddress { hasAddress = true } } if !hasPhone && user.Phone != "" { contacts = append(contacts, model.UserContact{ UserID: userID, Type: model.ContactPhone, Value: user.Phone, IsPrimary: true, }) } if !hasEmail && user.Email != "" { contacts = append(contacts, model.UserContact{ UserID: userID, Type: model.ContactEmail, Value: user.Email, IsPrimary: true, }) } if !hasAddress && user.Address != "" { contacts = append(contacts, model.UserContact{ UserID: userID, Type: model.ContactAddress, Value: user.Address, IsPrimary: true, }) } var edu []model.EducationHistory s.db.Where("user_id = ?", userID).Order("start_year desc, id desc").Find(&edu) return &ProfileResp{User: user, Contacts: contacts, Education: edu}, nil } // IsValidIDCard 校验中国大陆18位二代居民身份证合法性 func IsValidIDCard(idCard string) bool { idCard = strings.ToUpper(strings.TrimSpace(idCard)) if len(idCard) != 18 { return false } // 正则粗筛:前17位数字,第18位数字或字母X matched, _ := regexp.MatchString(`^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[0-9X]$`, idCard) if !matched { return false } // ISO 7064:1983.MOD 11-2 校验码加权 weights := []int{7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2} checkCodes := []byte{'1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'} sum := 0 for i := 0; i < 17; i++ { sum += int(idCard[i]-'0') * weights[i] } expectedCheck := checkCodes[sum%11] return idCard[17] == expectedCheck } type UpdateProfileReq struct { RealName string `json:"real_name"` Gender string `json:"gender"` Nation string `json:"nation"` PoliticalStatus string `json:"political_status"` IdCard string `json:"id_card"` Address string `json:"address"` Phone string `json:"phone"` Nickname string `json:"nickname"` Email string `json:"email"` Bio string `json:"bio"` Avatar string `json:"avatar"` } func (s *UserService) UpdateProfile(userID uint, req *UpdateProfileReq) error { updates := map[string]interface{}{} updates["real_name"] = strings.TrimSpace(req.RealName) updates["gender"] = strings.TrimSpace(req.Gender) updates["nation"] = strings.TrimSpace(req.Nation) updates["political_status"] = strings.TrimSpace(req.PoliticalStatus) idCard := strings.ToUpper(strings.TrimSpace(req.IdCard)) if idCard != "" && !strings.Contains(idCard, "*") { if !IsValidIDCard(idCard) { return errors.New("身份证号码格式不正确,请输入18位有效居民身份证号") } updates["id_card"] = idCard } else if idCard == "" { updates["id_card"] = "" } address := strings.TrimSpace(req.Address) updates["address"] = address phone := strings.TrimSpace(req.Phone) updates["phone"] = phone if req.Nickname != "" { updates["nickname"] = strings.TrimSpace(req.Nickname) } email := strings.TrimSpace(req.Email) if email != "" { updates["email"] = email } updates["bio"] = req.Bio updates["avatar"] = strings.TrimSpace(req.Avatar) if err := s.db.Model(&model.User{}).Where("id = ?", userID).Updates(updates).Error; err != nil { return err } // 融合基本资料与联系方式:同步手机号、邮箱、住址到联系方式表 if phone != "" { var primaryPhone model.UserContact if err := s.db.Where("user_id = ? AND type = ? AND is_primary = ?", userID, model.ContactPhone, true).First(&primaryPhone).Error; err == nil { s.db.Model(&primaryPhone).Update("value", phone) } else { var anyPhone model.UserContact if err := s.db.Where("user_id = ? AND type = ?", userID, model.ContactPhone).First(&anyPhone).Error; err == nil { s.db.Model(&anyPhone).Updates(map[string]interface{}{"value": phone, "is_primary": true}) } else { s.db.Create(&model.UserContact{ UserID: userID, Type: model.ContactPhone, Value: phone, IsPrimary: true, }) } } } if email != "" { var primaryEmail model.UserContact if err := s.db.Where("user_id = ? AND type = ? AND is_primary = ?", userID, model.ContactEmail, true).First(&primaryEmail).Error; err == nil { s.db.Model(&primaryEmail).Update("value", email) } else { var anyEmail model.UserContact if err := s.db.Where("user_id = ? AND type = ?", userID, model.ContactEmail).First(&anyEmail).Error; err == nil { s.db.Model(&anyEmail).Updates(map[string]interface{}{"value": email, "is_primary": true}) } else { s.db.Create(&model.UserContact{ UserID: userID, Type: model.ContactEmail, Value: email, IsPrimary: true, }) } } } if address != "" { var anyAddr model.UserContact if err := s.db.Where("user_id = ? AND type = ?", userID, model.ContactAddress).First(&anyAddr).Error; err == nil { s.db.Model(&anyAddr).Updates(map[string]interface{}{"value": address, "is_primary": true}) } else { s.db.Create(&model.UserContact{ UserID: userID, Type: model.ContactAddress, Value: address, IsPrimary: true, }) } } return nil } // AddContact 添加联系方式,校验各类型数量上限 func (s *UserService) AddContact(userID uint, contactType model.ContactType, value string, isPrimary bool) (*model.UserContact, error) { if value == "" { return nil, errors.New("联系方式不能为空") } max, ok := model.ContactMaxCount[contactType] if !ok { return nil, errors.New("不支持的联系方式类型") } var count int64 s.db.Model(&model.UserContact{}).Where("user_id = ? AND type = ?", userID, contactType).Count(&count) if int(count) >= max { return nil, fmt.Errorf("%s 最多只能添加 %d 个", contactType, max) } // 如果设为主,先取消同类型其他主 if isPrimary { s.db.Model(&model.UserContact{}).Where("user_id = ? AND type = ?", userID, contactType).Update("is_primary", false) } c := &model.UserContact{ UserID: userID, Type: contactType, Value: value, IsPrimary: isPrimary, } if err := s.db.Create(c).Error; err != nil { return nil, err } // 融合基本资料与联系方式:若设为主要联系方式或为首个联系方式,同步回写到 User 表中 if isPrimary || count == 0 { switch contactType { case model.ContactPhone: s.db.Model(&model.User{}).Where("id = ?", userID).Update("phone", value) case model.ContactEmail: s.db.Model(&model.User{}).Where("id = ?", userID).Update("email", value) case model.ContactAddress: s.db.Model(&model.User{}).Where("id = ?", userID).Update("address", value) } } return c, nil } func (s *UserService) UpdateContact(userID, contactID uint, value string, isPrimary *bool) error { var c model.UserContact if err := s.db.Where("id = ? AND user_id = ?", contactID, userID).First(&c).Error; err != nil { return errors.New("联系方式不存在") } if value != "" { c.Value = value } if isPrimary != nil && *isPrimary { s.db.Model(&model.UserContact{}).Where("user_id = ? AND type = ?", userID, c.Type).Update("is_primary", false) c.IsPrimary = true // 同步回写至 User 表 switch c.Type { case model.ContactPhone: s.db.Model(&model.User{}).Where("id = ?", userID).Update("phone", c.Value) case model.ContactEmail: s.db.Model(&model.User{}).Where("id = ?", userID).Update("email", c.Value) case model.ContactAddress: s.db.Model(&model.User{}).Where("id = ?", userID).Update("address", c.Value) } } return s.db.Save(&c).Error } func (s *UserService) DeleteContact(userID, contactID uint) error { var c model.UserContact if err := s.db.Where("id = ? AND user_id = ?", contactID, userID).First(&c).Error; err != nil { return errors.New("联系方式不存在") } res := s.db.Where("id = ? AND user_id = ?", contactID, userID).Delete(&model.UserContact{}) if res.Error != nil { return res.Error } if res.RowsAffected == 0 { return errors.New("联系方式不存在") } // 若删除的是主联系方式,同步 User 表中对应信息 switch c.Type { case model.ContactPhone: var nextPhone model.UserContact if err := s.db.Where("user_id = ? AND type = ?", userID, model.ContactPhone).Order("is_primary desc, id").First(&nextPhone).Error; err == nil { s.db.Model(&nextPhone).Update("is_primary", true) s.db.Model(&model.User{}).Where("id = ?", userID).Update("phone", nextPhone.Value) } else { s.db.Model(&model.User{}).Where("id = ?", userID).Update("phone", "") } case model.ContactEmail: var nextEmail model.UserContact if err := s.db.Where("user_id = ? AND type = ?", userID, model.ContactEmail).Order("is_primary desc, id").First(&nextEmail).Error; err == nil { s.db.Model(&nextEmail).Update("is_primary", true) s.db.Model(&model.User{}).Where("id = ?", userID).Update("email", nextEmail.Value) } else { s.db.Model(&model.User{}).Where("id = ?", userID).Update("email", "") } case model.ContactAddress: var nextAddr model.UserContact if err := s.db.Where("user_id = ? AND type = ?", userID, model.ContactAddress).Order("is_primary desc, id").First(&nextAddr).Error; err == nil { s.db.Model(&nextAddr).Update("is_primary", true) s.db.Model(&model.User{}).Where("id = ?", userID).Update("address", nextAddr.Value) } else { s.db.Model(&model.User{}).Where("id = ?", userID).Update("address", "") } } return nil } // AddEducation 添加学习履历 func (s *UserService) AddEducation(userID uint, e *model.EducationHistory) error { e.UserID = userID e.ID = 0 return s.db.Create(e).Error } func (s *UserService) UpdateEducation(userID, id uint, e *model.EducationHistory) error { res := s.db.Model(&model.EducationHistory{}). Where("id = ? AND user_id = ?", id, userID). Updates(map[string]interface{}{ "school_id": e.SchoolID, "college_id": e.CollegeID, "class_id": e.ClassID, "school_name": e.SchoolName, "college_name": e.CollegeName, "major": e.Major, "degree": e.Degree, "start_year": e.StartYear, "end_year": e.EndYear, "description": e.Description, }) if res.Error != nil { return res.Error } if res.RowsAffected == 0 { return errors.New("履历不存在") } return nil } func (s *UserService) DeleteEducation(userID, id uint) error { res := s.db.Where("id = ? AND user_id = ?", id, userID).Delete(&model.EducationHistory{}) if res.Error != nil { return res.Error } if res.RowsAffected == 0 { return errors.New("履历不存在") } return nil } // HasEducation 检查用户是否有至少一条学习履历(创建班级前置条件) func (s *UserService) HasEducation(userID uint) bool { var count int64 s.db.Model(&model.EducationHistory{}).Where("user_id = ?", userID).Count(&count) return count > 0 } // GetUserByID 公开用户信息(班级成员查看) func (s *UserService) GetUserByID(userID uint) (*model.User, error) { var u model.User if err := s.db.First(&u, userID).Error; err != nil { return nil, errors.New("用户不存在") } return &u, nil } // GetUserContacts 公开用户联系方式(同班同学可查看) func (s *UserService) GetUserContacts(userID uint) []model.UserContact { var list []model.UserContact s.db.Where("user_id = ?", userID).Order("type, is_primary desc, id").Find(&list) var u model.User if s.db.Select("id, phone, email, address").First(&u, userID).Error == nil { hasPhone := false hasEmail := false hasAddress := false for _, c := range list { if c.Type == model.ContactPhone { hasPhone = true } if c.Type == model.ContactEmail { hasEmail = true } if c.Type == model.ContactAddress { hasAddress = true } } if !hasPhone && u.Phone != "" { list = append(list, model.UserContact{UserID: userID, Type: model.ContactPhone, Value: u.Phone, IsPrimary: true}) } if !hasEmail && u.Email != "" { list = append(list, model.UserContact{UserID: userID, Type: model.ContactEmail, Value: u.Email, IsPrimary: true}) } if !hasAddress && u.Address != "" { list = append(list, model.UserContact{UserID: userID, Type: model.ContactAddress, Value: u.Address, IsPrimary: true}) } } return list }