diff --git a/server/api/admin/cultivateapprelease/cultivate_app_release.go b/server/api/admin/cultivateapprelease/cultivate_app_release.go new file mode 100644 index 0000000..69e96fe --- /dev/null +++ b/server/api/admin/cultivateapprelease/cultivate_app_release.go @@ -0,0 +1,25 @@ +package cultivateapprelease + +import ( + "hotgo/internal/model/input/form" + "hotgo/internal/model/input/sysin" + + "github.com/gogf/gf/v2/frame/g" +) + +type ListReq struct { + g.Meta `path:"/cultivate/app-release/list" method:"get" tags:"养成版本更新" summary:"获取养成客户端版本策略列表"` + sysin.ImAppReleaseListInp +} + +type ListRes struct { + form.PageRes + List []*sysin.ImAppReleaseModel `json:"list" dc:"数据列表"` +} + +type EditReq struct { + g.Meta `path:"/cultivate/app-release/edit" method:"post" tags:"养成版本更新" summary:"新增/编辑养成客户端版本策略"` + sysin.ImAppReleaseEditInp +} + +type EditRes struct{} diff --git a/server/api/admin/imconfiginternal/im_config.go b/server/api/admin/imconfiginternal/im_config.go index 15f55f8..d0cc12a 100644 --- a/server/api/admin/imconfiginternal/im_config.go +++ b/server/api/admin/imconfiginternal/im_config.go @@ -223,12 +223,14 @@ type TenantLoginIPWhitelistRes struct { type AppReleaseLatestReq struct { g.Meta `path:"/internal/im-config/app/releases/latest" method:"get" tags:"IM内部配置" summary:"获取客户端最新版本策略"` + AppCode string `json:"app_code" dc:"应用标识,缺省 im"` Platform string `json:"platform" v:"required#平台不能为空" dc:"平台"` Channel string `json:"channel" dc:"渠道"` } type AppReleaseLatestRes struct { ID int64 `json:"id"` + AppCode string `json:"app_code"` Platform string `json:"platform"` Channel string `json:"channel"` LatestVersion string `json:"latest_version"` diff --git a/server/internal/consts/im_product_config.go b/server/internal/consts/im_product_config.go index a8503ec..3c6b397 100644 --- a/server/internal/consts/im_product_config.go +++ b/server/internal/consts/im_product_config.go @@ -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, "建议更新"), diff --git a/server/internal/controller/admin/sys/cultivate_app_release.go b/server/internal/controller/admin/sys/cultivate_app_release.go new file mode 100644 index 0000000..3c5774a --- /dev/null +++ b/server/internal/controller/admin/sys/cultivate_app_release.go @@ -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 +} diff --git a/server/internal/controller/internalapi/imconfig/im_config.go b/server/internal/controller/internalapi/imconfig/im_config.go index 8e803d8..bae1c9f 100644 --- a/server/internal/controller/internalapi/imconfig/im_config.go +++ b/server/internal/controller/internalapi/imconfig/im_config.go @@ -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, diff --git a/server/internal/logic/sys/im_app_release.go b/server/internal/logic/sys/im_app_release.go index 85c95b0..0dafae9 100644 --- a/server/internal/logic/sys/im_app_release.go +++ b/server/internal/logic/sys/im_app_release.go @@ -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 diff --git a/server/internal/model/input/sysin/im_app_release.go b/server/internal/model/input/sysin/im_app_release.go index 9147a8f..a3b203a 100644 --- a/server/internal/model/input/sysin/im_app_release.go +++ b/server/internal/model/input/sysin/im_app_release.go @@ -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("客户端平台不合法") } diff --git a/server/internal/router/genrouter/cultivate_app_release.go b/server/internal/router/genrouter/cultivate_app_release.go new file mode 100644 index 0000000..fe6aa64 --- /dev/null +++ b/server/internal/router/genrouter/cultivate_app_release.go @@ -0,0 +1,7 @@ +package genrouter + +import "hotgo/internal/controller/admin/sys" + +func init() { + LoginRequiredRouter = append(LoginRequiredRouter, sys.CultivateAppRelease) // 养成版本更新 +} diff --git a/server/storage/data/generate/cultivate_app_release_menu.sql b/server/storage/data/generate/cultivate_app_release_menu.sql new file mode 100644 index 0000000..fc0bdf4 --- /dev/null +++ b/server/storage/data/generate/cultivate_app_release_menu.sql @@ -0,0 +1,12 @@ +SET @now := NOW(); +SET @cultivateRootId := (SELECT `id` FROM `hg_admin_menu` WHERE `name` = 'cultivateManager' LIMIT 1); + +INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`) +SELECT NULL, @cultivateRootId, '版本更新', 'cultivateAppReleaseIndex', 'app-release', '', '2', '', '/cultivate/app-release/list', '', '/cultivate/appRelease/index', '1', 'cultivateAppReleaseIndex', '0', '0', '', '0', '0', '0', '2', CONCAT('tr_', @cultivateRootId, ' '), '50', '禾圃集应用版本策略(app_code=garden)', '1', @now, @now +WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'cultivateAppReleaseIndex'); + +SET @cultivateAppReleaseId := (SELECT `id` FROM `hg_admin_menu` WHERE `name` = 'cultivateAppReleaseIndex' LIMIT 1); + +INSERT INTO `hg_admin_menu` (`id`, `pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`, `permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`, `frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`, `status`, `created_at`, `updated_at`) +SELECT NULL, @cultivateAppReleaseId, '版本编辑', 'cultivateAppReleaseEdit', '', '', '3', '', '/cultivate/app-release/edit', '', '', '1', '', '0', '0', '', '0', '0', '0', '3', CONCAT('tr_', @cultivateRootId, ' tr_', @cultivateAppReleaseId, ' '), '10', '', '1', @now, @now +WHERE NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'cultivateAppReleaseEdit'); diff --git a/server/storage/data/generate/im_app_release_app_code_migration.sql b/server/storage/data/generate/im_app_release_app_code_migration.sql new file mode 100644 index 0000000..ae1206f --- /dev/null +++ b/server/storage/data/generate/im_app_release_app_code_migration.sql @@ -0,0 +1,26 @@ +-- 存量库升级:im_app_releases 增加 app_code 维度(im=讯工捷使, garden=禾圃集) +-- 幂等:已存在 app_code 列时跳过 ALTER(通过 information_schema 判断由执行方保证) + +ALTER TABLE `im_app_releases` + ADD COLUMN `app_code` VARCHAR(32) NOT NULL DEFAULT 'im' AFTER `id`; + +ALTER TABLE `im_app_releases` + DROP INDEX `uk_im_app_releases_platform_channel`, + ADD UNIQUE KEY `uk_im_app_releases_app_platform_channel` (`app_code`, `platform`, `channel`); + +INSERT INTO `im_app_releases` + (`app_code`, `platform`, `channel`, `latest_version`, `min_supported_version`, `update_type`, `release_notes`) +VALUES + ('garden', 'ios', 'stable', '1.0.0', '1.0.0', 'none', '当前已是最新版本。'), + ('garden', 'android', 'stable', '1.0.0', '1.0.0', 'none', '当前已是最新版本。') +ON DUPLICATE KEY UPDATE `platform` = VALUES(`platform`); + +-- 菜单 +SOURCE cultivate_app_release_menu.sql; + +-- 超管角色绑定新菜单(role_id 按实际环境调整) +SET @cultivateAppReleaseMenuId := (SELECT `id` FROM `hg_admin_menu` WHERE `name` = 'cultivateAppReleaseIndex' LIMIT 1); +SET @cultivateAppReleaseEditId := (SELECT `id` FROM `hg_admin_menu` WHERE `name` = 'cultivateAppReleaseEdit' LIMIT 1); +INSERT IGNORE INTO `hg_admin_role_menu` (`role_id`, `menu_id`) +SELECT r.`id`, m.`menu_id` FROM (SELECT 1 AS id) r +CROSS JOIN (SELECT @cultivateAppReleaseMenuId AS menu_id UNION SELECT @cultivateAppReleaseEditId) m; diff --git a/server/storage/data/generate/im_app_releases_global.sql b/server/storage/data/generate/im_app_releases_global.sql index bab3f58..f88bcab 100644 --- a/server/storage/data/generate/im_app_releases_global.sql +++ b/server/storage/data/generate/im_app_releases_global.sql @@ -1,6 +1,8 @@ -- Global MeetingIM client release policy. This table belongs to hotgo and is not tenant-scoped. +-- app_code: im=讯工捷使主应用, garden=禾圃集(后续新增 app 在此扩展) CREATE TABLE IF NOT EXISTS `im_app_releases` ( `id` BIGINT NOT NULL AUTO_INCREMENT, + `app_code` VARCHAR(32) NOT NULL DEFAULT 'im', `platform` VARCHAR(32) NOT NULL, `channel` VARCHAR(32) NOT NULL DEFAULT 'stable', `latest_version` VARCHAR(64) NOT NULL, @@ -15,12 +17,14 @@ CREATE TABLE IF NOT EXISTS `im_app_releases` ( `created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), `updated_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3), PRIMARY KEY (`id`), - UNIQUE KEY `uk_im_app_releases_platform_channel` (`platform`, `channel`) + UNIQUE KEY `uk_im_app_releases_app_platform_channel` (`app_code`, `platform`, `channel`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='MeetingIM全局客户端版本策略'; INSERT INTO `im_app_releases` - (`platform`, `channel`, `latest_version`, `min_supported_version`, `update_type`, `release_notes`) + (`app_code`, `platform`, `channel`, `latest_version`, `min_supported_version`, `update_type`, `release_notes`) VALUES - ('ios', 'stable', '1.0.0', '1.0.0', 'none', '当前已是最新版本。'), - ('android', 'stable', '1.0.0', '1.0.0', 'none', '当前已是最新版本。') + ('im', 'ios', 'stable', '1.0.0', '1.0.0', 'none', '当前已是最新版本。'), + ('im', 'android', 'stable', '1.0.0', '1.0.0', 'none', '当前已是最新版本。'), + ('garden', 'ios', 'stable', '1.0.0', '1.0.0', 'none', '当前已是最新版本。'), + ('garden', 'android', 'stable', '1.0.0', '1.0.0', 'none', '当前已是最新版本。') ON DUPLICATE KEY UPDATE `platform` = VALUES(`platform`); diff --git a/web/src/api/cultivate/appRelease.ts b/web/src/api/cultivate/appRelease.ts new file mode 100644 index 0000000..c6c799b --- /dev/null +++ b/web/src/api/cultivate/appRelease.ts @@ -0,0 +1,10 @@ +import { http } from '@/utils/http/axios'; + +// 本页仅管理禾圃集应用(appCode=garden)的版本策略 +export function List(params) { + return http.request({ url: '/cultivate/app-release/list', method: 'GET', params: { ...params, appCode: 'garden' } }); +} + +export function Edit(params) { + return http.request({ url: '/cultivate/app-release/edit', method: 'POST', params: { ...params, appCode: 'garden' } }); +} diff --git a/web/src/api/imAppRelease/index.ts b/web/src/api/imAppRelease/index.ts index a77503b..8269a49 100644 --- a/web/src/api/imAppRelease/index.ts +++ b/web/src/api/imAppRelease/index.ts @@ -1,9 +1,10 @@ import { http } from '@/utils/http/axios'; +// 本页仅管理讯工捷使主应用(appCode=im)的版本策略 export function List(params) { - return http.request({ url: '/imAppRelease/list', method: 'GET', params }); + return http.request({ url: '/imAppRelease/list', method: 'GET', params: { ...params, appCode: 'im' } }); } export function Edit(params) { - return http.request({ url: '/imAppRelease/edit', method: 'POST', params }); + return http.request({ url: '/imAppRelease/edit', method: 'POST', params: { ...params, appCode: 'im' } }); } diff --git a/web/src/views/cultivate/appRelease/index.vue b/web/src/views/cultivate/appRelease/index.vue new file mode 100644 index 0000000..b693547 --- /dev/null +++ b/web/src/views/cultivate/appRelease/index.vue @@ -0,0 +1,215 @@ + + + diff --git a/web/src/views/cultivate/appRelease/model.ts b/web/src/views/cultivate/appRelease/model.ts new file mode 100644 index 0000000..a4cee45 --- /dev/null +++ b/web/src/views/cultivate/appRelease/model.ts @@ -0,0 +1,112 @@ +import { ref } from 'vue'; +import { cloneDeep } from 'lodash-es'; +import { FormSchema } from '@/components/Form'; +import { useDictStore } from '@/store/modules/dict'; + +const dict = useDictStore(); + +export class State { + public id = 0; + public platform = 'android'; + public channel = 'stable'; + public latestVersion = '1.0.0'; + public minSupportedVersion = '1.0.0'; + public updateType = 'none'; + public releaseNotes = ''; + public storeUrl = ''; + public packageUrl = ''; + public checksum = ''; + public remindAfterSeconds = 0; + public publishedAt = ''; + public updatedAt = ''; + + constructor(state?: Partial) { + if (state) Object.assign(this, state); + } +} + +export function newState(state: State | Record | null): State { + if (state !== null) { + if (state instanceof State) return cloneDeep(state); + return new State(state); + } + return new State(); +} + +export const schemas = ref([ + { + field: 'platform', + component: 'NSelect', + label: '平台', + componentProps: { + clearable: true, + options: dict.getOption('ImAppPlatformOptions'), + placeholder: '请选择平台', + }, + }, + { + field: 'channel', + component: 'NInput', + label: '渠道', + componentProps: { + clearable: true, + placeholder: '请输入渠道', + }, + }, +]); + +export const columns = [ + { + title: '平台', + key: 'platform', + align: 'left', + width: 100, + render(row: State) { + return dict.getLabel('ImAppPlatformOptions', row.platform) || row.platform; + }, + }, + { title: '渠道', key: 'channel', align: 'left', width: 100 }, + { title: '最新版本', key: 'latestVersion', align: 'left', width: 120 }, + { title: '最低支持', key: 'minSupportedVersion', align: 'left', width: 120 }, + { + title: '更新类型', + key: 'updateType', + align: 'left', + width: 110, + render(row: State) { + return dict.getLabel('ImAppUpdateTypeOptions', row.updateType) || row.updateType; + }, + }, + { title: '再次提醒', key: 'remindAfterSeconds', align: 'left', width: 110 }, + { title: '商店地址', key: 'storeUrl', align: 'left', width: 220 }, + { title: '安装包地址', key: 'packageUrl', align: 'left', width: 220 }, + { title: '发布时间', key: 'publishedAt', align: 'left', width: 180 }, + { title: '更新时间', key: 'updatedAt', align: 'left', width: 180 }, +]; + +export const rules = { + platform: { required: true, trigger: ['blur', 'change'], type: 'string', message: '请选择平台' }, + channel: { required: true, trigger: ['blur', 'input'], type: 'string', message: '请输入渠道' }, + latestVersion: { + required: true, + trigger: ['blur', 'input'], + type: 'string', + message: '请输入最新版本', + }, + minSupportedVersion: { + required: true, + trigger: ['blur', 'input'], + type: 'string', + message: '请输入最低支持版本', + }, + updateType: { + required: true, + trigger: ['blur', 'change'], + type: 'string', + message: '请选择更新类型', + }, +}; + +export function loadOptions() { + dict.loadOptions(['ImAppPlatformOptions', 'ImAppUpdateTypeOptions']); +}