first commit

This commit is contained in:
2024-10-02 00:57:17 +08:00
commit 6773f86bc5
312 changed files with 19169 additions and 0 deletions

31
apps/gateway/Dockerfile Normal file
View File

@@ -0,0 +1,31 @@
FROM golang:alpine AS builder
LABEL stage=gobuilder
ENV CGO_ENABLED 0
ENV GOPROXY https://goproxy.cn,direct
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
RUN apk update --no-cache && apk add --no-cache tzdata
WORKDIR /build
ADD go.mod .
ADD go.sum .
RUN go mod download
COPY . .
COPY apps/gateway/etc /app/etc
RUN go build -ldflags="-s -w" -o /app/gateway apps/gateway/.\gateway.go
FROM scratch
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
COPY --from=builder /usr/share/zoneinfo/Asia/Shanghai /usr/share/zoneinfo/Asia/Shanghai
ENV TZ Asia/Shanghai
WORKDIR /app
COPY --from=builder /app/gateway /app/gateway
COPY --from=builder /app/etc /app/etc
CMD ["./gateway", "-f", "etc/gateway-api.yaml"]

View File

@@ -0,0 +1,38 @@
Name: gateway-api
Host: 0.0.0.0
Port: 10001
DataSource: "tianyuanapi:g3h98u0291j@tcp(127.0.0.1:3307)/tianyuanapi?charset=utf8mb4&parseTime=True&loc=Local"
AuthJWT:
AccessSecret: "Mf5Xph3PoyKzVpRw0Zy1+X4uR/tM7JvGMEV/5p2M/tU="
AccessExpire: 86400 # JWT过期时间
CacheRedis:
- Host: "127.0.0.1:6379"
Pass: "" # Redis 密码,如果未设置则留空
Type: "node" # 单节点模式
VerifyCode:
AccessKeyID: "LTAI5tHKcV1RbC8t68UfsATy"
AccessKeySecret: "wLWjMBnAlchFMa9gC8B7ZVBKaew4t5"
EndpointURL: "dysmsapi.aliyuncs.com"
SignName: "天远查"
TemplateCode: "SMS_299200388"
ValidTime: 300
Qiniu:
AccessKey: "AO6u6sDWi6L9TsPfr4awC7FYP85JTjt3bodZACCM"
SecretKey: "2fjxweGtSAEaUdVgDkWEmN7JbBxHBQDv1cLORb9_"
Bucket: "tianyuanapi"
Domain: "https://file.tianyuanapi.com"
Baidu:
ApiKey: "aMsrBNGUJxgcgqdm3SEdcumm"
SecretKey: "sWlv2h2AWA3aAt5bjXCkE6WeA5AzpAAD"
UserRpc:
Etcd:
Hosts:
- 127.0.0.1:2379
Key: user.rpc
SentinelRpc:
Etcd:
Hosts:
- 127.0.0.1:2379
Key: sentinel.rpc

252
apps/gateway/gateway.api Normal file
View File

@@ -0,0 +1,252 @@
syntax = "console"
//---------------------------- Base ------------------------
type (
healthResp {
time string `json:"time"`
}
)
@server (
group: base
prefix: /api/console/base
)
service gateway-api {
@handler health
get /health returns (healthResp)
}
//--------------------------- Auth ------------------------
type (
LoginReq {
username string `json:"username"`
password string `json:"password"`
}
phoneLoginReq {
phone string `json:"phone"`
code string `json:"code"`
}
RegisterReq {
username string `json:"username"`
password string `json:"password"`
confirmPassword string `json:"confirmPassword"`
phone string `json:"phone"`
code string `json:"code"`
}
GetVerifyCodeReq {
phone string `json:"phone"`
actionType string `json:"actionType"`
}
)
@server (
group: auth
prefix: /api/console/auth
)
service gateway-api {
@handler registerUser
post /register (RegisterReq)
@handler loginUser
post /login (LoginReq)
@handler phoneLoginUser
post /phoneLogin (phoneLoginReq)
@handler getVerifyCode
post /getVerifyCode (GetVerifyCodeReq)
@handler Logout
post /logout
}
//------------------------------ User -----------------------------
type (
enterpriseAuthReq {
enterpriseName string `json:"enterpriseName"`
creditCode string `json:"creditCode"`
legalPerson string `json:"legalPerson"`
businessLicense string `json:"businessLicense"`
enterpriseContact string `json:"enterpriseContact"`
}
UserInfoResp {
username string `json:"username"`
phone string `json:"phone"`
enterpriseAuthStatus string `json:"enterpriseAuthStatus"`
enterpriseName string `json:"enterpriseName"`
creditCode string `json:"creditCode"`
legalPerson string `json:"legalPerson"`
}
UploadBusinessLicenseResp {
url string `json:"url"`
enterpriseName string `json:"enterpriseName"`
creditCode string `json:"creditCode"`
legalPerson string `json:"legalPerson"`
}
secretInfoResp {
AccessId string `json:"accessId"`
AccessKey string `json:"accessKey"`
}
)
@server (
group: user
prefix: /api/console/user
middleware: AuthInterceptor
)
service gateway-api {
@handler getUserInfo
get /info returns (UserInfoResp)
@handler enterpriseAuth
post /enterpriseAuth (enterpriseAuthReq)
@handler UploadBusinessLicense
post /businessLicenseUpload returns (UploadBusinessLicenseResp)
@handler GetSecretInfo
post /getSecretInfo returns (secretInfoResp)
}
type (
GetProductByIdReq {
ProductId int64 `path:"productId"`
}
GetProductByIdResp {
ProductItem
}
GetProductListReq {
Page int64 `form:"page"`
PageSize int64 `form:"pageSize"`
}
GetProductListResp {
Total int64 `json:"total"`
List []ProductItem `json:"list"`
}
ProductItem {
ProductId int64 `json:"productId"`
ProductName string `json:"productName"`
ProductCode string `json:"productCode"`
ProductDescription string `json:"productDescription"`
ProductContent string `json:"productContent"`
ProductGroup string `json:"productGroup"`
ProductPrice float64 `json:"productPrice"`
CreatedAt string `json:"createdAt"`
UpdatedAt string `json:"updatedAt"`
}
)
@server (
group: product
prefix: /api/console/product
middleware: AuthInterceptor
)
service gateway-api {
@handler getProductById
get /:productId (GetProductByIdReq) returns (GetProductByIdResp)
@handler getProductList
get /list (GetProductListReq) returns (GetProductListResp)
}
type (
// 添加用户产品请求
AddUserProductReq {
ProductId int64 `json:"productId"`
}
// 删除用户产品请求
DeleteUserProductReq {
Id int64 `json:"id"`
}
// 分页查询用户产品列表请求
GetUserProductListReq {
Page int64 `form:"page"`
PageSize int64 `form:"pageSize"`
}
// 分页查询用户产品列表响应
GetUserProductListResp {
Total int64 `json:"total"`
List []UserProductItem `json:"list"`
}
// 用户产品条目
UserProductItem {
Id int64 `json:"id"` // 用户产品ID
ProductId int64 `json:"productId"` // 产品ID
ProductName string `json:"productName"` // 产品名称
ProductCode string `json:"productCode"` // 产品编号
ProductDescription string `json:"productDescription"` // 产品简介
ProductGroup string `json:"productGroup"` // 产品分类
ProductPrice float64 `json:"productPrice"` // 产品价格
CreatedAt string `json:"createdAt"` // 创建时间
UpdatedAt string `json:"updatedAt"` // 更新时间
}
)
@server (
group: userProduct
prefix: /api/console/user-product
middleware: AuthInterceptor
)
service gateway-api {
@handler getUserProductList
get /userProductList (GetUserProductListReq) returns (GetUserProductListResp)
}
@server (
group: userProduct
prefix: /api/console/user-product
middleware: AuthInterceptor,EntAuthInterceptor
)
service gateway-api {
@handler addUserProduct
post /userProductAdd (AddUserProductReq)
@handler deleteUserProduct
delete /userProductDel/:id (DeleteUserProductReq)
}
type (
AddWhitelistReq {
Ip string `json:"ip"`
}
DeleteWhitelistReq {
Id int64 `json:"id"`
}
GetWhitelistListReq {
Page int64 `form:"page"`
PageSize int64 `form:"pageSize"`
}
GetWhitelistListResp {
Total int64 `json:"total"`
List []WhitelistItem `json:"list"`
}
WhitelistItem {
Id int64 `json:"id"` // 用户产品ID
WhitelistIp string `json:"whiteIp"` // 产品名称
CreatedAt string `json:"createdAt"` // 创建时间
UpdatedAt string `json:"updatedAt"` // 更新时间
}
)
@server (
group: whitelistr
prefix: /api/console/whitelist
middleware: AuthInterceptor
)
service gateway-api {
@handler getWhitelistList
get /getWhitelistList (GetWhitelistListReq) returns (GetUserProductListResp)
}
@server (
group: whitelistr
prefix: /api/console/whitelist
middleware: AuthInterceptor,EntAuthInterceptor
)
service gateway-api {
@handler addWhitelist
post /addWhitelist (AddWhitelistReq)
@handler deleteWhitelist
delete /delWhitelist (DeleteWhitelistReq)
}

31
apps/gateway/gateway.go Normal file
View File

@@ -0,0 +1,31 @@
package main
import (
"flag"
"fmt"
"tianyuan-api/apps/gateway/internal/config"
"tianyuan-api/apps/gateway/internal/handler"
"tianyuan-api/apps/gateway/internal/svc"
"github.com/zeromicro/go-zero/core/conf"
"github.com/zeromicro/go-zero/rest"
)
var configFile = flag.String("f", "etc/gateway-api.yaml", "the config file")
func main() {
flag.Parse()
var c config.Config
conf.MustLoad(*configFile, &c)
server := rest.MustNewServer(c.RestConf)
defer server.Stop()
ctx := svc.NewServiceContext(c)
handler.RegisterHandlers(server, ctx)
fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)
server.Start()
}

View File

@@ -0,0 +1,46 @@
package config
import (
"github.com/zeromicro/go-zero/core/stores/cache"
"github.com/zeromicro/go-zero/rest"
"github.com/zeromicro/go-zero/zrpc"
)
type Config struct {
rest.RestConf
DataSource string // 数据库连接的 DSN 字符串
AuthJWT AuthConfig // JWT 鉴权相关配置
CacheRedis cache.CacheConf // 缓存配置,使用 go-zero 自带的缓存配置结构体
UserRpc zrpc.RpcClientConf
SentinelRpc zrpc.RpcClientConf
VerifyCode VerifyCode
Qiniu QiniuConfig
Baidu BaiduConfig
}
// AuthConfig 用于 JWT 鉴权配置
type AuthConfig struct {
AccessSecret string // JWT 密钥,用于签发 Token
AccessExpire int64 // Token 过期时间,单位为秒
}
type VerifyCode struct {
AccessKeyID string
AccessKeySecret string
EndpointURL string
SignName string
TemplateCode string
ValidTime int
}
type QiniuConfig struct {
AccessKey string
SecretKey string
Bucket string
Domain string
}
type BaiduConfig struct {
ApiKey string
SecretKey string
}

View File

@@ -0,0 +1,30 @@
package auth
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"tianyuan-api/apps/gateway/internal/logic/auth"
"tianyuan-api/apps/gateway/internal/svc"
"tianyuan-api/apps/gateway/internal/types"
xhttp "github.com/zeromicro/x/http"
)
func GetVerifyCodeHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.GetVerifyCodeReq
if err := httpx.Parse(r, &req); err != nil {
xhttp.JsonBaseResponseCtx(r.Context(), w, err)
return
}
l := auth.NewGetVerifyCodeLogic(r.Context(), svcCtx)
err := l.GetVerifyCode(&req)
if err != nil {
xhttp.JsonBaseResponseCtx(r.Context(), w, err)
} else {
xhttp.JsonBaseResponseCtx(r.Context(), w, nil)
}
}
}

View File

@@ -0,0 +1,40 @@
package auth
import (
"net/http"
"time"
"github.com/zeromicro/go-zero/rest/httpx"
"tianyuan-api/apps/gateway/internal/logic/auth"
"tianyuan-api/apps/gateway/internal/svc"
"tianyuan-api/apps/gateway/internal/types"
xhttp "github.com/zeromicro/x/http"
)
func LoginUserHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.LoginReq
if err := httpx.Parse(r, &req); err != nil {
xhttp.JsonBaseResponseCtx(r.Context(), w, err)
return
}
l := auth.NewLoginUserLogic(r.Context(), svcCtx)
token, err := l.LoginUser(&req)
if err != nil {
xhttp.JsonBaseResponseCtx(r.Context(), w, err)
} else {
http.SetCookie(w, &http.Cookie{
Name: "Authorization",
Value: token, // JWT 令牌
HttpOnly: true, // 防止 JavaScript 访问
Secure: false, // HTTPS 使用
SameSite: http.SameSiteLaxMode, // 防止 CSRF 攻击
Path: "/",
Expires: time.Now().Add(time.Duration(svcCtx.Config.AuthJWT.AccessExpire) * time.Second), // 过期时间
})
xhttp.JsonBaseResponseCtx(r.Context(), w, nil)
}
}
}

View File

@@ -0,0 +1,23 @@
package auth
import (
"net/http"
"time"
xhttp "github.com/zeromicro/x/http"
"tianyuan-api/apps/gateway/internal/svc"
)
func LogoutHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// 设置空的JWT Cookie覆盖之前的JWT
http.SetCookie(w, &http.Cookie{
Name: "Authorization", // 你的JWT cookie名
Value: "", // 清空cookies
Path: "/",
HttpOnly: true,
Expires: time.Unix(0, 0), // 过期时间设置为过去
})
xhttp.JsonBaseResponseCtx(r.Context(), w, nil)
}
}

View File

@@ -0,0 +1,40 @@
package auth
import (
"net/http"
"time"
"github.com/zeromicro/go-zero/rest/httpx"
"tianyuan-api/apps/gateway/internal/logic/auth"
"tianyuan-api/apps/gateway/internal/svc"
"tianyuan-api/apps/gateway/internal/types"
xhttp "github.com/zeromicro/x/http"
)
func PhoneLoginUserHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.PhoneLoginReq
if err := httpx.Parse(r, &req); err != nil {
xhttp.JsonBaseResponseCtx(r.Context(), w, err)
return
}
l := auth.NewPhoneLoginUserLogic(r.Context(), svcCtx)
token, err := l.PhoneLoginUser(&req)
http.SetCookie(w, &http.Cookie{
Name: "Authorization",
Value: token, // JWT 令牌
HttpOnly: true, // 防止 JavaScript 访问
Secure: false, // HTTPS 使用
SameSite: http.SameSiteLaxMode, // 防止 CSRF 攻击
Path: "/",
Expires: time.Now().Add(time.Duration(svcCtx.Config.AuthJWT.AccessExpire) * time.Second), // 过期时间
})
if err != nil {
xhttp.JsonBaseResponseCtx(r.Context(), w, err)
} else {
xhttp.JsonBaseResponseCtx(r.Context(), w, nil)
}
}
}

View File

@@ -0,0 +1,30 @@
package auth
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"tianyuan-api/apps/gateway/internal/logic/auth"
"tianyuan-api/apps/gateway/internal/svc"
"tianyuan-api/apps/gateway/internal/types"
xhttp "github.com/zeromicro/x/http"
)
func RegisterUserHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.RegisterReq
if err := httpx.Parse(r, &req); err != nil {
xhttp.JsonBaseResponseCtx(r.Context(), w, err)
return
}
l := auth.NewRegisterUserLogic(r.Context(), svcCtx)
err := l.RegisterUser(&req)
if err != nil {
xhttp.JsonBaseResponseCtx(r.Context(), w, err)
} else {
xhttp.JsonBaseResponseCtx(r.Context(), w, nil)
}
}
}

View File

@@ -0,0 +1,21 @@
package base
import (
"net/http"
xhttp "github.com/zeromicro/x/http"
"tianyuan-api/apps/gateway/internal/logic/base"
"tianyuan-api/apps/gateway/internal/svc"
)
func HealthHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
l := base.NewHealthLogic(r.Context(), svcCtx)
resp, err := l.Health()
if err != nil {
xhttp.JsonBaseResponseCtx(r.Context(), w, err)
} else {
xhttp.JsonBaseResponseCtx(r.Context(), w, resp)
}
}
}

View File

@@ -0,0 +1,30 @@
package product
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"tianyuan-api/apps/gateway/internal/logic/product"
"tianyuan-api/apps/gateway/internal/svc"
"tianyuan-api/apps/gateway/internal/types"
xhttp "github.com/zeromicro/x/http"
)
func GetProductByIdHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.GetProductByIdReq
if err := httpx.Parse(r, &req); err != nil {
xhttp.JsonBaseResponseCtx(r.Context(), w, err)
return
}
l := product.NewGetProductByIdLogic(r.Context(), svcCtx)
resp, err := l.GetProductById(&req)
if err != nil {
xhttp.JsonBaseResponseCtx(r.Context(), w, err)
} else {
xhttp.JsonBaseResponseCtx(r.Context(), w, resp)
}
}
}

View File

@@ -0,0 +1,30 @@
package product
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"tianyuan-api/apps/gateway/internal/logic/product"
"tianyuan-api/apps/gateway/internal/svc"
"tianyuan-api/apps/gateway/internal/types"
xhttp "github.com/zeromicro/x/http"
)
func GetProductListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.GetProductListReq
if err := httpx.Parse(r, &req); err != nil {
xhttp.JsonBaseResponseCtx(r.Context(), w, err)
return
}
l := product.NewGetProductListLogic(r.Context(), svcCtx)
resp, err := l.GetProductList(&req)
if err != nil {
xhttp.JsonBaseResponseCtx(r.Context(), w, err)
} else {
xhttp.JsonBaseResponseCtx(r.Context(), w, resp)
}
}
}

View File

@@ -0,0 +1,167 @@
// Code generated by goctl. DO NOT EDIT.
// goctl 1.7.2
package handler
import (
"net/http"
auth "tianyuan-api/apps/gateway/internal/handler/auth"
base "tianyuan-api/apps/gateway/internal/handler/base"
product "tianyuan-api/apps/gateway/internal/handler/product"
user "tianyuan-api/apps/gateway/internal/handler/user"
userProduct "tianyuan-api/apps/gateway/internal/handler/userProduct"
whitelistr "tianyuan-api/apps/gateway/internal/handler/whitelistr"
"tianyuan-api/apps/gateway/internal/svc"
"github.com/zeromicro/go-zero/rest"
)
func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
server.AddRoutes(
[]rest.Route{
{
Method: http.MethodPost,
Path: "/getVerifyCode",
Handler: auth.GetVerifyCodeHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/login",
Handler: auth.LoginUserHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/logout",
Handler: auth.LogoutHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/phoneLogin",
Handler: auth.PhoneLoginUserHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/register",
Handler: auth.RegisterUserHandler(serverCtx),
},
},
rest.WithPrefix("/api/console/auth"),
)
server.AddRoutes(
[]rest.Route{
{
Method: http.MethodGet,
Path: "/health",
Handler: base.HealthHandler(serverCtx),
},
},
rest.WithPrefix("/api/console/base"),
)
server.AddRoutes(
rest.WithMiddlewares(
[]rest.Middleware{serverCtx.AuthInterceptor},
[]rest.Route{
{
Method: http.MethodGet,
Path: "/:productId",
Handler: product.GetProductByIdHandler(serverCtx),
},
{
Method: http.MethodGet,
Path: "/list",
Handler: product.GetProductListHandler(serverCtx),
},
}...,
),
rest.WithPrefix("/api/console/product"),
)
server.AddRoutes(
rest.WithMiddlewares(
[]rest.Middleware{serverCtx.AuthInterceptor},
[]rest.Route{
{
Method: http.MethodPost,
Path: "/businessLicenseUpload",
Handler: user.UploadBusinessLicenseHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/enterpriseAuth",
Handler: user.EnterpriseAuthHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/getSecretInfo",
Handler: user.GetSecretInfoHandler(serverCtx),
},
{
Method: http.MethodGet,
Path: "/info",
Handler: user.GetUserInfoHandler(serverCtx),
},
}...,
),
rest.WithPrefix("/api/console/user"),
)
server.AddRoutes(
rest.WithMiddlewares(
[]rest.Middleware{serverCtx.AuthInterceptor},
[]rest.Route{
{
Method: http.MethodPost,
Path: "/userProductAdd",
Handler: userProduct.AddUserProductHandler(serverCtx),
},
{
Method: http.MethodDelete,
Path: "/userProductDel/:id",
Handler: userProduct.DeleteUserProductHandler(serverCtx),
},
{
Method: http.MethodGet,
Path: "/userProductList",
Handler: userProduct.GetUserProductListHandler(serverCtx),
},
}...,
),
rest.WithPrefix("/api/console/user-product"),
)
server.AddRoutes(
rest.WithMiddlewares(
[]rest.Middleware{serverCtx.AuthInterceptor},
[]rest.Route{
{
Method: http.MethodGet,
Path: "/getWhitelistList",
Handler: whitelistr.GetWhitelistListHandler(serverCtx),
},
}...,
),
rest.WithPrefix("/api/console/whitelist"),
)
server.AddRoutes(
rest.WithMiddlewares(
[]rest.Middleware{serverCtx.AuthInterceptor, serverCtx.EntAuthInterceptor},
[]rest.Route{
{
Method: http.MethodPost,
Path: "/addWhitelist",
Handler: whitelistr.AddWhitelistHandler(serverCtx),
},
{
Method: http.MethodDelete,
Path: "/delWhitelist",
Handler: whitelistr.DeleteWhitelistHandler(serverCtx),
},
}...,
),
rest.WithPrefix("/api/console/whitelist"),
)
}

View File

@@ -0,0 +1,30 @@
package user
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"tianyuan-api/apps/gateway/internal/logic/user"
"tianyuan-api/apps/gateway/internal/svc"
"tianyuan-api/apps/gateway/internal/types"
xhttp "github.com/zeromicro/x/http"
)
func EnterpriseAuthHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.EnterpriseAuthReq
if err := httpx.Parse(r, &req); err != nil {
xhttp.JsonBaseResponseCtx(r.Context(), w, err)
return
}
l := user.NewEnterpriseAuthLogic(r.Context(), svcCtx)
err := l.EnterpriseAuth(&req)
if err != nil {
xhttp.JsonBaseResponseCtx(r.Context(), w, err)
} else {
xhttp.JsonBaseResponseCtx(r.Context(), w, nil)
}
}
}

View File

@@ -0,0 +1,21 @@
package user
import (
"net/http"
xhttp "github.com/zeromicro/x/http"
"tianyuan-api/apps/gateway/internal/logic/user"
"tianyuan-api/apps/gateway/internal/svc"
)
func GetSecretInfoHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
l := user.NewGetSecretInfoLogic(r.Context(), svcCtx)
resp, err := l.GetSecretInfo()
if err != nil {
xhttp.JsonBaseResponseCtx(r.Context(), w, err)
} else {
xhttp.JsonBaseResponseCtx(r.Context(), w, resp)
}
}
}

View File

@@ -0,0 +1,21 @@
package user
import (
"net/http"
xhttp "github.com/zeromicro/x/http"
"tianyuan-api/apps/gateway/internal/logic/user"
"tianyuan-api/apps/gateway/internal/svc"
)
func GetUserInfoHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
l := user.NewGetUserInfoLogic(r.Context(), svcCtx)
resp, err := l.GetUserInfo()
if err != nil {
xhttp.JsonBaseResponseCtx(r.Context(), w, err)
} else {
xhttp.JsonBaseResponseCtx(r.Context(), w, resp)
}
}
}

View File

@@ -0,0 +1,21 @@
package user
import (
"net/http"
xhttp "github.com/zeromicro/x/http"
"tianyuan-api/apps/gateway/internal/logic/user"
"tianyuan-api/apps/gateway/internal/svc"
)
func UploadBusinessLicenseHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
l := user.NewUploadBusinessLicenseLogic(r.Context(), svcCtx)
resp, err := l.UploadBusinessLicense(r)
if err != nil {
xhttp.JsonBaseResponseCtx(r.Context(), w, err)
} else {
xhttp.JsonBaseResponseCtx(r.Context(), w, resp)
}
}
}

View File

@@ -0,0 +1,30 @@
package userProduct
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"tianyuan-api/apps/gateway/internal/logic/userProduct"
"tianyuan-api/apps/gateway/internal/svc"
"tianyuan-api/apps/gateway/internal/types"
xhttp "github.com/zeromicro/x/http"
)
func AddUserProductHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.AddUserProductReq
if err := httpx.Parse(r, &req); err != nil {
xhttp.JsonBaseResponseCtx(r.Context(), w, err)
return
}
l := userProduct.NewAddUserProductLogic(r.Context(), svcCtx)
err := l.AddUserProduct(&req)
if err != nil {
xhttp.JsonBaseResponseCtx(r.Context(), w, err)
} else {
xhttp.JsonBaseResponseCtx(r.Context(), w, nil)
}
}
}

View File

@@ -0,0 +1,30 @@
package userProduct
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"tianyuan-api/apps/gateway/internal/logic/userProduct"
"tianyuan-api/apps/gateway/internal/svc"
"tianyuan-api/apps/gateway/internal/types"
xhttp "github.com/zeromicro/x/http"
)
func DeleteUserProductHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.DeleteUserProductReq
if err := httpx.Parse(r, &req); err != nil {
xhttp.JsonBaseResponseCtx(r.Context(), w, err)
return
}
l := userProduct.NewDeleteUserProductLogic(r.Context(), svcCtx)
err := l.DeleteUserProduct(&req)
if err != nil {
xhttp.JsonBaseResponseCtx(r.Context(), w, err)
} else {
xhttp.JsonBaseResponseCtx(r.Context(), w, nil)
}
}
}

View File

@@ -0,0 +1,30 @@
package userProduct
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"tianyuan-api/apps/gateway/internal/logic/userProduct"
"tianyuan-api/apps/gateway/internal/svc"
"tianyuan-api/apps/gateway/internal/types"
xhttp "github.com/zeromicro/x/http"
)
func GetUserProductListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.GetUserProductListReq
if err := httpx.Parse(r, &req); err != nil {
xhttp.JsonBaseResponseCtx(r.Context(), w, err)
return
}
l := userProduct.NewGetUserProductListLogic(r.Context(), svcCtx)
resp, err := l.GetUserProductList(&req)
if err != nil {
xhttp.JsonBaseResponseCtx(r.Context(), w, err)
} else {
xhttp.JsonBaseResponseCtx(r.Context(), w, resp)
}
}
}

View File

@@ -0,0 +1,30 @@
package whitelistr
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"tianyuan-api/apps/gateway/internal/logic/whitelistr"
"tianyuan-api/apps/gateway/internal/svc"
"tianyuan-api/apps/gateway/internal/types"
xhttp "github.com/zeromicro/x/http"
)
func AddWhitelistHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.AddWhitelistReq
if err := httpx.Parse(r, &req); err != nil {
xhttp.JsonBaseResponseCtx(r.Context(), w, err)
return
}
l := whitelistr.NewAddWhitelistLogic(r.Context(), svcCtx)
err := l.AddWhitelist(&req)
if err != nil {
xhttp.JsonBaseResponseCtx(r.Context(), w, err)
} else {
xhttp.JsonBaseResponseCtx(r.Context(), w, nil)
}
}
}

View File

@@ -0,0 +1,30 @@
package whitelistr
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"tianyuan-api/apps/gateway/internal/logic/whitelistr"
"tianyuan-api/apps/gateway/internal/svc"
"tianyuan-api/apps/gateway/internal/types"
xhttp "github.com/zeromicro/x/http"
)
func DeleteWhitelistHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.DeleteWhitelistReq
if err := httpx.Parse(r, &req); err != nil {
xhttp.JsonBaseResponseCtx(r.Context(), w, err)
return
}
l := whitelistr.NewDeleteWhitelistLogic(r.Context(), svcCtx)
err := l.DeleteWhitelist(&req)
if err != nil {
xhttp.JsonBaseResponseCtx(r.Context(), w, err)
} else {
xhttp.JsonBaseResponseCtx(r.Context(), w, nil)
}
}
}

View File

@@ -0,0 +1,30 @@
package whitelistr
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"tianyuan-api/apps/gateway/internal/logic/whitelistr"
"tianyuan-api/apps/gateway/internal/svc"
"tianyuan-api/apps/gateway/internal/types"
xhttp "github.com/zeromicro/x/http"
)
func GetWhitelistListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.GetWhitelistListReq
if err := httpx.Parse(r, &req); err != nil {
xhttp.JsonBaseResponseCtx(r.Context(), w, err)
return
}
l := whitelistr.NewGetWhitelistListLogic(r.Context(), svcCtx)
resp, err := l.GetWhitelistList(&req)
if err != nil {
xhttp.JsonBaseResponseCtx(r.Context(), w, err)
} else {
xhttp.JsonBaseResponseCtx(r.Context(), w, resp)
}
}
}

View File

@@ -0,0 +1,95 @@
package auth
import (
"context"
"fmt"
openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
dysmsapi "github.com/alibabacloud-go/dysmsapi-20170525/v3/client"
"github.com/alibabacloud-go/tea-utils/v2/service"
"github.com/alibabacloud-go/tea/tea"
"github.com/zeromicro/go-zero/core/logx"
"tianyuan-api/apps/gateway/internal/svc"
"tianyuan-api/apps/gateway/internal/types"
"math/rand"
"time"
)
type GetVerifyCodeLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetVerifyCodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetVerifyCodeLogic {
return &GetVerifyCodeLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetVerifyCodeLogic) GetVerifyCode(req *types.GetVerifyCodeReq) (err error) {
// 校验 actionType 参数的值是否为 "login" 或 "register"
if req.ActionType != "login" && req.ActionType != "register" {
return fmt.Errorf("action_type 参数只能是 'login' 或 'register'")
}
// 检查手机号是否在一分钟内已发送过验证码
redisKey := fmt.Sprintf("%s:%s", req.ActionType, req.Phone)
exists, err := l.svcCtx.Redis.Exists(redisKey)
if err != nil {
return err
}
if exists {
// 如果 Redis 中已经存在标记,说明在 1 分钟内请求过,返回错误
return fmt.Errorf("一分钟内不能重复发送验证码")
}
// 生成随机验证码
code := fmt.Sprintf("%06d", rand.New(rand.NewSource(time.Now().UnixNano())).Intn(1000000))
// 调用阿里云短信服务发送验证码
client, err := l.CreateClient()
if err != nil {
return err
}
sendSmsRequest := &dysmsapi.SendSmsRequest{
SignName: tea.String(l.svcCtx.Config.VerifyCode.SignName),
TemplateCode: tea.String(l.svcCtx.Config.VerifyCode.TemplateCode),
PhoneNumbers: tea.String(req.Phone),
TemplateParam: tea.String(fmt.Sprintf("{\"code\":\"%s\"}", code)),
}
runtime := &service.RuntimeOptions{}
// 这里使用 *dysmsapi.SendSmsResponse 接收返回值
smsResp, err := client.SendSmsWithOptions(sendSmsRequest, runtime)
if err != nil {
return err
}
if *smsResp.Body.Code != "OK" {
return fmt.Errorf("短信发送失败: %s", *smsResp.Body.Message)
}
// 将验证码保存到 Redis设置过期时间
err = l.svcCtx.Redis.Setex(req.Phone, code, l.svcCtx.Config.VerifyCode.ValidTime) // 验证码有效期5分钟
if err != nil {
return err
}
// 在 Redis 中设置 1 分钟的标记,限制重复请求
err = l.svcCtx.Redis.Setex(redisKey, code, 60) // 标记 1 分钟内不能重复请求
if err != nil {
return err
}
// 构建返回给前端的响应
return nil
}
// 创建阿里云短信客户端
func (l *GetVerifyCodeLogic) CreateClient() (*dysmsapi.Client, error) {
config := &openapi.Config{
AccessKeyId: &l.svcCtx.Config.VerifyCode.AccessKeyID,
AccessKeySecret: &l.svcCtx.Config.VerifyCode.AccessKeySecret,
}
config.Endpoint = tea.String(l.svcCtx.Config.VerifyCode.EndpointURL)
return dysmsapi.NewClient(config)
}

View File

@@ -0,0 +1,35 @@
package auth
import (
"context"
"github.com/zeromicro/go-zero/core/logx"
"tianyuan-api/apps/gateway/internal/svc"
"tianyuan-api/apps/gateway/internal/types"
"tianyuan-api/apps/user/user"
)
type LoginUserLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewLoginUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LoginUserLogic {
return &LoginUserLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *LoginUserLogic) LoginUser(req *types.LoginReq) (token string, err error) {
loginResp, err := l.svcCtx.AuthRpc.LoginUser(l.ctx, &user.LoginReq{
Username: req.Username,
Password: req.Password,
})
if err != nil {
return "", err
}
// 返回成功的登录响应
return loginResp.Token, nil
}

View File

@@ -0,0 +1,28 @@
package auth
import (
"context"
"github.com/zeromicro/go-zero/core/logx"
"tianyuan-api/apps/gateway/internal/svc"
)
type LogoutLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewLogoutLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LogoutLogic {
return &LogoutLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *LogoutLogic) Logout() error {
// todo: add your logic here and delete this line
return nil
}

View File

@@ -0,0 +1,35 @@
package auth
import (
"context"
"github.com/zeromicro/go-zero/core/logx"
"tianyuan-api/apps/gateway/internal/svc"
"tianyuan-api/apps/gateway/internal/types"
"tianyuan-api/apps/user/user"
)
type PhoneLoginUserLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewPhoneLoginUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *PhoneLoginUserLogic {
return &PhoneLoginUserLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *PhoneLoginUserLogic) PhoneLoginUser(req *types.PhoneLoginReq) (token string, err error) {
loginResp, err := l.svcCtx.AuthRpc.PhoneLoginUser(l.ctx, &user.PhoneLoginReq{
Phone: req.Phone,
Code: req.Code,
})
if err != nil {
return "", err
}
// 返回成功的登录响应
return loginResp.Token, nil
}

View File

@@ -0,0 +1,32 @@
package auth
import (
"context"
"github.com/zeromicro/go-zero/core/logx"
"tianyuan-api/apps/gateway/internal/svc"
"tianyuan-api/apps/gateway/internal/types"
"tianyuan-api/apps/user/user"
)
type RegisterUserLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewRegisterUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *RegisterUserLogic {
return &RegisterUserLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *RegisterUserLogic) RegisterUser(req *types.RegisterReq) error {
_, err := l.svcCtx.AuthRpc.RegisterUser(l.ctx, &user.RegisterReq{Username: req.Username, Password: req.Password, ConfirmPassword: req.ConfirmPassword, Phone: req.Phone, Code: req.Code})
if err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,33 @@
package base
import (
"context"
"time"
"tianyuan-api/apps/gateway/internal/svc"
"tianyuan-api/apps/gateway/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type HealthLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewHealthLogic(ctx context.Context, svcCtx *svc.ServiceContext) *HealthLogic {
return &HealthLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *HealthLogic) Health() (resp *types.HealthResp, err error) {
nowTime := time.Now()
resp = &types.HealthResp{
Time: nowTime.Format("2006-01-02 15:04:05"),
}
return
}

View File

@@ -0,0 +1,48 @@
package product
import (
"context"
"tianyuan-api/apps/sentinel/client/product"
"tianyuan-api/apps/gateway/internal/svc"
"tianyuan-api/apps/gateway/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type GetProductByIdLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetProductByIdLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetProductByIdLogic {
return &GetProductByIdLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetProductByIdLogic) GetProductById(req *types.GetProductByIdReq) (resp *types.GetProductByIdResp, err error) {
productResp, err := l.svcCtx.ProductRpc.GetProductById(l.ctx, &product.GetRecordByIdRequest{
Id: req.ProductId,
})
if err != nil {
return nil, err
}
return &types.GetProductByIdResp{
ProductItem: types.ProductItem{
ProductId: productResp.Id,
ProductName: productResp.ProductName,
ProductCode: productResp.ProductCode,
ProductDescription: productResp.ProductDescription,
ProductContent: productResp.ProductContent,
ProductGroup: productResp.ProductGroup,
ProductPrice: productResp.ProductPrice,
},
}, nil
return
}

View File

@@ -0,0 +1,51 @@
package product
import (
"context"
"tianyuan-api/apps/sentinel/client/product"
"tianyuan-api/apps/gateway/internal/svc"
"tianyuan-api/apps/gateway/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type GetProductListLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetProductListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetProductListLogic {
return &GetProductListLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetProductListLogic) GetProductList(req *types.GetProductListReq) (resp *types.GetProductListResp, err error) {
productList, err := l.svcCtx.ProductRpc.GetProductPageList(l.ctx, &product.PageListRequest{Page: req.Page, PageSize: req.PageSize})
if err != nil {
return nil, err
}
var list []types.ProductItem
for _, p := range productList.Products {
list = append(list, types.ProductItem{
ProductId: p.Id,
ProductName: p.ProductName,
ProductCode: p.ProductCode,
ProductDescription: p.ProductDescription,
ProductPrice: p.ProductPrice,
ProductGroup: p.ProductGroup,
CreatedAt: p.CreatedAt,
UpdatedAt: p.UpdatedAt,
})
}
resp = &types.GetProductListResp{
Total: productList.Total,
List: list,
}
return resp, nil
}

View File

@@ -0,0 +1,40 @@
package user
import (
"context"
"errors"
"tianyuan-api/apps/user/user"
"tianyuan-api/apps/gateway/internal/svc"
"tianyuan-api/apps/gateway/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type EnterpriseAuthLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewEnterpriseAuthLogic(ctx context.Context, svcCtx *svc.ServiceContext) *EnterpriseAuthLogic {
return &EnterpriseAuthLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *EnterpriseAuthLogic) EnterpriseAuth(req *types.EnterpriseAuthReq) error {
// 从上下文中解析 JWT获取用户ID
userId, ok := l.ctx.Value("userId").(int64)
if !ok {
return errors.New("无法获取 userId")
}
_, err := l.svcCtx.EntRpc.CreateEnterpriseAuth(l.ctx, &user.EnterpriseAuthReq{UserId: userId, EnterpriseName: req.EnterpriseName, EnterpriseContact: req.EnterpriseContact, CreditCode: req.CreditCode, LegalPerson: req.LegalPerson, BusinessLicense: req.BusinessLicense})
if err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,44 @@
package user
import (
"context"
"errors"
"tianyuan-api/apps/sentinel/sentinel"
"tianyuan-api/apps/gateway/internal/svc"
"tianyuan-api/apps/gateway/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type GetSecretInfoLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetSecretInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetSecretInfoLogic {
return &GetSecretInfoLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetSecretInfoLogic) GetSecretInfo() (resp *types.SecretInfoResp, err error) {
// 从上下文中解析 JWT获取用户ID
userId, ok := l.ctx.Value("userId").(int64)
if !ok {
return nil, errors.New("无法获取 userId")
}
secret, err := l.svcCtx.SecretRpc.GetSecretByUserId(l.ctx, &sentinel.GetRecordByIdRequest{
Id: userId,
})
if err != nil {
return nil, err
}
return &types.SecretInfoResp{
AccessId: secret.SecretId,
AccessKey: secret.AesKey,
}, nil
}

View File

@@ -0,0 +1,48 @@
package user
import (
"context"
"errors"
"tianyuan-api/apps/gateway/internal/svc"
"tianyuan-api/apps/gateway/internal/types"
"tianyuan-api/apps/user/user"
"github.com/zeromicro/go-zero/core/logx"
)
type GetUserInfoLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetUserInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUserInfoLogic {
return &GetUserInfoLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetUserInfoLogic) GetUserInfo() (resp *types.UserInfoResp, err error) {
// 从上下文中解析 JWT获取用户ID
userId, ok := l.ctx.Value("userId").(int64)
if !ok {
return nil, errors.New("无法获取 userId")
}
info, err := l.svcCtx.UserRpc.UserInfo(l.ctx, &user.UserInfoReq{UserId: userId})
if err != nil {
return nil, err
}
// 如果查到了企业信息,连带企业信息一起返回
return &types.UserInfoResp{
Username: info.Username,
Phone: info.Phone,
EnterpriseAuthStatus: info.EnterpriseAuthStatus,
EnterpriseName: info.EnterpriseName,
CreditCode: info.CreditCode,
LegalPerson: info.LegalPerson,
}, nil
}

View File

@@ -0,0 +1,249 @@
package user
import (
"context"
"crypto/rand"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"github.com/qiniu/go-sdk/v7/storagev2/credentials"
"github.com/qiniu/go-sdk/v7/storagev2/http_client"
"github.com/qiniu/go-sdk/v7/storagev2/uploader"
"tianyuan-api/apps/gateway/internal/svc"
"tianyuan-api/apps/gateway/internal/types"
"io"
"math/big"
"net/http"
"net/url"
"os"
"strings"
"time"
"github.com/zeromicro/go-zero/core/logx"
)
type UploadBusinessLicenseLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewUploadBusinessLicenseLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UploadBusinessLicenseLogic {
return &UploadBusinessLicenseLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *UploadBusinessLicenseLogic) UploadBusinessLicense(r *http.Request) (resp *types.UploadBusinessLicenseResp, err error) {
// 1. 解析文件上传表单,限制文件大小
err = r.ParseMultipartForm(4 << 20) // 限制最大文件大小为4MB
if err != nil {
return nil, errors.New("图片不能超过4MB")
}
// 2. 获取上传的文件
file, handler, err := r.FormFile("file")
if err != nil {
return nil, err
}
defer file.Close()
// 3. 创建临时文件保存上传的内容
tempFile, err := os.CreateTemp("", "upload-*.jpg")
if err != nil {
return nil, fmt.Errorf("创建临时文件失败: %v", err)
}
defer tempFile.Close()
// 4. 将文件内容保存到临时文件
_, err = io.Copy(tempFile, file)
if err != nil {
return nil, fmt.Errorf("保存文件失败: %v", err)
}
// 5. 调用百度智能云进行营业执照识别
tempFilePath := tempFile.Name()
fileBytes, err := os.ReadFile(tempFilePath)
if err != nil {
return nil, fmt.Errorf("读取临时文件失败: %v", err)
}
licenseInfo, err := l.RecognizeBusinessLicense(fileBytes)
if err != nil {
return nil, fmt.Errorf("营业执照识别失败: %v", err)
}
// 6. 生成新的文件名
newFileName := l.GenerateFileName("business_license_", handler.Filename)
// 7. 确认是营业执照后,将图片上传到七牛云
imageUrl, err := l.UploadToQiniu(tempFilePath, newFileName)
if err != nil {
return nil, fmt.Errorf("上传图片到七牛云失败: %v", err)
}
// 8. 返回百度智能云的识别信息和图片URL给前端
return &types.UploadBusinessLicenseResp{
Url: imageUrl,
EnterpriseName: licenseInfo["company_name"],
CreditCode: licenseInfo["credit_code"],
LegalPerson: licenseInfo["legal_person"],
}, nil
}
// 百度智能云营业执照识别
func (l *UploadBusinessLicenseLogic) RecognizeBusinessLicense(fileBytes []byte) (map[string]string, error) {
// 获取百度智能云Access Token
accessToken := l.GetAccessToken()
if accessToken == "" {
return nil, errors.New("获取百度智能云Access Token失败")
}
// 调用百度智能云营业执照识别接口
baiduUrl := "https://aip.baidubce.com/rest/2.0/ocr/v1/business_license?access_token=" + accessToken
// 将图片文件进行Base64编码
fileBase64 := base64.StdEncoding.EncodeToString(fileBytes)
fileBase64UrlEncoded := url.QueryEscape(fileBase64)
// 准备POST请求的Payload
payload := strings.NewReader(fmt.Sprintf("image=%s", fileBase64UrlEncoded))
req, err := http.NewRequest("POST", baiduUrl, payload)
if err != nil {
return nil, err
}
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
client := &http.Client{}
res, err := client.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
body, err := io.ReadAll(res.Body)
if err != nil {
return nil, err
}
// 解析响应体
var result map[string]interface{}
if err := json.Unmarshal(body, &result); err != nil {
return nil, fmt.Errorf("解析响应失败: %v", err)
}
// 检查是否有错误码
if _, exists := result["error_code"]; exists {
return nil, fmt.Errorf("图片解析失败,请上传清晰正确的图片")
}
// 成功,提取所需的字段
wordsResult := result["words_result"].(map[string]interface{})
companyName := wordsResult["单位名称"].(map[string]interface{})["words"].(string)
socialCreditCode := wordsResult["社会信用代码"].(map[string]interface{})["words"].(string)
legalPerson := wordsResult["法人"].(map[string]interface{})["words"].(string)
// 返回提取的信息
return map[string]string{
"company_name": companyName,
"credit_code": socialCreditCode,
"legal_person": legalPerson,
}, nil
}
// 获取百度智能云Access Token
func (l *UploadBusinessLicenseLogic) GetAccessToken() string {
apiKey := l.svcCtx.Config.Baidu.ApiKey
secretKey := l.svcCtx.Config.Baidu.SecretKey
baiduUrl := "https://aip.baidubce.com/oauth/2.0/token"
postData := fmt.Sprintf("grant_type=client_credentials&client_id=%s&client_secret=%s", apiKey, secretKey)
resp, err := http.Post(baiduUrl, "application/x-www-form-urlencoded", strings.NewReader(postData))
if err != nil {
return ""
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return ""
}
accessTokenObj := map[string]interface{}{}
_ = json.Unmarshal(body, &accessTokenObj)
return accessTokenObj["access_token"].(string)
}
// 七牛云上传图片
func (l *UploadBusinessLicenseLogic) UploadToQiniu(localFilePath string, fileName string) (string, error) {
// 从配置中获取七牛云的AccessKey和SecretKey
accessKey := l.svcCtx.Config.Qiniu.AccessKey
secretKey := l.svcCtx.Config.Qiniu.SecretKey
bucket := l.svcCtx.Config.Qiniu.Bucket
domain := l.svcCtx.Config.Qiniu.Domain
// 1. 构建上传凭证
mac := credentials.NewCredentials(accessKey, secretKey)
// 2. 构建上传管理器
options := uploader.UploadManagerOptions{
Options: http_client.Options{
Credentials: mac, // 这里传入认证信息
},
}
uploadManager := uploader.NewUploadManager(&options)
objectOptions := &uploader.ObjectOptions{
BucketName: bucket,
ObjectName: &fileName,
FileName: fileName,
}
// 3. 执行文件上传
err := uploadManager.UploadFile(context.Background(), localFilePath, objectOptions, nil)
if err != nil {
return "", err
}
// 返回文件的URL地址
fileUrl := fmt.Sprintf("%s/%s", domain, fileName)
return fileUrl, nil
}
// 生成新的文件名,包含前缀、时间戳和随机数
func (l *UploadBusinessLicenseLogic) GenerateFileName(prefix, originalFileName string) string {
timestamp := time.Now().Format("20060102150405") // 生成时间戳
randomNumber := l.GenerateRandomNumber(4) // 生成4位随机数
fileExtension := l.GetFileExtension(originalFileName) // 获取原文件扩展名
// 返回生成的文件名
return fmt.Sprintf("%s%s_%s%s", prefix, timestamp, randomNumber, fileExtension)
}
// 生成指定长度的随机数
func (l *UploadBusinessLicenseLogic) GenerateRandomNumber(length int) string {
var digits = "0123456789"
result := make([]byte, length)
for i := range result {
n, _ := rand.Int(rand.Reader, big.NewInt(int64(len(digits))))
result[i] = digits[n.Int64()]
}
return string(result)
}
// 获取文件扩展名
func (l *UploadBusinessLicenseLogic) GetFileExtension(fileName string) string {
if len(fileName) > 0 {
for i := len(fileName) - 1; i >= 0 && fileName[i] != '.'; i-- {
if i == 0 {
return "" // 无扩展名
}
}
return fileName[strings.LastIndex(fileName, "."):]
}
return ""
}

View File

@@ -0,0 +1,41 @@
package userProduct
import (
"context"
"errors"
"tianyuan-api/apps/sentinel/client/userproduct"
"tianyuan-api/apps/gateway/internal/svc"
"tianyuan-api/apps/gateway/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type AddUserProductLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAddUserProductLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AddUserProductLogic {
return &AddUserProductLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AddUserProductLogic) AddUserProduct(req *types.AddUserProductReq) error {
userId, ok := l.ctx.Value("userId").(int64)
if !ok {
return errors.New("无法获取 userId")
}
_, err := l.svcCtx.UserProductRpc.CreateUserProduct(l.ctx, &userproduct.CreateUserProductRequest{
UserId: userId,
ProductId: req.ProductId,
})
if err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,40 @@
package userProduct
import (
"context"
"errors"
"tianyuan-api/apps/gateway/internal/svc"
"tianyuan-api/apps/gateway/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type DeleteUserProductLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewDeleteUserProductLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteUserProductLogic {
return &DeleteUserProductLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *DeleteUserProductLogic) DeleteUserProduct(req *types.DeleteUserProductReq) error {
return errors.New("not implements")
// 未完善应加上判断是否该用户订阅后删除id也不是这个id暂不开放开放也没用
//_, ok := l.ctx.Value("userId").(int64)
//if !ok {
// return errors.New("无法获取 userId")
//}
//_, err := l.svcCtx.UserProductRpc.DeleteUserProduct(l.ctx, &userproduct.DeleteUserProductRequest{
// Id: req.Id,
//})
//if err != nil {
// return err
//}
//return nil
}

View File

@@ -0,0 +1,60 @@
package userProduct
import (
"context"
"errors"
"tianyuan-api/apps/sentinel/client/userproduct"
"tianyuan-api/apps/gateway/internal/svc"
"tianyuan-api/apps/gateway/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type GetUserProductListLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetUserProductListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUserProductListLogic {
return &GetUserProductListLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetUserProductListLogic) GetUserProductList(req *types.GetUserProductListReq) (resp *types.GetUserProductListResp, err error) {
userId, ok := l.ctx.Value("userId").(int64)
if !ok {
return nil, errors.New("无法获取 userId")
}
userProductPageListResp, err := l.svcCtx.UserProductRpc.GetUserProductPageList(l.ctx, &userproduct.UserProuctPageListRequest{
UserId: userId,
Page: req.Page,
PageSize: req.PageSize,
})
if err != nil {
return nil, err
}
var list []types.UserProductItem
for _, up := range userProductPageListResp.UserProducts {
list = append(list, types.UserProductItem{
Id: up.Id,
ProductId: up.ProductId,
ProductName: up.ProductName,
ProductPrice: up.ProductPrice,
ProductGroup: up.ProductGroup,
ProductCode: up.ProductCode,
ProductDescription: up.ProductDescription,
CreatedAt: up.CreatedAt,
UpdatedAt: up.UpdatedAt,
})
}
resp = &types.GetUserProductListResp{
Total: userProductPageListResp.Total,
List: list,
}
return resp, nil
}

View File

@@ -0,0 +1,46 @@
package whitelistr
import (
"context"
"errors"
"tianyuan-api/apps/gateway/internal/validator"
"tianyuan-api/apps/sentinel/client/whitelist"
"tianyuan-api/apps/gateway/internal/svc"
"tianyuan-api/apps/gateway/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type AddWhitelistLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAddWhitelistLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AddWhitelistLogic {
return &AddWhitelistLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AddWhitelistLogic) AddWhitelist(req *types.AddWhitelistReq) error {
userId, ok := l.ctx.Value("userId").(int64)
if !ok {
return errors.New("无法获取 userId")
}
isIp := validator.IsValidIPAddress(req.Ip)
if !isIp {
return errors.New("请输入正确的IP地址")
}
_, err := l.svcCtx.WhitelistRpc.CreateWhitelist(l.ctx, &whitelist.CreateWhitelistRequest{
UserId: userId,
WhitelistIp: req.Ip,
})
if err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,35 @@
package whitelistr
import (
"context"
"tianyuan-api/apps/sentinel/client/whitelist"
"tianyuan-api/apps/gateway/internal/svc"
"tianyuan-api/apps/gateway/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type DeleteWhitelistLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewDeleteWhitelistLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteWhitelistLogic {
return &DeleteWhitelistLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *DeleteWhitelistLogic) DeleteWhitelist(req *types.DeleteWhitelistReq) error {
_, err := l.svcCtx.WhitelistRpc.DeleteWhitelist(l.ctx, &whitelist.DeleteWhitelistRequest{
Id: req.Id,
})
if err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,55 @@
package whitelistr
import (
"context"
"errors"
"tianyuan-api/apps/sentinel/client/whitelist"
"tianyuan-api/apps/gateway/internal/svc"
"tianyuan-api/apps/gateway/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type GetWhitelistListLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetWhitelistListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetWhitelistListLogic {
return &GetWhitelistListLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetWhitelistListLogic) GetWhitelistList(req *types.GetWhitelistListReq) (resp *types.GetWhitelistListResp, err error) {
userId, ok := l.ctx.Value("userId").(int64)
if !ok {
return nil, errors.New("无法获取 userId")
}
whitelistPageResp, err := l.svcCtx.WhitelistRpc.GetWhitePageList(l.ctx, &whitelist.WhitePageListRequest{
UserId: userId,
Page: req.Page,
PageSize: req.PageSize,
})
if err != nil {
return nil, err
}
var list []types.WhitelistItem
for _, up := range whitelistPageResp.Whitelists {
list = append(list, types.WhitelistItem{
Id: up.Id,
WhitelistIp: up.WhitelistIp,
CreatedAt: up.CreatedAt,
UpdatedAt: up.UpdatedAt,
})
}
resp = &types.GetWhitelistListResp{
Total: whitelistPageResp.Total,
List: list,
}
return resp, nil
}

View File

@@ -0,0 +1,54 @@
package middleware
import (
"context"
"errors"
"github.com/zeromicro/go-zero/core/logx"
xhttp "github.com/zeromicro/x/http"
"tianyuan-api/apps/gateway/internal/config"
jwtx "tianyuan-api/pkg/jwt"
"net/http"
"time"
)
type AuthInterceptorMiddleware struct {
Config config.Config
}
func NewAuthInterceptorMiddleware(c config.Config) *AuthInterceptorMiddleware {
return &AuthInterceptorMiddleware{
Config: c,
}
}
func (m *AuthInterceptorMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// 从 Cookie 中获取 JWT
cookie, err := r.Cookie("Authorization")
if err != nil {
xhttp.JsonBaseResponseCtx(r.Context(), w, errors.New("用户未登录"))
return
}
tokenStr := cookie.Value
// 验证并解析 JWT
userId, err := jwtx.ParseJwtToken(tokenStr, m.Config.AuthJWT.AccessSecret)
if err != nil {
// 设置过期的 Cookie 来删除无效的 Token
http.SetCookie(w, &http.Cookie{
Name: "Authorization",
Value: "",
Path: "/",
HttpOnly: true,
Expires: time.Unix(0, 0), // 过期时间设置为过去
})
logx.Error("Invalid JWT: ", err)
xhttp.JsonBaseResponseCtx(r.Context(), w, errors.New("未经授权的访问"))
return
}
// 将 userId 存入 context供后续逻辑使用
ctx := context.WithValue(r.Context(), "userId", userId)
next(w, r.WithContext(ctx))
}
}

View File

@@ -0,0 +1,39 @@
package middleware
import (
"errors"
"github.com/zeromicro/go-zero/core/logx"
xhttp "github.com/zeromicro/x/http"
"tianyuan-api/apps/user/user"
"net/http"
)
type EntAuthInterceptorMiddleware struct {
UserRpc user.UserClient
}
func NewEntAuthInterceptorMiddleware(userRpc user.UserClient) *EntAuthInterceptorMiddleware {
return &EntAuthInterceptorMiddleware{
UserRpc: userRpc,
}
}
func (m *EntAuthInterceptorMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
userId, ok := r.Context().Value("userId").(int64)
if !ok {
xhttp.JsonBaseResponseCtx(r.Context(), w, errors.New("无法获取 userId"))
}
status, err := m.UserRpc.GetEnterpriseAuthStatus(r.Context(), &user.GetEnterpriseAuthStatusReq{UserId: userId})
if err != nil {
logx.Error("校验认证状态错误: %v", err)
xhttp.JsonBaseResponseCtx(r.Context(), w, errors.New("系统错误"))
return
}
if !status.IsAuth {
xhttp.JsonBaseResponseCtx(r.Context(), w, errors.New("请先通过企业认证"))
return
}
next(w, r)
}
}

View File

@@ -0,0 +1,57 @@
package svc
import (
"github.com/zeromicro/go-zero/core/stores/redis"
"github.com/zeromicro/go-zero/rest"
"github.com/zeromicro/go-zero/zrpc"
"tianyuan-api/apps/gateway/internal/config"
"tianyuan-api/apps/gateway/internal/middleware"
"tianyuan-api/apps/sentinel/sentinel"
"tianyuan-api/apps/user/user"
)
type ServiceContext struct {
Config config.Config
AuthInterceptor rest.Middleware
EntAuthInterceptor rest.Middleware
Redis *redis.Redis
AuthRpc user.AuthClient
EntRpc user.EnterpriseClient
UserRpc user.UserClient
ProductRpc sentinel.ProductClient
UserProductRpc sentinel.UserProductClient
WhitelistRpc sentinel.WhitelistClient
SecretRpc sentinel.SecretClient
}
func NewServiceContext(c config.Config) *ServiceContext {
redisConf := redis.RedisConf{
Host: c.CacheRedis[0].Host,
Pass: c.CacheRedis[0].Pass,
Type: c.CacheRedis[0].Type, // Redis 节点类型,如 "node"
}
// 使用 MustNewRedis 来初始化 Redis 客户端
rds := redis.MustNewRedis(redisConf)
authRpc := user.NewAuthClient(zrpc.MustNewClient(c.UserRpc).Conn())
entRpc := user.NewEnterpriseClient(zrpc.MustNewClient(c.UserRpc).Conn())
userRpc := user.NewUserClient(zrpc.MustNewClient(c.UserRpc).Conn())
productRpc := sentinel.NewProductClient(zrpc.MustNewClient(c.SentinelRpc).Conn())
userProductRpc := sentinel.NewUserProductClient(zrpc.MustNewClient(c.SentinelRpc).Conn())
whitelistRpc := sentinel.NewWhitelistClient(zrpc.MustNewClient(c.SentinelRpc).Conn())
secretRpc := sentinel.NewSecretClient(zrpc.MustNewClient(c.SentinelRpc).Conn())
return &ServiceContext{
Config: c,
AuthInterceptor: middleware.NewAuthInterceptorMiddleware(c).Handle,
EntAuthInterceptor: middleware.NewEntAuthInterceptorMiddleware(userRpc).Handle,
Redis: rds, // 单独使用的 Redis 客户端
AuthRpc: authRpc,
EntRpc: entRpc,
UserRpc: userRpc,
ProductRpc: productRpc,
UserProductRpc: userProductRpc,
WhitelistRpc: whitelistRpc,
SecretRpc: secretRpc,
}
}

View File

@@ -0,0 +1,145 @@
// Code generated by goctl. DO NOT EDIT.
// goctl 1.7.2
package types
type AddUserProductReq struct {
ProductId int64 `json:"productId"`
}
type AddWhitelistReq struct {
Ip string `json:"ip"`
}
type DeleteUserProductReq struct {
Id int64 `json:"id"`
}
type DeleteWhitelistReq struct {
Id int64 `json:"id"`
}
type GetProductByIdReq struct {
ProductId int64 `path:"productId"`
}
type GetProductByIdResp struct {
ProductItem
}
type GetProductListReq struct {
Page int64 `form:"page"`
PageSize int64 `form:"pageSize"`
}
type GetProductListResp struct {
Total int64 `json:"total"`
List []ProductItem `json:"list"`
}
type GetUserProductListReq struct {
Page int64 `form:"page"`
PageSize int64 `form:"pageSize"`
}
type GetUserProductListResp struct {
Total int64 `json:"total"`
List []UserProductItem `json:"list"`
}
type GetVerifyCodeReq struct {
Phone string `json:"phone"`
ActionType string `json:"actionType"`
}
type GetWhitelistListReq struct {
Page int64 `form:"page"`
PageSize int64 `form:"pageSize"`
}
type GetWhitelistListResp struct {
Total int64 `json:"total"`
List []WhitelistItem `json:"list"`
}
type LoginReq struct {
Username string `json:"username"`
Password string `json:"password"`
}
type ProductItem struct {
ProductId int64 `json:"productId"`
ProductName string `json:"productName"`
ProductCode string `json:"productCode"`
ProductDescription string `json:"productDescription"`
ProductContent string `json:"productContent"`
ProductGroup string `json:"productGroup"`
ProductPrice float64 `json:"productPrice"`
CreatedAt string `json:"createdAt"`
UpdatedAt string `json:"updatedAt"`
}
type RegisterReq struct {
Username string `json:"username"`
Password string `json:"password"`
ConfirmPassword string `json:"confirmPassword"`
Phone string `json:"phone"`
Code string `json:"code"`
}
type UploadBusinessLicenseResp struct {
Url string `json:"url"`
EnterpriseName string `json:"enterpriseName"`
CreditCode string `json:"creditCode"`
LegalPerson string `json:"legalPerson"`
}
type UserInfoResp struct {
Username string `json:"username"`
Phone string `json:"phone"`
EnterpriseAuthStatus string `json:"enterpriseAuthStatus"`
EnterpriseName string `json:"enterpriseName"`
CreditCode string `json:"creditCode"`
LegalPerson string `json:"legalPerson"`
}
type UserProductItem struct {
Id int64 `json:"id"` // 用户产品ID
ProductId int64 `json:"productId"` // 产品ID
ProductName string `json:"productName"` // 产品名称
ProductCode string `json:"productCode"` // 产品编号
ProductDescription string `json:"productDescription"` // 产品简介
ProductGroup string `json:"productGroup"` // 产品分类
ProductPrice float64 `json:"productPrice"` // 产品价格
CreatedAt string `json:"createdAt"` // 创建时间
UpdatedAt string `json:"updatedAt"` // 更新时间
}
type WhitelistItem struct {
Id int64 `json:"id"` // 用户产品ID
WhitelistIp string `json:"whiteIp"` // 产品名称
CreatedAt string `json:"createdAt"` // 创建时间
UpdatedAt string `json:"updatedAt"` // 更新时间
}
type EnterpriseAuthReq struct {
EnterpriseName string `json:"enterpriseName"`
CreditCode string `json:"creditCode"`
LegalPerson string `json:"legalPerson"`
BusinessLicense string `json:"businessLicense"`
EnterpriseContact string `json:"enterpriseContact"`
}
type HealthResp struct {
Time string `json:"time"`
}
type PhoneLoginReq struct {
Phone string `json:"phone"`
Code string `json:"code"`
}
type SecretInfoResp struct {
AccessId string `json:"accessId"`
AccessKey string `json:"accessKey"`
}

View File

@@ -0,0 +1,20 @@
package types
import (
"tianyuan-api/apps/gateway/internal/validator"
)
func (l *RegisterReq) Validate() error {
if err := validator.ValidateUsername(l.Username); err != nil {
return err
}
if err := validator.ValidatePassword(l.Password); err != nil {
return err
}
if err := validator.ValidatePhoneNumber(l.Phone); err != nil {
return err
}
// 其他逻辑
return nil
}

View File

@@ -0,0 +1,72 @@
// internal/validator/validation.go
package validator
import (
"errors"
"fmt"
"github.com/zeromicro/go-zero/core/stores/redis"
"regexp"
"unicode/utf8"
)
func ValidateUsername(username string) error {
if utf8.RuneCountInString(username) < 2 {
return errors.New("用户名长度不能少于2个中文或4个英文字符")
}
return nil
}
func ValidatePassword(password string) error {
if len(password) < 8 {
return errors.New("密码长度不能少于8位")
}
return nil
}
func ValidatePhoneNumber(phone string) error {
phoneRegex := `^1[3-9]\d{9}$`
re := regexp.MustCompile(phoneRegex)
if !re.MatchString(phone) {
return errors.New("手机号格式不正确")
}
return nil
}
func ValidateVerifyCode(redisClient *redis.Redis, phone, code string) error {
// 从 Redis 获取验证码
savedCode, err := redisClient.Get(phone)
if err != nil {
if errors.Is(err, redis.Nil) {
return errors.New("验证码已过期")
}
return err
}
// 验证码不匹配
if savedCode != code {
return errors.New("验证码不正确")
}
return nil
}
func IsValidIPAddress(ip string) bool {
// 正则表达式:匹配 IPv4 地址格式
var ipRegex = regexp.MustCompile(`^([0-9]{1,3}\.){3}[0-9]{1,3}$`)
// 判断格式是否匹配
if !ipRegex.MatchString(ip) {
return false
}
// 验证每个段是否在 0 到 255 之间
var segments = ipRegex.FindStringSubmatch(ip)
for _, segment := range segments {
var num int
fmt.Sscanf(segment, "%d", &num)
if num < 0 || num > 255 {
return false
}
}
return true
}