gujiheimao před 2 roky
revize
5a1bdd5e3d

+ 72 - 0
configs/config.go

@@ -0,0 +1,72 @@
+package configs
+
+import (
+	"fmt"
+	"github.com/go-redis/redis/v8"
+	_ "github.com/go-sql-driver/mysql"
+	"github.com/spf13/viper"
+	"os"
+	"xorm.io/xorm"
+)
+
+var (
+	Config  *viper.Viper
+	Engine  *xorm.Engine
+	RedisDb *redis.Client
+)
+
+type User struct {
+	Id            int    `xorm:"'id'" json:"id"`
+	Username      string `xorm:"'username'" json:"username"`
+	Password      string `xorm:"'password'" json:"password"`
+	Creation_time int    `xorm:"'creation_time'" json:"creation_Time"`
+	Login_time    int    `xorm:"'login_time'" json:"login_Time"`
+	Status        int    `xorm:"'status'" json:"status"`
+	Role_id       int    `xorm:"'role_id'" json:"role_id"`
+	Phone         string `xorm:"'phone'" json:"phone"`
+}
+type Role struct {
+	Id        int    `xorm:"'id'" json:"id"`
+	Role_name string `xorm:"'role_name'" json:"role_Name"`
+}
+type Role_authority struct {
+	Authority_id int `xorm:"'authority_id'" json:"authority_id"`
+	Role_id      int `xorm:"'role_id'" json:"role_id"`
+}
+type Authority struct {
+	Id             int    `xorm:"'id'" json:"id"`
+	Authority_name string `xorm:"'authority_name'" json:"authority_Name"`
+	Authority_path string `xorm:"'authority_path'" json:"authority_Path"`
+}
+type MysqlData struct {
+	User           `xorm:"extends"`
+	Role           `xorm:"extends"`
+	Role_authority `xorm:"extends"`
+	Authority      `xorm:"extends"`
+}
+
+func ConfigInit() {
+	//获取项目的执行路径
+	path, err := os.Getwd()
+	if err != nil {
+		panic(err)
+	}
+	config := viper.New()
+
+	config.AddConfigPath(path + "\\configs") //设置读取的文件路径
+	config.SetConfigName("config")           //设置读取的文件名
+	config.SetConfigType("yaml")             //设置文件的类型
+	//尝试进行配置读取
+	if err := config.ReadInConfig(); err != nil {
+		panic(err)
+	}
+	fmt.Println(fmt.Sprintf("%s:%s@(%s:%s)/%s?charset=utf8", config.Get("mysql.user"), config.Get("mysql.password"), config.Get("mysql.host"), config.Get("mysql.port"), config.Get("mysql.database")))
+	Engine, err = xorm.NewEngine("mysql", fmt.Sprintf("%s:%s@(%s:%s)/%s?charset=utf8", config.Get("mysql.user"), config.Get("mysql.password"), config.Get("mysql.host"), config.Get("mysql.port"), config.Get("mysql.database")))
+	Engine.Ping() //连接测试
+	RedisDb = redis.NewClient(&redis.Options{
+		Addr:     fmt.Sprintf("%s:%s", config.Get("redis.host"), config.Get("redis.port")), // Redis地址
+		Password: "",                                                                       // Redis密码,如果没有则为空字符串
+		DB:       0,                                                                        // 使用默认DB
+	})
+	Config = config
+}

+ 9 - 0
configs/config.yaml

@@ -0,0 +1,9 @@
+mysql:
+    host: localhost
+    port: "3306"
+    user: root
+    password: "root"
+    database: virtual_mall
+redis:
+    host: localhost
+    port: "6379"

+ 1 - 0
data/dao/GoodsAttributeKeyDao.go

@@ -0,0 +1 @@
+package domain

+ 1 - 0
data/dao/GoodsAttributeValueDao.go

@@ -0,0 +1 @@
+package domain

+ 1 - 0
data/dao/GoodsCategoryDao.go

@@ -0,0 +1 @@
+package domain

+ 23 - 0
data/dao/GoodsDao.go

@@ -0,0 +1,23 @@
+package domain
+
+import (
+	"demo/configs"
+	"demo/data/domain"
+)
+
+func SelectAll() []domain.Goods {
+	var goods domain.Goods
+
+	rows, err := configs.Engine.Table(domain.GetGoodsTableName()).Rows(&goods)
+	if err != nil {
+		print(err)
+	}
+	defer rows.Close()
+	var goodsDatas []domain.Goods = make([]domain.Goods, 0)
+	for rows.Next() {
+		rows.Scan(&goods)
+		goodsDatas = append(goodsDatas, goods)
+	}
+	println("获取goods表,全部数据,运行结果:", rows)
+	return goodsDatas
+}

+ 1 - 0
data/dao/GoodsSpecsDao.go

@@ -0,0 +1 @@
+package domain

+ 20 - 0
data/domain/Goods.go

@@ -0,0 +1,20 @@
+package domain
+
+type Goods struct {
+	Id            int64   `xorm:"id" json:"id"`
+	CategoryId    int64   `xorm:"category_id" json:"category_id"`
+	GoodsName     string  `xorm:"goods_name" json:"goods_name"`
+	GoodsDescribe string  `xorm:"goods_describe" json:"goods_describe"`
+	AttributeList string  `xorm:"attribute_list" json:"attribute_list"`
+	GoodsDetails  string  `xorm:"goods_details" json:"goods_details"`
+	GoodsPrice    float32 `xorm:"goods_price" json:"goods_price"`
+	GoodsCount    int32   `xorm:"goods_count" json:"goods_count"`
+}
+
+func (receiver Goods) GetTableName() string {
+	return "goods"
+}
+
+func GetGoodsTableName() string {
+	return "goods"
+}

+ 16 - 0
data/domain/GoodsAttributeKey.go

@@ -0,0 +1,16 @@
+package domain
+
+type GoodsAttributeKey struct {
+	Id            int64  `xorm:"id" json:"id"`
+	CategoryId    int64  `xorm:"category_id" json:"category_id"`
+	AttributeName string `xorm:"attribute_name" json:"attribute_name"`
+	CreateBy      string `xorm:"create_by" json:"create_by"`
+	CreateTime    int    `xorm:"create_time" json:"create_time"`
+}
+
+func (GoodsAttributeKey) TableName() string {
+	return "goods_attribute_key"
+}
+func GetGoodsAttributeTableName() string {
+	return "goods_attribute_key"
+}

+ 16 - 0
data/domain/GoodsAttributeValue.go

@@ -0,0 +1,16 @@
+package domain
+
+type GoodsAttributeValue struct {
+	Id             int64  `xorm:"id" json:"id"`
+	AttributeId    int64  `xorm:"attribute_id" json:"attribute_id"`
+	AttributeValue string `xorm:"attribute_value" json:"attribute_value"`
+	CreateBy       string `xorm:"create_by" json:"create_by"`
+	CreateTime     int    `xorm:"create_time" json:"create_time"`
+}
+
+func (GoodsAttributeValue) TableName() string {
+	return "goods_attribute_value"
+}
+func GetGoodsAttributeValueTableName() string {
+	return "goods_attribute_value"
+}

+ 16 - 0
data/domain/GoodsCategory.go

@@ -0,0 +1,16 @@
+package domain
+
+type GoodsCategory struct {
+	Id           int64  `xorm:"id" json:"id"`
+	ParentId     int64  `xorm:"parent_id" json:"parent_id"`
+	CategoryName string `xorm:"category_name" json:"category_name"`
+	CreateBy     string `xorm:"create_by" json:"create_by"`
+	CreateTime   int    `xorm:"create_time" json:"create_time"`
+}
+
+func (GoodsCategory) TableName() string {
+	return "goods_category"
+}
+func GetGoodsCategoryTableName() string {
+	return "goods_category"
+}

+ 17 - 0
data/domain/GoodsSpecs.go

@@ -0,0 +1,17 @@
+package domain
+
+type GoosSpecs struct {
+	Id         int64   `xorm:"id" json:"id"`
+	GoodsId    int64   `xorm:"goods_id" json:"goods_id"`
+	GoodsSpecs string  `xorm:"goods_specs" json:"goods_specs"`
+	GoodsStock int32   `xorm:"goods_stock" json:"goods_stock"`
+	GoodsPrice float32 `xorm:"goods_price" json:"goods_price"`
+}
+
+func (GoosSpecs) TableName() string {
+	return "goos_specs"
+}
+
+func GetGoodsSpecsTableName() string {
+	return "goos_specs"
+}

+ 5 - 0
data/domain/domain.go

@@ -0,0 +1,5 @@
+package domain
+
+type name interface {
+	GetTableName() string
+}

+ 67 - 0
go.mod

@@ -0,0 +1,67 @@
+module demo
+
+go 1.19
+
+require (
+	github.com/dgrijalva/jwt-go v3.2.0+incompatible
+	github.com/gin-gonic/gin v1.9.1
+	github.com/go-redis/redis/v8 v8.11.5
+	github.com/go-sql-driver/mysql v1.8.0
+	github.com/mojocn/base64Captcha v1.3.6
+	github.com/spf13/cast v1.6.0
+	github.com/spf13/viper v1.18.2
+	github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.887
+	github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ses v1.0.887
+	github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/sms v1.0.885
+	xorm.io/xorm v1.3.9
+)
+
+require (
+	filippo.io/edwards25519 v1.1.0 // indirect
+	github.com/bytedance/sonic v1.11.3 // indirect
+	github.com/cespare/xxhash/v2 v2.2.0 // indirect
+	github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
+	github.com/chenzhuoyu/iasm v0.9.1 // indirect
+	github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
+	github.com/fsnotify/fsnotify v1.7.0 // indirect
+	github.com/gabriel-vasile/mimetype v1.4.3 // indirect
+	github.com/gin-contrib/sse v0.1.0 // indirect
+	github.com/go-playground/locales v0.14.1 // indirect
+	github.com/go-playground/universal-translator v0.18.1 // indirect
+	github.com/go-playground/validator/v10 v10.19.0 // indirect
+	github.com/goccy/go-json v0.10.2 // indirect
+	github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
+	github.com/golang/snappy v0.0.4 // indirect
+	github.com/hashicorp/hcl v1.0.0 // indirect
+	github.com/json-iterator/go v1.1.12 // indirect
+	github.com/klauspost/cpuid/v2 v2.2.7 // indirect
+	github.com/leodido/go-urn v1.4.0 // indirect
+	github.com/magiconair/properties v1.8.7 // indirect
+	github.com/mattn/go-isatty v0.0.20 // indirect
+	github.com/mitchellh/mapstructure v1.5.0 // indirect
+	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+	github.com/modern-go/reflect2 v1.0.2 // indirect
+	github.com/pelletier/go-toml/v2 v2.2.0 // indirect
+	github.com/sagikazarmark/locafero v0.4.0 // indirect
+	github.com/sagikazarmark/slog-shim v0.1.0 // indirect
+	github.com/sourcegraph/conc v0.3.0 // indirect
+	github.com/spf13/afero v1.11.0 // indirect
+	github.com/spf13/pflag v1.0.5 // indirect
+	github.com/subosito/gotenv v1.6.0 // indirect
+	github.com/syndtr/goleveldb v1.0.0 // indirect
+	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
+	github.com/ugorji/go/codec v1.2.12 // indirect
+	go.uber.org/atomic v1.9.0 // indirect
+	go.uber.org/multierr v1.9.0 // indirect
+	golang.org/x/arch v0.7.0 // indirect
+	golang.org/x/crypto v0.21.0 // indirect
+	golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
+	golang.org/x/image v0.15.0 // indirect
+	golang.org/x/net v0.22.0 // indirect
+	golang.org/x/sys v0.18.0 // indirect
+	golang.org/x/text v0.14.0 // indirect
+	google.golang.org/protobuf v1.33.0 // indirect
+	gopkg.in/ini.v1 v1.67.0 // indirect
+	gopkg.in/yaml.v3 v3.0.1 // indirect
+	xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978 // indirect
+)

+ 22 - 0
main.go

@@ -0,0 +1,22 @@
+package main
+
+import (
+	"demo/configs"
+	"demo/router"
+	"demo/share"
+	"demo/user"
+	"github.com/gin-gonic/gin"
+)
+
+func main() {
+	configs.ConfigInit()
+	Router := gin.Default()
+
+	apiGroup := Router.Group("/api")
+	user.UserRouth(apiGroup)
+	router.GoodsRouth(apiGroup)
+	apiGroup.GET("/ping", share.JwtMiddleware(), func(context *gin.Context) {
+		context.String(200, "pong")
+	})
+	Router.Run()
+}

+ 24 - 0
router/GoodsRouter.go

@@ -0,0 +1,24 @@
+package router
+
+import (
+	domain "demo/data/dao"
+	"github.com/gin-gonic/gin"
+)
+
+func GoodsRouth(engine *gin.RouterGroup) {
+	user := engine.Group("/goods")
+	{
+		user.GET("/all", getAllGoods)
+		//user.POST("sendSms", SendVerificationCode)
+		//
+		//user.GET("/captcha", VerificationCode)
+	}
+}
+
+func getAllGoods(c *gin.Context) {
+	c.JSON(200, gin.H{
+		"code": 200,
+		"msg":  "success",
+		"data": domain.SelectAll(),
+	})
+}

+ 121 - 0
share/json.go

@@ -0,0 +1,121 @@
+package share
+
+import (
+	"crypto/rand"
+	"demo/configs"
+	"errors"
+	"fmt"
+	"github.com/dgrijalva/jwt-go"
+	"github.com/gin-gonic/gin"
+	"math/big"
+	"time"
+)
+
+const (
+	SECRETKEY = "243223ffslsfsldfl412fdsfsdf" //私钥
+)
+
+type CustomClaims struct {
+	UserId int64
+	jwt.StandardClaims
+}
+
+func GetJsonAnyParam(c *gin.Context) func(param string) (interface{}, error) {
+	jsonData := map[string]interface{}{}
+	err := c.BindJSON(&jsonData)
+
+	return func(param string) (interface{}, error) {
+		if err != nil {
+			return nil, err
+		}
+		value, err := func() (interface{}, error) {
+
+			i, exists := jsonData[param]
+			if !exists {
+				return nil, errors.New("缺少" + param + "字段")
+			}
+
+			return i, nil
+		}()
+		if err != nil {
+			return nil, err
+		}
+		return value, err
+	}
+}
+
+func GenerateToken(userId string) (string, error) {
+	maxAge := 60 * 60 * 24
+	// Create the Claims
+	claims := &jwt.StandardClaims{
+		ExpiresAt: time.Now().Add(time.Duration(maxAge) * time.Second).Unix(), // 过期时间,必须设置,
+		Issuer:    userId,                                                     // 非必须,也可以填充用户名,
+	}
+
+	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
+	tokenString, err := token.SignedString([]byte(SECRETKEY))
+	if err != nil {
+		fmt.Println(err)
+	}
+	return tokenString, err
+}
+func ParseToken(tokenString string) (jwt.MapClaims, error) {
+	token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
+		// Don't forget to validate the alg is what you expect:
+		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
+			return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
+		}
+
+		// hmacSampleSecret is a []byte containing your secret, e.g. []byte("my_secret_key")
+		return []byte(SECRETKEY), nil
+	})
+	if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
+		return claims, nil
+	} else {
+		return nil, err
+	}
+}
+func JwtMiddleware() gin.HandlerFunc {
+	return func(c *gin.Context) {
+		tokenString := c.GetHeader("auth-sign")
+		if tokenString == "" {
+			c.JSON(401, gin.H{"message": "缺少token"})
+			c.Abort()
+			return
+		}
+
+		token, err := ParseToken(tokenString)
+
+		if err != nil {
+			c.JSON(401, gin.H{"message": "无效令牌"})
+			c.Abort()
+			return
+		}
+		users := make([]configs.MysqlData, 0)
+		bools, err := configs.Engine.Table("user").Join("INNER", "role", "role.id = user.role_id").Join("INNER", "role_authority", "role_authority.authority_id = role.id").Join("INNER", "authority", "authority.id = role_authority.authority_id").Where("authority.authority_path=?", c.Request.URL.Path).Where("user.id = ?", token["iss"]).Exist(&users)
+		if err == nil {
+			fmt.Println(bools)
+			if bools {
+				c.Next()
+			} else {
+				c.JSON(200, gin.H{"message": "权限不足"})
+				c.Abort()
+				return
+			}
+		}
+
+	}
+}
+func RandomInt(min, max *big.Int) *big.Int {
+	// 读取密码学安全的随机比特
+	byteLen := (max.BitLen() + 7) / 8
+	b := make([]byte, byteLen)
+	rand.Read(b)
+
+	// 将字节转换为大整数
+	r := new(big.Int).SetBytes(b)
+
+	// 需要将生成的大整数范围限制在[min,max]
+	r.Rem(r, new(big.Int).Sub(max, min)).Add(r, min)
+	return r
+}

+ 154 - 0
share/sms.go

@@ -0,0 +1,154 @@
+package share
+
+import (
+	"encoding/json"
+	"fmt"
+	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
+	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors"
+	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
+	ses "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ses/v20201002"
+	sms "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/sms/v20210111" // 引入sms
+)
+
+const (
+	SMTPHost     = "smtp.qcloudmail.com"
+	SMTPPort     = ":465"
+	SMTPUsername = "info@mail.1zz.com"
+	SMTPPassword = "TengxunYun890"
+)
+
+func SendSms(verification_code, phone string) {
+	/* 必要步骤:
+	 * 实例化一个认证对象,入参需要传入腾讯云账户密钥对secretId,secretKey。
+	 * 这里采用的是从环境变量读取的方式,需要在环境变量中先设置这两个值。
+	 * 您也可以直接在代码中写死密钥对,但是小心不要将代码复制、上传或者分享给他人,
+	 * 以免泄露密钥对危及您的财产安全。
+	 * SecretId、SecretKey 查询: https://console.cloud.tencent.com/cam/capi */
+	credential := common.NewCredential(
+		// os.Getenv("TENCENTCLOUD_SECRET_ID"),
+		// os.Getenv("TENCENTCLOUD_SECRET_KEY"),
+		"AKIDZ3Wt8ACBCA1pVGyUr20pV1KQNOHLFwRk",
+		"SxVtM3d6vc5PUKwJwc9YrxpSpxrWw62i",
+	)
+	/* 非必要步骤:
+	 * 实例化一个客户端配置对象,可以指定超时时间等配置 */
+	cpf := profile.NewClientProfile()
+
+	/* SDK默认使用POST方法。
+	 * 如果您一定要使用GET方法,可以在这里设置。GET方法无法处理一些较大的请求 */
+	cpf.HttpProfile.ReqMethod = "POST"
+
+	/* SDK有默认的超时时间,非必要请不要进行调整
+	 * 如有需要请在代码中查阅以获取最新的默认值 */
+	// cpf.HttpProfile.ReqTimeout = 5
+
+	/* 指定接入地域域名,默认就近地域接入域名为 sms.tencentcloudapi.com ,也支持指定地域域名访问,例如广州地域的域名为 sms.ap-guangzhou.tencentcloudapi.com */
+	cpf.HttpProfile.Endpoint = "sms.tencentcloudapi.com"
+
+	/* SDK默认用TC3-HMAC-SHA256进行签名,非必要请不要修改这个字段 */
+	cpf.SignMethod = "HmacSHA1"
+
+	/* 实例化要请求产品(以sms为例)的client对象
+	 * 第二个参数是地域信息,可以直接填写字符串ap-guangzhou,支持的地域列表参考 https://cloud.tencent.com/document/api/382/52071#.E5.9C.B0.E5.9F.9F.E5.88.97.E8.A1.A8 */
+	client, _ := sms.NewClient(credential, "ap-guangzhou", cpf)
+
+	/* 实例化一个请求对象,根据调用的接口和实际情况,可以进一步设置请求参数
+	 * 您可以直接查询SDK源码确定接口有哪些属性可以设置
+	 * 属性可能是基本类型,也可能引用了另一个数据结构
+	 * 推荐使用IDE进行开发,可以方便的跳转查阅各个接口和数据结构的文档说明 */
+	request := sms.NewSendSmsRequest()
+
+	/* 基本类型的设置:
+	 * SDK采用的是指针风格指定参数,即使对于基本类型您也需要用指针来对参数赋值。
+	 * SDK提供对基本类型的指针引用封装函数
+	 * 帮助链接:
+	 * 短信控制台: https://console.cloud.tencent.com/smsv2
+	 * 腾讯云短信小助手: https://cloud.tencent.com/document/product/382/3773#.E6.8A.80.E6.9C.AF.E4.BA.A4.E6.B5.81 */
+
+	/* 短信应用ID: 短信SdkAppId在 [短信控制台] 添加应用后生成的实际SdkAppId,示例如1400006666 */
+	// 应用 ID 可前往 [短信控制台](https://console.cloud.tencent.com/smsv2/app-manage) 查看
+	request.SmsSdkAppId = common.StringPtr("1400896622")
+
+	/* 短信签名内容: 使用 UTF-8 编码,必须填写已审核通过的签名 */
+	// 签名信息可前往 [国内短信](https://console.cloud.tencent.com/smsv2/csms-sign) 或 [国际/港澳台短信](https://console.cloud.tencent.com/smsv2/isms-sign) 的签名管理查看
+	request.SignName = common.StringPtr("易租租网站")
+
+	/* 模板 ID: 必须填写已审核通过的模板 ID */
+	// 模板 ID 可前往 [国内短信](https://console.cloud.tencent.com/smsv2/csms-template) 或 [国际/港澳台短信](https://console.cloud.tencent.com/smsv2/isms-template) 的正文模板管理查看
+	request.TemplateId = common.StringPtr("2107075")
+
+	/* 模板参数: 模板参数的个数需要与 TemplateId 对应模板的变量个数保持一致,若无模板参数,则设置为空*/
+	request.TemplateParamSet = common.StringPtrs([]string{verification_code, "5"})
+
+	/* 下发手机号码,采用 E.164 标准,+[国家或地区码][手机号]
+	 * 示例如:+8613711112222, 其中前面有一个+号 ,86为国家码,13711112222为手机号,最多不要超过200个手机号*/
+	request.PhoneNumberSet = common.StringPtrs([]string{"+86" + phone})
+
+	/* 用户的 session 内容(无需要可忽略): 可以携带用户侧 ID 等上下文信息,server 会原样返回 */
+	request.SessionContext = common.StringPtr("")
+
+	/* 短信码号扩展号(无需要可忽略): 默认未开通,如需开通请联系 [腾讯云短信小助手] */
+	request.ExtendCode = common.StringPtr("")
+
+	/* 国内短信无需填写该项;国际/港澳台短信已申请独立 SenderId 需要填写该字段,默认使用公共 SenderId,无需填写该字段。注:月度使用量达到指定量级可申请独立 SenderId 使用,详情请联系 [腾讯云短信小助手](https://cloud.tencent.com/document/product/382/3773#.E6.8A.80.E6.9C.AF.E4.BA.A4.E6.B5.81)。 */
+	request.SenderId = common.StringPtr("")
+
+	// 通过client对象调用想要访问的接口,需要传入请求对象
+	response, err := client.SendSms(request)
+	// 处理异常
+	if _, ok := err.(*errors.TencentCloudSDKError); ok {
+		fmt.Printf("An API error has returned: %s", err)
+		return
+	}
+	// 非SDK异常,直接失败。实际代码中可以加入其他的处理。
+	if err != nil {
+		panic(err)
+	}
+	b, _ := json.Marshal(response.Response)
+	// 打印返回的json字符串
+	fmt.Printf("%s", b)
+
+	/* 当出现以下错误码时,快速解决方案参考
+	 * [FailedOperation.SignatureIncorrectOrUnapproved](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Afailedoperation.signatureincorrectorunapproved-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F)
+	 * [FailedOperation.TemplateIncorrectOrUnapproved](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Afailedoperation.templateincorrectorunapproved-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F)
+	 * [UnauthorizedOperation.SmsSdkAppIdVerifyFail](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Aunauthorizedoperation.smssdkappidverifyfail-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F)
+	 * [UnsupportedOperation.ContainDomesticAndInternationalPhoneNumber](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Aunsupportedoperation.containdomesticandinternationalphonenumber-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F)
+	 * 更多错误,可咨询[腾讯云助手](https://tccc.qcloud.com/web/im/index.html#/chat?webAppId=8fa15978f85cb41f7e2ea36920cb3ae1&title=Sms)
+	 */
+}
+
+func SendMail(toemail, yzm string) {
+	// 设置邮箱主体
+	credential := common.NewCredential(
+		"AKIDZ3Wt8ACBCA1pVGyUr20pV1KQNOHLFwRk",
+		"SxVtM3d6vc5PUKwJwc9YrxpSpxrWw62i",
+	)
+	// 实例化一个client选项,可选的,没有特殊需求可以跳过
+	cpf := profile.NewClientProfile()
+	cpf.HttpProfile.Endpoint = "ses.tencentcloudapi.com"
+	// 实例化要请求产品的client对象,clientProfile是可选的
+	client, _ := ses.NewClient(credential, "ap-hongkong", cpf)
+
+	// 实例化一个请求对象,每个接口都会对应一个request对象
+	request := ses.NewSendEmailRequest()
+
+	request.FromEmailAddress = common.StringPtr("info@mail.1zz.com")
+	request.Destination = common.StringPtrs([]string{toemail})
+	request.Template = &ses.Template{
+		TemplateID:   common.Uint64Ptr(120852),
+		TemplateData: common.StringPtr(`{"yzm":"` + yzm + `"}`),
+	}
+	request.Subject = common.StringPtr("验证码")
+
+	// 返回的resp是一个SendEmailResponse的实例,与请求对象对应
+	response, err := client.SendEmail(request)
+	if _, ok := err.(*errors.TencentCloudSDKError); ok {
+		fmt.Printf("An API error has returned: %s", err)
+		return
+	}
+	if err != nil {
+		panic(err)
+	}
+	// 输出json格式的字符串回包
+	fmt.Printf("%s", response.ToJsonString())
+}

+ 7 - 0
test/test.http

@@ -0,0 +1,7 @@
+POST http://localhost:8080/api/user/login
+Content-Type: application/json
+
+{
+
+}
+###

+ 128 - 0
user/user.go

@@ -0,0 +1,128 @@
+package user
+
+import (
+	"context"
+	"demo/configs"
+	"demo/share"
+	"fmt"
+	"github.com/gin-gonic/gin"
+	"github.com/mojocn/base64Captcha"
+	"github.com/spf13/cast"
+	"log"
+	"net/http"
+	"regexp"
+	"time"
+)
+
+var ctx = context.Background()
+
+func UserRouth(engine *gin.RouterGroup) {
+	user := engine.Group("/user")
+	{
+		user.POST("/login", login)
+		user.POST("sendSms", SendVerificationCode)
+
+		user.GET("/captcha", VerificationCode)
+	}
+}
+func VerificationCode(c *gin.Context) {
+
+	// 配置
+	driver := base64Captcha.NewDriverDigit(80, 240, 4, 0.5, 80)
+	store := base64Captcha.DefaultMemStore
+	captcha := base64Captcha.NewCaptcha(driver, store)
+
+	// 生成验证码
+	id, b64s, answer, err := captcha.Generate()
+	if err != nil {
+		c.JSON(500, gin.H{"error": err.Error()})
+		return
+	}
+	fmt.Println(answer)
+	err = configs.RedisDb.Set(ctx, "VerificationCode_"+id, answer, 0).Err()
+	if err != nil {
+		fmt.Println(err)
+	}
+	err = configs.RedisDb.Expire(ctx, "VerificationCode_"+id, 60*time.Second).Err()
+	if err != nil {
+		panic(err)
+	}
+	// 返回验证码图片
+	c.JSON(200, gin.H{
+		"message": "获取成功!!!",
+		"expires": time.Now().Add(time.Minute * 5).Unix(),
+		"code":    200,
+		"data":    map[string]string{"id": id, "image": b64s},
+	})
+
+}
+func login(c *gin.Context) {
+	data := share.GetJsonAnyParam(c)
+	var user configs.User
+	var err error
+	username, _ := data("username")
+	password, _ := data("password")
+	// 定义正则表达式
+	regexPattern := `^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`
+
+	// 编译正则表达式
+	reg, err := regexp.Compile(regexPattern)
+	if err != nil {
+		fmt.Println("Error compiling regex:", err)
+		return
+	}
+	matched := reg.MatchString(cast.ToString(username))
+
+	user, err = logins(cast.ToString(username), cast.ToString(password), matched)
+
+	if err == nil {
+		token, err := share.GenerateToken(cast.ToString(user.Id))
+		if err == nil {
+			c.Header("auth-sign", token)
+			c.JSON(http.StatusOK, gin.H{
+				"code":    200,
+				"message": "成功!!!",
+			})
+		} else {
+			c.JSON(http.StatusOK, gin.H{
+				"code":    400,
+				"message": "生成token失败!!!",
+			})
+		}
+	} else {
+		c.JSON(http.StatusOK, gin.H{
+			"code":    401,
+			"message": "用户密码错误!!!",
+		})
+	}
+}
+
+// SendVerificationCode
+func SendVerificationCode(c *gin.Context) {
+	data := share.GetJsonAnyParam(c)
+	var err error
+	username, _ := data("username")
+	code, _ := data("code")
+	codeId, _ := data("codeId")
+	val, err := configs.RedisDb.Get(ctx, cast.ToString(codeId)).Result()
+	if err != nil {
+		log.Fatal(err)
+	}
+	if val != cast.ToString(code) {
+		c.JSON(200, gin.H{"code": 400, "message": "验证码错误!!!"})
+		c.Abort()
+		return
+	}
+	// 定义正则表达式
+	regexPattern := `^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`
+
+	// 编译正则表达式
+	reg, err := regexp.Compile(regexPattern)
+	if err != nil {
+		fmt.Println("Error compiling regex:", err)
+		return
+	}
+	matched := reg.MatchString(cast.ToString(username))
+	sendSms(matched, cast.ToString(username))
+	c.JSON(200, gin.H{"code": 200, "message": "发送成功!!!"})
+}

+ 41 - 0
user/userdao.go

@@ -0,0 +1,41 @@
+package user
+
+import (
+	"demo/configs"
+	"demo/share"
+	"fmt"
+	"math/big"
+	"strconv"
+)
+
+func logins(username, password string, matched bool) (configs.User, error) {
+	user := configs.User{}
+	var err error
+	fmt.Println("username", username, "password", password)
+	if matched {
+		_, err = configs.Engine.Table("user").Where("username = ?", username).Where("password = ?", password).Get(&user)
+	} else {
+		_, err = configs.Engine.Table("user").Where("phone = ?", username).Where("password = ?", password).Get(&user)
+	}
+
+	fmt.Println(err)
+	if err == nil {
+		return user, err
+	}
+	return configs.User{}, err
+}
+
+// 发送验证码
+func sendSms(matched bool, username string) {
+	mins := big.NewInt(100000)
+	maxs := big.NewInt(999999)
+	randomNum := share.RandomInt(mins, maxs)
+	if matched {
+		//bools, err = configs.Engine.Table("user").Where("username = ?", username).Exist(&user)
+
+		share.SendMail(username, strconv.Itoa(int(randomNum.Int64())))
+
+	} else {
+		share.SendSms(strconv.Itoa(int(randomNum.Int64())), username)
+	}
+}