temp
This commit is contained in:
@@ -1,72 +0,0 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"tyapi-server/internal/domains/user/entities"
|
||||
)
|
||||
|
||||
// SendCodeRequest 发送验证码请求
|
||||
type SendCodeRequest struct {
|
||||
Phone string `json:"phone" binding:"required,len=11" example:"13800138000"`
|
||||
Scene entities.SMSScene `json:"scene" binding:"required,oneof=register login change_password reset_password bind unbind" example:"register"`
|
||||
}
|
||||
|
||||
// SendCodeResponse 发送验证码响应
|
||||
type SendCodeResponse struct {
|
||||
Message string `json:"message" example:"验证码发送成功"`
|
||||
ExpiresAt time.Time `json:"expires_at" example:"2024-01-01T00:05:00Z"`
|
||||
}
|
||||
|
||||
// VerifyCodeRequest 验证验证码请求
|
||||
type VerifyCodeRequest struct {
|
||||
Phone string `json:"phone" binding:"required,len=11" example:"13800138000"`
|
||||
Code string `json:"code" binding:"required,len=6" example:"123456"`
|
||||
Scene entities.SMSScene `json:"scene" binding:"required,oneof=register login change_password reset_password bind unbind" example:"register"`
|
||||
}
|
||||
|
||||
// SMSCodeResponse SMS验证码记录响应
|
||||
type SMSCodeResponse struct {
|
||||
ID string `json:"id" example:"123e4567-e89b-12d3-a456-426614174000"`
|
||||
Phone string `json:"phone" example:"13800138000"`
|
||||
Scene entities.SMSScene `json:"scene" example:"register"`
|
||||
Used bool `json:"used" example:"false"`
|
||||
ExpiresAt time.Time `json:"expires_at" example:"2024-01-01T00:05:00Z"`
|
||||
CreatedAt time.Time `json:"created_at" example:"2024-01-01T00:00:00Z"`
|
||||
}
|
||||
|
||||
// SMSCodeListRequest SMS验证码列表请求
|
||||
type SMSCodeListRequest struct {
|
||||
Phone string `form:"phone" binding:"omitempty,len=11" example:"13800138000"`
|
||||
Scene entities.SMSScene `form:"scene" binding:"omitempty,oneof=register login change_password reset_password bind unbind" example:"register"`
|
||||
Page int `form:"page" binding:"omitempty,min=1" example:"1"`
|
||||
PageSize int `form:"page_size" binding:"omitempty,min=1,max=100" example:"20"`
|
||||
}
|
||||
|
||||
// 转换方法
|
||||
func FromSMSCodeEntity(smsCode *entities.SMSCode) *SMSCodeResponse {
|
||||
if smsCode == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &SMSCodeResponse{
|
||||
ID: smsCode.ID,
|
||||
Phone: smsCode.Phone,
|
||||
Scene: smsCode.Scene,
|
||||
Used: smsCode.Used,
|
||||
ExpiresAt: smsCode.ExpiresAt,
|
||||
CreatedAt: smsCode.CreatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
func FromSMSCodeEntities(smsCodes []*entities.SMSCode) []*SMSCodeResponse {
|
||||
if smsCodes == nil {
|
||||
return []*SMSCodeResponse{}
|
||||
}
|
||||
|
||||
responses := make([]*SMSCodeResponse, len(smsCodes))
|
||||
for i, smsCode := range smsCodes {
|
||||
responses[i] = FromSMSCodeEntity(smsCode)
|
||||
}
|
||||
return responses
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"tyapi-server/internal/domains/user/entities"
|
||||
)
|
||||
|
||||
// RegisterRequest 用户注册请求
|
||||
type RegisterRequest struct {
|
||||
Phone string `json:"phone" binding:"required,len=11" example:"13800138000"`
|
||||
Password string `json:"password" binding:"required,min=6,max=128" example:"password123"`
|
||||
ConfirmPassword string `json:"confirm_password" binding:"required,eqfield=Password" example:"password123"`
|
||||
Code string `json:"code" binding:"required,len=6" example:"123456"`
|
||||
}
|
||||
|
||||
// LoginWithPasswordRequest 密码登录请求
|
||||
type LoginWithPasswordRequest struct {
|
||||
Phone string `json:"phone" binding:"required,len=11" example:"13800138000"`
|
||||
Password string `json:"password" binding:"required" example:"password123"`
|
||||
}
|
||||
|
||||
// LoginWithSMSRequest 短信验证码登录请求
|
||||
type LoginWithSMSRequest struct {
|
||||
Phone string `json:"phone" binding:"required,len=11" example:"13800138000"`
|
||||
Code string `json:"code" binding:"required,len=6" example:"123456"`
|
||||
}
|
||||
|
||||
// ChangePasswordRequest 修改密码请求
|
||||
type ChangePasswordRequest struct {
|
||||
OldPassword string `json:"old_password" binding:"required" example:"oldpassword123"`
|
||||
NewPassword string `json:"new_password" binding:"required,min=6,max=128" example:"newpassword123"`
|
||||
ConfirmNewPassword string `json:"confirm_new_password" binding:"required,eqfield=NewPassword" example:"newpassword123"`
|
||||
Code string `json:"code" binding:"required,len=6" example:"123456"`
|
||||
}
|
||||
|
||||
// UpdateProfileRequest 更新用户信息请求
|
||||
type UpdateProfileRequest struct {
|
||||
Phone string `json:"phone" binding:"omitempty,len=11" example:"13800138000"`
|
||||
// 可以在这里添加更多用户信息字段,如昵称、头像等
|
||||
}
|
||||
|
||||
// UserResponse 用户响应
|
||||
type UserResponse struct {
|
||||
ID string `json:"id" example:"123e4567-e89b-12d3-a456-426614174000"`
|
||||
Phone string `json:"phone" example:"13800138000"`
|
||||
CreatedAt time.Time `json:"created_at" example:"2024-01-01T00:00:00Z"`
|
||||
UpdatedAt time.Time `json:"updated_at" example:"2024-01-01T00:00:00Z"`
|
||||
}
|
||||
|
||||
// LoginResponse 登录响应
|
||||
type LoginResponse struct {
|
||||
User *UserResponse `json:"user"`
|
||||
AccessToken string `json:"access_token" example:"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."`
|
||||
TokenType string `json:"token_type" example:"Bearer"`
|
||||
ExpiresIn int64 `json:"expires_in" example:"86400"`
|
||||
LoginMethod string `json:"login_method" example:"password"` // password 或 sms
|
||||
}
|
||||
|
||||
// 转换方法
|
||||
func (r *RegisterRequest) ToEntity() *entities.User {
|
||||
return &entities.User{
|
||||
Phone: r.Phone,
|
||||
Password: r.Password,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *LoginWithPasswordRequest) ToEntity() *entities.User {
|
||||
return &entities.User{
|
||||
Phone: r.Phone,
|
||||
Password: r.Password,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *LoginWithSMSRequest) ToEntity() *entities.User {
|
||||
return &entities.User{
|
||||
Phone: r.Phone,
|
||||
}
|
||||
}
|
||||
|
||||
func FromEntity(user *entities.User) *UserResponse {
|
||||
if user == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &UserResponse{
|
||||
ID: user.ID,
|
||||
Phone: user.Phone,
|
||||
CreatedAt: user.CreatedAt,
|
||||
UpdatedAt: user.UpdatedAt,
|
||||
}
|
||||
}
|
||||
@@ -21,19 +21,7 @@ type EnterpriseInfo struct {
|
||||
UnifiedSocialCode string `gorm:"type:varchar(50);not null;index" json:"unified_social_code" comment:"统一社会信用代码"`
|
||||
LegalPersonName string `gorm:"type:varchar(100);not null" json:"legal_person_name" comment:"法定代表人姓名"`
|
||||
LegalPersonID string `gorm:"type:varchar(50);not null" json:"legal_person_id" comment:"法定代表人身份证号"`
|
||||
|
||||
// 认证状态 - 各环节的验证结果
|
||||
IsOCRVerified bool `gorm:"default:false" json:"is_ocr_verified" comment:"OCR验证是否通过"`
|
||||
IsFaceVerified bool `gorm:"default:false" json:"is_face_verified" comment:"人脸识别是否通过"`
|
||||
IsCertified bool `gorm:"default:false" json:"is_certified" comment:"是否已完成认证"`
|
||||
VerificationData string `gorm:"type:text" json:"verification_data,omitempty" comment:"验证数据(JSON格式)"`
|
||||
|
||||
// OCR识别结果 - 从营业执照中自动识别的信息
|
||||
OCRRawData string `gorm:"type:text" json:"ocr_raw_data,omitempty" comment:"OCR原始返回数据(JSON格式)"`
|
||||
OCRConfidence float64 `gorm:"type:decimal(5,2)" json:"ocr_confidence,omitempty" comment:"OCR识别置信度(0-1)"`
|
||||
|
||||
// 认证完成时间
|
||||
CertifiedAt *time.Time `json:"certified_at,omitempty" comment:"认证完成时间"`
|
||||
LegalPersonPhone string `gorm:"type:varchar(50);not null" json:"legal_person_phone" comment:"法定代表人手机号"`
|
||||
|
||||
// 时间戳字段
|
||||
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at" comment:"创建时间"`
|
||||
@@ -70,34 +58,6 @@ func (e *EnterpriseInfo) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsFullyVerified 检查是否已完成所有验证
|
||||
func (e *EnterpriseInfo) IsFullyVerified() bool {
|
||||
return e.IsOCRVerified && e.IsFaceVerified && e.IsCertified
|
||||
}
|
||||
|
||||
// UpdateOCRVerification 更新OCR验证状态
|
||||
func (e *EnterpriseInfo) UpdateOCRVerification(isVerified bool, rawData string, confidence float64) {
|
||||
e.IsOCRVerified = isVerified
|
||||
e.OCRRawData = rawData
|
||||
e.OCRConfidence = confidence
|
||||
}
|
||||
|
||||
// UpdateFaceVerification 更新人脸识别验证状态
|
||||
func (e *EnterpriseInfo) UpdateFaceVerification(isVerified bool) {
|
||||
e.IsFaceVerified = isVerified
|
||||
}
|
||||
|
||||
// CompleteCertification 完成认证
|
||||
func (e *EnterpriseInfo) CompleteCertification() {
|
||||
e.IsCertified = true
|
||||
now := time.Now()
|
||||
e.CertifiedAt = &now
|
||||
}
|
||||
|
||||
// IsReadOnly 检查企业信息是否只读(认证完成后不可修改)
|
||||
func (e *EnterpriseInfo) IsReadOnly() bool {
|
||||
return e.IsCertified
|
||||
}
|
||||
|
||||
// BeforeCreate GORM钩子:创建前自动生成UUID
|
||||
func (e *EnterpriseInfo) BeforeCreate(tx *gorm.DB) error {
|
||||
|
||||
@@ -41,6 +41,7 @@ const (
|
||||
SMSSceneResetPassword SMSScene = "reset_password" // 重置密码 - 忘记密码重置
|
||||
SMSSceneBind SMSScene = "bind" // 绑定手机号 - 绑定新手机号
|
||||
SMSSceneUnbind SMSScene = "unbind" // 解绑手机号 - 解绑当前手机号
|
||||
SMSSceneCertification SMSScene = "certification" // 企业认证 - 企业入驻认证
|
||||
)
|
||||
|
||||
// BeforeCreate GORM钩子:创建前自动生成UUID
|
||||
@@ -195,6 +196,7 @@ func (s *SMSCode) IsSceneValid() bool {
|
||||
SMSSceneResetPassword,
|
||||
SMSSceneBind,
|
||||
SMSSceneUnbind,
|
||||
SMSSceneCertification,
|
||||
}
|
||||
|
||||
for _, scene := range validScenes {
|
||||
@@ -214,6 +216,7 @@ func (s *SMSCode) GetSceneName() string {
|
||||
SMSSceneResetPassword: "重置密码",
|
||||
SMSSceneBind: "绑定手机号",
|
||||
SMSSceneUnbind: "解绑手机号",
|
||||
SMSSceneCertification: "企业认证",
|
||||
}
|
||||
|
||||
if name, exists := sceneNames[s.Scene]; exists {
|
||||
@@ -283,6 +286,7 @@ func IsValidScene(scene SMSScene) bool {
|
||||
SMSSceneResetPassword,
|
||||
SMSSceneBind,
|
||||
SMSSceneUnbind,
|
||||
SMSSceneCertification,
|
||||
}
|
||||
|
||||
for _, validScene := range validScenes {
|
||||
@@ -302,6 +306,7 @@ func GetSceneName(scene SMSScene) string {
|
||||
SMSSceneResetPassword: "重置密码",
|
||||
SMSSceneBind: "绑定手机号",
|
||||
SMSSceneUnbind: "解绑手机号",
|
||||
SMSSceneCertification: "企业认证",
|
||||
}
|
||||
|
||||
if name, exists := sceneNames[scene]; exists {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package entities
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"time"
|
||||
@@ -11,6 +10,14 @@ import (
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// UserType 用户类型枚举
|
||||
type UserType string
|
||||
|
||||
const (
|
||||
UserTypeNormal UserType = "user" // 普通用户
|
||||
UserTypeAdmin UserType = "admin" // 管理员
|
||||
)
|
||||
|
||||
// User 用户实体
|
||||
// 系统用户的核心信息,提供基础的账户管理功能
|
||||
// 支持手机号登录,密码加密存储,实现Entity接口便于统一管理
|
||||
@@ -20,6 +27,16 @@ type User struct {
|
||||
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:"账户是否激活"`
|
||||
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:"更新时间"`
|
||||
@@ -78,6 +95,42 @@ func (u *User) Validate() error {
|
||||
|
||||
// ================ 业务方法 ================
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// ChangePassword 修改密码
|
||||
// 验证旧密码,检查新密码强度,更新密码
|
||||
func (u *User) ChangePassword(oldPassword, newPassword, confirmPassword string) error {
|
||||
@@ -168,6 +221,11 @@ func (u *User) CanLogin() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// 如果是管理员,检查是否激活
|
||||
if u.IsAdmin() && !u.Active {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -205,7 +263,7 @@ func (u *User) GetMaskedPhone() string {
|
||||
return u.Phone[:3] + "****" + u.Phone[len(u.Phone)-4:]
|
||||
}
|
||||
|
||||
// ================ 私有辅助方法 ================
|
||||
// ================ 私有方法 ================
|
||||
|
||||
// hashPassword 加密密码
|
||||
func (u *User) hashPassword(password string) (string, error) {
|
||||
@@ -218,61 +276,30 @@ func (u *User) hashPassword(password string) (string, error) {
|
||||
|
||||
// validatePasswordStrength 验证密码强度
|
||||
func (u *User) validatePasswordStrength(password string) error {
|
||||
if len(password) < 8 {
|
||||
return NewValidationError("密码长度至少8位")
|
||||
if len(password) < 6 {
|
||||
return NewValidationError("密码长度不能少于6位")
|
||||
}
|
||||
|
||||
if len(password) > 128 {
|
||||
return NewValidationError("密码长度不能超过128位")
|
||||
if len(password) > 20 {
|
||||
return NewValidationError("密码长度不能超过20位")
|
||||
}
|
||||
|
||||
// 检查是否包含数字
|
||||
hasDigit := regexp.MustCompile(`[0-9]`).MatchString(password)
|
||||
if !hasDigit {
|
||||
return NewValidationError("密码必须包含数字")
|
||||
}
|
||||
|
||||
// 检查是否包含字母
|
||||
hasLetter := regexp.MustCompile(`[a-zA-Z]`).MatchString(password)
|
||||
if !hasLetter {
|
||||
return NewValidationError("密码必须包含字母")
|
||||
}
|
||||
|
||||
// 检查是否包含特殊字符(可选,可以根据需求调整)
|
||||
hasSpecial := regexp.MustCompile(`[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]`).MatchString(password)
|
||||
if !hasSpecial {
|
||||
return NewValidationError("密码必须包含特殊字符")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ================ 静态工具方法 ================
|
||||
|
||||
// IsValidPhoneFormat 验证手机号格式(静态方法)
|
||||
// IsValidPhoneFormat 验证手机号格式
|
||||
func IsValidPhoneFormat(phone string) bool {
|
||||
if phone == "" {
|
||||
return false
|
||||
}
|
||||
// 中国手机号验证(11位数字,以1开头)
|
||||
pattern := `^1[3-9]\d{9}$`
|
||||
matched, _ := regexp.MatchString(pattern, phone)
|
||||
return matched
|
||||
}
|
||||
|
||||
// NewUser 创建新用户(工厂方法)
|
||||
// NewUser 创建新用户
|
||||
func NewUser(phone, password string) (*User, error) {
|
||||
user := &User{
|
||||
ID: "", // 由数据库或调用方设置
|
||||
Phone: phone,
|
||||
Phone: phone,
|
||||
UserType: string(UserTypeNormal), // 默认为普通用户
|
||||
Active: true,
|
||||
}
|
||||
|
||||
// 验证手机号
|
||||
if err := user.SetPhone(phone); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 设置密码
|
||||
if err := user.SetPassword(password); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -280,41 +307,60 @@ func NewUser(phone, password string) (*User, error) {
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// TableName 指定表名
|
||||
// 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"
|
||||
}
|
||||
|
||||
// ValidationError 验证错误
|
||||
// 自定义验证错误类型,提供结构化的错误信息
|
||||
// ================ 错误处理 ================
|
||||
|
||||
type ValidationError struct {
|
||||
Message string
|
||||
}
|
||||
|
||||
// Error 实现error接口
|
||||
func (e *ValidationError) Error() string {
|
||||
return e.Message
|
||||
}
|
||||
|
||||
// NewValidationError 创建新的验证错误
|
||||
// 工厂方法,用于创建验证错误实例
|
||||
func NewValidationError(message string) *ValidationError {
|
||||
return &ValidationError{Message: message}
|
||||
}
|
||||
|
||||
// IsValidationError 检查是否为验证错误
|
||||
func IsValidationError(err error) bool {
|
||||
var validationErr *ValidationError
|
||||
return errors.As(err, &validationErr)
|
||||
_, ok := err.(*ValidationError)
|
||||
return ok
|
||||
}
|
||||
|
||||
// UserCache 用户缓存结构体
|
||||
// 专门用于缓存序列化,包含Password字段
|
||||
// ================ 缓存相关 ================
|
||||
|
||||
type UserCache struct {
|
||||
// 基础标识
|
||||
ID string `json:"id" comment:"用户唯一标识"`
|
||||
Phone string `json:"phone" comment:"手机号码(登录账号)"`
|
||||
Password string `json:"password" comment:"登录密码(加密存储)"`
|
||||
UserType string `json:"user_type" comment:"用户类型"`
|
||||
Username string `json:"username" comment:"用户名"`
|
||||
|
||||
Active bool `gorm:"default:true" json:"is_active" 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 `json:"created_at" comment:"创建时间"`
|
||||
@@ -322,24 +368,38 @@ type UserCache struct {
|
||||
DeletedAt gorm.DeletedAt `json:"deleted_at" comment:"软删除时间"`
|
||||
}
|
||||
|
||||
// ToCache 转换为缓存结构体
|
||||
// ToCache 转换为缓存结构
|
||||
func (u *User) ToCache() *UserCache {
|
||||
return &UserCache{
|
||||
ID: u.ID,
|
||||
Phone: u.Phone,
|
||||
Password: u.Password,
|
||||
UserType: u.UserType,
|
||||
Username: u.Username,
|
||||
CreatedAt: u.CreatedAt,
|
||||
UpdatedAt: u.UpdatedAt,
|
||||
DeletedAt: u.DeletedAt,
|
||||
// 补充所有字段
|
||||
// 管理员特有字段
|
||||
Active: u.Active,
|
||||
LastLoginAt: u.LastLoginAt,
|
||||
LoginCount: u.LoginCount,
|
||||
Permissions: u.Permissions,
|
||||
}
|
||||
}
|
||||
|
||||
// FromCache 从缓存结构体转换
|
||||
// FromCache 从缓存结构恢复
|
||||
func (u *User) FromCache(cache *UserCache) {
|
||||
u.ID = cache.ID
|
||||
u.Phone = cache.Phone
|
||||
u.Password = cache.Password
|
||||
u.UserType = cache.UserType
|
||||
u.Username = cache.Username
|
||||
u.CreatedAt = cache.CreatedAt
|
||||
u.UpdatedAt = cache.UpdatedAt
|
||||
u.DeletedAt = cache.DeletedAt
|
||||
u.Active = cache.Active
|
||||
u.LastLoginAt = cache.LastLoginAt
|
||||
u.LoginCount = cache.LoginCount
|
||||
u.Permissions = cache.Permissions
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@ type UserRepository interface {
|
||||
|
||||
// 基础查询 - 直接使用实体
|
||||
GetByPhone(ctx context.Context, phone string) (*entities.User, error)
|
||||
GetByUsername(ctx context.Context, username string) (*entities.User, error)
|
||||
GetByUserType(ctx context.Context, userType string) ([]*entities.User, error)
|
||||
|
||||
// 复杂查询 - 使用查询参数
|
||||
ListUsers(ctx context.Context, query *queries.ListUsersQuery) ([]*entities.User, int64, error)
|
||||
@@ -32,6 +34,7 @@ type UserRepository interface {
|
||||
CheckPassword(ctx context.Context, userID string, password string) (bool, error)
|
||||
ActivateUser(ctx context.Context, userID string) error
|
||||
DeactivateUser(ctx context.Context, userID string) error
|
||||
UpdateLoginStats(ctx context.Context, userID string) error
|
||||
|
||||
// 统计信息
|
||||
GetStats(ctx context.Context) (*UserStats, error)
|
||||
|
||||
@@ -107,11 +107,6 @@ func (s *EnterpriseService) UpdateEnterpriseInfo(ctx context.Context, userID, co
|
||||
return nil, fmt.Errorf("企业信息不存在: %w", err)
|
||||
}
|
||||
|
||||
// 检查企业信息是否已认证完成(认证完成后不可修改)
|
||||
if enterpriseInfo.IsReadOnly() {
|
||||
return nil, fmt.Errorf("企业信息已认证完成,不可修改")
|
||||
}
|
||||
|
||||
// 检查统一社会信用代码是否已被其他用户使用
|
||||
if unifiedSocialCode != enterpriseInfo.UnifiedSocialCode {
|
||||
exists, err := s.enterpriseInfoRepo.CheckUnifiedSocialCodeExists(ctx, unifiedSocialCode, userID)
|
||||
@@ -142,55 +137,6 @@ func (s *EnterpriseService) UpdateEnterpriseInfo(ctx context.Context, userID, co
|
||||
return enterpriseInfo, nil
|
||||
}
|
||||
|
||||
// UpdateOCRVerification 更新OCR验证状态
|
||||
func (s *EnterpriseService) UpdateOCRVerification(ctx context.Context, userID string, isVerified bool, rawData string, confidence float64) error {
|
||||
// 获取企业信息
|
||||
enterpriseInfo, err := s.enterpriseInfoRepo.GetByUserID(ctx, userID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("企业信息不存在: %w", err)
|
||||
}
|
||||
|
||||
// 更新OCR验证状态
|
||||
enterpriseInfo.UpdateOCRVerification(isVerified, rawData, confidence)
|
||||
|
||||
if err := s.enterpriseInfoRepo.Update(ctx, *enterpriseInfo); err != nil {
|
||||
s.logger.Error("更新OCR验证状态失败", zap.Error(err))
|
||||
return fmt.Errorf("更新OCR验证状态失败: %w", err)
|
||||
}
|
||||
|
||||
s.logger.Info("OCR验证状态更新成功",
|
||||
zap.String("user_id", userID),
|
||||
zap.Bool("is_verified", isVerified),
|
||||
zap.Float64("confidence", confidence),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateFaceVerification 更新人脸识别验证状态
|
||||
func (s *EnterpriseService) UpdateFaceVerification(ctx context.Context, userID string, isVerified bool) error {
|
||||
// 获取企业信息
|
||||
enterpriseInfo, err := s.enterpriseInfoRepo.GetByUserID(ctx, userID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("企业信息不存在: %w", err)
|
||||
}
|
||||
|
||||
// 更新人脸识别验证状态
|
||||
enterpriseInfo.UpdateFaceVerification(isVerified)
|
||||
|
||||
if err := s.enterpriseInfoRepo.Update(ctx, *enterpriseInfo); err != nil {
|
||||
s.logger.Error("更新人脸识别验证状态失败", zap.Error(err))
|
||||
return fmt.Errorf("更新人脸识别验证状态失败: %w", err)
|
||||
}
|
||||
|
||||
s.logger.Info("人脸识别验证状态更新成功",
|
||||
zap.String("user_id", userID),
|
||||
zap.Bool("is_verified", isVerified),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CompleteEnterpriseCertification 完成企业认证
|
||||
func (s *EnterpriseService) CompleteEnterpriseCertification(ctx context.Context, userID string) error {
|
||||
// 获取企业信息
|
||||
@@ -199,14 +145,6 @@ func (s *EnterpriseService) CompleteEnterpriseCertification(ctx context.Context,
|
||||
return fmt.Errorf("企业信息不存在: %w", err)
|
||||
}
|
||||
|
||||
// 检查是否已完成所有验证
|
||||
if !enterpriseInfo.IsOCRVerified || !enterpriseInfo.IsFaceVerified {
|
||||
return fmt.Errorf("企业信息验证未完成,无法完成认证")
|
||||
}
|
||||
|
||||
// 完成认证
|
||||
enterpriseInfo.CompleteCertification()
|
||||
|
||||
if err := s.enterpriseInfoRepo.Update(ctx, *enterpriseInfo); err != nil {
|
||||
s.logger.Error("完成企业认证失败", zap.Error(err))
|
||||
return fmt.Errorf("完成企业认证失败: %w", err)
|
||||
@@ -264,17 +202,6 @@ func (s *EnterpriseService) GetEnterpriseInfoByUnifiedSocialCode(ctx context.Con
|
||||
return s.enterpriseInfoRepo.GetByUnifiedSocialCode(ctx, unifiedSocialCode)
|
||||
}
|
||||
|
||||
// IsEnterpriseCertified 检查用户是否已完成企业认证
|
||||
func (s *EnterpriseService) IsEnterpriseCertified(ctx context.Context, userID string) (bool, error) {
|
||||
enterpriseInfo, err := s.enterpriseInfoRepo.GetByUserID(ctx, userID)
|
||||
if err != nil {
|
||||
// 没有企业信息,认为未认证
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return enterpriseInfo.IsFullyVerified(), nil
|
||||
}
|
||||
|
||||
// GetEnterpriseCertificationStatus 获取企业认证状态
|
||||
func (s *EnterpriseService) GetEnterpriseCertificationStatus(ctx context.Context, userID string) (map[string]interface{}, error) {
|
||||
enterpriseInfo, err := s.enterpriseInfoRepo.GetByUserID(ctx, userID)
|
||||
@@ -288,11 +215,6 @@ func (s *EnterpriseService) GetEnterpriseCertificationStatus(ctx context.Context
|
||||
|
||||
status := map[string]interface{}{
|
||||
"has_enterprise_info": true,
|
||||
"is_certified": enterpriseInfo.IsFullyVerified(),
|
||||
"is_readonly": enterpriseInfo.IsReadOnly(),
|
||||
"ocr_verified": enterpriseInfo.IsOCRVerified,
|
||||
"face_verified": enterpriseInfo.IsFaceVerified,
|
||||
"certified_at": enterpriseInfo.CertifiedAt,
|
||||
"company_name": enterpriseInfo.CompanyName,
|
||||
"unified_social_code": enterpriseInfo.UnifiedSocialCode,
|
||||
"legal_person_name": enterpriseInfo.LegalPersonName,
|
||||
|
||||
130
internal/domains/user/services/user_auth_service.go
Normal file
130
internal/domains/user/services/user_auth_service.go
Normal file
@@ -0,0 +1,130 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"tyapi-server/internal/domains/user/entities"
|
||||
"tyapi-server/internal/domains/user/repositories"
|
||||
)
|
||||
|
||||
// UserAuthService 用户认证领域服务
|
||||
// 负责用户认证相关的业务逻辑,包括密码验证、登录状态管理等
|
||||
type UserAuthService struct {
|
||||
userRepo repositories.UserRepository
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewUserAuthService 创建用户认证领域服务
|
||||
func NewUserAuthService(
|
||||
userRepo repositories.UserRepository,
|
||||
logger *zap.Logger,
|
||||
) *UserAuthService {
|
||||
return &UserAuthService{
|
||||
userRepo: userRepo,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// ValidatePassword 验证用户密码
|
||||
func (s *UserAuthService) ValidatePassword(ctx context.Context, phone, password string) (*entities.User, error) {
|
||||
user, err := s.userRepo.GetByPhone(ctx, phone)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("用户名或密码错误")
|
||||
}
|
||||
|
||||
if !user.CanLogin() {
|
||||
return nil, fmt.Errorf("用户状态异常,无法登录")
|
||||
}
|
||||
|
||||
if !user.CheckPassword(password) {
|
||||
return nil, fmt.Errorf("用户名或密码错误")
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// ValidateUserLogin 验证用户登录状态
|
||||
func (s *UserAuthService) ValidateUserLogin(ctx context.Context, phone string) (*entities.User, error) {
|
||||
user, err := s.userRepo.GetByPhone(ctx, phone)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("用户不存在")
|
||||
}
|
||||
|
||||
if !user.CanLogin() {
|
||||
return nil, fmt.Errorf("用户状态异常,无法登录")
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// ChangePassword 修改用户密码
|
||||
func (s *UserAuthService) ChangePassword(ctx context.Context, userID, oldPassword, newPassword string) error {
|
||||
user, err := s.userRepo.GetByID(ctx, userID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("用户不存在: %w", err)
|
||||
}
|
||||
|
||||
if err := user.ChangePassword(oldPassword, newPassword, newPassword); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.userRepo.Update(ctx, user); err != nil {
|
||||
s.logger.Error("密码修改失败", zap.Error(err))
|
||||
return fmt.Errorf("密码修改失败: %w", err)
|
||||
}
|
||||
|
||||
s.logger.Info("密码修改成功",
|
||||
zap.String("user_id", userID),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ResetPassword 重置用户密码
|
||||
func (s *UserAuthService) ResetPassword(ctx context.Context, phone, newPassword string) error {
|
||||
user, err := s.userRepo.GetByPhone(ctx, phone)
|
||||
if err != nil {
|
||||
return fmt.Errorf("用户不存在: %w", err)
|
||||
}
|
||||
|
||||
if err := user.ResetPassword(newPassword, newPassword); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.userRepo.Update(ctx, *user); err != nil {
|
||||
s.logger.Error("密码重置失败", zap.Error(err))
|
||||
return fmt.Errorf("密码重置失败: %w", err)
|
||||
}
|
||||
|
||||
s.logger.Info("密码重置成功",
|
||||
zap.String("user_id", user.ID),
|
||||
zap.String("phone", user.Phone),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetUserPermissions 获取用户权限
|
||||
func (s *UserAuthService) GetUserPermissions(ctx context.Context, user *entities.User) ([]string, error) {
|
||||
if !user.IsAdmin() {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
// 这里可以根据用户角色返回不同的权限
|
||||
// 目前返回默认的管理员权限
|
||||
permissions := []string{
|
||||
"user:read",
|
||||
"user:write",
|
||||
"product:read",
|
||||
"product:write",
|
||||
"certification:read",
|
||||
"certification:write",
|
||||
"finance:read",
|
||||
"finance:write",
|
||||
}
|
||||
|
||||
return permissions, nil
|
||||
}
|
||||
128
internal/domains/user/services/user_management_service.go
Normal file
128
internal/domains/user/services/user_management_service.go
Normal file
@@ -0,0 +1,128 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"tyapi-server/internal/domains/user/entities"
|
||||
"tyapi-server/internal/domains/user/repositories"
|
||||
)
|
||||
|
||||
// UserManagementService 用户管理领域服务
|
||||
// 负责用户的基本管理操作,包括创建、查询、更新等
|
||||
type UserManagementService struct {
|
||||
userRepo repositories.UserRepository
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewUserManagementService 创建用户管理领域服务
|
||||
func NewUserManagementService(
|
||||
userRepo repositories.UserRepository,
|
||||
logger *zap.Logger,
|
||||
) *UserManagementService {
|
||||
return &UserManagementService{
|
||||
userRepo: userRepo,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateUser 创建用户
|
||||
func (s *UserManagementService) CreateUser(ctx context.Context, phone, password string) (*entities.User, error) {
|
||||
// 检查手机号是否已注册
|
||||
exists, err := s.IsPhoneRegistered(ctx, phone)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("检查手机号失败: %w", err)
|
||||
}
|
||||
if exists {
|
||||
return nil, fmt.Errorf("手机号已注册")
|
||||
}
|
||||
|
||||
// 创建用户
|
||||
user, err := entities.NewUser(phone, password)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("创建用户失败: %w", err)
|
||||
}
|
||||
|
||||
createdUser, err := s.userRepo.Create(ctx, *user)
|
||||
if err != nil {
|
||||
s.logger.Error("创建用户失败", zap.Error(err))
|
||||
return nil, fmt.Errorf("创建用户失败: %w", err)
|
||||
}
|
||||
|
||||
s.logger.Info("用户创建成功",
|
||||
zap.String("user_id", user.ID),
|
||||
zap.String("phone", user.Phone),
|
||||
)
|
||||
|
||||
return &createdUser, nil
|
||||
}
|
||||
|
||||
// GetUserByID 根据ID获取用户信息
|
||||
func (s *UserManagementService) GetUserByID(ctx context.Context, userID string) (*entities.User, error) {
|
||||
user, err := s.userRepo.GetByID(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("用户不存在: %w", err)
|
||||
}
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
// GetUserByPhone 根据手机号获取用户信息
|
||||
func (s *UserManagementService) GetUserByPhone(ctx context.Context, phone string) (*entities.User, error) {
|
||||
user, err := s.userRepo.GetByPhone(ctx, phone)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("用户不存在: %w", err)
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// UpdateUser 更新用户信息
|
||||
func (s *UserManagementService) UpdateUser(ctx context.Context, user *entities.User) error {
|
||||
if err := s.userRepo.Update(ctx, *user); err != nil {
|
||||
s.logger.Error("更新用户信息失败", zap.Error(err))
|
||||
return fmt.Errorf("更新用户信息失败: %w", err)
|
||||
}
|
||||
|
||||
s.logger.Info("用户信息更新成功",
|
||||
zap.String("user_id", user.ID),
|
||||
zap.String("phone", user.Phone),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsPhoneRegistered 检查手机号是否已注册
|
||||
func (s *UserManagementService) IsPhoneRegistered(ctx context.Context, phone string) (bool, error) {
|
||||
_, err := s.userRepo.GetByPhone(ctx, phone)
|
||||
if err != nil {
|
||||
return false, nil // 用户不存在,可以注册
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// ValidateUser 验证用户信息
|
||||
func (s *UserManagementService) ValidateUser(ctx context.Context, userID string) error {
|
||||
user, err := s.userRepo.GetByID(ctx, userID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("用户不存在: %w", err)
|
||||
}
|
||||
|
||||
// 这里可以添加更多的用户验证逻辑
|
||||
if user.Phone == "" {
|
||||
return fmt.Errorf("用户手机号不能为空")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateLoginStats 更新登录统计
|
||||
func (s *UserManagementService) UpdateLoginStats(ctx context.Context, userID string) error {
|
||||
if err := s.userRepo.UpdateLoginStats(ctx, userID); err != nil {
|
||||
s.logger.Error("更新登录统计失败", zap.Error(err))
|
||||
return fmt.Errorf("更新登录统计失败: %w", err)
|
||||
}
|
||||
|
||||
s.logger.Info("登录统计更新成功", zap.String("user_id", userID))
|
||||
return nil
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"tyapi-server/internal/domains/user/entities"
|
||||
"tyapi-server/internal/domains/user/repositories"
|
||||
)
|
||||
|
||||
// UserService 用户领域服务
|
||||
type UserService struct {
|
||||
userRepo repositories.UserRepository
|
||||
enterpriseService *EnterpriseService
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewUserService 创建用户领域服务
|
||||
func NewUserService(
|
||||
userRepo repositories.UserRepository,
|
||||
enterpriseService *EnterpriseService,
|
||||
logger *zap.Logger,
|
||||
) *UserService {
|
||||
return &UserService{
|
||||
userRepo: userRepo,
|
||||
enterpriseService: enterpriseService,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// IsPhoneRegistered 检查手机号是否已注册
|
||||
func (s *UserService) IsPhoneRegistered(ctx context.Context, phone string) (bool, error) {
|
||||
_, err := s.userRepo.GetByPhone(ctx, phone)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// GetUserWithEnterpriseInfo 获取用户信息(包含企业信息)
|
||||
func (s *UserService) GetUserWithEnterpriseInfo(ctx context.Context, userID string) (*entities.User, error) {
|
||||
// 通过企业服务获取用户信息(包含企业信息)
|
||||
return s.enterpriseService.GetUserWithEnterpriseInfo(ctx, userID)
|
||||
}
|
||||
|
||||
// GetUserByID 根据ID获取用户信息
|
||||
func (s *UserService) GetUserByID(ctx context.Context, userID string) (*entities.User, error) {
|
||||
user, err := s.userRepo.GetByID(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("用户不存在: %w", err)
|
||||
}
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
// GetUserByPhone 根据手机号获取用户信息
|
||||
func (s *UserService) GetUserByPhone(ctx context.Context, phone string) (*entities.User, error) {
|
||||
user, err := s.userRepo.GetByPhone(ctx, phone)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("用户不存在: %w", err)
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// UpdateUser 更新用户信息
|
||||
func (s *UserService) UpdateUser(ctx context.Context, user *entities.User) error {
|
||||
if err := s.userRepo.Update(ctx, *user); err != nil {
|
||||
s.logger.Error("更新用户信息失败", zap.Error(err))
|
||||
return fmt.Errorf("更新用户信息失败: %w", err)
|
||||
}
|
||||
|
||||
s.logger.Info("用户信息更新成功",
|
||||
zap.String("user_id", user.ID),
|
||||
zap.String("phone", user.Phone),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ChangePassword 修改用户密码
|
||||
func (s *UserService) ChangePassword(ctx context.Context, userID, oldPassword, newPassword string) error {
|
||||
user, err := s.userRepo.GetByID(ctx, userID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("用户不存在: %w", err)
|
||||
}
|
||||
|
||||
if err := user.ChangePassword(oldPassword, newPassword, newPassword); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.userRepo.Update(ctx, user); err != nil {
|
||||
s.logger.Error("密码修改失败", zap.Error(err))
|
||||
return fmt.Errorf("密码修改失败: %w", err)
|
||||
}
|
||||
|
||||
s.logger.Info("密码修改成功",
|
||||
zap.String("user_id", userID),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateUser 验证用户信息
|
||||
func (s *UserService) ValidateUser(ctx context.Context, userID string) error {
|
||||
user, err := s.userRepo.GetByID(ctx, userID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("用户不存在: %w", err)
|
||||
}
|
||||
|
||||
// 这里可以添加更多的用户验证逻辑
|
||||
if user.Phone == "" {
|
||||
return fmt.Errorf("用户手机号不能为空")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetUserStats 获取用户统计信息
|
||||
func (s *UserService) GetUserStats(ctx context.Context) (map[string]interface{}, error) {
|
||||
// 这里可以添加用户统计逻辑
|
||||
stats := map[string]interface{}{
|
||||
"total_users": 0, // 需要实现具体的统计逻辑
|
||||
"active_users": 0,
|
||||
"new_users_today": 0,
|
||||
}
|
||||
|
||||
return stats, nil
|
||||
}
|
||||
Reference in New Issue
Block a user