2025-06-30 19:21:56 +08:00
|
|
|
|
package entities
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
2025-07-28 01:46:39 +08:00
|
|
|
|
"crypto/sha256"
|
|
|
|
|
|
"encoding/hex"
|
2025-07-11 21:05:58 +08:00
|
|
|
|
"fmt"
|
|
|
|
|
|
"regexp"
|
2025-06-30 19:21:56 +08:00
|
|
|
|
"time"
|
|
|
|
|
|
|
2025-07-13 16:36:20 +08:00
|
|
|
|
"github.com/google/uuid"
|
2025-06-30 19:21:56 +08:00
|
|
|
|
"gorm.io/gorm"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2025-07-20 20:53:26 +08:00
|
|
|
|
// UserType 用户类型枚举
|
|
|
|
|
|
type UserType string
|
|
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
|
UserTypeNormal UserType = "user" // 普通用户
|
|
|
|
|
|
UserTypeAdmin UserType = "admin" // 管理员
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
|
// User 用户聚合根
|
2025-07-11 21:05:58 +08:00
|
|
|
|
// 系统用户的核心信息,提供基础的账户管理功能
|
|
|
|
|
|
// 支持手机号登录,密码加密存储,实现Entity接口便于统一管理
|
2025-06-30 19:21:56 +08:00
|
|
|
|
type User struct {
|
2025-07-11 21:05:58 +08:00
|
|
|
|
// 基础标识
|
|
|
|
|
|
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:"登录密码(加密存储,不返回前端)"`
|
|
|
|
|
|
|
2025-07-20 20:53:26 +08:00
|
|
|
|
// 用户类型和基本信息
|
|
|
|
|
|
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:"账户是否激活"`
|
2025-07-28 01:46:39 +08:00
|
|
|
|
IsCertified bool `gorm:"default:false" json:"is_certified" comment:"是否完成认证"`
|
2025-07-20 20:53:26 +08:00
|
|
|
|
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格式存储)"`
|
|
|
|
|
|
|
2025-07-11 21:05:58 +08:00
|
|
|
|
// 时间戳字段
|
|
|
|
|
|
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:"软删除时间"`
|
2025-07-13 16:36:20 +08:00
|
|
|
|
|
|
|
|
|
|
// 关联关系
|
|
|
|
|
|
EnterpriseInfo *EnterpriseInfo `gorm:"foreignKey:UserID" json:"enterprise_info,omitempty" comment:"企业信息(认证后获得)"`
|
2025-07-28 01:46:39 +08:00
|
|
|
|
|
|
|
|
|
|
// 领域事件 (不持久化)
|
|
|
|
|
|
domainEvents []interface{} `gorm:"-" json:"-"`
|
2025-07-13 16:36:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// BeforeCreate GORM钩子:创建前自动生成UUID
|
|
|
|
|
|
func (u *User) BeforeCreate(tx *gorm.DB) error {
|
|
|
|
|
|
if u.ID == "" {
|
|
|
|
|
|
u.ID = uuid.New().String()
|
|
|
|
|
|
}
|
|
|
|
|
|
return nil
|
2025-06-30 19:21:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-11 21:05:58 +08:00
|
|
|
|
// 实现 Entity 接口 - 提供统一的实体管理接口
|
|
|
|
|
|
// GetID 获取实体唯一标识
|
2025-06-30 19:21:56 +08:00
|
|
|
|
func (u *User) GetID() string {
|
|
|
|
|
|
return u.ID
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-11 21:05:58 +08:00
|
|
|
|
// GetCreatedAt 获取创建时间
|
2025-06-30 19:21:56 +08:00
|
|
|
|
func (u *User) GetCreatedAt() time.Time {
|
|
|
|
|
|
return u.CreatedAt
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-11 21:05:58 +08:00
|
|
|
|
// GetUpdatedAt 获取更新时间
|
2025-06-30 19:21:56 +08:00
|
|
|
|
func (u *User) GetUpdatedAt() time.Time {
|
|
|
|
|
|
return u.UpdatedAt
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-11 21:05:58 +08:00
|
|
|
|
// Validate 验证用户信息
|
|
|
|
|
|
// 检查用户必填字段是否完整,确保数据的有效性
|
2025-06-30 19:21:56 +08:00
|
|
|
|
func (u *User) Validate() error {
|
2025-07-02 16:17:59 +08:00
|
|
|
|
if u.Phone == "" {
|
|
|
|
|
|
return NewValidationError("手机号不能为空")
|
2025-06-30 19:21:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
if u.Password == "" {
|
2025-07-02 16:17:59 +08:00
|
|
|
|
return NewValidationError("密码不能为空")
|
2025-06-30 19:21:56 +08:00
|
|
|
|
}
|
2025-07-11 21:05:58 +08:00
|
|
|
|
|
|
|
|
|
|
// 验证手机号格式
|
|
|
|
|
|
if !u.IsValidPhone() {
|
|
|
|
|
|
return NewValidationError("手机号格式无效")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// CompleteCertification 完成认证
|
|
|
|
|
|
func (u *User) CompleteCertification() error {
|
|
|
|
|
|
u.IsCertified = true
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ================ 企业信息管理方法 ================
|
|
|
|
|
|
|
|
|
|
|
|
// CreateEnterpriseInfo 创建企业信息
|
2025-07-30 00:51:22 +08:00
|
|
|
|
func (u *User) CreateEnterpriseInfo(companyName, unifiedSocialCode, legalPersonName, legalPersonID, legalPersonPhone, enterpriseAddress string) error {
|
2025-07-28 01:46:39 +08:00
|
|
|
|
// 检查是否已有企业信息
|
|
|
|
|
|
if u.EnterpriseInfo != nil {
|
|
|
|
|
|
return fmt.Errorf("用户已有企业信息")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 创建企业信息实体
|
2025-07-30 00:51:22 +08:00
|
|
|
|
enterpriseInfo, err := NewEnterpriseInfo(u.ID, companyName, unifiedSocialCode, legalPersonName, legalPersonID, legalPersonPhone, enterpriseAddress)
|
2025-07-28 01:46:39 +08:00
|
|
|
|
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 更新企业信息
|
2025-07-30 00:51:22 +08:00
|
|
|
|
func (u *User) UpdateEnterpriseInfo(companyName, unifiedSocialCode, legalPersonName, legalPersonID, legalPersonPhone, enterpriseAddress string) error {
|
2025-07-28 01:46:39 +08:00
|
|
|
|
// 检查是否有企业信息
|
|
|
|
|
|
if u.EnterpriseInfo == nil {
|
|
|
|
|
|
return fmt.Errorf("用户暂无企业信息")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 记录原始值用于事件
|
|
|
|
|
|
oldCompanyName := u.EnterpriseInfo.CompanyName
|
|
|
|
|
|
oldUnifiedSocialCode := u.EnterpriseInfo.UnifiedSocialCode
|
|
|
|
|
|
|
|
|
|
|
|
// 更新企业信息
|
2025-07-30 00:51:22 +08:00
|
|
|
|
err := u.EnterpriseInfo.UpdateEnterpriseInfo(companyName, unifiedSocialCode, legalPersonName, legalPersonID, legalPersonPhone, enterpriseAddress)
|
2025-07-28 01:46:39 +08:00
|
|
|
|
if err != nil {
|
2025-07-11 21:05:58 +08:00
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
|
// 添加领域事件
|
|
|
|
|
|
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("已删除用户不能是激活状态")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-11 21:05:58 +08:00
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
|
// ================ 领域事件管理 ================
|
|
|
|
|
|
|
|
|
|
|
|
// 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)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-11 21:05:58 +08:00
|
|
|
|
// ================ 业务方法 ================
|
|
|
|
|
|
|
2025-07-20 20:53:26 +08:00
|
|
|
|
// 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
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-11 21:05:58 +08:00
|
|
|
|
// CheckPassword 验证密码是否正确
|
|
|
|
|
|
func (u *User) CheckPassword(password string) bool {
|
2025-07-28 01:46:39 +08:00
|
|
|
|
return u.Password == hashPassword(password)
|
2025-07-11 21:05:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// SetPassword 设置密码(用于注册或重置密码)
|
|
|
|
|
|
func (u *User) SetPassword(password string) error {
|
2025-07-28 01:46:39 +08:00
|
|
|
|
// 只对明文做强度校验
|
2025-07-11 21:05:58 +08:00
|
|
|
|
if err := u.validatePasswordStrength(password); err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
2025-07-28 01:46:39 +08:00
|
|
|
|
u.Password = hashPassword(password)
|
2025-07-11 21:05:58 +08:00
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-15 13:21:34 +08:00
|
|
|
|
// ResetPassword 重置密码(忘记密码时使用)
|
|
|
|
|
|
func (u *User) ResetPassword(newPassword, confirmPassword string) error {
|
|
|
|
|
|
if newPassword != confirmPassword {
|
|
|
|
|
|
return NewValidationError("新密码和确认新密码不匹配")
|
|
|
|
|
|
}
|
|
|
|
|
|
if err := u.validatePasswordStrength(newPassword); err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
2025-07-28 01:46:39 +08:00
|
|
|
|
u.Password = hashPassword(newPassword)
|
2025-07-15 13:21:34 +08:00
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-11 21:05:58 +08:00
|
|
|
|
// CanLogin 检查用户是否可以登录
|
|
|
|
|
|
func (u *User) CanLogin() bool {
|
|
|
|
|
|
// 检查用户是否被删除
|
|
|
|
|
|
if !u.DeletedAt.Time.IsZero() {
|
|
|
|
|
|
return false
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查必要字段是否存在
|
|
|
|
|
|
if u.Phone == "" || u.Password == "" {
|
|
|
|
|
|
return false
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-20 20:53:26 +08:00
|
|
|
|
// 如果是管理员,检查是否激活
|
|
|
|
|
|
if u.IsAdmin() && !u.Active {
|
|
|
|
|
|
return false
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-11 21:05:58 +08:00
|
|
|
|
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:]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-20 20:53:26 +08:00
|
|
|
|
// ================ 私有方法 ================
|
2025-07-11 21:05:58 +08:00
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
|
// hashPassword 使用sha256+hex加密密码
|
|
|
|
|
|
func hashPassword(password string) string {
|
|
|
|
|
|
h := sha256.New()
|
|
|
|
|
|
h.Write([]byte(password))
|
|
|
|
|
|
return hex.EncodeToString(h.Sum(nil))
|
2025-07-11 21:05:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
|
// validatePasswordStrength 只对明文做长度/强度校验
|
2025-07-11 21:05:58 +08:00
|
|
|
|
func (u *User) validatePasswordStrength(password string) error {
|
2025-07-20 20:53:26 +08:00
|
|
|
|
if len(password) < 6 {
|
|
|
|
|
|
return NewValidationError("密码长度不能少于6位")
|
2025-07-11 21:05:58 +08:00
|
|
|
|
}
|
2025-07-20 20:53:26 +08:00
|
|
|
|
if len(password) > 20 {
|
|
|
|
|
|
return NewValidationError("密码长度不能超过20位")
|
2025-07-11 21:05:58 +08:00
|
|
|
|
}
|
2025-06-30 19:21:56 +08:00
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-20 20:53:26 +08:00
|
|
|
|
// IsValidPhoneFormat 验证手机号格式
|
2025-07-11 21:05:58 +08:00
|
|
|
|
func IsValidPhoneFormat(phone string) bool {
|
|
|
|
|
|
pattern := `^1[3-9]\d{9}$`
|
|
|
|
|
|
matched, _ := regexp.MatchString(pattern, phone)
|
|
|
|
|
|
return matched
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-20 20:53:26 +08:00
|
|
|
|
// NewUser 创建新用户
|
2025-07-11 21:05:58 +08:00
|
|
|
|
func NewUser(phone, password string) (*User, error) {
|
|
|
|
|
|
user := &User{
|
2025-07-20 20:53:26 +08:00
|
|
|
|
Phone: phone,
|
|
|
|
|
|
UserType: string(UserTypeNormal), // 默认为普通用户
|
|
|
|
|
|
Active: true,
|
2025-07-11 21:05:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-20 20:53:26 +08:00
|
|
|
|
if err := user.SetPassword(password); err != nil {
|
2025-07-11 21:05:58 +08:00
|
|
|
|
return nil, err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-20 20:53:26 +08:00
|
|
|
|
return user, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// NewAdminUser 创建新管理员用户
|
|
|
|
|
|
func NewAdminUser(phone, password, username string) (*User, error) {
|
|
|
|
|
|
user := &User{
|
|
|
|
|
|
Phone: phone,
|
|
|
|
|
|
Username: username,
|
|
|
|
|
|
UserType: string(UserTypeAdmin),
|
|
|
|
|
|
Active: true,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-11 21:05:58 +08:00
|
|
|
|
if err := user.SetPassword(password); err != nil {
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return user, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-20 20:53:26 +08:00
|
|
|
|
// TableName 指定数据库表名
|
2025-06-30 19:21:56 +08:00
|
|
|
|
func (User) TableName() string {
|
|
|
|
|
|
return "users"
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-20 20:53:26 +08:00
|
|
|
|
// ================ 错误处理 ================
|
|
|
|
|
|
|
2025-06-30 19:21:56 +08:00
|
|
|
|
type ValidationError struct {
|
|
|
|
|
|
Message string
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (e *ValidationError) Error() string {
|
|
|
|
|
|
return e.Message
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func NewValidationError(message string) *ValidationError {
|
|
|
|
|
|
return &ValidationError{Message: message}
|
|
|
|
|
|
}
|
2025-07-11 21:05:58 +08:00
|
|
|
|
|
|
|
|
|
|
func IsValidationError(err error) bool {
|
2025-07-20 20:53:26 +08:00
|
|
|
|
_, ok := err.(*ValidationError)
|
|
|
|
|
|
return ok
|
2025-07-11 21:05:58 +08:00
|
|
|
|
}
|
2025-07-13 16:36:20 +08:00
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
|
// ================ 领域事件定义 ================
|
2025-07-20 20:53:26 +08:00
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
|
// UserRegisteredEvent 用户注册事件
|
|
|
|
|
|
type UserRegisteredEvent struct {
|
|
|
|
|
|
UserID string `json:"user_id"`
|
|
|
|
|
|
Phone string `json:"phone"`
|
|
|
|
|
|
UserType string `json:"user_type"`
|
|
|
|
|
|
CreatedAt time.Time `json:"created_at"`
|
|
|
|
|
|
}
|
2025-07-20 20:53:26 +08:00
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
|
// 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"`
|
|
|
|
|
|
}
|
2025-07-13 16:36:20 +08:00
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
|
// UserPasswordChangedEvent 用户密码修改事件
|
|
|
|
|
|
type UserPasswordChangedEvent struct {
|
|
|
|
|
|
UserID string `json:"user_id"`
|
|
|
|
|
|
Phone string `json:"phone"`
|
|
|
|
|
|
ChangedAt time.Time `json:"changed_at"`
|
2025-07-13 16:36:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
|
// 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"`
|
2025-07-13 16:36:20 +08:00
|
|
|
|
}
|