package sync import ( "context" svc "dataengine/service/sync" "fmt" "gitea.redpowerfuture.com/red-future/common/beans" "github.com/gogf/gf/v2/frame/g" "github.com/sirupsen/logrus" ) var PlatformSyncController = new(syncCtrl) type syncCtrl struct{} // RefreshTokenReq 刷新 Token 请求 type RefreshTokenReq struct { g.Meta `path:"/refreshToken" method:"post" tags:"平台同步" summary:"刷新Token" dc:"刷新指定平台的认证Token(如腾讯OAuth2)"` PlatformCode string `json:"platformCode" v:"required" dc:"平台编码"` } // RefreshTokenRes 刷新 Token 响应 type RefreshTokenRes struct { Success bool `json:"success"` Message string `json:"message"` } // RefreshToken 手动触发 Token 刷新 // 由 PPGo_Job 定时调度,定期刷新平台 Token(如腾讯 OAuth2 access_token) func (c *syncCtrl) RefreshToken(ctx context.Context, req *RefreshTokenReq) (*RefreshTokenRes, error) { ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin", TenantId: 1}) logrus.Infof("[HTTP] 触发 Token 刷新: platform=%s", req.PlatformCode) pm := &svc.PlatformManager{} platform, _, err := pm.GetPlatformWithInterfaces(ctx, req.PlatformCode) if err != nil { return nil, fmt.Errorf("读取平台配置失败: %w", err) } if err := svc.RefreshTencentToken(ctx, platform); err != nil { return &RefreshTokenRes{Success: false, Message: err.Error()}, nil } return &RefreshTokenRes{Success: true, Message: "Token 刷新成功"}, nil } // TriggerSyncReq 触发同步请求 type TriggerSyncReq struct { g.Meta `path:"/trigger" method:"post" tags:"平台同步" summary:"触发同步" dc:"根据平台编码和接口编码触发数据同步"` PlatformCode string `json:"platformCode" v:"required" dc:"平台编码"` InterfaceCode string `json:"interfaceCode" v:"required" dc:"接口编码"` FullSync bool `json:"fullSync" dc:"是否全量同步,true=全量 false=增量"` } // TriggerSyncRes 触发同步响应 type TriggerSyncRes struct { Success bool `json:"success"` TableName string `json:"tableName"` TotalRows int `json:"totalRows"` InsertedRows int `json:"insertedRows"` Duration string `json:"duration"` } // TriggerSync 触发同步 func (c *syncCtrl) TriggerSync(ctx context.Context, req *TriggerSyncReq) (*TriggerSyncRes, error) { ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin", TenantId: 1}) logrus.Infof("触发同步: platform=%s, interface=%s, fullSync=%v", req.PlatformCode, req.InterfaceCode, req.FullSync) result, err := svc.SyncByConfig(ctx, req.PlatformCode, req.InterfaceCode, req.FullSync) if err != nil { return nil, err } return &TriggerSyncRes{ Success: true, TableName: result.TableName, TotalRows: result.TotalRows, InsertedRows: result.InsertedRows, Duration: result.Duration, }, nil } // QueryPlatformConfigReq 查询平台配置请求 type QueryPlatformConfigReq struct { g.Meta `path:"/config" method:"get" tags:"平台同步" summary:"查询平台配置" dc:"查看平台和接口配置"` PlatformCode string `json:"platformCode" v:"required" dc:"平台编码"` } // QueryPlatformConfigRes 查询平台配置响应 type QueryPlatformConfigRes struct { PlatformName string `json:"platformName"` PlatformCode string `json:"platformCode"` ApiBaseUrl string `json:"apiBaseUrl"` AuthType string `json:"authType"` HasToken bool `json:"hasToken"` InterfaceCount int `json:"interfaceCount"` Interfaces []struct { Code string `json:"code"` Name string `json:"name"` Url string `json:"url"` HasTableDef bool `json:"hasTableDef"` } `json:"interfaces"` } // ============================================================ // 批量操作(供 PPGo_Job 调度) // ============================================================ // TriggerAllSyncReq 触发全平台同步请求 type TriggerAllSyncReq struct { g.Meta `path:"/triggerAll" method:"post" tags:"平台同步" summary:"触发全平台同步" dc:"遍历所有ACTIVE平台/接口执行同步"` FullSync bool `json:"fullSync" dc:"true=强制全量同步,false=自动判断增量/全量"` } // TriggerAllSyncRes 触发全平台同步响应 type TriggerAllSyncRes struct { Success bool `json:"success"` Message string `json:"message"` Results []svc.SyncRunItemResult `json:"results,omitempty"` } // TriggerAllSync 触发全平台同步(等价于内部自动同步的一次完整执行) func (c *syncCtrl) TriggerAllSync(ctx context.Context, req *TriggerAllSyncReq) (*TriggerAllSyncRes, error) { ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin", TenantId: 1}) logrus.Infof("[HTTP] 触发全平台同步, fullSync=%v", req.FullSync) results := svc.TriggerAllSync(ctx, req.FullSync) hasError := false for _, r := range results { if !r.Success { hasError = true break } } res := &TriggerAllSyncRes{Results: results} if hasError { res.Success = false res.Message = "全平台同步完成,部分失败" } else { res.Success = true res.Message = "全平台同步完成" } return res, nil } // CompensateReq 触发补偿扫描请求 type CompensateReq struct { g.Meta `path:"/compensate" method:"post" tags:"平台同步" summary:"触发补偿扫描" dc:"扫描失败任务并重试"` } // CompensateRes 触发补偿扫描响应 type CompensateRes struct { Success bool `json:"success"` Message string `json:"message"` Result *svc.CompensateResult `json:"result,omitempty"` } // Compensate 触发补偿扫描(等价于内部补偿调度的一次完整执行) func (c *syncCtrl) Compensate(ctx context.Context, req *CompensateReq) (*CompensateRes, error) { ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin", TenantId: 1}) logrus.Info("[HTTP] 触发补偿扫描") result := svc.TriggerCompensation(ctx) res := &CompensateRes{Result: result} if result.TotalFailed == 0 { res.Success = true res.Message = "补偿扫描完成,没有待补偿任务" } else if result.Failed > 0 { res.Success = false res.Message = fmt.Sprintf("补偿扫描完成,共 %d 个失败任务,成功补偿 %d 个,失败 %d 个,已达最大重试 %d 个", result.TotalFailed, result.Succeeded, result.Failed, result.MaxRetryReached) } else { res.Success = true res.Message = fmt.Sprintf("补偿扫描完成,共 %d 个失败任务,成功补偿 %d 个", result.TotalFailed, result.Succeeded) } return res, nil } // QueryConfig 查询配置 func (c *syncCtrl) QueryConfig(ctx context.Context, req *QueryPlatformConfigReq) (*QueryPlatformConfigRes, error) { ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin", TenantId: 1}) pm := &svc.PlatformManager{} platform, interfaces, err := pm.GetPlatformWithInterfaces(ctx, req.PlatformCode) if err != nil { return nil, err } res := &QueryPlatformConfigRes{ PlatformName: platform.PlatformName, PlatformCode: platform.PlatformCode, ApiBaseUrl: platform.ApiBaseUrl, AuthType: platform.AuthType, HasToken: platform.AccessToken != "", InterfaceCount: len(interfaces), } for _, iface := range interfaces { res.Interfaces = append(res.Interfaces, struct { Code string `json:"code"` Name string `json:"name"` Url string `json:"url"` HasTableDef bool `json:"hasTableDef"` }{ Code: iface.Code, Name: iface.Name, Url: iface.Url, HasTableDef: iface.TableDefinition != nil && len(iface.TableDefinition) > 0, }) } return res, nil }