kobe6258 7 місяців тому
батько
коміт
ac7e8da771
41 змінених файлів з 1066 додано та 271 видалено
  1. 14 0
      common/component/cache/redis.go
  2. 45 0
      common/component/config/eta_config.go
  3. 43 24
      common/component/database/db_connector.go
  4. 19 0
      common/component/log/log_plugin.go
  5. 5 5
      common/component/sms/emas_sms_sender.go
  6. 1 1
      common/component/sms/juhe_sms_sender.go
  7. 32 25
      common/component/wechat/wechat_client.go
  8. 2 0
      common/contants/contants.go
  9. 49 3
      common/exception/exc_enums.go
  10. 3 0
      common/utils/client/http_client.go
  11. 77 0
      common/utils/jwt/jwt_utils.go
  12. 6 1
      common/utils/redis/key_generator.go
  13. 4 0
      common/utils/redis/redis_client.go
  14. 1 1
      common/utils/sms/sms_client.go
  15. 6 6
      conf/log/log_config.json
  16. 20 21
      controllers/base_controller.go
  17. 50 37
      controllers/user/auth_controller.go
  18. 2 6
      controllers/user/user_controller.go
  19. 7 1
      domian/sms/emas_sms_service.go
  20. 70 0
      domian/sms/sms_service.go
  21. 56 3
      domian/user/user_serivce.go
  22. BIN
      eta_mini_ht_api.exe~
  23. 1 0
      go.mod
  24. 2 0
      go.sum
  25. 27 0
      job/eta/eta_report_task.go
  26. 11 0
      job/eta/eta_report_task_test.go
  27. 30 0
      job/sms/code_expired_task.go
  28. 15 0
      job/task_service.go
  29. 5 2
      main.go
  30. 71 10
      middleware/auth_middleware.go
  31. 18 0
      models/base_db.go
  32. 49 0
      models/eta/eta_report.go
  33. 7 11
      models/sms/api_mt_zsyyt.go
  34. 0 5
      models/sms/sms_code.go
  35. 80 0
      models/sms/verification_record.go
  36. 0 22
      models/user.go
  37. 40 0
      models/user/template_user.go
  38. 1 1
      routers/commentsRouter.go
  39. 182 0
      service/auth/auth_service.go
  40. 9 51
      swagger/swagger.json
  41. 6 35
      swagger/swagger.yml

+ 14 - 0
common/component/cache/redis.go

@@ -57,6 +57,20 @@ type RedisCache struct {
 	redisTemplate redis.UniversalClient
 }
 
+func (r *RedisCache) GetHSet(key string) map[string]string {
+	return r.GetHSetWithContext(context.Background(), key)
+}
+func (r *RedisCache) GetHSetWithContext(ctx context.Context, key string) map[string]string {
+	value, _ := r.redisTemplate.HGetAll(ctx, key).Result()
+	return value
+}
+func (r *RedisCache) SetHSet(key string, val string, timeout time.Duration) error {
+	return r.SetHSetWithContext(context.Background(), key, val, timeout)
+}
+func (r *RedisCache) SetHSetWithContext(ctx context.Context, key string, val string, timeout time.Duration) error {
+	return r.redisTemplate.HSet(ctx, key, val).Err()
+}
+
 // GetString  获取一个值
 func (r *RedisCache) GetString(key string) string {
 	return r.GetStringWithContext(context.Background(), key)

+ 45 - 0
common/component/config/eta_config.go

@@ -0,0 +1,45 @@
+package config
+
+import (
+	"eta_mini_ht_api/common/contants"
+	"sync"
+)
+
+var (
+	etaOnce   sync.Once
+	etaConfig *ETAConfig
+)
+
+type ETAOpts struct {
+	DBUrl string
+}
+type ETAConfig struct {
+	BaseConfig
+	opts ETAOpts
+}
+
+func (r *ETAConfig) GetDBUrl() string {
+	return r.opts.DBUrl
+}
+
+func (r *ETAConfig) InitConfig() {
+	opts := ETAOpts{
+		DBUrl: r.GetString("database.url"),
+	}
+	r.opts = opts
+}
+func NewETAConfig() Config {
+	if etaConfig == nil {
+		etaOnce.Do(func() {
+			etaConfig = &ETAConfig{
+				BaseConfig: BaseConfig{prefix: contants.ETA},
+				opts:       ETAOpts{},
+			}
+		})
+	}
+	return etaConfig
+}
+
+func init() {
+	Register(contants.ETA, NewETAConfig)
+}

+ 43 - 24
common/component/database/db_connector.go

@@ -13,17 +13,16 @@ import (
 const (
 	MAIN = "main"
 	EMAS = "emas"
+
+	ETA       = "eta"
+	etaDriver = "mysql"
 )
 
 var (
-	db     *gorm.DB
-	dbOnce sync.Once
+	dbOnce    sync.Once
+	etaDBOnce sync.Once
 )
 
-func GetInstance() *gorm.DB {
-	return db
-}
-
 var dbInsts = make(map[string]*gorm.DB)
 
 func Register(name string, dbInst *gorm.DB) {
@@ -44,22 +43,42 @@ func Select(name string) *gorm.DB {
 }
 func init() {
 	//初始化主数据库
-	if db == nil {
-		dbOnce.Do(func() {
-			dbConfig, ok := config.GetConfig(contants.DATABASE).(*config.DBConfig)
-			if !ok {
-				panic("初始化数据库失败,配置文件格式不正确")
-			}
-			driver := dbConfig.GetDriver()
-			dns := dbConfig.GetUrl()
-			open, err := gorm.Open(dialector.GetGormDial(driver).GetDial(dns), &gorm.Config{})
-			if err != nil {
-				logger.Error("初始化数据库失败:%v", err)
-				os.Exit(0)
-			}
-			db = open
-			logger.Info("初始化数据库成功")
-		})
-	}
-	Register(MAIN, db)
+	dbOnce.Do(func() {
+		dbConfig, ok := config.GetConfig(contants.DATABASE).(*config.DBConfig)
+		if !ok {
+			panic("初始化数据库失败,配置文件格式不正确")
+		}
+		driver := dbConfig.GetDriver()
+		dns := dbConfig.GetUrl()
+		open, err := gorm.Open(dialector.GetGormDial(driver).GetDial(dns), &gorm.Config{})
+		if err != nil {
+			logger.Error("初始化数据库失败:%v", err)
+			os.Exit(0)
+		}
+		Register(MAIN, open)
+		logger.Info("初始化主库数据库成功")
+		//第三方的数据库
+		initDataBases()
+
+	})
+}
+
+func initDataBases() {
+	initEtaDatabase()
+}
+func initEtaDatabase() {
+	etaDBOnce.Do(func() {
+		etaConfig, ok := config.GetConfig(contants.ETA).(*config.ETAConfig)
+		if !ok {
+			panic("初始化eta数据库失败,配置文件格式不正确")
+		}
+		dns := etaConfig.GetDBUrl()
+		open, err := gorm.Open(dialector.GetGormDial(etaDriver).GetDial(dns), &gorm.Config{})
+		if err != nil {
+			logger.Error("初始化ETA数据库失败:%v", err)
+			os.Exit(0)
+		}
+		Register(ETA, open)
+		logger.Info("初始化ETA数据库成功")
+	})
 }

+ 19 - 0
common/component/log/log_plugin.go

@@ -8,6 +8,7 @@ import (
 	"log"
 	"os"
 	"path"
+	"sync"
 )
 
 const (
@@ -17,6 +18,7 @@ const (
 
 var (
 	loggerHandler *CustomLogger
+	logMutex      = &sync.Mutex{}
 )
 
 type logger struct {
@@ -36,16 +38,33 @@ type Logger interface {
 }
 
 func Info(msg string, v ...interface{}) {
+	logMutex.Lock()
+	defer logMutex.Unlock()
 	loggerHandler.Info(msg, v...)
 }
 
 func Error(msg string, v ...interface{}) {
+	logMutex.Lock()
+	defer logMutex.Unlock()
 	loggerHandler.Error(msg, v...)
 }
 
 func Warn(msg string, v ...interface{}) {
+	logMutex.Lock()
+	defer logMutex.Unlock()
 	loggerHandler.Warn(msg, v...)
 }
+func Debug(msg string, v ...interface{}) {
+	loggerHandler.Debug(msg, v...)
+}
+func (c *CustomLogger) Debug(msg string, v ...interface{}) {
+	for _, appender := range c.logs {
+		if appender.GetLevel() >= logs.LevelInfo {
+			appender.Debug(msg, v...)
+		}
+
+	}
+}
 func (c *CustomLogger) Info(msg string, v ...interface{}) {
 	for _, appender := range c.logs {
 		if appender.GetLevel() >= logs.LevelInfo {

+ 5 - 5
common/component/sms/emas_sms_sender.go

@@ -9,7 +9,7 @@ import (
 	"eta_mini_ht_api/common/exception"
 	"eta_mini_ht_api/common/utils/sms"
 	stringUtils "eta_mini_ht_api/common/utils/string"
-	smsService "eta_mini_ht_api/domian/sms"
+	emasSmsService "eta_mini_ht_api/domian/sms"
 	"gorm.io/gorm"
 	"os"
 	"text/template"
@@ -32,7 +32,7 @@ func initDBClient(url string) *gorm.DB {
 		logger.Error("初始化亿美短信数据库失败:%v", err)
 		os.Exit(0)
 	}
-	logger.Info("初始化数据库成功")
+	logger.Info("初始化亿美短信数据库成功")
 	return open
 }
 func (emas *EMASSmsSender) InitSender(config *config.SMSConfig) {
@@ -59,15 +59,15 @@ type emasTemplate struct {
 	Code         string
 }
 
-func (emas *EMASSmsSender) SendSms(mobile string, code string) (result string, err error) {
+func (emas *EMASSmsSender) SendSms(mobile string, code string, smId int) (result string, err error) {
 	var content bytes.Buffer
 	if err = emas.TemplateGenerator.Execute(&content, &emasTemplate{emas.SmsExpireMinute, code}); err != nil {
 		// 处理错误,例如记录日志或返回错误给调用者
 		logger.Warn("[短信发送]生成短信内容失败:%v", err)
 		return SendFail, exception.New(exception.SendingSMSFailed)
 	}
-	msg := smsService.Msg{Mobile: mobile, Content: content.String()}
-	err = smsService.SendSms(msg)
+	msg := emasSmsService.Msg{SMId: smId, Mobile: mobile, Content: content.String()}
+	err = emasSmsService.SendSms(msg)
 	if err != nil {
 		logger.Warn("[短信发送]发送短信失败:%v", err)
 		return SendFail, exception.New(exception.SendingSMSFailed)

+ 1 - 1
common/component/sms/juhe_sms_sender.go

@@ -45,7 +45,7 @@ type Result struct {
 	Count int    `json:"count"`
 }
 
-func (jh *JuheSmsSender) SendSms(mobile string, code string) (result string, err error) {
+func (jh *JuheSmsSender) SendSms(mobile string, code string, _ int) (result string, err error) {
 	var Url *url.URL
 	//初始化参数
 	param := url.Values{}

+ 32 - 25
common/component/wechat/wechat_client.go

@@ -2,13 +2,13 @@ package wechat
 
 import (
 	"encoding/json"
-	"errors"
 	"eta_mini_ht_api/common/component/config"
 	"eta_mini_ht_api/common/component/log"
 	"eta_mini_ht_api/common/component/wechat/we_http"
 	"eta_mini_ht_api/common/contants"
+	"eta_mini_ht_api/common/exception"
 	"eta_mini_ht_api/common/utils/client"
-	"net/http"
+	"io"
 	"net/url"
 	"sync"
 )
@@ -18,8 +18,8 @@ const (
 	codeAPI = "/sns/jscode2session"
 )
 const (
-	// WeChatServerError 微信服务器错误时返回返回消息
-	WeChatServerError = "微信服务器发生错误"
+// WeChatServerError 微信服务器错误时返回返回消息
+
 )
 
 var (
@@ -45,8 +45,7 @@ func GetInstance() *Client {
 			return // 或者采取其他适当的错误处理措施
 		}
 		// 默认配置
-		wechatClient = NewClient(wechatConf.GetAppid(), wechatConf.GetSecret())//WithHttpClient(http.DefaultClient),
-
+		wechatClient = NewClient(wechatConf.GetAppid(), wechatConf.GetSecret(), WithHttpClient(client.DefaultClient()))
 	})
 	return wechatClient
 }
@@ -60,19 +59,17 @@ func NewClient(appid, secret string, opts ...func(*Client)) *Client {
 		appid:  appid,
 		secret: secret,
 	}
-
 	// 执行额外的配置函数
 	for _, fn := range opts {
 		fn(cli)
 	}
-
 	return cli
 }
 
 // WithHttpClient 自定义 HTTP Client
 func WithHttpClient(hc *client.HttpClient) func(*Client) {
 	return func(cli *Client) {
-		//cli.request = request.NewRequest(hc, request.ContentTypeJSON, cli.Logger)
+		cli.client = hc
 	}
 }
 
@@ -81,42 +78,52 @@ type loginResponse struct {
 	we_http.LoginResponse
 }
 
-// 小程序登录
-func (cli *Client) Login(code string) (lres we_http.LoginResponse, err error) {
+// Login 小程序登录
+func (cli *Client) Login(code string) (wxUser WxUser, err error) {
 	if code == "" {
-		err = errors.New("code不能为空")
+		err = exception.New(exception.WeChatCodeEmpty)
 		return
 	}
 	api, err := code2url(cli.appid, cli.secret, code)
 	if err != nil {
+		err = exception.New(exception.WeChatIllegalRequest)
 		return
 	}
-	res, err := http.Get(api)
-	if err != nil {
-		return
-	}
-	defer res.Body.Close()
-
-	if res.StatusCode != 200 {
-		err = errors.New(WeChatServerError)
+	//请求微信接口
+	res, err := cli.client.Get(api)
+	defer func(Body io.ReadCloser) {
+		Body.Close()
+	}(res.Body)
+
+	if err != nil || res.StatusCode != 200 {
+		logger.Error("获取微信用户信息失败:%v", res.Status)
+		err = exception.New(exception.WeChatServerError)
 		return
 	}
-
 	var data loginResponse
 	err = json.NewDecoder(res.Body).Decode(&data)
 	if err != nil {
+		logger.Error("解析微信应答失败:%v", err)
+		err = exception.New(exception.WeChatResponseError)
 		return
 	}
-
 	if data.Errcode != 0 {
-		err = errors.New(data.Errmsg)
+		logger.Error("获取用户信息失败:%v", data.Errmsg)
+		err = exception.New(exception.WechatUserInfoFailed)
 		return
 	}
-
-	lres = data.LoginResponse
+	wxUser = WxUser{
+		OpenId:  data.LoginResponse.OpenID,
+		UnionId: data.LoginResponse.UnionID,
+	}
 	return
 }
 
+type WxUser struct {
+	OpenId  string
+	UnionId string
+}
+
 // 拼接 获取 session_key 的 URL
 func code2url(appID, secret, code string) (string, error) {
 

+ 2 - 0
common/contants/contants.go

@@ -6,4 +6,6 @@ const (
 	REDIS    = "redis"
 
 	WECHAT = "wechat"
+
+	ETA = "eta"
 )

+ 49 - 3
common/exception/exc_enums.go

@@ -16,24 +16,70 @@ const (
 	SysErrCode int = iota + 10000 // iota 自动递增,从 1 开始
 	UnknownError
 	Unauthorized
-	// BIZErrCode 业务错误
-	BIZErrCode int = iota + 20000 // iota 自动递增,从 1 开始
+	// SMSErrCode BIZErrCode 业务错误
+	SMSErrCode int = iota + 20000 // iota 自动递增,从 1 开始
+	//短信
 	IllegalCodeLength
 	IllegalPhoneNumber
 	SMSCodeGenerateFailed
 	SendingSMSFailed
+	SMSCodeAlreadySent
+	SMSCodeExpired
+	SMSCodeError
+	// UserErrCode 用户
+	UserErrCode int = iota + 30000 // iota 自动递增,从 1 开始
+	TemplateUserNotFound
+	TemplateUserCreateFailed
+	GenerateTokenFailed
+	JWTTokenDecodeFailed
+
+	JWTTokenExpired
+	JWTTokenInvalid
+	// WechatErrCode 微信
+	WechatErrCode int = iota + 40000 // iota 自动递增,从 1 开始
+	WeChatServerError
+	WeChatResponseError
+	WechatUserInfoFailed
+	WeChatCodeEmpty
+	WeChatIllegalRequest
 )
 
 // ErrorMap 用于存储错误码和错误信息的映射
 var ErrorMap = map[int]string{
 	UnknownError:          "未知错误",
-	Unauthorized:          "未授权",
+	Unauthorized:          "用户未授权",
 	IllegalCodeLength:     "无效的验证码位数设置",
 	IllegalPhoneNumber:    "无效的手机号码",
 	SMSCodeGenerateFailed: "生成手机验证码失败",
 	SendingSMSFailed:      "发送手机验证码失败",
+	SMSCodeAlreadySent:    "当前手机验证码已发送,请稍后再试",
+	SMSCodeExpired:        "验证码已过期",
+	SMSCodeError:          "验证码错误",
+	//用户
+	TemplateUserNotFound:     "临时用户记录不存在",
+	TemplateUserCreateFailed: "创建临时用户失败",
+	GenerateTokenFailed:      "创建token失败",
+	JWTTokenDecodeFailed:     "token解析失败",
+	JWTTokenExpired:          "token已过期",
+	JWTTokenInvalid:          "token无效",
+	//微信
+	WeChatServerError:    "微信服务器发生错误",
+	WechatUserInfoFailed: "获取微信用户信息失败",
+	WeChatResponseError:  "解析微信响应数据失败",
+	WeChatCodeEmpty:      "微信获取用户信息,code不能为空",
+	WeChatIllegalRequest: "不合法的微信请求",
 }
 
+func Equals(code int, message string) bool {
+	if stringUtils.IsEmptyOrNil(message) {
+		return false
+	}
+	return ErrorMap[code] == message
+}
+
+func GetMsg(code int) string {
+	return ErrorMap[code]
+}
 func newException(code int, msg string) error {
 	return &EtaError{
 		ErrorCode: code,

+ 3 - 0
common/utils/client/http_client.go

@@ -30,6 +30,9 @@ func NewClient(timeout time.Duration, maxRetries int, delayFunc ...RetryDelayFun
 	}
 }
 
+func DefaultClient() *HttpClient {
+	return NewClient(time.Second*10, 3)
+}
 func defaultRetryDelayFunc(attempt int) time.Duration {
 	delay := time.Duration(attempt) * time.Second
 	if attempt > 0 {

+ 77 - 0
common/utils/jwt/jwt_utils.go

@@ -0,0 +1,77 @@
+package jwt
+
+import (
+	logger "eta_mini_ht_api/common/component/log"
+	"eta_mini_ht_api/common/exception"
+	"github.com/dgrijalva/jwt-go"
+	"time"
+)
+
+type Claims struct {
+	OpenId string `json:"open_id"`
+	Mobile string `json:"mobile"`
+	jwt.StandardClaims
+}
+
+var (
+	KEY = []byte("5Mb5Gdmb5y")
+)
+
+// CreateToken 生成token
+func CreateToken(openId string, mobile string) (tokenString string, err error) {
+
+	claims := &Claims{
+		openId,
+		mobile,
+		jwt.StandardClaims{
+			NotBefore: time.Now().Unix(),
+			ExpiresAt: time.Now().Add(time.Hour * 24 * 90).Unix(),
+		},
+	}
+	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
+	tokenString, err = token.SignedString(KEY)
+	if err != nil {
+		logger.Error("生成token失败", err)
+	}
+	return
+}
+
+type JwtInfo struct {
+	OpenId string
+	Mobile string
+}
+
+// CheckToken 校验token
+func CheckToken(tokenStr string) (info JwtInfo, err error) {
+	token, err := jwt.Parse(tokenStr, func(*jwt.Token) (interface{}, error) {
+		return KEY, nil
+	})
+	if err != nil {
+		logger.Error("解析Token失败:%v", err)
+		err = exception.New(exception.JWTTokenDecodeFailed)
+		return
+	}
+	if claims, ok := token.Claims.(jwt.MapClaims); ok {
+		nbf, _ := claims["nbf"].(float64)
+		exp, _ := claims["exp"].(float64)
+
+		if float64(time.Now().Unix()) < nbf {
+			logger.Error("Token无效")
+			err = exception.New(exception.JWTTokenInvalid)
+			return
+		}
+		if float64(time.Now().Unix()) >= exp {
+			logger.Error("Token已过期")
+			err = exception.New(exception.JWTTokenExpired)
+			return
+		}
+		info = JwtInfo{
+			OpenId: claims["open_id"].(string),
+			Mobile: claims["mobile"].(string),
+		}
+		return
+	}
+	logger.Error("claims解析失败")
+	err = exception.New(exception.JWTTokenDecodeFailed)
+	return
+}

+ 6 - 1
common/utils/redis/key_generator.go

@@ -3,9 +3,14 @@ package redis
 import "fmt"
 
 const (
-	SmsKeyPrefix = "sms_code:"
+	SmsKeyPrefix     = "sms_code:"
+	LoginTokenPrefix = "login:token:"
 )
 
 func GenerateSmsKey(mobile string) string {
 	return fmt.Sprint(SmsKeyPrefix, mobile)
 }
+
+func GenerateTokenKey(mobile string) string {
+	return fmt.Sprint(LoginTokenPrefix, mobile)
+}

+ 4 - 0
common/utils/redis/redis_client.go

@@ -11,6 +11,10 @@ type RedisClient interface {
 	GetStringWithContext(ctx context.Context, key string) string
 	SetString(key string, val string, timeout time.Duration) error
 	SetStringWithContext(ctx context.Context, key string, val string, timeout time.Duration) error
+	GetHSet(key string) string
+	GetHSetWithContext(ctx context.Context, key string) string
+	SetHSet(key string, val string, timeout time.Duration) error
+	SetHSetWithContext(ctx context.Context, key string, val string, timeout time.Duration) error
 	Delete(key string) error
 	DeleteWithContext(ctx context.Context, key string) error
 	IsExist(key string) bool

+ 1 - 1
common/utils/sms/sms_client.go

@@ -17,7 +17,7 @@ const (
 type SMSClient interface {
 	InitSender(config *config.SMSConfig)
 	GetExpireMinute() int
-	SendSms(mobile string, code string) (rs string, err error)
+	SendSms(mobile string, code string, smId int) (rs string, err error)
 }
 
 var clients = make(map[string]Instance)

+ 6 - 6
conf/log/log_config.json

@@ -10,8 +10,8 @@
     {
       "type": "file",
       "filename": "file/error.log",
-      "maxlines": 10000,
-      "maxsize": 100,
+      "maxlines": 1000000,
+      "maxsize": 102400,
       "daily": true,
       "maxdays": 7,
       "rotate": true,
@@ -21,8 +21,8 @@
     {
       "type": "file",
       "filename": "file/info.log",
-      "maxlines": 50000,
-      "maxsize": 200,
+      "maxlines": 1000000,
+      "maxsize": 102400,
       "daily": true,
       "maxdays": 30,
       "rotate": true,
@@ -33,8 +33,8 @@
       "filter": "coin,orm....*",
       "type": "file",
       "filename": "db/db.log",
-      "maxlines": 50000,
-      "maxsize": 200,
+      "maxlines": 1000000,
+      "maxsize": 102400,
       "daily": false,
       "maxdays": 30,
       "rotate": true,

+ 20 - 21
controllers/base_controller.go

@@ -3,33 +3,28 @@ package controllers
 import (
 	"encoding/json"
 	"errors"
-	"eta_mini_ht_api/common/component/cache"
 	logger "eta_mini_ht_api/common/component/log"
 	"eta_mini_ht_api/common/exception"
 	"eta_mini_ht_api/common/http"
 	"github.com/beego/beego/v2/server/web"
 )
 
-var (
-	redis *cache.RedisCache
-)
-
 type BaseController struct {
 	web.Controller
 }
 
 type BaseResponse struct {
-	Ret         int `description:"返回状态码"`
-	Msg         string
-	ErrMsg      string
-	ErrCode     int
-	Data        interface{}
-	Success     bool `description:"true 执行成功,false 执行失败"`
-	IsSendEmail bool `json:"-" description:"true 发送邮件,false 不发送邮件"`
-	IsAddLog    bool `json:"-" description:"true 新增操作日志,false 不新增操作日志" `
+	Ret     int `description:"返回状态码"`
+	Msg     string
+	ErrMsg  string
+	ErrCode int
+	Data    interface{}
+	Success bool `description:"true 执行成功,false 执行失败"`
+	//IsSendEmail bool `json:"-" description:"true 发送邮件,false 不发送邮件"`
+	//IsAddLog    bool `json:"-" description:"true 新增操作日志,false 不新增操作日志" `
 }
 
-func (b *BaseController) FailResponse(errInfo error) {
+func (b *BaseController) FailResponse(errInfo error, msg string) {
 
 	var retData BaseResponse
 	var etaError *exception.EtaError
@@ -38,7 +33,7 @@ func (b *BaseController) FailResponse(errInfo error) {
 	}
 	retData = BaseResponse{
 		Ret:     200,
-		Msg:     "",
+		Msg:     msg,
 		ErrMsg:  etaError.ErrorMsg,
 		ErrCode: etaError.ErrorCode,
 		Data:    nil}
@@ -78,7 +73,7 @@ func Wrap(a *BaseController, fn func() (*WrapData, error)) {
 	result, err := fn()
 	if err != nil {
 		logger.Error("%v", err)
-		a.FailResponse(err)
+		a.FailResponse(err, result.Msg)
 		return
 	}
 	a.JsonResult(http.GetHttpStatusByAlias("ok"), http.ErrOK, "", result.Msg, http.Success, result.Data)
@@ -89,9 +84,13 @@ type WrapData struct {
 	Data interface{}
 }
 
-func (b *BaseController) Cache() *cache.RedisCache {
-	if redis == nil {
-		redis = cache.GetInstance()
-	}
-	return redis
+func (b *BaseController) InitWrapData(msg string) *WrapData {
+	return &WrapData{Msg: msg}
+}
+func (b *BaseController) SuccessResult(msg string, data interface{}, wrapData *WrapData) {
+	wrapData.Msg = msg
+	wrapData.Data = data
+}
+func (b *BaseController) FailedResult(msg string, wrapData *WrapData) {
+	wrapData.Msg = msg
 }

+ 50 - 37
controllers/user/auth_controller.go

@@ -2,53 +2,71 @@ package user
 
 import (
 	logger "eta_mini_ht_api/common/component/log"
-	"eta_mini_ht_api/common/component/wechat"
 	"eta_mini_ht_api/common/exception"
 	authUtils "eta_mini_ht_api/common/utils/auth"
-	"eta_mini_ht_api/common/utils/redis"
-	"eta_mini_ht_api/common/utils/sms"
 	"eta_mini_ht_api/controllers"
-	"fmt"
+	"eta_mini_ht_api/service/auth"
 )
 
 type AuthController struct {
 	controllers.BaseController
 }
 
-var (
-	smsSender sms.SMSClient
+//func (a *AuthController) Prepare() {
+//	if wechatClient == nil {
+//		wechatClient = wechat.GetInstance()
+//	}
+//}
 
-	wechatClient *wechat.Client
-)
-
-func (a *AuthController) message() sms.SMSClient {
-	if smsSender == nil {
-		smsSender = sms.GetInstance()
-	}
-	return smsSender
-}
-func (a *AuthController) Prepare() {
-	if wechatClient == nil {
-		wechatClient = wechat.GetInstance()
-	}
-}
-
-// SmsCodeReq 获取验证码请求
+// LoginReq  获取验证码请求
 type LoginReq struct {
-	Code string `json:"code"`
+	Code       string `json:"code"`
+	VerifyCode string `json:"verify_code"`
+	Mobile     string `json:"mobile"`
 }
 
 // Login 小程序登录接口
 // @Summary 小程序用户登录
 // @Description 用户通过微信小程序登录
-// @Param   code     body    string  true        "获取微信小程序code"
+// @Param   mobile     body    LoginReq  true        "登录请求体"
 // @Success 200 {object} controllers.BaseResponse
 // @router /login [post]
 func (a *AuthController) Login() {
-	loginReq := new(LoginReq)
-	a.GetPostParams(loginReq)
-	wechatClient.Login(loginReq.Code)
-	//wechatClient.GetAccessToken(loginReq.Code)
+	controllers.Wrap(&a.BaseController, func() (result *controllers.WrapData, err error) {
+		result = a.InitWrapData("登录失败")
+		loginReq := new(LoginReq)
+		a.GetPostParams(loginReq)
+		if !authUtils.IsValidMobile(loginReq.Mobile) {
+			a.FailedResult("登录失败", result)
+			err = exception.New(exception.IllegalPhoneNumber)
+			return
+		}
+		err = auth.CheckUser(loginReq.Mobile, loginReq.VerifyCode)
+		if err != nil {
+			a.FailedResult("登录失败", result)
+			logger.Warn("验证码校验失败:%v", err)
+			return
+		}
+		//注册用户或者登录
+		var token string
+		token, err = auth.Login(auth.LoginDTO{
+			VerifyCode: loginReq.VerifyCode,
+			Code:       loginReq.Code,
+			Mobile:     loginReq.Mobile,
+		})
+		if err != nil {
+			a.FailedResult("登录失败", result)
+			return
+		}
+		a.SuccessResult("登录成功", &LoginResp{
+			Token: token,
+		}, result)
+		return
+	})
+}
+
+type LoginResp struct {
+	Token string
 }
 
 // SmsCodeReq 获取验证码请求
@@ -64,25 +82,20 @@ type SmsCodeReq struct {
 // @router /sendCode [post]
 func (a *AuthController) SMSCode() {
 	controllers.Wrap(&a.BaseController, func() (result *controllers.WrapData, err error) {
+		result = a.InitWrapData("发送短信失败")
 		mobile := new(SmsCodeReq)
 		a.GetPostParams(mobile)
 		phoneNum := mobile.Mobile
 		if !authUtils.IsValidMobile(phoneNum) {
 			return result, exception.New(exception.IllegalPhoneNumber)
 		}
-		code, err := authUtils.GenerateCode(6)
-		if err != nil {
-			logger.Warn("生成验证码失败:%v", err)
-			return result, exception.New(exception.SMSCodeGenerateFailed)
-		}
-		fmt.Println("验证码:", code)
+		//
 		//发送短息
-		_, err = a.message().SendSms(phoneNum, code)
+		err = auth.SendSMSCode(phoneNum)
 		if err != nil {
 			logger.Warn("发送短信失败:%v", err)
-			return result, exception.New(exception.SendingSMSFailed)
+			return result, err
 		}
-		err = a.Cache().SetString(redis.GenerateSmsKey(phoneNum), code, a.message().GetExpireMinute())
 		if err != nil {
 			return result, err
 		}

+ 2 - 6
controllers/user/user_controller.go

@@ -1,11 +1,9 @@
 package user
 
 import (
-	"errors"
 	"eta_mini_ht_api/common/component/cache"
 	logger "eta_mini_ht_api/common/component/log"
 	"eta_mini_ht_api/controllers"
-	"eta_mini_ht_api/domian/user"
 	"fmt"
 )
 
@@ -28,10 +26,8 @@ type LoginCode struct {
 // @Description get all Users
 // @Success 200 {object} models.User
 // @router / [get]
-func (u *UserController) GetAll() {
-	logger.Warn("查询用户列表:%s", errors.New("chakanjieuog"))
+func (u *UserController) Get() {
+	logger.Warn("查询用户列表:%s")
 	fmt.Print("查询用户列表")
-	users := user.GetUsers()
-	u.Data["json"] = users
 	u.ServeJSON()
 }

+ 7 - 1
domian/sms/emas_sms_service.go

@@ -2,7 +2,12 @@ package sms
 
 import "eta_mini_ht_api/models/sms"
 
+const (
+	hzCode = 1
+)
+
 type Msg struct {
+	SMId    int
 	Mobile  string
 	Content string
 }
@@ -18,7 +23,8 @@ func SendSms(msg Msg) (err error) {
 
 func convert(msg Msg) sms.ApiMtZsyyt {
 	return sms.ApiMtZsyyt{
-		SMID:    1,
+		SrcID:   hzCode,
+		SMID:    msg.SMId,
 		Mobiles: msg.Mobile,
 		Content: msg.Content,
 	}

+ 70 - 0
domian/sms/sms_service.go

@@ -0,0 +1,70 @@
+package sms
+
+import (
+	"eta_mini_ht_api/common/component/cache"
+	logger "eta_mini_ht_api/common/component/log"
+	"eta_mini_ht_api/common/utils/redis"
+	"eta_mini_ht_api/models/sms"
+)
+
+const (
+	secondToMinute = 60
+)
+
+var (
+	redisCache *cache.RedisCache
+)
+
+type CodeDTO struct {
+	Mobile       string
+	Code         string
+	ExpireMinute int
+}
+
+func rd() *cache.RedisCache {
+	if redisCache == nil {
+		redisCache = cache.GetInstance()
+	}
+	return redisCache
+}
+
+func TryExpireCode(codeDTO CodeDTO) (err error) {
+	verificationRecord := convertCodeDTO(codeDTO)
+	verificationRecord.Status = sms.StatusExpired
+	return sms.UpdateVerificationRecordByCode(codeDTO.Mobile, codeDTO.Code, verificationRecord)
+}
+
+func TryVerifiedCode(codeDTO CodeDTO) (err error) {
+	verificationRecord := convertCodeDTO(codeDTO)
+	verificationRecord.Status = sms.StatusVerified
+	return sms.UpdateVerificationRecordByCode(codeDTO.Mobile, codeDTO.Code, verificationRecord)
+}
+
+func VerifiedCodeTask() (err error) {
+	return sms.ExpiredCodesByTask()
+}
+func SendSMSCode(codeDTO CodeDTO) (smid int, err error) {
+	verificationRecord := convertCodeDTO(codeDTO)
+	//写库
+	smid, err = sms.InsertVerificationRecord(verificationRecord)
+	if err != nil {
+		logger.Error("插入验证记录失败:", err)
+		return
+	}
+	//设置redis
+	err = rd().SetString(redis.GenerateSmsKey(codeDTO.Mobile), codeDTO.Code, codeDTO.ExpireMinute*secondToMinute)
+	if err != nil {
+		logger.Error("redis验证记录操作失败:", err)
+		return
+	}
+	return
+}
+
+func convertCodeDTO(codeDTO CodeDTO) sms.VerificationRecord {
+	return sms.VerificationRecord{
+		Mobile:          codeDTO.Mobile,
+		Code:            codeDTO.Code,
+		ExpiredDuration: codeDTO.ExpireMinute,
+		Status:          sms.StatusPending,
+	}
+}

+ 56 - 3
domian/user/user_serivce.go

@@ -1,7 +1,60 @@
 package user
 
-import "eta_mini_ht_api/models"
+import (
+	"errors"
+	logger "eta_mini_ht_api/common/component/log"
+	"eta_mini_ht_api/models/user"
+	"gorm.io/gorm"
+)
 
-func GetUsers() *[]models.User {
-	return models.GetAllUsers()
+type UserDTO struct {
+	Username string
+	Mobile   string
+	OpenId   string
+	UnionId  string
+}
+
+func convertUserDTO(user user.TemplateUser) UserDTO {
+	return UserDTO{
+		Username: user.Username,
+		Mobile:   user.Mobile,
+	}
+}
+func GetUserByMobile(mobile string) (UserDTO, error) {
+	var dto UserDTO
+	templateUser, err := user.GetUserByMobile(mobile)
+	if err != nil {
+		if !errors.Is(err, gorm.ErrRecordNotFound) {
+			logger.Error("查询用户失败:%v", err)
+		}
+		return dto, err
+	}
+	dto = convertUserDTO(templateUser)
+	return dto, nil
+}
+
+func RegisterTemplateUser(dto *UserDTO) (err error) {
+	templateUser := convertToTemplateUser(dto)
+	err = user.RegisterTemplateUser(&templateUser)
+	if err != nil {
+		logger.Error("创建临时用户失败:%v", err)
+		return
+	}
+	convertToUserDTO(templateUser, dto)
+	return
+}
+func convertToTemplateUser(dto *UserDTO) user.TemplateUser {
+	return user.TemplateUser{
+		Username: dto.Username,
+		Mobile:   dto.Mobile,
+		OpenId:   dto.OpenId,
+		UnionId:  dto.UnionId,
+	}
+}
+
+func convertToUserDTO(templateUser user.TemplateUser, dto *UserDTO) {
+	dto.Username = templateUser.Username
+	dto.Mobile = templateUser.Mobile
+	dto.OpenId = templateUser.OpenId
+	dto.UnionId = templateUser.UnionId
 }

BIN
eta_mini_ht_api.exe~


+ 1 - 0
go.mod

@@ -15,6 +15,7 @@ require (
 	filippo.io/edwards25519 v1.1.0 // indirect
 	github.com/beorn7/perks v1.0.1 // indirect
 	github.com/cespare/xxhash/v2 v2.2.0 // indirect
+	github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
 	github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
 	github.com/fatih/color v1.16.0 // indirect
 	github.com/go-sql-driver/mysql v1.8.1 // indirect

+ 2 - 0
go.sum

@@ -9,6 +9,8 @@ github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
+github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
 github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
 github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
 github.com/elazarl/go-bindata-assetfs v1.0.1 h1:m0kkaHRKEu7tUIUFVwhGGGYClXvyl4RE03qmvRTNfbw=

+ 27 - 0
job/eta/eta_report_task.go

@@ -0,0 +1,27 @@
+package eta
+
+import (
+	"context"
+	"eta_mini_ht_api/models/eta"
+	"fmt"
+	"github.com/beego/beego/v2/task"
+)
+
+const (
+	TaskName = "etaReportSyncTask"
+)
+
+// Task ETA取研报的数据
+func Task() task.TaskFunc {
+	return func(ctx context.Context) error {
+		fmt.Println("开始同步研报库")
+		eta.GetETAReports()
+
+		return nil
+	}
+}
+func Init() {
+	tk := task.NewTask(TaskName, "* 0/10 * * * *", Task())
+	task.AddTask(TaskName, tk)
+	//task.StartTask()
+}

+ 11 - 0
job/eta/eta_report_task_test.go

@@ -0,0 +1,11 @@
+package eta
+
+import (
+	"eta_mini_ht_api/models/eta"
+	"testing"
+)
+
+func TestInit(t *testing.T) {
+	eta.GetETAReports()
+
+}

+ 30 - 0
job/sms/code_expired_task.go

@@ -0,0 +1,30 @@
+package sms
+
+import (
+	"context"
+	logger "eta_mini_ht_api/common/component/log"
+	"eta_mini_ht_api/domian/sms"
+	"fmt"
+	"github.com/beego/beego/v2/task"
+)
+
+const (
+	TaskName = "expiredCodeTask"
+)
+
+// Task ETA取研报的数据
+func Task() task.TaskFunc {
+	return func(ctx context.Context) error {
+		fmt.Println("开始处理过期短信")
+		err := sms.VerifiedCodeTask()
+		if err != nil {
+			logger.Error("自动更新短信状态失败:%v", err)
+		}
+		return err
+	}
+}
+func Init() {
+	tk := task.NewTask(TaskName, "0/10 * * * * *", Task())
+	task.AddTask(TaskName, tk)
+	task.StartTask()
+}

+ 15 - 0
job/task_service.go

@@ -0,0 +1,15 @@
+package job
+
+import (
+	"eta_mini_ht_api/job/sms"
+	"github.com/beego/beego/v2/task"
+)
+
+type ETATask interface {
+	Task() task.TaskFunc
+}
+
+func init() {
+	//eta.Init()
+	sms.Init()
+}

+ 5 - 2
main.go

@@ -3,6 +3,9 @@ package main
 import (
 	_ "eta_mini_ht_api/common/component"
 	logger "eta_mini_ht_api/common/component/log"
+	"eta_mini_ht_api/common/exception"
+	_ "eta_mini_ht_api/job"
+	"eta_mini_ht_api/middleware"
 	_ "eta_mini_ht_api/routers"
 	"github.com/beego/beego/v2/server/web"
 )
@@ -22,9 +25,9 @@ func main() {
 	//	MaxAge:           12 * time.Hour,
 	//}))
 	//增加授权拦截
-	//web.InsertFilter("*", web.BeforeRouter, middleware.AuthMiddleware())
+	web.InsertFilter("*", web.BeforeRouter, middleware.AuthMiddleware())
 	//web.ErrorHandler("*", exception.ControllerAdvice())
-	//web.BConfig.RecoverFunc = exception.PanicAdvice
+	web.BConfig.RecoverFunc = exception.PanicAdvice
 	go func() {
 		//内存数据预热预加载
 		logger.Info("开始预加载数据")

+ 71 - 10
middleware/auth_middleware.go

@@ -1,29 +1,90 @@
 package middleware
 
 import (
+	"eta_mini_ht_api/common/component/cache"
 	logger "eta_mini_ht_api/common/component/log"
+	"eta_mini_ht_api/common/exception"
+	"eta_mini_ht_api/common/utils/jwt"
+	"eta_mini_ht_api/common/utils/redis"
+	stringUtils "eta_mini_ht_api/common/utils/string"
+	"eta_mini_ht_api/controllers"
 	"github.com/beego/beego/v2/server/web"
 	"github.com/beego/beego/v2/server/web/context"
-	"net/http"
+	"strings"
+)
+
+var (
+	rdCache *cache.RedisCache
 )
 
 const (
-	UNAUTHORIZED = "未授权"
-	FORBIDDEN    = "禁止访问"
-	NOTFOUND     = "未找到"
+	UNAUTHORIZED  = "未授权"
+	FORBIDDEN     = "禁止访问"
+	NOTFOUND      = "未找到"
+	authorization = "Authorization"
+	baseUrl       = "/htapi"
 )
 
+func rd() *cache.RedisCache {
+	if rdCache == nil {
+		rdCache = cache.GetInstance()
+	}
+	return rdCache
+}
+
+var publicRoutes = []string{
+	"/auth/*",
+}
+
 func AuthMiddleware() web.FilterFunc {
 	return func(ctx *context.Context) {
 		path := ctx.Input.URL()
 		logger.Info("请求路径:%v", path)
-		if path == "/swagger" || path == "/swagger/*" || path == "/favicon.ico" {
-			return
+		if !allowed(path) {
+			auth := ctx.Input.Header(authorization)
+			if auth == "" {
+				logger.Error("token信息不存在")
+				_ = ctx.JSONResp(unAuthorized)
+			}
+			info, err := jwt.CheckToken(auth)
+			if err != nil {
+				logger.Error("token无效:%v", err)
+				_ = ctx.JSONResp(unAuthorized)
+			}
+			//校验redis中是否合法
+			redisToken := rd().GetString(redis.GenerateTokenKey(info.Mobile))
+			if redisToken != auth {
+				logger.Error("token无效:用户token已刷新")
+				_ = ctx.JSONResp(unAuthorized())
+			}
 		}
-		auth := ctx.Input.Header("author")
-		if auth == "" {
-			logger.Error("author is empty")
-			ctx.Abort(http.StatusUnauthorized, UNAUTHORIZED)
+	}
+}
+
+func unAuthorized() controllers.BaseResponse {
+	return controllers.BaseResponse{
+		Ret:    401,
+		Msg:    UNAUTHORIZED,
+		ErrMsg: exception.GetMsg(exception.Unauthorized),
+	}
+}
+func allowed(path string) bool {
+	for _, p := range publicRoutes {
+		if stringUtils.IsBlank(p) {
+			continue
+		}
+		src := baseUrl + p
+		if strings.HasSuffix(p, "*") {
+
+			target := src[:len(src)-1]
+			if strings.HasPrefix(path, target) {
+				return true
+			}
+		} else {
+			if src == path {
+				return true
+			}
 		}
 	}
+	return false
 }

+ 18 - 0
models/base_db.go

@@ -0,0 +1,18 @@
+package models
+
+import (
+	"eta_mini_ht_api/common/component/database"
+	"gorm.io/gorm"
+)
+
+func Main() *gorm.DB {
+	return database.Select(database.MAIN)
+}
+
+func EMAS() *gorm.DB {
+	return database.Select(database.EMAS)
+}
+
+func ETA() *gorm.DB {
+	return database.Select(database.ETA)
+}

+ 49 - 0
models/eta/eta_report.go

@@ -0,0 +1,49 @@
+package eta
+
+import (
+	"encoding/json"
+	logger "eta_mini_ht_api/common/component/log"
+	"eta_mini_ht_api/models"
+	"strings"
+)
+
+const (
+	colunms   = "id,author,abstract,title,publish_time,"
+	published = 2
+	passed    = 6
+
+	limit = 500
+)
+
+var (
+	classifyIds = []string{"classify_id_third", "classify_id_second", "classify_id_first"}
+)
+
+func (ETAReport) TableName() string {
+	return "report"
+}
+
+type ETAReport struct {
+	ID               int    `gorm:"primary_key;auto_increment" json:"id"`
+	ClassifyID       int    `gorm:"_" json:"classify_id"`
+	ClassifyIDFirst  int    `gorm:"column:classify_id_first;default:0" json:"classify_id_first"`
+	ClassifyIDSecond int    `gorm:"column:classify_id_second;default:0" json:"classify_id_second"`
+	ClassifyIDThird  int    `gorm:"column:classify_id_third;default:0" json:"classify_id_third"`
+	Title            string `gorm:"column:title;size:125;" json:"title"`
+	Abstract         string `gorm:"column:abstract;size:255;" json:"abstract"`
+	Author           string `gorm:"column:author;size:50;" json:"author"`
+	PublishTime      string `gorm:"column:publish_time" json:"publish_time"`
+}
+
+func GetETAReports() (reports []ETAReport, err error) {
+	err = models.ETA().Table("report").Select(colunms+strings.Join(classifyIds, ",")).Where("state =? or state=?", published, passed).Where("id > ?", 0).Order("id").Limit(limit).Find(&reports).Error
+	if err != nil {
+		logger.Error("同步eta数据失败", err)
+	}
+	str, _ := json.Marshal(reports[0])
+	logger.Info(string(str))
+	return
+}
+func ClassifyId() {
+
+}

+ 7 - 11
models/sms/api_mt_zsyyt.go

@@ -1,23 +1,22 @@
 package sms
 
 import (
-	"eta_mini_ht_api/common/component/database"
-	"gorm.io/gorm"
+	"eta_mini_ht_api/models"
 	"time"
 )
 
 type ApiMtZsyyt struct {
-	SMID           uint32    `gorm:"column:SM_ID;type:decimal(8,0) unsigned;not null;default:0"`
-	SrcID          uint32    `gorm:"column:SRC_ID;type:decimal(8,0) unsigned;not null;default:0"`
+	SMID           int       `gorm:"column:SM_ID;type:decimal(8,0) unsigned;not null;default:0"`
+	SrcID          int       `gorm:"column:SRC_ID;type:decimal(8,0) unsigned;not null;default:0"`
 	Mobiles        string    `gorm:"column:MOBILES;type:longtext;not null"`
 	Content        string    `gorm:"column:CONTENT;type:text;not null"`
 	IsWap          bool      `gorm:"column:IS_WAP;type:tinyint(1);not null;default:0"`
 	URL            string    `gorm:"column:URL;type:varchar(110);not null;default:''"`
 	SendTime       time.Time `gorm:"column:SEND_TIME;type:datetime;default:'0000-00-00 00:00:00'"`
-	SmType         uint8     `gorm:"column:SM_TYPE;type:tinyint(1);not null;default:0"`
+	SmType         int       `gorm:"column:SM_TYPE;type:tinyint(1);not null;default:0"`
 	MsgFmt         int       `gorm:"column:MSG_FMT;type:int(11);not null;default:0"`
-	TpPid          uint8     `gorm:"column:TP_PID;type:tinyint(1);not null;default:0"`
-	TpUdhi         uint8     `gorm:"column:TP_UDHI;type:tinyint(1);not null;default:0"`
+	TpPid          int       `gorm:"column:TP_PID;type:tinyint(1);not null;default:0"`
+	TpUdhi         int       `gorm:"column:TP_UDHI;type:tinyint(1);not null;default:0"`
 	FeeTerminalId  string    `gorm:"column:FEE_TERMINAL_ID;type:varchar(10);not null;default:''"`
 	FeeType        string    `gorm:"column:FEE_TYPE;type:varchar(10);not null;default:''"`
 	FeeCode        string    `gorm:"column:FEE_CODE;type:varchar(10);not null;default:''"`
@@ -28,14 +27,11 @@ type ApiMtZsyyt struct {
 func (ApiMtZsyyt) TableName() string {
 	return "api_mt_zsyyt"
 }
-func db() *gorm.DB {
-	return database.Select(database.EMAS)
-}
 
 // 发送短信方法
 func Send(msg ApiMtZsyyt) error {
 	// 创建记录
-	err := db().Create(&msg).Error
+	err := models.EMAS().Create(&msg).Error
 	if err != nil {
 		return err
 	}

+ 0 - 5
models/sms/sms_code.go

@@ -1,5 +0,0 @@
-package sms
-
-func saveCode() {
-
-}

+ 80 - 0
models/sms/verification_record.go

@@ -0,0 +1,80 @@
+package sms
+
+import (
+	logger "eta_mini_ht_api/common/component/log"
+	stringUtils "eta_mini_ht_api/common/utils/string"
+	"eta_mini_ht_api/models"
+	"gorm.io/gorm"
+	"time"
+)
+
+// VerificationRecord represents a record of verification request and status.
+type VerificationRecord struct {
+	ID              int                `gorm:"primaryKey;autoIncrement;type:bigint(20) unsigned"`
+	Mobile          string             `gorm:"not null;comment:'手机号码';type:varchar(15)"`
+	Code            string             `gorm:"not null;comment:'验证码';type:varchar(10)"`
+	RequestTime     time.Time          `gorm:"default:CURRENT_TIMESTAMP;not null;comment:'请求时间'"`
+	Status          VerificationStatus `gorm:"not null;comment:'操作状态';type:enum('PENDING','VERIFIED','EXPIRED','FAILED')"`
+	ExpiredTime     time.Time          `gorm:"not null;comment:'过期时间'"`
+	ExpiredDuration int                `gorm:"-"`
+}
+
+// VerificationStatus is an enumeration for the status field.
+type VerificationStatus string
+
+const (
+	StatusPending  VerificationStatus = "PENDING"
+	StatusVerified VerificationStatus = "VERIFIED"
+	StatusExpired  VerificationStatus = "EXPIRED"
+)
+
+// BeforeCreate hook to set the current time as the request time before creating a new record.
+func (v *VerificationRecord) BeforeCreate(tx *gorm.DB) (err error) {
+	v.RequestTime = time.Now()
+	v.ExpiredTime = v.RequestTime.Add(time.Duration(v.ExpiredDuration) * time.Minute)
+	return
+}
+
+func UpdateVerificationRecordByCode(mobile string, code string, record VerificationRecord) (err error) {
+	if stringUtils.IsEmptyOrNil(code) {
+		logger.Info("code为空,不做更新")
+		return
+	}
+	conditions := map[string]interface{}{
+		"mobile": mobile,
+		"code":   code,
+	}
+	err = updateByConditions(conditions, record)
+	if err != nil {
+		logger.Error("更新验证码状态失败:%v", err)
+	}
+	return
+}
+func ExpiredCodesByTask() (err error) {
+	db := models.Main()
+	err = db.Model(&VerificationRecord{}).Where("expired_time < ?", time.Now()).Where("status =?", StatusPending).Update("status", StatusExpired).Error
+	if err != nil {
+		logger.Error("批量过期验证码失败:%v", err)
+	}
+	return
+}
+func updateByConditions(conditions map[string]interface{}, record VerificationRecord) (err error) {
+	db := models.Main()
+	// 使用 conditions 构建 WHERE 条件
+	for k, v := range conditions {
+		db = db.Where(k, v)
+	}
+	err = db.Updates(record).Error
+	if err != nil {
+		logger.Error("更新验证码记录失败:%v", err)
+	}
+	return
+}
+func InsertVerificationRecord(record VerificationRecord) (smId int, err error) {
+	err = models.Main().Create(&record).Error
+	if err != nil {
+		logger.Error("插入验证码记录失败:%v", err)
+	}
+	smId = int(record.ID)
+	return
+}

+ 0 - 22
models/user.go

@@ -1,22 +0,0 @@
-package models
-
-import (
-	"eta_mini_ht_api/common/component/database"
-	"gorm.io/gorm"
-)
-
-type User struct {
-	gorm.Model
-	Gender   string `gorm:"column:gender;type:varchar(10);comment:性别"`
-	Username string `gorm:"column:username;type:varchar(20);comment:用户名"`
-	Password string
-	Mobile   string
-	OpenId   string
-	NickName string
-}
-
-func GetAllUsers() *[]User {
-	var users []User
-	database.GetInstance().Unscoped().Find(&users)
-	return &users
-}

+ 40 - 0
models/user/template_user.go

@@ -0,0 +1,40 @@
+package user
+
+import (
+	"eta_mini_ht_api/models"
+	"gorm.io/gorm"
+	"time"
+)
+
+const (
+	NotDeleted = 0
+	Deleted    = 1
+)
+
+type TemplateUser struct {
+	Username    string    `gorm:"column:username;type:varchar(20);comment:用户名"`
+	Mobile      string    `gorm:"column:mobile;type:varchar(15);comment:手机号"`
+	OpenId      string    `gorm:"column:open_id;type:varchar(50);comment:open_id"`
+	UnionId     string    `gorm:"column:union_id;type:varchar(50);comment:union_id"`
+	IsDeleted   int       `gorm:"column:is_deleted;type:int(11);comment:是否删除"`
+	CreatedTime time.Time `gorm:"column:created_time;type:timestamps;comment:创建时间"`
+	UpdatedTime time.Time `gorm:"column:updated_time;type:timestamps;comment:更新时间"`
+}
+
+func (t *TemplateUser) BeforeCreate(tx *gorm.DB) (err error) {
+	t.CreatedTime = time.Now()
+	t.IsDeleted = NotDeleted
+	return
+}
+func GetUserByMobile(mobile string) (user TemplateUser, err error) {
+	return queryByColumn("mobile", mobile)
+}
+
+func queryByColumn(column string, value string) (user TemplateUser, err error) {
+	err = models.Main().Unscoped().Where(column, value).First(&user).Error
+	return
+}
+func RegisterTemplateUser(user *TemplateUser) (err error) {
+	err = models.Main().Create(&user).Error
+	return
+}

+ 1 - 1
routers/commentsRouter.go

@@ -27,7 +27,7 @@ func init() {
 
     beego.GlobalControllerRouter["eta_mini_ht_api/controllers/user:UserController"] = append(beego.GlobalControllerRouter["eta_mini_ht_api/controllers/user:UserController"],
         beego.ControllerComments{
-            Method: "GetAll",
+            Method: "Get",
             Router: `/`,
             AllowHTTPMethods: []string{"get"},
             MethodParams: param.Make(),

+ 182 - 0
service/auth/auth_service.go

@@ -0,0 +1,182 @@
+package auth
+
+import (
+	"crypto/rand"
+	"encoding/base64"
+	"errors"
+	"eta_mini_ht_api/common/component/cache"
+	logger "eta_mini_ht_api/common/component/log"
+	"eta_mini_ht_api/common/component/wechat"
+	"eta_mini_ht_api/common/exception"
+	authUtils "eta_mini_ht_api/common/utils/auth"
+	"eta_mini_ht_api/common/utils/jwt"
+	"eta_mini_ht_api/common/utils/redis"
+	"eta_mini_ht_api/common/utils/sms"
+	stringUtils "eta_mini_ht_api/common/utils/string"
+	smsService "eta_mini_ht_api/domian/sms"
+	userService "eta_mini_ht_api/domian/user"
+	"fmt"
+	"gorm.io/gorm"
+	"strings"
+)
+
+var (
+	smsSender sms.SMSClient
+
+	wechatClient *wechat.Client
+
+	redisCache *cache.RedisCache
+)
+
+func message() sms.SMSClient {
+	if smsSender == nil {
+		smsSender = sms.GetInstance()
+	}
+	return smsSender
+}
+
+func wx() *wechat.Client {
+	if wechatClient == nil {
+		wechatClient = wechat.GetInstance()
+	}
+	return wechatClient
+}
+func SendSMSCode(mobile string) (err error) {
+	code := rd().GetString(redis.GenerateSmsKey(mobile))
+	if stringUtils.IsEmptyOrNil(code) {
+		code, err = authUtils.GenerateCode(6)
+		if err != nil {
+			logger.Warn("生成验证码失败:%v", err)
+			return exception.New(exception.SMSCodeGenerateFailed)
+		}
+		codeDTO := smsService.CodeDTO{
+			Mobile:       mobile,
+			Code:         code,
+			ExpireMinute: message().GetExpireMinute(),
+		}
+		//消息domain层
+		var smid int
+		smid, err = smsService.SendSMSCode(codeDTO)
+		if err != nil {
+			logger.Error("发送短信失败:%v", err)
+			err = exception.New(exception.SendingSMSFailed)
+		}
+		logger.Debug("验证码:%v", code)
+		_, err = message().SendSms(mobile, code, smid)
+		if err != nil {
+			logger.Error("发送短信失败:%v", err)
+			err = exception.New(exception.SendingSMSFailed)
+		}
+		return
+	}
+	return exception.New(exception.SMSCodeAlreadySent)
+}
+
+func CheckUser(mobile string, code string) (err error) {
+	smsCode := rd().GetString(redis.GenerateSmsKey(mobile))
+	if stringUtils.IsEmptyOrNil(smsCode) {
+		logger.Warn("验证码已过期:%v", code)
+		codeDTO := smsService.CodeDTO{
+			Mobile: mobile,
+			Code:   code,
+		}
+		_ = smsService.TryExpireCode(codeDTO)
+		err = exception.New(exception.SMSCodeExpired)
+		return
+	}
+	if smsCode != code {
+		err = exception.New(exception.SMSCodeError)
+		return
+	}
+	return nil
+}
+
+type LoginDTO struct {
+	VerifyCode string
+	Code       string
+	Mobile     string
+}
+
+func Login(login LoginDTO) (token string, err error) {
+	var user userService.UserDTO
+	user, err = userService.GetUserByMobile(login.Mobile)
+	if err != nil {
+		if !errors.Is(err, gorm.ErrRecordNotFound) {
+			err = exception.New(exception.UnknownError)
+			return
+		}
+		//注册用户
+		var wechatInfo wechat.WxUser
+		//微信请求异常
+		wechatInfo, err = wx().Login(login.Code)
+		//微信客户端的异常不做处理,已经是EtaError
+		if err != nil {
+			return
+		}
+		user = initUser(wechatInfo, login)
+		err = userService.RegisterTemplateUser(&user)
+		if err != nil {
+			err = exception.New(exception.TemplateUserCreateFailed)
+			return
+		}
+	}
+	token, err = jwt.CreateToken(user.OpenId, user.Mobile)
+	if err != nil {
+		err = exception.New(exception.GenerateTokenFailed)
+		return
+	}
+	err = rd().SetString(redis.GenerateTokenKey(user.Mobile), token, 90*24*60*60)
+	if err != nil {
+		err = exception.New(exception.GenerateTokenFailed)
+		return
+	}
+	codeDTO := smsService.CodeDTO{
+		Mobile: login.Mobile,
+		Code:   login.VerifyCode,
+	}
+	//登录成功删除短信验证码,数据库留痕
+	err = rd().Delete(redis.GenerateSmsKey(login.Mobile))
+	if err != nil {
+		logger.Error("清除redis 短信验证码失败,%v", err)
+		_ = rd().SetString(redis.GenerateSmsKey(login.Mobile), "", 1)
+		return "", err
+	}
+	_ = smsService.TryVerifiedCode(codeDTO)
+	return
+}
+
+func initUser(user wechat.WxUser, dto LoginDTO) userService.UserDTO {
+	username := generateRandomUsername(user.OpenId)
+	if stringUtils.IsBlank(username) {
+		username = dto.Mobile
+	}
+	return userService.UserDTO{
+		OpenId:   user.OpenId,
+		UnionId:  user.UnionId,
+		Username: username,
+		Mobile:   dto.Mobile,
+	}
+}
+
+// GenerateRandomUsername 生成基于UnionId的随机用户名
+func generateRandomUsername(openId string) string {
+	// 生成一个随机的4字节二进制数据
+	randomBytes := make([]byte, 4)
+	_, err := rand.Read(randomBytes)
+	if err != nil {
+		return ""
+	}
+	// 将随机字节转换为base64编码的URL安全字符串
+	// 并去掉可能出现的等于号
+	randomStr := strings.TrimRight(base64.URLEncoding.EncodeToString(randomBytes), "=")
+	// 拼接UnionId和随机字符串
+	username := fmt.Sprintf("%s_%s", openId, randomStr)
+	return username
+}
+
+func rd() *cache.RedisCache {
+	if redisCache == nil {
+		redisCache = cache.GetInstance()
+	}
+	return redisCache
+}

+ 9 - 51
swagger/swagger.json

@@ -15,13 +15,12 @@
                 "parameters": [
                     {
                         "in": "body",
-                        "name": "code",
-                        "description": "获取微信小程序code",
+                        "name": "mobile",
+                        "description": "登录请求体",
                         "required": true,
                         "schema": {
-                            "type": "string"
-                        },
-                        "type": "string"
+                            "$ref": "#/definitions/LoginReq"
+                        }
                     }
                 ],
                 "responses": {
@@ -81,6 +80,10 @@
         }
     },
     "definitions": {
+        "LoginReq": {
+            "title": "LoginReq",
+            "type": "object"
+        },
         "SmsCodeReq": {
             "title": "SmsCodeReq",
             "type": "object"
@@ -117,54 +120,9 @@
             "title": "interface",
             "type": "object"
         },
-        "gorm.DeletedAt": {
-            "title": "DeletedAt",
-            "type": "object"
-        },
-        "gorm.Model": {
-            "title": "Model",
-            "type": "object",
-            "properties": {
-                "CreatedAt": {
-                    "type": "string",
-                    "format": "datetime"
-                },
-                "DeletedAt": {
-                    "$ref": "#/definitions/gorm.DeletedAt"
-                },
-                "ID": {
-                    "type": "integer",
-                    "format": "int32"
-                },
-                "UpdatedAt": {
-                    "type": "string",
-                    "format": "datetime"
-                }
-            }
-        },
         "models.User": {
             "title": "User",
-            "type": "object",
-            "properties": {
-                "Gender": {
-                    "type": "string"
-                },
-                "Mobile": {
-                    "type": "string"
-                },
-                "NickName": {
-                    "type": "string"
-                },
-                "OpenId": {
-                    "type": "string"
-                },
-                "Password": {
-                    "type": "string"
-                },
-                "Username": {
-                    "type": "string"
-                }
-            }
+            "type": "object"
         }
     },
     "tags": [

+ 6 - 35
swagger/swagger.yml

@@ -12,12 +12,11 @@ paths:
         <br>
       parameters:
       - in: body
-        name: code
-        description: 获取微信小程序code
+        name: mobile
+        description: 登录请求体
         required: true
         schema:
-          type: string
-        type: string
+          $ref: '#/definitions/LoginReq'
       responses:
         "200":
           description: ""
@@ -57,6 +56,9 @@ paths:
           schema:
             $ref: '#/definitions/models.User'
 definitions:
+  LoginReq:
+    title: LoginReq
+    type: object
   SmsCodeReq:
     title: SmsCodeReq
     type: object
@@ -83,40 +85,9 @@ definitions:
   controllers.interface:
     title: interface
     type: object
-  gorm.DeletedAt:
-    title: DeletedAt
-    type: object
-  gorm.Model:
-    title: Model
-    type: object
-    properties:
-      CreatedAt:
-        type: string
-        format: datetime
-      DeletedAt:
-        $ref: '#/definitions/gorm.DeletedAt'
-      ID:
-        type: integer
-        format: int32
-      UpdatedAt:
-        type: string
-        format: datetime
   models.User:
     title: User
     type: object
-    properties:
-      Gender:
-        type: string
-      Mobile:
-        type: string
-      NickName:
-        type: string
-      OpenId:
-        type: string
-      Password:
-        type: string
-      Username:
-        type: string
 tags:
 - name: user
   description: |