Переглянути джерело

add public and internal api

chengliang 2 місяців тому
батько
коміт
05bddb55ea

+ 6 - 0
.idea/thriftCompilerAssist.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ThriftCompilerAssist">
+    <compilers />
+  </component>
+</project>

+ 159 - 0
app/api/hotupdate/internal.go

@@ -0,0 +1,159 @@
+/**
+ * @author chengliang
+ * @date 2026/1/26 15:40
+ * @brief
+ *
+ **/
+
+package hotupdate
+
+import (
+	"dsbqj-admin/app/service"
+	"dsbqj-admin/pkg/app"
+	"dsbqj-admin/pkg/e"
+	"dsbqj-admin/pkg/logger"
+	"dsbqj-admin/tool"
+	"encoding/json"
+	"fmt"
+	"github.com/gin-gonic/gin"
+	"net/http"
+	"time"
+)
+
+const DEF_REQ_TIMEOUT_SEC = 30 * 60
+
+// 内部请求
+func AddVersion(c *gin.Context) {
+	var appG = app.Gin{C: c}
+	logger.Info("uri %s", c.Request.RequestURI)
+	req := service.TAddVersionReq{}
+	err := c.ShouldBind(&req)
+	if err == nil {
+		err = checkAddVersionHash(req, true)
+		if err != nil {
+			appG.Response(http.StatusOK, e.ERROR_UPLOAD_SIGNURL_FAIL, err.Error())
+			return
+		}
+		var err = service.GetTHotUpdateVerManager().AddVersion(&req)
+		if err != nil {
+			appG.Response(http.StatusOK, e.NO_RECORD, err.Error())
+		} else {
+			appG.Response(http.StatusOK, e.SUCCESS, "")
+		}
+	} else {
+		appG.Response(http.StatusOK, e.INVALID_PARAMS, err.Error())
+	}
+}
+
+// 获取最大version
+func GetMaxVerInfo(c *gin.Context) {
+	logger.Info("uri %s", c.Request.RequestURI)
+	var appG = app.Gin{C: c}
+	req := service.TGetVersionReq{}
+	err := c.ShouldBind(&req)
+	if err == nil {
+		err = checkAddVersionHash(req, false)
+		if err != nil {
+			appG.Response(http.StatusOK, e.ERROR_UPLOAD_SIGNURL_FAIL, err.Error())
+			return
+		}
+		var rsp, err = service.GetTHotUpdateVerManager().GetMaxVerInfo(req.Proj, req.Os)
+		if err != nil {
+			appG.Response(http.StatusOK, e.NO_RECORD, err.Error())
+		} else {
+			appG.Response(http.StatusOK, e.SUCCESS, rsp)
+		}
+	} else {
+		appG.Response(http.StatusOK, e.INVALID_PARAMS, err.Error())
+	}
+}
+
+// 获取version list
+func GetVersionList(c *gin.Context) {
+	logger.Info("uri %s", c.Request.RequestURI)
+	var appG = app.Gin{C: c}
+	req := service.TGetVersionListReq{}
+	err := c.ShouldBind(&req)
+	if err == nil {
+		err = checkAddVersionHash(req, false)
+		if err != nil {
+			appG.Response(http.StatusOK, e.ERROR_UPLOAD_SIGNURL_FAIL, err.Error())
+			return
+		}
+		var rsp, err = service.GetTHotUpdateVerManager().GetVersionList(req.Start, req.Limit)
+		if err != nil {
+			appG.Response(http.StatusOK, e.NO_RECORD, err.Error())
+		} else {
+			appG.Response(http.StatusOK, e.SUCCESS, rsp)
+		}
+	} else {
+		appG.Response(http.StatusOK, e.INVALID_PARAMS, err.Error())
+	}
+}
+
+// 修改状态
+func ChangeStatus(c *gin.Context) {
+	logger.Info("uri %s", c.Request.RequestURI)
+	var appG = app.Gin{C: c}
+	req := service.TChangeStautsReq{}
+	err := c.ShouldBind(&req)
+	if err == nil {
+		err = checkAddVersionHash(req, false)
+		if err != nil {
+			appG.Response(http.StatusOK, e.ERROR_UPLOAD_SIGNURL_FAIL, err.Error())
+			return
+		}
+		var rsp, err = service.GetTHotUpdateVerManager().ChangeStatus(req.ID, req.Status)
+		if err != nil {
+			appG.Response(http.StatusOK, e.NO_RECORD, err.Error())
+		} else {
+			appG.Response(http.StatusOK, e.SUCCESS, rsp)
+		}
+	} else {
+		appG.Response(http.StatusOK, e.INVALID_PARAMS, err.Error())
+	}
+}
+
+func checkAddVersionHash(tReq any, checkTimeout bool) error {
+
+	jsonBytes, err := json.Marshal(tReq)
+	if err != nil {
+		return err
+	}
+	var res map[string]any
+	err = json.Unmarshal(jsonBytes, &res)
+	if err != nil {
+		return err
+	}
+	if checkTimeout {
+		reqTimeStr, ok := res["timesec"]
+		if !ok {
+			return fmt.Errorf("time is empty")
+		}
+		curTime := time.Now().Unix()
+		reqTime, ok := reqTimeStr.(int64) //strconv.ParseInt(reqTimeStr, 10, 64)
+		if !ok {
+			return fmt.Errorf("timesec:%s is not int64", reqTimeStr)
+		}
+		deltaTime := curTime - reqTime
+		if deltaTime < 0 {
+			deltaTime = -deltaTime
+		}
+		if deltaTime > DEF_REQ_TIMEOUT_SEC {
+			return fmt.Errorf("time:%d, nowtime:%d", reqTime, curTime)
+		}
+	}
+
+	reqSign, ok := res["sign"]
+	if !ok {
+		return fmt.Errorf("sign is empty")
+	}
+	delete(res, "sign")
+
+	sign := tool.GenSign(res)
+	if sign != reqSign {
+		return fmt.Errorf("reqSign:%s, but now sign:%s, sign error", reqSign, sign)
+	}
+
+	return nil
+}

+ 3 - 20
app/api/hotupdate/hotupdate.go → app/api/hotupdate/public.go

@@ -16,18 +16,18 @@ import (
 	"net/http"
 )
 
-/**
+/*
+*
 hotupdate/getversion?os=ios&proj=bzst
 获取最新的version内容
 */
-
 func GetVersion(c *gin.Context) {
 	logger.Info("uri %s", c.Request.RequestURI)
 	var appG = app.Gin{C: c}
 	req := service.TGetVersionReq{}
 	err := c.ShouldBind(&req)
 	if err == nil {
-		var rsp, err = service.GetTHotUpdateVerManager().GetMaxVerInfo(req.Proj, req.Os)
+		var rsp, err = service.GetTHotUpdateVerManager().GetMaxPubVerInfo(req.Proj, req.Os)
 		if err != nil {
 			appG.Response(http.StatusOK, e.NO_RECORD, err.Error())
 		} else {
@@ -38,20 +38,3 @@ func GetVersion(c *gin.Context) {
 		appG.Response(http.StatusOK, e.INVALID_PARAMS, err.Error())
 	}
 }
-
-func AddVersion(c *gin.Context) {
-	var appG = app.Gin{C: c}
-	logger.Info("uri %s", c.Request.RequestURI)
-	req := service.TAddVersionReq{}
-	err := c.ShouldBind(&req)
-	if err == nil {
-		var err = service.GetTHotUpdateVerManager().AddVersion(&req)
-		if err != nil {
-			appG.Response(http.StatusOK, e.NO_RECORD, err.Error())
-		} else {
-			appG.Response(http.StatusOK, e.SUCCESS, "")
-		}
-	} else {
-		appG.Response(http.StatusOK, e.INVALID_PARAMS, err.Error())
-	}
-}

+ 10 - 3
app/router/router.go

@@ -4,9 +4,8 @@ import (
 	"dsbqj-admin/app/api/hotupdate"
 	v1 "dsbqj-admin/app/api/v1"
 	"dsbqj-admin/middleware"
-	"os"
-
 	"github.com/gin-gonic/gin"
+	"os"
 )
 
 // NewRouter 路由配置
@@ -53,7 +52,15 @@ func NewRouter() *gin.Engine {
 	hotupdateR := r.Group("/hotupdate")
 	{
 		hotupdateR.GET("/getversion", hotupdate.GetVersion)
-		hotupdateR.GET("/addversion", hotupdate.AddVersion)
+	}
+
+	// TODO 内网请求,需要添加ip白名单等
+	internalR := r.Group("/internal")
+	{
+		internalR.GET("/addversion", hotupdate.AddVersion)
+		internalR.GET("/getmaxverinfo", hotupdate.GetMaxVerInfo)
+		internalR.GET("/getversionlist", hotupdate.GetVersionList)
+		internalR.GET("/changestatus", hotupdate.ChangeStatus)
 	}
 	//webv1.Use(middleware.BodyHandler())
 	//{

+ 100 - 30
app/service/hotupdate.go

@@ -12,11 +12,23 @@ import (
 	"dsbqj-admin/pkg/logger"
 	"errors"
 	"fmt"
+	"go.mongodb.org/mongo-driver/bson"
+	"go.mongodb.org/mongo-driver/mongo/options"
 	"strconv"
 	"strings"
 	"sync"
 )
 
+var validProj = map[string]bool{
+	"bzst": true,
+	"ds20": true,
+}
+
+var validOs = map[string]bool{
+	"ios":     true,
+	"android": true,
+}
+
 type THotUpdateVerManager struct {
 	mu         sync.RWMutex
 	versionMap map[string]map[string]*TGetVersionRsp // proj:os:versionInfo
@@ -39,26 +51,31 @@ func GetTHotUpdateVerManager() *THotUpdateVerManager {
 }
 
 func (this *THotUpdateVerManager) Init() {
-	_, err := this.GetMaxVerInfo("bzst", "ios")
+	_, err := this.GetMaxPubVerInfo("bzst", "ios")
 	if err != nil {
 		logger.Error(err.Error())
 	}
-	_, err = this.GetMaxVerInfo("bzst", "android")
+	_, err = this.GetMaxPubVerInfo("bzst", "android")
 	if err != nil {
 		logger.Error(err.Error())
 	}
 }
 
-func (this *THotUpdateVerManager) GetMaxVerInfo(proj, os string) (*TGetVersionRsp, error) {
-
+func (this *THotUpdateVerManager) GetMaxPubVerInfo(proj, os string) (*TGetVersionRsp, error) {
 	if proj == "" || os == "" {
 		return nil, errors.New("project or os can't be empty")
 	}
+	if !validProj[proj] {
+		return nil, fmt.Errorf("project:%s invalid", proj)
+	}
+	if !validOs[os] {
+		return nil, fmt.Errorf("os:%s invalid", os)
+	}
 
 	if ver, err := this.getVersionMapBy(proj, os); err == nil {
 		return ver, nil
 	} else {
-		versionRsp, err := this.findMaxVersion(proj, os)
+		versionRsp, err := this.findDBMaxPubVersion(proj, os)
 		if err != nil {
 			return nil, err
 		}
@@ -67,6 +84,15 @@ func (this *THotUpdateVerManager) GetMaxVerInfo(proj, os string) (*TGetVersionRs
 	}
 }
 
+// 内部API调用
+func (this *THotUpdateVerManager) GetMaxVerInfo(proj, os string) (*TGetVersionRsp, error) {
+	if proj == "" || os == "" {
+		return nil, errors.New("project or os can't be empty")
+	}
+	return this.findDBMaxVersion(proj, os)
+}
+
+// 内部API调用
 func (this *THotUpdateVerManager) AddVersion(req *TAddVersionReq) error {
 	if !this.isInvalid(req) {
 		return errors.New("version is invalid")
@@ -78,20 +104,48 @@ func (this *THotUpdateVerManager) AddVersion(req *TAddVersionReq) error {
 		PackageUrl:        req.PackageUrl,
 		RemoteVersionUrl:  req.RemoteVersionUrl,
 		RemoteManifestUrl: req.RemoteManifestUrl,
+		Status:            hotupdate.DEF_VER_STATUS_NONE,
 	}
-	err := this.versionDB.Create(mVersion)
-	if err != nil {
-		return err
+	return this.versionDB.Create(mVersion)
+}
+
+func (this *THotUpdateVerManager) ChangeStatus(id string, status int16) (*TGetVersionRsp, error) {
+	if id == "" {
+		return nil, errors.New("id can't be empty")
 	}
-	verRsp := &TGetVersionRsp{
-		Version:           req.Version,
-		PackageUrl:        req.PackageUrl,
-		RemoteVersionUrl:  req.RemoteVersionUrl,
-		RemoteManifestUrl: req.RemoteManifestUrl,
+	if status != hotupdate.DEF_VER_STATUS_NONE && status != hotupdate.DEF_VER_STATUS_PUBLIC {
+		return nil, fmt.Errorf("invalid status %d", status)
 	}
-	this.updateVersionMap(req.Proj, req.Os, verRsp)
+	updateMap := make(map[string]interface{})
+	updateMap["status"] = status
+	mVersion, err := this.versionDB.Update(id, updateMap)
+	if err != nil {
+		return nil, err
+	}
+	logger.Info("after update mVersion", mVersion)
+	vRsp := &TGetVersionRsp{}
+	vRsp.FromMVersion(mVersion)
+	return vRsp, nil
+}
 
-	return nil
+// 内部API调用 分页获取version list
+func (this *THotUpdateVerManager) GetVersionList(start, limit int64) ([]*TGetVersionRsp, error) {
+	opts := options.Find().
+		SetSort(bson.M{"created_at": -1}). // 按版本降序
+		SetSkip(start).
+		SetLimit(limit)
+
+	dbVersionArr, err := this.versionDB.Find(nil, opts)
+	if err != nil {
+		return nil, err
+	}
+	rspArr := make([]*TGetVersionRsp, len(dbVersionArr))
+	for _, mVersion := range dbVersionArr {
+		vRsp := &TGetVersionRsp{}
+		vRsp.FromMVersion(&mVersion)
+		rspArr = append(rspArr, vRsp)
+	}
+	return rspArr, nil
 }
 
 func (this *THotUpdateVerManager) getVersionMapBy(proj, os string) (*TGetVersionRsp, error) {
@@ -119,7 +173,7 @@ func (this *THotUpdateVerManager) updateVersionMap(proj, os string, versionRsp *
 
 // 版本号是递增的
 func (this *THotUpdateVerManager) isInvalid(req *TAddVersionReq) bool {
-	curVersion, err := this.GetMaxVerInfo(req.Proj, req.Os)
+	curVersion, err := this.findDBMaxVersion(req.Proj, req.Os)
 	if err != nil {
 		// 默认只有系统初始化时
 		return true
@@ -128,28 +182,44 @@ func (this *THotUpdateVerManager) isInvalid(req *TAddVersionReq) bool {
 	return CompareVersion(req.Version, curVersion.Version) == 1
 }
 
-func (this *THotUpdateVerManager) findMaxVersion(proj, os string) (*TGetVersionRsp, error) {
+// 对外的最大version
+func (this *THotUpdateVerManager) findDBMaxPubVersion(proj, os string) (*TGetVersionRsp, error) {
+	return this.findMaxVersionByStatus(proj, os, hotupdate.DEF_VER_STATUS_PUBLIC)
+}
+
+// 查找数据库中最大的version
+func (this *THotUpdateVerManager) findDBMaxVersion(proj, os string) (*TGetVersionRsp, error) {
 	if proj == "" || os == "" {
 		return nil, errors.New("project or os can't be empty")
 	}
+	return this.findMaxVersionByStatus(proj, os, hotupdate.DEF_VER_STATUS_INVALID)
+}
 
-	versions, num, err := this.versionDB.ListByProjectAndOS(proj, os, 1, 20)
-	// 因为是按时间降序 潜规则最近一条的version最大
-	if err != nil {
-		return nil, err
+func (this *THotUpdateVerManager) findMaxVersionByStatus(proj, os string, status int16) (*TGetVersionRsp, error) {
+	if proj == "" || os == "" {
+		return nil, errors.New("project or os can't be empty")
+	}
+
+	filte := bson.M{
+		"project": proj,
+		"os":      os,
 	}
-	logger.Debug("versions:", num, versions)
-	if num == 0 {
-		return nil, errors.New(fmt.Sprintf("proj:%s os:%s not find", proj, os))
+	if status == hotupdate.DEF_VER_STATUS_PUBLIC || status == hotupdate.DEF_VER_STATUS_NONE {
+		filte["status"] = status
 	}
+	opts := options.Find().
+		SetSort(bson.M{"created_at": -1})
 
-	versionObj := versions[0]
-	versionRsp := &TGetVersionRsp{
-		Version:           versionObj.Version,
-		PackageUrl:        versionObj.PackageUrl,
-		RemoteManifestUrl: versionObj.RemoteManifestUrl,
-		RemoteVersionUrl:  versionObj.RemoteVersionUrl,
+	versionArr, err := this.versionDB.Find(filte, opts)
+	if err != nil {
+		return nil, err
+	}
+	if len(versionArr) == 0 {
+		return nil, errors.New("version not found")
 	}
+	mVersion := versionArr[0]
+	versionRsp := &TGetVersionRsp{}
+	versionRsp.FromMVersion(&mVersion)
 	return versionRsp, nil
 }
 

+ 29 - 0
app/service/type.go

@@ -7,16 +7,33 @@
 
 package service
 
+import "dsbqj-admin/model/mongo/hotupdate"
+
 type TGetVersionReq struct {
 	Proj string `form:"proj" binding:"required" json:"proj"`
 	Os   string `form:"os" binding:"required" json:"os"`
 }
 
 type TGetVersionRsp struct {
+	ID                string `json:"id"`
+	CreateTimeSec     int64  `json:"createTime"`
+	UpdateTimeSec     int64  `json:"updateTime"`
 	Version           string `json:"version"`
 	PackageUrl        string `json:"packageUrl"`
 	RemoteManifestUrl string `json:"remoteManifestUrl"`
 	RemoteVersionUrl  string `json:"remoteVersionUrl"`
+	Status            int16  `json:"status"`
+}
+
+func (this *TGetVersionRsp) FromMVersion(mVersion *hotupdate.MVersionInfo) {
+	this.ID = mVersion.ID.Hex()
+	this.CreateTimeSec = mVersion.CreatedAt.Unix()
+	this.UpdateTimeSec = mVersion.UpdatedAt.Unix()
+	this.Version = mVersion.Version
+	this.PackageUrl = mVersion.PackageUrl
+	this.RemoteVersionUrl = mVersion.RemoteVersionUrl
+	this.RemoteManifestUrl = mVersion.RemoteManifestUrl
+	this.Status = mVersion.Status
 }
 
 type TAddVersionReq struct {
@@ -26,4 +43,16 @@ type TAddVersionReq struct {
 	PackageUrl        string `form:"packageUrl" binding:"required" json:"packageUrl"`
 	RemoteManifestUrl string `form:"remoteManifestUrl" binding:"required" json:"remoteManifestUrl"`
 	RemoteVersionUrl  string `form:"remoteVersionUrl" binding:"required" json:"remoteVersionUrl"`
+	TimeSec           int64  `form:"timesec" binding:"required" json:"timesec"`
+	Sign              string `form:"sign" binding:"required" json:"sign"`
+}
+
+type TGetVersionListReq struct {
+	Start int64 `form:"start" binding:"required" json:"start"`
+	Limit int64 `form:"limit" binding:"required" json:"limit"`
+}
+
+type TChangeStautsReq struct {
+	ID     string `form:"id" binding:"required" json:"id"`
+	Status int16  `form:"status" binding:"required" json:"status"`
 }

+ 3 - 3
model/mongo/hotupdate/hotupdate.go

@@ -112,7 +112,7 @@ func (s *TVersionDB) Find(filter bson.M, opts ...*options.FindOptions) ([]MVersi
 }
 
 // ListByProjectAndOS 根据项目和系统列出版本
-func (s *TVersionDB) ListByProjectAndOS(project, os string, page, size int64) ([]MVersionInfo, int64, error) {
+func (s *TVersionDB) ListByProjectAndOS(project, os string, start, limit int64) ([]MVersionInfo, int64, error) {
 	filter := bson.M{"project": project}
 	if os != "" {
 		filter["os"] = os
@@ -130,8 +130,8 @@ func (s *TVersionDB) ListByProjectAndOS(project, os string, page, size int64) ([
 	// 分页查询
 	opts := options.Find().
 		SetSort(bson.M{"created_at": -1}). // 按版本降序
-		SetSkip((page - 1) * size).
-		SetLimit(size)
+		SetSkip(start).
+		SetLimit(limit)
 
 	results, err := s.Find(filter, opts)
 	if err != nil {

+ 8 - 2
model/mongo/hotupdate/type.go

@@ -9,16 +9,22 @@ package hotupdate
 
 import "github.com/kamva/mgm/v3"
 
-// MVersionInfo
+const (
+	DEF_VER_STATUS_INVALID int16 = -1
+	DEF_VER_STATUS_NONE    int16 = 0 // 默认不生效
+	DEF_VER_STATUS_PUBLIC  int16 = 1 // 对外
+)
+
+// mongo model
 type MVersionInfo struct {
 	mgm.DefaultModel  `bson:",inline"`
 	Proj              string `json:"project" bson:"project"`
 	Os                string `json:"os" bson:"os"`
+	Status            int16  `json:"status" bson:"status"`
 	Version           string `json:"version" bson:"version"`
 	PackageUrl        string `json:"packageUrl" bson:"packageUrl"`
 	RemoteManifestUrl string `json:"remoteManifestUrl" bson:"remoteManifestUrl"`
 	RemoteVersionUrl  string `json:"remoteVersionUrl" bson:"remoteVersionUrl"`
-	Status            string `json:"status" bson:"status"`
 }
 
 // 定义集合名称

+ 50 - 0
tool/signweb.go

@@ -0,0 +1,50 @@
+/**
+ * @author chengliang
+ * @date 2026/1/23 18:38
+ * @brief
+ *
+ **/
+
+package tool
+
+import (
+	"crypto/md5"
+	"encoding/hex"
+	"fmt"
+	"hash"
+	"sort"
+	"strings"
+	"sync"
+)
+
+const KEY_SECRET = "secret"
+
+var md5Pool = sync.Pool{
+	New: func() interface{} {
+		return md5.New()
+	},
+}
+
+func GenSign(params map[string]any) string {
+	var keys []string
+	for k := range params {
+		keys = append(keys, k)
+	}
+	sort.Strings(keys)
+	fmt.Println("keys:", keys)
+	var paramParts []string
+	for _, k := range keys {
+		paramParts = append(paramParts, fmt.Sprintf("%s=%s", k, params[k]))
+	}
+	paramStr := strings.Join(paramParts, "&")
+	paramStr = fmt.Sprintf("%s&%s", paramStr, KEY_SECRET)
+
+	md5Obj := md5Pool.Get().(hash.Hash)
+	defer md5Pool.Put(md5Obj)
+
+	md5Obj.Reset()
+	md5Obj.Write([]byte(paramStr))
+	md5ObjSum := md5Obj.Sum(nil)
+
+	return hex.EncodeToString(md5ObjSum)
+}