增加包含sdk包的区分

This commit is contained in:
hahwu 2026-05-07 15:34:50 +08:00
parent 1bca99d539
commit fa9b8df0c3
2 changed files with 251 additions and 26 deletions

View File

@ -22,13 +22,16 @@ const (
apkUploadTokenEnvName = "APK_UPLOAD_TOKEN"
uploadedAtTimeFormat = time.RFC3339
defaultApkStorageName = "current.apk"
defaultApkPackageType = "without_sdk"
)
var apkEnvironments = []string{"dev", "stable", "prod"}
var apkPackageTypes = []string{"with_sdk", "without_sdk"}
type apkPackageMeta struct {
DownloadPath string `json:"downloadPath"`
Env string `json:"env"`
PackageType string `json:"packageType"`
Exists bool `json:"exists"`
FileName string `json:"fileName"`
Size int64 `json:"size"`
@ -36,8 +39,13 @@ type apkPackageMeta struct {
Version string `json:"version"`
}
type apkEnvPackages struct {
Env string `json:"env"`
Variants map[string]apkPackageMeta `json:"variants"`
}
type apkManifest struct {
Packages map[string]apkPackageMeta `json:"packages"`
Packages map[string]apkEnvPackages `json:"packages"`
}
func UploadApkPackage(c *gin.Context) {
@ -52,6 +60,12 @@ func UploadApkPackage(c *gin.Context) {
return
}
packageType, err := parseApkPackageType(c.PostForm("packageType"))
if err != nil {
failed(c, err.Error())
return
}
fileHeader, err := c.FormFile("file")
if err != nil {
failed(c, "获取 apk 文件失败: "+err.Error())
@ -63,12 +77,12 @@ func UploadApkPackage(c *gin.Context) {
return
}
if err := os.MkdirAll(apkEnvDir(env), 0755); err != nil {
if err := os.MkdirAll(apkPackageDir(env, packageType), 0755); err != nil {
failed(c, "创建 apk 目录失败: "+err.Error())
return
}
targetPath := apkFilePath(env)
targetPath := apkFilePath(env, packageType)
if err := c.SaveUploadedFile(fileHeader, targetPath); err != nil {
failed(c, "保存 apk 文件失败: "+err.Error())
return
@ -80,8 +94,9 @@ func UploadApkPackage(c *gin.Context) {
}
meta := apkPackageMeta{
DownloadPath: apkDownloadPath(env),
DownloadPath: apkDownloadPath(env, packageType),
Env: env,
PackageType: packageType,
Exists: true,
FileName: fileHeader.Filename,
Size: fileHeader.Size,
@ -94,14 +109,16 @@ func UploadApkPackage(c *gin.Context) {
failed(c, "读取 apk 清单失败: "+err.Error())
return
}
manifest.Packages[env] = meta
envPackages := ensureEnvPackages(manifest, env)
envPackages.Variants[packageType] = meta
manifest.Packages[env] = envPackages
if err := saveApkManifest(manifest); err != nil {
failed(c, "保存 apk 清单失败: "+err.Error())
return
}
log.Printf("apk uploaded env=%s file=%s size=%d", env, fileHeader.Filename, fileHeader.Size)
log.Printf("apk uploaded env=%s packageType=%s file=%s size=%d", env, packageType, fileHeader.Filename, fileHeader.Size)
success(c, meta)
}
@ -112,15 +129,20 @@ func GetApkPackages(c *gin.Context) {
return
}
packages := make([]apkPackageMeta, 0, len(apkEnvironments))
packages := make([]apkEnvPackages, 0, len(apkEnvironments))
for _, env := range apkEnvironments {
meta := manifest.Packages[env]
if meta.Env == "" {
meta = apkPackageMeta{Env: env}
envPackages := ensureEnvPackages(manifest, env)
for _, packageType := range apkPackageTypes {
meta := envPackages.Variants[packageType]
if meta.Env == "" {
meta = apkPackageMeta{Env: env, PackageType: packageType}
}
meta.Exists = fileExists(apkFilePath(env, packageType))
meta.DownloadPath = apkDownloadPath(env, packageType)
meta.PackageType = packageType
envPackages.Variants[packageType] = meta
}
meta.Exists = fileExists(apkFilePath(env))
meta.DownloadPath = apkDownloadPath(env)
packages = append(packages, meta)
packages = append(packages, envPackages)
}
success(c, packages)
@ -132,6 +154,11 @@ func DownloadApkPackage(c *gin.Context) {
failed(c, "无效的 apk 环境")
return
}
packageType, err := parseApkPackageType(c.Query("packageType"))
if err != nil {
failed(c, err.Error())
return
}
manifest, err := loadApkManifest()
if err != nil {
@ -139,19 +166,20 @@ func DownloadApkPackage(c *gin.Context) {
return
}
targetPath := apkFilePath(env)
targetPath := apkFilePath(env, packageType)
if !fileExists(targetPath) {
failed(c, fmt.Sprintf("%s 环境暂无可下载的 apk 包", env))
failed(c, fmt.Sprintf("%s 环境暂无可下载的 %s apk 包", env, packageType))
return
}
meta := manifest.Packages[env]
envPackages := ensureEnvPackages(manifest, env)
meta := envPackages.Variants[packageType]
downloadName := meta.FileName
if strings.TrimSpace(downloadName) == "" {
downloadName = fmt.Sprintf("%s.apk", env)
downloadName = fmt.Sprintf("%s-%s.apk", env, packageType)
}
util.AddAdminLog(c, "下载客户端APK", gin.H{"env": env, "file": downloadName})
util.AddAdminLog(c, "下载客户端APK", gin.H{"env": env, "packageType": packageType, "file": downloadName})
c.FileAttachment(targetPath, downloadName)
}
@ -167,16 +195,20 @@ func apkEnvDir(env string) string {
return filepath.Join(apkStorageRoot, env)
}
func apkFilePath(env string) string {
return filepath.Join(apkEnvDir(env), defaultApkStorageName)
func apkPackageDir(env string, packageType string) string {
return filepath.Join(apkEnvDir(env), packageType)
}
func apkFilePath(env string, packageType string) string {
return filepath.Join(apkPackageDir(env, packageType), defaultApkStorageName)
}
func apkManifestPath() string {
return filepath.Join(apkStorageRoot, apkManifestName)
}
func apkDownloadPath(env string) string {
return fmt.Sprintf("/api/apk/download/%s", env)
func apkDownloadPath(env string, packageType string) string {
return fmt.Sprintf("/api/apk/download/%s?packageType=%s", env, packageType)
}
func isValidApkEnv(env string) bool {
@ -188,8 +220,50 @@ func isValidApkEnv(env string) bool {
return false
}
func parseApkPackageType(rawType string) (string, error) {
value := strings.TrimSpace(strings.ToLower(rawType))
if value == "" {
return defaultApkPackageType, nil
}
aliases := map[string]string{
"sdk": "with_sdk",
"with-sdk": "with_sdk",
"with_sdk": "with_sdk",
"withsdk": "with_sdk",
"nosdk": "without_sdk",
"no-sdk": "without_sdk",
"no_sdk": "without_sdk",
"without-sdk": "without_sdk",
"without_sdk": "without_sdk",
"withoutsdk": "without_sdk",
}
if alias, ok := aliases[value]; ok {
return alias, nil
}
for _, item := range apkPackageTypes {
if item == value {
return value, nil
}
}
return "", fmt.Errorf("无效的 apk 包类型,必须是 with_sdk 或 without_sdk")
}
func ensureEnvPackages(manifest *apkManifest, env string) apkEnvPackages {
envPackages, ok := manifest.Packages[env]
if !ok || envPackages.Env == "" {
envPackages = apkEnvPackages{Env: env, Variants: map[string]apkPackageMeta{}}
}
if envPackages.Variants == nil {
envPackages.Variants = map[string]apkPackageMeta{}
}
return envPackages
}
func loadApkManifest() (*apkManifest, error) {
manifest := &apkManifest{Packages: map[string]apkPackageMeta{}}
manifest := &apkManifest{Packages: map[string]apkEnvPackages{}}
if err := os.MkdirAll(apkStorageRoot, 0755); err != nil {
return nil, err
}
@ -206,11 +280,39 @@ func loadApkManifest() (*apkManifest, error) {
return manifest, nil
}
if err := json.Unmarshal(data, manifest); err != nil {
var raw struct {
Packages map[string]json.RawMessage `json:"packages"`
}
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
if manifest.Packages == nil {
manifest.Packages = map[string]apkPackageMeta{}
for env, rawPackage := range raw.Packages {
var envPackages apkEnvPackages
if err := json.Unmarshal(rawPackage, &envPackages); err == nil && len(envPackages.Variants) > 0 {
if envPackages.Env == "" {
envPackages.Env = env
}
for packageType, meta := range envPackages.Variants {
meta.Env = env
meta.PackageType = packageType
envPackages.Variants[packageType] = meta
}
manifest.Packages[env] = envPackages
continue
}
var legacyMeta apkPackageMeta
if err := json.Unmarshal(rawPackage, &legacyMeta); err != nil {
return nil, err
}
legacyMeta.Env = env
legacyMeta.PackageType = defaultApkPackageType
manifest.Packages[env] = apkEnvPackages{
Env: env,
Variants: map[string]apkPackageMeta{
defaultApkPackageType: legacyMeta,
},
}
}
return manifest, nil
}

View File

@ -0,0 +1,123 @@
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
[GIN-debug] POST /api/open/apk/upload --> backend/controller.UploadApkPackage (3 handlers)
[GIN-debug] POST /api/auth/login --> backend/controller.Login (3 handlers)
[GIN-debug] GET /api/auth/Codes --> backend/controller.Codes (3 handlers)
[GIN-debug] POST /api/auth/phoneCode --> backend/controller.PhoneCode (3 handlers)
[GIN-debug] POST /api/auth/phoneLogin --> backend/controller.LoginCode (3 handlers)
[GIN-debug] POST /api/feishu/sendInfo --> backend/controller.FeishuSendInfo (3 handlers)
[GIN-debug] POST /api/feishu/sendInfo2 --> backend/controller.FeishuSendInfo2 (3 handlers)
[GIN-debug] POST /api/feishu/sendWeekInfo --> backend/controller.FeishuSendWeekInfo (3 handlers)
[GIN-debug] POST /api/feishu/updateApp --> backend/controller.FeishuUpdateApp (3 handlers)
[GIN-debug] POST /api/feishu/serverInfo --> backend/controller.FeishuServerInfo (3 handlers)
[GIN-debug] POST /api/feishu/notify --> backend/controller.FeishuNotify (3 handlers)
[GIN-debug] POST /api/feishu/notify/client --> backend/controller.FeishuNotifyClient (3 handlers)
[GIN-debug] POST /api/feishu/notify/order --> backend/controller.FeishuNotifyOrder (3 handlers)
[GIN-debug] POST /api/alibaba/zabbix/notify --> backend/controller.AlibabaNotify (3 handlers)
[GIN-debug] POST /api/alibaba/zabbix/recovery --> backend/controller.AlibabaRecovery (3 handlers)
[GIN-debug] POST /api/alibaba/game/notify --> backend/controller.AlibabaGameNotify (3 handlers)
[GIN-debug] POST /api/alibaba/notify/order --> backend/controller.AlibabaNotifyOrder (3 handlers)
[GIN-debug] GET /api/v1/experiments --> backend/controller.ExperimentList (4 handlers)
[GIN-debug] POST /api/v1/experiments --> backend/controller.ExperimentCreate (4 handlers)
[GIN-debug] PUT /api/v1/experiments/:id --> backend/controller.ExperimentUpdate (4 handlers)
[GIN-debug] DELETE /api/v1/experiments/:id --> backend/controller.ExperimentDelete (4 handlers)
[GIN-debug] GET /api/v1/experiments/:id/variants --> backend/controller.ExperimentVariantList (4 handlers)
[GIN-debug] POST /api/v1/experiments/:id/variants --> backend/controller.ExperimentVariantCreate (4 handlers)
[GIN-debug] PUT /api/v1/experiments/:id/variants/:variantId --> backend/controller.ExperimentVariantUpdate (4 handlers)
[GIN-debug] DELETE /api/v1/experiments/:id/variants/:variantId --> backend/controller.ExperimentVariantDelete (4 handlers)
[GIN-debug] GET /api/v1/experiments/:id/whitelist --> backend/controller.ExperimentWhitelistList (4 handlers)
[GIN-debug] POST /api/v1/experiments/:id/whitelist --> backend/controller.ExperimentWhitelistCreate (4 handlers)
[GIN-debug] POST /api/v1/experiments/:id/whitelist/batch --> backend/controller.ExperimentWhitelistBatchCreate (4 handlers)
[GIN-debug] DELETE /api/v1/experiments/:id/whitelist/:userId --> backend/controller.ExperimentWhitelistDelete (4 handlers)
[GIN-debug] GET /api/v1/experiments/:id/results --> backend/controller.ExperimentResult (4 handlers)
[GIN-debug] GET /api/v1/users/:userId/groups --> backend/controller.UserExperimentGroups (4 handlers)
[GIN-debug] GET /api/user/info --> backend/controller.UserInfo (4 handlers)
[GIN-debug] POST /api/statistics/info --> backend/controller.StatisticsInfo (4 handlers)
[GIN-debug] POST /api/statistics/level --> backend/controller.StatisticsLevel (4 handlers)
[GIN-debug] POST /api/statistics/order --> backend/controller.StatisticsOrder (4 handlers)
[GIN-debug] POST /api/statistics/heat --> backend/controller.StatisticsHeat (4 handlers)
[GIN-debug] POST /api/admin/list --> backend/controller.AdminList (4 handlers)
[GIN-debug] POST /api/admin/add --> backend/controller.AdminAdd (4 handlers)
[GIN-debug] POST /api/admin/edit --> backend/controller.AdminEdit (4 handlers)
[GIN-debug] POST /api/admin/delete --> backend/controller.AdminDelete (4 handlers)
[GIN-debug] POST /api/admin/log/list --> backend/controller.AdminLogList (4 handlers)
[GIN-debug] POST /api/admin/config/list --> backend/controller.AdminConfigList (4 handlers)
[GIN-debug] POST /api/admin/config/add --> backend/controller.AdminConfigAdd (4 handlers)
[GIN-debug] POST /api/admin/config/edit --> backend/controller.AdminConfigEdit (4 handlers)
[GIN-debug] POST /api/admin/usergroup/list --> backend/controller.PermissionUserGroupList (4 handlers)
[GIN-debug] POST /api/admin/usergroup/add --> backend/controller.PermissionUserGroupAdd (4 handlers)
[GIN-debug] POST /api/admin/usergroup/edit --> backend/controller.PermissionUserGroupEdit (4 handlers)
[GIN-debug] POST /api/admin/usergroup/delete --> backend/controller.PermissionUserGroupDelete (4 handlers)
[GIN-debug] POST /api/admin/usergroup/role/set --> backend/controller.PermissionGroupRoleSet (4 handlers)
[GIN-debug] POST /api/admin/usergroup/role/list --> backend/controller.PermissionGroupRoleList (4 handlers)
[GIN-debug] POST /api/admin/role/list --> backend/controller.PermissionRoleList (4 handlers)
[GIN-debug] POST /api/admin/role/add --> backend/controller.PermissionRoleAdd (4 handlers)
[GIN-debug] POST /api/admin/role/edit --> backend/controller.PermissionRoleEdit (4 handlers)
[GIN-debug] POST /api/admin/role/delete --> backend/controller.PermissionRoleDelete (4 handlers)
[GIN-debug] POST /api/admin/role/permission/set --> backend/controller.PermissionRolePermissionSet (4 handlers)
[GIN-debug] POST /api/admin/role/permission/list --> backend/controller.PermissionRolePermissionList (4 handlers)
[GIN-debug] POST /api/admin/permission/list --> backend/controller.PermissionPointList (4 handlers)
[GIN-debug] POST /api/admin/permission/add --> backend/controller.PermissionPointAdd (4 handlers)
[GIN-debug] POST /api/admin/permission/edit --> backend/controller.PermissionPointEdit (4 handlers)
[GIN-debug] POST /api/admin/permission/delete --> backend/controller.PermissionPointDelete (4 handlers)
[GIN-debug] POST /api/admin/user/group/list --> backend/controller.PermissionUserGroupRelList (4 handlers)
[GIN-debug] POST /api/admin/user/group/set --> backend/controller.PermissionUserGroupRelSet (4 handlers)
[GIN-debug] POST /api/admin/user/permission/list --> backend/controller.PermissionUserPermissionList (4 handlers)
[GIN-debug] POST /api/admin/user/permission/set --> backend/controller.PermissionUserPermissionSet (4 handlers)
[GIN-debug] POST /api/admin/user/role/list --> backend/controller.PermissionUserRoleList (4 handlers)
[GIN-debug] POST /api/admin/user/role/batch-list --> backend/controller.PermissionUserRoleBatchList (4 handlers)
[GIN-debug] POST /api/log/user --> backend/controller.UserDetail (4 handlers)
[GIN-debug] POST /api/log/asset --> backend/controller.Asset (4 handlers)
[GIN-debug] POST /api/log/event --> backend/controller.Event (4 handlers)
[GIN-debug] POST /api/log/loginCountByMonth --> backend/controller.LoginCountByMonth (4 handlers)
[GIN-debug] POST /api/log/order --> backend/controller.Order (4 handlers)
[GIN-debug] POST /api/user/list --> backend/controller.UserList (4 handlers)
[GIN-debug] POST /api/user/gm --> backend/controller.UserGM (4 handlers)
[GIN-debug] POST /api/user/ban --> backend/controller.UserBan (4 handlers)
[GIN-debug] POST /api/server/list --> backend/controller.AppList (4 handlers)
[GIN-debug] POST /api/server/serverList --> backend/controller.ServerList (4 handlers)
[GIN-debug] POST /api/server/addServer --> backend/controller.AddServer (4 handlers)
[GIN-debug] POST /api/server/nodeList --> backend/controller.NodeList (4 handlers)
[GIN-debug] POST /api/server/addNode --> backend/controller.AddNode (4 handlers)
[GIN-debug] POST /api/server/editServer --> backend/controller.EditServer (4 handlers)
[GIN-debug] POST /api/server/updateApp --> backend/controller.UpdateApp (4 handlers)
[GIN-debug] POST /api/server/updateAppReview --> backend/controller.UpdateAppReview (4 handlers)
[GIN-debug] POST /api/server/updateAppFeishu --> backend/controller.UpdateAppFeishu (4 handlers)
[GIN-debug] POST /api/server/restart --> backend/controller.RestartServer (4 handlers)
[GIN-debug] POST /api/server/reload --> backend/controller.ReloadServer (4 handlers)
[GIN-debug] POST /api/activity/list --> backend/controller.ActivityList (4 handlers)
[GIN-debug] POST /api/activity/edit --> backend/controller.ActivityEdit (4 handlers)
[GIN-debug] POST /api/activity/add --> backend/controller.ActivityAdd (4 handlers)
[GIN-debug] POST /api/activity/delete --> backend/controller.ActivityDelete (4 handlers)
[GIN-debug] POST /api/activity/sync --> backend/controller.ActivitySync (4 handlers)
[GIN-debug] POST /api/mail/send --> backend/controller.SendMail (4 handlers)
[GIN-debug] POST /api/mail/list --> backend/controller.MailList (4 handlers)
[GIN-debug] POST /api/mail/delete --> backend/controller.MailDelete (4 handlers)
[GIN-debug] POST /api/operation/copyUser --> backend/controller.CopyUser (4 handlers)
[GIN-debug] GET /api/apk/packages --> backend/controller.GetApkPackages (4 handlers)
[GIN-debug] GET /api/apk/download/:env --> backend/controller.DownloadApkPackage (4 handlers)
[GIN-debug] POST /api/language/list --> backend/controller.Language (4 handlers)
[GIN-debug] POST /api/language/export --> backend/controller.LanguageExport (4 handlers)
[GIN-debug] POST /api/language/save --> backend/controller.LanguageSave (4 handlers)
[GIN-debug] POST /api/language/add --> backend/controller.LanguageAdd (4 handlers)
[GIN-debug] POST /api/language/delete --> backend/controller.LanguageDelete (4 handlers)
[GIN-debug] GET /api/config/notification --> backend/controller.NotificationConfigGet (4 handlers)
[GIN-debug] PUT /api/config/notification/update --> backend/controller.NotificationConfigSave (4 handlers)
[GIN-debug] POST /api/scripts/copywriting --> backend/controller.Copywriting (4 handlers)
[GIN-debug] POST /api/scripts/copyonline --> backend/controller.CopyOnline (4 handlers)
[GIN-debug] POST /api/scripts/clientImageGitPull --> backend/controller.ClientImageGitPull (4 handlers)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies for details.
[GIN-debug] Listening and serving HTTP on :5320
2026/05/07 15:32:59 apk uploaded env=prod packageType=without_sdk file=prod_v1.0.73_20260430_1423_656bd841f_#78.apk size=282603268
[GIN] 2026/05/07 - 15:32:59 | 200 | 6.29s | ::1 | POST "/api/open/apk/upload"
2026/05/07 15:33:00 monitor server failed, AppId=2, ServerId=0, err=method1 and method2 both failed, method1Err=ServerInfo failed: rpc error: code = Unavailable desc = connection error: desc = "transport: Error while dialing: dial tcp 112.124.54.243:0: connectex: The requested address is not valid in its context.", method2Err=failed to send admin message: failed to read from websocket: websocket: close 1006 (abnormal closure): unexpected EOF
2026/05/07 15:33:03 monitor server failed, AppId=0, ServerId=0, err=method1 and method2 both failed, method1Err=dial tcp 101.132.170.198:0: connectex: The requested address is not valid in its context., method2Err=dial tcp 101.132.170.198:0: connectex: The requested address is not valid in its context.
[GIN] 2026/05/07 - 15:33:04 | 200 | 92.07ms | ::1 | GET "/api/apk/packages"
2026/05/07 15:33:05 monitor server failed, AppId=4, ServerId=1, err=method1 and method2 both failed, method1Err=ServerInfo failed: rpc error: code = Unavailable desc = connection error: desc = "transport: Error while dialing: dial tcp 1.15.182.107:0: connectex: The requested address is not valid in its context.", method2Err=failed to get websocket: failed to connect to websocket: dial tcp 112.124.54.243:4701: i/o timeout
2026/05/07 15:33:06 monitor server failed, AppId=1, ServerId=3, err=method1 and method2 both failed, method1Err=ServerInfo failed: rpc error: code = DeadlineExceeded desc = received context error while waiting for new LB policy update: context deadline exceeded, method2Err=failed to send admin message: failed to read from websocket: websocket: close 1006 (abnormal closure): unexpected EOF
2026/05/07 15:33:06 monitor server failed, AppId=2, ServerId=3, err=method1 and method2 both failed, method1Err=ServerInfo failed: rpc error: code = Unavailable desc = connection error: desc = "transport: Error while dialing: dial tcp 112.124.54.243:50063: connectex: No connection could be made because the target machine actively refused it.", method2Err=dial tcp 112.124.54.243:3653: connectex: No connection could be made because the target machine actively refused it.