diff --git a/api/admin/crm/customerdomain/customer.api b/api/admin/crm/customerdomain/customer.api index 78fad142..81d2a93e 100644 --- a/api/admin/crm/customerdomain/customer.api +++ b/api/admin/crm/customerdomain/customer.api @@ -66,6 +66,7 @@ type ( Name string `json:"name"` Mobile string `json:"mobile"` Email string `json:"email,optional"` + UUID string `json:"uuid,optional"` Inviter *CustomerInviter `json:"inviter,optional"` InviterId int64 `json:"inviter,optional"` Source int `json:"source,optional"` diff --git a/api/web/customerdomain/auth.api b/api/web/customerdomain/auth.api index 4fb971b7..bc1c6baa 100644 --- a/api/web/customerdomain/auth.api +++ b/api/web/customerdomain/auth.api @@ -50,6 +50,7 @@ type ( Phone string `json:"phone"` Password string `json:"password"` VerifyCode string `json:"verifyCode"` + InviteCode string `json:"inviteCode,optional"` } CustomerRegisterByPhoneReply { diff --git a/internal/logic/admin/crm/customerdomain/customer/createcustomerlogic.go b/internal/logic/admin/crm/customerdomain/customer/createcustomerlogic.go index 8e94ea18..aece7050 100644 --- a/internal/logic/admin/crm/customerdomain/customer/createcustomerlogic.go +++ b/internal/logic/admin/crm/customerdomain/customer/createcustomerlogic.go @@ -4,8 +4,8 @@ import ( "PowerX/internal/model/crm/customerdomain" "PowerX/internal/svc" "PowerX/internal/types" + "PowerX/pkg/securityx" "context" - "github.com/zeromicro/go-zero/core/logx" ) @@ -29,6 +29,7 @@ func (l *CreateCustomerLogic) CreateCustomer(req *types.CreateCustomerRequest) ( Name: req.Name, Mobile: req.Mobile, Email: req.Email, + Uuid: securityx.GenerateUUID(), InviterId: req.InviterId, Source: req.Source, Type: req.Type, @@ -46,19 +47,20 @@ func (l *CreateCustomerLogic) CreateCustomer(req *types.CreateCustomerRequest) ( func TransformRequestToCustomer(customerRequest *types.Customer) (mdlCustomer *customerdomain.Customer) { mdlCustomer = &customerdomain.Customer{ - Name: customerRequest.Name, - Mobile: customerRequest.Mobile, + Name: customerRequest.Name, + //Mobile: customerRequest.Mobile, Email: customerRequest.Email, InviterId: customerRequest.InviterId, Source: customerRequest.Source, Type: customerRequest.Type, IsActivated: customerRequest.IsActivated, - ExternalId: customerdomain.ExternalId{ - OpenIdInMiniProgram: customerRequest.CustomerExternalId.OpenIdInMiniProgram, - OpenIdInWeChatOfficialAccount: customerRequest.CustomerExternalId.OpenIdInWeChatOfficialAccount, - OpenIdInWeCom: customerRequest.CustomerExternalId.OpenIdInWeCom, - }, + //ExternalId: customerdomain.ExternalId{ + // OpenIdInMiniProgram: customerRequest.CustomerExternalId.OpenIdInMiniProgram, + // OpenIdInWeChatOfficialAccount: customerRequest.CustomerExternalId.OpenIdInWeChatOfficialAccount, + // OpenIdInWeCom: customerRequest.CustomerExternalId.OpenIdInWeCom, + //}, } + mdlCustomer.Id = customerRequest.Id return mdlCustomer diff --git a/internal/logic/admin/crm/customerdomain/customer/getcustomerlogic.go b/internal/logic/admin/crm/customerdomain/customer/getcustomerlogic.go index e0683566..39d98506 100644 --- a/internal/logic/admin/crm/customerdomain/customer/getcustomerlogic.go +++ b/internal/logic/admin/crm/customerdomain/customer/getcustomerlogic.go @@ -3,7 +3,6 @@ package customer import ( "PowerX/internal/model/crm/customerdomain" "PowerX/internal/types/errorx" - "PowerX/pkg/securityx" "context" "PowerX/internal/svc" @@ -51,16 +50,17 @@ func TransformCustomerToReply(svcCtx *svc.ServiceContext, mdlCustomer *customerd mobile := mdlCustomer.Mobile openIdInMiniProgram := mdlCustomer.OpenIdInMiniProgram - if svcCtx.Config.PowerXDatabase.SeedCommerceData { - mobile = securityx.MaskMobile(mobile) - openIdInMiniProgram = securityx.MaskName(openIdInMiniProgram, 20) - } + //if svcCtx.Config.PowerXDatabase.SeedCommerceData { + // mobile = securityx.MaskMobile(mobile) + // openIdInMiniProgram = securityx.MaskName(openIdInMiniProgram, 20) + //} return &types.Customer{ Id: mdlCustomer.Id, Name: mdlCustomer.Name, Mobile: mobile, Email: mdlCustomer.Email, + UUID: mdlCustomer.Uuid, InviterId: mdlCustomer.InviterId, Source: mdlCustomer.Source, Type: mdlCustomer.Type, diff --git a/internal/logic/admin/crm/customerdomain/customer/patchcustomerlogic.go b/internal/logic/admin/crm/customerdomain/customer/patchcustomerlogic.go index d09a02a7..7e0d3d8c 100644 --- a/internal/logic/admin/crm/customerdomain/customer/patchcustomerlogic.go +++ b/internal/logic/admin/crm/customerdomain/customer/patchcustomerlogic.go @@ -27,11 +27,11 @@ func NewPatchCustomerLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Pat func (l *PatchCustomerLogic) PatchCustomer(req *types.PatchCustomerRequest) (resp *types.PatchCustomerReply, err error) { mdlCustomer := &customerdomain.Customer{ - Name: req.Name, - Email: req.Email, - InviterId: req.InviterId, - Source: req.Source, - Type: req.Type, + Name: req.Name, + Email: req.Email, + //InviterId: req.InviterId, + //Source: req.Source, + //Type: req.Type, IsActivated: req.IsActivated, } diff --git a/internal/logic/admin/crm/customerdomain/customer/putcustomerlogic.go b/internal/logic/admin/crm/customerdomain/customer/putcustomerlogic.go index 61e8ad7d..722fb27c 100644 --- a/internal/logic/admin/crm/customerdomain/customer/putcustomerlogic.go +++ b/internal/logic/admin/crm/customerdomain/customer/putcustomerlogic.go @@ -1,10 +1,10 @@ package customer import ( - "context" - "PowerX/internal/svc" "PowerX/internal/types" + "PowerX/pkg/securityx" + "context" "github.com/zeromicro/go-zero/core/logx" ) @@ -26,6 +26,15 @@ func NewPutCustomerLogic(ctx context.Context, svcCtx *svc.ServiceContext) *PutCu func (l *PutCustomerLogic) PutCustomer(req *types.PutCustomerRequest) (resp *types.PutCustomerReply, err error) { mdlCustomer := TransformRequestToCustomer(&(req.Customer)) + cCustomer, err := l.svcCtx.PowerX.Customer.GetCustomer(l.ctx, req.Id) + if err != nil { + return nil, err + } + // 如果当前数据库的用户已经有了UUID + if cCustomer.Uuid == "" { + mdlCustomer.Uuid = securityx.GenerateUUID() + } + // 更新产品对象 err = l.svcCtx.PowerX.Customer.UpdateCustomer(l.ctx, req.CustomerId, mdlCustomer) diff --git a/internal/logic/mp/crm/customer/auth/authbyphonelogic.go b/internal/logic/mp/crm/customer/auth/authbyphonelogic.go index b3784ba5..2f59da34 100644 --- a/internal/logic/mp/crm/customer/auth/authbyphonelogic.go +++ b/internal/logic/mp/crm/customer/auth/authbyphonelogic.go @@ -7,7 +7,6 @@ import ( "PowerX/internal/svc" "PowerX/internal/types" customerdomain2 "PowerX/internal/uc/powerx/crm/customerdomain" - fmt2 "PowerX/pkg/printx" "context" "errors" "fmt" @@ -50,7 +49,7 @@ func (l *AuthByPhoneLogic) AuthByPhone(req *types.MPCustomerAuthRequest) (resp * // SessionKey: "+CG6t0FMK1QMLP4IKWNPUw==", //} - fmt2.Dump(rs, req) + //fmt2.Dump(rs, req) // 解码手机授权信息 msgData, errEncrypt := l.svcCtx.PowerX.WechatMP.App.Encryptor.DecryptData(req.EncryptedData, rs.SessionKey, req.IV) @@ -58,7 +57,7 @@ func (l *AuthByPhoneLogic) AuthByPhone(req *types.MPCustomerAuthRequest) (resp * return nil, errors.New(errEncrypt.ErrMsg) } - println(string(msgData)) + //println(string(msgData)) // 解析手机信息 mpPhoneInfo := &wechat.MPPhoneInfo{} err = object.JsonDecode(msgData, mpPhoneInfo) diff --git a/internal/logic/web/customer/auth/registercustomerbyphonelogic.go b/internal/logic/web/customer/auth/registercustomerbyphonelogic.go index 5883491c..79933bbd 100644 --- a/internal/logic/web/customer/auth/registercustomerbyphonelogic.go +++ b/internal/logic/web/customer/auth/registercustomerbyphonelogic.go @@ -3,13 +3,13 @@ package auth import ( "PowerX/internal/model" "PowerX/internal/model/crm/customerdomain" + "PowerX/internal/model/crm/market" + "PowerX/internal/svc" + "PowerX/internal/types" "PowerX/internal/types/errorx" "PowerX/pkg/securityx" "context" - "PowerX/internal/svc" - "PowerX/internal/types" - "github.com/zeromicro/go-zero/core/logx" ) @@ -46,19 +46,65 @@ func (l *RegisterCustomerByPhoneLogic) RegisterCustomerByPhone(req *types.Custom customerTypeId := l.svcCtx.PowerX.DataDictionary.GetCachedDDId(l.ctx, customerdomain.TypeCustomerType, customerdomain.CustomerPersonal) // upsert 客户 + uuid := securityx.GenerateUUID() + inviteCode := securityx.GenerateInviteCode(uuid) customer := &customerdomain.Customer{ Mobile: req.Phone, Password: hashedPassword, Source: customerSourceId, Type: customerTypeId, + Uuid: uuid, + InviteCode: inviteCode, IsActivated: true, } + + // 如果邀请码存在,需要绑定邀请人的UUID + var record *market.InviteRecord + if req.InviteCode != "" { + customer, record, err = l.BindCustomerByInviteCode(l.ctx, req.InviteCode, customer) + if err != nil { + return nil, errorx.WithCause(errorx.ErrBadRequest, "邀请码无效") + } + } + + // 创建新注册用户 err = l.svcCtx.PowerX.Customer.CreateCustomer(l.ctx, customer) if err != nil { - return nil, err + return nil, errorx.WithCause(errorx.ErrCreateObject, "创建注册永固失败") + } + + // 更新邀请记录的受邀者的ID + if req.InviteCode != "" { + record.InviteeID = customer.Id + l.svcCtx.PowerX.MGM.UpdateInviteRecord(l.ctx, record) } return &types.CustomerRegisterByPhoneReply{ CustomerId: customer.Id, }, nil } + +func (l *RegisterCustomerByPhoneLogic) BindCustomerByInviteCode(ctx context.Context, + inviteCode string, customer *customerdomain.Customer, +) (*customerdomain.Customer, *market.InviteRecord, error) { + + // 通过邀请码,获取邀请人 + inviter, err := l.svcCtx.PowerX.Customer.GetCustomerByInviteCode(ctx, inviteCode) + if err != nil { + return nil, nil, err + } + + // 保存邀请记录 + // 使用默认的MGM规则,直系关系 + mgmSceneId := l.svcCtx.PowerX.DataDictionary.GetCachedDDId(ctx, market.TypeMGMScene, market.MGMSceneDirectRecruitment) + inviteCode = securityx.GenerateInviteCode(inviter.Uuid) + record, err := l.svcCtx.PowerX.MGM.CreateInviteRecord(ctx, inviter, customer, inviteCode, mgmSceneId) + + // 绑定邀请人和注册人的关系 + customer.InviterId = inviter.Id + if err != nil { + return nil, nil, err + } + + return customer, record, err +} diff --git a/internal/model/crm/customerdomain/customer.go b/internal/model/crm/customerdomain/customer.go index 1038a12c..d3c456e9 100644 --- a/internal/model/crm/customerdomain/customer.go +++ b/internal/model/crm/customerdomain/customer.go @@ -18,12 +18,13 @@ type Customer struct { powermodel.PowerModel Name string `gorm:"comment:客户名称" json:"name"` Mobile string `gorm:"unique;not null;comment:店长Id" json:"mobile"` + Uuid string `gorm:"comment:识别码;index" json:"uuid"` Password string `gorm:"comment:客户密码" json:"password"` Email string `gorm:"comment:邮箱地址" json:"email"` InviterId int64 `gorm:"comment:邀请方" json:"inviterId"` MgmId int `gorm:"comment:MGM Id" json:"mgmId"` + InviteCode string `gorm:"comment:裂变邀请码" json:"inviteCode"` Source int `gorm:"comment:注册来源" json:"source"` - Uuid string `gorm:"comment:识别码;index" json:"uuid"` Type int `gorm:"comment:类型:个人,企业" json:"type"` IsActivated bool `gorm:"comment:激活状态" json:"isActivated"` ExternalId diff --git a/internal/model/powermodel/powermodel.go b/internal/model/powermodel/powermodel.go index 3a01af2c..220ed8f4 100644 --- a/internal/model/powermodel/powermodel.go +++ b/internal/model/powermodel/powermodel.go @@ -1,9 +1,9 @@ package powermodel import ( + "PowerX/pkg/securityx" "database/sql" "github.com/ArtisanCloud/PowerLibs/v3/object" - "github.com/google/uuid" "gorm.io/gorm" "gorm.io/gorm/clause" "gorm.io/gorm/schema" @@ -57,7 +57,7 @@ var ArrayModelFields *object.HashMap = &object.HashMap{} func NewPowerUUIDModel() *PowerUUIDModel { now := time.Now() return &PowerUUIDModel{ - UUID: uuid.New().String(), + UUID: securityx.GenerateUUID(), CreatedAt: now, UpdatedAt: now, } diff --git a/internal/types/types.go b/internal/types/types.go index d7de0852..6d07217d 100644 --- a/internal/types/types.go +++ b/internal/types/types.go @@ -879,6 +879,7 @@ type Customer struct { Name string `json:"name"` Mobile string `json:"mobile"` Email string `json:"email,optional"` + UUID string `json:"uuid,optional"` Inviter *CustomerInviter `json:"inviter,optional"` InviterId int64 `json:"inviter,optional"` Source int `json:"source,optional"` @@ -3414,6 +3415,7 @@ type CustomerRegisterByPhoneRequest struct { Phone string `json:"phone"` Password string `json:"password"` VerifyCode string `json:"verifyCode"` + InviteCode string `json:"inviteCode,optional"` } type CustomerRegisterByPhoneReply struct { diff --git a/internal/uc/powerx/crm/customerdomain/customer.go b/internal/uc/powerx/crm/customerdomain/customer.go index c81d13e0..e5f79341 100644 --- a/internal/uc/powerx/crm/customerdomain/customer.go +++ b/internal/uc/powerx/crm/customerdomain/customer.go @@ -5,6 +5,7 @@ import ( "PowerX/internal/model/powermodel" "PowerX/internal/types" "PowerX/internal/types/errorx" + "PowerX/pkg/securityx" "context" "github.com/pkg/errors" "gorm.io/gorm" @@ -100,10 +101,22 @@ func (uc *CustomerUseCase) UpsertCustomer(ctx context.Context, customer *custome customers := []*customerdomain.Customer{customer} - _, err := uc.UpsertCustomers(ctx, customers) - if err != nil { - panic(errors.Wrap(err, "upsert customerdomain failed")) - } + err := uc.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + err := powermodel.UpsertModelsOnUniqueID(tx, &customerdomain.Customer{}, customerdomain.CustomerUniqueId, customers, nil, false) + + if err != nil { + panic(errors.Wrap(err, "upsert customerdomain failed")) + } + // 如果是新增用户,那么需要给一个唯一识别号 + if customer.Uuid == "" { + customer.Uuid = securityx.GenerateUUID() + err = powermodel.UpsertModelsOnUniqueID(tx, &customerdomain.Customer{}, customerdomain.CustomerUniqueId, customer, []string{"uuid"}, false) + if err != nil { + return err + } + } + return err + }) return customer, err } @@ -120,15 +133,12 @@ func (uc *CustomerUseCase) UpsertCustomers(ctx context.Context, customers []*cus } func (uc *CustomerUseCase) UpdateCustomer(ctx context.Context, id int64, customer *customerdomain.Customer) error { - if err := uc.db.WithContext(ctx).Model(&customerdomain.Customer{}). - //Debug(). - Where(id).Updates(&customer).Error; err != nil { - if strings.Contains(err.Error(), "duplicate key value violates unique constraint") { - return errorx.WithCause(errorx.ErrDuplicatedInsert, "该对象不能重复创建") - } - panic(err) - } - return nil + //fmt.Dump(customer) + err := uc.db.WithContext(ctx).Model(&customerdomain.Customer{}). + Debug(). + Where(id).Updates(&customer).Error + + return err } func (uc *CustomerUseCase) GetCustomer(ctx context.Context, id int64) (*customerdomain.Customer, error) { @@ -155,6 +165,31 @@ func (uc *CustomerUseCase) GetCustomerByMobile(ctx context.Context, mobile strin return &customer, nil } +func (uc *CustomerUseCase) GetCustomerByUUID(ctx context.Context, uuid string) (*customerdomain.Customer, error) { + var customer customerdomain.Customer + if err := uc.db.WithContext(ctx). + Where("uuid", uuid). + First(&customer).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, errorx.WithCause(errorx.ErrBadRequest, "未找到客户") + } + panic(err) + } + return &customer, nil +} +func (uc *CustomerUseCase) GetCustomerByInviteCode(ctx context.Context, inviteCode string) (*customerdomain.Customer, error) { + var customer customerdomain.Customer + if err := uc.db.WithContext(ctx). + Where("invite_code", inviteCode). + First(&customer).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, errorx.WithCause(errorx.ErrBadRequest, "未找到客户") + } + panic(err) + } + return &customer, nil +} + func (uc *CustomerUseCase) DeleteCustomer(ctx context.Context, id int64) error { result := uc.db.WithContext(ctx).Delete(&customerdomain.Customer{}, id) if err := result.Error; err != nil { diff --git a/internal/uc/powerx/crm/market/mgm.go b/internal/uc/powerx/crm/market/mgm.go index 62052da6..150e9b04 100644 --- a/internal/uc/powerx/crm/market/mgm.go +++ b/internal/uc/powerx/crm/market/mgm.go @@ -1,6 +1,7 @@ package market import ( + "PowerX/internal/model/crm/customerdomain" model "PowerX/internal/model/crm/market" "PowerX/internal/model/powermodel" "PowerX/internal/types" @@ -147,6 +148,17 @@ func (uc *MGMRuleUseCase) GetMGMRule(ctx context.Context, id int64) (*model.MGMR return &m, nil } +func (uc *MGMRuleUseCase) GetMGMRuleBySceneId(ctx context.Context, sceneTypeId int64) (*model.MGMRule, error) { + var m model.MGMRule + if err := uc.db.WithContext(ctx).First(&m).Where("scene", sceneTypeId).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, errorx.WithCause(errorx.ErrBadRequest, "未找到媒体") + } + panic(err) + } + return &m, nil +} + func (uc *MGMRuleUseCase) DeleteMGMRule(ctx context.Context, id int64) error { result := uc.db.WithContext(ctx).Delete(&model.MGMRule{}, id) if err := result.Error; err != nil { @@ -164,3 +176,37 @@ func (uc *MGMRuleUseCase) ClearAssociations(db *gorm.DB, media *model.MGMRule) ( return media, err } + +func (uc *MGMRuleUseCase) CreateInviteRecord(ctx context.Context, + inviter *customerdomain.Customer, invitee *customerdomain.Customer, + inviteCode string, sceneId int, +) (*model.InviteRecord, error) { + record := &model.InviteRecord{ + InviterID: inviter.Id, + InviteeID: invitee.Id, + InvitationCode: inviteCode, + MgmSceneId: sceneId, + } + err := uc.db.WithContext(ctx). + //Debug(). + Model(&model.InviteRecord{}). + Create(record).Error + + if err != nil { + panic(err) + } + return record, nil +} + +func (uc *MGMRuleUseCase) UpdateInviteRecord(ctx context.Context, record *model.InviteRecord) { + + err := uc.db.WithContext(ctx). + //Debug(). + Model(&model.InviteRecord{}). + Where("id", record.Id). + Updates(record).Error + + if err != nil { + panic(err) + } +} diff --git a/pkg/securityx/security.go b/pkg/securityx/security.go index c8fc13ed..7f5304b9 100644 --- a/pkg/securityx/security.go +++ b/pkg/securityx/security.go @@ -2,8 +2,11 @@ package securityx import ( "crypto/md5" + "crypto/sha1" "crypto/sha256" + "encoding/base64" "fmt" + "github.com/google/uuid" "golang.org/x/crypto/bcrypt" "log" "strings" @@ -96,3 +99,22 @@ func CheckPassword(hashedPassword string, encodedPassword string) (isPasswordVal return true } + +func GenerateUUID() string { + return uuid.New().String() +} + +// 生成6位邀请码 +func GenerateInviteCode(uuid string) string { + // 使用SHA1算法生成哈希值 + hasher := sha1.New() + hasher.Write([]byte(uuid)) + hashBytes := hasher.Sum(nil) + + // 使用Base64编码将哈希值转换为字符串 + encoded := base64.URLEncoding.EncodeToString(hashBytes) + + // 只保留编码结果的前6位,并移除可能的特殊字符 + inviteCode := strings.TrimRight(strings.ReplaceAll(encoded[:6], "/", ""), "=") + return inviteCode +} diff --git a/pkg/securityx/security_test.go b/pkg/securityx/security_test.go index 789e5095..4f17527b 100644 --- a/pkg/securityx/security_test.go +++ b/pkg/securityx/security_test.go @@ -1,6 +1,7 @@ package securityx import ( + "fmt" "github.com/stretchr/testify/assert" "testing" ) @@ -20,3 +21,15 @@ func Test_HashPlainPassword(t *testing.T) { assert.EqualValues(t, true, result) } + +func Test_GenerateInviteCode(t *testing.T) { + + // 假设这是你的UUID + uuid := "3d5ec9ea-19fe-40f3-83a8-53f1f8195946" + + // 生成6位邀请码 + inviteCode := GenerateInviteCode(uuid) + + fmt.Println("生成的6位邀请码:", inviteCode) + +}