复制项目
This commit is contained in:
289
internal/rpc/admin/application.go
Normal file
289
internal/rpc/admin/application.go
Normal file
@@ -0,0 +1,289 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
admindb "git.imall.cloud/openim/chat/pkg/common/db/table/admin"
|
||||
"git.imall.cloud/openim/chat/pkg/common/mctx"
|
||||
"git.imall.cloud/openim/chat/pkg/protocol/admin"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"github.com/openimsdk/tools/log"
|
||||
"github.com/openimsdk/tools/utils/datautil"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
)
|
||||
|
||||
func IsNotFound(err error) bool {
|
||||
switch errs.Unwrap(err) {
|
||||
case redis.Nil, mongo.ErrNoDocuments:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (o *adminServer) db2pbApplication(val *admindb.Application) *admin.ApplicationVersion {
|
||||
return &admin.ApplicationVersion{
|
||||
Id: val.ID.Hex(),
|
||||
Platform: val.Platform,
|
||||
Version: val.Version,
|
||||
Url: val.Url,
|
||||
Text: val.Text,
|
||||
Force: val.Force,
|
||||
Latest: val.Latest,
|
||||
Hot: val.Hot,
|
||||
CreateTime: val.CreateTime.UnixMilli(),
|
||||
}
|
||||
}
|
||||
|
||||
// LatestVersionAPIResponse 外部 API 响应结构
|
||||
type LatestVersionAPIResponse struct {
|
||||
Success bool `json:"success"`
|
||||
ErrorCode string `json:"errorCode"`
|
||||
ErrorMessage string `json:"errorMessage"`
|
||||
ShowType int `json:"showType"`
|
||||
Data struct {
|
||||
APKPath string `json:"apk_path"`
|
||||
APKSize int64 `json:"apk_size"`
|
||||
AppLogo string `json:"app_logo"`
|
||||
AppName string `json:"app_name"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
Success bool `json:"success"`
|
||||
Version string `json:"version"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
func (o *adminServer) LatestApplicationVersion(ctx context.Context, req *admin.LatestApplicationVersionReq) (*admin.LatestApplicationVersionResp, error) {
|
||||
// 从系统配置读取 build_app_id
|
||||
buildAppIDConfig, err := o.ChatDatabase.GetSystemConfig(ctx, "build_app_id")
|
||||
if err != nil {
|
||||
log.ZWarn(ctx, "Failed to get build_app_id from system config, falling back to database", err)
|
||||
// 如果获取配置失败,回退到从数据库读取
|
||||
res, err := o.Database.LatestVersion(ctx, req.Platform)
|
||||
if err == nil {
|
||||
return &admin.LatestApplicationVersionResp{Version: o.db2pbApplication(res)}, nil
|
||||
} else if IsNotFound(err) {
|
||||
return &admin.LatestApplicationVersionResp{}, nil
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
buildAppID := buildAppIDConfig.Value
|
||||
if buildAppID == "" {
|
||||
log.ZWarn(ctx, "build_app_id is empty in system config, falling back to database", nil)
|
||||
// 如果配置值为空,回退到从数据库读取
|
||||
res, err := o.Database.LatestVersion(ctx, req.Platform)
|
||||
if err == nil {
|
||||
return &admin.LatestApplicationVersionResp{Version: o.db2pbApplication(res)}, nil
|
||||
} else if IsNotFound(err) {
|
||||
return &admin.LatestApplicationVersionResp{}, nil
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// 调用外部 API
|
||||
apiURL := "https://down.imall.cloud/api/download/latest"
|
||||
requestBody := map[string]string{
|
||||
"app_id": buildAppID,
|
||||
}
|
||||
jsonData, err := json.Marshal(requestBody)
|
||||
if err != nil {
|
||||
log.ZError(ctx, "Failed to marshal request body", err, "buildAppID", buildAppID)
|
||||
return nil, errs.ErrInternalServer.WrapMsg("failed to prepare request")
|
||||
}
|
||||
|
||||
httpReq, err := http.NewRequestWithContext(ctx, "POST", apiURL, bytes.NewBuffer(jsonData))
|
||||
if err != nil {
|
||||
log.ZError(ctx, "Failed to create HTTP request", err, "url", apiURL)
|
||||
return nil, errs.ErrInternalServer.WrapMsg("failed to create request")
|
||||
}
|
||||
httpReq.Header.Set("Content-Type", "application/json")
|
||||
|
||||
client := &http.Client{
|
||||
Timeout: 10 * time.Second,
|
||||
}
|
||||
resp, err := client.Do(httpReq)
|
||||
if err != nil {
|
||||
log.ZError(ctx, "Failed to call external API", err, "url", apiURL)
|
||||
// API 调用失败,回退到从数据库读取
|
||||
res, err := o.Database.LatestVersion(ctx, req.Platform)
|
||||
if err == nil {
|
||||
return &admin.LatestApplicationVersionResp{Version: o.db2pbApplication(res)}, nil
|
||||
} else if IsNotFound(err) {
|
||||
return &admin.LatestApplicationVersionResp{}, nil
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.ZError(ctx, "Failed to read response body", err)
|
||||
// 读取响应失败,回退到从数据库读取
|
||||
res, err := o.Database.LatestVersion(ctx, req.Platform)
|
||||
if err == nil {
|
||||
return &admin.LatestApplicationVersionResp{Version: o.db2pbApplication(res)}, nil
|
||||
} else if IsNotFound(err) {
|
||||
return &admin.LatestApplicationVersionResp{}, nil
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
log.ZWarn(ctx, "External API returned non-200 status", nil, "statusCode", resp.StatusCode, "body", string(body))
|
||||
// API 返回非 200,回退到从数据库读取
|
||||
res, err := o.Database.LatestVersion(ctx, req.Platform)
|
||||
if err == nil {
|
||||
return &admin.LatestApplicationVersionResp{Version: o.db2pbApplication(res)}, nil
|
||||
} else if IsNotFound(err) {
|
||||
return &admin.LatestApplicationVersionResp{}, nil
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var apiResp LatestVersionAPIResponse
|
||||
if err := json.Unmarshal(body, &apiResp); err != nil {
|
||||
log.ZError(ctx, "Failed to unmarshal API response", err, "body", string(body))
|
||||
// 解析响应失败,回退到从数据库读取
|
||||
res, err := o.Database.LatestVersion(ctx, req.Platform)
|
||||
if err == nil {
|
||||
return &admin.LatestApplicationVersionResp{Version: o.db2pbApplication(res)}, nil
|
||||
} else if IsNotFound(err) {
|
||||
return &admin.LatestApplicationVersionResp{}, nil
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if !apiResp.Success {
|
||||
log.ZWarn(ctx, "External API returned success=false", nil, "errorCode", apiResp.ErrorCode, "errorMessage", apiResp.ErrorMessage)
|
||||
// API 返回失败,回退到从数据库读取
|
||||
res, err := o.Database.LatestVersion(ctx, req.Platform)
|
||||
if err == nil {
|
||||
return &admin.LatestApplicationVersionResp{Version: o.db2pbApplication(res)}, nil
|
||||
} else if IsNotFound(err) {
|
||||
return &admin.LatestApplicationVersionResp{}, nil
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// 解析创建时间
|
||||
var createTime time.Time
|
||||
if apiResp.Data.CreatedAt != "" {
|
||||
createTime, err = time.Parse(time.RFC3339, apiResp.Data.CreatedAt)
|
||||
if err != nil {
|
||||
log.ZWarn(ctx, "Failed to parse created_at", err, "createdAt", apiResp.Data.CreatedAt)
|
||||
createTime = time.Now()
|
||||
}
|
||||
} else {
|
||||
createTime = time.Now()
|
||||
}
|
||||
|
||||
// 转换为 ApplicationVersion 格式
|
||||
version := &admin.ApplicationVersion{
|
||||
Id: "", // 外部 API 没有 ID
|
||||
Platform: req.Platform,
|
||||
Version: apiResp.Data.Version,
|
||||
Url: apiResp.Data.APKPath,
|
||||
Text: fmt.Sprintf("应用名称: %s", apiResp.Data.AppName),
|
||||
Force: false, // 外部 API 没有提供此字段
|
||||
Latest: true, // 从 latest 接口获取的肯定是最新版本
|
||||
Hot: false, // 外部 API 没有提供此字段
|
||||
CreateTime: createTime.UnixMilli(),
|
||||
}
|
||||
|
||||
return &admin.LatestApplicationVersionResp{Version: version}, nil
|
||||
}
|
||||
|
||||
func (o *adminServer) AddApplicationVersion(ctx context.Context, req *admin.AddApplicationVersionReq) (*admin.AddApplicationVersionResp, error) {
|
||||
if _, err := mctx.CheckAdmin(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
val := &admindb.Application{
|
||||
ID: primitive.NewObjectID(),
|
||||
Platform: req.Platform,
|
||||
Version: req.Version,
|
||||
Url: req.Url,
|
||||
Text: req.Text,
|
||||
Force: req.Force,
|
||||
Latest: req.Latest,
|
||||
Hot: req.Hot,
|
||||
CreateTime: time.Now(),
|
||||
}
|
||||
if err := o.Database.AddVersion(ctx, val); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin.AddApplicationVersionResp{}, nil
|
||||
}
|
||||
|
||||
func (o *adminServer) UpdateApplicationVersion(ctx context.Context, req *admin.UpdateApplicationVersionReq) (*admin.UpdateApplicationVersionResp, error) {
|
||||
if _, err := mctx.CheckAdmin(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
oid, err := primitive.ObjectIDFromHex(req.Id)
|
||||
if err != nil {
|
||||
return nil, errs.ErrArgs.WrapMsg("invalid id " + err.Error())
|
||||
}
|
||||
update := make(map[string]any)
|
||||
putUpdate(update, "platform", req.Platform)
|
||||
putUpdate(update, "version", req.Version)
|
||||
putUpdate(update, "url", req.Url)
|
||||
putUpdate(update, "text", req.Text)
|
||||
putUpdate(update, "force", req.Force)
|
||||
putUpdate(update, "latest", req.Latest)
|
||||
putUpdate(update, "hot", req.Hot)
|
||||
if err := o.Database.UpdateVersion(ctx, oid, update); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin.UpdateApplicationVersionResp{}, nil
|
||||
}
|
||||
|
||||
func (o *adminServer) DeleteApplicationVersion(ctx context.Context, req *admin.DeleteApplicationVersionReq) (*admin.DeleteApplicationVersionResp, error) {
|
||||
if _, err := mctx.CheckAdmin(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ids := make([]primitive.ObjectID, 0, len(req.Id))
|
||||
for _, id := range req.Id {
|
||||
oid, err := primitive.ObjectIDFromHex(id)
|
||||
if err != nil {
|
||||
return nil, errs.ErrArgs.WrapMsg("invalid id " + err.Error())
|
||||
}
|
||||
ids = append(ids, oid)
|
||||
}
|
||||
if err := o.Database.DeleteVersion(ctx, ids); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin.DeleteApplicationVersionResp{}, nil
|
||||
}
|
||||
|
||||
func (o *adminServer) PageApplicationVersion(ctx context.Context, req *admin.PageApplicationVersionReq) (*admin.PageApplicationVersionResp, error) {
|
||||
total, res, err := o.Database.PageVersion(ctx, req.Platform, req.Pagination)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin.PageApplicationVersionResp{
|
||||
Total: total,
|
||||
Versions: datautil.Slice(res, o.db2pbApplication),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func putUpdate[T any](update map[string]any, name string, val interface{ GetValuePtr() *T }) {
|
||||
ptrVal := val.GetValuePtr()
|
||||
if ptrVal == nil {
|
||||
return
|
||||
}
|
||||
update[name] = *ptrVal
|
||||
}
|
||||
Reference in New Issue
Block a user