Files
tyapi-server/internal/domains/user/entities/user.go
2025-07-30 00:51:22 +08:00

659 lines
17 KiB
Go
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.

package entities
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"regexp"
"time"
"github.com/google/uuid"
"gorm.io/gorm"
)
// UserType 用户类型枚举
type UserType string
const (
UserTypeNormal UserType = "user" // 普通用户
UserTypeAdmin UserType = "admin" // 管理员
)
// User 用户聚合根
// 系统用户的核心信息,提供基础的账户管理功能
// 支持手机号登录密码加密存储实现Entity接口便于统一管理
type User struct {
// 基础标识
ID string `gorm:"primaryKey;type:varchar(36)" json:"id" comment:"用户唯一标识"`
Phone string `gorm:"uniqueIndex;type:varchar(20);not null" json:"phone" comment:"手机号码(登录账号)"`
Password string `gorm:"type:varchar(255);not null" json:"-" comment:"登录密码(加密存储,不返回前端)"`
// 用户类型和基本信息
UserType string `gorm:"type:varchar(20);not null;default:'user'" json:"user_type" comment:"用户类型(user/admin)"`
Username string `gorm:"type:varchar(100)" json:"username" comment:"用户名(管理员专用)"`
// 管理员特有字段
Active bool `gorm:"default:true" json:"is_active" comment:"账户是否激活"`
IsCertified bool `gorm:"default:false" json:"is_certified" comment:"是否完成认证"`
LastLoginAt *time.Time `json:"last_login_at" comment:"最后登录时间"`
LoginCount int `gorm:"default:0" json:"login_count" comment:"登录次数统计"`
Permissions string `gorm:"type:text" json:"permissions" comment:"权限列表(JSON格式存储)"`
// 时间戳字段
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at" comment:"创建时间"`
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at" comment:"更新时间"`
DeletedAt gorm.DeletedAt `gorm:"index" json:"-" comment:"软删除时间"`
// 关联关系
EnterpriseInfo *EnterpriseInfo `gorm:"foreignKey:UserID" json:"enterprise_info,omitempty" comment:"企业信息(认证后获得)"`
// 领域事件 (不持久化)
domainEvents []interface{} `gorm:"-" json:"-"`
}
// BeforeCreate GORM钩子创建前自动生成UUID
func (u *User) BeforeCreate(tx *gorm.DB) error {
if u.ID == "" {
u.ID = uuid.New().String()
}
return nil
}
// 实现 Entity 接口 - 提供统一的实体管理接口
// GetID 获取实体唯一标识
func (u *User) GetID() string {
return u.ID
}
// GetCreatedAt 获取创建时间
func (u *User) GetCreatedAt() time.Time {
return u.CreatedAt
}
// GetUpdatedAt 获取更新时间
func (u *User) GetUpdatedAt() time.Time {
return u.UpdatedAt
}
// Validate 验证用户信息
// 检查用户必填字段是否完整,确保数据的有效性
func (u *User) Validate() error {
if u.Phone == "" {
return NewValidationError("手机号不能为空")
}
if u.Password == "" {
return NewValidationError("密码不能为空")
}
// 验证手机号格式
if !u.IsValidPhone() {
return NewValidationError("手机号格式无效")
}
return nil
}
// CompleteCertification 完成认证
func (u *User) CompleteCertification() error {
u.IsCertified = true
return nil
}
// ================ 企业信息管理方法 ================
// CreateEnterpriseInfo 创建企业信息
func (u *User) CreateEnterpriseInfo(companyName, unifiedSocialCode, legalPersonName, legalPersonID, legalPersonPhone, enterpriseAddress string) error {
// 检查是否已有企业信息
if u.EnterpriseInfo != nil {
return fmt.Errorf("用户已有企业信息")
}
// 创建企业信息实体
enterpriseInfo, err := NewEnterpriseInfo(u.ID, companyName, unifiedSocialCode, legalPersonName, legalPersonID, legalPersonPhone, enterpriseAddress)
if err != nil {
return fmt.Errorf("创建企业信息失败: %w", err)
}
// 设置关联关系
u.EnterpriseInfo = enterpriseInfo
// 添加领域事件
u.addDomainEvent(&UserEnterpriseInfoCreatedEvent{
UserID: u.ID,
EnterpriseInfoID: enterpriseInfo.ID,
CompanyName: companyName,
UnifiedSocialCode: unifiedSocialCode,
CreatedAt: time.Now(),
})
return nil
}
// UpdateEnterpriseInfo 更新企业信息
func (u *User) UpdateEnterpriseInfo(companyName, unifiedSocialCode, legalPersonName, legalPersonID, legalPersonPhone, enterpriseAddress string) error {
// 检查是否有企业信息
if u.EnterpriseInfo == nil {
return fmt.Errorf("用户暂无企业信息")
}
// 记录原始值用于事件
oldCompanyName := u.EnterpriseInfo.CompanyName
oldUnifiedSocialCode := u.EnterpriseInfo.UnifiedSocialCode
// 更新企业信息
err := u.EnterpriseInfo.UpdateEnterpriseInfo(companyName, unifiedSocialCode, legalPersonName, legalPersonID, legalPersonPhone, enterpriseAddress)
if err != nil {
return err
}
// 添加领域事件
u.addDomainEvent(&UserEnterpriseInfoUpdatedEvent{
UserID: u.ID,
EnterpriseInfoID: u.EnterpriseInfo.ID,
OldCompanyName: oldCompanyName,
NewCompanyName: companyName,
OldUnifiedSocialCode: oldUnifiedSocialCode,
NewUnifiedSocialCode: unifiedSocialCode,
UpdatedAt: time.Now(),
})
return nil
}
// GetEnterpriseInfo 获取企业信息
func (u *User) GetEnterpriseInfo() *EnterpriseInfo {
return u.EnterpriseInfo
}
// HasEnterpriseInfo 检查是否有企业信息
func (u *User) HasEnterpriseInfo() bool {
return u.EnterpriseInfo != nil
}
// RemoveEnterpriseInfo 移除企业信息
func (u *User) RemoveEnterpriseInfo() error {
if u.EnterpriseInfo == nil {
return fmt.Errorf("用户暂无企业信息")
}
enterpriseInfoID := u.EnterpriseInfo.ID
u.EnterpriseInfo = nil
// 添加领域事件
u.addDomainEvent(&UserEnterpriseInfoRemovedEvent{
UserID: u.ID,
EnterpriseInfoID: enterpriseInfoID,
RemovedAt: time.Now(),
})
return nil
}
// ValidateEnterpriseInfo 验证企业信息
func (u *User) ValidateEnterpriseInfo() error {
if u.EnterpriseInfo == nil {
return fmt.Errorf("用户暂无企业信息")
}
return u.EnterpriseInfo.ValidateBusinessRules()
}
// ================ 聚合根核心方法 ================
// Register 用户注册
func (u *User) Register() error {
// 验证用户信息
if err := u.Validate(); err != nil {
return fmt.Errorf("用户信息验证失败: %w", err)
}
// 添加领域事件
u.addDomainEvent(&UserRegisteredEvent{
UserID: u.ID,
Phone: u.Phone,
UserType: u.UserType,
CreatedAt: time.Now(),
})
return nil
}
// Login 用户登录
func (u *User) Login(ipAddress, userAgent string) error {
// 检查用户是否可以登录
if !u.CanLogin() {
return fmt.Errorf("用户无法登录")
}
// 更新登录信息
u.UpdateLastLoginAt()
u.IncrementLoginCount()
// 添加领域事件
u.addDomainEvent(&UserLoggedInEvent{
UserID: u.ID,
Phone: u.Phone,
IPAddress: ipAddress,
UserAgent: userAgent,
LoginAt: time.Now(),
})
return nil
}
// ChangePassword 修改密码
func (u *User) ChangePassword(oldPassword, newPassword, confirmPassword string) error {
// 验证旧密码
if !u.CheckPassword(oldPassword) {
return fmt.Errorf("原密码错误")
}
// 验证新密码
if newPassword != confirmPassword {
return fmt.Errorf("两次输入的密码不一致")
}
// 设置新密码
if err := u.SetPassword(newPassword); err != nil {
return fmt.Errorf("设置新密码失败: %w", err)
}
// 添加领域事件
u.addDomainEvent(&UserPasswordChangedEvent{
UserID: u.ID,
Phone: u.Phone,
ChangedAt: time.Now(),
})
return nil
}
// ActivateUser 激活用户
func (u *User) ActivateUser() error {
if u.Active {
return fmt.Errorf("用户已经是激活状态")
}
u.Activate()
// 添加领域事件
u.addDomainEvent(&UserActivatedEvent{
UserID: u.ID,
Phone: u.Phone,
ActivatedAt: time.Now(),
})
return nil
}
// DeactivateUser 停用用户
func (u *User) DeactivateUser() error {
if !u.Active {
return fmt.Errorf("用户已经是停用状态")
}
u.Deactivate()
// 添加领域事件
u.addDomainEvent(&UserDeactivatedEvent{
UserID: u.ID,
Phone: u.Phone,
DeactivatedAt: time.Now(),
})
return nil
}
// ================ 业务规则验证 ================
// ValidateBusinessRules 验证业务规则
func (u *User) ValidateBusinessRules() error {
// 1. 基础字段验证
if err := u.validateBasicFields(); err != nil {
return fmt.Errorf("基础字段验证失败: %w", err)
}
// 2. 业务规则验证
if err := u.validateBusinessLogic(); err != nil {
return fmt.Errorf("业务规则验证失败: %w", err)
}
// 3. 状态一致性验证
if err := u.validateStateConsistency(); err != nil {
return fmt.Errorf("状态一致性验证失败: %w", err)
}
return nil
}
// validateBasicFields 验证基础字段
func (u *User) validateBasicFields() error {
if u.Phone == "" {
return fmt.Errorf("手机号不能为空")
}
if u.Password == "" {
return fmt.Errorf("密码不能为空")
}
// 验证手机号格式
if !u.IsValidPhone() {
return fmt.Errorf("手机号格式无效")
}
// 不对加密后的hash做长度校验
return nil
}
// validateBusinessLogic 验证业务逻辑
func (u *User) validateBusinessLogic() error {
// 管理员用户必须有用户名
// if u.IsAdmin() && u.Username == "" {
// return fmt.Errorf("管理员用户必须有用户名")
// }
// // 普通用户不能有用户名
// if u.IsNormalUser() && u.Username != "" {
// return fmt.Errorf("普通用户不能有用户名")
// }
return nil
}
// validateStateConsistency 验证状态一致性
func (u *User) validateStateConsistency() error {
// 如果用户被删除,不能是激活状态
if u.IsDeleted() && u.Active {
return fmt.Errorf("已删除用户不能是激活状态")
}
return nil
}
// ================ 领域事件管理 ================
// addDomainEvent 添加领域事件
func (u *User) addDomainEvent(event interface{}) {
if u.domainEvents == nil {
u.domainEvents = make([]interface{}, 0)
}
u.domainEvents = append(u.domainEvents, event)
}
// GetDomainEvents 获取领域事件
func (u *User) GetDomainEvents() []interface{} {
return u.domainEvents
}
// ClearDomainEvents 清除领域事件
func (u *User) ClearDomainEvents() {
u.domainEvents = make([]interface{}, 0)
}
// ================ 业务方法 ================
// IsAdmin 检查是否为管理员
func (u *User) IsAdmin() bool {
return u.UserType == string(UserTypeAdmin)
}
// IsNormalUser 检查是否为普通用户
func (u *User) IsNormalUser() bool {
return u.UserType == string(UserTypeNormal)
}
// SetUserType 设置用户类型
func (u *User) SetUserType(userType UserType) {
u.UserType = string(userType)
}
// UpdateLastLoginAt 更新最后登录时间
func (u *User) UpdateLastLoginAt() {
now := time.Now()
u.LastLoginAt = &now
}
// IncrementLoginCount 增加登录次数
func (u *User) IncrementLoginCount() {
u.LoginCount++
}
// Activate 激活用户账户
func (u *User) Activate() {
u.Active = true
}
// Deactivate 停用用户账户
func (u *User) Deactivate() {
u.Active = false
}
// CheckPassword 验证密码是否正确
func (u *User) CheckPassword(password string) bool {
return u.Password == hashPassword(password)
}
// SetPassword 设置密码(用于注册或重置密码)
func (u *User) SetPassword(password string) error {
// 只对明文做强度校验
if err := u.validatePasswordStrength(password); err != nil {
return err
}
u.Password = hashPassword(password)
return nil
}
// ResetPassword 重置密码(忘记密码时使用)
func (u *User) ResetPassword(newPassword, confirmPassword string) error {
if newPassword != confirmPassword {
return NewValidationError("新密码和确认新密码不匹配")
}
if err := u.validatePasswordStrength(newPassword); err != nil {
return err
}
u.Password = hashPassword(newPassword)
return nil
}
// CanLogin 检查用户是否可以登录
func (u *User) CanLogin() bool {
// 检查用户是否被删除
if !u.DeletedAt.Time.IsZero() {
return false
}
// 检查必要字段是否存在
if u.Phone == "" || u.Password == "" {
return false
}
// 如果是管理员,检查是否激活
if u.IsAdmin() && !u.Active {
return false
}
return true
}
// IsActive 检查用户是否处于活跃状态
func (u *User) IsActive() bool {
return u.DeletedAt.Time.IsZero()
}
// IsDeleted 检查用户是否已被删除
func (u *User) IsDeleted() bool {
return !u.DeletedAt.Time.IsZero()
}
// ================ 手机号相关方法 ================
// IsValidPhone 验证手机号格式
func (u *User) IsValidPhone() bool {
return IsValidPhoneFormat(u.Phone)
}
// SetPhone 设置手机号
func (u *User) SetPhone(phone string) error {
if !IsValidPhoneFormat(phone) {
return NewValidationError("手机号格式无效")
}
u.Phone = phone
return nil
}
// GetMaskedPhone 获取脱敏的手机号
func (u *User) GetMaskedPhone() string {
if len(u.Phone) < 7 {
return u.Phone
}
return u.Phone[:3] + "****" + u.Phone[len(u.Phone)-4:]
}
// ================ 私有方法 ================
// hashPassword 使用sha256+hex加密密码
func hashPassword(password string) string {
h := sha256.New()
h.Write([]byte(password))
return hex.EncodeToString(h.Sum(nil))
}
// validatePasswordStrength 只对明文做长度/强度校验
func (u *User) validatePasswordStrength(password string) error {
if len(password) < 6 {
return NewValidationError("密码长度不能少于6位")
}
if len(password) > 20 {
return NewValidationError("密码长度不能超过20位")
}
return nil
}
// IsValidPhoneFormat 验证手机号格式
func IsValidPhoneFormat(phone string) bool {
pattern := `^1[3-9]\d{9}$`
matched, _ := regexp.MatchString(pattern, phone)
return matched
}
// NewUser 创建新用户
func NewUser(phone, password string) (*User, error) {
user := &User{
Phone: phone,
UserType: string(UserTypeNormal), // 默认为普通用户
Active: true,
}
if err := user.SetPassword(password); err != nil {
return nil, err
}
return user, nil
}
// NewAdminUser 创建新管理员用户
func NewAdminUser(phone, password, username string) (*User, error) {
user := &User{
Phone: phone,
Username: username,
UserType: string(UserTypeAdmin),
Active: true,
}
if err := user.SetPassword(password); err != nil {
return nil, err
}
return user, nil
}
// TableName 指定数据库表名
func (User) TableName() string {
return "users"
}
// ================ 错误处理 ================
type ValidationError struct {
Message string
}
func (e *ValidationError) Error() string {
return e.Message
}
func NewValidationError(message string) *ValidationError {
return &ValidationError{Message: message}
}
func IsValidationError(err error) bool {
_, ok := err.(*ValidationError)
return ok
}
// ================ 领域事件定义 ================
// UserRegisteredEvent 用户注册事件
type UserRegisteredEvent struct {
UserID string `json:"user_id"`
Phone string `json:"phone"`
UserType string `json:"user_type"`
CreatedAt time.Time `json:"created_at"`
}
// UserLoggedInEvent 用户登录事件
type UserLoggedInEvent struct {
UserID string `json:"user_id"`
Phone string `json:"phone"`
IPAddress string `json:"ip_address"`
UserAgent string `json:"user_agent"`
LoginAt time.Time `json:"login_at"`
}
// UserPasswordChangedEvent 用户密码修改事件
type UserPasswordChangedEvent struct {
UserID string `json:"user_id"`
Phone string `json:"phone"`
ChangedAt time.Time `json:"changed_at"`
}
// UserActivatedEvent 用户激活事件
type UserActivatedEvent struct {
UserID string `json:"user_id"`
Phone string `json:"phone"`
ActivatedAt time.Time `json:"activated_at"`
}
// UserDeactivatedEvent 用户停用事件
type UserDeactivatedEvent struct {
UserID string `json:"user_id"`
Phone string `json:"phone"`
DeactivatedAt time.Time `json:"deactivated_at"`
}
// UserEnterpriseInfoCreatedEvent 企业信息创建事件
type UserEnterpriseInfoCreatedEvent struct {
UserID string `json:"user_id"`
EnterpriseInfoID string `json:"enterprise_info_id"`
CompanyName string `json:"company_name"`
UnifiedSocialCode string `json:"unified_social_code"`
CreatedAt time.Time `json:"created_at"`
}
// UserEnterpriseInfoUpdatedEvent 企业信息更新事件
type UserEnterpriseInfoUpdatedEvent struct {
UserID string `json:"user_id"`
EnterpriseInfoID string `json:"enterprise_info_id"`
OldCompanyName string `json:"old_company_name"`
NewCompanyName string `json:"new_company_name"`
OldUnifiedSocialCode string `json:"old_unified_social_code"`
NewUnifiedSocialCode string `json:"new_unified_social_code"`
UpdatedAt time.Time `json:"updated_at"`
}
// UserEnterpriseInfoRemovedEvent 企业信息移除事件
type UserEnterpriseInfoRemovedEvent struct {
UserID string `json:"user_id"`
EnterpriseInfoID string `json:"enterprise_info_id"`
RemovedAt time.Time `json:"removed_at"`
}