feat: 新增禾圃集应用版本更新管理功能

新增禾圃集应用(appCode=garden)的版本策略管理功能,包括后端接口、前端API、页面路由、数据库迁移及相关常量配置,支持对不同平台和渠道的版本策略进行增删改查操作,与现有讯工捷使(appCode=im)版本管理功能保持一致的架构和交互逻辑。
This commit is contained in:
Austin
2026-10-12 15:31:11 +08:00
parent 315d97895b
commit 2c5995073f
15 changed files with 526 additions and 28 deletions
@@ -8,6 +8,7 @@ import (
func init() {
dict.RegisterEnums("ImAppPlatformOptions", "IM客户端平台选项", ImAppPlatformOptions)
dict.RegisterEnums("ImAppUpdateTypeOptions", "IM客户端更新类型选项", ImAppUpdateTypeOptions)
dict.RegisterEnums("ImAppCodeOptions", "IM客户端应用标识选项", ImAppCodeOptions)
dict.RegisterEnums("ImAbuseTargetTypeOptions", "IM举报对象类型选项", ImAbuseTargetTypeOptions)
dict.RegisterEnums("ImAbuseReasonEnabledOptions", "IM举报原因启用状态", ImAbuseReasonEnabledOptions)
}
@@ -17,6 +18,11 @@ const (
ImAppPlatformAndroid = "android"
)
const (
ImAppCodeIm = "im"
ImAppCodeGarden = "garden"
)
const (
ImAppUpdateTypeNone = "none"
ImAppUpdateTypeRecommended = "recommended"
@@ -34,6 +40,11 @@ var ImAppPlatformOptions = []*model.Option{
dict.GenSuccessOption(ImAppPlatformAndroid, "Android"),
}
var ImAppCodeOptions = []*model.Option{
dict.GenPrimaryOption(ImAppCodeIm, "讯工捷使"),
dict.GenSuccessOption(ImAppCodeGarden, "禾圃集"),
}
var ImAppUpdateTypeOptions = []*model.Option{
dict.GenDefaultOption(ImAppUpdateTypeNone, "无需更新"),
dict.GenWarningOption(ImAppUpdateTypeRecommended, "建议更新"),
@@ -0,0 +1,31 @@
package sys
import (
"context"
"hotgo/api/admin/cultivateapprelease"
"hotgo/internal/model/input/sysin"
"hotgo/internal/service"
)
var CultivateAppRelease = cCultivateAppRelease{}
type cCultivateAppRelease struct{}
func (c *cCultivateAppRelease) List(ctx context.Context, req *cultivateapprelease.ListReq) (res *cultivateapprelease.ListRes, err error) {
list, totalCount, err := service.SysImAppRelease().List(ctx, &req.ImAppReleaseListInp)
if err != nil {
return
}
if list == nil {
list = []*sysin.ImAppReleaseModel{}
}
res = new(cultivateapprelease.ListRes)
res.List = list
res.PageRes.Pack(req, totalCount)
return
}
func (c *cCultivateAppRelease) Edit(ctx context.Context, req *cultivateapprelease.EditReq) (res *cultivateapprelease.EditRes, err error) {
err = service.SysImAppRelease().Edit(ctx, &req.ImAppReleaseEditInp)
return
}
@@ -468,8 +468,15 @@ func (c *cConfig) TenantLoginIPWhitelist(ctx context.Context, req *api.TenantLog
}
func (c *cConfig) AppReleaseLatest(ctx context.Context, req *api.AppReleaseLatestReq) (res *api.AppReleaseLatestRes, err error) {
appCode := strings.ToLower(strings.TrimSpace(req.AppCode))
platform := strings.ToLower(strings.TrimSpace(req.Platform))
channel := strings.ToLower(strings.TrimSpace(req.Channel))
if appCode == "" {
appCode = "im"
}
if appCode != "im" && appCode != "garden" {
return nil, gerror.New("应用标识不合法")
}
if platform != "ios" && platform != "android" {
return nil, gerror.New("客户端平台不合法")
}
@@ -481,6 +488,7 @@ func (c *cConfig) AppReleaseLatest(ctx context.Context, req *api.AppReleaseLates
}
var row struct {
ID int64 `json:"id"`
AppCode string `json:"app_code"`
Platform string `json:"platform"`
Channel string `json:"channel"`
LatestVersion string `json:"latest_version"`
@@ -496,6 +504,7 @@ func (c *cConfig) AppReleaseLatest(ctx context.Context, req *api.AppReleaseLates
UpdatedAt *gtime.Time `json:"updated_at"`
}
err = imtenant.RegistryDB().Model("im_app_releases").Safe().Ctx(ctx).
Where("app_code", appCode).
Where("platform", platform).Where("channel", channel).Limit(1).Scan(&row)
if err != nil {
return nil, gerror.Wrap(err, "获取IM客户端版本策略失败")
@@ -504,7 +513,7 @@ func (c *cConfig) AppReleaseLatest(ctx context.Context, req *api.AppReleaseLates
return nil, gerror.New("IM客户端版本策略不存在")
}
return &api.AppReleaseLatestRes{
ID: row.ID, Platform: row.Platform, Channel: row.Channel,
ID: row.ID, AppCode: row.AppCode, Platform: row.Platform, Channel: row.Channel,
LatestVersion: row.LatestVersion, MinSupportedVersion: row.MinSupportedVersion,
UpdateType: row.UpdateType, ReleaseNotes: row.ReleaseNotes, StoreURL: row.StoreURL,
PackageURL: row.PackageURL, Checksum: row.Checksum, RemindAfterSeconds: row.RemindAfterSeconds,
+40 -21
View File
@@ -36,13 +36,16 @@ func (s *sSysImAppRelease) List(ctx context.Context, in *sysin.ImAppReleaseListI
return
}
mod := s.model(ctx, db)
if in.AppCode != "" {
mod = mod.Where("app_code", in.AppCode)
}
if in.Platform != "" {
mod = mod.Where("platform", in.Platform)
}
if in.Channel != "" {
mod = mod.WhereLike("channel", "%"+in.Channel+"%")
}
mod = mod.Page(in.Page, in.PerPage).OrderAsc("platform").OrderAsc("channel")
mod = mod.Page(in.Page, in.PerPage).OrderAsc("app_code").OrderAsc("platform").OrderAsc("channel")
if err = mod.ScanAndCount(&list, &totalCount, false); err != nil {
err = gerror.Wrap(err, "获取IM客户端版本策略失败,请稍后重试!")
return
@@ -59,6 +62,7 @@ func (s *sSysImAppRelease) Edit(ctx context.Context, in *sysin.ImAppReleaseEditI
return
}
data := g.Map{
"app_code": in.AppCode,
"platform": in.Platform,
"channel": in.Channel,
"latest_version": in.LatestVersion,
@@ -85,18 +89,18 @@ func (s *sSysImAppRelease) Edit(ctx context.Context, in *sysin.ImAppReleaseEditI
return gerror.Wrap(err, "新增IM客户端版本策略失败,请稍后重试!")
}
idValue, err := db.Model(imAppReleasesTable).Safe().Ctx(ctx).Fields("id").
Where("platform", in.Platform).Where("channel", in.Channel).Value()
Where("app_code", in.AppCode).Where("platform", in.Platform).Where("channel", in.Channel).Value()
if err != nil {
return gerror.Wrap(err, "获取IM客户端版本策略ID失败,请稍后重试!")
}
targetId = idValue.Int64()
}
_ = writeImAdminAudit(ctx, 0, action, "im_app_release", targetId, "success", g.Map{
"platform": in.Platform, "channel": in.Channel, "update_type": in.UpdateType,
"app_code": in.AppCode, "platform": in.Platform, "channel": in.Channel, "update_type": in.UpdateType,
})
if err = imkafka.PublishConfigEvent(ctx, imkafka.ConfigEventInput{
TenantCode: "*", EventType: "app.release.updated", Group: "app_release",
Keys: []string{in.Platform, in.Channel}, BizID: in.Platform + ":" + in.Channel,
Keys: []string{in.AppCode, in.Platform, in.Channel}, BizID: in.AppCode + ":" + in.Platform + ":" + in.Channel,
Actor: &imkafka.Actor{Type: "admin", ID: fmt.Sprint(contexts.GetUserId(ctx))},
CreatedBy: contexts.GetUserId(ctx),
}); err != nil {
@@ -108,6 +112,7 @@ func (s *sSysImAppRelease) Edit(ctx context.Context, in *sysin.ImAppReleaseEditI
func (s *sSysImAppRelease) model(ctx context.Context, db gdb.DB) *gdb.Model {
return db.Model(imAppReleasesTable).Safe().Ctx(ctx).Fields(
"id",
"app_code",
"platform",
"channel",
"latest_version",
@@ -125,28 +130,42 @@ func (s *sSysImAppRelease) model(ctx context.Context, db gdb.DB) *gdb.Model {
}
func (s *sSysImAppRelease) ensureDefaults(ctx context.Context, db gdb.DB) (err error) {
apps := []string{consts.ImAppCodeIm, consts.ImAppCodeGarden}
defaults := []g.Map{
{"platform": consts.ImAppPlatformIos, "channel": "stable", "latest_version": "1.0.0", "min_supported_version": "1.0.0", "update_type": consts.ImAppUpdateTypeNone, "release_notes": "当前已是最新版本。", "remind_after_seconds": 0},
{"platform": consts.ImAppPlatformAndroid, "channel": "stable", "latest_version": "1.0.0", "min_supported_version": "1.0.0", "update_type": consts.ImAppUpdateTypeNone, "release_notes": "当前已是最新版本。", "remind_after_seconds": 0},
}
now := gtime.Now()
for _, item := range defaults {
var count int
if count, err = db.Model(imAppReleasesTable).Safe().Ctx(ctx).
Where("platform", item["platform"]).Where("channel", item["channel"]).Count(); err != nil {
return gerror.Wrap(err, "检查IM客户端默认版本策略失败,请稍后重试!")
}
if count > 0 {
continue
}
item["store_url"] = ""
item["package_url"] = ""
item["checksum"] = ""
item["published_at"] = now
item["created_at"] = now
item["updated_at"] = now
if _, err = db.Model(imAppReleasesTable).Safe().Ctx(ctx).Data(item).Insert(); err != nil {
return gerror.Wrap(err, "初始化IM客户端默认版本策略失败,请稍后重试!")
for _, appCode := range apps {
for _, item := range defaults {
var count int
if count, err = db.Model(imAppReleasesTable).Safe().Ctx(ctx).
Where("app_code", appCode).
Where("platform", item["platform"]).Where("channel", item["channel"]).Count(); err != nil {
return gerror.Wrap(err, "检查IM客户端默认版本策略失败,请稍后重试!")
}
if count > 0 {
continue
}
row := g.Map{
"app_code": appCode,
"platform": item["platform"],
"channel": item["channel"],
"latest_version": item["latest_version"],
"min_supported_version": item["min_supported_version"],
"update_type": item["update_type"],
"release_notes": item["release_notes"],
"remind_after_seconds": item["remind_after_seconds"],
"store_url": "",
"package_url": "",
"checksum": "",
"published_at": now,
"created_at": now,
"updated_at": now,
}
if _, err = db.Model(imAppReleasesTable).Safe().Ctx(ctx).Data(row).Insert(); err != nil {
return gerror.Wrap(err, "初始化IM客户端默认版本策略失败,请稍后重试!")
}
}
}
return nil
@@ -16,13 +16,18 @@ import (
type ImAppReleaseListInp struct {
form.PageReq
AppCode string `json:"appCode" dc:"应用标识"`
Platform string `json:"platform" dc:"平台"`
Channel string `json:"channel" dc:"渠道"`
}
func (in *ImAppReleaseListInp) Filter(ctx context.Context) (err error) {
in.AppCode = strings.TrimSpace(in.AppCode)
in.Platform = strings.TrimSpace(in.Platform)
in.Channel = strings.TrimSpace(in.Channel)
if in.AppCode != "" && !dict.HasOptionKey(consts.ImAppCodeOptions, in.AppCode) {
return gerror.New("应用标识不合法")
}
if in.Platform != "" && !dict.HasOptionKey(consts.ImAppPlatformOptions, in.Platform) {
return gerror.New("客户端平台不合法")
}
@@ -31,6 +36,7 @@ func (in *ImAppReleaseListInp) Filter(ctx context.Context) (err error) {
type ImAppReleaseModel struct {
Id int64 `json:"id" dc:"ID"`
AppCode string `json:"appCode" dc:"应用标识"`
Platform string `json:"platform" dc:"平台"`
Channel string `json:"channel" dc:"渠道"`
LatestVersion string `json:"latestVersion" dc:"最新版本"`
@@ -48,6 +54,7 @@ type ImAppReleaseModel struct {
type ImAppReleaseEditInp struct {
Id int64 `json:"id" dc:"ID"`
AppCode string `json:"appCode" dc:"应用标识"`
Platform string `json:"platform" v:"required#平台不能为空" dc:"平台"`
Channel string `json:"channel" v:"required#渠道不能为空" dc:"渠道"`
LatestVersion string `json:"latestVersion" v:"required#最新版本不能为空" dc:"最新版本"`
@@ -61,6 +68,7 @@ type ImAppReleaseEditInp struct {
}
func (in *ImAppReleaseEditInp) Filter(ctx context.Context) (err error) {
in.AppCode = strings.TrimSpace(in.AppCode)
in.Platform = strings.TrimSpace(in.Platform)
in.Channel = strings.TrimSpace(in.Channel)
in.LatestVersion = strings.TrimSpace(in.LatestVersion)
@@ -69,6 +77,12 @@ func (in *ImAppReleaseEditInp) Filter(ctx context.Context) (err error) {
in.StoreUrl = strings.TrimSpace(in.StoreUrl)
in.PackageUrl = strings.TrimSpace(in.PackageUrl)
in.Checksum = strings.TrimSpace(in.Checksum)
if in.AppCode == "" {
in.AppCode = consts.ImAppCodeIm
}
if !dict.HasOptionKey(consts.ImAppCodeOptions, in.AppCode) {
return gerror.New("应用标识不合法")
}
if !dict.HasOptionKey(consts.ImAppPlatformOptions, in.Platform) {
return gerror.New("客户端平台不合法")
}
@@ -0,0 +1,7 @@
package genrouter
import "hotgo/internal/controller/admin/sys"
func init() {
LoginRequiredRouter = append(LoginRequiredRouter, sys.CultivateAppRelease) // 养成版本更新
}