code migration

This commit is contained in:
landaiqing
2024-11-15 02:02:19 +08:00
parent b2d753e832
commit 34c4690f80
85 changed files with 4349 additions and 421 deletions

View File

@@ -0,0 +1,44 @@
package generate
import (
"context"
"encoding/json"
"time"
"github.com/redis/go-redis/v9"
"github.com/wenlng/go-captcha-assets/helper"
"github.com/wenlng/go-captcha/v2/click"
"schisandra-album-cloud-microservices/app/core/api/common/constant"
)
// GenerateBasicTextCaptcha generates a basic text captcha and saves it to redis.
func GenerateBasicTextCaptcha(capt click.Captcha, redis redis.Client, ctx context.Context) map[string]interface{} {
captData, err := capt.Generate()
if err != nil {
return nil
}
dotData := captData.GetData()
if dotData == nil {
return nil
}
var masterImageBase64, thumbImageBase64 string
masterImageBase64 = captData.GetMasterImage().ToBase64()
thumbImageBase64 = captData.GetThumbImage().ToBase64()
dotsByte, err := json.Marshal(dotData)
if err != nil {
return nil
}
key := helper.StringToMD5(string(dotsByte))
err = redis.Set(ctx, constant.UserCaptchaPrefix+key, dotsByte, time.Minute).Err()
if err != nil {
return nil
}
return map[string]interface{}{
"key": key,
"image": masterImageBase64,
"thumb": thumbImageBase64,
}
}

View File

@@ -0,0 +1,44 @@
package generate
import (
"context"
"encoding/json"
"log"
"time"
"github.com/redis/go-redis/v9"
"github.com/wenlng/go-captcha-assets/helper"
"github.com/wenlng/go-captcha/v2/click"
"schisandra-album-cloud-microservices/app/core/api/common/constant"
)
// GenerateClickShapeCaptcha generate click shape captcha
func GenerateClickShapeCaptcha(click click.Captcha, redis redis.Client, ctx context.Context) map[string]interface{} {
captData, err := click.Generate()
if err != nil {
log.Fatalln(err)
}
dotData := captData.GetData()
if dotData == nil {
return nil
}
var masterImageBase64, thumbImageBase64 string
masterImageBase64 = captData.GetMasterImage().ToBase64()
thumbImageBase64 = captData.GetThumbImage().ToBase64()
dotsByte, err := json.Marshal(dotData)
if err != nil {
return nil
}
key := helper.StringToMD5(string(dotsByte))
err = redis.Set(ctx, constant.UserCaptchaPrefix+key, dotsByte, time.Minute).Err()
if err != nil {
return nil
}
return map[string]interface{}{
"key": key,
"image": masterImageBase64,
"thumb": thumbImageBase64,
}
}

View File

@@ -0,0 +1,42 @@
package generate
import (
"context"
"encoding/json"
"errors"
"time"
"github.com/redis/go-redis/v9"
"github.com/wenlng/go-captcha-assets/helper"
"github.com/wenlng/go-captcha/v2/rotate"
"schisandra-album-cloud-microservices/app/core/api/common/constant"
)
// GenerateRotateCaptcha generate rotate captcha
func GenerateRotateCaptcha(captcha rotate.Captcha, redis *redis.Client, ctx context.Context) (map[string]interface{}, error) {
captchaData, err := captcha.Generate()
if err != nil {
return nil, err
}
blockData := captchaData.GetData()
if blockData == nil {
return nil, errors.New("captcha data is nil")
}
masterImageBase64 := captchaData.GetMasterImage().ToBase64()
thumbImageBase64 := captchaData.GetThumbImage().ToBase64()
dotsByte, err := json.Marshal(blockData)
if err != nil {
return nil, err
}
key := helper.StringToMD5(string(dotsByte))
err = redis.Set(ctx, constant.UserCaptchaPrefix+key, dotsByte, time.Minute).Err()
if err != nil {
return nil, err
}
return map[string]interface{}{
"key": key,
"image": masterImageBase64,
"thumb": thumbImageBase64,
}, nil
}

View File

@@ -0,0 +1,50 @@
package generate
import (
"context"
"encoding/json"
"errors"
"time"
"github.com/redis/go-redis/v9"
"github.com/wenlng/go-captcha-assets/helper"
"github.com/wenlng/go-captcha/v2/slide"
"schisandra-album-cloud-microservices/app/core/api/common/constant"
)
// GenerateSlideBasicCaptcha generate slide basic captcha
func GenerateSlideBasicCaptcha(slide slide.Captcha, redis *redis.Client, ctx context.Context) (map[string]interface{}, error) {
captData, err := slide.Generate()
if err != nil {
return nil, err
}
blockData := captData.GetData()
if blockData == nil {
return nil, errors.New("block data is nil")
}
var masterImageBase64, tileImageBase64 string
masterImageBase64 = captData.GetMasterImage().ToBase64()
tileImageBase64 = captData.GetTileImage().ToBase64()
dotsByte, err := json.Marshal(blockData)
if err != nil {
return nil, err
}
key := helper.StringToMD5(string(dotsByte))
err = redis.Set(ctx, constant.UserCaptchaPrefix+key, dotsByte, time.Minute).Err()
if err != nil {
return nil, err
}
return map[string]interface{}{
"key": key,
"image": masterImageBase64,
"thumb": tileImageBase64,
"thumb_width": blockData.Width,
"thumb_height": blockData.Height,
"thumb_x": blockData.TileX,
"thumb_y": blockData.TileY,
}, nil
}

View File

@@ -0,0 +1,50 @@
package generate
import (
"context"
"encoding/json"
"time"
"github.com/redis/go-redis/v9"
"github.com/wenlng/go-captcha-assets/helper"
"github.com/wenlng/go-captcha/v2/slide"
"schisandra-album-cloud-microservices/app/core/api/common/constant"
)
// GenerateSlideRegionCaptcha generate slide region captcha
func GenerateSlideRegionCaptcha(slide slide.Captcha, redis redis.Client, ctx context.Context) map[string]interface{} {
captData, err := slide.Generate()
if err != nil {
return nil
}
blockData := captData.GetData()
if blockData == nil {
return nil
}
var masterImageBase64, tileImageBase64 string
masterImageBase64 = captData.GetMasterImage().ToBase64()
tileImageBase64 = captData.GetTileImage().ToBase64()
blockByte, err := json.Marshal(blockData)
if err != nil {
return nil
}
key := helper.StringToMD5(string(blockByte))
err = redis.Set(ctx, constant.UserCaptchaPrefix+key, blockByte, time.Minute).Err()
if err != nil {
return nil
}
return map[string]interface{}{
"code": 0,
"key": key,
"image": masterImageBase64,
"tile": tileImageBase64,
"tile_width": blockData.Width,
"tile_height": blockData.Height,
"tile_x": blockData.TileX,
"tile_y": blockData.TileY,
}
}

View File

@@ -0,0 +1,48 @@
package verify
import (
"context"
"encoding/json"
"fmt"
"strconv"
"strings"
"github.com/redis/go-redis/v9"
"github.com/wenlng/go-captcha/v2/click"
"schisandra-album-cloud-microservices/app/core/api/common/constant"
)
// VerifyBasicTextCaptcha verify basic text captcha
func VerifyBasicTextCaptcha(dots string, key string, redis *redis.Client, ctx context.Context) bool {
cacheDataByte, err := redis.Get(ctx, constant.UserCaptchaPrefix+key).Bytes()
if len(cacheDataByte) == 0 || err != nil {
return false
}
src := strings.Split(dots, ",")
var dct map[int]*click.Dot
if err := json.Unmarshal(cacheDataByte, &dct); err != nil {
return false
}
chkRet := false
if (len(dct) * 2) == len(src) {
for i := 0; i < len(dct); i++ {
dot := dct[i]
j := i * 2
k := i*2 + 1
sx, _ := strconv.ParseFloat(fmt.Sprintf("%v", src[j]), 64)
sy, _ := strconv.ParseFloat(fmt.Sprintf("%v", src[k]), 64)
chkRet = click.CheckPoint(int64(sx), int64(sy), int64(dot.X), int64(dot.Y), int64(dot.Width), int64(dot.Height), 0)
if !chkRet {
break
}
}
}
if chkRet {
return true
}
return false
}

View File

@@ -0,0 +1,8 @@
package constant
const (
OAuthSourceWechat = "wechat"
OAuthSourceQQ = "qq"
OAuthSourceGithub = "github"
OAuthSourceGitee = "gitee"
)

View File

@@ -1,9 +1,26 @@
package constant
// 用户相关的redis key
const (
UserClientPrefix string = "user:client:"
UserSessionPrefix string = "user:session:"
UserCaptchaPrefix string = "user:captcha:"
UserTokenPrefix string = "user:token:"
UserSmsRedisPrefix string = "user:sms:"
UserQrcodePrefix = "user:qrcode:"
)
// 评论相关的redis key
const (
CommentSubmitCaptchaPrefix = "comment:submit:captcha:"
CommentOfflineMessagePrefix = "comment:offline:message:"
CommentLikeLockPrefix = "comment:like:lock:"
CommentDislikeLockPrefix = "comment:dislike:lock:"
CommentLikeListPrefix = "comment:like:list:"
CommentUserListPrefix = "comment:user:list:"
)
// 系统相关的redis key
const (
SystemApiNonceRedisKey = "system:controller:nonce:"
)

View File

@@ -0,0 +1,20 @@
package utils
import (
"fmt"
"math/rand"
"strings"
"time"
)
func GenValidateCode(width int) string {
numeric := [10]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
r := len(numeric)
rand.New(rand.NewSource(time.Now().UnixNano()))
var sb strings.Builder
for i := 0; i < width; i++ {
fmt.Fprintf(&sb, "%d", numeric[rand.Intn(r)])
}
return sb.String()
}

View File

@@ -42,6 +42,29 @@ type (
}
)
// OAuth请求参数
type (
// OAuth请求参数
OAuthRequest {
state string `form:"state"`
}
// OAuth回调请求参数
OAuthCallbackRequest {
Code string `form:"code"`
}
OAuthWechatRequest {
client_id string `form:"client_id"`
}
)
type (
SmsSendRequest {
Phone string `json:"phone"`
Angle int64 `json:"angle"`
Key string `json:"key"`
}
)
// 统一响应参数
type (
Response {
@@ -54,7 +77,7 @@ type (
// 用户服务
@server (
group: user // 微服务分组
prefix: /api/auth/user // 微服务前缀
prefix: /api/user // 微服务前缀
timeout: 10s // 超时时间
maxBytes: 1048576 // 最大请求大小
signature: true // 是否开启签名验证
@@ -69,14 +92,17 @@ service core {
// 手机号登录
@handler phoneLogin
post /phone_login (PhoneLoginRequest) returns (Response)
post /phone/login (PhoneLoginRequest) returns (Response)
// 重置密码
@handler resetPassword
post /reset_password (ResetPasswordRequest) returns (Response)
post /reset/password (ResetPasswordRequest) returns (Response)
@handler refreshToken
post /token/refresh returns (Response)
@handler getUserDevice
get /device
}
// 客户端服务
@@ -92,6 +118,95 @@ service core {
)
service core {
@handler generateClientId
get /generate_client_id returns (Response)
get /generate returns (Response)
}
@server (
group: websocket // 微服务分组
prefix: /api/ws // 微服务前缀
timeout: 10s // 超时时间
Recover: true // 是否开启自动恢复
)
service core {
@handler qrcodeWebsocket
get /qrcode
@handler messageWebsocket
get /message
}
@server (
group: oauth // 微服务分组
prefix: /api/oauth // 微服务前缀
timeout: 10s // 超时时间
maxBytes: 1048576 // 最大请求大小
signature: false // 是否开启签名验证
middleware: SecurityHeadersMiddleware // 注册中间件
MaxConns: true // 是否开启最大连接数限制
Recover: true // 是否开启自动恢复
)
service core {
@handler getGiteeOauthUrl
get /gitee/url returns (Response)
@handler getGithubOauthUrl
get /github/url (OAuthRequest) returns (Response)
@handler getQqOauthUrl
get /qq/url (OAuthRequest) returns (Response)
@handler giteeCallback
get /gitee/callback (OAuthCallbackRequest)
@handler githubCallback
get /github/callback (OAuthCallbackRequest)
@handler qqCallback
get /qq/callback (OAuthCallbackRequest)
@handler wechatCallback
get /wechat/callback
@handler getWechatQrcode
get /wechat/qrcode (OAuthWechatRequest) returns (Response)
}
@server (
group: sms // 微服务分组
prefix: /api/sms // 微服务前缀
timeout: 10s // 超时时间
maxBytes: 1048576 // 最大请求大小
signature: false // 是否开启签名验证
middleware: SecurityHeadersMiddleware // 注册中间件
MaxConns: true // 是否开启最大连接数限制
Recover: true // 是否开启自动恢复
)
service core {
@handler sendSmsByAliyun
post /ali/send (SmsSendRequest) returns (Response)
@handler sendSmsBySmsbao
post /smsbao/send (SmsSendRequest) returns (Response)
@handler sendSmsByTest
post /test/send (SmsSendRequest) returns (Response)
}
@server (
group: captcha // 微服务分组
prefix: /api/captcha // 微服务前缀
timeout: 10s // 超时时间
maxBytes: 1048576 // 最大请求大小
signature: false // 是否开启签名验证
middleware: SecurityHeadersMiddleware // 注册中间件
MaxConns: true // 是否开启最大连接数限制
Recover: true // 是否开启自动恢复
)
service core {
@handler generateRotateCaptcha
get /rotate/generate returns (Response)
@handler generateSlideBasicCaptcha
get /slide/generate returns (Response)
}

View File

@@ -10,6 +10,7 @@ import (
"schisandra-album-cloud-microservices/app/core/api/common/middleware"
"schisandra-album-cloud-microservices/app/core/api/internal/config"
"schisandra-album-cloud-microservices/app/core/api/internal/handler"
"schisandra-album-cloud-microservices/app/core/api/internal/logic/websocket"
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
"schisandra-album-cloud-microservices/app/core/api/repository/idgenerator"
)
@@ -30,6 +31,8 @@ func main() {
handler.RegisterHandlers(server, ctx)
// init id generator
idgenerator.NewIDGenerator()
// init websocket handler
websocket.InitializeWebSocketHandler(ctx)
fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)
server.Start()
}

View File

@@ -2,7 +2,7 @@ Name: main
Host: 0.0.0.0
Port: 80
Web:
Addr: localhost:8080
URL: http://localhost:5173
Middlewares:
Log: true
Mysql:
@@ -20,3 +20,55 @@ Mongo:
Password: LDQ20020618xxx
Database: schisandra-cloud-album
AuthSource: admin
Wechat:
# 微信公众号APPID
AppID: wx55251c2f83b9fc25
# 微信公众号APPSECRET
AppSecret: d511800cd53d248afe1260bb8aeed230
# 微信公众号Token
Token: LDQ20020618xxx
# 微信公众号EncodingAESKey
AESKey:
OAuth:
# Github配置
Github:
# Github客户端ID
ClientID: Ov23limqLtbVhBngctSl
# Github客户端密钥
ClientSecret: 84a5be0317b8f4c6b55039a0eb23ea4606676e66
# Github回调地址
RedirectURI: https://landaiqing.cn/api/oauth/github/callback
# Gitee配置
Gitee:
# Gitee客户端ID
ClientID: bd2ff03b5f644242d862832b8cc749015d0a7c8f163fbd5ab67886d436fb198b
# Gitee客户端密钥
ClientSecret: 3994b463aa962c878a58a1255f59a92e050e205e9204cffe48a25ad17a758f97
# Gitee回调地址
RedirectURI: https://landaiqing.cn/api/oauth/gitee/callback
# QQ配置
QQ:
# QQ客户端ID
ClientID: '102296211'
# QQ客户端密钥
ClientSecret: ukOo10SggRxHVVIr
# QQ回调地址
RedirectURI: https://landaiqing.cn/api/oauth/qq/callback
# 短信配置
SMS:
Ali:
# 阿里云API地址
Host: http://dysmsapi.aliyuncs.com
# 阿里云AccessKeyId
AccessKeyId: LTAI5tDy2edL9LhW43rnus69
# 阿里云AccessKeySecret
AccessKeySecret: YWp44dcFrBICrjZgqvJBE7ZHArZfIP
# 短信模板ID
TemplateCode: SMS_154950909
# 短信签名
Signature: 阿里云短信测试
SMSBao:
# 短信宝用户账号
Username: landaiqing
# 短信宝用户密码
Password: $LDQ20020618xxx$

View File

@@ -4,6 +4,9 @@ import "github.com/zeromicro/go-zero/rest"
type Config struct {
rest.RestConf
Web struct {
URL string
}
Auth struct {
AccessSecret string
AccessExpire int64
@@ -23,4 +26,40 @@ type Config struct {
AuthSource string
Database string
}
Wechat struct {
AppID string
AppSecret string
Token string
AESKey string
}
OAuth struct {
Github struct {
ClientID string
ClientSecret string
RedirectURI string
}
QQ struct {
ClientID string
ClientSecret string
RedirectURI string
}
Gitee struct {
ClientID string
ClientSecret string
RedirectURI string
}
}
SMS struct {
Ali struct {
Host string
AccessKeyId string
AccessKeySecret string
Signature string
TemplateCode string
}
SMSBao struct {
Username string
Password string
}
}
}

View File

@@ -0,0 +1,21 @@
package captcha
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"schisandra-album-cloud-microservices/app/core/api/internal/logic/captcha"
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
)
func GenerateRotateCaptchaHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
l := captcha.NewGenerateRotateCaptchaLogic(r.Context(), svcCtx)
resp, err := l.GenerateRotateCaptcha()
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
}
}

View File

@@ -0,0 +1,21 @@
package captcha
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"schisandra-album-cloud-microservices/app/core/api/internal/logic/captcha"
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
)
func GenerateSlideBasicCaptchaHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
l := captcha.NewGenerateSlideBasicCaptchaLogic(r.Context(), svcCtx)
resp, err := l.GenerateSlideBasicCaptcha()
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
}
}

View File

@@ -0,0 +1,21 @@
package oauth
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"schisandra-album-cloud-microservices/app/core/api/internal/logic/oauth"
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
)
func GetGiteeOauthUrlHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
l := oauth.NewGetGiteeOauthUrlLogic(r.Context(), svcCtx)
resp, err := l.GetGiteeOauthUrl()
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
}
}

View File

@@ -0,0 +1,28 @@
package oauth
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"schisandra-album-cloud-microservices/app/core/api/internal/logic/oauth"
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
"schisandra-album-cloud-microservices/app/core/api/internal/types"
)
func GetGithubOauthUrlHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.OAuthRequest
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := oauth.NewGetGithubOauthUrlLogic(r.Context(), svcCtx)
resp, err := l.GetGithubOauthUrl(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
}
}

View File

@@ -0,0 +1,28 @@
package oauth
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"schisandra-album-cloud-microservices/app/core/api/internal/logic/oauth"
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
"schisandra-album-cloud-microservices/app/core/api/internal/types"
)
func GetQqOauthUrlHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.OAuthRequest
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := oauth.NewGetQqOauthUrlLogic(r.Context(), svcCtx)
resp, err := l.GetQqOauthUrl(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
}
}

View File

@@ -0,0 +1,29 @@
package oauth
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"schisandra-album-cloud-microservices/app/core/api/internal/logic/oauth"
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
"schisandra-album-cloud-microservices/app/core/api/internal/types"
)
func GetWechatQrcodeHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.OAuthWechatRequest
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := oauth.NewGetWechatQrcodeLogic(r.Context(), svcCtx)
resp, err := l.GetWechatQrcode(r, &req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
}
}

View File

@@ -0,0 +1,29 @@
package oauth
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"schisandra-album-cloud-microservices/app/core/api/internal/logic/oauth"
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
"schisandra-album-cloud-microservices/app/core/api/internal/types"
)
func GiteeCallbackHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.OAuthCallbackRequest
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := oauth.NewGiteeCallbackLogic(r.Context(), svcCtx)
err := l.GiteeCallback(w, r, &req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.Ok(w)
}
}
}

View File

@@ -0,0 +1,29 @@
package oauth
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"schisandra-album-cloud-microservices/app/core/api/internal/logic/oauth"
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
"schisandra-album-cloud-microservices/app/core/api/internal/types"
)
func GithubCallbackHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.OAuthCallbackRequest
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := oauth.NewGithubCallbackLogic(r.Context(), svcCtx)
err := l.GithubCallback(w, r, &req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.Ok(w)
}
}
}

View File

@@ -0,0 +1,29 @@
package oauth
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"schisandra-album-cloud-microservices/app/core/api/internal/logic/oauth"
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
"schisandra-album-cloud-microservices/app/core/api/internal/types"
)
func QqCallbackHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.OAuthCallbackRequest
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := oauth.NewQqCallbackLogic(r.Context(), svcCtx)
err := l.QqCallback(w, r, &req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.Ok(w)
}
}
}

View File

@@ -0,0 +1,22 @@
package oauth
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"schisandra-album-cloud-microservices/app/core/api/internal/logic/oauth"
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
)
func WechatCallbackHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
l := oauth.NewWechatCallbackLogic(r.Context(), svcCtx)
err := l.WechatCallback(w, r)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.Ok(w)
}
}
}

View File

@@ -7,8 +7,12 @@ import (
"net/http"
"time"
captcha "schisandra-album-cloud-microservices/app/core/api/internal/handler/captcha"
client "schisandra-album-cloud-microservices/app/core/api/internal/handler/client"
oauth "schisandra-album-cloud-microservices/app/core/api/internal/handler/oauth"
sms "schisandra-album-cloud-microservices/app/core/api/internal/handler/sms"
user "schisandra-album-cloud-microservices/app/core/api/internal/handler/user"
websocket "schisandra-album-cloud-microservices/app/core/api/internal/handler/websocket"
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
"github.com/zeromicro/go-zero/rest"
@@ -21,7 +25,28 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
[]rest.Route{
{
Method: http.MethodGet,
Path: "/generate_client_id",
Path: "/rotate/generate",
Handler: captcha.GenerateRotateCaptchaHandler(serverCtx),
},
{
Method: http.MethodGet,
Path: "/slide/generate",
Handler: captcha.GenerateSlideBasicCaptchaHandler(serverCtx),
},
}...,
),
rest.WithPrefix("/api/captcha"),
rest.WithTimeout(10000*time.Millisecond),
rest.WithMaxBytes(1048576),
)
server.AddRoutes(
rest.WithMiddlewares(
[]rest.Middleware{serverCtx.SecurityHeadersMiddleware},
[]rest.Route{
{
Method: http.MethodGet,
Path: "/generate",
Handler: client.GenerateClientIdHandler(serverCtx),
},
}...,
@@ -35,6 +60,88 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
rest.WithMiddlewares(
[]rest.Middleware{serverCtx.SecurityHeadersMiddleware},
[]rest.Route{
{
Method: http.MethodGet,
Path: "/gitee/callback",
Handler: oauth.GiteeCallbackHandler(serverCtx),
},
{
Method: http.MethodGet,
Path: "/gitee/url",
Handler: oauth.GetGiteeOauthUrlHandler(serverCtx),
},
{
Method: http.MethodGet,
Path: "/github/callback",
Handler: oauth.GithubCallbackHandler(serverCtx),
},
{
Method: http.MethodGet,
Path: "/github/url",
Handler: oauth.GetGithubOauthUrlHandler(serverCtx),
},
{
Method: http.MethodGet,
Path: "/qq/callback",
Handler: oauth.QqCallbackHandler(serverCtx),
},
{
Method: http.MethodGet,
Path: "/qq/url",
Handler: oauth.GetQqOauthUrlHandler(serverCtx),
},
{
Method: http.MethodGet,
Path: "/wechat/callback",
Handler: oauth.WechatCallbackHandler(serverCtx),
},
{
Method: http.MethodGet,
Path: "/wechat/qrcode",
Handler: oauth.GetWechatQrcodeHandler(serverCtx),
},
}...,
),
rest.WithPrefix("/api/oauth"),
rest.WithTimeout(10000*time.Millisecond),
rest.WithMaxBytes(1048576),
)
server.AddRoutes(
rest.WithMiddlewares(
[]rest.Middleware{serverCtx.SecurityHeadersMiddleware},
[]rest.Route{
{
Method: http.MethodPost,
Path: "/ali/send",
Handler: sms.SendSmsByAliyunHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/smsbao/send",
Handler: sms.SendSmsBySmsbaoHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/test/send",
Handler: sms.SendSmsByTestHandler(serverCtx),
},
}...,
),
rest.WithPrefix("/api/sms"),
rest.WithTimeout(10000*time.Millisecond),
rest.WithMaxBytes(1048576),
)
server.AddRoutes(
rest.WithMiddlewares(
[]rest.Middleware{serverCtx.SecurityHeadersMiddleware},
[]rest.Route{
{
Method: http.MethodGet,
Path: "/device",
Handler: user.GetUserDeviceHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/login",
@@ -42,12 +149,12 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
},
{
Method: http.MethodPost,
Path: "/phone_login",
Path: "/phone/login",
Handler: user.PhoneLoginHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/reset_password",
Path: "/reset/password",
Handler: user.ResetPasswordHandler(serverCtx),
},
{
@@ -58,8 +165,25 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
}...,
),
rest.WithSignature(serverCtx.Config.Signature),
rest.WithPrefix("/api/auth/user"),
rest.WithPrefix("/api/user"),
rest.WithTimeout(10000*time.Millisecond),
rest.WithMaxBytes(1048576),
)
server.AddRoutes(
[]rest.Route{
{
Method: http.MethodGet,
Path: "/message",
Handler: websocket.MessageWebsocketHandler(serverCtx),
},
{
Method: http.MethodGet,
Path: "/qrcode",
Handler: websocket.QrcodeWebsocketHandler(serverCtx),
},
},
rest.WithPrefix("/api/ws"),
rest.WithTimeout(10000*time.Millisecond),
)
}

View File

@@ -0,0 +1,28 @@
package sms
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"schisandra-album-cloud-microservices/app/core/api/internal/logic/sms"
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
"schisandra-album-cloud-microservices/app/core/api/internal/types"
)
func SendSmsByAliyunHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.SmsSendRequest
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := sms.NewSendSmsByAliyunLogic(r.Context(), svcCtx)
resp, err := l.SendSmsByAliyun(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
}
}

View File

@@ -0,0 +1,28 @@
package sms
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"schisandra-album-cloud-microservices/app/core/api/internal/logic/sms"
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
"schisandra-album-cloud-microservices/app/core/api/internal/types"
)
func SendSmsBySmsbaoHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.SmsSendRequest
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := sms.NewSendSmsBySmsbaoLogic(r.Context(), svcCtx)
resp, err := l.SendSmsBySmsbao(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
}
}

View File

@@ -0,0 +1,28 @@
package sms
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"schisandra-album-cloud-microservices/app/core/api/internal/logic/sms"
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
"schisandra-album-cloud-microservices/app/core/api/internal/types"
)
func SendSmsByTestHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.SmsSendRequest
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := sms.NewSendSmsByTestLogic(r.Context(), svcCtx)
resp, err := l.SendSmsByTest(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
}
}

View File

@@ -0,0 +1,22 @@
package user
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"schisandra-album-cloud-microservices/app/core/api/internal/logic/user"
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
)
func GetUserDeviceHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
l := user.NewGetUserDeviceLogic(r.Context(), svcCtx)
err := l.GetUserDevice(r)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.Ok(w)
}
}
}

View File

@@ -0,0 +1,22 @@
package websocket
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"schisandra-album-cloud-microservices/app/core/api/internal/logic/websocket"
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
)
func MessageWebsocketHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
l := websocket.NewMessageWebsocketLogic(r.Context(), svcCtx)
err := l.MessageWebsocket(w, r)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.Ok(w)
}
}
}

View File

@@ -0,0 +1,22 @@
package websocket
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"schisandra-album-cloud-microservices/app/core/api/internal/logic/websocket"
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
)
func QrcodeWebsocketHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
l := websocket.NewQrcodeWebsocketLogic(r.Context(), svcCtx)
err := l.QrcodeWebsocket(w, r)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.Ok(w)
}
}
}

View File

@@ -0,0 +1,34 @@
package captcha
import (
"context"
"schisandra-album-cloud-microservices/app/core/api/common/captcha/generate"
"schisandra-album-cloud-microservices/app/core/api/common/response"
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
"schisandra-album-cloud-microservices/app/core/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type GenerateRotateCaptchaLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGenerateRotateCaptchaLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GenerateRotateCaptchaLogic {
return &GenerateRotateCaptchaLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GenerateRotateCaptchaLogic) GenerateRotateCaptcha() (resp *types.Response, err error) {
captcha, err := generate.GenerateRotateCaptcha(l.svcCtx.RotateCaptcha, l.svcCtx.RedisClient, l.ctx)
if err != nil {
return response.Error(), err
}
return response.SuccessWithData(captcha), nil
}

View File

@@ -0,0 +1,34 @@
package captcha
import (
"context"
"schisandra-album-cloud-microservices/app/core/api/common/captcha/generate"
"schisandra-album-cloud-microservices/app/core/api/common/response"
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
"schisandra-album-cloud-microservices/app/core/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type GenerateSlideBasicCaptchaLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGenerateSlideBasicCaptchaLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GenerateSlideBasicCaptchaLogic {
return &GenerateSlideBasicCaptchaLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GenerateSlideBasicCaptchaLogic) GenerateSlideBasicCaptcha() (resp *types.Response, err error) {
captcha, err := generate.GenerateSlideBasicCaptcha(l.svcCtx.SlideCaptcha, l.svcCtx.RedisClient, l.ctx)
if err != nil {
return response.Error(), err
}
return response.SuccessWithData(captcha), nil
}

View File

@@ -0,0 +1,32 @@
package oauth
import (
"context"
"schisandra-album-cloud-microservices/app/core/api/common/response"
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
"schisandra-album-cloud-microservices/app/core/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type GetGiteeOauthUrlLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetGiteeOauthUrlLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetGiteeOauthUrlLogic {
return &GetGiteeOauthUrlLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetGiteeOauthUrlLogic) GetGiteeOauthUrl() (resp *types.Response, err error) {
clientID := l.svcCtx.Config.OAuth.Gitee.ClientID
redirectURI := l.svcCtx.Config.OAuth.Gitee.RedirectURI
url := "https://gitee.com/oauth/authorize?client_id=" + clientID + "&redirect_uri=" + redirectURI + "&response_type=code"
return response.SuccessWithData(url), nil
}

View File

@@ -0,0 +1,32 @@
package oauth
import (
"context"
"schisandra-album-cloud-microservices/app/core/api/common/response"
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
"schisandra-album-cloud-microservices/app/core/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type GetGithubOauthUrlLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetGithubOauthUrlLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetGithubOauthUrlLogic {
return &GetGithubOauthUrlLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetGithubOauthUrlLogic) GetGithubOauthUrl(req *types.OAuthRequest) (resp *types.Response, err error) {
clientId := l.svcCtx.Config.OAuth.Github.ClientID
redirectUrl := l.svcCtx.Config.OAuth.Github.RedirectURI
url := "https://github.com/login/oauth/authorize?client_id=" + clientId + "&redirect_uri=" + redirectUrl + "&state=" + req.State
return response.SuccessWithData(url), nil
}

View File

@@ -0,0 +1,32 @@
package oauth
import (
"context"
"schisandra-album-cloud-microservices/app/core/api/common/response"
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
"schisandra-album-cloud-microservices/app/core/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type GetQqOauthUrlLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetQqOauthUrlLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetQqOauthUrlLogic {
return &GetQqOauthUrlLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetQqOauthUrlLogic) GetQqOauthUrl(req *types.OAuthRequest) (resp *types.Response, err error) {
clientId := l.svcCtx.Config.OAuth.QQ.ClientID
redirectURI := l.svcCtx.Config.OAuth.QQ.RedirectURI
url := "https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=" + clientId + "&redirect_uri=" + redirectURI + "&state=" + req.State
return response.SuccessWithData(url), nil
}

View File

@@ -0,0 +1,64 @@
package oauth
import (
"context"
"encoding/json"
"net/http"
"time"
"github.com/ArtisanCloud/PowerWeChat/v3/src/basicService/qrCode/response"
"schisandra-album-cloud-microservices/app/core/api/common/constant"
response2 "schisandra-album-cloud-microservices/app/core/api/common/response"
"schisandra-album-cloud-microservices/app/core/api/common/utils"
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
"schisandra-album-cloud-microservices/app/core/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type GetWechatQrcodeLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetWechatQrcodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetWechatQrcodeLogic {
return &GetWechatQrcodeLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetWechatQrcodeLogic) GetWechatQrcode(r *http.Request, req *types.OAuthWechatRequest) (resp *types.Response, err error) {
ip := utils.GetClientIP(r) // 使用工具函数获取客户端IP
key := constant.UserQrcodePrefix + ip
// 从Redis获取二维码数据
qrcode := l.svcCtx.RedisClient.Get(l.ctx, key).Val()
if qrcode != "" {
data := new(response.ResponseQRCodeCreate)
if err = json.Unmarshal([]byte(qrcode), data); err != nil {
return response2.Error(), err
}
return response2.SuccessWithData(data.Url), nil
}
// 生成临时二维码
data, err := l.svcCtx.WechatPublic.QRCode.Temporary(l.ctx, req.Client_id, 7*24*3600)
if err != nil {
return response2.Error(), err
}
// 序列化数据并存储到Redis
serializedData, err := json.Marshal(data)
if err != nil {
return response2.Error(), err
}
if err = l.svcCtx.RedisClient.Set(l.ctx, key, serializedData, time.Hour*24*7).Err(); err != nil {
return response2.Error(), err
}
return response2.SuccessWithData(data.Url), nil
}

View File

@@ -0,0 +1,257 @@
package oauth
import (
"context"
"encoding/json"
"fmt"
"net/http"
"strconv"
"time"
"github.com/yitter/idgenerator-go/idgen"
"schisandra-album-cloud-microservices/app/core/api/common/constant"
"schisandra-album-cloud-microservices/app/core/api/common/response"
"schisandra-album-cloud-microservices/app/core/api/internal/logic/user"
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
"schisandra-album-cloud-microservices/app/core/api/internal/types"
"schisandra-album-cloud-microservices/app/core/api/repository/mysql/ent"
"schisandra-album-cloud-microservices/app/core/api/repository/mysql/ent/scaauthuser"
"schisandra-album-cloud-microservices/app/core/api/repository/mysql/ent/scaauthusersocial"
"github.com/zeromicro/go-zero/core/logx"
)
type GiteeCallbackLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
type GiteeUser struct {
AvatarURL string `json:"avatar_url"`
Bio string `json:"bio"`
Blog string `json:"blog"`
CreatedAt time.Time `json:"created_at"`
Email string `json:"email"`
EventsURL string `json:"events_url"`
Followers int `json:"followers"`
FollowersURL string `json:"followers_url"`
Following int `json:"following"`
FollowingURL string `json:"following_url"`
GistsURL string `json:"gists_url"`
HTMLURL string `json:"html_url"`
ID int `json:"id"`
Login string `json:"login"`
Name string `json:"name"`
OrganizationsURL string `json:"organizations_url"`
PublicGists int `json:"public_gists"`
PublicRepos int `json:"public_repos"`
ReceivedEventsURL string `json:"received_events_url"`
Remark string `json:"remark"`
ReposURL string `json:"repos_url"`
Stared int `json:"stared"`
StarredURL string `json:"starred_url"`
SubscriptionsURL string `json:"subscriptions_url"`
Type string `json:"type"`
UpdatedAt time.Time `json:"updated_at"`
URL string `json:"url"`
Watched int `json:"watched"`
Weibo interface{} `json:"weibo"`
}
type Token struct {
AccessToken string `json:"access_token"`
}
var Script = `
<script>
window.opener.postMessage('%s', '%s');
window.close();
</script>
`
func NewGiteeCallbackLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GiteeCallbackLogic {
return &GiteeCallbackLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GiteeCallbackLogic) GiteeCallback(w http.ResponseWriter, r *http.Request, req *types.OAuthCallbackRequest) error {
// 获取 token
tokenAuthUrl := l.GetGiteeTokenAuthUrl(req.Code)
token, err := l.GetGiteeToken(tokenAuthUrl)
if err != nil {
return err
}
if token == nil {
return nil
}
// 获取用户信息
userInfo, err := l.GetGiteeUserInfo(token)
if err != nil {
return err
}
var giteeUser GiteeUser
marshal, err := json.Marshal(userInfo)
if err != nil {
return err
}
if err = json.Unmarshal(marshal, &giteeUser); err != nil {
return err
}
tx, err := l.svcCtx.MySQLClient.Tx(l.ctx)
if err != nil {
return err
}
Id := strconv.Itoa(giteeUser.ID)
socialUser, err := l.svcCtx.MySQLClient.ScaAuthUserSocial.Query().
Where(scaauthusersocial.OpenID(Id),
scaauthusersocial.Source(constant.OAuthSourceGitee),
scaauthusersocial.Deleted(constant.NotDeleted)).
First(l.ctx)
if err != nil && !ent.IsNotFound(err) {
return err
}
if ent.IsNotFound(err) {
// 创建用户
uid := idgen.NextId()
uidStr := strconv.FormatInt(uid, 10)
addUser, fault := l.svcCtx.MySQLClient.ScaAuthUser.Create().
SetUID(uidStr).
SetAvatar(giteeUser.AvatarURL).
SetUsername(giteeUser.Login).
SetNickname(giteeUser.Name).
SetBlog(giteeUser.Blog).
SetEmail(giteeUser.Email).
SetDeleted(constant.NotDeleted).
SetGender(constant.Male).
Save(l.ctx)
if fault != nil {
return tx.Rollback()
}
if err = l.svcCtx.MySQLClient.ScaAuthUserSocial.Create().
SetUserID(uidStr).
SetOpenID(Id).
SetSource(constant.OAuthSourceGitee).
Exec(l.ctx); err != nil {
return tx.Rollback()
}
if res, err := l.svcCtx.CasbinEnforcer.AddRoleForUser(uidStr, constant.User); !res || err != nil {
return tx.Rollback()
}
if result := HandleOauthLoginResponse(addUser, l.svcCtx, r, w, l.ctx); !result {
return tx.Rollback()
}
} else {
sacAuthUser, fault := l.svcCtx.MySQLClient.ScaAuthUser.Query().
Where(scaauthuser.UID(socialUser.UserID), scaauthuser.Deleted(constant.NotDeleted)).
First(l.ctx)
if fault != nil {
return tx.Rollback()
}
if result := HandleOauthLoginResponse(sacAuthUser, l.svcCtx, r, w, l.ctx); !result {
return tx.Rollback()
}
}
if err := tx.Commit(); err != nil {
return tx.Rollback()
}
return nil
}
// HandleOauthLoginResponse 处理登录响应
func HandleOauthLoginResponse(scaAuthUser *ent.ScaAuthUser, svcCtx *svc.ServiceContext, r *http.Request, w http.ResponseWriter, ctx context.Context) bool {
data, result := user.HandleUserLogin(scaAuthUser, svcCtx, true, r, w, ctx)
if !result {
return false
}
responseData := response.SuccessWithData(data)
formattedScript := fmt.Sprintf(Script, responseData, svcCtx.Config.Web.URL)
// 设置响应状态码和内容类型
w.WriteHeader(http.StatusOK)
w.Header().Set("Content-Type", "text/html; charset=utf-8")
// 写入响应内容
if _, writeErr := w.Write([]byte(formattedScript)); writeErr != nil {
return false
}
return true
}
// GetGiteeTokenAuthUrl 获取Gitee token
func (l *GiteeCallbackLogic) GetGiteeTokenAuthUrl(code string) string {
clientId := l.svcCtx.Config.OAuth.Gitee.ClientID
clientSecret := l.svcCtx.Config.OAuth.Gitee.ClientSecret
redirectURI := l.svcCtx.Config.OAuth.Gitee.RedirectURI
return fmt.Sprintf(
"https://gitee.com/oauth/token?grant_type=authorization_code&code=%s&client_id=%s&redirect_uri=%s&client_secret=%s",
code, clientId, redirectURI, clientSecret,
)
}
// GetGiteeToken 获取 token
func (l *GiteeCallbackLogic) GetGiteeToken(url string) (*Token, error) {
// 形成请求
var req *http.Request
var err error
if req, err = http.NewRequest(http.MethodPost, url, nil); err != nil {
return nil, err
}
req.Header.Set("accept", "application/json")
// 发送请求并获得响应
var httpClient = http.Client{}
var res *http.Response
if res, err = httpClient.Do(req); err != nil {
return nil, err
}
// 将响应体解析为 token并返回
var token Token
if err = json.NewDecoder(res.Body).Decode(&token); err != nil {
return nil, err
}
return &token, nil
}
// GetGiteeUserInfo 获取用户信息
func (l *GiteeCallbackLogic) GetGiteeUserInfo(token *Token) (map[string]interface{}, error) {
// 形成请求
var userInfoUrl = "https://gitee.com/api/v5/user" // github用户信息获取接口
var req *http.Request
var err error
if req, err = http.NewRequest(http.MethodGet, userInfoUrl, nil); err != nil {
return nil, err
}
req.Header.Set("accept", "application/json")
req.Header.Set("Authorization", fmt.Sprintf("token %s", token.AccessToken))
// 发送请求并获取响应
var client = http.Client{}
var res *http.Response
if res, err = client.Do(req); err != nil {
return nil, err
}
// 将响应的数据写入 userInfo 中,并返回
var userInfo = make(map[string]interface{})
if err = json.NewDecoder(res.Body).Decode(&userInfo); err != nil {
return nil, err
}
return userInfo, nil
}

View File

@@ -0,0 +1,236 @@
package oauth
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"strconv"
"github.com/yitter/idgenerator-go/idgen"
"schisandra-album-cloud-microservices/app/core/api/common/constant"
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
"schisandra-album-cloud-microservices/app/core/api/internal/types"
"schisandra-album-cloud-microservices/app/core/api/repository/mysql/ent"
"schisandra-album-cloud-microservices/app/core/api/repository/mysql/ent/scaauthuser"
"schisandra-album-cloud-microservices/app/core/api/repository/mysql/ent/scaauthusersocial"
"github.com/zeromicro/go-zero/core/logx"
)
type GithubCallbackLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
type GitHubUser struct {
AvatarURL string `json:"avatar_url"`
Bio interface{} `json:"bio"`
Blog string `json:"blog"`
Company interface{} `json:"company"`
CreatedAt string `json:"created_at"`
Email string `json:"email"`
EventsURL string `json:"events_url"`
Followers int `json:"followers"`
FollowersURL string `json:"followers_url"`
Following int `json:"following"`
FollowingURL string `json:"following_url"`
GistsURL string `json:"gists_url"`
GravatarID string `json:"gravatar_id"`
Hireable interface{} `json:"hireable"`
HTMLURL string `json:"html_url"`
ID int `json:"id"`
Location interface{} `json:"location"`
Login string `json:"login"`
Name string `json:"name"`
NodeID string `json:"node_id"`
NotificationEmail interface{} `json:"notification_email"`
OrganizationsURL string `json:"organizations_url"`
PublicGists int `json:"public_gists"`
PublicRepos int `json:"public_repos"`
ReceivedEventsURL string `json:"received_events_url"`
ReposURL string `json:"repos_url"`
SiteAdmin bool `json:"site_admin"`
StarredURL string `json:"starred_url"`
SubscriptionsURL string `json:"subscriptions_url"`
TwitterUsername interface{} `json:"twitter_username"`
Type string `json:"type"`
UpdatedAt string `json:"updated_at"`
URL string `json:"url"`
}
func NewGithubCallbackLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GithubCallbackLogic {
return &GithubCallbackLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GithubCallbackLogic) GithubCallback(w http.ResponseWriter, r *http.Request, req *types.OAuthCallbackRequest) error {
// 获取 token
tokenAuthUrl := l.GetTokenAuthUrl(req.Code)
token, err := l.GetToken(tokenAuthUrl)
if err != nil {
return err
}
if token == nil {
return errors.New("failed to get token")
}
// 获取用户信息
userInfo, err := l.GetUserInfo(token)
if err != nil {
return err
}
if userInfo == nil {
return errors.New("failed to get user info")
}
// 处理用户信息
userInfoBytes, err := json.Marshal(userInfo)
if err != nil {
return err
}
var gitHubUser GitHubUser
err = json.Unmarshal(userInfoBytes, &gitHubUser)
if err != nil {
return err
}
tx, err := l.svcCtx.MySQLClient.Tx(l.ctx)
if err != nil {
return err
}
Id := strconv.Itoa(gitHubUser.ID)
socialUser, err := l.svcCtx.MySQLClient.ScaAuthUserSocial.Query().
Where(scaauthusersocial.OpenID(Id),
scaauthusersocial.Source(constant.OAuthSourceGithub),
scaauthusersocial.Deleted(constant.NotDeleted)).
First(l.ctx)
if err != nil && !ent.IsNotFound(err) {
return err
}
if ent.IsNotFound(err) {
// 创建用户
uid := idgen.NextId()
uidStr := strconv.FormatInt(uid, 10)
addUser, fault := l.svcCtx.MySQLClient.ScaAuthUser.Create().
SetUID(uidStr).
SetAvatar(gitHubUser.AvatarURL).
SetUsername(gitHubUser.Login).
SetNickname(gitHubUser.Name).
SetBlog(gitHubUser.Blog).
SetEmail(gitHubUser.Email).
SetDeleted(constant.NotDeleted).
SetGender(constant.Male).
Save(l.ctx)
if fault != nil {
return tx.Rollback()
}
if err = l.svcCtx.MySQLClient.ScaAuthUserSocial.Create().
SetUserID(uidStr).
SetOpenID(Id).
SetSource(constant.OAuthSourceGithub).
Exec(l.ctx); err != nil {
return tx.Rollback()
}
if res, err := l.svcCtx.CasbinEnforcer.AddRoleForUser(uidStr, constant.User); !res || err != nil {
return tx.Rollback()
}
if result := HandleOauthLoginResponse(addUser, l.svcCtx, r, w, l.ctx); !result {
return tx.Rollback()
}
} else {
sacAuthUser, fault := l.svcCtx.MySQLClient.ScaAuthUser.Query().
Where(scaauthuser.UID(socialUser.UserID), scaauthuser.Deleted(constant.NotDeleted)).
First(l.ctx)
if fault != nil {
return tx.Rollback()
}
if result := HandleOauthLoginResponse(sacAuthUser, l.svcCtx, r, w, l.ctx); !result {
return tx.Rollback()
}
}
if err := tx.Commit(); err != nil {
return tx.Rollback()
}
return nil
}
// GetTokenAuthUrl 通过code获取token认证url
func (l *GithubCallbackLogic) GetTokenAuthUrl(code string) string {
clientId := l.svcCtx.Config.OAuth.Github.ClientID
clientSecret := l.svcCtx.Config.OAuth.Github.ClientSecret
return fmt.Sprintf(
"https://github.com/login/oauth/access_token?client_id=%s&client_secret=%s&code=%s",
clientId, clientSecret, code,
)
}
// GetToken 获取 token
func (l *GithubCallbackLogic) GetToken(url string) (*Token, error) {
// 形成请求
var req *http.Request
var err error
if req, err = http.NewRequest(http.MethodGet, url, nil); err != nil {
return nil, err
}
req.Header.Set("accept", "application/json")
// 发送请求并获得响应
var httpClient = http.Client{}
var res *http.Response
if res, err = httpClient.Do(req); err != nil {
return nil, err
}
// 将响应体解析为 token并返回
var token Token
if err = json.NewDecoder(res.Body).Decode(&token); err != nil {
return nil, err
}
return &token, nil
}
// GetUserInfo 获取用户信息
func (l *GithubCallbackLogic) GetUserInfo(token *Token) (map[string]interface{}, error) {
// 形成请求
var userInfoUrl = "https://api.github.com/user" // github用户信息获取接口
var req *http.Request
var err error
if req, err = http.NewRequest(http.MethodGet, userInfoUrl, nil); err != nil {
return nil, err
}
req.Header.Set("accept", "application/json")
req.Header.Set("Authorization", fmt.Sprintf("token %s", token.AccessToken))
// 发送请求并获取响应
var client = http.Client{}
var res *http.Response
if res, err = client.Do(req); err != nil {
return nil, err
}
// 将响应的数据写入 userInfo 中,并返回
var userInfo = make(map[string]interface{})
if err = json.NewDecoder(res.Body).Decode(&userInfo); err != nil {
return nil, err
}
return userInfo, nil
}

View File

@@ -0,0 +1,252 @@
package oauth
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"strconv"
"github.com/yitter/idgenerator-go/idgen"
"schisandra-album-cloud-microservices/app/core/api/common/constant"
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
"schisandra-album-cloud-microservices/app/core/api/internal/types"
"schisandra-album-cloud-microservices/app/core/api/repository/mysql/ent"
"schisandra-album-cloud-microservices/app/core/api/repository/mysql/ent/scaauthuser"
"schisandra-album-cloud-microservices/app/core/api/repository/mysql/ent/scaauthusersocial"
"github.com/zeromicro/go-zero/core/logx"
)
type QqCallbackLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
type AuthQQme struct {
ClientID string `json:"client_id"`
OpenID string `json:"openid"`
}
type QQToken struct {
AccessToken string `json:"access_token"`
ExpireIn string `json:"expire_in"`
RefreshToken string `json:"refresh_token"`
}
type QQUserInfo struct {
City string `json:"city"`
Figureurl string `json:"figureurl"`
Figureurl1 string `json:"figureurl_1"`
Figureurl2 string `json:"figureurl_2"`
FigureurlQq string `json:"figureurl_qq"`
FigureurlQq1 string `json:"figureurl_qq_1"`
FigureurlQq2 string `json:"figureurl_qq_2"`
Gender string `json:"gender"`
GenderType int `json:"gender_type"`
IsLost int `json:"is_lost"`
IsYellowVip string `json:"is_yellow_vip"`
IsYellowYearVip string `json:"is_yellow_year_vip"`
Level string `json:"level"`
Msg string `json:"msg"`
Nickname string `json:"nickname"`
Province string `json:"province"`
Ret int `json:"ret"`
Vip string `json:"vip"`
Year string `json:"year"`
YellowVipLevel string `json:"yellow_vip_level"`
}
func NewQqCallbackLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QqCallbackLogic {
return &QqCallbackLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *QqCallbackLogic) QqCallback(w http.ResponseWriter, r *http.Request, req *types.OAuthCallbackRequest) error {
tokenAuthUrl := l.GetQQTokenAuthUrl(req.Code)
token, err := l.GetQQToken(tokenAuthUrl)
if err != nil {
return err
}
if token == nil {
return errors.New("get qq token failed")
}
// 通过 token 获取 openid
authQQme, err := l.GetQQUserOpenID(token)
if err != nil {
return err
}
// 通过 token 和 openid 获取用户信息
userInfo, err := l.GetQQUserUserInfo(token, authQQme.OpenID)
if err != nil {
return err
}
// 处理用户信息
userInfoBytes, err := json.Marshal(userInfo)
if err != nil {
return err
}
var qqUserInfo QQUserInfo
err = json.Unmarshal(userInfoBytes, &qqUserInfo)
if err != nil {
return err
}
tx, err := l.svcCtx.MySQLClient.Tx(l.ctx)
if err != nil {
return err
}
socialUser, err := l.svcCtx.MySQLClient.ScaAuthUserSocial.Query().
Where(scaauthusersocial.OpenID(authQQme.OpenID),
scaauthusersocial.Source(constant.OAuthSourceQQ),
scaauthusersocial.Deleted(constant.NotDeleted)).
First(l.ctx)
if err != nil && !ent.IsNotFound(err) {
return err
}
if ent.IsNotFound(err) {
// 创建用户
uid := idgen.NextId()
uidStr := strconv.FormatInt(uid, 10)
addUser, fault := l.svcCtx.MySQLClient.ScaAuthUser.Create().
SetUID(uidStr).
SetAvatar(qqUserInfo.FigureurlQq1).
SetUsername(authQQme.OpenID).
SetNickname(qqUserInfo.Nickname).
SetDeleted(constant.NotDeleted).
SetGender(constant.Male).
Save(l.ctx)
if fault != nil {
return tx.Rollback()
}
if err = l.svcCtx.MySQLClient.ScaAuthUserSocial.Create().
SetUserID(uidStr).
SetOpenID(authQQme.OpenID).
SetSource(constant.OAuthSourceQQ).
Exec(l.ctx); err != nil {
return tx.Rollback()
}
if res, err := l.svcCtx.CasbinEnforcer.AddRoleForUser(uidStr, constant.User); !res || err != nil {
return tx.Rollback()
}
if result := HandleOauthLoginResponse(addUser, l.svcCtx, r, w, l.ctx); !result {
return tx.Rollback()
}
} else {
sacAuthUser, fault := l.svcCtx.MySQLClient.ScaAuthUser.Query().
Where(scaauthuser.UID(socialUser.UserID), scaauthuser.Deleted(constant.NotDeleted)).
First(l.ctx)
if fault != nil {
return tx.Rollback()
}
if result := HandleOauthLoginResponse(sacAuthUser, l.svcCtx, r, w, l.ctx); !result {
return tx.Rollback()
}
}
if err := tx.Commit(); err != nil {
return tx.Rollback()
}
return nil
}
// GetQQTokenAuthUrl 通过code获取token认证url
func (l *QqCallbackLogic) GetQQTokenAuthUrl(code string) string {
clientId := l.svcCtx.Config.OAuth.QQ.ClientID
clientSecret := l.svcCtx.Config.OAuth.QQ.ClientSecret
redirectURI := l.svcCtx.Config.OAuth.QQ.RedirectURI
return fmt.Sprintf(
"https://graph.qq.com/oauth2.0/token?grant_type=authorization_code&client_id=%s&client_secret=%s&code=%s&redirect_uri=%s&fmt=json",
clientId, clientSecret, code, redirectURI,
)
}
// GetQQToken 获取 token
func (l *QqCallbackLogic) GetQQToken(url string) (*QQToken, error) {
// 形成请求
var req *http.Request
var err error
if req, err = http.NewRequest(http.MethodGet, url, nil); err != nil {
return nil, err
}
// 发送请求并获得响应
var httpClient = http.Client{}
var res *http.Response
if res, err = httpClient.Do(req); err != nil {
return nil, err
}
// 将响应体解析为 token并返回
var token QQToken
if err = json.NewDecoder(res.Body).Decode(&token); err != nil {
return nil, err
}
return &token, nil
}
// GetQQUserOpenID 获取用户 openid
func (l *QqCallbackLogic) GetQQUserOpenID(token *QQToken) (*AuthQQme, error) {
// 形成请求
var userInfoUrl = "https://graph.qq.com/oauth2.0/me?access_token=" + token.AccessToken + "&fmt=json"
var req *http.Request
var err error
if req, err = http.NewRequest(http.MethodGet, userInfoUrl, nil); err != nil {
return nil, err
}
// 发送请求并获取响应
var client = http.Client{}
var res *http.Response
if res, err = client.Do(req); err != nil {
return nil, err
}
// 将响应体解析为 AuthQQme并返回
var authQQme AuthQQme
if err = json.NewDecoder(res.Body).Decode(&authQQme); err != nil {
return nil, err
}
return &authQQme, nil
}
// GetQQUserUserInfo 获取用户信息
func (l *QqCallbackLogic) GetQQUserUserInfo(token *QQToken, openId string) (map[string]interface{}, error) {
clientId := l.svcCtx.Config.OAuth.QQ.ClientID
// 形成请求
var userInfoUrl = "https://graph.qq.com/user/get_user_info?access_token=" + token.AccessToken + "&oauth_consumer_key=" + clientId + "&openid=" + openId
var req *http.Request
var err error
if req, err = http.NewRequest(http.MethodGet, userInfoUrl, nil); err != nil {
return nil, err
}
// 发送请求并获取响应
var client = http.Client{}
var res *http.Response
if res, err = client.Do(req); err != nil {
return nil, err
}
// 将响应的数据写入 userInfo 中,并返回
var userInfo = make(map[string]interface{})
if err = json.NewDecoder(res.Body).Decode(&userInfo); err != nil {
return nil, err
}
return userInfo, nil
}

View File

@@ -0,0 +1,201 @@
package oauth
import (
"context"
"encoding/json"
"errors"
"net/http"
"strconv"
"strings"
"github.com/ArtisanCloud/PowerLibs/v3/http/helper"
"github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/contract"
"github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/messages"
models2 "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/models"
"github.com/ArtisanCloud/PowerWeChat/v3/src/officialAccount/server/handlers/models"
"github.com/yitter/idgenerator-go/idgen"
"github.com/zeromicro/go-zero/core/logx"
"schisandra-album-cloud-microservices/app/core/api/common/constant"
"schisandra-album-cloud-microservices/app/core/api/common/i18n"
randomname "schisandra-album-cloud-microservices/app/core/api/common/random_name"
"schisandra-album-cloud-microservices/app/core/api/common/utils"
"schisandra-album-cloud-microservices/app/core/api/internal/logic/user"
"schisandra-album-cloud-microservices/app/core/api/internal/logic/websocket"
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
"schisandra-album-cloud-microservices/app/core/api/repository/mysql/ent"
"schisandra-album-cloud-microservices/app/core/api/repository/mysql/ent/scaauthuser"
"schisandra-album-cloud-microservices/app/core/api/repository/mysql/ent/scaauthusersocial"
)
type WechatCallbackLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewWechatCallbackLogic(ctx context.Context, svcCtx *svc.ServiceContext) *WechatCallbackLogic {
return &WechatCallbackLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *WechatCallbackLogic) WechatCallback(w http.ResponseWriter, r *http.Request) error {
_, err := l.svcCtx.WechatPublic.Server.VerifyURL(r)
if err != nil {
return err
}
rs, err := l.svcCtx.WechatPublic.Server.Notify(r, func(event contract.EventInterface) interface{} {
switch event.GetMsgType() {
case models2.CALLBACK_MSG_TYPE_EVENT:
switch event.GetEvent() {
case models.CALLBACK_EVENT_SUBSCRIBE:
msg := models.EventSubscribe{}
err = event.ReadMessage(&msg)
if err != nil {
println(err.Error())
return "error"
}
key := strings.TrimPrefix(msg.EventKey, "qrscene_")
err = l.HandlerWechatLogin(msg.FromUserName, key, w, r)
if err != nil {
return messages.NewText(i18n.FormatText(l.ctx, "login.loginFailed", "登录失败"))
}
return messages.NewText(i18n.FormatText(l.ctx, "login.loginSuccess", "登录成功"))
case models.CALLBACK_EVENT_UNSUBSCRIBE:
msg := models.EventUnSubscribe{}
err := event.ReadMessage(&msg)
if err != nil {
println(err.Error())
return "error"
}
return messages.NewText("ok")
case models.CALLBACK_EVENT_SCAN:
msg := models.EventScan{}
err = event.ReadMessage(&msg)
if err != nil {
println(err.Error())
return "error"
}
err = l.HandlerWechatLogin(msg.FromUserName, msg.EventKey, w, r)
if err != nil {
return messages.NewText(i18n.FormatText(l.ctx, "login.loginFailed", "登录失败"))
}
return messages.NewText(i18n.FormatText(l.ctx, "login.loginSuccess", "登录成功"))
}
case models2.CALLBACK_MSG_TYPE_TEXT:
msg := models.MessageText{}
err := event.ReadMessage(&msg)
if err != nil {
println(err.Error())
return "error"
}
}
return messages.NewText("ok")
})
if err != nil {
panic(err)
}
err = helper.HttpResponseSend(rs, w)
if err != nil {
panic(err)
}
return nil
}
// HandlerWechatLogin 处理微信登录
func (l *WechatCallbackLogic) HandlerWechatLogin(openId string, clientId string, w http.ResponseWriter, r *http.Request) error {
if openId == "" {
return errors.New("openId is empty")
}
socialUser, err := l.svcCtx.MySQLClient.ScaAuthUserSocial.Query().
Where(scaauthusersocial.OpenID(openId),
scaauthusersocial.Source(constant.OAuthSourceWechat),
scaauthusersocial.Deleted(constant.NotDeleted)).
First(l.ctx)
if err != nil && !ent.IsNotFound(err) {
return err
}
tx, err := l.svcCtx.MySQLClient.Tx(l.ctx)
if err != nil {
return err
}
if ent.IsNotFound(err) {
// 创建用户
uid := idgen.NextId()
uidStr := strconv.FormatInt(uid, 10)
avatar := utils.GenerateAvatar(uidStr)
name := randomname.GenerateName()
addUser, fault := l.svcCtx.MySQLClient.ScaAuthUser.Create().
SetUID(uidStr).
SetAvatar(avatar).
SetUsername(openId).
SetNickname(name).
SetDeleted(constant.NotDeleted).
SetGender(constant.Male).
Save(l.ctx)
if fault != nil {
return tx.Rollback()
}
if err = l.svcCtx.MySQLClient.ScaAuthUserSocial.Create().
SetUserID(uidStr).
SetOpenID(openId).
SetSource(constant.OAuthSourceWechat).
Exec(l.ctx); err != nil {
return tx.Rollback()
}
if res, err := l.svcCtx.CasbinEnforcer.AddRoleForUser(uidStr, constant.User); !res || err != nil {
return tx.Rollback()
}
data, result := user.HandleUserLogin(addUser, l.svcCtx, true, r, w, l.ctx)
if !result {
return tx.Rollback()
}
marshal, fault := json.Marshal(data)
if fault != nil {
return tx.Rollback()
}
err = websocket.QrcodeWebSocketHandler.SendMessageToClient(clientId, marshal)
if err != nil {
return tx.Rollback()
}
} else {
sacAuthUser, fault := l.svcCtx.MySQLClient.ScaAuthUser.Query().
Where(scaauthuser.UID(socialUser.UserID), scaauthuser.Deleted(constant.NotDeleted)).
First(l.ctx)
if fault != nil {
return tx.Rollback()
}
data, result := user.HandleUserLogin(sacAuthUser, l.svcCtx, true, r, w, l.ctx)
if !result {
return tx.Rollback()
}
marshal, fault := json.Marshal(data)
if fault != nil {
return tx.Rollback()
}
err = websocket.QrcodeWebSocketHandler.SendMessageToClient(clientId, marshal)
if err != nil {
return tx.Rollback()
}
}
if err := tx.Commit(); err != nil {
return tx.Rollback()
}
return nil
}

View File

@@ -0,0 +1,73 @@
package sms
import (
"context"
"time"
gosms "github.com/pkg6/go-sms"
"github.com/pkg6/go-sms/gateways"
"github.com/pkg6/go-sms/gateways/aliyun"
"schisandra-album-cloud-microservices/app/core/api/common/captcha/verify"
"schisandra-album-cloud-microservices/app/core/api/common/constant"
"schisandra-album-cloud-microservices/app/core/api/common/response"
"schisandra-album-cloud-microservices/app/core/api/common/utils"
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
"schisandra-album-cloud-microservices/app/core/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type SendSmsByAliyunLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewSendSmsByAliyunLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SendSmsByAliyunLogic {
return &SendSmsByAliyunLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *SendSmsByAliyunLogic) SendSmsByAliyun(req *types.SmsSendRequest) (resp *types.Response, err error) {
checkRotateData := verify.VerifyRotateCaptcha(l.ctx, l.svcCtx.RedisClient, req.Angle, req.Key)
if !checkRotateData {
return response.ErrorWithI18n(l.ctx, "captcha.verificationFailure", "验证码错误"), nil
}
isPhone := utils.IsPhone(req.Phone)
if !isPhone {
return response.ErrorWithI18n(l.ctx, "login.phoneFormatError", "手机号格式错误"), nil
}
val := l.svcCtx.RedisClient.Get(l.ctx, constant.UserSmsRedisPrefix+req.Phone).Val()
if val != "" {
return response.ErrorWithI18n(l.ctx, "sms.smsSendTooFrequently", "验证码发送过于频繁,请稍后再试"), nil
}
sms := gosms.NewParser(gateways.Gateways{
ALiYun: aliyun.ALiYun{
Host: l.svcCtx.Config.SMS.Ali.Host,
AccessKeyId: l.svcCtx.Config.SMS.Ali.AccessKeyId,
AccessKeySecret: l.svcCtx.Config.SMS.Ali.AccessKeySecret,
},
})
code := utils.GenValidateCode(6)
wrong := l.svcCtx.RedisClient.Set(l.ctx, constant.UserSmsRedisPrefix+req.Phone, code, time.Minute).Err()
if wrong != nil {
return response.ErrorWithI18n(l.ctx, "sms.smsSendFailed", "验证码发送失败"), wrong
}
_, err = sms.Send(req.Phone, gosms.MapStringAny{
"content": "您的验证码是:****。请不要把验证码泄露给其他人。",
"template": l.svcCtx.Config.SMS.Ali.TemplateCode,
"signName": l.svcCtx.Config.SMS.Ali.Signature,
"data": gosms.MapStrings{
"code": code,
},
}, nil)
if err != nil {
return response.ErrorWithI18n(l.ctx, "sms.smsSendFailed", "验证码发送失败"), err
}
return response.Success(), nil
}

View File

@@ -0,0 +1,66 @@
package sms
import (
"context"
"time"
gosms "github.com/pkg6/go-sms"
"github.com/pkg6/go-sms/gateways"
"github.com/pkg6/go-sms/gateways/smsbao"
"schisandra-album-cloud-microservices/app/core/api/common/captcha/verify"
"schisandra-album-cloud-microservices/app/core/api/common/constant"
"schisandra-album-cloud-microservices/app/core/api/common/response"
"schisandra-album-cloud-microservices/app/core/api/common/utils"
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
"schisandra-album-cloud-microservices/app/core/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type SendSmsBySmsbaoLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewSendSmsBySmsbaoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SendSmsBySmsbaoLogic {
return &SendSmsBySmsbaoLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *SendSmsBySmsbaoLogic) SendSmsBySmsbao(req *types.SmsSendRequest) (resp *types.Response, err error) {
checkRotateData := verify.VerifyRotateCaptcha(l.ctx, l.svcCtx.RedisClient, req.Angle, req.Key)
if !checkRotateData {
return response.ErrorWithI18n(l.ctx, "captcha.verificationFailure", "验证码错误"), nil
}
isPhone := utils.IsPhone(req.Phone)
if !isPhone {
return response.ErrorWithI18n(l.ctx, "login.phoneFormatError", "手机号格式错误"), nil
}
val := l.svcCtx.RedisClient.Get(l.ctx, constant.UserSmsRedisPrefix+req.Phone).Val()
if val != "" {
return response.ErrorWithI18n(l.ctx, "sms.smsSendTooFrequently", "验证码发送过于频繁,请稍后再试"), nil
}
sms := gosms.NewParser(gateways.Gateways{
SmsBao: smsbao.SmsBao{
User: l.svcCtx.Config.SMS.SMSBao.Username,
Password: l.svcCtx.Config.SMS.SMSBao.Password,
},
})
code := utils.GenValidateCode(6)
wrong := l.svcCtx.RedisClient.Set(l.ctx, constant.UserSmsRedisPrefix+req.Phone, code, time.Minute).Err()
if wrong != nil {
return response.ErrorWithI18n(l.ctx, "sms.smsSendFailed", "验证码发送失败"), wrong
}
_, err = sms.Send(req.Phone, gosms.MapStringAny{
"content": "您的验证码是:" + code + "。请不要把验证码泄露给其他人。",
}, nil)
if err != nil {
return response.ErrorWithI18n(l.ctx, "sms.smsSendFailed", "验证码发送失败"), err
}
return response.Success(), nil
}

View File

@@ -0,0 +1,50 @@
package sms
import (
"context"
"time"
"schisandra-album-cloud-microservices/app/core/api/common/captcha/verify"
"schisandra-album-cloud-microservices/app/core/api/common/constant"
"schisandra-album-cloud-microservices/app/core/api/common/response"
"schisandra-album-cloud-microservices/app/core/api/common/utils"
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
"schisandra-album-cloud-microservices/app/core/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type SendSmsByTestLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewSendSmsByTestLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SendSmsByTestLogic {
return &SendSmsByTestLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *SendSmsByTestLogic) SendSmsByTest(req *types.SmsSendRequest) (resp *types.Response, err error) {
checkRotateData := verify.VerifyRotateCaptcha(l.ctx, l.svcCtx.RedisClient, req.Angle, req.Key)
if !checkRotateData {
return response.ErrorWithI18n(l.ctx, "captcha.verificationFailure", "验证码错误"), nil
}
isPhone := utils.IsPhone(req.Phone)
if !isPhone {
return response.ErrorWithI18n(l.ctx, "login.phoneFormatError", "手机号格式错误"), nil
}
val := l.svcCtx.RedisClient.Get(l.ctx, constant.UserSmsRedisPrefix+req.Phone).Val()
if val != "" {
return response.ErrorWithI18n(l.ctx, "sms.smsSendTooFrequently", "验证码发送过于频繁,请稍后再试"), nil
}
code := utils.GenValidateCode(6)
wrong := l.svcCtx.RedisClient.Set(l.ctx, constant.UserSmsRedisPrefix+req.Phone, code, time.Minute).Err()
if wrong != nil {
return response.ErrorWithI18n(l.ctx, "sms.smsSendFailed", "验证码发送失败"), wrong
}
return response.Success(), nil
}

View File

@@ -5,8 +5,6 @@ import (
"net/http"
"time"
"github.com/lionsoul2014/ip2region/binding/golang/xdb"
"github.com/mssola/useragent"
"github.com/zeromicro/go-zero/core/logc"
"github.com/zeromicro/go-zero/core/logx"
@@ -19,7 +17,6 @@ import (
"schisandra-album-cloud-microservices/app/core/api/internal/types"
"schisandra-album-cloud-microservices/app/core/api/repository/mysql/ent"
"schisandra-album-cloud-microservices/app/core/api/repository/mysql/ent/scaauthuser"
"schisandra-album-cloud-microservices/app/core/api/repository/mysql/ent/scaauthuserdevice"
)
type AccountLoginLogic struct {
@@ -116,87 +113,14 @@ func HandleUserLogin(user *ent.ScaAuthUser, svcCtx *svc.ServiceContext, autoLogi
return nil, false
}
session.Values[constant.SESSION_KEY] = sessionData
if err = session.Save(r, w); err != nil {
logc.Error(ctx, err)
err = session.Save(r, w)
if err != nil {
return nil, false
}
// 记录用户登录设备
if !getUserLoginDevice(user.UID, r, svcCtx.Ip2Region, svcCtx.MySQLClient, ctx) {
if !GetUserLoginDevice(user.UID, r, svcCtx.Ip2Region, svcCtx.MySQLClient, ctx) {
return nil, false
}
return &data, true
}
// getUserLoginDevice 获取用户登录设备
func getUserLoginDevice(userId string, r *http.Request, ip2location *xdb.Searcher, entClient *ent.Client, ctx context.Context) bool {
userAgent := r.Header.Get("User-Agent")
if userAgent == "" {
return false
}
ip := utils.GetClientIP(r)
location, err := ip2location.SearchByStr(ip)
if err != nil {
return false
}
location = utils.RemoveZeroAndAdjust(location)
ua := useragent.New(userAgent)
isBot := ua.Bot()
browser, browserVersion := ua.Browser()
os := ua.OS()
mobile := ua.Mobile()
mozilla := ua.Mozilla()
platform := ua.Platform()
engine, engineVersion := ua.Engine()
device, err := entClient.ScaAuthUserDevice.Query().
Where(scaauthuserdevice.UserID(userId), scaauthuserdevice.IP(ip), scaauthuserdevice.Agent(userAgent)).
Only(ctx)
// 如果有错误,表示设备不存在,执行插入
if ent.IsNotFound(err) {
// 创建新的设备记录
err = entClient.ScaAuthUserDevice.Create().
SetBot(isBot).
SetAgent(userAgent).
SetBrowser(browser).
SetBrowserVersion(browserVersion).
SetEngineName(engine).
SetEngineVersion(engineVersion).
SetIP(ip).
SetLocation(location).
SetOperatingSystem(os).
SetMobile(mobile).
SetMozilla(mozilla).
SetPlatform(platform).
Exec(ctx)
if err != nil {
return false
}
return true
} else if err == nil {
// 如果设备存在,执行更新
err = device.Update().
SetBot(isBot).
SetAgent(userAgent).
SetBrowser(browser).
SetBrowserVersion(browserVersion).
SetEngineName(engine).
SetEngineVersion(engineVersion).
SetIP(ip).
SetLocation(location).
SetOperatingSystem(os).
SetMobile(mobile).
SetMozilla(mozilla).
SetPlatform(platform).
Exec(ctx)
if err != nil {
return false
}
return true
} else {
logc.Error(ctx, err)
return false
}
}

View File

@@ -0,0 +1,133 @@
package user
import (
"context"
"encoding/json"
"errors"
"net/http"
"github.com/lionsoul2014/ip2region/binding/golang/xdb"
"github.com/mssola/useragent"
"github.com/zeromicro/go-zero/core/logx"
"schisandra-album-cloud-microservices/app/core/api/common/constant"
"schisandra-album-cloud-microservices/app/core/api/common/utils"
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
"schisandra-album-cloud-microservices/app/core/api/internal/types"
"schisandra-album-cloud-microservices/app/core/api/repository/mysql/ent"
"schisandra-album-cloud-microservices/app/core/api/repository/mysql/ent/scaauthuserdevice"
)
type GetUserDeviceLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetUserDeviceLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUserDeviceLogic {
return &GetUserDeviceLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetUserDeviceLogic) GetUserDevice(r *http.Request) error {
session, err := l.svcCtx.Session.Get(r, constant.SESSION_KEY)
if err != nil {
return err
}
sessionData, ok := session.Values[constant.SESSION_KEY]
if !ok {
return errors.New("User not found or device not found")
}
var data types.SessionData
err = json.Unmarshal(sessionData.([]byte), &data)
if err != nil {
return err
}
res := GetUserLoginDevice(data.UID, r, l.svcCtx.Ip2Region, l.svcCtx.MySQLClient, l.ctx)
if !res {
return errors.New("User not found or device not found")
}
return nil
}
// GetUserLoginDevice 获取用户登录设备
func GetUserLoginDevice(userId string, r *http.Request, ip2location *xdb.Searcher, entClient *ent.Client, ctx context.Context) bool {
userAgent := r.Header.Get("User-Agent")
if userAgent == "" {
return false
}
ip := utils.GetClientIP(r)
location, err := ip2location.SearchByStr(ip)
if err != nil {
logx.Error(err)
return false
}
location = utils.RemoveZeroAndAdjust(location)
ua := useragent.New(userAgent)
isBot := ua.Bot()
browser, browserVersion := ua.Browser()
os := ua.OS()
mobile := ua.Mobile()
mozilla := ua.Mozilla()
platform := ua.Platform()
engine, engineVersion := ua.Engine()
device, err := entClient.ScaAuthUserDevice.Query().
Where(scaauthuserdevice.UserID(userId), scaauthuserdevice.IP(ip), scaauthuserdevice.Agent(userAgent)).
Only(ctx)
// 如果有错误,表示设备不存在,执行插入
if ent.IsNotFound(err) {
// 创建新的设备记录
err = entClient.ScaAuthUserDevice.Create().
SetUserID(userId).
SetBot(isBot).
SetAgent(userAgent).
SetBrowser(browser).
SetBrowserVersion(browserVersion).
SetEngineName(engine).
SetEngineVersion(engineVersion).
SetIP(ip).
SetLocation(location).
SetOperatingSystem(os).
SetMobile(mobile).
SetMozilla(mozilla).
SetPlatform(platform).
Exec(ctx)
if err != nil {
logx.Error(err)
return false
}
return true
} else if err == nil {
// 如果设备存在,执行更新
err = device.Update().
SetUserID(userId).
SetBot(isBot).
SetAgent(userAgent).
SetBrowser(browser).
SetBrowserVersion(browserVersion).
SetEngineName(engine).
SetEngineVersion(engineVersion).
SetIP(ip).
SetLocation(location).
SetOperatingSystem(os).
SetMobile(mobile).
SetMozilla(mozilla).
SetPlatform(platform).
Exec(ctx)
if err != nil {
logx.Error(err)
return false
}
return true
} else {
logx.Error(err)
return false
}
}

View File

@@ -43,7 +43,7 @@ func (l *PhoneLoginLogic) PhoneLogin(r *http.Request, w http.ResponseWriter, req
if req.Captcha != code {
return response.ErrorWithI18n(l.ctx, "login.captchaError", "验证码错误"), nil
}
user, err := l.svcCtx.MySQLClient.ScaAuthUser.Query().Where(scaauthuser.Phone(req.Phone), scaauthuser.Deleted(0)).Only(l.ctx)
user, err := l.svcCtx.MySQLClient.ScaAuthUser.Query().Where(scaauthuser.Phone(req.Phone), scaauthuser.Deleted(0)).First(l.ctx)
tx, wrong := l.svcCtx.MySQLClient.Tx(l.ctx)
if wrong != nil {
return response.ErrorWithI18n(l.ctx, "login.loginFailed", "登录失败"), err
@@ -54,7 +54,7 @@ func (l *PhoneLoginLogic) PhoneLogin(r *http.Request, w http.ResponseWriter, req
avatar := utils.GenerateAvatar(uidStr)
name := randomname.GenerateName()
addUser, wrong := l.svcCtx.MySQLClient.ScaAuthUser.Create().
addUser, fault := l.svcCtx.MySQLClient.ScaAuthUser.Create().
SetUID(uidStr).
SetPhone(req.Phone).
SetAvatar(avatar).
@@ -62,7 +62,12 @@ func (l *PhoneLoginLogic) PhoneLogin(r *http.Request, w http.ResponseWriter, req
SetDeleted(constant.NotDeleted).
SetGender(constant.Male).
Save(l.ctx)
if wrong != nil {
if fault != nil {
err = tx.Rollback()
return response.ErrorWithI18n(l.ctx, "login.registerError", "注册失败"), err
}
_, err = l.svcCtx.CasbinEnforcer.AddRoleForUser(uidStr, constant.User)
if err != nil {
err = tx.Rollback()
return response.ErrorWithI18n(l.ctx, "login.registerError", "注册失败"), err
}
@@ -72,15 +77,21 @@ func (l *PhoneLoginLogic) PhoneLogin(r *http.Request, w http.ResponseWriter, req
return response.ErrorWithI18n(l.ctx, "login.registerError", "注册失败"), err
}
err = tx.Commit()
return response.SuccessWithData(data), err
} else if err != nil {
if err != nil {
tx.Rollback()
}
return response.SuccessWithData(data), nil
} else if err == nil {
data, result := HandleUserLogin(user, l.svcCtx, req.AutoLogin, r, w, l.ctx)
if !result {
err = tx.Rollback()
return response.ErrorWithI18n(l.ctx, "login.loginFailed", "登录失败"), err
}
err = tx.Commit()
return response.SuccessWithData(data), err
if err != nil {
tx.Rollback()
}
return response.SuccessWithData(data), nil
} else {
return response.ErrorWithI18n(l.ctx, "login.loginFailed", "登录失败"), nil
}

View File

@@ -0,0 +1,193 @@
package websocket
import (
"context"
"fmt"
"net/http"
"time"
"github.com/lxzan/gws"
"github.com/redis/go-redis/v9"
"github.com/zeromicro/go-zero/core/logx"
"schisandra-album-cloud-microservices/app/core/api/common/constant"
"schisandra-album-cloud-microservices/app/core/api/common/jwt"
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
)
type MessageWebsocketLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewMessageWebsocketLogic(ctx context.Context, svcCtx *svc.ServiceContext) *MessageWebsocketLogic {
return &MessageWebsocketLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
type MessageWebSocket struct {
redis *redis.Client
ctx context.Context
gws.BuiltinEventHandler
sessions *gws.ConcurrentMap[string, *gws.Conn] // 使用内置的ConcurrentMap存储连接, 可以减少锁冲突
}
var MessageWebSocketHandler *MessageWebSocket
// InitializeWebSocketHandler 初始化WebSocketHandler
func InitializeWebSocketHandler(svcCtx *svc.ServiceContext) {
MessageWebSocketHandler = NewMessageWebSocket(svcCtx)
}
func (l *MessageWebsocketLogic) MessageWebsocket(w http.ResponseWriter, r *http.Request) error {
upgrader := gws.NewUpgrader(MessageWebSocketHandler, &gws.ServerOption{
HandshakeTimeout: 5 * time.Second, // 握手超时时间
ReadBufferSize: 1024, // 读缓冲区大小
ParallelEnabled: true, // 开启并行消息处理
Recovery: gws.Recovery, // 开启异常恢复
CheckUtf8Enabled: false, // 关闭UTF8校验
PermessageDeflate: gws.PermessageDeflate{
Enabled: true, // 开启压缩
},
Authorize: func(r *http.Request, session gws.SessionStorage) bool {
var clientId = r.URL.Query().Get("user_id")
if clientId == "" {
return false
}
token := r.URL.Query().Get("token")
if token == "" {
return false
}
accessToken, res := jwt.ParseAccessToken(l.svcCtx.Config.Auth.AccessSecret, token)
if !res || accessToken.UserID != clientId {
return false
}
session.Store("user_id", clientId)
return true
},
})
socket, err := upgrader.Upgrade(w, r)
if err != nil {
return err
}
go func() {
socket.ReadLoop() // 此处阻塞会使请求上下文不能顺利被GC
}()
return nil
}
// NewMessageWebSocket 创建WebSocket实例
func NewMessageWebSocket(svcCtx *svc.ServiceContext) *MessageWebSocket {
return &MessageWebSocket{
redis: svcCtx.RedisClient,
ctx: context.Background(),
sessions: gws.NewConcurrentMap[string, *gws.Conn](64, 128),
}
}
// OnOpen 连接建立
func (c *MessageWebSocket) OnOpen(socket *gws.Conn) {
clientId := MustLoad[string](socket.Session(), "user_id")
c.sessions.Store(clientId, socket)
// 订阅该用户的频道
go c.subscribeUserChannel(clientId, c.redis)
fmt.Printf("websocket client %s connected\n", clientId)
}
// OnClose 关闭连接
func (c *MessageWebSocket) OnClose(socket *gws.Conn, err error) {
name := MustLoad[string](socket.Session(), "user_id")
sharding := c.sessions.GetSharding(name)
c.sessions.Delete(name)
sharding.Lock()
defer sharding.Unlock()
fmt.Printf("websocket closed, name=%s, msg=%s\n", name, err.Error())
}
// OnPing 处理客户端的Ping消息
func (c *MessageWebSocket) OnPing(socket *gws.Conn, payload []byte) {
_ = socket.SetDeadline(time.Now().Add(PingInterval + HeartbeatWaitTimeout))
_ = socket.WritePong(payload)
}
// OnPong 处理客户端的Pong消息
func (c *MessageWebSocket) OnPong(_ *gws.Conn, _ []byte) {}
// OnMessage 接受消息
func (c *MessageWebSocket) OnMessage(socket *gws.Conn, message *gws.Message) {
defer message.Close()
clientId := MustLoad[string](socket.Session(), "user_id")
if conn, ok := c.sessions.Load(clientId); ok {
_ = conn.WriteMessage(gws.OpcodeText, message.Bytes())
}
}
// SendMessageToClient 向指定客户端发送消息
func (c *MessageWebSocket) SendMessageToClient(clientId string, message []byte) error {
conn, ok := c.sessions.Load(clientId)
if ok {
return conn.WriteMessage(gws.OpcodeText, message)
}
return fmt.Errorf("client %s not found", clientId)
}
// SendMessageToUser 发送消息到指定用户的 Redis 频道
func (c *MessageWebSocket) SendMessageToUser(clientId string, message []byte, redis *redis.Client, ctx context.Context) error {
if _, ok := c.sessions.Load(clientId); ok {
return redis.Publish(ctx, clientId, message).Err()
} else {
return redis.LPush(ctx, constant.CommentOfflineMessagePrefix+clientId, message).Err()
}
}
// 订阅用户频道
func (c *MessageWebSocket) subscribeUserChannel(clientId string, redis *redis.Client) {
conn, ok := c.sessions.Load(clientId)
if !ok {
return
}
// 获取离线消息
messages, err := redis.LRange(c.ctx, constant.CommentOfflineMessagePrefix+clientId, 0, -1).Result()
if err != nil {
fmt.Printf("Error loading offline messages for user %s: %v\n", clientId, err)
return
}
// 逐条发送离线消息
for _, msg := range messages {
if writeErr := conn.WriteMessage(gws.OpcodeText, []byte(msg)); writeErr != nil {
fmt.Printf("Error writing offline message to user %s: %v\n", clientId, writeErr)
return
}
}
// 清空离线消息列表
if delErr := redis.Del(c.ctx, constant.CommentOfflineMessagePrefix+clientId); delErr.Err() != nil {
fmt.Printf("Error clearing offline messages for user %s: %v\n", clientId, delErr.Err())
return
}
pubsub := redis.Subscribe(c.ctx, clientId)
defer func() {
if closeErr := pubsub.Close(); closeErr != nil {
fmt.Printf("Error closing pubsub for user %s: %v\n", clientId, closeErr)
}
}()
for {
msg, waitErr := pubsub.ReceiveMessage(context.Background())
if waitErr != nil {
fmt.Printf("Error receiving message for user %s: %v\n", clientId, err)
return
}
if writeErr := conn.WriteMessage(gws.OpcodeText, []byte(msg.Payload)); writeErr != nil {
fmt.Printf("Error writing message to user %s: %v\n", clientId, writeErr)
return
}
}
}

View File

@@ -0,0 +1,134 @@
package websocket
import (
"context"
"fmt"
"net/http"
"time"
"github.com/lxzan/gws"
"github.com/zeromicro/go-zero/core/logx"
"schisandra-album-cloud-microservices/app/core/api/common/constant"
"schisandra-album-cloud-microservices/app/core/api/common/utils"
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
)
type QrcodeWebsocketLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewQrcodeWebsocketLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QrcodeWebsocketLogic {
return &QrcodeWebsocketLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
const (
PingInterval = 5 * time.Second // 客户端心跳间隔
HeartbeatWaitTimeout = 10 * time.Second // 心跳等待超时时间
)
type QrcodeWebSocket struct {
gws.BuiltinEventHandler
sessions *gws.ConcurrentMap[string, *gws.Conn] // 使用内置的ConcurrentMap存储连接, 可以减少锁冲突
}
var QrcodeWebSocketHandler = NewWebSocket()
func (l *QrcodeWebsocketLogic) QrcodeWebsocket(w http.ResponseWriter, r *http.Request) error {
upgrader := gws.NewUpgrader(QrcodeWebSocketHandler, &gws.ServerOption{
HandshakeTimeout: 5 * time.Second, // 握手超时时间
ReadBufferSize: 1024, // 读缓冲区大小
ParallelEnabled: true, // 开启并行消息处理
Recovery: gws.Recovery, // 开启异常恢复
CheckUtf8Enabled: false, // 关闭UTF8校验
PermessageDeflate: gws.PermessageDeflate{
Enabled: true, // 开启压缩
},
Authorize: func(r *http.Request, session gws.SessionStorage) bool {
var clientId = r.URL.Query().Get("client_id")
if clientId == "" {
return false
}
ip := utils.GetClientIP(r)
exists := l.svcCtx.RedisClient.Get(l.ctx, constant.UserClientPrefix+ip).Val()
if clientId != exists {
return false
}
session.Store("client_id", clientId)
return true
},
})
socket, err := upgrader.Upgrade(w, r)
if err != nil {
return err
}
go func() {
socket.ReadLoop() // 此处阻塞会使请求上下文不能顺利被GC
}()
return nil
}
// MustLoad 从session中加载数据
func MustLoad[T any](session gws.SessionStorage, key string) (v T) {
if value, exist := session.Load(key); exist {
v = value.(T)
}
return
}
// NewWebSocket 创建WebSocket实例
func NewWebSocket() *QrcodeWebSocket {
return &QrcodeWebSocket{
sessions: gws.NewConcurrentMap[string, *gws.Conn](64, 128),
}
}
// OnOpen 连接建立
func (c *QrcodeWebSocket) OnOpen(socket *gws.Conn) {
clientId := MustLoad[string](socket.Session(), "client_id")
c.sessions.Store(clientId, socket)
fmt.Printf("websocket client %s connected\n", clientId)
}
// OnClose 关闭连接
func (c *QrcodeWebSocket) OnClose(socket *gws.Conn, err error) {
name := MustLoad[string](socket.Session(), "client_id")
sharding := c.sessions.GetSharding(name)
c.sessions.Delete(name)
sharding.Lock()
defer sharding.Unlock()
fmt.Printf("websocket closed, name=%s, err=%s\n", name, err.Error())
}
// OnPing 处理客户端的Ping消息
func (c *QrcodeWebSocket) OnPing(socket *gws.Conn, payload []byte) {
_ = socket.SetDeadline(time.Now().Add(PingInterval + HeartbeatWaitTimeout))
_ = socket.WritePong(payload)
}
// OnPong 处理客户端的Pong消息
func (c *QrcodeWebSocket) OnPong(_ *gws.Conn, _ []byte) {}
// OnMessage 接受消息
func (c *QrcodeWebSocket) OnMessage(socket *gws.Conn, message *gws.Message) {
defer message.Close()
clientId := MustLoad[string](socket.Session(), "client_id")
if conn, ok := c.sessions.Load(clientId); ok {
_ = conn.WriteMessage(gws.OpcodeText, message.Bytes())
}
}
// SendMessageToClient 向指定客户端发送消息
func (c *QrcodeWebSocket) SendMessageToClient(clientId string, message []byte) error {
conn, ok := c.sessions.Load(clientId)
if ok {
return conn.WriteMessage(gws.OpcodeText, message)
}
return fmt.Errorf("client %s not found", clientId)
}

View File

@@ -1,16 +1,21 @@
package svc
import (
"github.com/ArtisanCloud/PowerWeChat/v3/src/officialAccount"
"github.com/casbin/casbin/v2"
"github.com/lionsoul2014/ip2region/binding/golang/xdb"
"github.com/rbcervilla/redisstore/v9"
"github.com/redis/go-redis/v9"
"github.com/wenlng/go-captcha/v2/rotate"
"github.com/wenlng/go-captcha/v2/slide"
sensitive "github.com/zmexing/go-sensitive-word"
"github.com/zeromicro/go-zero/rest"
"go.mongodb.org/mongo-driver/v2/mongo"
"schisandra-album-cloud-microservices/app/core/api/internal/config"
"schisandra-album-cloud-microservices/app/core/api/internal/middleware"
"schisandra-album-cloud-microservices/app/core/api/repository/captcha"
"schisandra-album-cloud-microservices/app/core/api/repository/casbinx"
"schisandra-album-cloud-microservices/app/core/api/repository/ip2region"
"schisandra-album-cloud-microservices/app/core/api/repository/mongodb"
@@ -18,6 +23,8 @@ import (
"schisandra-album-cloud-microservices/app/core/api/repository/mysql/ent"
"schisandra-album-cloud-microservices/app/core/api/repository/redis_session"
"schisandra-album-cloud-microservices/app/core/api/repository/redisx"
"schisandra-album-cloud-microservices/app/core/api/repository/sensitivex"
"schisandra-album-cloud-microservices/app/core/api/repository/wechat_public"
)
type ServiceContext struct {
@@ -29,6 +36,10 @@ type ServiceContext struct {
Session *redisstore.RedisStore
Ip2Region *xdb.Searcher
CasbinEnforcer *casbin.CachedEnforcer
WechatPublic *officialAccount.OfficialAccount
Sensitive *sensitive.Manager
RotateCaptcha rotate.Captcha
SlideCaptcha slide.Captcha
}
func NewServiceContext(c config.Config) *ServiceContext {
@@ -41,5 +52,9 @@ func NewServiceContext(c config.Config) *ServiceContext {
Session: redis_session.NewRedisSession(c.Redis.Host, c.Redis.Pass),
Ip2Region: ip2region.NewIP2Region(),
CasbinEnforcer: casbinx.NewCasbin(c.Mysql.DataSource),
WechatPublic: wechat_public.NewWechatPublic(c.Wechat.AppID, c.Wechat.AppSecret, c.Wechat.Token, c.Wechat.AESKey, c.Redis.Host, c.Redis.Pass, c.Redis.DB),
Sensitive: sensitivex.NewSensitive(),
RotateCaptcha: captcha.NewRotateCaptcha(),
SlideCaptcha: captcha.NewSlideCaptcha(),
}
}

View File

@@ -20,6 +20,18 @@ type LoginResponse struct {
Status int8 `json:"status"`
}
type OAuthCallbackRequest struct {
Code string `form:"code"`
}
type OAuthRequest struct {
State string `form:"state"`
}
type OAuthWechatRequest struct {
Client_id string `form:"client_id"`
}
type PhoneLoginRequest struct {
Phone string `json:"phone"`
Captcha string `json:"captcha"`
@@ -38,3 +50,9 @@ type Response struct {
Message string `json:"message"`
Data interface{} `json:"data,optional"`
}
type SmsSendRequest struct {
Phone string `json:"phone"`
Angle int64 `json:"angle"`
Key string `json:"key"`
}

View File

@@ -0,0 +1,38 @@
package captcha
import (
"github.com/wenlng/go-captcha-assets/resources/images"
"github.com/wenlng/go-captcha-assets/resources/shapes"
"github.com/wenlng/go-captcha/v2/base/option"
"github.com/wenlng/go-captcha/v2/click"
)
// NewClickShapeCaptcha 初始化点击形状验证码
func NewClickShapeCaptcha() click.Captcha {
builder := click.NewBuilder(
click.WithRangeLen(option.RangeVal{Min: 3, Max: 6}),
click.WithRangeVerifyLen(option.RangeVal{Min: 2, Max: 3}),
click.WithRangeThumbBgDistort(1),
click.WithIsThumbNonDeformAbility(true),
)
// shape
// click.WithUseShapeOriginalColor(false) -> Random rewriting of graphic colors
shapeMaps, err := shapes.GetShapes()
if err != nil {
panic(err)
}
// background images
imgs, err := images.GetImages()
if err != nil {
panic(err)
}
// set resources
builder.SetResources(
click.WithShapes(shapeMaps),
click.WithBackgrounds(imgs),
)
return builder.MakeWithShape()
}

View File

@@ -0,0 +1,26 @@
package captcha
import (
"github.com/wenlng/go-captcha-assets/resources/images"
"github.com/wenlng/go-captcha/v2/base/option"
"github.com/wenlng/go-captcha/v2/rotate"
)
// NewRotateCaptcha 初始化旋转验证码
func NewRotateCaptcha() rotate.Captcha {
builder := rotate.NewBuilder(rotate.WithRangeAnglePos([]option.RangeVal{
{Min: 20, Max: 330},
}))
// background images
imgs, err := images.GetImages()
if err != nil {
panic(err)
}
// set resources
builder.SetResources(
rotate.WithImages(imgs),
)
return builder.Make()
}

View File

@@ -0,0 +1,44 @@
package captcha
import (
"github.com/wenlng/go-captcha-assets/resources/images"
"github.com/wenlng/go-captcha-assets/resources/tiles"
"github.com/wenlng/go-captcha/v2/slide"
)
// NewSlideCaptcha 初始化滑动验证码
func NewSlideCaptcha() slide.Captcha {
builder := slide.NewBuilder(
// slide.WithGenGraphNumber(2),
slide.WithEnableGraphVerticalRandom(true),
)
// background images
imgs, err := images.GetImages()
if err != nil {
panic(err)
}
graphs, err := tiles.GetTiles()
if err != nil {
panic(err)
}
var newGraphs = make([]*slide.GraphImage, 0, len(graphs))
for i := 0; i < len(graphs); i++ {
graph := graphs[i]
newGraphs = append(newGraphs, &slide.GraphImage{
OverlayImage: graph.OverlayImage,
MaskImage: graph.MaskImage,
ShadowImage: graph.ShadowImage,
})
}
// set resources
builder.SetResources(
slide.WithGraphImages(newGraphs),
slide.WithBackgrounds(imgs),
)
return builder.Make()
}

View File

@@ -0,0 +1,43 @@
package captcha
import (
"github.com/wenlng/go-captcha-assets/resources/images"
"github.com/wenlng/go-captcha-assets/resources/tiles"
"github.com/wenlng/go-captcha/v2/slide"
)
// NewSlideRegionCaptcha 初始化滑动区域验证码
func NewSlideRegionCaptcha() slide.Captcha {
builder := slide.NewBuilder(
slide.WithGenGraphNumber(2),
slide.WithEnableGraphVerticalRandom(true),
)
// background image
imgs, err := images.GetImages()
if err != nil {
panic(err)
}
graphs, err := tiles.GetTiles()
if err != nil {
panic(err)
}
var newGraphs = make([]*slide.GraphImage, 0, len(graphs))
for i := 0; i < len(graphs); i++ {
graph := graphs[i]
newGraphs = append(newGraphs, &slide.GraphImage{
OverlayImage: graph.OverlayImage,
MaskImage: graph.MaskImage,
ShadowImage: graph.ShadowImage,
})
}
// set resources
builder.SetResources(
slide.WithGraphImages(newGraphs),
slide.WithBackgrounds(imgs),
)
return builder.MakeWithRegion()
}

View File

@@ -0,0 +1,97 @@
package captcha
import (
"github.com/golang/freetype/truetype"
"github.com/wenlng/go-captcha-assets/resources/fonts/fzshengsksjw"
"github.com/wenlng/go-captcha-assets/resources/images"
"github.com/wenlng/go-captcha-assets/sourcedata/chars"
"github.com/wenlng/go-captcha/v2/base/option"
"github.com/wenlng/go-captcha/v2/click"
)
// NewTextCaptcha 初始化点选验证码
func NewTextCaptcha() click.Captcha {
builder := click.NewBuilder(
click.WithRangeLen(option.RangeVal{Min: 4, Max: 6}),
click.WithRangeVerifyLen(option.RangeVal{Min: 2, Max: 4}),
click.WithRangeThumbColors([]string{
"#1f55c4",
"#780592",
"#2f6b00",
"#910000",
"#864401",
"#675901",
"#016e5c",
}),
click.WithRangeColors([]string{
"#fde98e",
"#60c1ff",
"#fcb08e",
"#fb88ff",
"#b4fed4",
"#cbfaa9",
"#78d6f8",
}),
)
// fonts
fonts, err := fzshengsksjw.GetFont()
if err != nil {
panic(err)
}
// background images
imgs, err := images.GetImages()
if err != nil {
panic(err)
}
// thumb images
// thumbImages, err := thumbs.GetThumbs()
// if err != nil {
// log.Fatalln(err)
// }
// set resources
builder.SetResources(
click.WithChars(chars.GetChineseChars()),
// click.WithChars([]string{
// "1A",
// "5E",
// "3d",
// "0p",
// "78",
// "DL",
// "CB",
// "9M",
// }),
// click.WithChars(chars.GetAlphaChars()),
click.WithFonts([]*truetype.Font{fonts}),
click.WithBackgrounds(imgs),
// click.WithThumbBackgrounds(thumbImages),
)
// global.TextCaptcha = builder.Make()
// ============================
builder.Clear()
builder.SetOptions(
click.WithRangeLen(option.RangeVal{Min: 4, Max: 6}),
click.WithRangeVerifyLen(option.RangeVal{Min: 2, Max: 4}),
click.WithRangeThumbColors([]string{
"#4a85fb",
"#d93ffb",
"#56be01",
"#ee2b2b",
"#cd6904",
"#b49b03",
"#01ad90",
}),
)
builder.SetResources(
click.WithChars(chars.GetChineseChars()),
click.WithFonts([]*truetype.Font{fonts}),
click.WithBackgrounds(imgs),
)
return builder.Make()
}

View File

@@ -31,8 +31,8 @@ var (
ScaAuthRoleColumns = []*schema.Column{
{Name: "id", Type: field.TypeInt64, Increment: true, Comment: "主键ID", SchemaType: map[string]string{"mysql": "bigint(20)"}},
{Name: "created_at", Type: field.TypeTime, Comment: "创建时间"},
{Name: "updated_at", Type: field.TypeTime, Comment: "更新时间"},
{Name: "deleted", Type: field.TypeInt8, Nullable: true, Comment: "是否删除 0 未删除 1 已删除", Default: 0},
{Name: "updated_at", Type: field.TypeTime, Nullable: true, Comment: "更新时间"},
{Name: "deleted", Type: field.TypeInt8, Comment: "是否删除 0 未删除 1 已删除", Default: 0},
{Name: "role_name", Type: field.TypeString, Size: 32, Comment: "角色名称"},
{Name: "role_key", Type: field.TypeString, Size: 64, Comment: "角色关键字"},
}
@@ -47,8 +47,8 @@ var (
ScaAuthUserColumns = []*schema.Column{
{Name: "id", Type: field.TypeInt64, Increment: true, Comment: "自增ID", SchemaType: map[string]string{"mysql": "bigint(20)"}},
{Name: "created_at", Type: field.TypeTime, Comment: "创建时间"},
{Name: "updated_at", Type: field.TypeTime, Comment: "更新时间"},
{Name: "deleted", Type: field.TypeInt8, Nullable: true, Comment: "是否删除 0 未删除 1 已删除", Default: 0},
{Name: "updated_at", Type: field.TypeTime, Nullable: true, Comment: "更新时间"},
{Name: "deleted", Type: field.TypeInt8, Comment: "是否删除 0 未删除 1 已删除", Default: 0},
{Name: "uid", Type: field.TypeString, Unique: true, Size: 20, Comment: "唯一ID"},
{Name: "username", Type: field.TypeString, Nullable: true, Size: 32, Comment: "用户名"},
{Name: "nickname", Type: field.TypeString, Nullable: true, Size: 32, Comment: "昵称"},
@@ -56,7 +56,7 @@ var (
{Name: "phone", Type: field.TypeString, Nullable: true, Size: 32, Comment: "电话"},
{Name: "password", Type: field.TypeString, Nullable: true, Size: 64, Comment: "密码"},
{Name: "gender", Type: field.TypeInt8, Nullable: true, Comment: "性别"},
{Name: "avatar", Type: field.TypeString, Nullable: true, Comment: "头像"},
{Name: "avatar", Type: field.TypeString, Nullable: true, Size: 2147483647, Comment: "头像"},
{Name: "status", Type: field.TypeInt8, Nullable: true, Comment: "状态 0 正常 1 封禁", Default: 0},
{Name: "introduce", Type: field.TypeString, Nullable: true, Size: 255, Comment: "介绍"},
{Name: "blog", Type: field.TypeString, Nullable: true, Size: 30, Comment: "博客"},
@@ -91,8 +91,8 @@ var (
ScaAuthUserDeviceColumns = []*schema.Column{
{Name: "id", Type: field.TypeInt64, Increment: true, Comment: "主键ID", SchemaType: map[string]string{"mysql": "bigint(20)"}},
{Name: "created_at", Type: field.TypeTime, Comment: "创建时间"},
{Name: "updated_at", Type: field.TypeTime, Comment: "更新时间"},
{Name: "deleted", Type: field.TypeInt8, Nullable: true, Comment: "是否删除 0 未删除 1 已删除", Default: 0},
{Name: "updated_at", Type: field.TypeTime, Nullable: true, Comment: "更新时间"},
{Name: "deleted", Type: field.TypeInt8, Comment: "是否删除 0 未删除 1 已删除", Default: 0},
{Name: "user_id", Type: field.TypeString, Size: 20, Comment: "用户ID"},
{Name: "ip", Type: field.TypeString, Size: 20, Comment: "登录IP"},
{Name: "location", Type: field.TypeString, Size: 20, Comment: "地址"},
@@ -125,8 +125,8 @@ var (
ScaAuthUserSocialColumns = []*schema.Column{
{Name: "id", Type: field.TypeInt64, Increment: true, Comment: "主键ID", SchemaType: map[string]string{"mysql": "bigint(20)"}},
{Name: "created_at", Type: field.TypeTime, Comment: "创建时间"},
{Name: "updated_at", Type: field.TypeTime, Comment: "更新时间"},
{Name: "deleted", Type: field.TypeInt8, Nullable: true, Comment: "是否删除 0 未删除 1 已删除", Default: 0},
{Name: "updated_at", Type: field.TypeTime, Nullable: true, Comment: "更新时间"},
{Name: "deleted", Type: field.TypeInt8, Comment: "是否删除 0 未删除 1 已删除", Default: 0},
{Name: "user_id", Type: field.TypeString, Size: 20, Comment: "用户ID"},
{Name: "open_id", Type: field.TypeString, Size: 50, Comment: "第三方用户的 open id"},
{Name: "source", Type: field.TypeString, Size: 10, Comment: "第三方用户来源"},

View File

@@ -1016,9 +1016,22 @@ func (m *ScaAuthRoleMutation) OldUpdatedAt(ctx context.Context) (v time.Time, er
return oldValue.UpdatedAt, nil
}
// ClearUpdatedAt clears the value of the "updated_at" field.
func (m *ScaAuthRoleMutation) ClearUpdatedAt() {
m.updated_at = nil
m.clearedFields[scaauthrole.FieldUpdatedAt] = struct{}{}
}
// UpdatedAtCleared returns if the "updated_at" field was cleared in this mutation.
func (m *ScaAuthRoleMutation) UpdatedAtCleared() bool {
_, ok := m.clearedFields[scaauthrole.FieldUpdatedAt]
return ok
}
// ResetUpdatedAt resets all changes to the "updated_at" field.
func (m *ScaAuthRoleMutation) ResetUpdatedAt() {
m.updated_at = nil
delete(m.clearedFields, scaauthrole.FieldUpdatedAt)
}
// SetDeleted sets the "deleted" field.
@@ -1071,24 +1084,10 @@ func (m *ScaAuthRoleMutation) AddedDeleted() (r int8, exists bool) {
return *v, true
}
// ClearDeleted clears the value of the "deleted" field.
func (m *ScaAuthRoleMutation) ClearDeleted() {
m.deleted = nil
m.adddeleted = nil
m.clearedFields[scaauthrole.FieldDeleted] = struct{}{}
}
// DeletedCleared returns if the "deleted" field was cleared in this mutation.
func (m *ScaAuthRoleMutation) DeletedCleared() bool {
_, ok := m.clearedFields[scaauthrole.FieldDeleted]
return ok
}
// ResetDeleted resets all changes to the "deleted" field.
func (m *ScaAuthRoleMutation) ResetDeleted() {
m.deleted = nil
m.adddeleted = nil
delete(m.clearedFields, scaauthrole.FieldDeleted)
}
// SetRoleName sets the "role_name" field.
@@ -1339,8 +1338,8 @@ func (m *ScaAuthRoleMutation) AddField(name string, value ent.Value) error {
// mutation.
func (m *ScaAuthRoleMutation) ClearedFields() []string {
var fields []string
if m.FieldCleared(scaauthrole.FieldDeleted) {
fields = append(fields, scaauthrole.FieldDeleted)
if m.FieldCleared(scaauthrole.FieldUpdatedAt) {
fields = append(fields, scaauthrole.FieldUpdatedAt)
}
return fields
}
@@ -1356,8 +1355,8 @@ func (m *ScaAuthRoleMutation) FieldCleared(name string) bool {
// error if the field is not defined in the schema.
func (m *ScaAuthRoleMutation) ClearField(name string) error {
switch name {
case scaauthrole.FieldDeleted:
m.ClearDeleted()
case scaauthrole.FieldUpdatedAt:
m.ClearUpdatedAt()
return nil
}
return fmt.Errorf("unknown ScaAuthRole nullable field %s", name)
@@ -1636,9 +1635,22 @@ func (m *ScaAuthUserMutation) OldUpdatedAt(ctx context.Context) (v time.Time, er
return oldValue.UpdatedAt, nil
}
// ClearUpdatedAt clears the value of the "updated_at" field.
func (m *ScaAuthUserMutation) ClearUpdatedAt() {
m.updated_at = nil
m.clearedFields[scaauthuser.FieldUpdatedAt] = struct{}{}
}
// UpdatedAtCleared returns if the "updated_at" field was cleared in this mutation.
func (m *ScaAuthUserMutation) UpdatedAtCleared() bool {
_, ok := m.clearedFields[scaauthuser.FieldUpdatedAt]
return ok
}
// ResetUpdatedAt resets all changes to the "updated_at" field.
func (m *ScaAuthUserMutation) ResetUpdatedAt() {
m.updated_at = nil
delete(m.clearedFields, scaauthuser.FieldUpdatedAt)
}
// SetDeleted sets the "deleted" field.
@@ -1691,24 +1703,10 @@ func (m *ScaAuthUserMutation) AddedDeleted() (r int8, exists bool) {
return *v, true
}
// ClearDeleted clears the value of the "deleted" field.
func (m *ScaAuthUserMutation) ClearDeleted() {
m.deleted = nil
m.adddeleted = nil
m.clearedFields[scaauthuser.FieldDeleted] = struct{}{}
}
// DeletedCleared returns if the "deleted" field was cleared in this mutation.
func (m *ScaAuthUserMutation) DeletedCleared() bool {
_, ok := m.clearedFields[scaauthuser.FieldDeleted]
return ok
}
// ResetDeleted resets all changes to the "deleted" field.
func (m *ScaAuthUserMutation) ResetDeleted() {
m.deleted = nil
m.adddeleted = nil
delete(m.clearedFields, scaauthuser.FieldDeleted)
}
// SetUID sets the "uid" field.
@@ -2731,8 +2729,8 @@ func (m *ScaAuthUserMutation) AddField(name string, value ent.Value) error {
// mutation.
func (m *ScaAuthUserMutation) ClearedFields() []string {
var fields []string
if m.FieldCleared(scaauthuser.FieldDeleted) {
fields = append(fields, scaauthuser.FieldDeleted)
if m.FieldCleared(scaauthuser.FieldUpdatedAt) {
fields = append(fields, scaauthuser.FieldUpdatedAt)
}
if m.FieldCleared(scaauthuser.FieldUsername) {
fields = append(fields, scaauthuser.FieldUsername)
@@ -2784,8 +2782,8 @@ func (m *ScaAuthUserMutation) FieldCleared(name string) bool {
// error if the field is not defined in the schema.
func (m *ScaAuthUserMutation) ClearField(name string) error {
switch name {
case scaauthuser.FieldDeleted:
m.ClearDeleted()
case scaauthuser.FieldUpdatedAt:
m.ClearUpdatedAt()
return nil
case scaauthuser.FieldUsername:
m.ClearUsername()
@@ -3131,9 +3129,22 @@ func (m *ScaAuthUserDeviceMutation) OldUpdatedAt(ctx context.Context) (v time.Ti
return oldValue.UpdatedAt, nil
}
// ClearUpdatedAt clears the value of the "updated_at" field.
func (m *ScaAuthUserDeviceMutation) ClearUpdatedAt() {
m.updated_at = nil
m.clearedFields[scaauthuserdevice.FieldUpdatedAt] = struct{}{}
}
// UpdatedAtCleared returns if the "updated_at" field was cleared in this mutation.
func (m *ScaAuthUserDeviceMutation) UpdatedAtCleared() bool {
_, ok := m.clearedFields[scaauthuserdevice.FieldUpdatedAt]
return ok
}
// ResetUpdatedAt resets all changes to the "updated_at" field.
func (m *ScaAuthUserDeviceMutation) ResetUpdatedAt() {
m.updated_at = nil
delete(m.clearedFields, scaauthuserdevice.FieldUpdatedAt)
}
// SetDeleted sets the "deleted" field.
@@ -3186,24 +3197,10 @@ func (m *ScaAuthUserDeviceMutation) AddedDeleted() (r int8, exists bool) {
return *v, true
}
// ClearDeleted clears the value of the "deleted" field.
func (m *ScaAuthUserDeviceMutation) ClearDeleted() {
m.deleted = nil
m.adddeleted = nil
m.clearedFields[scaauthuserdevice.FieldDeleted] = struct{}{}
}
// DeletedCleared returns if the "deleted" field was cleared in this mutation.
func (m *ScaAuthUserDeviceMutation) DeletedCleared() bool {
_, ok := m.clearedFields[scaauthuserdevice.FieldDeleted]
return ok
}
// ResetDeleted resets all changes to the "deleted" field.
func (m *ScaAuthUserDeviceMutation) ResetDeleted() {
m.deleted = nil
m.adddeleted = nil
delete(m.clearedFields, scaauthuserdevice.FieldDeleted)
}
// SetUserID sets the "user_id" field.
@@ -4004,8 +4001,8 @@ func (m *ScaAuthUserDeviceMutation) AddField(name string, value ent.Value) error
// mutation.
func (m *ScaAuthUserDeviceMutation) ClearedFields() []string {
var fields []string
if m.FieldCleared(scaauthuserdevice.FieldDeleted) {
fields = append(fields, scaauthuserdevice.FieldDeleted)
if m.FieldCleared(scaauthuserdevice.FieldUpdatedAt) {
fields = append(fields, scaauthuserdevice.FieldUpdatedAt)
}
return fields
}
@@ -4021,8 +4018,8 @@ func (m *ScaAuthUserDeviceMutation) FieldCleared(name string) bool {
// error if the field is not defined in the schema.
func (m *ScaAuthUserDeviceMutation) ClearField(name string) error {
switch name {
case scaauthuserdevice.FieldDeleted:
m.ClearDeleted()
case scaauthuserdevice.FieldUpdatedAt:
m.ClearUpdatedAt()
return nil
}
return fmt.Errorf("unknown ScaAuthUserDevice nullable field %s", name)
@@ -4324,9 +4321,22 @@ func (m *ScaAuthUserSocialMutation) OldUpdatedAt(ctx context.Context) (v time.Ti
return oldValue.UpdatedAt, nil
}
// ClearUpdatedAt clears the value of the "updated_at" field.
func (m *ScaAuthUserSocialMutation) ClearUpdatedAt() {
m.updated_at = nil
m.clearedFields[scaauthusersocial.FieldUpdatedAt] = struct{}{}
}
// UpdatedAtCleared returns if the "updated_at" field was cleared in this mutation.
func (m *ScaAuthUserSocialMutation) UpdatedAtCleared() bool {
_, ok := m.clearedFields[scaauthusersocial.FieldUpdatedAt]
return ok
}
// ResetUpdatedAt resets all changes to the "updated_at" field.
func (m *ScaAuthUserSocialMutation) ResetUpdatedAt() {
m.updated_at = nil
delete(m.clearedFields, scaauthusersocial.FieldUpdatedAt)
}
// SetDeleted sets the "deleted" field.
@@ -4379,24 +4389,10 @@ func (m *ScaAuthUserSocialMutation) AddedDeleted() (r int8, exists bool) {
return *v, true
}
// ClearDeleted clears the value of the "deleted" field.
func (m *ScaAuthUserSocialMutation) ClearDeleted() {
m.deleted = nil
m.adddeleted = nil
m.clearedFields[scaauthusersocial.FieldDeleted] = struct{}{}
}
// DeletedCleared returns if the "deleted" field was cleared in this mutation.
func (m *ScaAuthUserSocialMutation) DeletedCleared() bool {
_, ok := m.clearedFields[scaauthusersocial.FieldDeleted]
return ok
}
// ResetDeleted resets all changes to the "deleted" field.
func (m *ScaAuthUserSocialMutation) ResetDeleted() {
m.deleted = nil
m.adddeleted = nil
delete(m.clearedFields, scaauthusersocial.FieldDeleted)
}
// SetUserID sets the "user_id" field.
@@ -4779,8 +4775,8 @@ func (m *ScaAuthUserSocialMutation) AddField(name string, value ent.Value) error
// mutation.
func (m *ScaAuthUserSocialMutation) ClearedFields() []string {
var fields []string
if m.FieldCleared(scaauthusersocial.FieldDeleted) {
fields = append(fields, scaauthusersocial.FieldDeleted)
if m.FieldCleared(scaauthusersocial.FieldUpdatedAt) {
fields = append(fields, scaauthusersocial.FieldUpdatedAt)
}
return fields
}
@@ -4796,8 +4792,8 @@ func (m *ScaAuthUserSocialMutation) FieldCleared(name string) bool {
// error if the field is not defined in the schema.
func (m *ScaAuthUserSocialMutation) ClearField(name string) error {
switch name {
case scaauthusersocial.FieldDeleted:
m.ClearDeleted()
case scaauthusersocial.FieldUpdatedAt:
m.ClearUpdatedAt()
return nil
}
return fmt.Errorf("unknown ScaAuthUserSocial nullable field %s", name)

View File

@@ -57,8 +57,6 @@ func init() {
scaauthrole.DefaultCreatedAt = scaauthroleDescCreatedAt.Default.(func() time.Time)
// scaauthroleDescUpdatedAt is the schema descriptor for updated_at field.
scaauthroleDescUpdatedAt := scaauthroleMixinFields0[1].Descriptor()
// scaauthrole.DefaultUpdatedAt holds the default value on creation for the updated_at field.
scaauthrole.DefaultUpdatedAt = scaauthroleDescUpdatedAt.Default.(func() time.Time)
// scaauthrole.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field.
scaauthrole.UpdateDefaultUpdatedAt = scaauthroleDescUpdatedAt.UpdateDefault.(func() time.Time)
// scaauthroleDescDeleted is the schema descriptor for deleted field.
@@ -86,8 +84,6 @@ func init() {
scaauthuser.DefaultCreatedAt = scaauthuserDescCreatedAt.Default.(func() time.Time)
// scaauthuserDescUpdatedAt is the schema descriptor for updated_at field.
scaauthuserDescUpdatedAt := scaauthuserMixinFields0[1].Descriptor()
// scaauthuser.DefaultUpdatedAt holds the default value on creation for the updated_at field.
scaauthuser.DefaultUpdatedAt = scaauthuserDescUpdatedAt.Default.(func() time.Time)
// scaauthuser.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field.
scaauthuser.UpdateDefaultUpdatedAt = scaauthuserDescUpdatedAt.UpdateDefault.(func() time.Time)
// scaauthuserDescDeleted is the schema descriptor for deleted field.
@@ -151,8 +147,6 @@ func init() {
scaauthuserdevice.DefaultCreatedAt = scaauthuserdeviceDescCreatedAt.Default.(func() time.Time)
// scaauthuserdeviceDescUpdatedAt is the schema descriptor for updated_at field.
scaauthuserdeviceDescUpdatedAt := scaauthuserdeviceMixinFields0[1].Descriptor()
// scaauthuserdevice.DefaultUpdatedAt holds the default value on creation for the updated_at field.
scaauthuserdevice.DefaultUpdatedAt = scaauthuserdeviceDescUpdatedAt.Default.(func() time.Time)
// scaauthuserdevice.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field.
scaauthuserdevice.UpdateDefaultUpdatedAt = scaauthuserdeviceDescUpdatedAt.UpdateDefault.(func() time.Time)
// scaauthuserdeviceDescDeleted is the schema descriptor for deleted field.
@@ -216,8 +210,6 @@ func init() {
scaauthusersocial.DefaultCreatedAt = scaauthusersocialDescCreatedAt.Default.(func() time.Time)
// scaauthusersocialDescUpdatedAt is the schema descriptor for updated_at field.
scaauthusersocialDescUpdatedAt := scaauthusersocialMixinFields0[1].Descriptor()
// scaauthusersocial.DefaultUpdatedAt holds the default value on creation for the updated_at field.
scaauthusersocial.DefaultUpdatedAt = scaauthusersocialDescUpdatedAt.Default.(func() time.Time)
// scaauthusersocial.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field.
scaauthusersocial.UpdateDefaultUpdatedAt = scaauthusersocialDescUpdatedAt.UpdateDefault.(func() time.Time)
// scaauthusersocialDescDeleted is the schema descriptor for deleted field.

View File

@@ -50,8 +50,6 @@ func ValidColumn(column string) bool {
var (
// DefaultCreatedAt holds the default value on creation for the "created_at" field.
DefaultCreatedAt func() time.Time
// DefaultUpdatedAt holds the default value on creation for the "updated_at" field.
DefaultUpdatedAt func() time.Time
// UpdateDefaultUpdatedAt holds the default value on update for the "updated_at" field.
UpdateDefaultUpdatedAt func() time.Time
// DefaultDeleted holds the default value on creation for the "deleted" field.

View File

@@ -159,6 +159,16 @@ func UpdatedAtLTE(v time.Time) predicate.ScaAuthRole {
return predicate.ScaAuthRole(sql.FieldLTE(FieldUpdatedAt, v))
}
// UpdatedAtIsNil applies the IsNil predicate on the "updated_at" field.
func UpdatedAtIsNil() predicate.ScaAuthRole {
return predicate.ScaAuthRole(sql.FieldIsNull(FieldUpdatedAt))
}
// UpdatedAtNotNil applies the NotNil predicate on the "updated_at" field.
func UpdatedAtNotNil() predicate.ScaAuthRole {
return predicate.ScaAuthRole(sql.FieldNotNull(FieldUpdatedAt))
}
// DeletedEQ applies the EQ predicate on the "deleted" field.
func DeletedEQ(v int8) predicate.ScaAuthRole {
return predicate.ScaAuthRole(sql.FieldEQ(FieldDeleted, v))
@@ -199,16 +209,6 @@ func DeletedLTE(v int8) predicate.ScaAuthRole {
return predicate.ScaAuthRole(sql.FieldLTE(FieldDeleted, v))
}
// DeletedIsNil applies the IsNil predicate on the "deleted" field.
func DeletedIsNil() predicate.ScaAuthRole {
return predicate.ScaAuthRole(sql.FieldIsNull(FieldDeleted))
}
// DeletedNotNil applies the NotNil predicate on the "deleted" field.
func DeletedNotNil() predicate.ScaAuthRole {
return predicate.ScaAuthRole(sql.FieldNotNull(FieldDeleted))
}
// RoleNameEQ applies the EQ predicate on the "role_name" field.
func RoleNameEQ(v string) predicate.ScaAuthRole {
return predicate.ScaAuthRole(sql.FieldEQ(FieldRoleName, v))

View File

@@ -119,10 +119,6 @@ func (sarc *ScaAuthRoleCreate) defaults() {
v := scaauthrole.DefaultCreatedAt()
sarc.mutation.SetCreatedAt(v)
}
if _, ok := sarc.mutation.UpdatedAt(); !ok {
v := scaauthrole.DefaultUpdatedAt()
sarc.mutation.SetUpdatedAt(v)
}
if _, ok := sarc.mutation.Deleted(); !ok {
v := scaauthrole.DefaultDeleted
sarc.mutation.SetDeleted(v)
@@ -134,8 +130,8 @@ func (sarc *ScaAuthRoleCreate) check() error {
if _, ok := sarc.mutation.CreatedAt(); !ok {
return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "ScaAuthRole.created_at"`)}
}
if _, ok := sarc.mutation.UpdatedAt(); !ok {
return &ValidationError{Name: "updated_at", err: errors.New(`ent: missing required field "ScaAuthRole.updated_at"`)}
if _, ok := sarc.mutation.Deleted(); !ok {
return &ValidationError{Name: "deleted", err: errors.New(`ent: missing required field "ScaAuthRole.deleted"`)}
}
if v, ok := sarc.mutation.Deleted(); ok {
if err := scaauthrole.DeletedValidator(v); err != nil {

View File

@@ -34,6 +34,12 @@ func (saru *ScaAuthRoleUpdate) SetUpdatedAt(t time.Time) *ScaAuthRoleUpdate {
return saru
}
// ClearUpdatedAt clears the value of the "updated_at" field.
func (saru *ScaAuthRoleUpdate) ClearUpdatedAt() *ScaAuthRoleUpdate {
saru.mutation.ClearUpdatedAt()
return saru
}
// SetDeleted sets the "deleted" field.
func (saru *ScaAuthRoleUpdate) SetDeleted(i int8) *ScaAuthRoleUpdate {
saru.mutation.ResetDeleted()
@@ -55,12 +61,6 @@ func (saru *ScaAuthRoleUpdate) AddDeleted(i int8) *ScaAuthRoleUpdate {
return saru
}
// ClearDeleted clears the value of the "deleted" field.
func (saru *ScaAuthRoleUpdate) ClearDeleted() *ScaAuthRoleUpdate {
saru.mutation.ClearDeleted()
return saru
}
// SetRoleName sets the "role_name" field.
func (saru *ScaAuthRoleUpdate) SetRoleName(s string) *ScaAuthRoleUpdate {
saru.mutation.SetRoleName(s)
@@ -124,7 +124,7 @@ func (saru *ScaAuthRoleUpdate) ExecX(ctx context.Context) {
// defaults sets the default values of the builder before save.
func (saru *ScaAuthRoleUpdate) defaults() {
if _, ok := saru.mutation.UpdatedAt(); !ok {
if _, ok := saru.mutation.UpdatedAt(); !ok && !saru.mutation.UpdatedAtCleared() {
v := scaauthrole.UpdateDefaultUpdatedAt()
saru.mutation.SetUpdatedAt(v)
}
@@ -165,15 +165,15 @@ func (saru *ScaAuthRoleUpdate) sqlSave(ctx context.Context) (n int, err error) {
if value, ok := saru.mutation.UpdatedAt(); ok {
_spec.SetField(scaauthrole.FieldUpdatedAt, field.TypeTime, value)
}
if saru.mutation.UpdatedAtCleared() {
_spec.ClearField(scaauthrole.FieldUpdatedAt, field.TypeTime)
}
if value, ok := saru.mutation.Deleted(); ok {
_spec.SetField(scaauthrole.FieldDeleted, field.TypeInt8, value)
}
if value, ok := saru.mutation.AddedDeleted(); ok {
_spec.AddField(scaauthrole.FieldDeleted, field.TypeInt8, value)
}
if saru.mutation.DeletedCleared() {
_spec.ClearField(scaauthrole.FieldDeleted, field.TypeInt8)
}
if value, ok := saru.mutation.RoleName(); ok {
_spec.SetField(scaauthrole.FieldRoleName, field.TypeString, value)
}
@@ -206,6 +206,12 @@ func (saruo *ScaAuthRoleUpdateOne) SetUpdatedAt(t time.Time) *ScaAuthRoleUpdateO
return saruo
}
// ClearUpdatedAt clears the value of the "updated_at" field.
func (saruo *ScaAuthRoleUpdateOne) ClearUpdatedAt() *ScaAuthRoleUpdateOne {
saruo.mutation.ClearUpdatedAt()
return saruo
}
// SetDeleted sets the "deleted" field.
func (saruo *ScaAuthRoleUpdateOne) SetDeleted(i int8) *ScaAuthRoleUpdateOne {
saruo.mutation.ResetDeleted()
@@ -227,12 +233,6 @@ func (saruo *ScaAuthRoleUpdateOne) AddDeleted(i int8) *ScaAuthRoleUpdateOne {
return saruo
}
// ClearDeleted clears the value of the "deleted" field.
func (saruo *ScaAuthRoleUpdateOne) ClearDeleted() *ScaAuthRoleUpdateOne {
saruo.mutation.ClearDeleted()
return saruo
}
// SetRoleName sets the "role_name" field.
func (saruo *ScaAuthRoleUpdateOne) SetRoleName(s string) *ScaAuthRoleUpdateOne {
saruo.mutation.SetRoleName(s)
@@ -309,7 +309,7 @@ func (saruo *ScaAuthRoleUpdateOne) ExecX(ctx context.Context) {
// defaults sets the default values of the builder before save.
func (saruo *ScaAuthRoleUpdateOne) defaults() {
if _, ok := saruo.mutation.UpdatedAt(); !ok {
if _, ok := saruo.mutation.UpdatedAt(); !ok && !saruo.mutation.UpdatedAtCleared() {
v := scaauthrole.UpdateDefaultUpdatedAt()
saruo.mutation.SetUpdatedAt(v)
}
@@ -367,15 +367,15 @@ func (saruo *ScaAuthRoleUpdateOne) sqlSave(ctx context.Context) (_node *ScaAuthR
if value, ok := saruo.mutation.UpdatedAt(); ok {
_spec.SetField(scaauthrole.FieldUpdatedAt, field.TypeTime, value)
}
if saruo.mutation.UpdatedAtCleared() {
_spec.ClearField(scaauthrole.FieldUpdatedAt, field.TypeTime)
}
if value, ok := saruo.mutation.Deleted(); ok {
_spec.SetField(scaauthrole.FieldDeleted, field.TypeInt8, value)
}
if value, ok := saruo.mutation.AddedDeleted(); ok {
_spec.AddField(scaauthrole.FieldDeleted, field.TypeInt8, value)
}
if saruo.mutation.DeletedCleared() {
_spec.ClearField(scaauthrole.FieldDeleted, field.TypeInt8)
}
if value, ok := saruo.mutation.RoleName(); ok {
_spec.SetField(scaauthrole.FieldRoleName, field.TypeString, value)
}

View File

@@ -83,8 +83,6 @@ func ValidColumn(column string) bool {
var (
// DefaultCreatedAt holds the default value on creation for the "created_at" field.
DefaultCreatedAt func() time.Time
// DefaultUpdatedAt holds the default value on creation for the "updated_at" field.
DefaultUpdatedAt func() time.Time
// UpdateDefaultUpdatedAt holds the default value on update for the "updated_at" field.
UpdateDefaultUpdatedAt func() time.Time
// DefaultDeleted holds the default value on creation for the "deleted" field.

View File

@@ -214,6 +214,16 @@ func UpdatedAtLTE(v time.Time) predicate.ScaAuthUser {
return predicate.ScaAuthUser(sql.FieldLTE(FieldUpdatedAt, v))
}
// UpdatedAtIsNil applies the IsNil predicate on the "updated_at" field.
func UpdatedAtIsNil() predicate.ScaAuthUser {
return predicate.ScaAuthUser(sql.FieldIsNull(FieldUpdatedAt))
}
// UpdatedAtNotNil applies the NotNil predicate on the "updated_at" field.
func UpdatedAtNotNil() predicate.ScaAuthUser {
return predicate.ScaAuthUser(sql.FieldNotNull(FieldUpdatedAt))
}
// DeletedEQ applies the EQ predicate on the "deleted" field.
func DeletedEQ(v int8) predicate.ScaAuthUser {
return predicate.ScaAuthUser(sql.FieldEQ(FieldDeleted, v))
@@ -254,16 +264,6 @@ func DeletedLTE(v int8) predicate.ScaAuthUser {
return predicate.ScaAuthUser(sql.FieldLTE(FieldDeleted, v))
}
// DeletedIsNil applies the IsNil predicate on the "deleted" field.
func DeletedIsNil() predicate.ScaAuthUser {
return predicate.ScaAuthUser(sql.FieldIsNull(FieldDeleted))
}
// DeletedNotNil applies the NotNil predicate on the "deleted" field.
func DeletedNotNil() predicate.ScaAuthUser {
return predicate.ScaAuthUser(sql.FieldNotNull(FieldDeleted))
}
// UIDEQ applies the EQ predicate on the "uid" field.
func UIDEQ(v string) predicate.ScaAuthUser {
return predicate.ScaAuthUser(sql.FieldEQ(FieldUID, v))

View File

@@ -281,10 +281,6 @@ func (sauc *ScaAuthUserCreate) defaults() {
v := scaauthuser.DefaultCreatedAt()
sauc.mutation.SetCreatedAt(v)
}
if _, ok := sauc.mutation.UpdatedAt(); !ok {
v := scaauthuser.DefaultUpdatedAt()
sauc.mutation.SetUpdatedAt(v)
}
if _, ok := sauc.mutation.Deleted(); !ok {
v := scaauthuser.DefaultDeleted
sauc.mutation.SetDeleted(v)
@@ -300,8 +296,8 @@ func (sauc *ScaAuthUserCreate) check() error {
if _, ok := sauc.mutation.CreatedAt(); !ok {
return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "ScaAuthUser.created_at"`)}
}
if _, ok := sauc.mutation.UpdatedAt(); !ok {
return &ValidationError{Name: "updated_at", err: errors.New(`ent: missing required field "ScaAuthUser.updated_at"`)}
if _, ok := sauc.mutation.Deleted(); !ok {
return &ValidationError{Name: "deleted", err: errors.New(`ent: missing required field "ScaAuthUser.deleted"`)}
}
if v, ok := sauc.mutation.Deleted(); ok {
if err := scaauthuser.DeletedValidator(v); err != nil {

View File

@@ -34,6 +34,12 @@ func (sauu *ScaAuthUserUpdate) SetUpdatedAt(t time.Time) *ScaAuthUserUpdate {
return sauu
}
// ClearUpdatedAt clears the value of the "updated_at" field.
func (sauu *ScaAuthUserUpdate) ClearUpdatedAt() *ScaAuthUserUpdate {
sauu.mutation.ClearUpdatedAt()
return sauu
}
// SetDeleted sets the "deleted" field.
func (sauu *ScaAuthUserUpdate) SetDeleted(i int8) *ScaAuthUserUpdate {
sauu.mutation.ResetDeleted()
@@ -55,12 +61,6 @@ func (sauu *ScaAuthUserUpdate) AddDeleted(i int8) *ScaAuthUserUpdate {
return sauu
}
// ClearDeleted clears the value of the "deleted" field.
func (sauu *ScaAuthUserUpdate) ClearDeleted() *ScaAuthUserUpdate {
sauu.mutation.ClearDeleted()
return sauu
}
// SetUID sets the "uid" field.
func (sauu *ScaAuthUserUpdate) SetUID(s string) *ScaAuthUserUpdate {
sauu.mutation.SetUID(s)
@@ -364,7 +364,7 @@ func (sauu *ScaAuthUserUpdate) ExecX(ctx context.Context) {
// defaults sets the default values of the builder before save.
func (sauu *ScaAuthUserUpdate) defaults() {
if _, ok := sauu.mutation.UpdatedAt(); !ok {
if _, ok := sauu.mutation.UpdatedAt(); !ok && !sauu.mutation.UpdatedAtCleared() {
v := scaauthuser.UpdateDefaultUpdatedAt()
sauu.mutation.SetUpdatedAt(v)
}
@@ -445,15 +445,15 @@ func (sauu *ScaAuthUserUpdate) sqlSave(ctx context.Context) (n int, err error) {
if value, ok := sauu.mutation.UpdatedAt(); ok {
_spec.SetField(scaauthuser.FieldUpdatedAt, field.TypeTime, value)
}
if sauu.mutation.UpdatedAtCleared() {
_spec.ClearField(scaauthuser.FieldUpdatedAt, field.TypeTime)
}
if value, ok := sauu.mutation.Deleted(); ok {
_spec.SetField(scaauthuser.FieldDeleted, field.TypeInt8, value)
}
if value, ok := sauu.mutation.AddedDeleted(); ok {
_spec.AddField(scaauthuser.FieldDeleted, field.TypeInt8, value)
}
if sauu.mutation.DeletedCleared() {
_spec.ClearField(scaauthuser.FieldDeleted, field.TypeInt8)
}
if value, ok := sauu.mutation.UID(); ok {
_spec.SetField(scaauthuser.FieldUID, field.TypeString, value)
}
@@ -561,6 +561,12 @@ func (sauuo *ScaAuthUserUpdateOne) SetUpdatedAt(t time.Time) *ScaAuthUserUpdateO
return sauuo
}
// ClearUpdatedAt clears the value of the "updated_at" field.
func (sauuo *ScaAuthUserUpdateOne) ClearUpdatedAt() *ScaAuthUserUpdateOne {
sauuo.mutation.ClearUpdatedAt()
return sauuo
}
// SetDeleted sets the "deleted" field.
func (sauuo *ScaAuthUserUpdateOne) SetDeleted(i int8) *ScaAuthUserUpdateOne {
sauuo.mutation.ResetDeleted()
@@ -582,12 +588,6 @@ func (sauuo *ScaAuthUserUpdateOne) AddDeleted(i int8) *ScaAuthUserUpdateOne {
return sauuo
}
// ClearDeleted clears the value of the "deleted" field.
func (sauuo *ScaAuthUserUpdateOne) ClearDeleted() *ScaAuthUserUpdateOne {
sauuo.mutation.ClearDeleted()
return sauuo
}
// SetUID sets the "uid" field.
func (sauuo *ScaAuthUserUpdateOne) SetUID(s string) *ScaAuthUserUpdateOne {
sauuo.mutation.SetUID(s)
@@ -904,7 +904,7 @@ func (sauuo *ScaAuthUserUpdateOne) ExecX(ctx context.Context) {
// defaults sets the default values of the builder before save.
func (sauuo *ScaAuthUserUpdateOne) defaults() {
if _, ok := sauuo.mutation.UpdatedAt(); !ok {
if _, ok := sauuo.mutation.UpdatedAt(); !ok && !sauuo.mutation.UpdatedAtCleared() {
v := scaauthuser.UpdateDefaultUpdatedAt()
sauuo.mutation.SetUpdatedAt(v)
}
@@ -1002,15 +1002,15 @@ func (sauuo *ScaAuthUserUpdateOne) sqlSave(ctx context.Context) (_node *ScaAuthU
if value, ok := sauuo.mutation.UpdatedAt(); ok {
_spec.SetField(scaauthuser.FieldUpdatedAt, field.TypeTime, value)
}
if sauuo.mutation.UpdatedAtCleared() {
_spec.ClearField(scaauthuser.FieldUpdatedAt, field.TypeTime)
}
if value, ok := sauuo.mutation.Deleted(); ok {
_spec.SetField(scaauthuser.FieldDeleted, field.TypeInt8, value)
}
if value, ok := sauuo.mutation.AddedDeleted(); ok {
_spec.AddField(scaauthuser.FieldDeleted, field.TypeInt8, value)
}
if sauuo.mutation.DeletedCleared() {
_spec.ClearField(scaauthuser.FieldDeleted, field.TypeInt8)
}
if value, ok := sauuo.mutation.UID(); ok {
_spec.SetField(scaauthuser.FieldUID, field.TypeString, value)
}

View File

@@ -83,8 +83,6 @@ func ValidColumn(column string) bool {
var (
// DefaultCreatedAt holds the default value on creation for the "created_at" field.
DefaultCreatedAt func() time.Time
// DefaultUpdatedAt holds the default value on creation for the "updated_at" field.
DefaultUpdatedAt func() time.Time
// UpdateDefaultUpdatedAt holds the default value on update for the "updated_at" field.
UpdateDefaultUpdatedAt func() time.Time
// DefaultDeleted holds the default value on creation for the "deleted" field.

View File

@@ -214,6 +214,16 @@ func UpdatedAtLTE(v time.Time) predicate.ScaAuthUserDevice {
return predicate.ScaAuthUserDevice(sql.FieldLTE(FieldUpdatedAt, v))
}
// UpdatedAtIsNil applies the IsNil predicate on the "updated_at" field.
func UpdatedAtIsNil() predicate.ScaAuthUserDevice {
return predicate.ScaAuthUserDevice(sql.FieldIsNull(FieldUpdatedAt))
}
// UpdatedAtNotNil applies the NotNil predicate on the "updated_at" field.
func UpdatedAtNotNil() predicate.ScaAuthUserDevice {
return predicate.ScaAuthUserDevice(sql.FieldNotNull(FieldUpdatedAt))
}
// DeletedEQ applies the EQ predicate on the "deleted" field.
func DeletedEQ(v int8) predicate.ScaAuthUserDevice {
return predicate.ScaAuthUserDevice(sql.FieldEQ(FieldDeleted, v))
@@ -254,16 +264,6 @@ func DeletedLTE(v int8) predicate.ScaAuthUserDevice {
return predicate.ScaAuthUserDevice(sql.FieldLTE(FieldDeleted, v))
}
// DeletedIsNil applies the IsNil predicate on the "deleted" field.
func DeletedIsNil() predicate.ScaAuthUserDevice {
return predicate.ScaAuthUserDevice(sql.FieldIsNull(FieldDeleted))
}
// DeletedNotNil applies the NotNil predicate on the "deleted" field.
func DeletedNotNil() predicate.ScaAuthUserDevice {
return predicate.ScaAuthUserDevice(sql.FieldNotNull(FieldDeleted))
}
// UserIDEQ applies the EQ predicate on the "user_id" field.
func UserIDEQ(v string) predicate.ScaAuthUserDevice {
return predicate.ScaAuthUserDevice(sql.FieldEQ(FieldUserID, v))

View File

@@ -185,10 +185,6 @@ func (saudc *ScaAuthUserDeviceCreate) defaults() {
v := scaauthuserdevice.DefaultCreatedAt()
saudc.mutation.SetCreatedAt(v)
}
if _, ok := saudc.mutation.UpdatedAt(); !ok {
v := scaauthuserdevice.DefaultUpdatedAt()
saudc.mutation.SetUpdatedAt(v)
}
if _, ok := saudc.mutation.Deleted(); !ok {
v := scaauthuserdevice.DefaultDeleted
saudc.mutation.SetDeleted(v)
@@ -200,8 +196,8 @@ func (saudc *ScaAuthUserDeviceCreate) check() error {
if _, ok := saudc.mutation.CreatedAt(); !ok {
return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "ScaAuthUserDevice.created_at"`)}
}
if _, ok := saudc.mutation.UpdatedAt(); !ok {
return &ValidationError{Name: "updated_at", err: errors.New(`ent: missing required field "ScaAuthUserDevice.updated_at"`)}
if _, ok := saudc.mutation.Deleted(); !ok {
return &ValidationError{Name: "deleted", err: errors.New(`ent: missing required field "ScaAuthUserDevice.deleted"`)}
}
if v, ok := saudc.mutation.Deleted(); ok {
if err := scaauthuserdevice.DeletedValidator(v); err != nil {

View File

@@ -34,6 +34,12 @@ func (saudu *ScaAuthUserDeviceUpdate) SetUpdatedAt(t time.Time) *ScaAuthUserDevi
return saudu
}
// ClearUpdatedAt clears the value of the "updated_at" field.
func (saudu *ScaAuthUserDeviceUpdate) ClearUpdatedAt() *ScaAuthUserDeviceUpdate {
saudu.mutation.ClearUpdatedAt()
return saudu
}
// SetDeleted sets the "deleted" field.
func (saudu *ScaAuthUserDeviceUpdate) SetDeleted(i int8) *ScaAuthUserDeviceUpdate {
saudu.mutation.ResetDeleted()
@@ -55,12 +61,6 @@ func (saudu *ScaAuthUserDeviceUpdate) AddDeleted(i int8) *ScaAuthUserDeviceUpdat
return saudu
}
// ClearDeleted clears the value of the "deleted" field.
func (saudu *ScaAuthUserDeviceUpdate) ClearDeleted() *ScaAuthUserDeviceUpdate {
saudu.mutation.ClearDeleted()
return saudu
}
// SetUserID sets the "user_id" field.
func (saudu *ScaAuthUserDeviceUpdate) SetUserID(s string) *ScaAuthUserDeviceUpdate {
saudu.mutation.SetUserID(s)
@@ -278,7 +278,7 @@ func (saudu *ScaAuthUserDeviceUpdate) ExecX(ctx context.Context) {
// defaults sets the default values of the builder before save.
func (saudu *ScaAuthUserDeviceUpdate) defaults() {
if _, ok := saudu.mutation.UpdatedAt(); !ok {
if _, ok := saudu.mutation.UpdatedAt(); !ok && !saudu.mutation.UpdatedAtCleared() {
v := scaauthuserdevice.UpdateDefaultUpdatedAt()
saudu.mutation.SetUpdatedAt(v)
}
@@ -364,15 +364,15 @@ func (saudu *ScaAuthUserDeviceUpdate) sqlSave(ctx context.Context) (n int, err e
if value, ok := saudu.mutation.UpdatedAt(); ok {
_spec.SetField(scaauthuserdevice.FieldUpdatedAt, field.TypeTime, value)
}
if saudu.mutation.UpdatedAtCleared() {
_spec.ClearField(scaauthuserdevice.FieldUpdatedAt, field.TypeTime)
}
if value, ok := saudu.mutation.Deleted(); ok {
_spec.SetField(scaauthuserdevice.FieldDeleted, field.TypeInt8, value)
}
if value, ok := saudu.mutation.AddedDeleted(); ok {
_spec.AddField(scaauthuserdevice.FieldDeleted, field.TypeInt8, value)
}
if saudu.mutation.DeletedCleared() {
_spec.ClearField(scaauthuserdevice.FieldDeleted, field.TypeInt8)
}
if value, ok := saudu.mutation.UserID(); ok {
_spec.SetField(scaauthuserdevice.FieldUserID, field.TypeString, value)
}
@@ -438,6 +438,12 @@ func (sauduo *ScaAuthUserDeviceUpdateOne) SetUpdatedAt(t time.Time) *ScaAuthUser
return sauduo
}
// ClearUpdatedAt clears the value of the "updated_at" field.
func (sauduo *ScaAuthUserDeviceUpdateOne) ClearUpdatedAt() *ScaAuthUserDeviceUpdateOne {
sauduo.mutation.ClearUpdatedAt()
return sauduo
}
// SetDeleted sets the "deleted" field.
func (sauduo *ScaAuthUserDeviceUpdateOne) SetDeleted(i int8) *ScaAuthUserDeviceUpdateOne {
sauduo.mutation.ResetDeleted()
@@ -459,12 +465,6 @@ func (sauduo *ScaAuthUserDeviceUpdateOne) AddDeleted(i int8) *ScaAuthUserDeviceU
return sauduo
}
// ClearDeleted clears the value of the "deleted" field.
func (sauduo *ScaAuthUserDeviceUpdateOne) ClearDeleted() *ScaAuthUserDeviceUpdateOne {
sauduo.mutation.ClearDeleted()
return sauduo
}
// SetUserID sets the "user_id" field.
func (sauduo *ScaAuthUserDeviceUpdateOne) SetUserID(s string) *ScaAuthUserDeviceUpdateOne {
sauduo.mutation.SetUserID(s)
@@ -695,7 +695,7 @@ func (sauduo *ScaAuthUserDeviceUpdateOne) ExecX(ctx context.Context) {
// defaults sets the default values of the builder before save.
func (sauduo *ScaAuthUserDeviceUpdateOne) defaults() {
if _, ok := sauduo.mutation.UpdatedAt(); !ok {
if _, ok := sauduo.mutation.UpdatedAt(); !ok && !sauduo.mutation.UpdatedAtCleared() {
v := scaauthuserdevice.UpdateDefaultUpdatedAt()
sauduo.mutation.SetUpdatedAt(v)
}
@@ -798,15 +798,15 @@ func (sauduo *ScaAuthUserDeviceUpdateOne) sqlSave(ctx context.Context) (_node *S
if value, ok := sauduo.mutation.UpdatedAt(); ok {
_spec.SetField(scaauthuserdevice.FieldUpdatedAt, field.TypeTime, value)
}
if sauduo.mutation.UpdatedAtCleared() {
_spec.ClearField(scaauthuserdevice.FieldUpdatedAt, field.TypeTime)
}
if value, ok := sauduo.mutation.Deleted(); ok {
_spec.SetField(scaauthuserdevice.FieldDeleted, field.TypeInt8, value)
}
if value, ok := sauduo.mutation.AddedDeleted(); ok {
_spec.AddField(scaauthuserdevice.FieldDeleted, field.TypeInt8, value)
}
if sauduo.mutation.DeletedCleared() {
_spec.ClearField(scaauthuserdevice.FieldDeleted, field.TypeInt8)
}
if value, ok := sauduo.mutation.UserID(); ok {
_spec.SetField(scaauthuserdevice.FieldUserID, field.TypeString, value)
}

View File

@@ -56,8 +56,6 @@ func ValidColumn(column string) bool {
var (
// DefaultCreatedAt holds the default value on creation for the "created_at" field.
DefaultCreatedAt func() time.Time
// DefaultUpdatedAt holds the default value on creation for the "updated_at" field.
DefaultUpdatedAt func() time.Time
// UpdateDefaultUpdatedAt holds the default value on update for the "updated_at" field.
UpdateDefaultUpdatedAt func() time.Time
// DefaultDeleted holds the default value on creation for the "deleted" field.

View File

@@ -169,6 +169,16 @@ func UpdatedAtLTE(v time.Time) predicate.ScaAuthUserSocial {
return predicate.ScaAuthUserSocial(sql.FieldLTE(FieldUpdatedAt, v))
}
// UpdatedAtIsNil applies the IsNil predicate on the "updated_at" field.
func UpdatedAtIsNil() predicate.ScaAuthUserSocial {
return predicate.ScaAuthUserSocial(sql.FieldIsNull(FieldUpdatedAt))
}
// UpdatedAtNotNil applies the NotNil predicate on the "updated_at" field.
func UpdatedAtNotNil() predicate.ScaAuthUserSocial {
return predicate.ScaAuthUserSocial(sql.FieldNotNull(FieldUpdatedAt))
}
// DeletedEQ applies the EQ predicate on the "deleted" field.
func DeletedEQ(v int8) predicate.ScaAuthUserSocial {
return predicate.ScaAuthUserSocial(sql.FieldEQ(FieldDeleted, v))
@@ -209,16 +219,6 @@ func DeletedLTE(v int8) predicate.ScaAuthUserSocial {
return predicate.ScaAuthUserSocial(sql.FieldLTE(FieldDeleted, v))
}
// DeletedIsNil applies the IsNil predicate on the "deleted" field.
func DeletedIsNil() predicate.ScaAuthUserSocial {
return predicate.ScaAuthUserSocial(sql.FieldIsNull(FieldDeleted))
}
// DeletedNotNil applies the NotNil predicate on the "deleted" field.
func DeletedNotNil() predicate.ScaAuthUserSocial {
return predicate.ScaAuthUserSocial(sql.FieldNotNull(FieldDeleted))
}
// UserIDEQ applies the EQ predicate on the "user_id" field.
func UserIDEQ(v string) predicate.ScaAuthUserSocial {
return predicate.ScaAuthUserSocial(sql.FieldEQ(FieldUserID, v))

View File

@@ -139,10 +139,6 @@ func (sausc *ScaAuthUserSocialCreate) defaults() {
v := scaauthusersocial.DefaultCreatedAt()
sausc.mutation.SetCreatedAt(v)
}
if _, ok := sausc.mutation.UpdatedAt(); !ok {
v := scaauthusersocial.DefaultUpdatedAt()
sausc.mutation.SetUpdatedAt(v)
}
if _, ok := sausc.mutation.Deleted(); !ok {
v := scaauthusersocial.DefaultDeleted
sausc.mutation.SetDeleted(v)
@@ -158,8 +154,8 @@ func (sausc *ScaAuthUserSocialCreate) check() error {
if _, ok := sausc.mutation.CreatedAt(); !ok {
return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "ScaAuthUserSocial.created_at"`)}
}
if _, ok := sausc.mutation.UpdatedAt(); !ok {
return &ValidationError{Name: "updated_at", err: errors.New(`ent: missing required field "ScaAuthUserSocial.updated_at"`)}
if _, ok := sausc.mutation.Deleted(); !ok {
return &ValidationError{Name: "deleted", err: errors.New(`ent: missing required field "ScaAuthUserSocial.deleted"`)}
}
if v, ok := sausc.mutation.Deleted(); ok {
if err := scaauthusersocial.DeletedValidator(v); err != nil {

View File

@@ -34,6 +34,12 @@ func (sausu *ScaAuthUserSocialUpdate) SetUpdatedAt(t time.Time) *ScaAuthUserSoci
return sausu
}
// ClearUpdatedAt clears the value of the "updated_at" field.
func (sausu *ScaAuthUserSocialUpdate) ClearUpdatedAt() *ScaAuthUserSocialUpdate {
sausu.mutation.ClearUpdatedAt()
return sausu
}
// SetDeleted sets the "deleted" field.
func (sausu *ScaAuthUserSocialUpdate) SetDeleted(i int8) *ScaAuthUserSocialUpdate {
sausu.mutation.ResetDeleted()
@@ -55,12 +61,6 @@ func (sausu *ScaAuthUserSocialUpdate) AddDeleted(i int8) *ScaAuthUserSocialUpdat
return sausu
}
// ClearDeleted clears the value of the "deleted" field.
func (sausu *ScaAuthUserSocialUpdate) ClearDeleted() *ScaAuthUserSocialUpdate {
sausu.mutation.ClearDeleted()
return sausu
}
// SetUserID sets the "user_id" field.
func (sausu *ScaAuthUserSocialUpdate) SetUserID(s string) *ScaAuthUserSocialUpdate {
sausu.mutation.SetUserID(s)
@@ -159,7 +159,7 @@ func (sausu *ScaAuthUserSocialUpdate) ExecX(ctx context.Context) {
// defaults sets the default values of the builder before save.
func (sausu *ScaAuthUserSocialUpdate) defaults() {
if _, ok := sausu.mutation.UpdatedAt(); !ok {
if _, ok := sausu.mutation.UpdatedAt(); !ok && !sausu.mutation.UpdatedAtCleared() {
v := scaauthusersocial.UpdateDefaultUpdatedAt()
sausu.mutation.SetUpdatedAt(v)
}
@@ -205,15 +205,15 @@ func (sausu *ScaAuthUserSocialUpdate) sqlSave(ctx context.Context) (n int, err e
if value, ok := sausu.mutation.UpdatedAt(); ok {
_spec.SetField(scaauthusersocial.FieldUpdatedAt, field.TypeTime, value)
}
if sausu.mutation.UpdatedAtCleared() {
_spec.ClearField(scaauthusersocial.FieldUpdatedAt, field.TypeTime)
}
if value, ok := sausu.mutation.Deleted(); ok {
_spec.SetField(scaauthusersocial.FieldDeleted, field.TypeInt8, value)
}
if value, ok := sausu.mutation.AddedDeleted(); ok {
_spec.AddField(scaauthusersocial.FieldDeleted, field.TypeInt8, value)
}
if sausu.mutation.DeletedCleared() {
_spec.ClearField(scaauthusersocial.FieldDeleted, field.TypeInt8)
}
if value, ok := sausu.mutation.UserID(); ok {
_spec.SetField(scaauthusersocial.FieldUserID, field.TypeString, value)
}
@@ -255,6 +255,12 @@ func (sausuo *ScaAuthUserSocialUpdateOne) SetUpdatedAt(t time.Time) *ScaAuthUser
return sausuo
}
// ClearUpdatedAt clears the value of the "updated_at" field.
func (sausuo *ScaAuthUserSocialUpdateOne) ClearUpdatedAt() *ScaAuthUserSocialUpdateOne {
sausuo.mutation.ClearUpdatedAt()
return sausuo
}
// SetDeleted sets the "deleted" field.
func (sausuo *ScaAuthUserSocialUpdateOne) SetDeleted(i int8) *ScaAuthUserSocialUpdateOne {
sausuo.mutation.ResetDeleted()
@@ -276,12 +282,6 @@ func (sausuo *ScaAuthUserSocialUpdateOne) AddDeleted(i int8) *ScaAuthUserSocialU
return sausuo
}
// ClearDeleted clears the value of the "deleted" field.
func (sausuo *ScaAuthUserSocialUpdateOne) ClearDeleted() *ScaAuthUserSocialUpdateOne {
sausuo.mutation.ClearDeleted()
return sausuo
}
// SetUserID sets the "user_id" field.
func (sausuo *ScaAuthUserSocialUpdateOne) SetUserID(s string) *ScaAuthUserSocialUpdateOne {
sausuo.mutation.SetUserID(s)
@@ -393,7 +393,7 @@ func (sausuo *ScaAuthUserSocialUpdateOne) ExecX(ctx context.Context) {
// defaults sets the default values of the builder before save.
func (sausuo *ScaAuthUserSocialUpdateOne) defaults() {
if _, ok := sausuo.mutation.UpdatedAt(); !ok {
if _, ok := sausuo.mutation.UpdatedAt(); !ok && !sausuo.mutation.UpdatedAtCleared() {
v := scaauthusersocial.UpdateDefaultUpdatedAt()
sausuo.mutation.SetUpdatedAt(v)
}
@@ -456,15 +456,15 @@ func (sausuo *ScaAuthUserSocialUpdateOne) sqlSave(ctx context.Context) (_node *S
if value, ok := sausuo.mutation.UpdatedAt(); ok {
_spec.SetField(scaauthusersocial.FieldUpdatedAt, field.TypeTime, value)
}
if sausuo.mutation.UpdatedAtCleared() {
_spec.ClearField(scaauthusersocial.FieldUpdatedAt, field.TypeTime)
}
if value, ok := sausuo.mutation.Deleted(); ok {
_spec.SetField(scaauthusersocial.FieldDeleted, field.TypeInt8, value)
}
if value, ok := sausuo.mutation.AddedDeleted(); ok {
_spec.AddField(scaauthusersocial.FieldDeleted, field.TypeInt8, value)
}
if sausuo.mutation.DeletedCleared() {
_spec.ClearField(scaauthusersocial.FieldDeleted, field.TypeInt8)
}
if value, ok := sausuo.mutation.UserID(); ok {
_spec.SetField(scaauthusersocial.FieldUserID, field.TypeString, value)
}

View File

@@ -26,8 +26,6 @@ func NewMySQL(url string) *ent.Client {
drv := entsql.OpenDB("mysql", db)
client := ent.NewClient(ent.Driver(drv), ent.Debug())
defer client.Close()
if err = client.Schema.Create(context.Background()); err != nil {
log.Panicf("failed creating model resources: %v", err)
}

View File

@@ -19,13 +19,12 @@ func (DefaultMixin) Fields() []ent.Field {
Default(time.Now).
Comment("创建时间"),
field.Time("updated_at").
Default(time.Now).
Comment("更新时间").
Optional().
UpdateDefault(time.Now),
field.Int8("deleted").
Default(0).
Max(1).
Optional().
Comment("是否删除 0 未删除 1 已删除"),
}
}

View File

@@ -59,7 +59,7 @@ func (ScaAuthUser) Fields() []ent.Field {
field.Int8("gender").
Optional().
Comment("性别"),
field.String("avatar").
field.Text("avatar").
Optional().
Comment("头像"),
field.Int8("status").

View File

@@ -0,0 +1,34 @@
package sensitivex
import (
"os"
"path/filepath"
sensitive "github.com/zmexing/go-sensitive-word"
)
func NewSensitive() *sensitive.Manager {
filter, err := sensitive.NewFilter(
sensitive.StoreOption{Type: sensitive.StoreMemory},
sensitive.FilterOption{Type: sensitive.FilterDfa},
)
if err != nil {
panic(err)
}
cwd, err := os.Getwd()
if err != nil {
panic(err)
}
// 加载敏感词库
err = filter.Store.LoadDictPath(
filepath.Join(cwd, "resources/sensitive/", "反动词库.txt"),
filepath.Join(cwd, "resources/sensitive/", "暴恐词库.txt"),
filepath.Join(cwd, "resources/sensitive/", "色情词库.txt"),
filepath.Join(cwd, "resources/sensitive/", "贪腐词库.txt"),
filepath.Join(cwd, "resources/sensitive/", "民生词库.txt"),
)
if err != nil {
panic(err)
}
return filter
}

View File

@@ -0,0 +1,34 @@
package wechat_public
import (
"os"
"github.com/ArtisanCloud/PowerWeChat/v3/src/kernel"
"github.com/ArtisanCloud/PowerWeChat/v3/src/officialAccount"
)
// NewWechatPublic 微信公众号实例化
func NewWechatPublic(appId, appSecret, token, aesKey, addr, pass string, db int) *officialAccount.OfficialAccount {
OfficialAccountApp, err := officialAccount.NewOfficialAccount(&officialAccount.UserConfig{
AppID: appId,
Secret: appSecret,
Token: token,
AESKey: aesKey,
Log: officialAccount.Log{
Level: "error",
Stdout: true,
},
ResponseType: os.Getenv("response_type"),
HttpDebug: true,
Debug: true,
Cache: kernel.NewRedisClient(&kernel.UniversalOptions{
Addrs: []string{addr},
Password: pass,
DB: db,
}),
})
if err != nil {
panic(err)
}
return OfficialAccountApp
}

View File

@@ -12,4 +12,10 @@ userNotRegistered = "user not registered"
registerError = "register error"
passwordNotMatch = "password not match"
passwordFormatError = "password format error"
resetPasswordError = "reset password error"
resetPasswordError = "reset password error"
loginSuccess = "login success"
[sms]
smsSendTooFrequently = "sms send too frequently"
smsSendFailed = "sms send failed"
smsSendSuccess = "sms send success"

View File

@@ -12,4 +12,10 @@ userNotRegistered = "用户未注册!"
registerError = "注册失败!"
passwordNotMatch = "两次输入的密码不一致!"
passwordFormatError = "密码格式错误!"
resetPasswordError = "重置密码失败!"
resetPasswordError = "重置密码失败!"
loginSuccess = "登录成功!"
[sms]
smsSendTooFrequently = "验证码发送过于频繁,请稍后再试!"
smsSendFailed = "短信发送失败!"
smsSendSuccess = "短信发送成功!"