Преглед изворни кода

Merge branch 'master' of http://192.168.1.30:3000/niezenghua/dsbqj_admin

DESKTOP-HN5QP3V\Administrator пре 2 месеци
родитељ
комит
4f3c2805f8

+ 57 - 0
app/api/hotupdate/hotupdate.go

@@ -0,0 +1,57 @@
+/**
+ * @author chengliang
+ * @date 2026/1/12 21:06
+ * @brief
+ *
+ **/
+
+package hotupdate
+
+import (
+	"dsbqj-admin/app/service"
+	"dsbqj-admin/pkg/app"
+	"dsbqj-admin/pkg/e"
+	"dsbqj-admin/pkg/logger"
+	"github.com/gin-gonic/gin"
+	"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)
+		if err != nil {
+			appG.Response(http.StatusOK, e.NO_RECORD, err.Error())
+		} else {
+			// 不需要code等
+			c.JSON(http.StatusOK, rsp)
+		}
+	} else {
+		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())
+	}
+}

+ 6 - 0
app/router/router.go

@@ -1,6 +1,7 @@
 package router
 
 import (
+	"dsbqj-admin/app/api/hotupdate"
 	v1 "dsbqj-admin/app/api/v1"
 	"dsbqj-admin/middleware"
 	"os"
@@ -49,6 +50,11 @@ func NewRouter() *gin.Engine {
 		subscribev1.POST("/task", v1.Task)   // 触发订阅任务发送
 	}
 
+	hotupdateR := r.Group("/hotupdate")
+	{
+		hotupdateR.GET("/getversion", hotupdate.GetVersion)
+		hotupdateR.GET("/addversion", hotupdate.AddVersion)
+	}
 	//webv1.Use(middleware.BodyHandler())
 	//{
 	//

+ 192 - 0
app/service/hotupdate.go

@@ -0,0 +1,192 @@
+/**
+ * @author chengliang
+ * @date 2026/1/12 21:55
+ * @brief
+ *
+ **/
+
+package service
+
+import (
+	"dsbqj-admin/model/mongo/hotupdate"
+	"dsbqj-admin/pkg/logger"
+	"errors"
+	"fmt"
+	"strconv"
+	"strings"
+	"sync"
+)
+
+type THotUpdateVerManager struct {
+	mu         sync.RWMutex
+	versionMap map[string]map[string]*TGetVersionRsp // proj:os:versionInfo
+	versionDB  *hotupdate.TVersionDB
+}
+
+var (
+	verManager *THotUpdateVerManager
+	once       sync.Once
+)
+
+func GetTHotUpdateVerManager() *THotUpdateVerManager {
+	once.Do(func() {
+		verManager = &THotUpdateVerManager{
+			versionMap: make(map[string]map[string]*TGetVersionRsp),
+			versionDB:  hotupdate.NewVersionService(),
+		}
+	})
+	return verManager
+}
+
+func (this *THotUpdateVerManager) Init() {
+	_, err := this.GetMaxVerInfo("bzst", "ios")
+	if err != nil {
+		logger.Error(err.Error())
+	}
+	_, err = this.GetMaxVerInfo("bzst", "android")
+	if err != nil {
+		logger.Error(err.Error())
+	}
+}
+
+func (this *THotUpdateVerManager) GetMaxVerInfo(proj, os string) (*TGetVersionRsp, error) {
+
+	if proj == "" || os == "" {
+		return nil, errors.New("project or os can't be empty")
+	}
+
+	if ver, err := this.getVersionMapBy(proj, os); err == nil {
+		return ver, nil
+	} else {
+		versionRsp, err := this.findMaxVersion(proj, os)
+		if err != nil {
+			return nil, err
+		}
+		this.updateVersionMap(proj, os, versionRsp)
+		return versionRsp, nil
+	}
+}
+
+func (this *THotUpdateVerManager) AddVersion(req *TAddVersionReq) error {
+	if !this.isInvalid(req) {
+		return errors.New("version is invalid")
+	}
+	mVersion := &hotupdate.MVersionInfo{
+		Proj:              req.Proj,
+		Os:                req.Os,
+		Version:           req.Version,
+		PackageUrl:        req.PackageUrl,
+		RemoteVersionUrl:  req.RemoteVersionUrl,
+		RemoteManifestUrl: req.RemoteManifestUrl,
+	}
+	err := this.versionDB.Create(mVersion)
+	if err != nil {
+		return err
+	}
+	verRsp := &TGetVersionRsp{
+		Version:           req.Version,
+		PackageUrl:        req.PackageUrl,
+		RemoteVersionUrl:  req.RemoteVersionUrl,
+		RemoteManifestUrl: req.RemoteManifestUrl,
+	}
+	this.updateVersionMap(req.Proj, req.Os, verRsp)
+
+	return nil
+}
+
+func (this *THotUpdateVerManager) getVersionMapBy(proj, os string) (*TGetVersionRsp, error) {
+	this.mu.RLock()
+	defer this.mu.RUnlock()
+	if _, ok := this.versionMap[proj]; !ok {
+		return nil, errors.New("project or os can't be empty")
+	}
+	if ver, ok := this.versionMap[proj][os]; ok {
+		return ver, nil
+	} else {
+		return nil, errors.New("version not found")
+	}
+}
+
+func (this *THotUpdateVerManager) updateVersionMap(proj, os string, versionRsp *TGetVersionRsp) {
+	this.mu.Lock()
+	defer this.mu.Unlock()
+	logger.Info(fmt.Sprintf("updateVersionMap <UNK>%s<UNK>%s<UNK>%s", proj, os, versionRsp.Version))
+	if _, ok := this.versionMap[proj]; !ok {
+		this.versionMap[proj] = make(map[string]*TGetVersionRsp)
+	}
+	this.versionMap[proj][os] = versionRsp
+}
+
+// 版本号是递增的
+func (this *THotUpdateVerManager) isInvalid(req *TAddVersionReq) bool {
+	curVersion, err := this.GetMaxVerInfo(req.Proj, req.Os)
+	if err != nil {
+		// 默认只有系统初始化时
+		return true
+	}
+	logger.Info("curVersion:%s, remoteVersion:%s", curVersion, req.Version)
+	return CompareVersion(req.Version, curVersion.Version) == 1
+}
+
+func (this *THotUpdateVerManager) findMaxVersion(proj, os string) (*TGetVersionRsp, error) {
+	if proj == "" || os == "" {
+		return nil, errors.New("project or os can't be empty")
+	}
+
+	versions, num, err := this.versionDB.ListByProjectAndOS(proj, os, 1, 20)
+	// 因为是按时间降序 潜规则最近一条的version最大
+	if err != nil {
+		return nil, err
+	}
+	logger.Debug("versions:", num, versions)
+	if num == 0 {
+		return nil, errors.New(fmt.Sprintf("proj:%s os:%s not find", proj, os))
+	}
+
+	versionObj := versions[0]
+	versionRsp := &TGetVersionRsp{
+		Version:           versionObj.Version,
+		PackageUrl:        versionObj.PackageUrl,
+		RemoteManifestUrl: versionObj.RemoteManifestUrl,
+		RemoteVersionUrl:  versionObj.RemoteVersionUrl,
+	}
+	return versionRsp, nil
+}
+
+// CompareVersion 比较两个版本号
+// 返回: -1(v1<v2), 0(v1=v2), 1(v1>v2)
+func CompareVersion(v1, v2 string) int {
+	// 将版本号按点号分割
+	parts1 := strings.Split(v1, ".")
+	parts2 := strings.Split(v2, ".")
+
+	// 获取最大长度
+	maxLen := len(parts1)
+	if len(parts2) > maxLen {
+		maxLen = len(parts2)
+	}
+
+	// 逐级比较
+	for i := 0; i < maxLen; i++ {
+		// 获取当前级别的版本号,如果不存在则为0
+		num1 := 0
+		if i < len(parts1) {
+			num1, _ = strconv.Atoi(parts1[i])
+		}
+
+		num2 := 0
+		if i < len(parts2) {
+			num2, _ = strconv.Atoi(parts2[i])
+		}
+
+		// 比较当前级别
+		if num1 < num2 {
+			return -1
+		} else if num1 > num2 {
+			return 1
+		}
+		// 如果相等,继续比较下一级
+	}
+
+	return 0
+}

+ 29 - 0
app/service/type.go

@@ -0,0 +1,29 @@
+/**
+ * @author chengliang
+ * @date 2026/1/12 21:16
+ * @brief
+ *
+ **/
+
+package service
+
+type TGetVersionReq struct {
+	Proj string `form:"proj" binding:"required" json:"proj"`
+	Os   string `form:"os" binding:"required" json:"os"`
+}
+
+type TGetVersionRsp struct {
+	Version           string `json:"version"`
+	PackageUrl        string `json:"packageUrl"`
+	RemoteManifestUrl string `json:"remoteManifestUrl"`
+	RemoteVersionUrl  string `json:"remoteVersionUrl"`
+}
+
+type TAddVersionReq struct {
+	Proj              string `form:"proj" binding:"required" json:"proj"`
+	Os                string `form:"os" binding:"required" json:"os"`
+	Version           string `form:"version" binding:"required" json:"version"`
+	PackageUrl        string `form:"packageUrl" binding:"required" json:"packageUrl"`
+	RemoteManifestUrl string `form:"remoteManifestUrl" binding:"required" json:"remoteManifestUrl"`
+	RemoteVersionUrl  string `form:"remoteVersionUrl" binding:"required" json:"remoteVersionUrl"`
+}

+ 3 - 0
main-admin/main.go

@@ -2,6 +2,7 @@ package main
 
 import (
 	"dsbqj-admin/app/router"
+	"dsbqj-admin/app/service"
 	"dsbqj-admin/app/task"
 	"dsbqj-admin/model/mongo"
 	"dsbqj-admin/pkg/logger"
@@ -47,6 +48,8 @@ func main() {
 
 	// 启动任务
 	task.Run(task.VersionInit(), 5*time.Minute)
+	// 初始化热更版本
+	service.GetTHotUpdateVerManager().Init()
 
 	if os.Getenv("GIN_MODE") == "release" {
 		gin.SetMode(gin.ReleaseMode)

+ 142 - 0
model/mongo/hotupdate/hotupdate.go

@@ -0,0 +1,142 @@
+/**
+ * @author chengliang
+ * @date 2026/1/13 10:54
+ * @brief
+ *
+ **/
+
+package hotupdate
+
+import (
+	"context"
+	"fmt"
+	"time"
+
+	"github.com/kamva/mgm/v3"
+	"go.mongodb.org/mongo-driver/bson"
+	"go.mongodb.org/mongo-driver/bson/primitive"
+	"go.mongodb.org/mongo-driver/mongo/options"
+)
+
+// VersionService 版本信息服务
+type TVersionDB struct {
+	collection *mgm.Collection
+}
+
+func NewVersionService() *TVersionDB {
+	return &TVersionDB{
+		collection: mgm.Coll(&MVersionInfo{}),
+	}
+}
+
+// Create 创建版本
+func (s *TVersionDB) Create(version *MVersionInfo) error {
+	// 检查是否存在相同版本
+	existing, err := s.FindOne(bson.M{
+		"project": version.Proj,
+		"os":      version.Os,
+		"version": version.Version,
+	})
+
+	if err == nil && existing != nil {
+		return fmt.Errorf("版本 %s-%s-%s 已存在", version.Proj, version.Os, version.Version)
+	}
+
+	// 插入新版本
+	return s.collection.Create(version)
+}
+
+// Update 更新版本
+func (s *TVersionDB) Update(id string, updates map[string]interface{}) (*MVersionInfo, error) {
+	objectID, err := primitive.ObjectIDFromHex(id)
+	if err != nil {
+		return nil, fmt.Errorf("无效ID: %w", err)
+	}
+
+	// 查找并更新
+	filter := bson.M{"_id": objectID}
+	update := bson.M{"$set": updates}
+
+	opts := options.FindOneAndUpdate().
+		SetReturnDocument(options.After)
+
+	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+	defer cancel()
+
+	var result MVersionInfo
+	err = s.collection.FindOneAndUpdate(ctx, filter, update, opts).Decode(&result)
+	if err != nil {
+		return nil, fmt.Errorf("更新失败: %w", err)
+	}
+
+	return &result, nil
+}
+
+// Delete 删除版本
+func (s *TVersionDB) Delete(id string) error {
+	objectID, err := primitive.ObjectIDFromHex(id)
+	if err != nil {
+		return fmt.Errorf("无效ID: %w", err)
+	}
+
+	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+	defer cancel()
+
+	result, err := s.collection.DeleteOne(ctx, bson.M{"_id": objectID})
+	if err != nil {
+		return fmt.Errorf("删除失败: %w", err)
+	}
+
+	if result.DeletedCount == 0 {
+		return fmt.Errorf("未找到记录")
+	}
+
+	return nil
+}
+
+// FindOne 查找单个版本
+func (s *TVersionDB) FindOne(filter bson.M) (*MVersionInfo, error) {
+	var result MVersionInfo
+	err := s.collection.First(filter, &result)
+	if err != nil {
+		return nil, err
+	}
+	return &result, nil
+}
+
+// Find 查找多个版本
+func (s *TVersionDB) Find(filter bson.M, opts ...*options.FindOptions) ([]MVersionInfo, error) {
+	var results []MVersionInfo
+	err := s.collection.SimpleFind(&results, filter, opts...)
+	return results, err
+}
+
+// ListByProjectAndOS 根据项目和系统列出版本
+func (s *TVersionDB) ListByProjectAndOS(project, os string, page, size int64) ([]MVersionInfo, int64, error) {
+	filter := bson.M{"project": project}
+	if os != "" {
+		filter["os"] = os
+	}
+
+	// 获取总数
+	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+	defer cancel()
+
+	total, err := s.collection.CountDocuments(ctx, filter)
+	if err != nil {
+		return nil, 0, err
+	}
+
+	// 分页查询
+	opts := options.Find().
+		SetSort(bson.M{"created_at": -1}). // 按版本降序
+		SetSkip((page - 1) * size).
+		SetLimit(size)
+
+	results, err := s.Find(filter, opts)
+	if err != nil {
+		return nil, 0, err
+	}
+
+	return results, total, nil
+}

+ 27 - 0
model/mongo/hotupdate/type.go

@@ -0,0 +1,27 @@
+/**
+ * @author chengliang
+ * @date 2026/1/13 14:13
+ * @brief
+ *
+ **/
+
+package hotupdate
+
+import "github.com/kamva/mgm/v3"
+
+// MVersionInfo
+type MVersionInfo struct {
+	mgm.DefaultModel  `bson:",inline"`
+	Proj              string `json:"project" bson:"project"`
+	Os                string `json:"os" bson:"os"`
+	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"`
+}
+
+// 定义集合名称
+func (model *MVersionInfo) CollectionName() string {
+	return "version_infos" // 你的集合名称
+}