mirror of
https://github.com/APIParkLab/APIPark.git
synced 2026-06-04 10:13:53 +08:00
1429 lines
39 KiB
Go
1429 lines
39 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"net/url"
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
|
|
service_overview "github.com/APIParkLab/APIPark/service/service-overview"
|
|
|
|
"github.com/APIParkLab/APIPark/common"
|
|
|
|
"github.com/eolinker/go-common/register"
|
|
|
|
mcp_server "github.com/APIParkLab/APIPark/mcp-server"
|
|
|
|
"github.com/APIParkLab/APIPark/service/release"
|
|
|
|
"github.com/APIParkLab/APIPark/gateway"
|
|
|
|
ai_local "github.com/APIParkLab/APIPark/service/ai-local"
|
|
"github.com/APIParkLab/APIPark/service/cluster"
|
|
|
|
model_runtime "github.com/APIParkLab/APIPark/ai-provider/model-runtime"
|
|
|
|
"github.com/APIParkLab/APIPark/resources/access"
|
|
log_service "github.com/APIParkLab/APIPark/service/log"
|
|
"github.com/eolinker/eosc/log"
|
|
"github.com/eolinker/go-common/server"
|
|
|
|
"github.com/eolinker/ap-account/service/role"
|
|
|
|
application_authorization "github.com/APIParkLab/APIPark/service/application-authorization"
|
|
service_model_mapping "github.com/APIParkLab/APIPark/service/service-model-mapping"
|
|
|
|
api_doc "github.com/APIParkLab/APIPark/service/api-doc"
|
|
|
|
service_tag "github.com/APIParkLab/APIPark/service/service-tag"
|
|
|
|
service_doc "github.com/APIParkLab/APIPark/service/service-doc"
|
|
|
|
serviceDto "github.com/APIParkLab/APIPark/module/service/dto"
|
|
|
|
"github.com/APIParkLab/APIPark/service/tag"
|
|
|
|
"github.com/APIParkLab/APIPark/service/service"
|
|
|
|
"github.com/APIParkLab/APIPark/service/subscribe"
|
|
"gorm.io/gorm"
|
|
|
|
"github.com/APIParkLab/APIPark/service/api"
|
|
|
|
"github.com/eolinker/go-common/auto"
|
|
|
|
team_member "github.com/APIParkLab/APIPark/service/team-member"
|
|
|
|
"github.com/eolinker/go-common/store"
|
|
|
|
"github.com/google/uuid"
|
|
|
|
"github.com/eolinker/go-common/utils"
|
|
|
|
"github.com/APIParkLab/APIPark/service/team"
|
|
|
|
service_dto "github.com/APIParkLab/APIPark/module/service/dto"
|
|
)
|
|
|
|
var (
|
|
_ IServiceModule = (*imlServiceModule)(nil)
|
|
_ IExportServiceModule = (*imlServiceModule)(nil)
|
|
)
|
|
|
|
type imlServiceModule struct {
|
|
serviceService service.IServiceService `autowired:""`
|
|
teamService team.ITeamService `autowired:""`
|
|
teamMemberService team_member.ITeamMemberService `autowired:""`
|
|
tagService tag.ITagService `autowired:""`
|
|
localModelService ai_local.ILocalModelService `autowired:""`
|
|
|
|
serviceOverviewService service_overview.IOverviewService `autowired:""`
|
|
serviceTagService service_tag.ITagService `autowired:""`
|
|
apiService api.IAPIService `autowired:""`
|
|
apiDocService api_doc.IAPIDocService `autowired:""`
|
|
clusterService cluster.IClusterService `autowired:""`
|
|
subscribeServer subscribe.ISubscribeService `autowired:""`
|
|
|
|
releaseService release.IReleaseService `autowired:""`
|
|
serviceModelMappingService service_model_mapping.IServiceModelMappingService `autowired:""`
|
|
logService log_service.ILogService `autowired:""`
|
|
|
|
transaction store.ITransaction `autowired:""`
|
|
}
|
|
|
|
func formatHeader(header string) string {
|
|
result, err := url.QueryUnescape(header)
|
|
if err != nil {
|
|
return header
|
|
}
|
|
result = strings.ReplaceAll(result, "&", "\n")
|
|
result = strings.ReplaceAll(result, "=", ": ")
|
|
return result
|
|
}
|
|
|
|
func (i *imlServiceModule) RestLogInfo(ctx context.Context, serviceId string, logId string) (*service_dto.RestLogInfo, error) {
|
|
c, err := i.clusterService.Get(ctx, cluster.DefaultClusterID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("cluster %s not found", cluster.DefaultClusterID)
|
|
}
|
|
info, err := i.logService.LogInfo(ctx, "loki", c.Cluster, logId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if info.Service != serviceId {
|
|
return nil, errors.New("service not match")
|
|
}
|
|
|
|
logInfo := &service_dto.RestLogInfo{
|
|
Id: info.ID,
|
|
API: auto.UUID(info.API),
|
|
Consumer: auto.UUID(info.Consumer),
|
|
Status: info.StatusCode,
|
|
Ip: info.RemoteIP,
|
|
ResponseTime: common.FormatTime(info.ResponseTime),
|
|
Traffic: common.FormatByte(info.Traffic),
|
|
LogTime: auto.TimeLabel(info.RecordTime),
|
|
Request: service_dto.OriginRequest{
|
|
Header: formatHeader(info.RequestHeader),
|
|
Origin: info.RequestBody,
|
|
Body: info.RequestBody,
|
|
},
|
|
Response: service_dto.OriginRequest{
|
|
Header: formatHeader(info.ResponseHeader),
|
|
Origin: info.ResponseBody,
|
|
Body: info.ResponseBody,
|
|
},
|
|
}
|
|
|
|
if info.Consumer == "apipark-global" {
|
|
logInfo.IsSystemConsumer = true
|
|
logInfo.Consumer = auto.Label{
|
|
Id: info.Consumer,
|
|
Name: "System Consumer",
|
|
}
|
|
}
|
|
return logInfo, nil
|
|
}
|
|
|
|
func (i *imlServiceModule) AILogInfo(ctx context.Context, serviceId string, logId string) (*service_dto.AILogInfo, error) {
|
|
c, err := i.clusterService.Get(ctx, cluster.DefaultClusterID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("cluster %s not found", cluster.DefaultClusterID)
|
|
}
|
|
info, err := i.logService.LogInfo(ctx, "loki", c.Cluster, logId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if info.Service != serviceId {
|
|
return nil, errors.New("service not match")
|
|
}
|
|
response, err := parseAIResponse(info.ResponseBody)
|
|
if err != nil {
|
|
response = info.ResponseBody
|
|
}
|
|
|
|
logInfo := &service_dto.AILogInfo{
|
|
Id: info.ID,
|
|
API: auto.UUID(info.API),
|
|
Consumer: auto.UUID(info.Consumer),
|
|
Status: info.StatusCode,
|
|
Ip: info.RemoteIP,
|
|
Provider: auto.UUID(info.AIProvider),
|
|
Model: info.AIModel,
|
|
LogTime: auto.TimeLabel(info.RecordTime),
|
|
Request: service_dto.OriginAIRequest{
|
|
OriginRequest: service_dto.OriginRequest{
|
|
Header: formatHeader(info.RequestHeader),
|
|
Origin: info.RequestBody,
|
|
Body: parseAIRequest(info.RequestBody),
|
|
},
|
|
|
|
Token: info.InputToken,
|
|
},
|
|
Response: service_dto.OriginAIRequest{
|
|
OriginRequest: service_dto.OriginRequest{
|
|
Header: formatHeader(info.ResponseHeader),
|
|
Origin: info.ResponseBody,
|
|
Body: response,
|
|
},
|
|
|
|
Token: info.OutputToken,
|
|
},
|
|
}
|
|
if info.Consumer == "apipark-global" {
|
|
logInfo.IsSystemConsumer = true
|
|
logInfo.Consumer = auto.Label{
|
|
Id: info.Consumer,
|
|
Name: "System Consumer",
|
|
}
|
|
}
|
|
return logInfo, nil
|
|
}
|
|
|
|
func (i *imlServiceModule) RestLogs(ctx context.Context, serviceId string, start int64, end int64, page int, size int) ([]*service_dto.RestLogItem, int64, error) {
|
|
list, total, err := i.logService.LogRecordsByService(ctx, serviceId, time.Unix(start, 0), time.Unix(end, 0), page, size)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
return utils.SliceToSlice(list, func(s *log_service.Item) *service_dto.RestLogItem {
|
|
item := &service_dto.RestLogItem{
|
|
Id: s.ID,
|
|
API: auto.UUID(s.API),
|
|
Status: s.StatusCode,
|
|
LogTime: auto.TimeLabel(s.RecordTime),
|
|
Ip: s.RemoteIP,
|
|
Consumer: auto.UUID(s.Consumer),
|
|
ResponseTime: common.FormatTime(s.ResponseTime),
|
|
Traffic: common.FormatByte(s.Traffic),
|
|
}
|
|
if s.Consumer == "apipark-global" {
|
|
item.Consumer = auto.Label{
|
|
Id: s.Consumer,
|
|
Name: "System Consumer",
|
|
}
|
|
}
|
|
return item
|
|
}), total, nil
|
|
}
|
|
|
|
func (i *imlServiceModule) AILogs(ctx context.Context, serviceId string, start int64, end int64, page int, size int) ([]*service_dto.AILogItem, int64, error) {
|
|
list, total, err := i.logService.LogRecordsByService(ctx, serviceId, time.Unix(start, 0), time.Unix(end, 0), page, size)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
return utils.SliceToSlice(list, func(s *log_service.Item) *service_dto.AILogItem {
|
|
var tokenPerSecond int64 = 0
|
|
if s.ResponseTime > 0 {
|
|
tokenPerSecond = s.TotalToken * 1000 / s.ResponseTime
|
|
}
|
|
item := &service_dto.AILogItem{
|
|
Id: s.ID,
|
|
API: auto.UUID(s.API),
|
|
Status: s.StatusCode,
|
|
LogTime: auto.TimeLabel(s.RecordTime),
|
|
Ip: s.RemoteIP,
|
|
Token: s.TotalToken,
|
|
TokenPerSecond: tokenPerSecond,
|
|
Consumer: auto.UUID(s.Consumer),
|
|
Provider: auto.UUID(s.AIProvider),
|
|
Model: s.AIModel,
|
|
}
|
|
if s.Consumer == "apipark-global" {
|
|
item.Consumer = auto.Label{
|
|
Id: s.Consumer,
|
|
Name: "System Consumer",
|
|
}
|
|
}
|
|
return item
|
|
}), total, nil
|
|
}
|
|
|
|
func (i *imlServiceModule) ServiceOverview(ctx context.Context, id string) (*service_dto.Overview, error) {
|
|
info, err := i.serviceService.Get(ctx, id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
apiCountMap, err := i.apiDocService.APICountByServices(ctx, id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
subscribeMap, err := i.subscribeServer.CountMapByService(ctx, subscribe.ApplyStatusSubscribe, id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
result := &service_dto.Overview{
|
|
Id: info.Id,
|
|
Name: info.Name,
|
|
Description: info.Description,
|
|
EnableMCP: info.EnableMCP,
|
|
ServiceKind: info.Kind.String(),
|
|
SubscriberNum: subscribeMap[id],
|
|
Logo: info.Logo,
|
|
Catalogue: auto.UUID(info.Catalogue),
|
|
APINum: apiCountMap[id],
|
|
}
|
|
_, err = i.releaseService.GetRunning(ctx, id)
|
|
if err != nil {
|
|
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return nil, err
|
|
}
|
|
} else {
|
|
result.IsReleased = true
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func (i *imlServiceModule) OnInit() {
|
|
register.Handle(func(v server.Server) {
|
|
ctx := context.Background()
|
|
services, err := i.serviceService.ServiceList(ctx)
|
|
if err != nil {
|
|
log.Error(err)
|
|
return
|
|
}
|
|
|
|
for _, s := range services {
|
|
err = i.updateMCPServer(ctx, s.Id, s.Name, "1.0")
|
|
if err != nil {
|
|
log.Error(err)
|
|
return
|
|
}
|
|
}
|
|
overviews, err := i.serviceOverviewService.List(ctx)
|
|
if err != nil {
|
|
log.Error(err)
|
|
return
|
|
}
|
|
if len(overviews) > 0 {
|
|
return
|
|
}
|
|
countMap, err := i.apiDocService.APICountByServices(ctx)
|
|
if err != nil {
|
|
log.Error(err)
|
|
return
|
|
}
|
|
for k, v := range countMap {
|
|
err = i.serviceOverviewService.Update(ctx, k, &service_overview.Update{
|
|
ApiCount: &v,
|
|
})
|
|
if err != nil {
|
|
log.Error(err)
|
|
return
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
func (i *imlServiceModule) initGateway(ctx context.Context, clusterId string, clientDriver gateway.IClientDriver) error {
|
|
services, err := i.serviceService.ServiceList(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
subscribeReleases := make([]*gateway.SubscribeRelease, 0, len(services))
|
|
hashReleases := make([]*gateway.HashRelease, 0, len(services))
|
|
for _, s := range services {
|
|
subscribeReleases = append(subscribeReleases, &gateway.SubscribeRelease{
|
|
Service: s.Id,
|
|
Application: "apipark-global",
|
|
Expired: "0",
|
|
})
|
|
|
|
modelMap, err := i.serviceModelMappingService.Get(ctx, s.Id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if modelMap.Content == "" {
|
|
continue
|
|
}
|
|
m := make(map[string]string)
|
|
err = json.Unmarshal([]byte(modelMap.Content), &m)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
hashReleases = append(hashReleases, &gateway.HashRelease{
|
|
HashKey: fmt.Sprintf("%s:%s", gateway.KeyServiceMapping, s.Id),
|
|
HashMap: m,
|
|
})
|
|
|
|
}
|
|
err = clientDriver.Subscribe().Online(ctx, subscribeReleases...)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return clientDriver.Hash().Online(ctx, hashReleases...)
|
|
}
|
|
|
|
func (i *imlServiceModule) updateMCPServer(ctx context.Context, sid string, name string, version string) error {
|
|
r, err := i.releaseService.GetRunning(ctx, sid)
|
|
if err != nil {
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
_, _, apiDocCommit, _, _, err := i.releaseService.GetReleaseInfos(ctx, r.UUID)
|
|
if err != nil {
|
|
return fmt.Errorf("get release info error: %w", err)
|
|
}
|
|
commitDoc, err := i.apiDocService.GetDocCommit(ctx, apiDocCommit.Commit)
|
|
if err != nil {
|
|
return fmt.Errorf("get api doc commit error: %w", err)
|
|
}
|
|
return mcp_server.SetServerByOpenapi(sid, name, version, commitDoc.Data.Content)
|
|
}
|
|
|
|
func (i *imlServiceModule) deleteMCPServer(ctx context.Context, sid string) {
|
|
mcp_server.DelServer(sid)
|
|
}
|
|
|
|
func (i *imlServiceModule) ExportAll(ctx context.Context) ([]*service_dto.ExportService, error) {
|
|
services, err := i.serviceService.ServiceList(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
serviceIds := utils.SliceToSlice(services, func(s *service.Service) string {
|
|
return s.Id
|
|
})
|
|
serviceTags, err := i.serviceTagService.List(ctx, serviceIds, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
tagMap, err := i.tagService.Map(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
serviceTagMap := make(map[string][]string)
|
|
for _, st := range serviceTags {
|
|
if _, ok := tagMap[st.Tid]; !ok {
|
|
continue
|
|
}
|
|
if _, ok := serviceTagMap[st.Sid]; !ok {
|
|
serviceTagMap[st.Sid] = make([]string, 0)
|
|
}
|
|
serviceTagMap[st.Sid] = append(serviceTagMap[st.Sid], tagMap[st.Tid].Name)
|
|
}
|
|
|
|
items := make([]*service_dto.ExportService, 0, len(services))
|
|
for _, s := range services {
|
|
info := &service_dto.ExportService{
|
|
Id: s.Id,
|
|
Name: s.Name,
|
|
Prefix: s.Prefix,
|
|
Description: s.Description,
|
|
Team: s.Team,
|
|
ServiceType: s.ServiceType.String(),
|
|
Catalogue: s.Catalogue,
|
|
Logo: s.Logo,
|
|
}
|
|
|
|
if tags, ok := serviceTagMap[s.Id]; ok {
|
|
info.Tags = tags
|
|
}
|
|
items = append(items, info)
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
func (i *imlServiceModule) searchMyServices(ctx context.Context, teamId string, keyword string) ([]*service.Service, error) {
|
|
userID := utils.UserId(ctx)
|
|
condition := make(map[string]interface{})
|
|
condition["as_server"] = true
|
|
if teamId != "" {
|
|
_, err := i.teamService.Get(ctx, teamId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
condition["team"] = teamId
|
|
return i.serviceService.Search(ctx, keyword, condition, "create_at desc")
|
|
} else {
|
|
membersForUser, err := i.teamMemberService.FilterMembersForUser(ctx, userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
teamIds := membersForUser[userID]
|
|
condition["team"] = teamIds
|
|
return i.serviceService.Search(ctx, keyword, condition, "create_at desc")
|
|
}
|
|
}
|
|
|
|
func (i *imlServiceModule) SearchMyServices(ctx context.Context, teamId string, keyword string) ([]*service_dto.ServiceItem, error) {
|
|
services, err := i.searchMyServices(ctx, teamId, keyword)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
serviceIds := utils.SliceToSlice(services, func(p *service.Service) string {
|
|
return p.Id
|
|
})
|
|
//apiCountMap, err := i.apiDocService.APICountByServices(ctx, serviceIds...)
|
|
//if err != nil {
|
|
// return nil, err
|
|
//}
|
|
//serviceIds := utils.SliceToSlice(services, func(s *service.Service) string {
|
|
// return s.Id
|
|
//})
|
|
overviewMap, err := i.serviceOverviewService.Map(ctx, serviceIds...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
items := make([]*service_dto.ServiceItem, 0, len(services))
|
|
for _, model := range services {
|
|
if teamId != "" && model.Team != teamId {
|
|
continue
|
|
}
|
|
item := toServiceItem(model)
|
|
if ov, ok := overviewMap[model.Id]; ok {
|
|
item.ApiNum = ov.ApiCount
|
|
item.CanDelete = ov.ApiCount == 0
|
|
}
|
|
|
|
items = append(items, item)
|
|
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
func (i *imlServiceModule) Simple(ctx context.Context) ([]*service_dto.SimpleServiceItem, error) {
|
|
w := make(map[string]interface{})
|
|
w["as_server"] = true
|
|
|
|
services, err := i.serviceService.Search(ctx, "", w)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
items := make([]*service_dto.SimpleServiceItem, 0, len(services))
|
|
for _, p := range services {
|
|
items = append(items, &service_dto.SimpleServiceItem{
|
|
Id: p.Id,
|
|
Name: p.Name,
|
|
Description: p.Description,
|
|
Team: auto.UUID(p.Team),
|
|
})
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
func (i *imlServiceModule) MySimple(ctx context.Context) ([]*service_dto.SimpleServiceItem, error) {
|
|
services, err := i.searchMyServices(ctx, "", "")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
items := make([]*service_dto.SimpleServiceItem, 0, len(services))
|
|
for _, p := range services {
|
|
items = append(items, &service_dto.SimpleServiceItem{
|
|
Id: p.Id,
|
|
Name: p.Name,
|
|
Description: p.Description,
|
|
Team: auto.UUID(p.Team),
|
|
})
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
func (i *imlServiceModule) Get(ctx context.Context, id string) (*service_dto.Service, error) {
|
|
now := time.Now()
|
|
serviceInfo, err := i.serviceService.Get(ctx, id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
tags, err := i.serviceTagService.List(ctx, []string{serviceInfo.Id}, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
s := service_dto.ToService(serviceInfo)
|
|
s.Tags = auto.List(utils.SliceToSlice(tags, func(p *service_tag.Tag) string {
|
|
return p.Tid
|
|
}))
|
|
if s.Model == "" {
|
|
switch s.ProviderType {
|
|
case "online":
|
|
p, has := model_runtime.GetProvider(s.Provider.Id)
|
|
if has {
|
|
m, has := p.DefaultModel(model_runtime.ModelTypeLLM)
|
|
if has {
|
|
s.Model = m.ID()
|
|
}
|
|
}
|
|
case "local":
|
|
info, err := i.localModelService.DefaultModel(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
s.Model = info.Id
|
|
|
|
}
|
|
}
|
|
|
|
serviceModelMapping, err := i.serviceModelMappingService.Get(ctx, id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
s.ModelMapping = serviceModelMapping.Content
|
|
|
|
log.Infof("get service cost %d ms", time.Since(now).Milliseconds())
|
|
return s, nil
|
|
}
|
|
|
|
func (i *imlServiceModule) Search(ctx context.Context, teamID string, keyword string) ([]*service_dto.ServiceItem, error) {
|
|
var list []*service.Service
|
|
var err error
|
|
if teamID != "" {
|
|
_, err = i.teamService.Get(ctx, teamID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
list, err = i.serviceService.Search(ctx, keyword, map[string]interface{}{"team": teamID, "as_server": true}, "create_at desc")
|
|
} else {
|
|
list, err = i.serviceService.Search(ctx, keyword, map[string]interface{}{"as_server": true}, "create_at desc")
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
serviceIds := utils.SliceToSlice(list, func(s *service.Service) string {
|
|
return s.Id
|
|
})
|
|
overviewMap, err := i.serviceOverviewService.Map(ctx, serviceIds...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
items := make([]*service_dto.ServiceItem, 0, len(list))
|
|
for _, model := range list {
|
|
item := toServiceItem(model)
|
|
if v, ok := overviewMap[model.Id]; ok {
|
|
item.ApiNum = v.ApiCount
|
|
item.CanDelete = v.ApiCount == 0
|
|
}
|
|
|
|
items = append(items, item)
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
func toServiceItem(model *service.Service) *service_dto.ServiceItem {
|
|
item := &service_dto.ServiceItem{
|
|
Id: model.Id,
|
|
Name: model.Name,
|
|
Description: model.Description,
|
|
CreateTime: auto.TimeLabel(model.CreateTime),
|
|
UpdateTime: auto.TimeLabel(model.UpdateTime),
|
|
Team: auto.UUID(model.Team),
|
|
EnableMCP: model.EnableMCP,
|
|
ServiceKind: model.Kind.String(),
|
|
}
|
|
state := service_dto.FromServiceState(model.State)
|
|
if state == service_dto.ServiceStateNormal {
|
|
item.State = model.ServiceType.String()
|
|
} else {
|
|
item.State = state.String()
|
|
}
|
|
|
|
switch model.Kind {
|
|
case service.RestService:
|
|
item.State = model.ServiceType.String()
|
|
return item
|
|
case service.AIService:
|
|
provider := auto.UUID(model.AdditionalConfig["provider"])
|
|
item.Provider = &provider
|
|
return item
|
|
default:
|
|
return item
|
|
}
|
|
}
|
|
|
|
func (i *imlServiceModule) Create(ctx context.Context, teamID string, input *service_dto.CreateService) (*service_dto.Service, error) {
|
|
if input.Id == "" {
|
|
input.Id = uuid.New().String()
|
|
}
|
|
if teamID == "" {
|
|
item, err := i.teamService.DefaultTeam(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
teamID = item.Id
|
|
}
|
|
mo := &service.Create{
|
|
Id: input.Id,
|
|
Name: input.Name,
|
|
Description: input.Description,
|
|
Team: teamID,
|
|
ServiceType: service.ServiceType(input.ServiceType),
|
|
Catalogue: input.Catalogue,
|
|
Prefix: input.Prefix,
|
|
Logo: input.Logo,
|
|
State: service_dto.ServiceState(input.State).Int(),
|
|
ApprovalType: service.ApprovalType(input.ApprovalType),
|
|
AdditionalConfig: make(map[string]string),
|
|
Kind: service.Kind(input.Kind),
|
|
EnableMCP: input.EnableMCP,
|
|
}
|
|
if mo.ServiceType == service.PublicService && mo.Catalogue == "" {
|
|
return nil, fmt.Errorf("catalogue can not be empty")
|
|
}
|
|
switch mo.Kind {
|
|
case service.AIService:
|
|
if input.Provider == nil {
|
|
return nil, fmt.Errorf("ai service: provider can not be empty")
|
|
}
|
|
mo.AdditionalConfig["provider"] = *input.Provider
|
|
if input.Model == nil {
|
|
return nil, fmt.Errorf("ai service: model can not be empty")
|
|
}
|
|
mo.AdditionalConfig["model"] = *input.Model
|
|
}
|
|
if input.AsApp == nil {
|
|
// 默认值为false
|
|
mo.AsApp = false
|
|
} else {
|
|
mo.AsApp = *input.AsApp
|
|
}
|
|
if input.AsServer == nil {
|
|
// 默认值为true
|
|
mo.AsServer = true
|
|
} else {
|
|
mo.AsServer = *input.AsServer
|
|
}
|
|
|
|
//input.Prefix = strings.Trim(strings.Trim(input.Prefix, " "), "/")
|
|
err := i.transaction.Transaction(ctx, func(ctx context.Context) error {
|
|
if input.Tags != nil {
|
|
tags, err := i.getTagUuids(ctx, input.Tags)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, t := range tags {
|
|
err = i.serviceTagService.Create(ctx, &service_tag.CreateTag{
|
|
Tid: t,
|
|
Sid: input.Id,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
err := i.serviceService.Create(ctx, mo)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
client, err := i.clusterService.GatewayClient(ctx, cluster.DefaultClusterID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = client.Subscribe().Online(ctx, &gateway.SubscribeRelease{
|
|
Service: mo.Id,
|
|
Application: "apipark-global",
|
|
Expired: "0",
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if input.ModelMapping != "" {
|
|
m := make(map[string]string)
|
|
err = json.Unmarshal([]byte(input.ModelMapping), &m)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = i.serviceModelMappingService.Save(ctx, &service_model_mapping.Save{
|
|
Sid: input.Id,
|
|
Content: input.ModelMapping,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = client.Hash().Online(ctx, &gateway.HashRelease{
|
|
HashKey: fmt.Sprintf("%s:%s", gateway.KeyServiceMapping, input.Id),
|
|
HashMap: m,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if input.EnableMCP {
|
|
err = i.updateMCPServer(ctx, input.Id, input.Name, "1.0")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
i.deleteMCPServer(ctx, input.Id)
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return i.Get(ctx, input.Id)
|
|
}
|
|
|
|
func (i *imlServiceModule) Edit(ctx context.Context, id string, input *service_dto.EditService) (*service_dto.Service, error) {
|
|
info, err := i.serviceService.Get(ctx, id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
switch info.Kind {
|
|
case service.AIService:
|
|
if input.Provider != nil {
|
|
info.AdditionalConfig["provider"] = *input.Provider
|
|
}
|
|
if input.Model != nil {
|
|
info.AdditionalConfig["model"] = *input.Model
|
|
}
|
|
}
|
|
err = i.transaction.Transaction(ctx, func(ctx context.Context) error {
|
|
serviceType := (*service.ServiceType)(input.ServiceType)
|
|
if serviceType != nil && *serviceType == service.PublicService {
|
|
if input.Catalogue == nil || *input.Catalogue == "" {
|
|
return fmt.Errorf("catalogue can not be empty")
|
|
}
|
|
}
|
|
var approvalType service.ApprovalType
|
|
if input.ApprovalType != nil {
|
|
approvalType = service.ApprovalType(*input.ApprovalType)
|
|
}
|
|
editCfg := &service.Edit{
|
|
Name: input.Name,
|
|
Description: input.Description,
|
|
Logo: input.Logo,
|
|
ServiceType: serviceType,
|
|
Catalogue: input.Catalogue,
|
|
AdditionalConfig: &info.AdditionalConfig,
|
|
Prefix: input.Prefix,
|
|
ApprovalType: &approvalType,
|
|
EnableMCP: input.EnableMCP,
|
|
}
|
|
if input.State != nil {
|
|
state := service_dto.ServiceState(*input.State).Int()
|
|
editCfg.State = &state
|
|
}
|
|
|
|
err = i.serviceService.Save(ctx, id, editCfg)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if input.Tags != nil {
|
|
tags, err := i.getTagUuids(ctx, *input.Tags)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
i.serviceTagService.Delete(ctx, nil, []string{id})
|
|
for _, t := range tags {
|
|
err = i.serviceTagService.Create(ctx, &service_tag.CreateTag{
|
|
Tid: t,
|
|
Sid: id,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
}
|
|
}
|
|
client, err := i.clusterService.GatewayClient(ctx, cluster.DefaultClusterID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = client.Subscribe().Online(ctx, &gateway.SubscribeRelease{
|
|
Service: id,
|
|
Application: "apipark-global",
|
|
Expired: "0",
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if input.ModelMapping != nil && *input.ModelMapping != "" {
|
|
m := make(map[string]string)
|
|
err = json.Unmarshal([]byte(*input.ModelMapping), &m)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = i.serviceModelMappingService.Save(ctx, &service_model_mapping.Save{
|
|
Sid: id,
|
|
Content: *input.ModelMapping,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = client.Hash().Online(ctx, &gateway.HashRelease{
|
|
HashKey: fmt.Sprintf("%s:%s", gateway.KeyServiceMapping, id),
|
|
HashMap: m,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if input.EnableMCP != nil {
|
|
if *input.EnableMCP {
|
|
name := info.Name
|
|
if input.Name != nil {
|
|
name = *input.Name
|
|
}
|
|
err = i.updateMCPServer(ctx, id, name, "1.0")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
i.deleteMCPServer(ctx, id)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return i.Get(ctx, id)
|
|
}
|
|
|
|
func (i *imlServiceModule) Delete(ctx context.Context, id string) error {
|
|
err := i.transaction.Transaction(ctx, func(ctx context.Context) error {
|
|
count, err := i.apiService.CountByService(ctx, id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if count > 0 {
|
|
return fmt.Errorf("service has apis, can not delete")
|
|
}
|
|
|
|
err = i.serviceService.Delete(ctx, id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = i.serviceModelMappingService.Delete(ctx, id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
client, err := i.clusterService.GatewayClient(ctx, cluster.DefaultClusterID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = client.Project().Offline(ctx, &gateway.ProjectRelease{
|
|
Id: id,
|
|
})
|
|
if err != nil {
|
|
if err.Error() != "nil" {
|
|
return err
|
|
}
|
|
}
|
|
err = client.Subscribe().Offline(ctx, &gateway.SubscribeRelease{
|
|
Service: id,
|
|
Application: "apipark-global",
|
|
Expired: "0",
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = client.Hash().Offline(ctx, &gateway.HashRelease{
|
|
HashKey: fmt.Sprintf("%s:%s", gateway.KeyServiceMapping, id),
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
i.deleteMCPServer(ctx, id)
|
|
return nil
|
|
})
|
|
return err
|
|
}
|
|
|
|
func (i *imlServiceModule) getTagUuids(ctx context.Context, tags []string) ([]string, error) {
|
|
list, err := i.tagService.Search(ctx, "", map[string]interface{}{"name": tags})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
tagMap := make(map[string]string)
|
|
for _, t := range list {
|
|
tagMap[t.Name] = t.Id
|
|
}
|
|
tagList := make([]string, 0, len(tags))
|
|
repeatTag := make(map[string]struct{})
|
|
for _, t := range tags {
|
|
if _, ok := repeatTag[t]; ok {
|
|
continue
|
|
}
|
|
repeatTag[t] = struct{}{}
|
|
v := &tag.CreateTag{
|
|
Name: t,
|
|
}
|
|
id, ok := tagMap[t]
|
|
if !ok {
|
|
v.Id = uuid.New().String()
|
|
err = i.tagService.Create(ctx, v)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
tagMap[t] = v.Id
|
|
} else {
|
|
v.Id = id
|
|
}
|
|
tagList = append(tagList, v.Id)
|
|
}
|
|
return tagList, nil
|
|
}
|
|
|
|
type imlServiceDocModule struct {
|
|
serviceService service.IServiceService `autowired:""`
|
|
serviceDocService service_doc.IDocService `autowired:""`
|
|
}
|
|
|
|
func (i *imlServiceDocModule) ServiceDoc(ctx context.Context, pid string) (*serviceDto.ServiceDoc, error) {
|
|
_, err := i.serviceService.Check(ctx, pid, map[string]bool{"as_server": true})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
info, err := i.serviceService.Get(ctx, pid)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
doc, err := i.serviceDocService.Get(ctx, pid)
|
|
if err != nil {
|
|
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return nil, err
|
|
}
|
|
return &serviceDto.ServiceDoc{
|
|
Id: pid,
|
|
Name: info.Name,
|
|
Doc: "",
|
|
}, nil
|
|
}
|
|
return &serviceDto.ServiceDoc{
|
|
Id: pid,
|
|
Name: info.Name,
|
|
Doc: doc.Doc,
|
|
Creator: auto.UUID(doc.Creator),
|
|
CreateTime: auto.TimeLabel(doc.CreateTime),
|
|
Updater: auto.UUID(doc.Updater),
|
|
UpdateTime: auto.TimeLabel(doc.UpdateTime),
|
|
}, nil
|
|
}
|
|
|
|
func (i *imlServiceDocModule) SaveServiceDoc(ctx context.Context, pid string, input *serviceDto.SaveServiceDoc) error {
|
|
_, err := i.serviceService.Check(ctx, pid, map[string]bool{"as_server": true})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return i.serviceDocService.Save(ctx, &service_doc.SaveDoc{
|
|
Sid: pid,
|
|
Doc: input.Doc,
|
|
})
|
|
}
|
|
|
|
var (
|
|
_ IAppModule = &imlAppModule{}
|
|
_ IExportAppModule = &imlAppModule{}
|
|
)
|
|
|
|
type imlAppModule struct {
|
|
teamService team.ITeamService `autowired:""`
|
|
serviceService service.IServiceService `autowired:""`
|
|
teamMemberService team_member.ITeamMemberService `autowired:""`
|
|
subscribeService subscribe.ISubscribeService `autowired:""`
|
|
authService application_authorization.IAuthorizationService `autowired:""`
|
|
roleService role.IRoleService `autowired:""`
|
|
roleMemberService role.IRoleMemberService `autowired:""`
|
|
transaction store.ITransaction `autowired:""`
|
|
}
|
|
|
|
func (i *imlAppModule) SearchCanSubscribe(ctx context.Context, serviceId string) ([]*service_dto.SubscribeAppItem, bool, error) {
|
|
apps, err := i.searchMyApps(ctx, "", "")
|
|
if err != nil {
|
|
return nil, false, err
|
|
}
|
|
subscribes, err := i.subscribeService.ListByServices(ctx, serviceId)
|
|
if err != nil {
|
|
return nil, false, err
|
|
}
|
|
subscribeMap := utils.SliceToMapO(subscribes, func(p *subscribe.Subscribe) (string, struct{}) {
|
|
return p.Application, struct{}{}
|
|
}, func(s *subscribe.Subscribe) bool {
|
|
return s.ApplyStatus == subscribe.ApplyStatusSubscribe
|
|
})
|
|
canSubscribe := false
|
|
list, err := i.roleService.ListByPermit(ctx, access.SystemWorkspaceApplicationManagerAll)
|
|
if err == nil && len(list) > 0 {
|
|
return utils.SliceToSlice(apps, func(p *service.Service) *service_dto.SubscribeAppItem {
|
|
_, isSubscribed := subscribeMap[p.Id]
|
|
if !isSubscribed {
|
|
canSubscribe = true
|
|
}
|
|
return &service_dto.SubscribeAppItem{
|
|
Id: p.Id,
|
|
Name: p.Name,
|
|
IsSubscribed: isSubscribed,
|
|
}
|
|
}), canSubscribe, nil
|
|
}
|
|
list, err = i.roleService.ListByPermit(ctx, access.TeamConsumerSubscriptionSubscribe)
|
|
if err != nil {
|
|
return nil, false, nil
|
|
}
|
|
roleIds := utils.SliceToSlice(list, func(p *role.RoleByPermit) string {
|
|
return p.Id
|
|
})
|
|
members, err := i.roleMemberService.ListByRoleIds(ctx, utils.UserId(ctx), roleIds...)
|
|
if err != nil {
|
|
return nil, false, err
|
|
}
|
|
if len(members) == 0 {
|
|
return nil, false, nil
|
|
}
|
|
|
|
teamMap := utils.SliceToMapO(members, func(p *role.Member) (string, struct{}) {
|
|
return role.TrimTeamTarget(p.Target), struct{}{}
|
|
})
|
|
result := make([]*service_dto.SubscribeAppItem, 0, len(apps))
|
|
for _, app := range apps {
|
|
if _, ok := teamMap[app.Team]; !ok {
|
|
continue
|
|
}
|
|
_, isSubscribed := subscribeMap[app.Id]
|
|
if !isSubscribed {
|
|
canSubscribe = true
|
|
}
|
|
result = append(result, &service_dto.SubscribeAppItem{
|
|
Id: app.Id,
|
|
Name: app.Name,
|
|
IsSubscribed: isSubscribed,
|
|
})
|
|
}
|
|
|
|
return result, canSubscribe, nil
|
|
}
|
|
|
|
func (i *imlAppModule) ExportAll(ctx context.Context) ([]*service_dto.ExportApp, error) {
|
|
apps, err := i.serviceService.AppList(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return utils.SliceToSlice(apps, func(p *service.Service) *service_dto.ExportApp {
|
|
return &service_dto.ExportApp{
|
|
Id: p.Id,
|
|
Name: p.Name,
|
|
Description: p.Description,
|
|
Team: p.Team,
|
|
}
|
|
}), nil
|
|
}
|
|
|
|
func (i *imlAppModule) Search(ctx context.Context, teamId string, keyword string) ([]*service_dto.AppItem, error) {
|
|
var services []*service.Service
|
|
var err error
|
|
if teamId != "" {
|
|
_, err = i.teamService.Get(ctx, teamId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
services, err = i.serviceService.Search(ctx, keyword, map[string]interface{}{"team": teamId, "as_app": true}, "update_at desc")
|
|
} else {
|
|
services, err = i.serviceService.Search(ctx, keyword, map[string]interface{}{"as_app": true}, "update_at desc")
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
serviceIds := utils.SliceToSlice(services, func(p *service.Service) string {
|
|
return p.Id
|
|
})
|
|
|
|
subscribers, err := i.subscribeService.SubscriptionsByApplication(ctx, serviceIds...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
subscribeCount := map[string]int64{}
|
|
subscribeVerifyCount := map[string]int64{}
|
|
verifyTmp := map[string]struct{}{}
|
|
subscribeTmp := map[string]struct{}{}
|
|
for _, s := range subscribers {
|
|
key := fmt.Sprintf("%s-%s", s.Service, s.Application)
|
|
switch s.ApplyStatus {
|
|
case subscribe.ApplyStatusSubscribe:
|
|
if _, ok := subscribeTmp[key]; !ok {
|
|
subscribeTmp[key] = struct{}{}
|
|
subscribeCount[s.Application]++
|
|
}
|
|
case subscribe.ApplyStatusReview:
|
|
if _, ok := verifyTmp[key]; !ok {
|
|
verifyTmp[key] = struct{}{}
|
|
subscribeVerifyCount[s.Application]++
|
|
}
|
|
default:
|
|
|
|
}
|
|
}
|
|
authMap, err := i.authService.CountByApp(ctx, serviceIds...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
items := make([]*service_dto.AppItem, 0, len(services))
|
|
for _, model := range services {
|
|
subscribeNum := subscribeCount[model.Id]
|
|
verifyNum := subscribeVerifyCount[model.Id]
|
|
items = append(items, &service_dto.AppItem{
|
|
Id: model.Id,
|
|
Name: model.Name,
|
|
Description: model.Description,
|
|
CreateTime: auto.TimeLabel(model.CreateTime),
|
|
UpdateTime: auto.TimeLabel(model.UpdateTime),
|
|
Team: auto.UUID(model.Team),
|
|
SubscribeNum: subscribeNum,
|
|
SubscribeVerifyNum: verifyNum,
|
|
CanDelete: subscribeNum == 0,
|
|
AuthNum: authMap[model.Id],
|
|
})
|
|
}
|
|
sort.Slice(items, func(i, j int) bool {
|
|
if items[i].SubscribeNum != items[j].SubscribeNum {
|
|
return items[i].SubscribeNum > items[j].SubscribeNum
|
|
}
|
|
if items[i].SubscribeVerifyNum != items[j].SubscribeVerifyNum {
|
|
return items[i].SubscribeVerifyNum > items[j].SubscribeVerifyNum
|
|
}
|
|
return items[i].Name < items[j].Name
|
|
})
|
|
return items, nil
|
|
}
|
|
|
|
func (i *imlAppModule) CreateApp(ctx context.Context, teamID string, input *service_dto.CreateApp) (*service_dto.App, error) {
|
|
if input.Id == "" {
|
|
input.Id = uuid.New().String()
|
|
}
|
|
userId := utils.UserId(ctx)
|
|
mo := &service.Create{
|
|
Id: input.Id,
|
|
Name: input.Name,
|
|
Description: input.Description,
|
|
Team: teamID,
|
|
AsApp: true,
|
|
}
|
|
// 判断用户是否在团队内
|
|
members, err := i.teamMemberService.Members(ctx, []string{teamID}, []string{userId})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(members) == 0 {
|
|
return nil, fmt.Errorf("master is not in team")
|
|
}
|
|
|
|
err = i.transaction.Transaction(ctx, func(ctx context.Context) error {
|
|
return i.serviceService.Create(ctx, mo)
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return i.GetApp(ctx, input.Id)
|
|
}
|
|
|
|
func (i *imlAppModule) UpdateApp(ctx context.Context, appId string, input *service_dto.UpdateApp) (*service_dto.App, error) {
|
|
// userId := utils.UserId(ctx)
|
|
info, err := i.serviceService.Get(ctx, appId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !info.AsApp {
|
|
return nil, fmt.Errorf("not app")
|
|
}
|
|
//if info.Master != userId {
|
|
// return nil, fmt.Errorf("user is not app master, can not update")
|
|
//}
|
|
|
|
err = i.serviceService.Save(ctx, appId, &service.Edit{
|
|
Name: input.Name,
|
|
Description: input.Description,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return i.GetApp(ctx, info.Id)
|
|
}
|
|
|
|
func (i *imlAppModule) searchMyApps(ctx context.Context, teamId string, keyword string) ([]*service.Service, error) {
|
|
userID := utils.UserId(ctx)
|
|
condition := make(map[string]interface{})
|
|
condition["as_app"] = true
|
|
if teamId != "" {
|
|
_, err := i.teamService.Get(ctx, teamId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
condition["team"] = teamId
|
|
return i.serviceService.Search(ctx, keyword, condition, "update_at desc")
|
|
} else {
|
|
membersForUser, err := i.teamMemberService.FilterMembersForUser(ctx, userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
teamIds := membersForUser[userID]
|
|
condition["team"] = teamIds
|
|
|
|
return i.serviceService.Search(ctx, keyword, condition, "update_at desc")
|
|
}
|
|
}
|
|
|
|
func (i *imlAppModule) SearchMyApps(ctx context.Context, teamId string, keyword string) ([]*service_dto.AppItem, error) {
|
|
services, err := i.searchMyApps(ctx, teamId, keyword)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
serviceIds := utils.SliceToSlice(services, func(p *service.Service) string {
|
|
return p.Id
|
|
})
|
|
|
|
subscribers, err := i.subscribeService.SubscriptionsByApplication(ctx, serviceIds...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
authMap, err := i.authService.CountByApp(ctx, serviceIds...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
subscribeCount := map[string]int64{}
|
|
subscribeVerifyCount := map[string]int64{}
|
|
verifyTmp := map[string]struct{}{}
|
|
subscribeTmp := map[string]struct{}{}
|
|
for _, s := range subscribers {
|
|
key := fmt.Sprintf("%s-%s", s.Service, s.Application)
|
|
switch s.ApplyStatus {
|
|
case subscribe.ApplyStatusSubscribe:
|
|
if _, ok := subscribeTmp[key]; !ok {
|
|
subscribeTmp[key] = struct{}{}
|
|
subscribeCount[s.Application]++
|
|
}
|
|
case subscribe.ApplyStatusReview:
|
|
if _, ok := verifyTmp[key]; !ok {
|
|
verifyTmp[key] = struct{}{}
|
|
subscribeVerifyCount[s.Application]++
|
|
}
|
|
default:
|
|
|
|
}
|
|
}
|
|
items := make([]*service_dto.AppItem, 0, len(services))
|
|
for _, model := range services {
|
|
subscribeNum := subscribeCount[model.Id]
|
|
verifyNum := subscribeVerifyCount[model.Id]
|
|
items = append(items, &service_dto.AppItem{
|
|
Id: model.Id,
|
|
Name: model.Name,
|
|
Description: model.Description,
|
|
CreateTime: auto.TimeLabel(model.CreateTime),
|
|
UpdateTime: auto.TimeLabel(model.UpdateTime),
|
|
Team: auto.UUID(model.Team),
|
|
SubscribeNum: subscribeNum,
|
|
SubscribeVerifyNum: verifyNum,
|
|
CanDelete: subscribeNum == 0,
|
|
AuthNum: authMap[model.Id],
|
|
})
|
|
}
|
|
sort.Slice(items, func(i, j int) bool {
|
|
if items[i].SubscribeNum != items[j].SubscribeNum {
|
|
return items[i].SubscribeNum > items[j].SubscribeNum
|
|
}
|
|
if items[i].SubscribeVerifyNum != items[j].SubscribeVerifyNum {
|
|
return items[i].SubscribeVerifyNum > items[j].SubscribeVerifyNum
|
|
}
|
|
return items[i].Name < items[j].Name
|
|
})
|
|
return items, nil
|
|
}
|
|
|
|
func (i *imlAppModule) SimpleApps(ctx context.Context, keyword string) ([]*service_dto.SimpleAppItem, error) {
|
|
w := make(map[string]interface{})
|
|
w["as_app"] = true
|
|
services, err := i.serviceService.Search(ctx, keyword, w)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return utils.SliceToSlice(services, func(p *service.Service) *service_dto.SimpleAppItem {
|
|
return &service_dto.SimpleAppItem{
|
|
Id: p.Id,
|
|
Name: p.Name,
|
|
Description: p.Description,
|
|
Team: auto.UUID(p.Team),
|
|
}
|
|
}), nil
|
|
}
|
|
|
|
func (i *imlAppModule) MySimpleApps(ctx context.Context, keyword string) ([]*service_dto.SimpleAppItem, error) {
|
|
services, err := i.searchMyApps(ctx, "", keyword)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
items := make([]*service_dto.SimpleAppItem, 0, len(services))
|
|
for _, p := range services {
|
|
items = append(items, &service_dto.SimpleAppItem{
|
|
Id: p.Id,
|
|
Name: p.Name,
|
|
Description: p.Description,
|
|
Team: auto.UUID(p.Team),
|
|
})
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
func (i *imlAppModule) GetApp(ctx context.Context, appId string) (*service_dto.App, error) {
|
|
info, err := i.serviceService.Get(ctx, appId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !info.AsApp {
|
|
return nil, errors.New("not app")
|
|
}
|
|
return &service_dto.App{
|
|
Id: info.Id,
|
|
Name: info.Name,
|
|
Description: info.Description,
|
|
Team: auto.UUID(info.Team),
|
|
CreateTime: auto.TimeLabel(info.CreateTime),
|
|
UpdateTime: auto.TimeLabel(info.UpdateTime),
|
|
AsApp: info.AsApp,
|
|
}, nil
|
|
}
|
|
|
|
func (i *imlAppModule) DeleteApp(ctx context.Context, appId string) error {
|
|
info, err := i.serviceService.Get(ctx, appId)
|
|
if err != nil {
|
|
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
if !info.AsApp {
|
|
return errors.New("not app, can not delete")
|
|
}
|
|
|
|
return i.serviceService.Delete(ctx, appId)
|
|
}
|