From 493416551bb9f40729b505426b251fe26128d1e5 Mon Sep 17 00:00:00 2001 From: zzw <1464003642@qq.com> Date: Thu, 3 Sep 2026 10:33:48 +0800 Subject: [PATCH 1/5] feat: passwork login --- go.mod | 1 + go.sum | 2 + rpc/chore/chore.proto | 1 + services/user/internal/logic/authHelper.go | 15 ++++++ services/user/internal/logic/loginLogic.go | 59 +++++++++++++++++++++- 5 files changed, 77 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index d978be5..96b8523 100644 --- a/go.mod +++ b/go.mod @@ -35,6 +35,7 @@ require ( filippo.io/edwards25519 v1.2.0 // indirect github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 // indirect github.com/alibabacloud-go/debug v1.0.1 // indirect + github.com/alibabacloud-go/dysmsapi-20180501/v2 v2.0.8 // indirect github.com/aliyun/credentials-go v1.4.13 // indirect github.com/antlr4-go/antlr/v4 v4.13.0 // indirect github.com/beorn7/perks v1.0.1 // indirect diff --git a/go.sum b/go.sum index a36fde8..912bda8 100644 --- a/go.sum +++ b/go.sum @@ -30,6 +30,8 @@ github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68/go.mod h1:6p github.com/alibabacloud-go/debug v1.0.0/go.mod h1:8gfgZCCAC3+SCzjWtY053FrOcd4/qlH6IHTI4QyICOc= github.com/alibabacloud-go/debug v1.0.1 h1:MsW9SmUtbb1Fnt3ieC6NNZi6aEwrXfDksD4QA6GSbPg= github.com/alibabacloud-go/debug v1.0.1/go.mod h1:8gfgZCCAC3+SCzjWtY053FrOcd4/qlH6IHTI4QyICOc= +github.com/alibabacloud-go/dysmsapi-20180501/v2 v2.0.8 h1:aDPyz6C+nenypx24N5qEt09NjpS6mu7Cu1A+wf9UTaY= +github.com/alibabacloud-go/dysmsapi-20180501/v2 v2.0.8/go.mod h1:e/vWJ5gLVnraPROSh+3oMSodf5ukaUlqNgH0IIcnz98= github.com/alibabacloud-go/endpoint-util v1.1.0 h1:r/4D3VSw888XGaeNpP994zDUaxdgTSHBbVfZlzf6b5Q= github.com/alibabacloud-go/endpoint-util v1.1.0/go.mod h1:O5FuCALmCKs2Ff7JFJMudHs0I5EBgecXXxZRyswlEjE= github.com/alibabacloud-go/openapi-util v0.1.0 h1:0z75cIULkDrdEhkLWgi9tnLe+KhAFE/r5Pb3312/eAY= diff --git a/rpc/chore/chore.proto b/rpc/chore/chore.proto index e26ac36..ba5d13d 100644 --- a/rpc/chore/chore.proto +++ b/rpc/chore/chore.proto @@ -4,6 +4,7 @@ package chore; option go_package = "lone-services/rpc/chore"; import "google/api/annotations.proto"; +// 应放到pkg service Chore { rpc Policy(PolicyReq) returns (Response) { option (google.api.http) = { diff --git a/services/user/internal/logic/authHelper.go b/services/user/internal/logic/authHelper.go index c2f8d9b..fc4e244 100644 --- a/services/user/internal/logic/authHelper.go +++ b/services/user/internal/logic/authHelper.go @@ -68,6 +68,21 @@ func findCredential(credentialType, identifier string) (*dao.UserCredential, err return &cred, nil } +func userHasEnabledClient(userId int64, clientCode string) (bool, error) { + var row dao.UserClient + clientModel := model.UserClientModel{}.Init() + if err := clientModel.GetOne(modelbase.Params{ + Eq: map[string]string{ + "user_id": strconv.FormatInt(userId, utils.NumberTen), + "client_code": clientCode, + "status": strconv.Itoa(int(dao.StatusEnabled)), + }, + }, &row); err != nil { + return false, err + } + return row.Id >= utils.NumberOne, nil +} + func loadEnabledUser(userId int64) (*dao.UserRow, error) { var row dao.UserRow userModel := model.UserModel{}.Init() diff --git a/services/user/internal/logic/loginLogic.go b/services/user/internal/logic/loginLogic.go index f3f5870..bb1557e 100644 --- a/services/user/internal/logic/loginLogic.go +++ b/services/user/internal/logic/loginLogic.go @@ -39,7 +39,7 @@ func (l *LoginLogic) Login(in *user.LoginReq) (*user.Response, error) { case dao.GrantTypeOpenid: return l.loginByOpenid(req) case dao.GrantTypePassword: - return outResponse(utils.ErrorParams, "密码登录暂未开放"), nil + return l.loginByPassword(req) case dao.GrantTypeSms: return outResponse(utils.ErrorParams, "验证码登录暂未开放"), nil default: @@ -91,3 +91,60 @@ func (l *LoginLogic) loginByOpenid(req validator.LoginValidator) (*user.Response } return okResponse(ret), nil } + +func (l *LoginLogic) loginByPassword(req validator.LoginValidator) (*user.Response, error) { + client, err := loadEnabledClient(req.ClientCode) + if err != nil { + l.Errorf("login load client: %v", err) + return failResponse(utils.Fail), nil + } + if !clientAllowsGrant(client.AllowedGrants, dao.GrantTypePassword) { + return outResponse(utils.ErrorParams, "该端不支持密码登录"), nil + } + + if len(req.Mobile) != utils.NumberEleven { + return failResponse(utils.ErrorMobileError), nil + } + encryptMobile, encErr := utils.EncryptPhone(req.Mobile) + if encErr != nil { + l.Errorf("login encrypt mobile: %v", encErr) + return failResponse(utils.ErrorEncryptAesError), nil + } + + cred, credErr := findCredential(dao.CredentialTypePassword, encryptMobile) + if credErr != nil { + l.Errorf("login find credential: %v", credErr) + return failResponse(utils.Fail), nil + } + if cred == nil { + return failResponse(utils.ErrorNotFund), nil + } + if cred.Status == dao.StatusDisabled { + return outResponse(utils.Fail, "登录凭证已禁用"), nil + } + if !utils.EqualsPassword(req.Password, cred.Secret) { + return failResponse(utils.ErrorPwdError), nil + } + + hasClient, clientErr := userHasEnabledClient(cred.UserId, req.ClientCode) + if clientErr != nil { + l.Errorf("login check user client: %v", clientErr) + return failResponse(utils.Fail), nil + } + if !hasClient { + return outResponse(utils.ErrorParams, "该端未开通或已禁用"), nil + } + + row, userErr := loadEnabledUser(cred.UserId) + if userErr != nil { + l.Errorf("login load user: %v", userErr) + return failResponse(utils.ErrorNotFund), nil + } + + ret, tokenErr := issueLoginToken(l.ctx, l.svcCtx.JWT, l.svcCtx.SaleSvcName, row, client) + if tokenErr != nil { + l.Errorf("login issue token: %v", tokenErr) + return failResponse(utils.Fail), nil + } + return okResponse(ret), nil +} From 73a5fa41ffa7465a7898140c88c6854984504852 Mon Sep 17 00:00:00 2001 From: zzw <1464003642@qq.com> Date: Thu, 3 Sep 2026 17:11:34 +0800 Subject: [PATCH 2/5] feat: aliyun sms --- .gitignore | 1 + deploy/apisix/lua/auth.lua | 10 +- go.mod | 9 +- go.sum | 18 ++- pkg/sms/aliyun/aliyun.go | 60 +++++++++ pkg/sms/sms.go | 74 +++++++++++ rpc/user/pb/user.pb.go | 129 +++++++++++++------ rpc/user/pb/user_grpc.pb.go | 38 ++++++ rpc/user/user.pb | Bin 15634 -> 15768 bytes rpc/user/user.proto | 11 ++ services/user/internal/config/sms.go | 19 +++ services/user/internal/dao/const.go | 14 +- services/user/internal/logic/sendSmsLogic.go | 120 +++++++++++++++++ services/user/internal/server/userserver.go | 5 + services/user/internal/svc/servicecontext.go | 9 ++ services/user/userClient/user.go | 7 + services/user/validator/user.go | 14 ++ 17 files changed, 488 insertions(+), 50 deletions(-) create mode 100644 pkg/sms/aliyun/aliyun.go create mode 100644 pkg/sms/sms.go create mode 100644 services/user/internal/config/sms.go create mode 100644 services/user/internal/logic/sendSmsLogic.go diff --git a/.gitignore b/.gitignore index 1fa52a1..cfb16f4 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ deploy/mysql/data deploy/redis/data +deploy/rnacos/data # logs *.log diff --git a/deploy/apisix/lua/auth.lua b/deploy/apisix/lua/auth.lua index fefe3e7..277cba6 100644 --- a/deploy/apisix/lua/auth.lua +++ b/deploy/apisix/lua/auth.lua @@ -11,10 +11,12 @@ function _M.access(conf, ctx) core.log.info("REQUEST_DEBUG: uri = "..request_uri) local white_list = { - ["/api/v3/login"] = true, - ["/admin/v3/login"] = true, - ["/admin/v3/refresh"] = true, - ["/api/v3/version"] = true, + ["/api/v3/sms/send"] = true, + ["/api/v3/login"] = true, + ["/api/v3/version"] = true, + + ["/admin/v3/login"] = true, + ["/admin/v3/refresh"] = true, } local user_key = core.request.header(ctx, "authorization") diff --git a/go.mod b/go.mod index 96b8523..8b5686f 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,9 @@ go 1.26 require ( buf.build/go/protovalidate v0.14.0 + github.com/alibabacloud-go/darabonba-openapi v0.2.1 github.com/alibabacloud-go/darabonba-openapi/v2 v2.2.4 + github.com/alibabacloud-go/dysmsapi-20170525/v2 v2.0.18 github.com/alibabacloud-go/sts-20150401/v2 v2.1.0 github.com/alibabacloud-go/tea v1.5.3 github.com/alibabacloud-go/tea-utils/v2 v2.0.9 @@ -15,6 +17,7 @@ require ( github.com/json-iterator/go v1.1.12 github.com/mssola/user_agent v0.6.0 github.com/redis/go-redis/v9 v9.21.0 + github.com/samber/lo v1.53.0 github.com/spf13/viper v1.21.0 github.com/xuri/excelize/v2 v2.11.0 github.com/zeromicro/go-zero v1.10.3 @@ -35,7 +38,10 @@ require ( filippo.io/edwards25519 v1.2.0 // indirect github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 // indirect github.com/alibabacloud-go/debug v1.0.1 // indirect - github.com/alibabacloud-go/dysmsapi-20180501/v2 v2.0.8 // indirect + github.com/alibabacloud-go/endpoint-util v1.1.0 // indirect + github.com/alibabacloud-go/openapi-util v0.1.0 // indirect + github.com/alibabacloud-go/tea-utils v1.4.5 // indirect + github.com/alibabacloud-go/tea-xml v1.1.2 // indirect github.com/aliyun/credentials-go v1.4.13 // indirect github.com/antlr4-go/antlr/v4 v4.13.0 // indirect github.com/beorn7/perks v1.0.1 // indirect @@ -109,7 +115,6 @@ require ( github.com/richardlehane/mscfb v1.0.7 // indirect github.com/richardlehane/msoleps v1.0.6 // indirect github.com/sagikazarmark/locafero v0.11.0 // indirect - github.com/samber/lo v1.53.0 // indirect github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/spf13/afero v1.15.0 // indirect diff --git a/go.sum b/go.sum index 912bda8..8d6846d 100644 --- a/go.sum +++ b/go.sum @@ -19,21 +19,26 @@ github.com/alibabacloud-go/darabonba-encode-util v0.0.2 h1:1uJGrbsGEVqWcWxrS9MyC github.com/alibabacloud-go/darabonba-encode-util v0.0.2/go.mod h1:JiW9higWHYXm7F4PKuMgEUETNZasrDM6vqVr/Can7H8= github.com/alibabacloud-go/darabonba-map v0.0.2 h1:qvPnGB4+dJbJIxOOfawxzF3hzMnIpjmafa0qOTp6udc= github.com/alibabacloud-go/darabonba-map v0.0.2/go.mod h1:28AJaX8FOE/ym8OUFWga+MtEzBunJwQGceGQlvaPGPc= +github.com/alibabacloud-go/darabonba-openapi v0.1.18/go.mod h1:PB4HffMhJVmAgNKNq3wYbTUlFvPgxJpTzd1F5pTuUsc= +github.com/alibabacloud-go/darabonba-openapi v0.2.1 h1:WyzxxKvhdVDlwpAMOHgAiCJ+NXa6g5ZWPFEzaK/ewwY= +github.com/alibabacloud-go/darabonba-openapi v0.2.1/go.mod h1:zXOqLbpIqq543oioL9IuuZYOQgHQ5B8/n5OPrnko8aY= github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.13/go.mod h1:lxFGfobinVsQ49ntjpgWghXmIF0/Sm4+wvBJ1h5RtaE= github.com/alibabacloud-go/darabonba-openapi/v2 v2.2.4 h1:o6veen0IZ/Fe1JawwhwQMZcbw67CVDY1pQwXcNWPyQo= github.com/alibabacloud-go/darabonba-openapi/v2 v2.2.4/go.mod h1:eHjVxrT9g8uVYN/nyAwOFQEfkVA154ChiqRc2XnNKYU= github.com/alibabacloud-go/darabonba-signature-util v0.0.7 h1:UzCnKvsjPFzApvODDNEYqBHMFt1w98wC7FOo0InLyxg= github.com/alibabacloud-go/darabonba-signature-util v0.0.7/go.mod h1:oUzCYV2fcCH797xKdL6BDH8ADIHlzrtKVjeRtunBNTQ= +github.com/alibabacloud-go/darabonba-string v1.0.0/go.mod h1:93cTfV3vuPhhEwGGpKKqhVW4jLe7tDpo3LUM0i0g6mA= github.com/alibabacloud-go/darabonba-string v1.0.2 h1:E714wms5ibdzCqGeYJ9JCFywE5nDyvIXIIQbZVFkkqo= github.com/alibabacloud-go/darabonba-string v1.0.2/go.mod h1:93cTfV3vuPhhEwGGpKKqhVW4jLe7tDpo3LUM0i0g6mA= github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68/go.mod h1:6pb/Qy8c+lqua8cFpEy7g39NRRqOWc3rOwAy8m5Y2BY= github.com/alibabacloud-go/debug v1.0.0/go.mod h1:8gfgZCCAC3+SCzjWtY053FrOcd4/qlH6IHTI4QyICOc= github.com/alibabacloud-go/debug v1.0.1 h1:MsW9SmUtbb1Fnt3ieC6NNZi6aEwrXfDksD4QA6GSbPg= github.com/alibabacloud-go/debug v1.0.1/go.mod h1:8gfgZCCAC3+SCzjWtY053FrOcd4/qlH6IHTI4QyICOc= -github.com/alibabacloud-go/dysmsapi-20180501/v2 v2.0.8 h1:aDPyz6C+nenypx24N5qEt09NjpS6mu7Cu1A+wf9UTaY= -github.com/alibabacloud-go/dysmsapi-20180501/v2 v2.0.8/go.mod h1:e/vWJ5gLVnraPROSh+3oMSodf5ukaUlqNgH0IIcnz98= +github.com/alibabacloud-go/dysmsapi-20170525/v2 v2.0.18 h1:hfZA4cgIl6frNdsRmAyj8sn9J1bihQpYbzIVv2T/+Cs= +github.com/alibabacloud-go/dysmsapi-20170525/v2 v2.0.18/go.mod h1:di54xjBFHvKiQQo7st3TUmiMy0ywne5TOHup786Rhes= github.com/alibabacloud-go/endpoint-util v1.1.0 h1:r/4D3VSw888XGaeNpP994zDUaxdgTSHBbVfZlzf6b5Q= github.com/alibabacloud-go/endpoint-util v1.1.0/go.mod h1:O5FuCALmCKs2Ff7JFJMudHs0I5EBgecXXxZRyswlEjE= +github.com/alibabacloud-go/openapi-util v0.0.11/go.mod h1:sQuElr4ywwFRlCCberQwKRFhRzIyG4QTP/P4y1CJ6Ws= github.com/alibabacloud-go/openapi-util v0.1.0 h1:0z75cIULkDrdEhkLWgi9tnLe+KhAFE/r5Pb3312/eAY= github.com/alibabacloud-go/openapi-util v0.1.0/go.mod h1:sQuElr4ywwFRlCCberQwKRFhRzIyG4QTP/P4y1CJ6Ws= github.com/alibabacloud-go/sts-20150401/v2 v2.1.0 h1:Z5FOpAW003CjNSEPSemkPGj+OHWvF5bu7gz+Rdx/1zU= @@ -43,18 +48,23 @@ github.com/alibabacloud-go/tea v1.1.7/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeG github.com/alibabacloud-go/tea v1.1.8/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4= github.com/alibabacloud-go/tea v1.1.11/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4= github.com/alibabacloud-go/tea v1.1.17/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A= +github.com/alibabacloud-go/tea v1.1.19/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A= github.com/alibabacloud-go/tea v1.1.20/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A= github.com/alibabacloud-go/tea v1.2.2/go.mod h1:CF3vOzEMAG+bR4WOql8gc2G9H3EkH3ZLAQdpmpXMgwk= github.com/alibabacloud-go/tea v1.3.13/go.mod h1:A560v/JTQ1n5zklt2BEpurJzZTI8TUT+Psg2drWlxRg= github.com/alibabacloud-go/tea v1.5.2/go.mod h1:hgSs82CkOiehSQMoiFN79dL6zsGX7pVGvnn9SIEs8/0= github.com/alibabacloud-go/tea v1.5.3 h1:UMJTBcO48w7Zz1nlFe9bmRsHwgB7AjF8DBQzcvHhIp8= github.com/alibabacloud-go/tea v1.5.3/go.mod h1:hgSs82CkOiehSQMoiFN79dL6zsGX7pVGvnn9SIEs8/0= -github.com/alibabacloud-go/tea-utils v1.3.1 h1:iWQeRzRheqCMuiF3+XkfybB3kTgUXkXX+JMrqfLeB2I= github.com/alibabacloud-go/tea-utils v1.3.1/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQdSngxrpF8rKUDJjPE= +github.com/alibabacloud-go/tea-utils v1.4.3/go.mod h1:KNcT0oXlZZxOXINnZBs6YvgOd5aYp9U67G+E3R8fcQw= +github.com/alibabacloud-go/tea-utils v1.4.5 h1:h0/6Xd2f3bPE4XHTvkpjwxowIwRCJAJOqY6Eq8f3zfA= +github.com/alibabacloud-go/tea-utils v1.4.5/go.mod h1:KNcT0oXlZZxOXINnZBs6YvgOd5aYp9U67G+E3R8fcQw= github.com/alibabacloud-go/tea-utils/v2 v2.0.5/go.mod h1:dL6vbUT35E4F4bFTHL845eUloqaerYBYPsdWR2/jhe4= github.com/alibabacloud-go/tea-utils/v2 v2.0.7/go.mod h1:qxn986l+q33J5VkialKMqT/TTs3E+U9MJpd001iWQ9I= github.com/alibabacloud-go/tea-utils/v2 v2.0.9 h1:y6pUIlhjxbZl9ObDAcmA1H3c21eaAxADHTDQmBnAIgA= github.com/alibabacloud-go/tea-utils/v2 v2.0.9/go.mod h1:qxn986l+q33J5VkialKMqT/TTs3E+U9MJpd001iWQ9I= +github.com/alibabacloud-go/tea-xml v1.1.2 h1:oLxa7JUXm2EDFzMg+7oRsYc+kutgCVwm+bZlhhmvW5M= +github.com/alibabacloud-go/tea-xml v1.1.2/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8= github.com/alicebob/miniredis/v2 v2.38.0 h1:nZAzCR+Lj+Vxk4ZXzm2NuKq2O33RXj1XxJ2e2uP9jiw= github.com/alicebob/miniredis/v2 v2.38.0/go.mod h1:TcL7YfarKPGDAthEtl5NBeHZfeUQj6OXMm/+iu5cLMM= github.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw= @@ -86,6 +96,8 @@ github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F9 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= +github.com/clbanning/mxj/v2 v2.5.6/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME= github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= diff --git a/pkg/sms/aliyun/aliyun.go b/pkg/sms/aliyun/aliyun.go new file mode 100644 index 0000000..efe15c7 --- /dev/null +++ b/pkg/sms/aliyun/aliyun.go @@ -0,0 +1,60 @@ +package aliyun + +import ( + "encoding/json" + "fmt" + + openapi "github.com/alibabacloud-go/darabonba-openapi/client" + dysmsapi "github.com/alibabacloud-go/dysmsapi-20170525/v2/client" + "github.com/alibabacloud-go/tea/tea" +) + +type Client struct { + cli *dysmsapi.Client + signName string +} + +func New(accessKeyId, accessKeySecret, signName, endpoint string) (*Client, error) { + config := &openapi.Config{ + AccessKeyId: tea.String(accessKeyId), + AccessKeySecret: tea.String(accessKeySecret), + Endpoint: tea.String(endpoint), + } + cli, err := dysmsapi.NewClient(config) + if err != nil { + return nil, err + } + return &Client{cli: cli, signName: signName}, nil +} + +func (c *Client) Send(phone, templateCode string, params map[string]string) error { + if phone == "" || templateCode == "" { + return fmt.Errorf("phone and templateCode are required") + } + + var templateParam *string + if len(params) > 0 { + raw, err := json.Marshal(params) + if err != nil { + return fmt.Errorf("marshal template params: %w", err) + } + templateParam = tea.String(string(raw)) + } + + resp, err := c.cli.SendSms(&dysmsapi.SendSmsRequest{ + PhoneNumbers: tea.String(phone), + SignName: tea.String(c.signName), + TemplateCode: tea.String(templateCode), + TemplateParam: templateParam, + }) + if err != nil { + return err + } + if resp == nil || resp.Body == nil { + return fmt.Errorf("aliyun sms: empty response") + } + if code := tea.StringValue(resp.Body.Code); code != "OK" { + return fmt.Errorf("aliyun sms: %s %s", code, tea.StringValue(resp.Body.Message)) + } + return nil +} diff --git a/pkg/sms/sms.go b/pkg/sms/sms.go new file mode 100644 index 0000000..159d6d7 --- /dev/null +++ b/pkg/sms/sms.go @@ -0,0 +1,74 @@ +package sms + +import ( + "fmt" + + "lone-services/pkg/sms/aliyun" +) + +type TemplateKind string + +const ( + TemplateCode TemplateKind = "code" + TemplatePassword TemplateKind = "password" +) + +type SMS interface { + Send(phone string, templateCode string, params map[string]string) error + SendByKind(phone string, kind TemplateKind, params map[string]string) error + Template(kind TemplateKind) string + Timeout() int +} + +type Config struct { + AccessKeyId string + AccessKeySecret string + SignName string + Endpoint string + TemplateCode string + TemplatePwd string + Timeout int +} + +type client struct { + sender *aliyun.Client + templates map[TemplateKind]string + timeout int +} + +func New(cfg Config) (SMS, error) { + if cfg.AccessKeyId == "" || cfg.AccessKeySecret == "" || cfg.SignName == "" { + return nil, fmt.Errorf("config missing") + } + + sender, err := aliyun.New(cfg.AccessKeyId, cfg.AccessKeySecret, cfg.SignName, cfg.Endpoint) + if err != nil { + return nil, err + } + if cfg.Timeout <= 0 { + cfg.Timeout = 600 + } + return &client{ + sender: sender, + templates: map[TemplateKind]string{ + TemplateCode: cfg.TemplateCode, + TemplatePassword: cfg.TemplatePwd, + }, + timeout: cfg.Timeout, + }, nil +} + +func (c *client) Send(phone, templateCode string, params map[string]string) error { + return c.sender.Send(phone, templateCode, params) +} + +func (c *client) SendByKind(phone string, kind TemplateKind, params map[string]string) error { + code := c.Template(kind) + if code == "" { + return fmt.Errorf("sms template not configured for kind %s", kind) + } + return c.Send(phone, code, params) +} + +func (c *client) Template(kind TemplateKind) string { return c.templates[kind] } +func (c *client) Timeout() int { return c.timeout } diff --git a/rpc/user/pb/user.pb.go b/rpc/user/pb/user.pb.go index 03be1f0..1228cd1 100644 --- a/rpc/user/pb/user.pb.go +++ b/rpc/user/pb/user.pb.go @@ -82,15 +82,14 @@ func (x *Response) GetData() string { return "" } -// 统一登录:grant_type = openid | password | sms type LoginReq struct { state protoimpl.MessageState `protogen:"open.v1"` - ClientCode string `protobuf:"bytes,1,opt,name=client_code,json=clientCode,proto3" json:"client_code,omitempty"` // 端编码,如 user_skin / user_o2 / sale_beauty - GrantType string `protobuf:"bytes,2,opt,name=grant_type,json=grantType,proto3" json:"grant_type,omitempty"` // openid | password | sms - Code string `protobuf:"bytes,3,opt,name=code,proto3" json:"code,omitempty"` // grant_type=openid 时传微信 login code - Mobile string `protobuf:"bytes,4,opt,name=mobile,proto3" json:"mobile,omitempty"` // grant_type=password|sms - Password string `protobuf:"bytes,5,opt,name=password,proto3" json:"password,omitempty"` // grant_type=password - SmsCode string `protobuf:"bytes,6,opt,name=sms_code,json=smsCode,proto3" json:"sms_code,omitempty"` // grant_type=sms + ClientCode string `protobuf:"bytes,1,opt,name=client_code,json=clientCode,proto3" json:"client_code,omitempty"` + GrantType string `protobuf:"bytes,2,opt,name=grant_type,json=grantType,proto3" json:"grant_type,omitempty"` + Code string `protobuf:"bytes,3,opt,name=code,proto3" json:"code,omitempty"` + Mobile string `protobuf:"bytes,4,opt,name=mobile,proto3" json:"mobile,omitempty"` + Password string `protobuf:"bytes,5,opt,name=password,proto3" json:"password,omitempty"` + SmsCode string `protobuf:"bytes,6,opt,name=sms_code,json=smsCode,proto3" json:"sms_code,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -167,7 +166,6 @@ func (x *LoginReq) GetSmsCode() string { return "" } -// 管理端用户列表 type UserItemsReq struct { state protoimpl.MessageState `protogen:"open.v1"` Mobile string `protobuf:"bytes,1,opt,name=mobile,proto3" json:"mobile,omitempty"` @@ -228,7 +226,6 @@ func (x *UserItemsReq) GetSize() int32 { return 0 } -// 管理端修改用户状态 type UserStatusReq struct { state protoimpl.MessageState `protogen:"open.v1"` Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` @@ -837,6 +834,58 @@ func (x *RevokeBizClientData) GetRevoked() bool { return false } +type SendSmsReq struct { + state protoimpl.MessageState `protogen:"open.v1"` + Mobile string `protobuf:"bytes,1,opt,name=mobile,proto3" json:"mobile,omitempty"` + Scene int32 `protobuf:"varint,2,opt,name=scene,proto3" json:"scene,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SendSmsReq) Reset() { + *x = SendSmsReq{} + mi := &file_user_user_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SendSmsReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SendSmsReq) ProtoMessage() {} + +func (x *SendSmsReq) ProtoReflect() protoreflect.Message { + mi := &file_user_user_proto_msgTypes[13] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SendSmsReq.ProtoReflect.Descriptor instead. +func (*SendSmsReq) Descriptor() ([]byte, []int) { + return file_user_user_proto_rawDescGZIP(), []int{13} +} + +func (x *SendSmsReq) GetMobile() string { + if x != nil { + return x.Mobile + } + return "" +} + +func (x *SendSmsReq) GetScene() int32 { + if x != nil { + return x.Scene + } + return 0 +} + type InfoReq struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields @@ -845,7 +894,7 @@ type InfoReq struct { func (x *InfoReq) Reset() { *x = InfoReq{} - mi := &file_user_user_proto_msgTypes[13] + mi := &file_user_user_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -857,7 +906,7 @@ func (x *InfoReq) String() string { func (*InfoReq) ProtoMessage() {} func (x *InfoReq) ProtoReflect() protoreflect.Message { - mi := &file_user_user_proto_msgTypes[13] + mi := &file_user_user_proto_msgTypes[14] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -870,7 +919,7 @@ func (x *InfoReq) ProtoReflect() protoreflect.Message { // Deprecated: Use InfoReq.ProtoReflect.Descriptor instead. func (*InfoReq) Descriptor() ([]byte, []int) { - return file_user_user_proto_rawDescGZIP(), []int{13} + return file_user_user_proto_rawDescGZIP(), []int{14} } var File_user_user_proto protoreflect.FileDescriptor @@ -939,9 +988,14 @@ const file_user_user_proto_rawDesc = "" + "\vclient_code\x18\x02 \x01(\tR\n" + "clientCode\"/\n" + "\x13RevokeBizClientData\x12\x18\n" + - "\arevoked\x18\x01 \x01(\bR\arevoked\"\t\n" + - "\aInfoReq2\xc3\x04\n" + - "\x04User\x12A\n" + + "\arevoked\x18\x01 \x01(\bR\arevoked\":\n" + + "\n" + + "SendSmsReq\x12\x16\n" + + "\x06mobile\x18\x01 \x01(\tR\x06mobile\x12\x14\n" + + "\x05scene\x18\x02 \x01(\x05R\x05scene\"\t\n" + + "\aInfoReq2\x8d\x05\n" + + "\x04User\x12H\n" + + "\aSendSms\x12\x10.user.SendSmsReq\x1a\x0e.user.Response\"\x1b\x82\xd3\xe4\x93\x02\x15:\x01*\"\x10/api/v3/sms/send\x12A\n" + "\x05Login\x12\x0e.user.LoginReq\x1a\x0e.user.Response\"\x18\x82\xd3\xe4\x93\x02\x12:\x01*\"\r/api/v3/login\x12C\n" + "\x04Info\x12\r.user.InfoReq\x1a\x0e.user.Response\"\x1c\x82\xd3\xe4\x93\x02\x16:\x01*\x12\x11/api/v3/user/info\x12P\n" + "\tUserItems\x12\x12.user.UserItemsReq\x1a\x0e.user.Response\"\x1f\x82\xd3\xe4\x93\x02\x19:\x01*\x12\x14/admin/v3/user/items\x12S\n" + @@ -965,7 +1019,7 @@ func file_user_user_proto_rawDescGZIP() []byte { return file_user_user_proto_rawDescData } -var file_user_user_proto_msgTypes = make([]protoimpl.MessageInfo, 14) +var file_user_user_proto_msgTypes = make([]protoimpl.MessageInfo, 15) var file_user_user_proto_goTypes = []any{ (*Response)(nil), // 0: user.Response (*LoginReq)(nil), // 1: user.LoginReq @@ -980,28 +1034,31 @@ var file_user_user_proto_goTypes = []any{ (*UpdateMobileData)(nil), // 10: user.UpdateMobileData (*RevokeBizClientReq)(nil), // 11: user.RevokeBizClientReq (*RevokeBizClientData)(nil), // 12: user.RevokeBizClientData - (*InfoReq)(nil), // 13: user.InfoReq + (*SendSmsReq)(nil), // 13: user.SendSmsReq + (*InfoReq)(nil), // 14: user.InfoReq } var file_user_user_proto_depIdxs = []int32{ 7, // 0: user.UsersByIdsData.items:type_name -> user.UserBriefItem - 1, // 1: user.User.Login:input_type -> user.LoginReq - 13, // 2: user.User.Info:input_type -> user.InfoReq - 2, // 3: user.User.UserItems:input_type -> user.UserItemsReq - 3, // 4: user.User.UserStatus:input_type -> user.UserStatusReq - 4, // 5: user.User.EnsureBizIdentity:input_type -> user.EnsureBizIdentityReq - 6, // 6: user.User.UsersByIds:input_type -> user.UsersByIdsReq - 9, // 7: user.User.UpdateMobile:input_type -> user.UpdateMobileReq - 11, // 8: user.User.RevokeBizClient:input_type -> user.RevokeBizClientReq - 0, // 9: user.User.Login:output_type -> user.Response - 0, // 10: user.User.Info:output_type -> user.Response - 0, // 11: user.User.UserItems:output_type -> user.Response - 0, // 12: user.User.UserStatus:output_type -> user.Response - 5, // 13: user.User.EnsureBizIdentity:output_type -> user.EnsureBizIdentityData - 8, // 14: user.User.UsersByIds:output_type -> user.UsersByIdsData - 10, // 15: user.User.UpdateMobile:output_type -> user.UpdateMobileData - 12, // 16: user.User.RevokeBizClient:output_type -> user.RevokeBizClientData - 9, // [9:17] is the sub-list for method output_type - 1, // [1:9] is the sub-list for method input_type + 13, // 1: user.User.SendSms:input_type -> user.SendSmsReq + 1, // 2: user.User.Login:input_type -> user.LoginReq + 14, // 3: user.User.Info:input_type -> user.InfoReq + 2, // 4: user.User.UserItems:input_type -> user.UserItemsReq + 3, // 5: user.User.UserStatus:input_type -> user.UserStatusReq + 4, // 6: user.User.EnsureBizIdentity:input_type -> user.EnsureBizIdentityReq + 6, // 7: user.User.UsersByIds:input_type -> user.UsersByIdsReq + 9, // 8: user.User.UpdateMobile:input_type -> user.UpdateMobileReq + 11, // 9: user.User.RevokeBizClient:input_type -> user.RevokeBizClientReq + 0, // 10: user.User.SendSms:output_type -> user.Response + 0, // 11: user.User.Login:output_type -> user.Response + 0, // 12: user.User.Info:output_type -> user.Response + 0, // 13: user.User.UserItems:output_type -> user.Response + 0, // 14: user.User.UserStatus:output_type -> user.Response + 5, // 15: user.User.EnsureBizIdentity:output_type -> user.EnsureBizIdentityData + 8, // 16: user.User.UsersByIds:output_type -> user.UsersByIdsData + 10, // 17: user.User.UpdateMobile:output_type -> user.UpdateMobileData + 12, // 18: user.User.RevokeBizClient:output_type -> user.RevokeBizClientData + 10, // [10:19] is the sub-list for method output_type + 1, // [1:10] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name 1, // [1:1] is the sub-list for extension extendee 0, // [0:1] is the sub-list for field type_name @@ -1018,7 +1075,7 @@ func file_user_user_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_user_user_proto_rawDesc), len(file_user_user_proto_rawDesc)), NumEnums: 0, - NumMessages: 14, + NumMessages: 15, NumExtensions: 0, NumServices: 1, }, diff --git a/rpc/user/pb/user_grpc.pb.go b/rpc/user/pb/user_grpc.pb.go index 063ab4a..227a6d0 100644 --- a/rpc/user/pb/user_grpc.pb.go +++ b/rpc/user/pb/user_grpc.pb.go @@ -19,6 +19,7 @@ import ( const _ = grpc.SupportPackageIsVersion9 const ( + User_SendSms_FullMethodName = "/user.User/SendSms" User_Login_FullMethodName = "/user.User/Login" User_Info_FullMethodName = "/user.User/Info" User_UserItems_FullMethodName = "/user.User/UserItems" @@ -33,6 +34,7 @@ const ( // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type UserClient interface { + SendSms(ctx context.Context, in *SendSmsReq, opts ...grpc.CallOption) (*Response, error) Login(ctx context.Context, in *LoginReq, opts ...grpc.CallOption) (*Response, error) Info(ctx context.Context, in *InfoReq, opts ...grpc.CallOption) (*Response, error) UserItems(ctx context.Context, in *UserItemsReq, opts ...grpc.CallOption) (*Response, error) @@ -51,6 +53,16 @@ func NewUserClient(cc grpc.ClientConnInterface) UserClient { return &userClient{cc} } +func (c *userClient) SendSms(ctx context.Context, in *SendSmsReq, opts ...grpc.CallOption) (*Response, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Response) + err := c.cc.Invoke(ctx, User_SendSms_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *userClient) Login(ctx context.Context, in *LoginReq, opts ...grpc.CallOption) (*Response, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(Response) @@ -135,6 +147,7 @@ func (c *userClient) RevokeBizClient(ctx context.Context, in *RevokeBizClientReq // All implementations must embed UnimplementedUserServer // for forward compatibility. type UserServer interface { + SendSms(context.Context, *SendSmsReq) (*Response, error) Login(context.Context, *LoginReq) (*Response, error) Info(context.Context, *InfoReq) (*Response, error) UserItems(context.Context, *UserItemsReq) (*Response, error) @@ -153,6 +166,9 @@ type UserServer interface { // pointer dereference when methods are called. type UnimplementedUserServer struct{} +func (UnimplementedUserServer) SendSms(context.Context, *SendSmsReq) (*Response, error) { + return nil, status.Error(codes.Unimplemented, "method SendSms not implemented") +} func (UnimplementedUserServer) Login(context.Context, *LoginReq) (*Response, error) { return nil, status.Error(codes.Unimplemented, "method Login not implemented") } @@ -198,6 +214,24 @@ func RegisterUserServer(s grpc.ServiceRegistrar, srv UserServer) { s.RegisterService(&User_ServiceDesc, srv) } +func _User_SendSms_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SendSmsReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(UserServer).SendSms(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: User_SendSms_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(UserServer).SendSms(ctx, req.(*SendSmsReq)) + } + return interceptor(ctx, in, info, handler) +} + func _User_Login_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(LoginReq) if err := dec(in); err != nil { @@ -349,6 +383,10 @@ var User_ServiceDesc = grpc.ServiceDesc{ ServiceName: "user.User", HandlerType: (*UserServer)(nil), Methods: []grpc.MethodDesc{ + { + MethodName: "SendSms", + Handler: _User_SendSms_Handler, + }, { MethodName: "Login", Handler: _User_Login_Handler, diff --git a/rpc/user/user.pb b/rpc/user/user.pb index 8472f732b01c1e9a07bfa469814dcd6d0d929f87..808ebacaa6a5a83a1a632ca5e3deb34902e8e603 100644 GIT binary patch delta 163 zcmbPKHKTfilPS{`fz2+aF|1rxTwKAac`3oU#gqBWW$Hz^Sc{WW^HL?46c{yFgCHCw zPA+!OytMqF)Iy_PRxXy%;?yD`4=#47Mj-*cQlN+)lnYcP#Rm}yN-ZwP&nr$E tWHOVel7N0?6?F|5LzT Date: Thu, 3 Sep 2026 18:23:18 +0800 Subject: [PATCH 3/5] feat: verify code --- services/user/internal/logic/loginLogic.go | 68 +++++++++++++++++++- services/user/internal/logic/sendSmsLogic.go | 21 ++++++ 2 files changed, 88 insertions(+), 1 deletion(-) diff --git a/services/user/internal/logic/loginLogic.go b/services/user/internal/logic/loginLogic.go index bb1557e..f349890 100644 --- a/services/user/internal/logic/loginLogic.go +++ b/services/user/internal/logic/loginLogic.go @@ -3,11 +3,13 @@ package logic import ( "context" + "lone-services/pkg/modelbase" "lone-services/pkg/utils" "lone-services/pkg/validate" user "lone-services/rpc/user/pb" userconfig "lone-services/services/user/internal/config" "lone-services/services/user/internal/dao" + "lone-services/services/user/internal/model" "lone-services/services/user/internal/svc" "lone-services/services/user/validator" @@ -41,7 +43,7 @@ func (l *LoginLogic) Login(in *user.LoginReq) (*user.Response, error) { case dao.GrantTypePassword: return l.loginByPassword(req) case dao.GrantTypeSms: - return outResponse(utils.ErrorParams, "验证码登录暂未开放"), nil + return l.loginBySms(req) default: return outResponse(utils.ErrorParams, "不支持的登录方式"), nil } @@ -148,3 +150,67 @@ func (l *LoginLogic) loginByPassword(req validator.LoginValidator) (*user.Respon } return okResponse(ret), nil } + +func (l *LoginLogic) loginBySms(req validator.LoginValidator) (*user.Response, error) { + client, err := loadEnabledClient(req.ClientCode) + if err != nil { + l.Errorf("login load client: %v", err) + return failResponse(utils.Fail), nil + } + if !clientAllowsGrant(client.AllowedGrants, dao.GrantTypeSms) { + return outResponse(utils.ErrorParams, "该端不支持验证码登录"), nil + } + + if len(req.Mobile) != utils.NumberEleven { + return failResponse(utils.ErrorMobileError), nil + } + if ok, verifyErr := verifyAndConsumeSmsCode(l.ctx, dao.SmsSceneLogin, req.Mobile, req.SmsCode); verifyErr != nil { + l.Errorf("login verify sms: %v", verifyErr) + return failResponse(utils.Fail), nil + } else if !ok { + return outResponse(utils.ErrorParams, "验证码错误或已过期"), nil + } + + encryptMobile, encErr := utils.EncryptPhone(req.Mobile) + if encErr != nil { + l.Errorf("login encrypt mobile: %v", encErr) + return failResponse(utils.ErrorEncryptAesError), nil + } + + var exist dao.UserRow + userModel := model.UserModel{}.Init() + if getErr := userModel.GetOne(modelbase.Params{ + Eq: map[string]string{"mobile": encryptMobile}, + }, &exist); getErr != nil { + l.Errorf("login find user by mobile: %v", getErr) + return failResponse(utils.Fail), nil + } + if exist.Id < utils.NumberOne { + return failResponse(utils.ErrorNotFund), nil + } + if exist.Status == dao.StatusDisabled { + return outResponse(utils.Fail, "用户已禁用"), nil + } + + hasClient, clientErr := userHasEnabledClient(exist.Id, req.ClientCode) + if clientErr != nil { + l.Errorf("login check user client: %v", clientErr) + return failResponse(utils.Fail), nil + } + if !hasClient { + return outResponse(utils.ErrorParams, "该端未开通或已禁用"), nil + } + + row, userErr := loadEnabledUser(exist.Id) + if userErr != nil { + l.Errorf("login load user: %v", userErr) + return failResponse(utils.ErrorNotFund), nil + } + + ret, tokenErr := issueLoginToken(l.ctx, l.svcCtx.JWT, l.svcCtx.SaleSvcName, row, client) + if tokenErr != nil { + l.Errorf("login issue token: %v", tokenErr) + return failResponse(utils.Fail), nil + } + return okResponse(ret), nil +} diff --git a/services/user/internal/logic/sendSmsLogic.go b/services/user/internal/logic/sendSmsLogic.go index c5193e9..ec65892 100644 --- a/services/user/internal/logic/sendSmsLogic.go +++ b/services/user/internal/logic/sendSmsLogic.go @@ -104,6 +104,27 @@ func smsFreqRedisKey(scene int32, mobile string) string { return "sms:freq:" + strconv.Itoa(int(scene)) + ":" + mobile } +func verifyAndConsumeSmsCode(ctx context.Context, scene int32, mobile, code string) (bool, error) { + if code == utils.StringEmpty { + return false, nil + } + codeKey := smsCodeRedisKey(scene, mobile) + cached, err := redis.Client.Get(ctx, codeKey).Result() + if err == redis.Nil { + return false, nil + } + if err != nil { + return false, err + } + if cached != code { + return false, nil + } + if delErr := redis.Client.Del(ctx, codeKey).Err(); delErr != nil { + return false, delErr + } + return true, nil +} + func genDigitCode(length int) (string, error) { if length <= utils.NumberZero { return utils.StringEmpty, fmt.Errorf("invalid code length") From 0a717e4abd882dea0b54883f9113b0d6fe5a35ee Mon Sep 17 00:00:00 2001 From: zzw <1464003642@qq.com> Date: Fri, 4 Sep 2026 09:07:43 +0800 Subject: [PATCH 4/5] fix: toml clear --- services/chore/etc/chore.yaml | 7 --- services/chore/run.toml | 58 ------------------- .../{productserver.go => productServer.go} | 0 services/task/run.toml | 46 --------------- .../server/{userserver.go => userServer.go} | 0 5 files changed, 111 deletions(-) delete mode 100644 services/chore/etc/chore.yaml delete mode 100644 services/chore/run.toml rename services/product/internal/server/{productserver.go => productServer.go} (100%) delete mode 100644 services/task/run.toml rename services/user/internal/server/{userserver.go => userServer.go} (100%) diff --git a/services/chore/etc/chore.yaml b/services/chore/etc/chore.yaml deleted file mode 100644 index 38d8d4e..0000000 --- a/services/chore/etc/chore.yaml +++ /dev/null @@ -1,7 +0,0 @@ -Nacos: - Hosts: - - rnacos:8848 - NamespaceId: test - Group: LONE_SERVICES - RegisterIP: chore - ConfigID: chore \ No newline at end of file diff --git a/services/chore/run.toml b/services/chore/run.toml deleted file mode 100644 index 84a5227..0000000 --- a/services/chore/run.toml +++ /dev/null @@ -1,58 +0,0 @@ -[base] - login_out_time=43200 #api接口超时时间分 - login_refresh_out_time=83200 - name = "chore-service" - listenOn = "0.0.0.0:10103" - mode = "dev" -[log] - path = "logs" - serviceName = "chore-service" - mode = "file" - encoding = "plain" - level = "info" - keepDays = 7 - maxSize = 50 - maxBackups = 5 - compress = false - -[oss] - accessKeyId = "LTAI5t7U8KvxSiPE5auwQUuu" - accessKeySecret = "xjfgbQQRpL4TcIspNuClg6bYADa3pk" - roleArn = "acs:ram::1663896742753915:role/oss-upload" - roleSessionName = "oss-upload" - region = "oss-cn-hangzhou" - bucketName = "lone-images" - endpoint = "https://oss-accelerate.aliyuncs.com" - host = "https://images.ailuowan.com" - dir = "upload/" - expireSeconds = 3600 - -[mysql] - host = '39.106.171.204' - port = 33066 - user = 'root' - password = 'MOLXRZNOU4Y4' - database = 'dms-chore' - charset = 'utf8mb4' - prefix = '' - debug = true -[mysql_read] - host = '39.106.171.204' - port = 33066 - user = 'root' - password = 'MOLXRZNOU4Y4' - database = 'dms-chore' - charset = 'utf8mb4' - prefix = '' - -[redis] - host = '39.106.171.204' - password = 'lLMLcuPpzSj' - port = 6379 - db = 0 - -[encrypt] - data_key = "u2t9T3luZtoRfhBstkFN6TiIMW38BA8a" -[services] - product = "product-service" - sale = "sale-service" \ No newline at end of file diff --git a/services/product/internal/server/productserver.go b/services/product/internal/server/productServer.go similarity index 100% rename from services/product/internal/server/productserver.go rename to services/product/internal/server/productServer.go diff --git a/services/task/run.toml b/services/task/run.toml deleted file mode 100644 index 772c1fb..0000000 --- a/services/task/run.toml +++ /dev/null @@ -1,46 +0,0 @@ -[base] - port = 8060 - name = "task-service" - listenOn = "0.0.0.0:10900" - mode = "dev" -[log] - path = "logs" - serviceName = "task-service" - mode = "file" - encoding = "plain" - level = "info" - keepDays = 7 - maxSize = 50 - maxBackups = 5 - compress = false -# 测试的地址 -[mysql] - host = '39.106.171.204' - port = 33066 - user = 'root' - password = 'MOLXRZNOU4Y4' - database = 'dms-task' - charset = 'utf8mb4' - prefix = '' - debug = true -[mysql_read] - host = '39.106.171.204' - port = 33066 - user = 'root' - password = 'MOLXRZNOU4Y4' - database = 'dms-task' - charset = 'utf8mb4' - prefix = '' - -[redis] - host = '39.106.171.204' - password = 'lLMLcuPpzSj' - port = 6379 - db = 1 -[log-redis] - host = '39.106.171.204' - password = 'lLMLcuPpzSj' - port = 6379 - db = 3 -[services] - product = "product-service" \ No newline at end of file diff --git a/services/user/internal/server/userserver.go b/services/user/internal/server/userServer.go similarity index 100% rename from services/user/internal/server/userserver.go rename to services/user/internal/server/userServer.go From f8c74199643a223c1a6edfcf98793d6a8ffb4710 Mon Sep 17 00:00:00 2001 From: zzw <1464003642@qq.com> Date: Fri, 4 Sep 2026 15:56:12 +0800 Subject: [PATCH 5/5] feat: backuser list --- services/sale/run.toml | 6 +++-- services/user/internal/dao/const.go | 4 ++++ services/user/internal/dao/user.go | 2 +- services/user/internal/logic/authHelper.go | 2 +- .../user/internal/logic/userItemsLogic.go | 23 +++++++++++++++---- 5 files changed, 29 insertions(+), 8 deletions(-) diff --git a/services/sale/run.toml b/services/sale/run.toml index 26ee240..6f83ec6 100644 --- a/services/sale/run.toml +++ b/services/sale/run.toml @@ -2,7 +2,7 @@ login_out_time=43200 #api接口超时时间分 login_refresh_out_time=83200 name = "sale-service" - listenOn = "0.0.0.0:10700" + listenOn = "0.0.0.0:10300" mode = "dev" [log] path = "logs" @@ -53,5 +53,7 @@ [encrypt] data_key = "u2t9T3luZtoRfhBstkFN6TiIMW38BA8a" + [services] - chore = "chore-service" \ No newline at end of file + chore = "chore-service" + user = "user-service" \ No newline at end of file diff --git a/services/user/internal/dao/const.go b/services/user/internal/dao/const.go index 9f13c13..242206e 100644 --- a/services/user/internal/dao/const.go +++ b/services/user/internal/dao/const.go @@ -27,5 +27,9 @@ const ( SubjectTypeStore = "store" SubjectTypeUser = "user" + GenderMale uint8 = 1 + GenderFemale uint8 = 2 + GenderUnknown uint8 = 3 + ExtraJsonEmpty = "{}" ) diff --git a/services/user/internal/dao/user.go b/services/user/internal/dao/user.go index 5b724b3..3c28d64 100644 --- a/services/user/internal/dao/user.go +++ b/services/user/internal/dao/user.go @@ -29,7 +29,7 @@ type UserCreate struct { type UserListItem struct { Id int64 `json:"id"` Name string `json:"name"` - HeadPortrait string `json:"head_portrait"` + Avatar string `json:"avatar"` Mobile string `json:"mobile"` OriginMobile string `json:"origin_mobile"` Gender uint8 `json:"gender"` diff --git a/services/user/internal/logic/authHelper.go b/services/user/internal/logic/authHelper.go index fc4e244..96a6175 100644 --- a/services/user/internal/logic/authHelper.go +++ b/services/user/internal/logic/authHelper.go @@ -133,7 +133,7 @@ func resolveOrCreateWechatUser(ctx context.Context, db *gorm.DB, openid, unionid if userId < utils.NumberOne { userModel := model.UserModel{}.Init() userModel.Base = userModel.Base.WithTX(tx) - add := dao.UserCreate{Status: dao.StatusEnabled} + add := dao.UserCreate{Status: dao.StatusEnabled, Gender: dao.GenderUnknown} if err := userModel.Create(&add); err != nil { return err } diff --git a/services/user/internal/logic/userItemsLogic.go b/services/user/internal/logic/userItemsLogic.go index 60c2d43..572917f 100644 --- a/services/user/internal/logic/userItemsLogic.go +++ b/services/user/internal/logic/userItemsLogic.go @@ -2,6 +2,7 @@ package logic import ( "context" + "fmt" "lone-services/pkg/modelbase" "lone-services/pkg/utils" @@ -49,10 +50,23 @@ func (l *UserItemsLogic) UserItems(in *user.UserItemsReq) (*user.Response, error size = modelbase.DefaultSize } + prefix := modelbase.Prefix() + existsSQL := fmt.Sprintf(`EXISTS ( + SELECT 1 FROM %suser_client uc + INNER JOIN %sclient c ON c.code = uc.client_code + WHERE uc.user_id = %susers.id + AND uc.status = %d + AND c.status = %d + AND c.subject_type = ? + )`, prefix, prefix, prefix, dao.StatusEnabled, dao.StatusEnabled) + params := modelbase.Params{ Order: "id DESC", Page: page, Size: size, + Other: map[string]string{ + existsSQL: dao.SubjectTypeUser, + }, } if v.Mobile != utils.StringEmpty { phone := utils.GetSearchPhone(v.Mobile) @@ -73,15 +87,16 @@ func (l *UserItemsLogic) UserItems(in *user.UserItemsReq) (*user.Response, error items := make([]dao.UserListItem, 0, len(list)) for _, row := range list { - headPortrait, _ := utils.BuildImageURL(row.Avatar).(string) mobile := row.Mobile - if plain, dErr := utils.DecryptPhone(row.Mobile); dErr == nil { - mobile = utils.DecryptPhoneReplace(plain) + if len(row.Mobile) >= 72 { + if plain, dErr := utils.DecryptPhone(row.Mobile); dErr == nil { + mobile = utils.DecryptPhoneReplace(plain) + } } items = append(items, dao.UserListItem{ Id: row.Id, Name: row.Name, - HeadPortrait: headPortrait, + Avatar: row.Avatar, Mobile: mobile, OriginMobile: row.Mobile, Gender: row.Gender,