Compare commits

..

63 Commits

Author SHA1 Message Date
759e8384dd Merge branch 'feature-newApi-daiyb' 2025-12-15 09:40:53 +08:00
c85fad0d11 同步艺人 2025-12-15 09:40:46 +08:00
937527d657 Merge branch 'feature-newApi-daiyb' 2025-12-12 17:34:56 +08:00
99d643de39 1 2025-12-12 16:40:44 +08:00
aa30b879ef 过滤发送的平台 2025-12-12 16:38:21 +08:00
9c01669ef2 Update task.go 2025-12-12 14:10:25 +08:00
d495e168fd 刷新权限 2025-12-12 13:47:46 +08:00
0639bc973f 修改授权 2025-12-12 13:20:15 +08:00
a9368ce340 修改授权 2025-12-12 11:44:59 +08:00
bx1834938347-prog
c18bd4372f Merge branch 'wwq' 2025-12-12 10:19:50 +08:00
bx1834938347-prog
0a14025092 feat:ai生成图文新增选择分辨率 2025-12-12 09:51:56 +08:00
687d4face8 Update conf.ini 2025-12-11 21:17:51 +08:00
24d0d1f8a5 发布 2025-12-11 21:04:22 +08:00
ae060d8901 Update work.go 2025-12-11 16:01:04 +08:00
2b3a0e0afa Update media.go 2025-12-11 14:57:19 +08:00
bx1834938347-prog
92c6089096 feat:更改模型 2025-12-11 14:28:03 +08:00
bff4e13941 修改同步逻辑 2025-12-11 11:08:15 +08:00
05c423cb6d 消费队列 2025-12-10 11:22:26 +08:00
b73dd20458 Update media.go 2025-12-10 09:50:06 +08:00
6da16e7829 Update file.go 2025-12-09 20:44:48 +08:00
462c87b083 修改超时时间 2025-12-09 20:32:04 +08:00
afada2e43e 限制不能添加重复账号 2025-12-09 17:12:55 +08:00
ae92d3d86a 合并userinfo分支 2025-12-09 16:14:35 +08:00
9faf31971c 对接发布API 2025-12-09 16:01:54 +08:00
bx1834938347-prog
d53ace3306 style:ai生成图文 2025-12-09 14:56:17 +08:00
bx1834938347-prog
5f9aad3732 feat:调整prompt 2025-12-09 09:48:26 +08:00
bx1834938347-prog
16c6bae7d1 Merge branch 'dev' into wwq 2025-12-09 09:33:21 +08:00
bx1834938347-prog
8398fc4a6f Update imageContentProcessor.go 2025-12-08 17:10:23 +08:00
bx1834938347-prog
2413a185a2 feat:ai生成图文 2025-12-08 16:54:30 +08:00
bx1834938347-prog
5fe4be61b9 feat:ai生成图文 2025-12-08 14:12:38 +08:00
7bacad6415 完成auth授权 2025-12-08 13:13:25 +08:00
94f34d9562 添加新的pb 2025-12-08 13:10:51 +08:00
bx1834938347-prog
cfe6f2b756 style :ai生成图文 2025-12-08 11:18:44 +08:00
bx1834938347-prog
0518b547d8 Update generateImageToText.go 2025-12-08 09:02:36 +08:00
bx1834938347-prog
dea013471a feat:ai生成图文 2025-12-05 19:12:09 +08:00
bx1834938347-prog
a68502d957 feat:ai生成图文 2025-12-05 17:06:10 +08:00
27c26099e2 Update image.go 2025-12-05 16:41:48 +08:00
bx1834938347-prog
19fc4c0f77 Update imageContentProcessor.go 2025-12-05 16:23:03 +08:00
bx1834938347-prog
eae2db4942 feat:ai生成图文 2025-12-05 16:19:29 +08:00
bx1834938347-prog
0d45477f93 feat:ai生成图文 2025-12-05 15:46:21 +08:00
bx1834938347-prog
a093ab9e9c feat:ai生成图文 2025-12-05 15:12:11 +08:00
bx1834938347-prog
a60132086d feat:ai生成图文 2025-12-05 14:24:50 +08:00
bx1834938347-prog
b152fbe01b feat:ai生成图片 2025-12-05 14:16:48 +08:00
bx1834938347-prog
796ebd33b9 Update imageContentProcessor.go 2025-12-05 10:59:14 +08:00
bx1834938347-prog
7379c0b84e Update imageContentProcessor.go 2025-12-05 10:52:25 +08:00
bx1834938347-prog
b6c071fcec feat:修改ai生成图文 2025-12-05 10:43:23 +08:00
bx1834938347-prog
665c3e3b6a Update imageContentProcessor.go 2025-12-05 10:27:50 +08:00
bx1834938347-prog
6b63afbdff feat:ai生成图文 2025-12-05 10:21:51 +08:00
bx1834938347-prog
eb33d725eb feat:ai生成图文 2025-12-05 09:02:44 +08:00
bx1834938347-prog
d9233a0606 Update imageContentProcessor.go 2025-12-04 15:44:44 +08:00
181135eae4 图片黄反 2025-12-04 15:33:25 +08:00
bx1834938347-prog
b9259f1300 feat:ai生成图文 2025-12-04 15:25:40 +08:00
bx1834938347-prog
a753595b12 feat:ai批量生成图文 2025-12-04 14:59:19 +08:00
771ef3125b AI 生图 2025-12-04 14:33:04 +08:00
bx1834938347-prog
2eae0aa1a5 feat:新增ai批量生成图文 2025-12-04 11:18:35 +08:00
5756c0ea83 修改聊天 2025-12-04 11:05:50 +08:00
7c832a5d54 创建时间 2025-12-03 18:35:15 +08:00
f54a42d0a0 修改种子 2025-12-03 16:25:24 +08:00
ce7228652d 1 2025-12-03 16:16:00 +08:00
949f219932 Update image.go 2025-12-03 14:57:07 +08:00
76a5578396 Update image.go 2025-12-03 13:56:51 +08:00
c4d39be8aa Create 图文导入模板.xlsx 2025-12-03 13:49:50 +08:00
3a1db9b0ce 图文生成 2025-12-03 13:49:43 +08:00
67 changed files with 17282 additions and 7078 deletions

3258
api/aryshare/ayrshare.pb.go Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,194 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: pb/ayrshare.proto
package aryshare
import (
fmt "fmt"
math "math"
proto "github.com/golang/protobuf/proto"
_ "github.com/mwitkow/go-proto-validators"
github_com_mwitkow_go_proto_validators "github.com/mwitkow/go-proto-validators"
_ "google.golang.org/protobuf/types/descriptorpb"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
func (this *UserTag) Validate() error {
return nil
}
func (this *InstagramOptions) Validate() error {
for _, item := range this.UserTags {
if item != nil {
if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(item); err != nil {
return github_com_mwitkow_go_proto_validators.FieldError("UserTags", err)
}
}
}
return nil
}
func (this *TikTokOptions) Validate() error {
return nil
}
func (this *PostRequest) Validate() error {
if this.Post == "" {
return github_com_mwitkow_go_proto_validators.FieldError("Post", fmt.Errorf(`post内容不能为空`))
}
if len(this.Platforms) < 1 {
return github_com_mwitkow_go_proto_validators.FieldError("Platforms", fmt.Errorf(`platforms平台列表不能为空`))
}
if this.InstagramOptions != nil {
if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(this.InstagramOptions); err != nil {
return github_com_mwitkow_go_proto_validators.FieldError("InstagramOptions", err)
}
}
if this.TikTokOptions != nil {
if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(this.TikTokOptions); err != nil {
return github_com_mwitkow_go_proto_validators.FieldError("TikTokOptions", err)
}
}
return nil
}
func (this *PostId) Validate() error {
return nil
}
func (this *PostItem) Validate() error {
for _, item := range this.PostIds {
if item != nil {
if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(item); err != nil {
return github_com_mwitkow_go_proto_validators.FieldError("PostIds", err)
}
}
}
return nil
}
func (this *PostResponse) Validate() error {
for _, item := range this.Posts {
if item != nil {
if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(item); err != nil {
return github_com_mwitkow_go_proto_validators.FieldError("Posts", err)
}
}
}
for _, item := range this.PostIds {
if item != nil {
if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(item); err != nil {
return github_com_mwitkow_go_proto_validators.FieldError("PostIds", err)
}
}
}
return nil
}
func (this *GetPostRequest) Validate() error {
if this.Id == "" {
return github_com_mwitkow_go_proto_validators.FieldError("Id", fmt.Errorf(`帖子ID不能为空`))
}
return nil
}
func (this *GetPostResponse) Validate() error {
for _, item := range this.PostIds {
if item != nil {
if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(item); err != nil {
return github_com_mwitkow_go_proto_validators.FieldError("PostIds", err)
}
}
}
return nil
}
func (this *GetUserRequest) Validate() error {
return nil
}
func (this *Timestamp) Validate() error {
return nil
}
func (this *TwitterUsage) Validate() error {
return nil
}
func (this *DisplayName) Validate() error {
if this.TwitterUsage != nil {
if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(this.TwitterUsage); err != nil {
return github_com_mwitkow_go_proto_validators.FieldError("TwitterUsage", err)
}
}
return nil
}
func (this *GetUserResponse) Validate() error {
if this.Created != nil {
if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(this.Created); err != nil {
return github_com_mwitkow_go_proto_validators.FieldError("Created", err)
}
}
for _, item := range this.DisplayNames {
if item != nil {
if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(item); err != nil {
return github_com_mwitkow_go_proto_validators.FieldError("DisplayNames", err)
}
}
}
return nil
}
func (this *CreateProfileRequest) Validate() error {
if this.Title == "" {
return github_com_mwitkow_go_proto_validators.FieldError("Title", fmt.Errorf(`title不能为空`))
}
return nil
}
func (this *CreateProfileResponse) Validate() error {
return nil
}
func (this *GetProfilesRequest) Validate() error {
return nil
}
func (this *ProfileItem) Validate() error {
if this.Created != nil {
if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(this.Created); err != nil {
return github_com_mwitkow_go_proto_validators.FieldError("Created", err)
}
}
return nil
}
func (this *Pagination) Validate() error {
return nil
}
func (this *GetProfilesResponse) Validate() error {
for _, item := range this.Profiles {
if item != nil {
if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(item); err != nil {
return github_com_mwitkow_go_proto_validators.FieldError("Profiles", err)
}
}
}
if this.Pagination != nil {
if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(this.Pagination); err != nil {
return github_com_mwitkow_go_proto_validators.FieldError("Pagination", err)
}
}
return nil
}
func (this *Email) Validate() error {
return nil
}
func (this *GenerateJWTRequest) Validate() error {
if this.Domain == "" {
return github_com_mwitkow_go_proto_validators.FieldError("Domain", fmt.Errorf(`domain不能为空`))
}
if this.PrivateKey == "" {
return github_com_mwitkow_go_proto_validators.FieldError("PrivateKey", fmt.Errorf(`privateKey不能为空`))
}
if this.ProfileKey == "" {
return github_com_mwitkow_go_proto_validators.FieldError("ProfileKey", fmt.Errorf(`profileKey不能为空`))
}
if this.Email != nil {
if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(this.Email); err != nil {
return github_com_mwitkow_go_proto_validators.FieldError("Email", err)
}
}
return nil
}
func (this *GenerateJWTResponse) Validate() error {
return nil
}

View File

@ -0,0 +1,378 @@
// Code generated by protoc-gen-go-triple. DO NOT EDIT.
// versions:
// - protoc-gen-go-triple v1.0.8
// - protoc v3.21.1
// source: pb/ayrshare.proto
package aryshare
import (
context "context"
protocol "dubbo.apache.org/dubbo-go/v3/protocol"
dubbo3 "dubbo.apache.org/dubbo-go/v3/protocol/dubbo3"
invocation "dubbo.apache.org/dubbo-go/v3/protocol/invocation"
grpc_go "github.com/dubbogo/grpc-go"
codes "github.com/dubbogo/grpc-go/codes"
metadata "github.com/dubbogo/grpc-go/metadata"
status "github.com/dubbogo/grpc-go/status"
common "github.com/dubbogo/triple/pkg/common"
constant "github.com/dubbogo/triple/pkg/common/constant"
triple "github.com/dubbogo/triple/pkg/triple"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc_go.SupportPackageIsVersion7
// AyrshareClient is the client API for Ayrshare service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type AyrshareClient interface {
// 帖子相关 api
Post(ctx context.Context, in *PostRequest, opts ...grpc_go.CallOption) (*PostResponse, common.ErrorWithAttachment)
GetPost(ctx context.Context, in *GetPostRequest, opts ...grpc_go.CallOption) (*GetPostResponse, common.ErrorWithAttachment)
// 用户相关 api
GetUser(ctx context.Context, in *GetUserRequest, opts ...grpc_go.CallOption) (*GetUserResponse, common.ErrorWithAttachment)
// 用户配置相关 api
CreateProfile(ctx context.Context, in *CreateProfileRequest, opts ...grpc_go.CallOption) (*CreateProfileResponse, common.ErrorWithAttachment)
GetProfiles(ctx context.Context, in *GetProfilesRequest, opts ...grpc_go.CallOption) (*GetProfilesResponse, common.ErrorWithAttachment)
GenerateJWT(ctx context.Context, in *GenerateJWTRequest, opts ...grpc_go.CallOption) (*GenerateJWTResponse, common.ErrorWithAttachment)
}
type ayrshareClient struct {
cc *triple.TripleConn
}
type AyrshareClientImpl struct {
Post func(ctx context.Context, in *PostRequest) (*PostResponse, error)
GetPost func(ctx context.Context, in *GetPostRequest) (*GetPostResponse, error)
GetUser func(ctx context.Context, in *GetUserRequest) (*GetUserResponse, error)
CreateProfile func(ctx context.Context, in *CreateProfileRequest) (*CreateProfileResponse, error)
GetProfiles func(ctx context.Context, in *GetProfilesRequest) (*GetProfilesResponse, error)
GenerateJWT func(ctx context.Context, in *GenerateJWTRequest) (*GenerateJWTResponse, error)
}
func (c *AyrshareClientImpl) GetDubboStub(cc *triple.TripleConn) AyrshareClient {
return NewAyrshareClient(cc)
}
func (c *AyrshareClientImpl) XXX_InterfaceName() string {
return "aryshare.Ayrshare"
}
func NewAyrshareClient(cc *triple.TripleConn) AyrshareClient {
return &ayrshareClient{cc}
}
func (c *ayrshareClient) Post(ctx context.Context, in *PostRequest, opts ...grpc_go.CallOption) (*PostResponse, common.ErrorWithAttachment) {
out := new(PostResponse)
interfaceKey := ctx.Value(constant.InterfaceKey).(string)
return out, c.cc.Invoke(ctx, "/"+interfaceKey+"/Post", in, out)
}
func (c *ayrshareClient) GetPost(ctx context.Context, in *GetPostRequest, opts ...grpc_go.CallOption) (*GetPostResponse, common.ErrorWithAttachment) {
out := new(GetPostResponse)
interfaceKey := ctx.Value(constant.InterfaceKey).(string)
return out, c.cc.Invoke(ctx, "/"+interfaceKey+"/GetPost", in, out)
}
func (c *ayrshareClient) GetUser(ctx context.Context, in *GetUserRequest, opts ...grpc_go.CallOption) (*GetUserResponse, common.ErrorWithAttachment) {
out := new(GetUserResponse)
interfaceKey := ctx.Value(constant.InterfaceKey).(string)
return out, c.cc.Invoke(ctx, "/"+interfaceKey+"/GetUser", in, out)
}
func (c *ayrshareClient) CreateProfile(ctx context.Context, in *CreateProfileRequest, opts ...grpc_go.CallOption) (*CreateProfileResponse, common.ErrorWithAttachment) {
out := new(CreateProfileResponse)
interfaceKey := ctx.Value(constant.InterfaceKey).(string)
return out, c.cc.Invoke(ctx, "/"+interfaceKey+"/CreateProfile", in, out)
}
func (c *ayrshareClient) GetProfiles(ctx context.Context, in *GetProfilesRequest, opts ...grpc_go.CallOption) (*GetProfilesResponse, common.ErrorWithAttachment) {
out := new(GetProfilesResponse)
interfaceKey := ctx.Value(constant.InterfaceKey).(string)
return out, c.cc.Invoke(ctx, "/"+interfaceKey+"/GetProfiles", in, out)
}
func (c *ayrshareClient) GenerateJWT(ctx context.Context, in *GenerateJWTRequest, opts ...grpc_go.CallOption) (*GenerateJWTResponse, common.ErrorWithAttachment) {
out := new(GenerateJWTResponse)
interfaceKey := ctx.Value(constant.InterfaceKey).(string)
return out, c.cc.Invoke(ctx, "/"+interfaceKey+"/GenerateJWT", in, out)
}
// AyrshareServer is the server API for Ayrshare service.
// All implementations must embed UnimplementedAyrshareServer
// for forward compatibility
type AyrshareServer interface {
// 帖子相关 api
Post(context.Context, *PostRequest) (*PostResponse, error)
GetPost(context.Context, *GetPostRequest) (*GetPostResponse, error)
// 用户相关 api
GetUser(context.Context, *GetUserRequest) (*GetUserResponse, error)
// 用户配置相关 api
CreateProfile(context.Context, *CreateProfileRequest) (*CreateProfileResponse, error)
GetProfiles(context.Context, *GetProfilesRequest) (*GetProfilesResponse, error)
GenerateJWT(context.Context, *GenerateJWTRequest) (*GenerateJWTResponse, error)
mustEmbedUnimplementedAyrshareServer()
}
// UnimplementedAyrshareServer must be embedded to have forward compatible implementations.
type UnimplementedAyrshareServer struct {
proxyImpl protocol.Invoker
}
func (UnimplementedAyrshareServer) Post(context.Context, *PostRequest) (*PostResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Post not implemented")
}
func (UnimplementedAyrshareServer) GetPost(context.Context, *GetPostRequest) (*GetPostResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetPost not implemented")
}
func (UnimplementedAyrshareServer) GetUser(context.Context, *GetUserRequest) (*GetUserResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetUser not implemented")
}
func (UnimplementedAyrshareServer) CreateProfile(context.Context, *CreateProfileRequest) (*CreateProfileResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateProfile not implemented")
}
func (UnimplementedAyrshareServer) GetProfiles(context.Context, *GetProfilesRequest) (*GetProfilesResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetProfiles not implemented")
}
func (UnimplementedAyrshareServer) GenerateJWT(context.Context, *GenerateJWTRequest) (*GenerateJWTResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GenerateJWT not implemented")
}
func (s *UnimplementedAyrshareServer) XXX_SetProxyImpl(impl protocol.Invoker) {
s.proxyImpl = impl
}
func (s *UnimplementedAyrshareServer) XXX_GetProxyImpl() protocol.Invoker {
return s.proxyImpl
}
func (s *UnimplementedAyrshareServer) XXX_ServiceDesc() *grpc_go.ServiceDesc {
return &Ayrshare_ServiceDesc
}
func (s *UnimplementedAyrshareServer) XXX_InterfaceName() string {
return "aryshare.Ayrshare"
}
func (UnimplementedAyrshareServer) mustEmbedUnimplementedAyrshareServer() {}
// UnsafeAyrshareServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to AyrshareServer will
// result in compilation errors.
type UnsafeAyrshareServer interface {
mustEmbedUnimplementedAyrshareServer()
}
func RegisterAyrshareServer(s grpc_go.ServiceRegistrar, srv AyrshareServer) {
s.RegisterService(&Ayrshare_ServiceDesc, srv)
}
func _Ayrshare_Post_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc_go.UnaryServerInterceptor) (interface{}, error) {
in := new(PostRequest)
if err := dec(in); err != nil {
return nil, err
}
base := srv.(dubbo3.Dubbo3GrpcService)
args := []interface{}{}
args = append(args, in)
md, _ := metadata.FromIncomingContext(ctx)
invAttachment := make(map[string]interface{}, len(md))
for k, v := range md {
invAttachment[k] = v
}
invo := invocation.NewRPCInvocation("Post", args, invAttachment)
if interceptor == nil {
result := base.XXX_GetProxyImpl().Invoke(ctx, invo)
return result, result.Error()
}
info := &grpc_go.UnaryServerInfo{
Server: srv,
FullMethod: ctx.Value("XXX_TRIPLE_GO_INTERFACE_NAME").(string),
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
result := base.XXX_GetProxyImpl().Invoke(ctx, invo)
return result, result.Error()
}
return interceptor(ctx, in, info, handler)
}
func _Ayrshare_GetPost_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc_go.UnaryServerInterceptor) (interface{}, error) {
in := new(GetPostRequest)
if err := dec(in); err != nil {
return nil, err
}
base := srv.(dubbo3.Dubbo3GrpcService)
args := []interface{}{}
args = append(args, in)
md, _ := metadata.FromIncomingContext(ctx)
invAttachment := make(map[string]interface{}, len(md))
for k, v := range md {
invAttachment[k] = v
}
invo := invocation.NewRPCInvocation("GetPost", args, invAttachment)
if interceptor == nil {
result := base.XXX_GetProxyImpl().Invoke(ctx, invo)
return result, result.Error()
}
info := &grpc_go.UnaryServerInfo{
Server: srv,
FullMethod: ctx.Value("XXX_TRIPLE_GO_INTERFACE_NAME").(string),
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
result := base.XXX_GetProxyImpl().Invoke(ctx, invo)
return result, result.Error()
}
return interceptor(ctx, in, info, handler)
}
func _Ayrshare_GetUser_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc_go.UnaryServerInterceptor) (interface{}, error) {
in := new(GetUserRequest)
if err := dec(in); err != nil {
return nil, err
}
base := srv.(dubbo3.Dubbo3GrpcService)
args := []interface{}{}
args = append(args, in)
md, _ := metadata.FromIncomingContext(ctx)
invAttachment := make(map[string]interface{}, len(md))
for k, v := range md {
invAttachment[k] = v
}
invo := invocation.NewRPCInvocation("GetUser", args, invAttachment)
if interceptor == nil {
result := base.XXX_GetProxyImpl().Invoke(ctx, invo)
return result, result.Error()
}
info := &grpc_go.UnaryServerInfo{
Server: srv,
FullMethod: ctx.Value("XXX_TRIPLE_GO_INTERFACE_NAME").(string),
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
result := base.XXX_GetProxyImpl().Invoke(ctx, invo)
return result, result.Error()
}
return interceptor(ctx, in, info, handler)
}
func _Ayrshare_CreateProfile_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc_go.UnaryServerInterceptor) (interface{}, error) {
in := new(CreateProfileRequest)
if err := dec(in); err != nil {
return nil, err
}
base := srv.(dubbo3.Dubbo3GrpcService)
args := []interface{}{}
args = append(args, in)
md, _ := metadata.FromIncomingContext(ctx)
invAttachment := make(map[string]interface{}, len(md))
for k, v := range md {
invAttachment[k] = v
}
invo := invocation.NewRPCInvocation("CreateProfile", args, invAttachment)
if interceptor == nil {
result := base.XXX_GetProxyImpl().Invoke(ctx, invo)
return result, result.Error()
}
info := &grpc_go.UnaryServerInfo{
Server: srv,
FullMethod: ctx.Value("XXX_TRIPLE_GO_INTERFACE_NAME").(string),
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
result := base.XXX_GetProxyImpl().Invoke(ctx, invo)
return result, result.Error()
}
return interceptor(ctx, in, info, handler)
}
func _Ayrshare_GetProfiles_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc_go.UnaryServerInterceptor) (interface{}, error) {
in := new(GetProfilesRequest)
if err := dec(in); err != nil {
return nil, err
}
base := srv.(dubbo3.Dubbo3GrpcService)
args := []interface{}{}
args = append(args, in)
md, _ := metadata.FromIncomingContext(ctx)
invAttachment := make(map[string]interface{}, len(md))
for k, v := range md {
invAttachment[k] = v
}
invo := invocation.NewRPCInvocation("GetProfiles", args, invAttachment)
if interceptor == nil {
result := base.XXX_GetProxyImpl().Invoke(ctx, invo)
return result, result.Error()
}
info := &grpc_go.UnaryServerInfo{
Server: srv,
FullMethod: ctx.Value("XXX_TRIPLE_GO_INTERFACE_NAME").(string),
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
result := base.XXX_GetProxyImpl().Invoke(ctx, invo)
return result, result.Error()
}
return interceptor(ctx, in, info, handler)
}
func _Ayrshare_GenerateJWT_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc_go.UnaryServerInterceptor) (interface{}, error) {
in := new(GenerateJWTRequest)
if err := dec(in); err != nil {
return nil, err
}
base := srv.(dubbo3.Dubbo3GrpcService)
args := []interface{}{}
args = append(args, in)
md, _ := metadata.FromIncomingContext(ctx)
invAttachment := make(map[string]interface{}, len(md))
for k, v := range md {
invAttachment[k] = v
}
invo := invocation.NewRPCInvocation("GenerateJWT", args, invAttachment)
if interceptor == nil {
result := base.XXX_GetProxyImpl().Invoke(ctx, invo)
return result, result.Error()
}
info := &grpc_go.UnaryServerInfo{
Server: srv,
FullMethod: ctx.Value("XXX_TRIPLE_GO_INTERFACE_NAME").(string),
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
result := base.XXX_GetProxyImpl().Invoke(ctx, invo)
return result, result.Error()
}
return interceptor(ctx, in, info, handler)
}
// Ayrshare_ServiceDesc is the grpc_go.ServiceDesc for Ayrshare service.
// It's only intended for direct use with grpc_go.RegisterService,
// and not to be introspected or modified (even as a copy)
var Ayrshare_ServiceDesc = grpc_go.ServiceDesc{
ServiceName: "aryshare.Ayrshare",
HandlerType: (*AyrshareServer)(nil),
Methods: []grpc_go.MethodDesc{
{
MethodName: "Post",
Handler: _Ayrshare_Post_Handler,
},
{
MethodName: "GetPost",
Handler: _Ayrshare_GetPost_Handler,
},
{
MethodName: "GetUser",
Handler: _Ayrshare_GetUser_Handler,
},
{
MethodName: "CreateProfile",
Handler: _Ayrshare_CreateProfile_Handler,
},
{
MethodName: "GetProfiles",
Handler: _Ayrshare_GetProfiles_Handler,
},
{
MethodName: "GenerateJWT",
Handler: _Ayrshare_GenerateJWT_Handler,
},
},
Streams: []grpc_go.StreamDesc{},
Metadata: "pb/ayrshare.proto",
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
// Code generated by protoc-gen-go-triple. DO NOT EDIT. // Code generated by protoc-gen-go-triple. DO NOT EDIT.
// versions: // versions:
// - protoc-gen-go-triple v1.0.8 // - protoc-gen-go-triple v1.0.8
// - protoc v3.21.1 // - protoc v6.32.0--rc2
// source: pb/fiee/cast.proto // source: pb/fiee/cast.proto
package cast package cast

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,194 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: pb/ayrshare.proto
package aryshare
import (
fmt "fmt"
math "math"
proto "github.com/golang/protobuf/proto"
_ "github.com/mwitkow/go-proto-validators"
github_com_mwitkow_go_proto_validators "github.com/mwitkow/go-proto-validators"
_ "google.golang.org/protobuf/types/descriptorpb"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
func (this *UserTag) Validate() error {
return nil
}
func (this *InstagramOptions) Validate() error {
for _, item := range this.UserTags {
if item != nil {
if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(item); err != nil {
return github_com_mwitkow_go_proto_validators.FieldError("UserTags", err)
}
}
}
return nil
}
func (this *TikTokOptions) Validate() error {
return nil
}
func (this *PostRequest) Validate() error {
if this.Post == "" {
return github_com_mwitkow_go_proto_validators.FieldError("Post", fmt.Errorf(`post内容不能为空`))
}
if len(this.Platforms) < 1 {
return github_com_mwitkow_go_proto_validators.FieldError("Platforms", fmt.Errorf(`platforms平台列表不能为空`))
}
if this.InstagramOptions != nil {
if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(this.InstagramOptions); err != nil {
return github_com_mwitkow_go_proto_validators.FieldError("InstagramOptions", err)
}
}
if this.TikTokOptions != nil {
if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(this.TikTokOptions); err != nil {
return github_com_mwitkow_go_proto_validators.FieldError("TikTokOptions", err)
}
}
return nil
}
func (this *PostId) Validate() error {
return nil
}
func (this *PostItem) Validate() error {
for _, item := range this.PostIds {
if item != nil {
if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(item); err != nil {
return github_com_mwitkow_go_proto_validators.FieldError("PostIds", err)
}
}
}
return nil
}
func (this *PostResponse) Validate() error {
for _, item := range this.Posts {
if item != nil {
if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(item); err != nil {
return github_com_mwitkow_go_proto_validators.FieldError("Posts", err)
}
}
}
for _, item := range this.PostIds {
if item != nil {
if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(item); err != nil {
return github_com_mwitkow_go_proto_validators.FieldError("PostIds", err)
}
}
}
return nil
}
func (this *GetPostRequest) Validate() error {
if this.Id == "" {
return github_com_mwitkow_go_proto_validators.FieldError("Id", fmt.Errorf(`帖子ID不能为空`))
}
return nil
}
func (this *GetPostResponse) Validate() error {
for _, item := range this.PostIds {
if item != nil {
if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(item); err != nil {
return github_com_mwitkow_go_proto_validators.FieldError("PostIds", err)
}
}
}
return nil
}
func (this *GetUserRequest) Validate() error {
return nil
}
func (this *Timestamp) Validate() error {
return nil
}
func (this *TwitterUsage) Validate() error {
return nil
}
func (this *DisplayName) Validate() error {
if this.TwitterUsage != nil {
if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(this.TwitterUsage); err != nil {
return github_com_mwitkow_go_proto_validators.FieldError("TwitterUsage", err)
}
}
return nil
}
func (this *GetUserResponse) Validate() error {
if this.Created != nil {
if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(this.Created); err != nil {
return github_com_mwitkow_go_proto_validators.FieldError("Created", err)
}
}
for _, item := range this.DisplayNames {
if item != nil {
if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(item); err != nil {
return github_com_mwitkow_go_proto_validators.FieldError("DisplayNames", err)
}
}
}
return nil
}
func (this *CreateProfileRequest) Validate() error {
if this.Title == "" {
return github_com_mwitkow_go_proto_validators.FieldError("Title", fmt.Errorf(`title不能为空`))
}
return nil
}
func (this *CreateProfileResponse) Validate() error {
return nil
}
func (this *GetProfilesRequest) Validate() error {
return nil
}
func (this *ProfileItem) Validate() error {
if this.Created != nil {
if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(this.Created); err != nil {
return github_com_mwitkow_go_proto_validators.FieldError("Created", err)
}
}
return nil
}
func (this *Pagination) Validate() error {
return nil
}
func (this *GetProfilesResponse) Validate() error {
for _, item := range this.Profiles {
if item != nil {
if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(item); err != nil {
return github_com_mwitkow_go_proto_validators.FieldError("Profiles", err)
}
}
}
if this.Pagination != nil {
if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(this.Pagination); err != nil {
return github_com_mwitkow_go_proto_validators.FieldError("Pagination", err)
}
}
return nil
}
func (this *Email) Validate() error {
return nil
}
func (this *GenerateJWTRequest) Validate() error {
if this.Domain == "" {
return github_com_mwitkow_go_proto_validators.FieldError("Domain", fmt.Errorf(`domain不能为空`))
}
if this.PrivateKey == "" {
return github_com_mwitkow_go_proto_validators.FieldError("PrivateKey", fmt.Errorf(`privateKey不能为空`))
}
if this.ProfileKey == "" {
return github_com_mwitkow_go_proto_validators.FieldError("ProfileKey", fmt.Errorf(`profileKey不能为空`))
}
if this.Email != nil {
if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(this.Email); err != nil {
return github_com_mwitkow_go_proto_validators.FieldError("Email", err)
}
}
return nil
}
func (this *GenerateJWTResponse) Validate() error {
return nil
}

View File

@ -0,0 +1,378 @@
// Code generated by protoc-gen-go-triple. DO NOT EDIT.
// versions:
// - protoc-gen-go-triple v1.0.8
// - protoc v3.21.1
// source: pb/ayrshare.proto
package aryshare
import (
context "context"
protocol "dubbo.apache.org/dubbo-go/v3/protocol"
dubbo3 "dubbo.apache.org/dubbo-go/v3/protocol/dubbo3"
invocation "dubbo.apache.org/dubbo-go/v3/protocol/invocation"
grpc_go "github.com/dubbogo/grpc-go"
codes "github.com/dubbogo/grpc-go/codes"
metadata "github.com/dubbogo/grpc-go/metadata"
status "github.com/dubbogo/grpc-go/status"
common "github.com/dubbogo/triple/pkg/common"
constant "github.com/dubbogo/triple/pkg/common/constant"
triple "github.com/dubbogo/triple/pkg/triple"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc_go.SupportPackageIsVersion7
// AyrshareClient is the client API for Ayrshare service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type AyrshareClient interface {
// 帖子相关 api
Post(ctx context.Context, in *PostRequest, opts ...grpc_go.CallOption) (*PostResponse, common.ErrorWithAttachment)
GetPost(ctx context.Context, in *GetPostRequest, opts ...grpc_go.CallOption) (*GetPostResponse, common.ErrorWithAttachment)
// 用户相关 api
GetUser(ctx context.Context, in *GetUserRequest, opts ...grpc_go.CallOption) (*GetUserResponse, common.ErrorWithAttachment)
// 用户配置相关 api
CreateProfile(ctx context.Context, in *CreateProfileRequest, opts ...grpc_go.CallOption) (*CreateProfileResponse, common.ErrorWithAttachment)
GetProfiles(ctx context.Context, in *GetProfilesRequest, opts ...grpc_go.CallOption) (*GetProfilesResponse, common.ErrorWithAttachment)
GenerateJWT(ctx context.Context, in *GenerateJWTRequest, opts ...grpc_go.CallOption) (*GenerateJWTResponse, common.ErrorWithAttachment)
}
type ayrshareClient struct {
cc *triple.TripleConn
}
type AyrshareClientImpl struct {
Post func(ctx context.Context, in *PostRequest) (*PostResponse, error)
GetPost func(ctx context.Context, in *GetPostRequest) (*GetPostResponse, error)
GetUser func(ctx context.Context, in *GetUserRequest) (*GetUserResponse, error)
CreateProfile func(ctx context.Context, in *CreateProfileRequest) (*CreateProfileResponse, error)
GetProfiles func(ctx context.Context, in *GetProfilesRequest) (*GetProfilesResponse, error)
GenerateJWT func(ctx context.Context, in *GenerateJWTRequest) (*GenerateJWTResponse, error)
}
func (c *AyrshareClientImpl) GetDubboStub(cc *triple.TripleConn) AyrshareClient {
return NewAyrshareClient(cc)
}
func (c *AyrshareClientImpl) XXX_InterfaceName() string {
return "aryshare.Ayrshare"
}
func NewAyrshareClient(cc *triple.TripleConn) AyrshareClient {
return &ayrshareClient{cc}
}
func (c *ayrshareClient) Post(ctx context.Context, in *PostRequest, opts ...grpc_go.CallOption) (*PostResponse, common.ErrorWithAttachment) {
out := new(PostResponse)
interfaceKey := ctx.Value(constant.InterfaceKey).(string)
return out, c.cc.Invoke(ctx, "/"+interfaceKey+"/Post", in, out)
}
func (c *ayrshareClient) GetPost(ctx context.Context, in *GetPostRequest, opts ...grpc_go.CallOption) (*GetPostResponse, common.ErrorWithAttachment) {
out := new(GetPostResponse)
interfaceKey := ctx.Value(constant.InterfaceKey).(string)
return out, c.cc.Invoke(ctx, "/"+interfaceKey+"/GetPost", in, out)
}
func (c *ayrshareClient) GetUser(ctx context.Context, in *GetUserRequest, opts ...grpc_go.CallOption) (*GetUserResponse, common.ErrorWithAttachment) {
out := new(GetUserResponse)
interfaceKey := ctx.Value(constant.InterfaceKey).(string)
return out, c.cc.Invoke(ctx, "/"+interfaceKey+"/GetUser", in, out)
}
func (c *ayrshareClient) CreateProfile(ctx context.Context, in *CreateProfileRequest, opts ...grpc_go.CallOption) (*CreateProfileResponse, common.ErrorWithAttachment) {
out := new(CreateProfileResponse)
interfaceKey := ctx.Value(constant.InterfaceKey).(string)
return out, c.cc.Invoke(ctx, "/"+interfaceKey+"/CreateProfile", in, out)
}
func (c *ayrshareClient) GetProfiles(ctx context.Context, in *GetProfilesRequest, opts ...grpc_go.CallOption) (*GetProfilesResponse, common.ErrorWithAttachment) {
out := new(GetProfilesResponse)
interfaceKey := ctx.Value(constant.InterfaceKey).(string)
return out, c.cc.Invoke(ctx, "/"+interfaceKey+"/GetProfiles", in, out)
}
func (c *ayrshareClient) GenerateJWT(ctx context.Context, in *GenerateJWTRequest, opts ...grpc_go.CallOption) (*GenerateJWTResponse, common.ErrorWithAttachment) {
out := new(GenerateJWTResponse)
interfaceKey := ctx.Value(constant.InterfaceKey).(string)
return out, c.cc.Invoke(ctx, "/"+interfaceKey+"/GenerateJWT", in, out)
}
// AyrshareServer is the server API for Ayrshare service.
// All implementations must embed UnimplementedAyrshareServer
// for forward compatibility
type AyrshareServer interface {
// 帖子相关 api
Post(context.Context, *PostRequest) (*PostResponse, error)
GetPost(context.Context, *GetPostRequest) (*GetPostResponse, error)
// 用户相关 api
GetUser(context.Context, *GetUserRequest) (*GetUserResponse, error)
// 用户配置相关 api
CreateProfile(context.Context, *CreateProfileRequest) (*CreateProfileResponse, error)
GetProfiles(context.Context, *GetProfilesRequest) (*GetProfilesResponse, error)
GenerateJWT(context.Context, *GenerateJWTRequest) (*GenerateJWTResponse, error)
mustEmbedUnimplementedAyrshareServer()
}
// UnimplementedAyrshareServer must be embedded to have forward compatible implementations.
type UnimplementedAyrshareServer struct {
proxyImpl protocol.Invoker
}
func (UnimplementedAyrshareServer) Post(context.Context, *PostRequest) (*PostResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Post not implemented")
}
func (UnimplementedAyrshareServer) GetPost(context.Context, *GetPostRequest) (*GetPostResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetPost not implemented")
}
func (UnimplementedAyrshareServer) GetUser(context.Context, *GetUserRequest) (*GetUserResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetUser not implemented")
}
func (UnimplementedAyrshareServer) CreateProfile(context.Context, *CreateProfileRequest) (*CreateProfileResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateProfile not implemented")
}
func (UnimplementedAyrshareServer) GetProfiles(context.Context, *GetProfilesRequest) (*GetProfilesResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetProfiles not implemented")
}
func (UnimplementedAyrshareServer) GenerateJWT(context.Context, *GenerateJWTRequest) (*GenerateJWTResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GenerateJWT not implemented")
}
func (s *UnimplementedAyrshareServer) XXX_SetProxyImpl(impl protocol.Invoker) {
s.proxyImpl = impl
}
func (s *UnimplementedAyrshareServer) XXX_GetProxyImpl() protocol.Invoker {
return s.proxyImpl
}
func (s *UnimplementedAyrshareServer) XXX_ServiceDesc() *grpc_go.ServiceDesc {
return &Ayrshare_ServiceDesc
}
func (s *UnimplementedAyrshareServer) XXX_InterfaceName() string {
return "aryshare.Ayrshare"
}
func (UnimplementedAyrshareServer) mustEmbedUnimplementedAyrshareServer() {}
// UnsafeAyrshareServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to AyrshareServer will
// result in compilation errors.
type UnsafeAyrshareServer interface {
mustEmbedUnimplementedAyrshareServer()
}
func RegisterAyrshareServer(s grpc_go.ServiceRegistrar, srv AyrshareServer) {
s.RegisterService(&Ayrshare_ServiceDesc, srv)
}
func _Ayrshare_Post_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc_go.UnaryServerInterceptor) (interface{}, error) {
in := new(PostRequest)
if err := dec(in); err != nil {
return nil, err
}
base := srv.(dubbo3.Dubbo3GrpcService)
args := []interface{}{}
args = append(args, in)
md, _ := metadata.FromIncomingContext(ctx)
invAttachment := make(map[string]interface{}, len(md))
for k, v := range md {
invAttachment[k] = v
}
invo := invocation.NewRPCInvocation("Post", args, invAttachment)
if interceptor == nil {
result := base.XXX_GetProxyImpl().Invoke(ctx, invo)
return result, result.Error()
}
info := &grpc_go.UnaryServerInfo{
Server: srv,
FullMethod: ctx.Value("XXX_TRIPLE_GO_INTERFACE_NAME").(string),
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
result := base.XXX_GetProxyImpl().Invoke(ctx, invo)
return result, result.Error()
}
return interceptor(ctx, in, info, handler)
}
func _Ayrshare_GetPost_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc_go.UnaryServerInterceptor) (interface{}, error) {
in := new(GetPostRequest)
if err := dec(in); err != nil {
return nil, err
}
base := srv.(dubbo3.Dubbo3GrpcService)
args := []interface{}{}
args = append(args, in)
md, _ := metadata.FromIncomingContext(ctx)
invAttachment := make(map[string]interface{}, len(md))
for k, v := range md {
invAttachment[k] = v
}
invo := invocation.NewRPCInvocation("GetPost", args, invAttachment)
if interceptor == nil {
result := base.XXX_GetProxyImpl().Invoke(ctx, invo)
return result, result.Error()
}
info := &grpc_go.UnaryServerInfo{
Server: srv,
FullMethod: ctx.Value("XXX_TRIPLE_GO_INTERFACE_NAME").(string),
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
result := base.XXX_GetProxyImpl().Invoke(ctx, invo)
return result, result.Error()
}
return interceptor(ctx, in, info, handler)
}
func _Ayrshare_GetUser_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc_go.UnaryServerInterceptor) (interface{}, error) {
in := new(GetUserRequest)
if err := dec(in); err != nil {
return nil, err
}
base := srv.(dubbo3.Dubbo3GrpcService)
args := []interface{}{}
args = append(args, in)
md, _ := metadata.FromIncomingContext(ctx)
invAttachment := make(map[string]interface{}, len(md))
for k, v := range md {
invAttachment[k] = v
}
invo := invocation.NewRPCInvocation("GetUser", args, invAttachment)
if interceptor == nil {
result := base.XXX_GetProxyImpl().Invoke(ctx, invo)
return result, result.Error()
}
info := &grpc_go.UnaryServerInfo{
Server: srv,
FullMethod: ctx.Value("XXX_TRIPLE_GO_INTERFACE_NAME").(string),
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
result := base.XXX_GetProxyImpl().Invoke(ctx, invo)
return result, result.Error()
}
return interceptor(ctx, in, info, handler)
}
func _Ayrshare_CreateProfile_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc_go.UnaryServerInterceptor) (interface{}, error) {
in := new(CreateProfileRequest)
if err := dec(in); err != nil {
return nil, err
}
base := srv.(dubbo3.Dubbo3GrpcService)
args := []interface{}{}
args = append(args, in)
md, _ := metadata.FromIncomingContext(ctx)
invAttachment := make(map[string]interface{}, len(md))
for k, v := range md {
invAttachment[k] = v
}
invo := invocation.NewRPCInvocation("CreateProfile", args, invAttachment)
if interceptor == nil {
result := base.XXX_GetProxyImpl().Invoke(ctx, invo)
return result, result.Error()
}
info := &grpc_go.UnaryServerInfo{
Server: srv,
FullMethod: ctx.Value("XXX_TRIPLE_GO_INTERFACE_NAME").(string),
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
result := base.XXX_GetProxyImpl().Invoke(ctx, invo)
return result, result.Error()
}
return interceptor(ctx, in, info, handler)
}
func _Ayrshare_GetProfiles_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc_go.UnaryServerInterceptor) (interface{}, error) {
in := new(GetProfilesRequest)
if err := dec(in); err != nil {
return nil, err
}
base := srv.(dubbo3.Dubbo3GrpcService)
args := []interface{}{}
args = append(args, in)
md, _ := metadata.FromIncomingContext(ctx)
invAttachment := make(map[string]interface{}, len(md))
for k, v := range md {
invAttachment[k] = v
}
invo := invocation.NewRPCInvocation("GetProfiles", args, invAttachment)
if interceptor == nil {
result := base.XXX_GetProxyImpl().Invoke(ctx, invo)
return result, result.Error()
}
info := &grpc_go.UnaryServerInfo{
Server: srv,
FullMethod: ctx.Value("XXX_TRIPLE_GO_INTERFACE_NAME").(string),
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
result := base.XXX_GetProxyImpl().Invoke(ctx, invo)
return result, result.Error()
}
return interceptor(ctx, in, info, handler)
}
func _Ayrshare_GenerateJWT_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc_go.UnaryServerInterceptor) (interface{}, error) {
in := new(GenerateJWTRequest)
if err := dec(in); err != nil {
return nil, err
}
base := srv.(dubbo3.Dubbo3GrpcService)
args := []interface{}{}
args = append(args, in)
md, _ := metadata.FromIncomingContext(ctx)
invAttachment := make(map[string]interface{}, len(md))
for k, v := range md {
invAttachment[k] = v
}
invo := invocation.NewRPCInvocation("GenerateJWT", args, invAttachment)
if interceptor == nil {
result := base.XXX_GetProxyImpl().Invoke(ctx, invo)
return result, result.Error()
}
info := &grpc_go.UnaryServerInfo{
Server: srv,
FullMethod: ctx.Value("XXX_TRIPLE_GO_INTERFACE_NAME").(string),
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
result := base.XXX_GetProxyImpl().Invoke(ctx, invo)
return result, result.Error()
}
return interceptor(ctx, in, info, handler)
}
// Ayrshare_ServiceDesc is the grpc_go.ServiceDesc for Ayrshare service.
// It's only intended for direct use with grpc_go.RegisterService,
// and not to be introspected or modified (even as a copy)
var Ayrshare_ServiceDesc = grpc_go.ServiceDesc{
ServiceName: "aryshare.Ayrshare",
HandlerType: (*AyrshareServer)(nil),
Methods: []grpc_go.MethodDesc{
{
MethodName: "Post",
Handler: _Ayrshare_Post_Handler,
},
{
MethodName: "GetPost",
Handler: _Ayrshare_GetPost_Handler,
},
{
MethodName: "GetUser",
Handler: _Ayrshare_GetUser_Handler,
},
{
MethodName: "CreateProfile",
Handler: _Ayrshare_CreateProfile_Handler,
},
{
MethodName: "GetProfiles",
Handler: _Ayrshare_GetProfiles_Handler,
},
{
MethodName: "GenerateJWT",
Handler: _Ayrshare_GenerateJWT_Handler,
},
},
Streams: []grpc_go.StreamDesc{},
Metadata: "pb/ayrshare.proto",
}

297
api/pb/ayrshare.proto Normal file
View File

@ -0,0 +1,297 @@
syntax = "proto3";
package aryshare;
option go_package = "./aryshare";
import "pb/descriptor.proto";
import "pb/validator.proto";
service Ayrshare {
// api
rpc Post(PostRequest) returns (PostResponse);
rpc GetPost(GetPostRequest) returns (GetPostResponse);
// api
rpc GetUser(GetUserRequest) returns (GetUserResponse);
// api
rpc CreateProfile(CreateProfileRequest) returns (CreateProfileResponse);
rpc GetProfiles(GetProfilesRequest) returns (GetProfilesResponse);
rpc GenerateJWT(GenerateJWTRequest) returns (GenerateJWTResponse);
}
// Instagram
message UserTag {
string username = 1 [json_name = "username"];
double x = 2 [json_name = "x"]; // Required for images, cannot be used with Reels
double y = 3 [json_name = "y"]; // Required for images, cannot be used with Reels
}
// Instagram
message InstagramOptions {
bool shareReelsFeed = 1 [json_name = "shareReelsFeed"]; // Whether Reel can appear in both Feed and Reels tabs
string audioName = 2 [json_name = "audioName"]; // Name of the audio music for Reels
string thumbNail = 3 [json_name = "thumbNail"]; // URL of the Reel cover image (thumbnail)
int32 thumbNailOffset = 4 [json_name = "thumbNailOffset"]; // Offset in milliseconds of the thumbnail frame
bool stories = 5 [json_name = "stories"]; // Whether to post as Instagram Story
repeated string altText = 6 [json_name = "altText"]; // Array of alternative text for images (up to 1000 chars per image)
string locationId = 7 [json_name = "locationId"]; // Facebook Page ID or Page name
repeated UserTag userTags = 8 [json_name = "userTags"]; // Array of user tags with username and coordinates
repeated string collaborators = 9 [json_name = "collaborators"]; // Array of Instagram usernames (up to 3)
bool autoResize = 10 [json_name = "autoResize"]; // Auto resize images to 1080x1080px (Max Pack required)
bool disableComments = 11 [json_name = "disableComments"]; // Disable comments on the published post
}
// TikTok
message TikTokOptions {
bool autoAddMusic = 1 [json_name = "autoAddMusic"]; // Whether to automatically add recommended music (image only)
bool disableComments = 2 [json_name = "disableComments"]; // Whether to disable comments on the published post
bool disableDuet = 3 [json_name = "disableDuet"]; // Disable duets on the published video (video only)
bool disableStitch = 4 [json_name = "disableStitch"]; // Disable stitch on the published video (video only)
bool draft = 5 [json_name = "draft"]; // Whether to create a draft post
bool isAIGenerated = 6 [json_name = "isAIGenerated"]; // Whether to enable AI-generated content toggle (video only)
bool isBrandedContent = 7 [json_name = "isBrandedContent"]; // Whether to enable Branded Content toggle
bool isBrandOrganic = 8 [json_name = "isBrandOrganic"]; // Whether to enable Brand Organic Content toggle
int32 imageCoverIndex = 9 [json_name = "imageCoverIndex"]; // Index of mediaUrls to be used as cover (image only, default 0)
string title = 10 [json_name = "title"]; // Title of the post (image only)
int32 thumbNailOffset = 11 [json_name = "thumbNailOffset"]; // Frame to use for video cover in milliseconds (video only)
string thumbNail = 12 [json_name = "thumbNail"]; // URL of thumbnail image for video (video only)
string visibility = 13 [json_name = "visibility"]; // How the post is shared: "public", "private", "followers", "friends" (image only, default "public")
}
//
message PostRequest {
string post = 1 [json_name = "post", (validator.field) = {string_not_empty: true, human_error: "post内容不能为空"}];
repeated string platforms = 2 [json_name = "platforms", (validator.field) = {repeated_count_min: 1, human_error: "platforms平台列表不能为空"}];
repeated string mediaUrls = 3 [json_name = "mediaUrls"];
bool isVideo = 4 [json_name = "isVideo"];
string scheduleDate = 5 [json_name = "scheduleDate"];
bool validateScheduled = 6 [json_name = "validateScheduled"];
bool shortenLinks = 7 [json_name = "shortenLinks"];
bool disableComments = 8 [json_name = "disableComments"];
InstagramOptions instagramOptions = 9 [json_name = "instagramOptions"];
TikTokOptions tikTokOptions = 10 [json_name = "tikTokOptions"];
string profileKey = 11 [json_name = "profileKey"];
}
// ID响应
message PostId {
string status = 1 [json_name = "status"];
string id = 2 [json_name = "id"];
string cid = 3 [json_name = "cid"];
string postUrl = 4 [json_name = "postUrl"];
string platform = 5 [json_name = "platform"];
string type = 6 [json_name = "type"];
string owner = 7 [json_name = "owner"];
string mediaId = 8 [json_name = "mediaId"];
string ended = 9 [json_name = "ended"];
string idShare = 10 [json_name = "idShare"];
bool isVideo = 11 [json_name = "isVideo"];
int32 usedQuota = 12 [json_name = "usedQuota"];
}
// 使 profile key
message PostItem {
string status = 1 [json_name = "status"];
repeated string errors = 2 [json_name = "errors"];
repeated PostId postIds = 3 [json_name = "postIds"];
string id = 4 [json_name = "id"];
string refId = 5 [json_name = "refId"];
string profileTitle = 6 [json_name = "profileTitle"];
string post = 7 [json_name = "post"];
}
//
message PostResponse {
string status = 1 [json_name = "status"];
repeated PostItem posts = 2 [json_name = "posts"];
// 使 profile key
repeated string errors = 3 [json_name = "errors"];
repeated PostId postIds = 4 [json_name = "postIds"];
string id = 5 [json_name = "id"];
}
//
message GetPostRequest {
string id = 1 [json_name = "id", (validator.field) = {string_not_empty: true, human_error: "帖子ID不能为空"}];
string profileKey = 2 [json_name = "profileKey"];
}
//
message GetPostResponse {
string created = 1 [json_name = "created"];
repeated string errors = 2 [json_name = "errors"];
string id = 3 [json_name = "id"];
repeated string mediaUrls = 4 [json_name = "mediaUrls"];
repeated string platforms = 5 [json_name = "platforms"];
string post = 6 [json_name = "post"];
repeated PostId postIds = 7 [json_name = "postIds"];
string profileTitle = 8 [json_name = "profileTitle"];
string refId = 9 [json_name = "refId"];
string scheduleDate = 10 [json_name = "scheduleDate"];
bool shortenLinks = 11 [json_name = "shortenLinks"];
string status = 12 [json_name = "status"];
string type = 13 [json_name = "type"];
}
//
message GetUserRequest {
string profileKey = 1 [json_name = "profileKey"];
bool instagramDetails = 2 [json_name = "instagramDetails"];
}
//
message Timestamp {
int64 seconds = 1 [json_name = "_seconds"];
int64 nanoseconds = 2 [json_name = "_nanoseconds"];
string utc = 3 [json_name = "utc"];
}
// Twitter 使
message TwitterUsage {
int32 monthlyUsage = 1 [json_name = "monthlyUsage"];
int32 monthlyLimit = 2 [json_name = "monthlyLimit"];
string monthlyReset = 3 [json_name = "monthlyReset"];
}
//
message DisplayName {
string created = 1 [json_name = "created"];
string displayName = 2 [json_name = "displayName"];
string id = 3 [json_name = "id"];
string platform = 4 [json_name = "platform"];
string profileUrl = 5 [json_name = "profileUrl"];
string userImage = 6 [json_name = "userImage"];
string username = 7 [json_name = "username"];
string description = 8 [json_name = "description"];
bool messagingActive = 9 [json_name = "messagingActive"];
string pageName = 10 [json_name = "pageName"];
string userId = 11 [json_name = "userId"];
string mapsUrl = 12 [json_name = "mapsUrl"];
string placeId = 13 [json_name = "placeId"];
string reviewUrl = 14 [json_name = "reviewUrl"];
string igId = 15 [json_name = "igId"];
string type = 16 [json_name = "type"];
int32 usedQuota = 17 [json_name = "usedQuota"];
int32 refreshDaysRemaining = 18 [json_name = "refreshDaysRemaining"];
string refreshRequired = 19 [json_name = "refreshRequired"];
bool isEligibleForGeoRestrictions = 20 [json_name = "isEligibleForGeoRestrictions"];
bool isVerified = 21 [json_name = "isVerified"];
string subscriptionType = 22 [json_name = "subscriptionType"];
TwitterUsage twitterUsage = 23 [json_name = "twitterUsage"];
string verifiedType = 24 [json_name = "verifiedType"];
}
//
message GetUserResponse {
repeated string activeSocialAccounts = 1 [json_name = "activeSocialAccounts"];
Timestamp created = 2 [json_name = "created"];
repeated DisplayName displayNames = 3 [json_name = "displayNames"];
string email = 4 [json_name = "email"];
string lastApiCall = 5 [json_name = "lastApiCall"];
int32 messagingConversationMonthlyCount = 6 [json_name = "messagingConversationMonthlyCount"];
bool messagingEnabled = 7 [json_name = "messagingEnabled"];
int32 monthlyApiCalls = 8 [json_name = "monthlyApiCalls"];
int32 monthlyPostCount = 9 [json_name = "monthlyPostCount"];
int32 monthlyPostQuota = 10 [json_name = "monthlyPostQuota"];
int32 monthlyApiCallsQuota = 11 [json_name = "monthlyApiCallsQuota"];
string refId = 12 [json_name = "refId"];
string title = 13 [json_name = "title"];
string lastUpdated = 14 [json_name = "lastUpdated"];
string nextUpdate = 15 [json_name = "nextUpdate"];
}
//
message CreateProfileRequest {
string title = 1 [json_name = "title", (validator.field) = {string_not_empty: true, human_error: "title不能为空"}];
bool messagingActive = 2 [json_name = "messagingActive"];
bool hideTopHeader = 3 [json_name = "hideTopHeader"];
string topHeader = 4 [json_name = "topHeader"];
repeated string disableSocial = 5 [json_name = "disableSocial"];
bool team = 6 [json_name = "team"];
string email = 7 [json_name = "email"];
string subHeader = 8 [json_name = "subHeader"];
repeated string tags = 9 [json_name = "tags"];
}
//
message CreateProfileResponse {
string status = 1 [json_name = "status"];
string title = 2 [json_name = "title"];
string refId = 3 [json_name = "refId"];
string profileKey = 4 [json_name = "profileKey"];
bool messagingActive = 5 [json_name = "messagingActive"];
}
//
message GetProfilesRequest {
string title = 1 [json_name = "title"];
string refId = 2 [json_name = "refId"];
bool hasActiveSocialAccounts = 3 [json_name = "hasActiveSocialAccounts"];
repeated string includesActiveSocialAccounts = 4 [json_name = "includesActiveSocialAccounts"];
oneof actionLog {
bool actionLogBool = 5 [json_name = "actionLogBool"];
int32 actionLogInt = 6 [json_name = "actionLogInt"];
}
int32 limit = 7 [json_name = "limit"];
string cursor = 8 [json_name = "cursor"];
}
//
message ProfileItem {
string status = 1 [json_name = "status"];
string title = 2 [json_name = "title"];
string displayTitle = 3 [json_name = "displayTitle"];
Timestamp created = 4 [json_name = "created"];
string createdUTC = 5 [json_name = "createdUTC"];
string refId = 6 [json_name = "refId"];
repeated string activeSocialAccounts = 7 [json_name = "activeSocialAccounts"];
bool suspended = 8 [json_name = "suspended"];
}
//
message Pagination {
bool hasMore = 1 [json_name = "hasMore"];
string nextCursor = 2 [json_name = "nextCursor"];
int32 limit = 3 [json_name = "limit"];
}
//
message GetProfilesResponse {
repeated ProfileItem profiles = 1 [json_name = "profiles"];
int32 count = 2 [json_name = "count"];
string lastUpdated = 3 [json_name = "lastUpdated"];
string nextUpdate = 4 [json_name = "nextUpdate"];
Pagination pagination = 5 [json_name = "pagination"];
}
//
message Email {
string to = 1 [json_name = "to"];
string subject = 2 [json_name = "subject"];
string body = 3 [json_name = "body"];
}
// JWT请求
message GenerateJWTRequest {
string domain = 1 [json_name = "domain", (validator.field) = {string_not_empty: true, human_error: "domain不能为空"}];
string privateKey = 2 [json_name = "privateKey", (validator.field) = {string_not_empty: true, human_error: "privateKey不能为空"}];
string profileKey = 3 [json_name = "profileKey", (validator.field) = {string_not_empty: true, human_error: "profileKey不能为空"}];
bool logout = 4 [json_name = "logout"];
string redirect = 5 [json_name = "redirect"];
repeated string allowedSocial = 6 [json_name = "allowedSocial"];
bool verify = 7 [json_name = "verify"];
bool base64 = 8 [json_name = "base64"];
int32 expiresIn = 9 [json_name = "expiresIn"];
Email email = 10 [json_name = "email"];
}
// JWT响应
message GenerateJWTResponse {
string status = 1 [json_name = "status"];
string title = 2 [json_name = "title"];
string token = 3 [json_name = "token"];
string url = 4 [json_name = "url"];
bool emailSent = 5 [json_name = "emailSent"];
string expiresIn = 6 [json_name = "expiresIn"];
}

923
api/pb/descriptor.proto Normal file
View File

@ -0,0 +1,923 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
//
// The messages in this file describe the definitions found in .proto files.
// A valid .proto file can be translated directly to a FileDescriptorProto
// without any other information (e.g. without reading its imports).
syntax = "proto2";
package google.protobuf;
option go_package = "google.golang.org/protobuf/types/descriptorpb";
option java_package = "com.google.protobuf";
option java_outer_classname = "DescriptorProtos";
option csharp_namespace = "Google.Protobuf.Reflection";
option objc_class_prefix = "GPB";
option cc_enable_arenas = true;
// descriptor.proto must be optimized for speed because reflection-based
// algorithms don't work during bootstrapping.
option optimize_for = SPEED;
// The protocol compiler can output a FileDescriptorSet containing the .proto
// files it parses.
message FileDescriptorSet {
repeated FileDescriptorProto file = 1;
}
// Describes a complete .proto file.
message FileDescriptorProto {
optional string name = 1; // file name, relative to root of source tree
optional string package = 2; // e.g. "foo", "foo.bar", etc.
// Names of files imported by this file.
repeated string dependency = 3;
// Indexes of the public imported files in the dependency list above.
repeated int32 public_dependency = 10;
// Indexes of the weak imported files in the dependency list.
// For Google-internal migration only. Do not use.
repeated int32 weak_dependency = 11;
// All top-level definitions in this file.
repeated DescriptorProto message_type = 4;
repeated EnumDescriptorProto enum_type = 5;
repeated ServiceDescriptorProto service = 6;
repeated FieldDescriptorProto extension = 7;
optional FileOptions options = 8;
// This field contains optional information about the original source code.
// You may safely remove this entire field without harming runtime
// functionality of the descriptors -- the information is needed only by
// development tools.
optional SourceCodeInfo source_code_info = 9;
// The syntax of the proto file.
// The supported values are "proto2", "proto3", and "editions".
//
// If `edition` is present, this value must be "editions".
optional string syntax = 12;
// The edition of the proto file, which is an opaque string.
optional string edition = 13;
}
// Describes a message type.
message DescriptorProto {
optional string name = 1;
repeated FieldDescriptorProto field = 2;
repeated FieldDescriptorProto extension = 6;
repeated DescriptorProto nested_type = 3;
repeated EnumDescriptorProto enum_type = 4;
message ExtensionRange {
optional int32 start = 1; // Inclusive.
optional int32 end = 2; // Exclusive.
optional ExtensionRangeOptions options = 3;
}
repeated ExtensionRange extension_range = 5;
repeated OneofDescriptorProto oneof_decl = 8;
optional MessageOptions options = 7;
// Range of reserved tag numbers. Reserved tag numbers may not be used by
// fields or extension ranges in the same message. Reserved ranges may
// not overlap.
message ReservedRange {
optional int32 start = 1; // Inclusive.
optional int32 end = 2; // Exclusive.
}
repeated ReservedRange reserved_range = 9;
// Reserved field names, which may not be used by fields in the same message.
// A given name may only be reserved once.
repeated string reserved_name = 10;
}
message ExtensionRangeOptions {
// The parser stores options it doesn't recognize here. See above.
repeated UninterpretedOption uninterpreted_option = 999;
// Clients can define custom options in extensions of this message. See above.
extensions 1000 to max;
}
// Describes a field within a message.
message FieldDescriptorProto {
enum Type {
// 0 is reserved for errors.
// Order is weird for historical reasons.
TYPE_DOUBLE = 1;
TYPE_FLOAT = 2;
// Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT64 if
// negative values are likely.
TYPE_INT64 = 3;
TYPE_UINT64 = 4;
// Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT32 if
// negative values are likely.
TYPE_INT32 = 5;
TYPE_FIXED64 = 6;
TYPE_FIXED32 = 7;
TYPE_BOOL = 8;
TYPE_STRING = 9;
// Tag-delimited aggregate.
// Group type is deprecated and not supported in proto3. However, Proto3
// implementations should still be able to parse the group wire format and
// treat group fields as unknown fields.
TYPE_GROUP = 10;
TYPE_MESSAGE = 11; // Length-delimited aggregate.
// New in version 2.
TYPE_BYTES = 12;
TYPE_UINT32 = 13;
TYPE_ENUM = 14;
TYPE_SFIXED32 = 15;
TYPE_SFIXED64 = 16;
TYPE_SINT32 = 17; // Uses ZigZag encoding.
TYPE_SINT64 = 18; // Uses ZigZag encoding.
}
enum Label {
// 0 is reserved for errors
LABEL_OPTIONAL = 1;
LABEL_REQUIRED = 2;
LABEL_REPEATED = 3;
}
optional string name = 1;
optional int32 number = 3;
optional Label label = 4;
// If type_name is set, this need not be set. If both this and type_name
// are set, this must be one of TYPE_ENUM, TYPE_MESSAGE or TYPE_GROUP.
optional Type type = 5;
// For message and enum types, this is the name of the type. If the name
// starts with a '.', it is fully-qualified. Otherwise, C++-like scoping
// rules are used to find the type (i.e. first the nested types within this
// message are searched, then within the parent, on up to the root
// namespace).
optional string type_name = 6;
// For extensions, this is the name of the type being extended. It is
// resolved in the same manner as type_name.
optional string extendee = 2;
// For numeric types, contains the original text representation of the value.
// For booleans, "true" or "false".
// For strings, contains the default text contents (not escaped in any way).
// For bytes, contains the C escaped value. All bytes >= 128 are escaped.
optional string default_value = 7;
// If set, gives the index of a oneof in the containing type's oneof_decl
// list. This field is a member of that oneof.
optional int32 oneof_index = 9;
// JSON name of this field. The value is set by protocol compiler. If the
// user has set a "json_name" option on this field, that option's value
// will be used. Otherwise, it's deduced from the field's name by converting
// it to camelCase.
optional string json_name = 10;
optional FieldOptions options = 8;
// If true, this is a proto3 "optional". When a proto3 field is optional, it
// tracks presence regardless of field type.
//
// When proto3_optional is true, this field must be belong to a oneof to
// signal to old proto3 clients that presence is tracked for this field. This
// oneof is known as a "synthetic" oneof, and this field must be its sole
// member (each proto3 optional field gets its own synthetic oneof). Synthetic
// oneofs exist in the descriptor only, and do not generate any API. Synthetic
// oneofs must be ordered after all "real" oneofs.
//
// For message fields, proto3_optional doesn't create any semantic change,
// since non-repeated message fields always track presence. However it still
// indicates the semantic detail of whether the user wrote "optional" or not.
// This can be useful for round-tripping the .proto file. For consistency we
// give message fields a synthetic oneof also, even though it is not required
// to track presence. This is especially important because the parser can't
// tell if a field is a message or an enum, so it must always create a
// synthetic oneof.
//
// Proto2 optional fields do not set this flag, because they already indicate
// optional with `LABEL_OPTIONAL`.
optional bool proto3_optional = 17;
}
// Describes a oneof.
message OneofDescriptorProto {
optional string name = 1;
optional OneofOptions options = 2;
}
// Describes an enum type.
message EnumDescriptorProto {
optional string name = 1;
repeated EnumValueDescriptorProto value = 2;
optional EnumOptions options = 3;
// Range of reserved numeric values. Reserved values may not be used by
// entries in the same enum. Reserved ranges may not overlap.
//
// Note that this is distinct from DescriptorProto.ReservedRange in that it
// is inclusive such that it can appropriately represent the entire int32
// domain.
message EnumReservedRange {
optional int32 start = 1; // Inclusive.
optional int32 end = 2; // Inclusive.
}
// Range of reserved numeric values. Reserved numeric values may not be used
// by enum values in the same enum declaration. Reserved ranges may not
// overlap.
repeated EnumReservedRange reserved_range = 4;
// Reserved enum value names, which may not be reused. A given name may only
// be reserved once.
repeated string reserved_name = 5;
}
// Describes a value within an enum.
message EnumValueDescriptorProto {
optional string name = 1;
optional int32 number = 2;
optional EnumValueOptions options = 3;
}
// Describes a service.
message ServiceDescriptorProto {
optional string name = 1;
repeated MethodDescriptorProto method = 2;
optional ServiceOptions options = 3;
}
// Describes a method of a service.
message MethodDescriptorProto {
optional string name = 1;
// Input and output type names. These are resolved in the same way as
// FieldDescriptorProto.type_name, but must refer to a message type.
optional string input_type = 2;
optional string output_type = 3;
optional MethodOptions options = 4;
// Identifies if client streams multiple client messages
optional bool client_streaming = 5 [default = false];
// Identifies if server streams multiple server messages
optional bool server_streaming = 6 [default = false];
}
// ===================================================================
// Options
// Each of the definitions above may have "options" attached. These are
// just annotations which may cause code to be generated slightly differently
// or may contain hints for code that manipulates protocol messages.
//
// Clients may define custom options as extensions of the *Options messages.
// These extensions may not yet be known at parsing time, so the parser cannot
// store the values in them. Instead it stores them in a field in the *Options
// message called uninterpreted_option. This field must have the same name
// across all *Options messages. We then use this field to populate the
// extensions when we build a descriptor, at which point all protos have been
// parsed and so all extensions are known.
//
// Extension numbers for custom options may be chosen as follows:
// * For options which will only be used within a single application or
// organization, or for experimental options, use field numbers 50000
// through 99999. It is up to you to ensure that you do not use the
// same number for multiple options.
// * For options which will be published and used publicly by multiple
// independent entities, e-mail protobuf-global-extension-registry@google.com
// to reserve extension numbers. Simply provide your project name (e.g.
// Objective-C plugin) and your project website (if available) -- there's no
// need to explain how you intend to use them. Usually you only need one
// extension number. You can declare multiple options with only one extension
// number by putting them in a sub-message. See the Custom Options section of
// the docs for examples:
// https://developers.google.com/protocol-buffers/docs/proto#options
// If this turns out to be popular, a web service will be set up
// to automatically assign option numbers.
message FileOptions {
// Sets the Java package where classes generated from this .proto will be
// placed. By default, the proto package is used, but this is often
// inappropriate because proto packages do not normally start with backwards
// domain names.
optional string java_package = 1;
// Controls the name of the wrapper Java class generated for the .proto file.
// That class will always contain the .proto file's getDescriptor() method as
// well as any top-level extensions defined in the .proto file.
// If java_multiple_files is disabled, then all the other classes from the
// .proto file will be nested inside the single wrapper outer class.
optional string java_outer_classname = 8;
// If enabled, then the Java code generator will generate a separate .java
// file for each top-level message, enum, and service defined in the .proto
// file. Thus, these types will *not* be nested inside the wrapper class
// named by java_outer_classname. However, the wrapper class will still be
// generated to contain the file's getDescriptor() method as well as any
// top-level extensions defined in the file.
optional bool java_multiple_files = 10 [default = false];
// This option does nothing.
optional bool java_generate_equals_and_hash = 20 [deprecated=true];
// If set true, then the Java2 code generator will generate code that
// throws an exception whenever an attempt is made to assign a non-UTF-8
// byte sequence to a string field.
// Message reflection will do the same.
// However, an extension field still accepts non-UTF-8 byte sequences.
// This option has no effect on when used with the lite runtime.
optional bool java_string_check_utf8 = 27 [default = false];
// Generated classes can be optimized for speed or code size.
enum OptimizeMode {
SPEED = 1; // Generate complete code for parsing, serialization,
// etc.
CODE_SIZE = 2; // Use ReflectionOps to implement these methods.
LITE_RUNTIME = 3; // Generate code using MessageLite and the lite runtime.
}
optional OptimizeMode optimize_for = 9 [default = SPEED];
// Sets the Go package where structs generated from this .proto will be
// placed. If omitted, the Go package will be derived from the following:
// - The basename of the package import path, if provided.
// - Otherwise, the package statement in the .proto file, if present.
// - Otherwise, the basename of the .proto file, without extension.
optional string go_package = 11;
// Should generic services be generated in each language? "Generic" services
// are not specific to any particular RPC system. They are generated by the
// main code generators in each language (without additional plugins).
// Generic services were the only kind of service generation supported by
// early versions of google.protobuf.
//
// Generic services are now considered deprecated in favor of using plugins
// that generate code specific to your particular RPC system. Therefore,
// these default to false. Old code which depends on generic services should
// explicitly set them to true.
optional bool cc_generic_services = 16 [default = false];
optional bool java_generic_services = 17 [default = false];
optional bool py_generic_services = 18 [default = false];
optional bool php_generic_services = 42 [default = false];
// Is this file deprecated?
// Depending on the target platform, this can emit Deprecated annotations
// for everything in the file, or it will be completely ignored; in the very
// least, this is a formalization for deprecating files.
optional bool deprecated = 23 [default = false];
// Enables the use of arenas for the proto messages in this file. This applies
// only to generated classes for C++.
optional bool cc_enable_arenas = 31 [default = true];
// Sets the objective c class prefix which is prepended to all objective c
// generated classes from this .proto. There is no default.
optional string objc_class_prefix = 36;
// Namespace for generated classes; defaults to the package.
optional string csharp_namespace = 37;
// By default Swift generators will take the proto package and CamelCase it
// replacing '.' with underscore and use that to prefix the types/symbols
// defined. When this options is provided, they will use this value instead
// to prefix the types/symbols defined.
optional string swift_prefix = 39;
// Sets the php class prefix which is prepended to all php generated classes
// from this .proto. Default is empty.
optional string php_class_prefix = 40;
// Use this option to change the namespace of php generated classes. Default
// is empty. When this option is empty, the package name will be used for
// determining the namespace.
optional string php_namespace = 41;
// Use this option to change the namespace of php generated metadata classes.
// Default is empty. When this option is empty, the proto file name will be
// used for determining the namespace.
optional string php_metadata_namespace = 44;
// Use this option to change the package of ruby generated classes. Default
// is empty. When this option is not set, the package name will be used for
// determining the ruby package.
optional string ruby_package = 45;
// The parser stores options it doesn't recognize here.
// See the documentation for the "Options" section above.
repeated UninterpretedOption uninterpreted_option = 999;
// Clients can define custom options in extensions of this message.
// See the documentation for the "Options" section above.
extensions 1000 to max;
reserved 38;
}
message MessageOptions {
// Set true to use the old proto1 MessageSet wire format for extensions.
// This is provided for backwards-compatibility with the MessageSet wire
// format. You should not use this for any other reason: It's less
// efficient, has fewer features, and is more complicated.
//
// The message must be defined exactly as follows:
// message Foo {
// option message_set_wire_format = true;
// extensions 4 to max;
// }
// Note that the message cannot have any defined fields; MessageSets only
// have extensions.
//
// All extensions of your type must be singular messages; e.g. they cannot
// be int32s, enums, or repeated messages.
//
// Because this is an option, the above two restrictions are not enforced by
// the protocol compiler.
optional bool message_set_wire_format = 1 [default = false];
// Disables the generation of the standard "descriptor()" accessor, which can
// conflict with a field of the same name. This is meant to make migration
// from proto1 easier; new code should avoid fields named "descriptor".
optional bool no_standard_descriptor_accessor = 2 [default = false];
// Is this message deprecated?
// Depending on the target platform, this can emit Deprecated annotations
// for the message, or it will be completely ignored; in the very least,
// this is a formalization for deprecating messages.
optional bool deprecated = 3 [default = false];
reserved 4, 5, 6;
// Whether the message is an automatically generated map entry type for the
// maps field.
//
// For maps fields:
// map<KeyType, ValueType> map_field = 1;
// The parsed descriptor looks like:
// message MapFieldEntry {
// option map_entry = true;
// optional KeyType key = 1;
// optional ValueType value = 2;
// }
// repeated MapFieldEntry map_field = 1;
//
// Implementations may choose not to generate the map_entry=true message, but
// use a native map in the target language to hold the keys and values.
// The reflection APIs in such implementations still need to work as
// if the field is a repeated message field.
//
// NOTE: Do not set the option in .proto files. Always use the maps syntax
// instead. The option should only be implicitly set by the proto compiler
// parser.
optional bool map_entry = 7;
reserved 8; // javalite_serializable
reserved 9; // javanano_as_lite
// The parser stores options it doesn't recognize here. See above.
repeated UninterpretedOption uninterpreted_option = 999;
// Clients can define custom options in extensions of this message. See above.
extensions 1000 to max;
}
message FieldOptions {
// The ctype option instructs the C++ code generator to use a different
// representation of the field than it normally would. See the specific
// options below. This option is not yet implemented in the open source
// release -- sorry, we'll try to include it in a future version!
optional CType ctype = 1 [default = STRING];
enum CType {
// Default mode.
STRING = 0;
CORD = 1;
STRING_PIECE = 2;
}
// The packed option can be enabled for repeated primitive fields to enable
// a more efficient representation on the wire. Rather than repeatedly
// writing the tag and type for each element, the entire array is encoded as
// a single length-delimited blob. In proto3, only explicit setting it to
// false will avoid using packed encoding.
optional bool packed = 2;
// The jstype option determines the JavaScript type used for values of the
// field. The option is permitted only for 64 bit integral and fixed types
// (int64, uint64, sint64, fixed64, sfixed64). A field with jstype JS_STRING
// is represented as JavaScript string, which avoids loss of precision that
// can happen when a large value is converted to a floating point JavaScript.
// Specifying JS_NUMBER for the jstype causes the generated JavaScript code to
// use the JavaScript "number" type. The behavior of the default option
// JS_NORMAL is implementation dependent.
//
// This option is an enum to permit additional types to be added, e.g.
// goog.math.Integer.
optional JSType jstype = 6 [default = JS_NORMAL];
enum JSType {
// Use the default type.
JS_NORMAL = 0;
// Use JavaScript strings.
JS_STRING = 1;
// Use JavaScript numbers.
JS_NUMBER = 2;
}
// Should this field be parsed lazily? Lazy applies only to message-type
// fields. It means that when the outer message is initially parsed, the
// inner message's contents will not be parsed but instead stored in encoded
// form. The inner message will actually be parsed when it is first accessed.
//
// This is only a hint. Implementations are free to choose whether to use
// eager or lazy parsing regardless of the value of this option. However,
// setting this option true suggests that the protocol author believes that
// using lazy parsing on this field is worth the additional bookkeeping
// overhead typically needed to implement it.
//
// This option does not affect the public interface of any generated code;
// all method signatures remain the same. Furthermore, thread-safety of the
// interface is not affected by this option; const methods remain safe to
// call from multiple threads concurrently, while non-const methods continue
// to require exclusive access.
//
//
// Note that implementations may choose not to check required fields within
// a lazy sub-message. That is, calling IsInitialized() on the outer message
// may return true even if the inner message has missing required fields.
// This is necessary because otherwise the inner message would have to be
// parsed in order to perform the check, defeating the purpose of lazy
// parsing. An implementation which chooses not to check required fields
// must be consistent about it. That is, for any particular sub-message, the
// implementation must either *always* check its required fields, or *never*
// check its required fields, regardless of whether or not the message has
// been parsed.
//
// As of May 2022, lazy verifies the contents of the byte stream during
// parsing. An invalid byte stream will cause the overall parsing to fail.
optional bool lazy = 5 [default = false];
// unverified_lazy does no correctness checks on the byte stream. This should
// only be used where lazy with verification is prohibitive for performance
// reasons.
optional bool unverified_lazy = 15 [default = false];
// Is this field deprecated?
// Depending on the target platform, this can emit Deprecated annotations
// for accessors, or it will be completely ignored; in the very least, this
// is a formalization for deprecating fields.
optional bool deprecated = 3 [default = false];
// For Google-internal migration only. Do not use.
optional bool weak = 10 [default = false];
// The parser stores options it doesn't recognize here. See above.
repeated UninterpretedOption uninterpreted_option = 999;
// Clients can define custom options in extensions of this message. See above.
extensions 1000 to max;
reserved 4; // removed jtype
}
message OneofOptions {
// The parser stores options it doesn't recognize here. See above.
repeated UninterpretedOption uninterpreted_option = 999;
// Clients can define custom options in extensions of this message. See above.
extensions 1000 to max;
}
message EnumOptions {
// Set this option to true to allow mapping different tag names to the same
// value.
optional bool allow_alias = 2;
// Is this enum deprecated?
// Depending on the target platform, this can emit Deprecated annotations
// for the enum, or it will be completely ignored; in the very least, this
// is a formalization for deprecating enums.
optional bool deprecated = 3 [default = false];
reserved 5; // javanano_as_lite
// The parser stores options it doesn't recognize here. See above.
repeated UninterpretedOption uninterpreted_option = 999;
// Clients can define custom options in extensions of this message. See above.
extensions 1000 to max;
}
message EnumValueOptions {
// Is this enum value deprecated?
// Depending on the target platform, this can emit Deprecated annotations
// for the enum value, or it will be completely ignored; in the very least,
// this is a formalization for deprecating enum values.
optional bool deprecated = 1 [default = false];
// The parser stores options it doesn't recognize here. See above.
repeated UninterpretedOption uninterpreted_option = 999;
// Clients can define custom options in extensions of this message. See above.
extensions 1000 to max;
}
message ServiceOptions {
// Note: Field numbers 1 through 32 are reserved for Google's internal RPC
// framework. We apologize for hoarding these numbers to ourselves, but
// we were already using them long before we decided to release Protocol
// Buffers.
// Is this service deprecated?
// Depending on the target platform, this can emit Deprecated annotations
// for the service, or it will be completely ignored; in the very least,
// this is a formalization for deprecating services.
optional bool deprecated = 33 [default = false];
// The parser stores options it doesn't recognize here. See above.
repeated UninterpretedOption uninterpreted_option = 999;
// Clients can define custom options in extensions of this message. See above.
extensions 1000 to max;
}
message MethodOptions {
// Note: Field numbers 1 through 32 are reserved for Google's internal RPC
// framework. We apologize for hoarding these numbers to ourselves, but
// we were already using them long before we decided to release Protocol
// Buffers.
// Is this method deprecated?
// Depending on the target platform, this can emit Deprecated annotations
// for the method, or it will be completely ignored; in the very least,
// this is a formalization for deprecating methods.
optional bool deprecated = 33 [default = false];
// Is this method side-effect-free (or safe in HTTP parlance), or idempotent,
// or neither? HTTP based RPC implementation may choose GET verb for safe
// methods, and PUT verb for idempotent methods instead of the default POST.
enum IdempotencyLevel {
IDEMPOTENCY_UNKNOWN = 0;
NO_SIDE_EFFECTS = 1; // implies idempotent
IDEMPOTENT = 2; // idempotent, but may have side effects
}
optional IdempotencyLevel idempotency_level = 34
[default = IDEMPOTENCY_UNKNOWN];
// The parser stores options it doesn't recognize here. See above.
repeated UninterpretedOption uninterpreted_option = 999;
// Clients can define custom options in extensions of this message. See above.
extensions 1000 to max;
}
// A message representing a option the parser does not recognize. This only
// appears in options protos created by the compiler::Parser class.
// DescriptorPool resolves these when building Descriptor objects. Therefore,
// options protos in descriptor objects (e.g. returned by Descriptor::options(),
// or produced by Descriptor::CopyTo()) will never have UninterpretedOptions
// in them.
message UninterpretedOption {
// The name of the uninterpreted option. Each string represents a segment in
// a dot-separated name. is_extension is true iff a segment represents an
// extension (denoted with parentheses in options specs in .proto files).
// E.g.,{ ["foo", false], ["bar.baz", true], ["moo", false] } represents
// "foo.(bar.baz).moo".
message NamePart {
required string name_part = 1;
required bool is_extension = 2;
}
repeated NamePart name = 2;
// The value of the uninterpreted option, in whatever type the tokenizer
// identified it as during parsing. Exactly one of these should be set.
optional string identifier_value = 3;
optional uint64 positive_int_value = 4;
optional int64 negative_int_value = 5;
optional double double_value = 6;
optional bytes string_value = 7;
optional string aggregate_value = 8;
}
// ===================================================================
// Optional source code info
// Encapsulates information about the original source file from which a
// FileDescriptorProto was generated.
message SourceCodeInfo {
// A Location identifies a piece of source code in a .proto file which
// corresponds to a particular definition. This information is intended
// to be useful to IDEs, code indexers, documentation generators, and similar
// tools.
//
// For example, say we have a file like:
// message Foo {
// optional string foo = 1;
// }
// Let's look at just the field definition:
// optional string foo = 1;
// ^ ^^ ^^ ^ ^^^
// a bc de f ghi
// We have the following locations:
// span path represents
// [a,i) [ 4, 0, 2, 0 ] The whole field definition.
// [a,b) [ 4, 0, 2, 0, 4 ] The label (optional).
// [c,d) [ 4, 0, 2, 0, 5 ] The type (string).
// [e,f) [ 4, 0, 2, 0, 1 ] The name (foo).
// [g,h) [ 4, 0, 2, 0, 3 ] The number (1).
//
// Notes:
// - A location may refer to a repeated field itself (i.e. not to any
// particular index within it). This is used whenever a set of elements are
// logically enclosed in a single code segment. For example, an entire
// extend block (possibly containing multiple extension definitions) will
// have an outer location whose path refers to the "extensions" repeated
// field without an index.
// - Multiple locations may have the same path. This happens when a single
// logical declaration is spread out across multiple places. The most
// obvious example is the "extend" block again -- there may be multiple
// extend blocks in the same scope, each of which will have the same path.
// - A location's span is not always a subset of its parent's span. For
// example, the "extendee" of an extension declaration appears at the
// beginning of the "extend" block and is shared by all extensions within
// the block.
// - Just because a location's span is a subset of some other location's span
// does not mean that it is a descendant. For example, a "group" defines
// both a type and a field in a single declaration. Thus, the locations
// corresponding to the type and field and their components will overlap.
// - Code which tries to interpret locations should probably be designed to
// ignore those that it doesn't understand, as more types of locations could
// be recorded in the future.
repeated Location location = 1;
message Location {
// Identifies which part of the FileDescriptorProto was defined at this
// location.
//
// Each element is a field number or an index. They form a path from
// the root FileDescriptorProto to the place where the definition occurs.
// For example, this path:
// [ 4, 3, 2, 7, 1 ]
// refers to:
// file.message_type(3) // 4, 3
// .field(7) // 2, 7
// .name() // 1
// This is because FileDescriptorProto.message_type has field number 4:
// repeated DescriptorProto message_type = 4;
// and DescriptorProto.field has field number 2:
// repeated FieldDescriptorProto field = 2;
// and FieldDescriptorProto.name has field number 1:
// optional string name = 1;
//
// Thus, the above path gives the location of a field name. If we removed
// the last element:
// [ 4, 3, 2, 7 ]
// this path refers to the whole field declaration (from the beginning
// of the label to the terminating semicolon).
repeated int32 path = 1 [packed = true];
// Always has exactly three or four elements: start line, start column,
// end line (optional, otherwise assumed same as start line), end column.
// These are packed into a single field for efficiency. Note that line
// and column numbers are zero-based -- typically you will want to add
// 1 to each before displaying to a user.
repeated int32 span = 2 [packed = true];
// If this SourceCodeInfo represents a complete declaration, these are any
// comments appearing before and after the declaration which appear to be
// attached to the declaration.
//
// A series of line comments appearing on consecutive lines, with no other
// tokens appearing on those lines, will be treated as a single comment.
//
// leading_detached_comments will keep paragraphs of comments that appear
// before (but not connected to) the current element. Each paragraph,
// separated by empty lines, will be one comment element in the repeated
// field.
//
// Only the comment content is provided; comment markers (e.g. //) are
// stripped out. For block comments, leading whitespace and an asterisk
// will be stripped from the beginning of each line other than the first.
// Newlines are included in the output.
//
// Examples:
//
// optional int32 foo = 1; // Comment attached to foo.
// // Comment attached to bar.
// optional int32 bar = 2;
//
// optional string baz = 3;
// // Comment attached to baz.
// // Another line attached to baz.
//
// // Comment attached to moo.
// //
// // Another line attached to moo.
// optional double moo = 4;
//
// // Detached comment for corge. This is not leading or trailing comments
// // to moo or corge because there are blank lines separating it from
// // both.
//
// // Detached comment for corge paragraph 2.
//
// optional string corge = 5;
// /* Block comment attached
// * to corge. Leading asterisks
// * will be removed. */
// /* Block comment attached to
// * grault. */
// optional int32 grault = 6;
//
// // ignored detached comments.
optional string leading_comments = 3;
optional string trailing_comments = 4;
repeated string leading_detached_comments = 6;
}
}
// Describes the relationship between generated code and its original source
// file. A GeneratedCodeInfo message is associated with only one generated
// source file, but may contain references to different source .proto files.
message GeneratedCodeInfo {
// An Annotation connects some span of text in generated code to an element
// of its generating .proto file.
repeated Annotation annotation = 1;
message Annotation {
// Identifies the element in the original source .proto file. This field
// is formatted the same as SourceCodeInfo.Location.path.
repeated int32 path = 1 [packed = true];
// Identifies the filesystem path to the original source .proto.
optional string source_file = 2;
// Identifies the starting offset in bytes in the generated code
// that relates to the identified object.
optional int32 begin = 3;
// Identifies the ending offset in bytes in the generated code that
// relates to the identified offset. The end offset should be one past
// the last relevant byte (so the length of the text = end - begin).
optional int32 end = 4;
}
}

862
api/pb/validate.proto Normal file
View File

@ -0,0 +1,862 @@
syntax = "proto2";
package validate;
option go_package = "github.com/envoyproxy/protoc-gen-validate/validate";
option java_package = "io.envoyproxy.pgv.validate";
import "google/protobuf/descriptor.proto";
import "google/protobuf/duration.proto";
import "google/protobuf/timestamp.proto";
// Validation rules applied at the message level
extend google.protobuf.MessageOptions {
// Disabled nullifies any validation rules for this message, including any
// message fields associated with it that do support validation.
optional bool disabled = 1071;
// Ignore skips generation of validation methods for this message.
optional bool ignored = 1072;
}
// Validation rules applied at the oneof level
extend google.protobuf.OneofOptions {
// Required ensures that exactly one the field options in a oneof is set;
// validation fails if no fields in the oneof are set.
optional bool required = 1071;
}
// Validation rules applied at the field level
extend google.protobuf.FieldOptions {
// Rules specify the validations to be performed on this field. By default,
// no validation is performed against a field.
optional FieldRules rules = 1071;
}
// FieldRules encapsulates the rules for each type of field. Depending on the
// field, the correct set should be used to ensure proper validations.
message FieldRules {
optional MessageRules message = 17;
oneof type {
// Scalar Field Types
FloatRules float = 1;
DoubleRules double = 2;
Int32Rules int32 = 3;
Int64Rules int64 = 4;
UInt32Rules uint32 = 5;
UInt64Rules uint64 = 6;
SInt32Rules sint32 = 7;
SInt64Rules sint64 = 8;
Fixed32Rules fixed32 = 9;
Fixed64Rules fixed64 = 10;
SFixed32Rules sfixed32 = 11;
SFixed64Rules sfixed64 = 12;
BoolRules bool = 13;
StringRules string = 14;
BytesRules bytes = 15;
// Complex Field Types
EnumRules enum = 16;
RepeatedRules repeated = 18;
MapRules map = 19;
// Well-Known Field Types
AnyRules any = 20;
DurationRules duration = 21;
TimestampRules timestamp = 22;
}
}
// FloatRules describes the constraints applied to `float` values
message FloatRules {
// Const specifies that this field must be exactly the specified value
optional float const = 1;
// Lt specifies that this field must be less than the specified value,
// exclusive
optional float lt = 2;
// Lte specifies that this field must be less than or equal to the
// specified value, inclusive
optional float lte = 3;
// Gt specifies that this field must be greater than the specified value,
// exclusive. If the value of Gt is larger than a specified Lt or Lte, the
// range is reversed.
optional float gt = 4;
// Gte specifies that this field must be greater than or equal to the
// specified value, inclusive. If the value of Gte is larger than a
// specified Lt or Lte, the range is reversed.
optional float gte = 5;
// In specifies that this field must be equal to one of the specified
// values
repeated float in = 6;
// NotIn specifies that this field cannot be equal to one of the specified
// values
repeated float not_in = 7;
// IgnoreEmpty specifies that the validation rules of this field should be
// evaluated only if the field is not empty
optional bool ignore_empty = 8;
}
// DoubleRules describes the constraints applied to `double` values
message DoubleRules {
// Const specifies that this field must be exactly the specified value
optional double const = 1;
// Lt specifies that this field must be less than the specified value,
// exclusive
optional double lt = 2;
// Lte specifies that this field must be less than or equal to the
// specified value, inclusive
optional double lte = 3;
// Gt specifies that this field must be greater than the specified value,
// exclusive. If the value of Gt is larger than a specified Lt or Lte, the
// range is reversed.
optional double gt = 4;
// Gte specifies that this field must be greater than or equal to the
// specified value, inclusive. If the value of Gte is larger than a
// specified Lt or Lte, the range is reversed.
optional double gte = 5;
// In specifies that this field must be equal to one of the specified
// values
repeated double in = 6;
// NotIn specifies that this field cannot be equal to one of the specified
// values
repeated double not_in = 7;
// IgnoreEmpty specifies that the validation rules of this field should be
// evaluated only if the field is not empty
optional bool ignore_empty = 8;
}
// Int32Rules describes the constraints applied to `int32` values
message Int32Rules {
// Const specifies that this field must be exactly the specified value
optional int32 const = 1;
// Lt specifies that this field must be less than the specified value,
// exclusive
optional int32 lt = 2;
// Lte specifies that this field must be less than or equal to the
// specified value, inclusive
optional int32 lte = 3;
// Gt specifies that this field must be greater than the specified value,
// exclusive. If the value of Gt is larger than a specified Lt or Lte, the
// range is reversed.
optional int32 gt = 4;
// Gte specifies that this field must be greater than or equal to the
// specified value, inclusive. If the value of Gte is larger than a
// specified Lt or Lte, the range is reversed.
optional int32 gte = 5;
// In specifies that this field must be equal to one of the specified
// values
repeated int32 in = 6;
// NotIn specifies that this field cannot be equal to one of the specified
// values
repeated int32 not_in = 7;
// IgnoreEmpty specifies that the validation rules of this field should be
// evaluated only if the field is not empty
optional bool ignore_empty = 8;
}
// Int64Rules describes the constraints applied to `int64` values
message Int64Rules {
// Const specifies that this field must be exactly the specified value
optional int64 const = 1;
// Lt specifies that this field must be less than the specified value,
// exclusive
optional int64 lt = 2;
// Lte specifies that this field must be less than or equal to the
// specified value, inclusive
optional int64 lte = 3;
// Gt specifies that this field must be greater than the specified value,
// exclusive. If the value of Gt is larger than a specified Lt or Lte, the
// range is reversed.
optional int64 gt = 4;
// Gte specifies that this field must be greater than or equal to the
// specified value, inclusive. If the value of Gte is larger than a
// specified Lt or Lte, the range is reversed.
optional int64 gte = 5;
// In specifies that this field must be equal to one of the specified
// values
repeated int64 in = 6;
// NotIn specifies that this field cannot be equal to one of the specified
// values
repeated int64 not_in = 7;
// IgnoreEmpty specifies that the validation rules of this field should be
// evaluated only if the field is not empty
optional bool ignore_empty = 8;
}
// UInt32Rules describes the constraints applied to `uint32` values
message UInt32Rules {
// Const specifies that this field must be exactly the specified value
optional uint32 const = 1;
// Lt specifies that this field must be less than the specified value,
// exclusive
optional uint32 lt = 2;
// Lte specifies that this field must be less than or equal to the
// specified value, inclusive
optional uint32 lte = 3;
// Gt specifies that this field must be greater than the specified value,
// exclusive. If the value of Gt is larger than a specified Lt or Lte, the
// range is reversed.
optional uint32 gt = 4;
// Gte specifies that this field must be greater than or equal to the
// specified value, inclusive. If the value of Gte is larger than a
// specified Lt or Lte, the range is reversed.
optional uint32 gte = 5;
// In specifies that this field must be equal to one of the specified
// values
repeated uint32 in = 6;
// NotIn specifies that this field cannot be equal to one of the specified
// values
repeated uint32 not_in = 7;
// IgnoreEmpty specifies that the validation rules of this field should be
// evaluated only if the field is not empty
optional bool ignore_empty = 8;
}
// UInt64Rules describes the constraints applied to `uint64` values
message UInt64Rules {
// Const specifies that this field must be exactly the specified value
optional uint64 const = 1;
// Lt specifies that this field must be less than the specified value,
// exclusive
optional uint64 lt = 2;
// Lte specifies that this field must be less than or equal to the
// specified value, inclusive
optional uint64 lte = 3;
// Gt specifies that this field must be greater than the specified value,
// exclusive. If the value of Gt is larger than a specified Lt or Lte, the
// range is reversed.
optional uint64 gt = 4;
// Gte specifies that this field must be greater than or equal to the
// specified value, inclusive. If the value of Gte is larger than a
// specified Lt or Lte, the range is reversed.
optional uint64 gte = 5;
// In specifies that this field must be equal to one of the specified
// values
repeated uint64 in = 6;
// NotIn specifies that this field cannot be equal to one of the specified
// values
repeated uint64 not_in = 7;
// IgnoreEmpty specifies that the validation rules of this field should be
// evaluated only if the field is not empty
optional bool ignore_empty = 8;
}
// SInt32Rules describes the constraints applied to `sint32` values
message SInt32Rules {
// Const specifies that this field must be exactly the specified value
optional sint32 const = 1;
// Lt specifies that this field must be less than the specified value,
// exclusive
optional sint32 lt = 2;
// Lte specifies that this field must be less than or equal to the
// specified value, inclusive
optional sint32 lte = 3;
// Gt specifies that this field must be greater than the specified value,
// exclusive. If the value of Gt is larger than a specified Lt or Lte, the
// range is reversed.
optional sint32 gt = 4;
// Gte specifies that this field must be greater than or equal to the
// specified value, inclusive. If the value of Gte is larger than a
// specified Lt or Lte, the range is reversed.
optional sint32 gte = 5;
// In specifies that this field must be equal to one of the specified
// values
repeated sint32 in = 6;
// NotIn specifies that this field cannot be equal to one of the specified
// values
repeated sint32 not_in = 7;
// IgnoreEmpty specifies that the validation rules of this field should be
// evaluated only if the field is not empty
optional bool ignore_empty = 8;
}
// SInt64Rules describes the constraints applied to `sint64` values
message SInt64Rules {
// Const specifies that this field must be exactly the specified value
optional sint64 const = 1;
// Lt specifies that this field must be less than the specified value,
// exclusive
optional sint64 lt = 2;
// Lte specifies that this field must be less than or equal to the
// specified value, inclusive
optional sint64 lte = 3;
// Gt specifies that this field must be greater than the specified value,
// exclusive. If the value of Gt is larger than a specified Lt or Lte, the
// range is reversed.
optional sint64 gt = 4;
// Gte specifies that this field must be greater than or equal to the
// specified value, inclusive. If the value of Gte is larger than a
// specified Lt or Lte, the range is reversed.
optional sint64 gte = 5;
// In specifies that this field must be equal to one of the specified
// values
repeated sint64 in = 6;
// NotIn specifies that this field cannot be equal to one of the specified
// values
repeated sint64 not_in = 7;
// IgnoreEmpty specifies that the validation rules of this field should be
// evaluated only if the field is not empty
optional bool ignore_empty = 8;
}
// Fixed32Rules describes the constraints applied to `fixed32` values
message Fixed32Rules {
// Const specifies that this field must be exactly the specified value
optional fixed32 const = 1;
// Lt specifies that this field must be less than the specified value,
// exclusive
optional fixed32 lt = 2;
// Lte specifies that this field must be less than or equal to the
// specified value, inclusive
optional fixed32 lte = 3;
// Gt specifies that this field must be greater than the specified value,
// exclusive. If the value of Gt is larger than a specified Lt or Lte, the
// range is reversed.
optional fixed32 gt = 4;
// Gte specifies that this field must be greater than or equal to the
// specified value, inclusive. If the value of Gte is larger than a
// specified Lt or Lte, the range is reversed.
optional fixed32 gte = 5;
// In specifies that this field must be equal to one of the specified
// values
repeated fixed32 in = 6;
// NotIn specifies that this field cannot be equal to one of the specified
// values
repeated fixed32 not_in = 7;
// IgnoreEmpty specifies that the validation rules of this field should be
// evaluated only if the field is not empty
optional bool ignore_empty = 8;
}
// Fixed64Rules describes the constraints applied to `fixed64` values
message Fixed64Rules {
// Const specifies that this field must be exactly the specified value
optional fixed64 const = 1;
// Lt specifies that this field must be less than the specified value,
// exclusive
optional fixed64 lt = 2;
// Lte specifies that this field must be less than or equal to the
// specified value, inclusive
optional fixed64 lte = 3;
// Gt specifies that this field must be greater than the specified value,
// exclusive. If the value of Gt is larger than a specified Lt or Lte, the
// range is reversed.
optional fixed64 gt = 4;
// Gte specifies that this field must be greater than or equal to the
// specified value, inclusive. If the value of Gte is larger than a
// specified Lt or Lte, the range is reversed.
optional fixed64 gte = 5;
// In specifies that this field must be equal to one of the specified
// values
repeated fixed64 in = 6;
// NotIn specifies that this field cannot be equal to one of the specified
// values
repeated fixed64 not_in = 7;
// IgnoreEmpty specifies that the validation rules of this field should be
// evaluated only if the field is not empty
optional bool ignore_empty = 8;
}
// SFixed32Rules describes the constraints applied to `sfixed32` values
message SFixed32Rules {
// Const specifies that this field must be exactly the specified value
optional sfixed32 const = 1;
// Lt specifies that this field must be less than the specified value,
// exclusive
optional sfixed32 lt = 2;
// Lte specifies that this field must be less than or equal to the
// specified value, inclusive
optional sfixed32 lte = 3;
// Gt specifies that this field must be greater than the specified value,
// exclusive. If the value of Gt is larger than a specified Lt or Lte, the
// range is reversed.
optional sfixed32 gt = 4;
// Gte specifies that this field must be greater than or equal to the
// specified value, inclusive. If the value of Gte is larger than a
// specified Lt or Lte, the range is reversed.
optional sfixed32 gte = 5;
// In specifies that this field must be equal to one of the specified
// values
repeated sfixed32 in = 6;
// NotIn specifies that this field cannot be equal to one of the specified
// values
repeated sfixed32 not_in = 7;
// IgnoreEmpty specifies that the validation rules of this field should be
// evaluated only if the field is not empty
optional bool ignore_empty = 8;
}
// SFixed64Rules describes the constraints applied to `sfixed64` values
message SFixed64Rules {
// Const specifies that this field must be exactly the specified value
optional sfixed64 const = 1;
// Lt specifies that this field must be less than the specified value,
// exclusive
optional sfixed64 lt = 2;
// Lte specifies that this field must be less than or equal to the
// specified value, inclusive
optional sfixed64 lte = 3;
// Gt specifies that this field must be greater than the specified value,
// exclusive. If the value of Gt is larger than a specified Lt or Lte, the
// range is reversed.
optional sfixed64 gt = 4;
// Gte specifies that this field must be greater than or equal to the
// specified value, inclusive. If the value of Gte is larger than a
// specified Lt or Lte, the range is reversed.
optional sfixed64 gte = 5;
// In specifies that this field must be equal to one of the specified
// values
repeated sfixed64 in = 6;
// NotIn specifies that this field cannot be equal to one of the specified
// values
repeated sfixed64 not_in = 7;
// IgnoreEmpty specifies that the validation rules of this field should be
// evaluated only if the field is not empty
optional bool ignore_empty = 8;
}
// BoolRules describes the constraints applied to `bool` values
message BoolRules {
// Const specifies that this field must be exactly the specified value
optional bool const = 1;
}
// StringRules describe the constraints applied to `string` values
message StringRules {
// Const specifies that this field must be exactly the specified value
optional string const = 1;
// Len specifies that this field must be the specified number of
// characters (Unicode code points). Note that the number of
// characters may differ from the number of bytes in the string.
optional uint64 len = 19;
// MinLen specifies that this field must be the specified number of
// characters (Unicode code points) at a minimum. Note that the number of
// characters may differ from the number of bytes in the string.
optional uint64 min_len = 2;
// MaxLen specifies that this field must be the specified number of
// characters (Unicode code points) at a maximum. Note that the number of
// characters may differ from the number of bytes in the string.
optional uint64 max_len = 3;
// LenBytes specifies that this field must be the specified number of bytes
optional uint64 len_bytes = 20;
// MinBytes specifies that this field must be the specified number of bytes
// at a minimum
optional uint64 min_bytes = 4;
// MaxBytes specifies that this field must be the specified number of bytes
// at a maximum
optional uint64 max_bytes = 5;
// Pattern specifes that this field must match against the specified
// regular expression (RE2 syntax). The included expression should elide
// any delimiters.
optional string pattern = 6;
// Prefix specifies that this field must have the specified substring at
// the beginning of the string.
optional string prefix = 7;
// Suffix specifies that this field must have the specified substring at
// the end of the string.
optional string suffix = 8;
// Contains specifies that this field must have the specified substring
// anywhere in the string.
optional string contains = 9;
// NotContains specifies that this field cannot have the specified substring
// anywhere in the string.
optional string not_contains = 23;
// In specifies that this field must be equal to one of the specified
// values
repeated string in = 10;
// NotIn specifies that this field cannot be equal to one of the specified
// values
repeated string not_in = 11;
// WellKnown rules provide advanced constraints against common string
// patterns
oneof well_known {
// Email specifies that the field must be a valid email address as
// defined by RFC 5322
bool email = 12;
// Hostname specifies that the field must be a valid hostname as
// defined by RFC 1034. This constraint does not support
// internationalized domain names (IDNs).
bool hostname = 13;
// Ip specifies that the field must be a valid IP (v4 or v6) address.
// Valid IPv6 addresses should not include surrounding square brackets.
bool ip = 14;
// Ipv4 specifies that the field must be a valid IPv4 address.
bool ipv4 = 15;
// Ipv6 specifies that the field must be a valid IPv6 address. Valid
// IPv6 addresses should not include surrounding square brackets.
bool ipv6 = 16;
// Uri specifies that the field must be a valid, absolute URI as defined
// by RFC 3986
bool uri = 17;
// UriRef specifies that the field must be a valid URI as defined by RFC
// 3986 and may be relative or absolute.
bool uri_ref = 18;
// Address specifies that the field must be either a valid hostname as
// defined by RFC 1034 (which does not support internationalized domain
// names or IDNs), or it can be a valid IP (v4 or v6).
bool address = 21;
// Uuid specifies that the field must be a valid UUID as defined by
// RFC 4122
bool uuid = 22;
// WellKnownRegex specifies a common well known pattern defined as a regex.
KnownRegex well_known_regex = 24;
}
// This applies to regexes HTTP_HEADER_NAME and HTTP_HEADER_VALUE to enable
// strict header validation.
// By default, this is true, and HTTP header validations are RFC-compliant.
// Setting to false will enable a looser validations that only disallows
// \r\n\0 characters, which can be used to bypass header matching rules.
optional bool strict = 25 [default = true];
// IgnoreEmpty specifies that the validation rules of this field should be
// evaluated only if the field is not empty
optional bool ignore_empty = 26;
}
// WellKnownRegex contain some well-known patterns.
enum KnownRegex {
UNKNOWN = 0;
// HTTP header name as defined by RFC 7230.
HTTP_HEADER_NAME = 1;
// HTTP header value as defined by RFC 7230.
HTTP_HEADER_VALUE = 2;
}
// BytesRules describe the constraints applied to `bytes` values
message BytesRules {
// Const specifies that this field must be exactly the specified value
optional bytes const = 1;
// Len specifies that this field must be the specified number of bytes
optional uint64 len = 13;
// MinLen specifies that this field must be the specified number of bytes
// at a minimum
optional uint64 min_len = 2;
// MaxLen specifies that this field must be the specified number of bytes
// at a maximum
optional uint64 max_len = 3;
// Pattern specifes that this field must match against the specified
// regular expression (RE2 syntax). The included expression should elide
// any delimiters.
optional string pattern = 4;
// Prefix specifies that this field must have the specified bytes at the
// beginning of the string.
optional bytes prefix = 5;
// Suffix specifies that this field must have the specified bytes at the
// end of the string.
optional bytes suffix = 6;
// Contains specifies that this field must have the specified bytes
// anywhere in the string.
optional bytes contains = 7;
// In specifies that this field must be equal to one of the specified
// values
repeated bytes in = 8;
// NotIn specifies that this field cannot be equal to one of the specified
// values
repeated bytes not_in = 9;
// WellKnown rules provide advanced constraints against common byte
// patterns
oneof well_known {
// Ip specifies that the field must be a valid IP (v4 or v6) address in
// byte format
bool ip = 10;
// Ipv4 specifies that the field must be a valid IPv4 address in byte
// format
bool ipv4 = 11;
// Ipv6 specifies that the field must be a valid IPv6 address in byte
// format
bool ipv6 = 12;
}
// IgnoreEmpty specifies that the validation rules of this field should be
// evaluated only if the field is not empty
optional bool ignore_empty = 14;
}
// EnumRules describe the constraints applied to enum values
message EnumRules {
// Const specifies that this field must be exactly the specified value
optional int32 const = 1;
// DefinedOnly specifies that this field must be only one of the defined
// values for this enum, failing on any undefined value.
optional bool defined_only = 2;
// In specifies that this field must be equal to one of the specified
// values
repeated int32 in = 3;
// NotIn specifies that this field cannot be equal to one of the specified
// values
repeated int32 not_in = 4;
}
// MessageRules describe the constraints applied to embedded message values.
// For message-type fields, validation is performed recursively.
message MessageRules {
// Skip specifies that the validation rules of this field should not be
// evaluated
optional bool skip = 1;
// Required specifies that this field must be set
optional bool required = 2;
}
// RepeatedRules describe the constraints applied to `repeated` values
message RepeatedRules {
// MinItems specifies that this field must have the specified number of
// items at a minimum
optional uint64 min_items = 1;
// MaxItems specifies that this field must have the specified number of
// items at a maximum
optional uint64 max_items = 2;
// Unique specifies that all elements in this field must be unique. This
// contraint is only applicable to scalar and enum types (messages are not
// supported).
optional bool unique = 3;
// Items specifies the contraints to be applied to each item in the field.
// Repeated message fields will still execute validation against each item
// unless skip is specified here.
optional FieldRules items = 4;
// IgnoreEmpty specifies that the validation rules of this field should be
// evaluated only if the field is not empty
optional bool ignore_empty = 5;
}
// MapRules describe the constraints applied to `map` values
message MapRules {
// MinPairs specifies that this field must have the specified number of
// KVs at a minimum
optional uint64 min_pairs = 1;
// MaxPairs specifies that this field must have the specified number of
// KVs at a maximum
optional uint64 max_pairs = 2;
// NoSparse specifies values in this field cannot be unset. This only
// applies to map's with message value types.
optional bool no_sparse = 3;
// Keys specifies the constraints to be applied to each key in the field.
optional FieldRules keys = 4;
// Values specifies the constraints to be applied to the value of each key
// in the field. Message values will still have their validations evaluated
// unless skip is specified here.
optional FieldRules values = 5;
// IgnoreEmpty specifies that the validation rules of this field should be
// evaluated only if the field is not empty
optional bool ignore_empty = 6;
}
// AnyRules describe constraints applied exclusively to the
// `google.protobuf.Any` well-known type
message AnyRules {
// Required specifies that this field must be set
optional bool required = 1;
// In specifies that this field's `type_url` must be equal to one of the
// specified values.
repeated string in = 2;
// NotIn specifies that this field's `type_url` must not be equal to any of
// the specified values.
repeated string not_in = 3;
}
// DurationRules describe the constraints applied exclusively to the
// `google.protobuf.Duration` well-known type
message DurationRules {
// Required specifies that this field must be set
optional bool required = 1;
// Const specifies that this field must be exactly the specified value
optional google.protobuf.Duration const = 2;
// Lt specifies that this field must be less than the specified value,
// exclusive
optional google.protobuf.Duration lt = 3;
// Lt specifies that this field must be less than the specified value,
// inclusive
optional google.protobuf.Duration lte = 4;
// Gt specifies that this field must be greater than the specified value,
// exclusive
optional google.protobuf.Duration gt = 5;
// Gte specifies that this field must be greater than the specified value,
// inclusive
optional google.protobuf.Duration gte = 6;
// In specifies that this field must be equal to one of the specified
// values
repeated google.protobuf.Duration in = 7;
// NotIn specifies that this field cannot be equal to one of the specified
// values
repeated google.protobuf.Duration not_in = 8;
}
// TimestampRules describe the constraints applied exclusively to the
// `google.protobuf.Timestamp` well-known type
message TimestampRules {
// Required specifies that this field must be set
optional bool required = 1;
// Const specifies that this field must be exactly the specified value
optional google.protobuf.Timestamp const = 2;
// Lt specifies that this field must be less than the specified value,
// exclusive
optional google.protobuf.Timestamp lt = 3;
// Lte specifies that this field must be less than the specified value,
// inclusive
optional google.protobuf.Timestamp lte = 4;
// Gt specifies that this field must be greater than the specified value,
// exclusive
optional google.protobuf.Timestamp gt = 5;
// Gte specifies that this field must be greater than the specified value,
// inclusive
optional google.protobuf.Timestamp gte = 6;
// LtNow specifies that this must be less than the current time. LtNow
// can only be used with the Within rule.
optional bool lt_now = 7;
// GtNow specifies that this must be greater than the current time. GtNow
// can only be used with the Within rule.
optional bool gt_now = 8;
// Within specifies that this field must be within this duration of the
// current time. This constraint can be used alone or with the LtNow and
// GtNow rules.
optional google.protobuf.Duration within = 9;
}

78
api/pb/validator.proto Normal file
View File

@ -0,0 +1,78 @@
// Copyright 2016 Michal Witkowski. All Rights Reserved.
// See LICENSE for licensing terms.
// Protocol Buffers extensions for defining auto-generateable validators for messages.
syntax = "proto2";
package validator;
import "pb/descriptor.proto";
option go_package = "github.com/mwitkow/go-proto-validators;validator";
extend google.protobuf.FieldOptions {
optional FieldValidator field = 65020;
}
extend google.protobuf.OneofOptions {
optional OneofValidator oneof = 65021;
}
message FieldValidator {
// Uses a Golang RE2-syntax regex to match the field contents.
optional string regex = 1;
// Field value of integer strictly greater than this value.
optional int64 int_gt = 2;
// Field value of integer strictly smaller than this value.
optional int64 int_lt = 3;
// Used for nested message types, requires that the message type exists.
optional bool msg_exists = 4;
// Human error specifies a user-customizable error that is visible to the user.
optional string human_error = 5;
// Field value of double strictly greater than this value.
// Note that this value can only take on a valid floating point
// value. Use together with float_epsilon if you need something more specific.
optional double float_gt = 6;
// Field value of double strictly smaller than this value.
// Note that this value can only take on a valid floating point
// value. Use together with float_epsilon if you need something more specific.
optional double float_lt = 7;
// Field value of double describing the epsilon within which
// any comparison should be considered to be true. For example,
// when using float_gt = 0.35, using a float_epsilon of 0.05
// would mean that any value above 0.30 is acceptable. It can be
// thought of as a {float_value_condition} +- {float_epsilon}.
// If unset, no correction for floating point inaccuracies in
// comparisons will be attempted.
optional double float_epsilon = 8;
// Floating-point value compared to which the field content should be greater or equal.
optional double float_gte = 9;
// Floating-point value compared to which the field content should be smaller or equal.
optional double float_lte = 10;
// Used for string fields, requires the string to be not empty (i.e different from "").
optional bool string_not_empty = 11;
// Repeated field with at least this number of elements.
optional int64 repeated_count_min = 12;
// Repeated field with at most this number of elements.
optional int64 repeated_count_max = 13;
// Field value of length greater than this value.
optional int64 length_gt = 14;
// Field value of length smaller than this value.
optional int64 length_lt = 15;
// Field value of length strictly equal to this value.
optional int64 length_eq = 16;
// Requires that the value is in the enum.
optional bool is_in_enum = 17;
// Ensures that a string value is in UUID format.
// uuid_ver specifies the valid UUID versions. Valid values are: 0-5.
// If uuid_ver is 0 all UUID versions are accepted.
optional int32 uuid_ver = 18;
}
message OneofValidator {
// Require that one of the oneof fields is set.
optional bool required = 1;
}

View File

@ -119,7 +119,9 @@ type System struct {
ErpHost string ErpHost string
FieeHost string FieeHost string
AuthRedirectUrl string AuthRedirectUrl string
AuthCallback string
CronOpen bool CronOpen bool
ProxyUrl string
} }
type Oss struct { type Oss struct {
AccessKeyId string AccessKeyId string

16
conf/alibabacloud.env Normal file
View File

@ -0,0 +1,16 @@
#=========== 阿里云内容安全配置 ===========
# STS登录模式配置
# RAM用户AccessKey ID用于获取STS临时凭证
RAM_ACCESS_KEY_ID=LTAI5tNBzbeEbG1yCitvHsMb
# RAM用户AccessKey Secret
RAM_ACCESS_KEY_SECRET=G1xAUB8G6WDVo0SLr6DJaJjNWIlpmO
# 要扮演的RAM角色ARN
RAM_ROLE_ARN=acs:ram::5828544250383902:role/content-secret
# 阿里云区域可选默认为cn-shanghai
ALIBABA_CLOUD_REGION=ap-southeast-1
# 阿里云端点可选默认为green.cn-shanghai.aliyuncs.com
ALIBABA_CLOUD_ENDPOINT=green-cip.ap-southeast-1.aliyuncs.com

View File

@ -18,7 +18,7 @@ AccessKeyId = "${OSS_AK}"
AccessKeySecret = "${OSS_SK}" AccessKeySecret = "${OSS_SK}"
Endpoint = "${OSS_ENDPOINTT}" Endpoint = "${OSS_ENDPOINTT}"
BucketName = "${OSS_BUCKETNAME}" BucketName = "${OSS_BUCKETNAME}"
BaseDir = "fontree-fiee-test" BaseDir = "fonchain-main"
CdnHost = "${OSS_CDN}" CdnHost = "${OSS_CDN}"
[redis] [redis]
RedisDB = "2" RedisDB = "2"

View File

@ -27,7 +27,6 @@ dubbo:
protocol: tri protocol: tri
retries: 0 retries: 0
interface: com.fontree.microservices.fiee.bundle # must be compatible with grpc or dubbo-java interface: com.fontree.microservices.fiee.bundle # must be compatible with grpc or dubbo-java
SecFilingsClientImpl: CastClientImpl:
protocol: tri protocol: tri
retries: 0 interface: com.fontree.microservices.fiee.multicast
interface: com.fontree.microservices.fiee.SecFiling

Binary file not shown.

16
docs/dev/alibabacloud.env Normal file
View File

@ -0,0 +1,16 @@
#=========== 阿里云内容安全配置 ===========
# STS登录模式配置
# RAM用户AccessKey ID用于获取STS临时凭证
RAM_ACCESS_KEY_ID=LTAI5tNBzbeEbG1yCitvHsMb
# RAM用户AccessKey Secret
RAM_ACCESS_KEY_SECRET=G1xAUB8G6WDVo0SLr6DJaJjNWIlpmO
# 要扮演的RAM角色ARN
RAM_ROLE_ARN=acs:ram::5828544250383902:role/content-secret
# 阿里云区域可选默认为cn-shanghai
ALIBABA_CLOUD_REGION=ap-southeast-1
# 阿里云端点可选默认为green.cn-shanghai.aliyuncs.com
ALIBABA_CLOUD_ENDPOINT=green-cip.ap-southeast-1.aliyuncs.com

View File

@ -7,7 +7,9 @@ RedirectUri = "/api/redirect/url"
ErpHost = "http://erpapi.test.fontree.cn:8081" ErpHost = "http://erpapi.test.fontree.cn:8081"
FieeHost = "http://erpapi.test.fontree.cn:8081" FieeHost = "http://erpapi.test.fontree.cn:8081"
AuthRedirectUrl = "http://saas-erp.test.fontree.cn:8081/media_account" AuthRedirectUrl = "http://saas-erp.test.fontree.cn:8081/media_account"
AuthCallback = "https://saas-test.szjixun.cn/api/fiee/media/as-oauth2callback"
CronOpen = false CronOpen = false
proxyUrl = "http://47.84.75.255:6785"
[bos] [bos]
Ak = "ALTAKxrqOQHnAN525Tb2GX4Bhe" Ak = "ALTAKxrqOQHnAN525Tb2GX4Bhe"
Sk = "d2ecaa9d75114d3b9f42b99014198306" Sk = "d2ecaa9d75114d3b9f42b99014198306"

View File

@ -9,7 +9,7 @@ dubbo:
# address: 114.218.158.24:2181 # address: 114.218.158.24:2181
consumer: consumer:
filter: tracing filter: tracing
request-timeout: 30s request-timeout: 300s
references: references:
OrderClientImpl: OrderClientImpl:
protocol: tri protocol: tri
@ -47,6 +47,9 @@ dubbo:
protocol: tri protocol: tri
retries: 0 retries: 0
interface: com.fontree.microservices.fiee.SecFiling interface: com.fontree.microservices.fiee.SecFiling
AyrshareClientImpl:
protocol: tri
interface: com.fontree.microservices.fiee.ayrshare
logger: logger:
zap-config: zap-config:
level: error # 日志级别 level: error # 日志级别

View File

@ -0,0 +1,16 @@
#=========== 阿里云内容安全配置 ===========
# STS登录模式配置
# RAM用户AccessKey ID用于获取STS临时凭证
RAM_ACCESS_KEY_ID=LTAI5tNBzbeEbG1yCitvHsMb
# RAM用户AccessKey Secret
RAM_ACCESS_KEY_SECRET=G1xAUB8G6WDVo0SLr6DJaJjNWIlpmO
# 要扮演的RAM角色ARN
RAM_ROLE_ARN=acs:ram::5828544250383902:role/content-secret
# 阿里云区域可选默认为cn-shanghai
ALIBABA_CLOUD_REGION=ap-southeast-1
# 阿里云端点可选默认为green.cn-shanghai.aliyuncs.com
ALIBABA_CLOUD_ENDPOINT=green-cip.ap-southeast-1.aliyuncs.com

View File

@ -7,7 +7,9 @@ RedirectUri = "/api/redirect/url"
ErpHost = "https://erpapi.fontree.cn" ErpHost = "https://erpapi.fontree.cn"
FieeHost = "https://erpapi.fiee.com" FieeHost = "https://erpapi.fiee.com"
AuthRedirectUrl = "https://erp.fiee.com/media_account" AuthRedirectUrl = "https://erp.fiee.com/media_account"
AuthCallback = "https://erpapi.fiee.com/api/fiee/media/as-oauth2callback"
CronOpen = true CronOpen = true
proxyUrl = ""
[bos] [bos]
Ak = "ALTAKxrqOQHnAN525Tb2GX4Bhe" Ak = "ALTAKxrqOQHnAN525Tb2GX4Bhe"
Sk = "d2ecaa9d75114d3b9f42b99014198306" Sk = "d2ecaa9d75114d3b9f42b99014198306"

View File

@ -8,7 +8,7 @@ dubbo:
# address: 114.218.158.24:2181 # address: 114.218.158.24:2181
consumer: consumer:
filter: tracing filter: tracing
request-timeout: 30s request-timeout: 300s
references: references:
OrderClientImpl: OrderClientImpl:
protocol: tri protocol: tri
@ -49,6 +49,9 @@ dubbo:
protocol: tri protocol: tri
retries: 0 retries: 0
interface: com.fontree.microservices.fiee.SecFiling interface: com.fontree.microservices.fiee.SecFiling
AyrshareClientImpl:
protocol: tri
interface: com.fontree.microservices.fiee.ayrshare
logger: logger:
zap-config: zap-config:
level: error # 日志级别 level: error # 日志级别

View File

@ -0,0 +1,16 @@
#=========== 阿里云内容安全配置 ===========
# STS登录模式配置
# RAM用户AccessKey ID用于获取STS临时凭证
RAM_ACCESS_KEY_ID=LTAI5tNBzbeEbG1yCitvHsMb
# RAM用户AccessKey Secret
RAM_ACCESS_KEY_SECRET=G1xAUB8G6WDVo0SLr6DJaJjNWIlpmO
# 要扮演的RAM角色ARN
RAM_ROLE_ARN=acs:ram::5828544250383902:role/content-secret
# 阿里云区域可选默认为cn-shanghai
ALIBABA_CLOUD_REGION=ap-southeast-1
# 阿里云端点可选默认为green.cn-shanghai.aliyuncs.com
ALIBABA_CLOUD_ENDPOINT=green-cip.ap-southeast-1.aliyuncs.com

View File

@ -8,7 +8,9 @@ ErpHost = "http://erpapi.test.fontree.cn:8081"
FieeHost = "http://erpapi.test.fontree.cn:8081" FieeHost = "http://erpapi.test.fontree.cn:8081"
FieeApiHost = "https://saas-test.szjixun.cn" FieeApiHost = "https://saas-test.szjixun.cn"
AuthRedirectUrl = "http://saas-erp.test.fontree.cn:8081/media_account" AuthRedirectUrl = "http://saas-erp.test.fontree.cn:8081/media_account"
AuthCallback = "https://saas-test.szjixun.cn/api/fiee/media/as-oauth2callback"
CronOpen = true CronOpen = true
proxyUrl = "http://taifeng:fontree008@8.220.199.204:1081"
[bos] [bos]
Ak = "ALTAKxrqOQHnAN525Tb2GX4Bhe" Ak = "ALTAKxrqOQHnAN525Tb2GX4Bhe"
Sk = "d2ecaa9d75114d3b9f42b99014198306" Sk = "d2ecaa9d75114d3b9f42b99014198306"

View File

@ -9,7 +9,7 @@ dubbo:
# address: 114.218.158.24:2181 # address: 114.218.158.24:2181
consumer: consumer:
filter: tracing filter: tracing
request-timeout: 30s request-timeout: 300s
references: references:
OrderClientImpl: OrderClientImpl:
protocol: tri protocol: tri
@ -47,6 +47,9 @@ dubbo:
protocol: tri protocol: tri
retries: 0 retries: 0
interface: com.fontree.microservices.fiee.SecFiling interface: com.fontree.microservices.fiee.SecFiling
AyrshareClientImpl:
protocol: tri
interface: com.fontree.microservices.fiee.ayrshare
logger: logger:
zap-config: zap-config:
level: error # 日志级别 level: error # 日志级别

20
go.mod
View File

@ -5,6 +5,7 @@ go 1.23.0
toolchain go1.23.10 toolchain go1.23.10
replace ( replace (
github.com/fonchain/utils/security => ../utils/security
//github.com/fonchain_enterprise/utils/objstorage => ../../tyfon-/utils/objstorage //github.com/fonchain_enterprise/utils/objstorage => ../../tyfon-/utils/objstorage
github.com/fonchain/utils/voice => ../utils/voice github.com/fonchain/utils/voice => ../utils/voice
github.com/fonchain_enterprise/utils/aes => ../utils/aes github.com/fonchain_enterprise/utils/aes => ../utils/aes
@ -71,7 +72,7 @@ require (
github.com/mschoch/smat v0.2.0 // indirect github.com/mschoch/smat v0.2.0 // indirect
github.com/nacos-group/nacos-sdk-go v1.1.1 // indirect github.com/nacos-group/nacos-sdk-go v1.1.1 // indirect
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
github.com/pelletier/go-toml v1.7.0 // indirect github.com/pelletier/go-toml v1.7.0 // indirect
github.com/pelletier/go-toml/v2 v2.0.8 // indirect github.com/pelletier/go-toml/v2 v2.0.8 // indirect
github.com/prometheus/client_golang v1.12.2 // indirect github.com/prometheus/client_golang v1.12.2 // indirect
@ -82,7 +83,7 @@ require (
github.com/robfig/cron/v3 v3.0.1 github.com/robfig/cron/v3 v3.0.1
github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b
github.com/shirou/gopsutil v3.20.11+incompatible // indirect github.com/shirou/gopsutil v3.20.11+incompatible // indirect
github.com/uber/jaeger-client-go v2.29.1+incompatible // indirect github.com/uber/jaeger-client-go v2.30.0+incompatible // indirect
github.com/uber/jaeger-lib v2.4.1+incompatible // indirect github.com/uber/jaeger-lib v2.4.1+incompatible // indirect
github.com/ugorji/go/codec v1.2.11 // indirect github.com/ugorji/go/codec v1.2.11 // indirect
go.opencensus.io v0.23.0 // indirect go.opencensus.io v0.23.0 // indirect
@ -106,6 +107,7 @@ require (
github.com/disintegration/imaging v1.6.2 github.com/disintegration/imaging v1.6.2
github.com/duke-git/lancet/v2 v2.3.8 github.com/duke-git/lancet/v2 v2.3.8
github.com/envoyproxy/protoc-gen-validate v0.1.0 github.com/envoyproxy/protoc-gen-validate v0.1.0
github.com/fonchain/utils/security v0.0.0-00010101000000-000000000000
github.com/fonchain/utils/voice v0.0.0-00010101000000-000000000000 github.com/fonchain/utils/voice v0.0.0-00010101000000-000000000000
github.com/fonchain_enterprise/utils/objstorage v0.0.0-00010101000000-000000000000 github.com/fonchain_enterprise/utils/objstorage v0.0.0-00010101000000-000000000000
github.com/gin-contrib/pprof v1.4.0 github.com/gin-contrib/pprof v1.4.0
@ -128,7 +130,14 @@ require (
cloud.google.com/go v0.65.0 // indirect cloud.google.com/go v0.65.0 // indirect
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 // indirect github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 // indirect
github.com/alibaba/sentinel-golang v1.0.4 // indirect github.com/alibaba/sentinel-golang v1.0.4 // indirect
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1376 // indirect github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 // indirect
github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.12 // indirect
github.com/alibabacloud-go/debug v1.0.1 // indirect
github.com/alibabacloud-go/green-20220302/v2 v2.23.0 // indirect
github.com/alibabacloud-go/tea v1.3.13 // indirect
github.com/alibabacloud-go/tea-utils/v2 v2.0.7 // indirect
github.com/aliyun/alibaba-cloud-sdk-go v1.63.107 // indirect
github.com/aliyun/credentials-go v1.4.5 // indirect
github.com/andybalholm/cascadia v1.3.1 // indirect github.com/andybalholm/cascadia v1.3.1 // indirect
github.com/aws/aws-sdk-go v1.38.20 // indirect github.com/aws/aws-sdk-go v1.38.20 // indirect
github.com/baidubce/bce-sdk-go v0.9.123 // indirect github.com/baidubce/bce-sdk-go v0.9.123 // indirect
@ -136,6 +145,7 @@ require (
github.com/bytedance/sonic v1.9.1 // indirect github.com/bytedance/sonic v1.9.1 // indirect
github.com/census-instrumentation/opencensus-proto v0.2.1 // indirect github.com/census-instrumentation/opencensus-proto v0.2.1 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/clbanning/mxj/v2 v2.7.0 // indirect
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4 // indirect github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4 // indirect
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1 // indirect github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1 // indirect
github.com/coreos/go-semver v0.3.0 // indirect github.com/coreos/go-semver v0.3.0 // indirect
@ -153,15 +163,14 @@ require (
github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-resty/resty/v2 v2.7.0 // indirect github.com/go-resty/resty/v2 v2.7.0 // indirect
github.com/golang/mock v1.5.0 // indirect github.com/golang/mock v1.5.0 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/uuid v1.6.0 // indirect github.com/google/uuid v1.6.0 // indirect
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 // indirect
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 // indirect github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect
github.com/hashicorp/vault/sdk v0.3.0 // indirect github.com/hashicorp/vault/sdk v0.3.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/joho/godotenv v1.5.1 // indirect
github.com/klauspost/cpuid/v2 v2.2.4 // indirect github.com/klauspost/cpuid/v2 v2.2.4 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
@ -184,6 +193,7 @@ require (
github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.2.0 // indirect github.com/subosito/gotenv v1.2.0 // indirect
github.com/tiendc/go-deepcopy v1.6.0 // indirect github.com/tiendc/go-deepcopy v1.6.0 // indirect
github.com/tjfoc/gmsm v1.4.1 // indirect
github.com/tklauser/go-sysconf v0.3.6 // indirect github.com/tklauser/go-sysconf v0.3.6 // indirect
github.com/tklauser/numcpus v0.2.2 // indirect github.com/tklauser/numcpus v0.2.2 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect

130
go.sum
View File

@ -71,12 +71,59 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/alibaba/sentinel-golang v1.0.4 h1:i0wtMvNVdy7vM4DdzYrlC4r/Mpk1OKUUBurKKkWhEo8= github.com/alibaba/sentinel-golang v1.0.4 h1:i0wtMvNVdy7vM4DdzYrlC4r/Mpk1OKUUBurKKkWhEo8=
github.com/alibaba/sentinel-golang v1.0.4/go.mod h1:Lag5rIYyJiPOylK8Kku2P+a23gdKMMqzQS7wTnjWEpk= github.com/alibaba/sentinel-golang v1.0.4/go.mod h1:Lag5rIYyJiPOylK8Kku2P+a23gdKMMqzQS7wTnjWEpk=
github.com/alibabacloud-go/alibabacloud-gateway-pop v0.0.6 h1:eIf+iGJxdU4U9ypaUfbtOWCsZSbTb8AUHvyPrxu6mAA=
github.com/alibabacloud-go/alibabacloud-gateway-pop v0.0.6/go.mod h1:4EUIoxs/do24zMOGGqYVWgw0s9NtiylnJglOeEB5UJo=
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc=
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 h1:zE8vH9C7JiZLNJJQ5OwjU9mSi4T9ef9u3BURT6LCLC8=
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5/go.mod h1:tWnyE9AjF8J8qqLk645oUmVUnFybApTQWklQmi5tY6g=
github.com/alibabacloud-go/darabonba-array v0.1.0 h1:vR8s7b1fWAQIjEjWnuF0JiKsCvclSRTfDzZHTYqfufY=
github.com/alibabacloud-go/darabonba-array v0.1.0/go.mod h1:BLKxr0brnggqOJPqT09DFJ8g3fsDshapUD3C3aOEFaI=
github.com/alibabacloud-go/darabonba-encode-util v0.0.2 h1:1uJGrbsGEVqWcWxrS9MyC2NG0Ax+GpOM5gtupki31XE=
github.com/alibabacloud-go/darabonba-encode-util v0.0.2/go.mod h1:JiW9higWHYXm7F4PKuMgEUETNZasrDM6vqVr/Can7H8=
github.com/alibabacloud-go/darabonba-map v0.0.2 h1:qvPnGB4+dJbJIxOOfawxzF3hzMnIpjmafa0qOTp6udc=
github.com/alibabacloud-go/darabonba-map v0.0.2/go.mod h1:28AJaX8FOE/ym8OUFWga+MtEzBunJwQGceGQlvaPGPc=
github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.12 h1:e2yCrhtWd6Qcsy4he2OL+jIAU+93Lx9OcLlPRoFLT1w=
github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.12/go.mod h1:f2wDpbM7hK9SvLIH09zSKVU1TsyemUNOqErMscMMl7c=
github.com/alibabacloud-go/darabonba-signature-util v0.0.7 h1:UzCnKvsjPFzApvODDNEYqBHMFt1w98wC7FOo0InLyxg=
github.com/alibabacloud-go/darabonba-signature-util v0.0.7/go.mod h1:oUzCYV2fcCH797xKdL6BDH8ADIHlzrtKVjeRtunBNTQ=
github.com/alibabacloud-go/darabonba-string v1.0.2 h1:E714wms5ibdzCqGeYJ9JCFywE5nDyvIXIIQbZVFkkqo=
github.com/alibabacloud-go/darabonba-string v1.0.2/go.mod h1:93cTfV3vuPhhEwGGpKKqhVW4jLe7tDpo3LUM0i0g6mA=
github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68/go.mod h1:6pb/Qy8c+lqua8cFpEy7g39NRRqOWc3rOwAy8m5Y2BY=
github.com/alibabacloud-go/debug v1.0.0/go.mod h1:8gfgZCCAC3+SCzjWtY053FrOcd4/qlH6IHTI4QyICOc=
github.com/alibabacloud-go/debug v1.0.1 h1:MsW9SmUtbb1Fnt3ieC6NNZi6aEwrXfDksD4QA6GSbPg=
github.com/alibabacloud-go/debug v1.0.1/go.mod h1:8gfgZCCAC3+SCzjWtY053FrOcd4/qlH6IHTI4QyICOc=
github.com/alibabacloud-go/endpoint-util v1.1.0 h1:r/4D3VSw888XGaeNpP994zDUaxdgTSHBbVfZlzf6b5Q=
github.com/alibabacloud-go/endpoint-util v1.1.0/go.mod h1:O5FuCALmCKs2Ff7JFJMudHs0I5EBgecXXxZRyswlEjE=
github.com/alibabacloud-go/green-20220302/v2 v2.23.0 h1:t2WsqrKt/ztPq4X2Orh3cvG5PsFdes/IQDkxZmB/f5k=
github.com/alibabacloud-go/green-20220302/v2 v2.23.0/go.mod h1:iZWuUEakwGct+e0NDnTBzOMaoblCUXH8Lf7S5v4kGKg=
github.com/alibabacloud-go/openapi-util v0.1.0 h1:0z75cIULkDrdEhkLWgi9tnLe+KhAFE/r5Pb3312/eAY=
github.com/alibabacloud-go/openapi-util v0.1.0/go.mod h1:sQuElr4ywwFRlCCberQwKRFhRzIyG4QTP/P4y1CJ6Ws=
github.com/alibabacloud-go/tea v1.1.0/go.mod h1:IkGyUSX4Ba1V+k4pCtJUc6jDpZLFph9QMy2VUPTwukg=
github.com/alibabacloud-go/tea v1.1.7/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4=
github.com/alibabacloud-go/tea v1.1.8/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4=
github.com/alibabacloud-go/tea v1.1.11/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4=
github.com/alibabacloud-go/tea v1.1.17/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A=
github.com/alibabacloud-go/tea v1.1.20/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A=
github.com/alibabacloud-go/tea v1.2.2/go.mod h1:CF3vOzEMAG+bR4WOql8gc2G9H3EkH3ZLAQdpmpXMgwk=
github.com/alibabacloud-go/tea v1.3.12/go.mod h1:A560v/JTQ1n5zklt2BEpurJzZTI8TUT+Psg2drWlxRg=
github.com/alibabacloud-go/tea v1.3.13 h1:WhGy6LIXaMbBM6VBYcsDCz6K/TPsT1Ri2hPmmZffZ94=
github.com/alibabacloud-go/tea v1.3.13/go.mod h1:A560v/JTQ1n5zklt2BEpurJzZTI8TUT+Psg2drWlxRg=
github.com/alibabacloud-go/tea-utils v1.3.1 h1:iWQeRzRheqCMuiF3+XkfybB3kTgUXkXX+JMrqfLeB2I=
github.com/alibabacloud-go/tea-utils v1.3.1/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQdSngxrpF8rKUDJjPE=
github.com/alibabacloud-go/tea-utils/v2 v2.0.5/go.mod h1:dL6vbUT35E4F4bFTHL845eUloqaerYBYPsdWR2/jhe4=
github.com/alibabacloud-go/tea-utils/v2 v2.0.7 h1:WDx5qW3Xa5ZgJ1c8NfqJkF6w+AU5wB8835UdhPr6Ax0=
github.com/alibabacloud-go/tea-utils/v2 v2.0.7/go.mod h1:qxn986l+q33J5VkialKMqT/TTs3E+U9MJpd001iWQ9I=
github.com/aliyun/alibaba-cloud-sdk-go v1.61.18/go.mod h1:v8ESoHo4SyHmuB4b1tJqDHxfTGEciD+yhvOU/5s1Rfk= github.com/aliyun/alibaba-cloud-sdk-go v1.61.18/go.mod h1:v8ESoHo4SyHmuB4b1tJqDHxfTGEciD+yhvOU/5s1Rfk=
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1376 h1:lExo7heZgdFn5AbaNJEllbA0KSJ/Z8T7MphvMREJOOo= github.com/aliyun/alibaba-cloud-sdk-go v1.63.107 h1:qagvUyrgOnBIlVRQWOyCZGVKUIYbMBdGdJ104vBpRFU=
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1376/go.mod h1:9CMdKNL3ynIGPpfTcdwTvIm8SGuAZYYC4jFVSSvE1YQ= github.com/aliyun/alibaba-cloud-sdk-go v1.63.107/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ=
github.com/aliyun/aliyun-oss-go-sdk v2.2.4+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= github.com/aliyun/aliyun-oss-go-sdk v2.2.4+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
github.com/aliyun/aliyun-oss-go-sdk v2.2.6+incompatible h1:KXeJoM1wo9I/6xPTyt6qCxoSZnmASiAjlrr0dyTUKt8= github.com/aliyun/aliyun-oss-go-sdk v2.2.6+incompatible h1:KXeJoM1wo9I/6xPTyt6qCxoSZnmASiAjlrr0dyTUKt8=
github.com/aliyun/aliyun-oss-go-sdk v2.2.6+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= github.com/aliyun/aliyun-oss-go-sdk v2.2.6+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
github.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw=
github.com/aliyun/credentials-go v1.3.1/go.mod h1:8jKYhQuDawt8x2+fusqa1Y6mPxemTsBEN04dgcAcYz0=
github.com/aliyun/credentials-go v1.3.6/go.mod h1:1LxUuX7L5YrZUWzBrRyk0SwSdH4OmPrib8NVePL3fxM=
github.com/aliyun/credentials-go v1.4.5 h1:O76WYKgdy1oQYYiJkERjlA2dxGuvLRrzuO2ScrtGWSk=
github.com/aliyun/credentials-go v1.4.5/go.mod h1:Jm6d+xIgwJVLVWT561vy67ZRP4lPTQxMbEYRuT2Ti1U=
github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c= github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c=
github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA= github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
@ -144,6 +191,8 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME=
github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
@ -394,8 +443,8 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
@ -512,8 +561,9 @@ github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9Y
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ= github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ=
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
@ -678,8 +728,9 @@ github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b h1:FfH+VrHHk6Lxt9HdVS0PXzSXFyS2NbZKXv33FYPol0A=
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b/go.mod h1:AC62GU6hc0BrNm+9RK9VSiwa/EUe1bkIeFORAMcHvJU=
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
@ -805,6 +856,7 @@ github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVs
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
github.com/smartystreets/assertions v1.1.1 h1:T/YLemO5Yp7KPzS+lVtu+WsHn8yoSwTfItdAd1r3cck= github.com/smartystreets/assertions v1.1.1 h1:T/YLemO5Yp7KPzS+lVtu+WsHn8yoSwTfItdAd1r3cck=
github.com/smartystreets/assertions v1.1.1/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= github.com/smartystreets/assertions v1.1.1/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
@ -839,6 +891,7 @@ github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3
github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
@ -865,6 +918,9 @@ github.com/tevid/gohamcrest v1.1.1 h1:ou+xSqlIw1xfGTg1uq1nif/htZ2S3EzRqLm2BP+tYU
github.com/tevid/gohamcrest v1.1.1/go.mod h1:3UvtWlqm8j5JbwYZh80D/PVBt0mJ1eJiYgZMibh0H/k= github.com/tevid/gohamcrest v1.1.1/go.mod h1:3UvtWlqm8j5JbwYZh80D/PVBt0mJ1eJiYgZMibh0H/k=
github.com/tiendc/go-deepcopy v1.6.0 h1:0UtfV/imoCwlLxVsyfUd4hNHnB3drXsfle+wzSCA5Wo= github.com/tiendc/go-deepcopy v1.6.0 h1:0UtfV/imoCwlLxVsyfUd4hNHnB3drXsfle+wzSCA5Wo=
github.com/tiendc/go-deepcopy v1.6.0/go.mod h1:toXoeQoUqXOOS/X4sKuiAoSk6elIdqc0pN7MTgOOo2I= github.com/tiendc/go-deepcopy v1.6.0/go.mod h1:toXoeQoUqXOOS/X4sKuiAoSk6elIdqc0pN7MTgOOo2I=
github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
github.com/tklauser/go-sysconf v0.3.6 h1:oc1sJWvKkmvIxhDHeKWvZS4f6AW+YcoguSfRF2/Hmo4= github.com/tklauser/go-sysconf v0.3.6 h1:oc1sJWvKkmvIxhDHeKWvZS4f6AW+YcoguSfRF2/Hmo4=
github.com/tklauser/go-sysconf v0.3.6/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI= github.com/tklauser/go-sysconf v0.3.6/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI=
github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA= github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA=
@ -882,8 +938,9 @@ github.com/u2takey/ffmpeg-go v0.5.0 h1:r7d86XuL7uLWJ5mzSeQ03uvjfIhiJYvsRAJFCW4uk
github.com/u2takey/ffmpeg-go v0.5.0/go.mod h1:ruZWkvC1FEiUNjmROowOAps3ZcWxEiOpFoHCvk97kGc= github.com/u2takey/ffmpeg-go v0.5.0/go.mod h1:ruZWkvC1FEiUNjmROowOAps3ZcWxEiOpFoHCvk97kGc=
github.com/u2takey/go-utils v0.3.1 h1:TaQTgmEZZeDHQFYfd+AdUT1cT4QJgJn/XVPELhHw4ys= github.com/u2takey/go-utils v0.3.1 h1:TaQTgmEZZeDHQFYfd+AdUT1cT4QJgJn/XVPELhHw4ys=
github.com/u2takey/go-utils v0.3.1/go.mod h1:6e+v5vEZ/6gu12w/DC2ixZdZtCrNokVxD0JUklcqdCs= github.com/u2takey/go-utils v0.3.1/go.mod h1:6e+v5vEZ/6gu12w/DC2ixZdZtCrNokVxD0JUklcqdCs=
github.com/uber/jaeger-client-go v2.29.1+incompatible h1:R9ec3zO3sGpzs0abd43Y+fBZRJ9uiH6lXyR/+u6brW4=
github.com/uber/jaeger-client-go v2.29.1+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= github.com/uber/jaeger-client-go v2.29.1+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o=
github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg= github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg=
github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
@ -911,6 +968,7 @@ github.com/xuri/nfp v0.0.1 h1:MDamSGatIvp8uOmDP8FnmjuQpu90NzdJxo7242ANR9Q=
github.com/xuri/nfp v0.0.1/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ= github.com/xuri/nfp v0.0.1/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
@ -989,11 +1047,21 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@ -1037,6 +1105,10 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -1075,6 +1147,7 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
@ -1088,7 +1161,16 @@ golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20211105192438-b53810dc28af/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211105192438-b53810dc28af/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@ -1111,6 +1193,10 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -1155,6 +1241,7 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -1191,11 +1278,28 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -1206,6 +1310,11 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@ -1261,6 +1370,7 @@ golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjs
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
@ -1274,6 +1384,9 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -1406,7 +1519,7 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.56.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
@ -1429,6 +1542,7 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=

View File

@ -1,11 +1,14 @@
package common package common
import "fonchain-fiee/pkg/utils" import (
"fonchain-fiee/pkg/utils"
)
func Init() { func Init() {
_ = utils.CreateDirPath("./runtime") _ = utils.CreateDirPath("./runtime")
utils.CopyFile("./data/policy.html", "./runtime") //pkgSecurity.Init()
utils.CopyFile("./data/service.html", "./runtime") //utils.CopyFile("./data/policy.html", "./runtime")
utils.CopyFile("../data/policy.html", "./runtime") //utils.CopyFile("./data/service.html", "./runtime")
utils.CopyFile("../data/service.html", "./runtime") //utils.CopyFile("../data/policy.html", "./runtime")
//utils.CopyFile("../data/service.html", "./runtime")
} }

32
pkg/common/qwen/chat.go Normal file
View File

@ -0,0 +1,32 @@
package qwen
import (
"encoding/json"
"errors"
"fmt"
modelQwen "fonchain-fiee/pkg/model/qwen"
"fonchain-fiee/pkg/utils"
"go.uber.org/zap"
)
func Chat(req modelQwen.ChatRequest) (resp *modelQwen.ChatResponse, err error) {
jsonData, err := json.Marshal(req)
if err != nil {
zap.L().Error("GenerateTextImage Marshal failed", zap.Error(err))
return nil, errors.New("序列化请求失败")
}
body, err := utils.PostBytes("https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions", map[string]interface{}{
"Authorization": "Bearer " + modelQwen.DashscopeAPIKey,
"Content-Type": "application/json",
}, jsonData)
if err != nil {
zap.L().Error("Chat Post err", zap.Error(err))
return nil, errors.New("对话异常")
}
var result modelQwen.ChatResponse
if err = json.Unmarshal(body, &result); err != nil {
return nil, fmt.Errorf("解析响应失败: %v", "")
}
return &result, nil
}

View File

@ -2,10 +2,13 @@ package qwen
import ( import (
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
modelQwen "fonchain-fiee/pkg/model/qwen" modelQwen "fonchain-fiee/pkg/model/qwen"
"fonchain-fiee/pkg/utils" "fonchain-fiee/pkg/utils"
"time" "time"
"go.uber.org/zap"
) )
// GenerateTextImage 调用通义千问文生图API // GenerateTextImage 调用通义千问文生图API
@ -19,12 +22,14 @@ func GenerateTextImage(prompt string, size string) (resp *modelQwen.QwenImageRes
Watermark: false, Watermark: false,
PromptExtend: true, PromptExtend: true,
Size: size, Size: size,
N: 1, N: 4,
Seed: time.Now().Unix(),
}, },
} }
jsonData, err := json.Marshal(reqBody) jsonData, err := json.Marshal(reqBody)
if err != nil { if err != nil {
return nil, fmt.Errorf("序列化请求失败: %v", err) zap.L().Error("GenerateTextImage Marshal failed", zap.Error(err))
return nil, errors.New("序列化请求失败")
} }
body, err := utils.PostBytes(modelQwen.DashscopeText2ImageURL, map[string]interface{}{ body, err := utils.PostBytes(modelQwen.DashscopeText2ImageURL, map[string]interface{}{
"Authorization": "Bearer " + modelQwen.DashscopeAPIKey, "Authorization": "Bearer " + modelQwen.DashscopeAPIKey,
@ -32,14 +37,15 @@ func GenerateTextImage(prompt string, size string) (resp *modelQwen.QwenImageRes
"X-DashScope-Async": "enable", "X-DashScope-Async": "enable",
}, jsonData) }, jsonData)
if err != nil { if err != nil {
return nil, fmt.Errorf("请求失败: %v", err) zap.L().Error("GenerateTextImage Post failed", zap.Error(err))
return nil, errors.New("请求生图失败")
} }
var result modelQwen.QwenImageResponse var result modelQwen.QwenImageResponse
if err = json.Unmarshal(body, &result); err != nil { if err = json.Unmarshal(body, &result); err != nil {
return nil, fmt.Errorf("解析响应失败: %v", err) return nil, fmt.Errorf("解析响应失败: %v", "")
} }
if result.Code != "" { if result.Code != "" {
return nil, fmt.Errorf("生成失败: %s", result.Message) return nil, fmt.Errorf("生成失败: %s", "")
} }
return &result, nil return &result, nil
} }
@ -58,12 +64,13 @@ func GenerateEditImage(prompt string, images []string, size string) (*modelQwen.
Size: size, Size: size,
N: 4, N: 4,
Watermark: false, Watermark: false,
Seed: 0, Seed: time.Now().Unix(),
}, },
} }
jsonData, err := json.Marshal(reqBody) jsonData, err := json.Marshal(reqBody)
if err != nil { if err != nil {
return nil, fmt.Errorf("序列化请求失败: %v", err) zap.L().Error("GenerateEditImage Marshal failed", zap.Error(err))
return nil, errors.New("序列化请求失败")
} }
body, err := utils.PostBytes(modelQwen.DashscopeEditImageURL, map[string]interface{}{ body, err := utils.PostBytes(modelQwen.DashscopeEditImageURL, map[string]interface{}{
"Authorization": "Bearer " + modelQwen.DashscopeAPIKey, "Authorization": "Bearer " + modelQwen.DashscopeAPIKey,
@ -71,14 +78,16 @@ func GenerateEditImage(prompt string, images []string, size string) (*modelQwen.
"X-DashScope-Async": "enable", "X-DashScope-Async": "enable",
}, jsonData) }, jsonData)
if err != nil { if err != nil {
return nil, fmt.Errorf("请求失败: %v", err) zap.L().Error("GenerateEditImage PostBytes failed", zap.Error(err))
return nil, errors.New("请求异常")
} }
var result modelQwen.QwenImageResponse var result modelQwen.QwenImageResponse
if err = json.Unmarshal(body, &result); err != nil { if err = json.Unmarshal(body, &result); err != nil {
return nil, fmt.Errorf("解析响应失败: %v", err) zap.L().Error("GenerateEditImage PostBytes failed", zap.Error(err))
return nil, errors.New("解析响应失败")
} }
if result.Code != "" { if result.Code != "" {
return nil, fmt.Errorf("生成失败: %s", result.Message) return nil, fmt.Errorf("生成失败: %s", "")
} }
return &result, nil return &result, nil
} }
@ -90,20 +99,22 @@ func ImgTaskResult(taskID string) (*modelQwen.QwenImageResponse, error) {
"Content-Type": "application/json", "Content-Type": "application/json",
}) })
if err != nil { if err != nil {
return nil, fmt.Errorf("请求失败: %v", err) zap.L().Error("ImgTaskResult GetBytes failed", zap.Error(err))
return nil, errors.New("请求失败")
} }
var info modelQwen.QwenImageResponse var info modelQwen.QwenImageResponse
if err = json.Unmarshal(body, &info); err != nil { if err = json.Unmarshal(body, &info); err != nil {
return nil, fmt.Errorf("解析响应失败: %v", err) zap.L().Error("ImgTaskResult Unmarshal failed", zap.Error(err))
return nil, errors.New("解析响应失败")
} }
if info.Code != "" { if info.Code != "" {
return nil, fmt.Errorf("生成失败: %s", info.Message) return nil, fmt.Errorf("生成失败: %s", "")
} }
if info.Output.TaskStatus == "FAILED" { if info.Output.TaskStatus == "FAILED" {
return nil, fmt.Errorf("生成失败") return nil, fmt.Errorf("生成失败")
} }
if len(info.Output.Results) > 0 { if len(info.Output.Results) > 0 {
zap.L().Info("ImgTaskResult GetBytes success", zap.String("taskID", taskID), zap.Any("results", info))
return &info, nil return &info, nil
} }
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)

View File

@ -2,6 +2,8 @@ package cron
import ( import (
"context" "context"
"encoding/json"
"errors"
"fmt" "fmt"
"fonchain-fiee/api/cast" "fonchain-fiee/api/cast"
"fonchain-fiee/pkg/cache" "fonchain-fiee/pkg/cache"
@ -20,13 +22,15 @@ func InitTasks() error {
cm := GetCronManager() cm := GetCronManager()
err := cm.AddTask("refreshWorkApprovalStatus", "0 */1 * * * *", RefreshWorkApprovalStatusTask) err := cm.AddTask("refreshWorkApprovalStatus", "0 */1 * * * *", RefreshWorkApprovalStatusTask)
err = cm.AddTask("artistAutoConfirm", "0 */1 * * * *", ArtistAutoConfirmTask) err = cm.AddTask("artistAutoConfirm", "0 */1 * * * *", ArtistAutoConfirmTask)
err = cm.AddTask("refreshPublishStatus", "0 */1 * * * *", RefreshPublishStatusTask)
err = cm.AddTask("artistAutoConfirmAnalysis", "0 */1 * * * *", ArtistAutoConfirmAnalysisTask)
err = cm.AddTask("refreshWorkAnalysisApprovalStatus", "0 */1 * * * *", RefreshWorkAnalysisApprovalStatusTask)
if err != nil { if err != nil {
log.Printf("添加测试任务失败: %v", err) log.Printf("添加定时任务失败: %v", err)
} }
cm.Start() cm.Start()
// 启动队列消费者
go WorkPublishQueueConsumer()
return nil return nil
} }
@ -46,22 +50,6 @@ func RefreshWorkApprovalStatusTask() {
serverCast.RefreshWorkApproval(nil, resp.Data) serverCast.RefreshWorkApproval(nil, resp.Data)
} }
func RefreshWorkAnalysisApprovalStatusTask() {
resp, err := service.CastProvider.ListWorkAnalysis(context.Background(), &cast.ListWorkAnalysisReq{
Page: 1,
StatusList: []uint32{2}, // 状态为2表示待审批
PageSize: 999999,
})
if err != nil {
log.Printf("获取数据分析列表失败: %v", err)
return
}
if resp.Data == nil || len(resp.Data) == 0 {
return
}
serverCast.RefreshWorkAnalysisApproval(nil, resp.Data)
}
func ArtistAutoConfirmTask() { func ArtistAutoConfirmTask() {
now := float64(time.Now().Unix()) now := float64(time.Now().Unix())
opt := redis.ZRangeBy{ opt := redis.ZRangeBy{
@ -83,23 +71,75 @@ func ArtistAutoConfirmTask() {
} }
} }
func ArtistAutoConfirmAnalysisTask() { func RefreshPublishStatusTask() {
now := float64(time.Now().Unix()) // 加上锁 万一上一批没有同步完
opt := redis.ZRangeBy{ lockKey := "refresh_publish_status:lock"
Min: fmt.Sprintf("%d", 0), reply := cache.RedisClient.SetNX(lockKey, "1", 5*time.Minute)
Max: fmt.Sprintf("%f", now), if !reply.Val() {
zap.L().Warn("任务正在被其他实例处理")
return
} }
analysisUuids, err := cache.RedisClient.ZRangeByScore(modelCast.AutoConfirmAnalysisQueueKey, opt).Result() defer func() {
cache.RedisClient.Del(lockKey)
}()
err := serverCast.RefreshPublish()
if err != nil { if err != nil {
zap.L().Error("获取到期数据分析任务失败", zap.Error(err)) zap.L().Error("刷新发布状态失败", zap.Error(err))
return return
} }
if len(analysisUuids) == 0 { zap.L().Info("刷新发布状态成功")
zap.L().Debug("没有到期的数据分析任务") }
return
// WorkPublishQueueConsumer 监听work:publish:queue队列的消费者
func WorkPublishQueueConsumer() {
zap.L().Info("开始监听work:publish:queue队列")
for {
result, err := cache.RedisClient.BRPop(0*time.Second, modelCast.WorkPublishQueueKey).Result()
if err != nil {
zap.L().Error("监听work:publish:queue队列失败", zap.Error(err))
time.Sleep(5 * time.Second) // 出错后等待5秒再重试
continue
} }
zap.L().Info("发现到期数据分析任务", zap.Int("count", len(analysisUuids)))
for _, analysisUuid := range analysisUuids { if len(result) < 2 {
serverCast.ProcessAnalysisTask(context.Background(), analysisUuid) zap.L().Warn("队列返回数据格式异常", zap.Any("result", result))
continue
}
workData := result[1] // BRPOP返回[key, value]value在第二个元素
zap.L().Info("从work:publish:queue队列收到数据", zap.String("data", workData))
// 处理队列数据
if err = processWorkPublishQueueData(workData); err != nil {
zap.L().Error("处理work:publish:queue队列数据失败", zap.Error(err), zap.String("data", workData))
continue
}
zap.L().Info("成功处理work:publish:queue队列数据", zap.String("data", workData))
} }
} }
// processWorkPublishQueueData 处理从work:publish:queue队列中取出的数据
func processWorkPublishQueueData(data string) error {
// 延时1秒消费
time.Sleep(time.Second * 1)
var workData map[string]string
_ = json.Unmarshal([]byte(data), &workData)
workUuid := workData["workUuid"]
if workUuid == "" {
zap.L().Error("队列数据为空", zap.String("raw_data", data))
return errors.New("队列数据为空")
}
zap.L().Info("处理发布工作队列数据", zap.String("work_uuid", workUuid))
// 调用发布工作逻辑
err := serverCast.PublishWork(context.Background(), &cast.PublishReq{
WorkUuids: []string{workUuid},
})
if err != nil {
zap.L().Error("发布工作失败",
zap.String("work_uuid", workUuid),
zap.Error(err))
return err
}
zap.L().Info("发布工作成功", zap.String("work_uuid", workUuid))
return nil
}

View File

@ -6,12 +6,6 @@ type UserWorkConfirmReq struct {
ConfirmStatus int `json:"confirmStatus"` // 1确认 2 驳回 ConfirmStatus int `json:"confirmStatus"` // 1确认 2 驳回
} }
type UserWorkAnalysisConfirmReq struct {
Uuid string `json:"uuid"` // 分析UUID
ConfirmRemark string `json:"confirmRemark"` // 确认备注
ConfirmStatus int `json:"confirmStatus"` // 1确认 2 驳回
}
type GetBundleBalanceListResp struct { type GetBundleBalanceListResp struct {
Total int64 `protobuf:"varint,1,opt,name=total,proto3" json:"total"` Total int64 `protobuf:"varint,1,opt,name=total,proto3" json:"total"`

View File

@ -0,0 +1 @@
package cast

View File

@ -2,6 +2,10 @@ package cast
type BalanceTypeEnum int32 type BalanceTypeEnum int32
type SyncAsProfileReq struct {
ID uint64 `json:"id"`
}
// 定义枚举值 // 定义枚举值
const ( const (
BalanceTypeAccountValue BalanceTypeEnum = 1 BalanceTypeAccountValue BalanceTypeEnum = 1
@ -9,3 +13,20 @@ const (
BalanceTypeVideoValue BalanceTypeEnum = 3 BalanceTypeVideoValue BalanceTypeEnum = 3
BalanceTypeDataValue BalanceTypeEnum = 4 BalanceTypeDataValue BalanceTypeEnum = 4
) )
var PlatformNameKv = map[uint32]string{
1: "tiktok",
3: "instagram",
4: "DM",
}
var NamePlatformIDKv = map[string]uint32{
"tiktok": 1,
"instagram": 3,
"DM": 4,
}
var PlatformIDStrKv = map[string]uint8{
"TIKTOK": 1,
"INS": 2,
"DM": 4,
}

View File

@ -1,8 +1,9 @@
package cast package cast
type OAuthPlatformReq struct { type OAuthPlatformReq struct {
MediaAccountUuid string `json:"mediaAccountUuid" form:"mediaAccountUuid" binding:"required"` MediaAccountUuid string `json:"mediaAccountUuid" form:"mediaAccountUuid"`
PlatformID int `json:"platformID" form:"platformID" binding:"required"` PlatformID int `json:"platformID" form:"platformID"`
ArtistUuid string `json:"artistUuid" form:"artistUuid"`
} }
type OAuthPlatformResp struct { type OAuthPlatformResp struct {

View File

@ -13,9 +13,7 @@ const (
const ( const (
AutoConfirmQueueKey = "auto_confirm:queue" AutoConfirmQueueKey = "auto_confirm:queue"
AutoConfirmLockKey = "auto_confirm:lock:%s" AutoConfirmLockKey = "auto_confirm:lock:%s"
WorkPublishQueueKey = "work:publish:queue"
AutoConfirmAnalysisQueueKey = "auto_confirm:analysis:queue"
AutoConfirmAnalysisLockKey = "auto_confirm:analysis:lock:%s"
) )
var WorkCategoryMM = map[int]string{ var WorkCategoryMM = map[int]string{
@ -46,3 +44,11 @@ var WorkStatusMM = map[int]string{
8: "未知", 8: "未知",
9: "验收确认", 9: "验收确认",
} }
type DMPost struct {
ID string `json:"id"`
Title string `json:"title"`
Url string `json:"url"`
Published bool `json:"published"`
Error interface{}
}

43
pkg/model/qwen/chat.go Normal file
View File

@ -0,0 +1,43 @@
package qwen
type ChatRequest struct {
Model string `json:"model"`
Messages []Message `json:"messages"`
Seed int64 `json:"seed,omitempty"`
EnableSearch bool `json:"enable_search,omitempty"`
}
type Message struct {
Role string `json:"role"`
Content []Content `json:"content"`
}
type Content struct {
Type string `json:"type"`
Text string `json:"text,omitempty"` // 只有 type=text 时有
ImageURL *ImageURL `json:"image_url,omitempty"` // 只有 type=image_url 时有
}
type ImageURL struct {
URL string `json:"url"`
}
type ChatResponse struct {
Choices []Choice `json:"choices"`
}
type Choice struct {
Message struct {
Content string `json:"content"`
ReasoningContent string `json:"reasoning_content"`
Role string `json:"role"`
} `json:"message"`
FinishReason string `json:"finish_reason"`
}
type MoreTextReq struct {
TitlePrompt string `json:"titlePrompt"`
ContentPrompt string `json:"contentPrompt"`
ImagePrompt string `json:"imagePrompt"`
Images []string `json:"images"`
}

View File

@ -11,6 +11,7 @@ type QwenImageRequest struct {
Model string `json:"model"` Model string `json:"model"`
Input QwenImageInput `json:"input"` Input QwenImageInput `json:"input"`
Parameters QwenImageParameters `json:"parameters"` Parameters QwenImageParameters `json:"parameters"`
Seed int64 `json:"seed"`
} }
type QwenImageInput struct { type QwenImageInput struct {
@ -28,7 +29,7 @@ type QwenImageParameters struct {
N int `json:"n,omitempty"` N int `json:"n,omitempty"`
PromptExtend bool `json:"prompt_extend,omitempty"` PromptExtend bool `json:"prompt_extend,omitempty"`
Watermark bool `json:"watermark"` //水印 Watermark bool `json:"watermark"` //水印
Seed int `json:"seed,omitempty"` Seed int64 `json:"seed,omitempty"`
} }
// QwenImageResponse 通义千问文生图响应 // QwenImageResponse 通义千问文生图响应
@ -71,7 +72,7 @@ type QwenEditImageParameters struct {
Size string `json:"size"` Size string `json:"size"`
N int `json:"n"` N int `json:"n"`
Watermark bool `json:"watermark"` Watermark bool `json:"watermark"`
Seed int `json:"seed"` Seed int64 `json:"seed"`
} }
type QwenEditImageResponse struct { type QwenEditImageResponse struct {

View File

@ -1,46 +0,0 @@
package router
import (
"fonchain-fiee/pkg/middleware"
"fonchain-fiee/pkg/service"
"fonchain-fiee/pkg/service/bundle"
serviceCast "fonchain-fiee/pkg/service/cast"
"github.com/gin-gonic/gin"
)
func AnalysisRouter(r *gin.RouterGroup) {
analysis := r.Group("analysis")
analysis.Use(middleware.CheckWebLogin(service.AccountProvider))
{
analysis.POST("create", serviceCast.CreateWorkAnalysis)
analysis.POST("update", serviceCast.UpdateWorkAnalysis)
analysis.POST("update-status", serviceCast.UpdateWorkAnalysisStatus)
analysis.POST("detail", serviceCast.GetWorkAnalysis)
analysis.POST("latest", serviceCast.GetLatestWorkAnalysis)
analysis.POST("list", serviceCast.ListWorkAnalysis)
analysis.POST("delete", serviceCast.DeleteWorkAnalysis)
analysis.POST("export-list", serviceCast.ListWorkAnalysisExport) // 数据分析分析列表导出
analysis.POST("export-single-list", serviceCast.ListWorkAnalysisSingleExport) // 数据分析分析单个列表导出
analysis.POST("artist-data-list", serviceCast.ArtistDataList) // 艺人数据列表
analysis.POST("media-data-list", serviceCast.MediaDataList) // 自媒体数据列表
analysis.POST("data-overview", serviceCast.DataOverview) // 数据概览
analysis.POST("artist-metrics", serviceCast.ArtistMetricsSeries) // 艺人指标系列
analysis.POST("artist-metrics-single", serviceCast.ArtistMetricsDailyWindow) // 艺人指标日窗口
analysis.POST("tobe-confirmed-list", serviceCast.TobeConfirmedList) // 待确认数据列表
analysis.POST("update-approval-id", serviceCast.UpdateWorkAnalysisApprovalID) // 更新作品分析审批ID
}
// 员工任务相关路由需要App登录验证
analysisAppRoute := r.Group("app/analysis")
analysisAppRoute.Use(middleware.CheckLogin(service.AccountFieeProvider))
{
analysisAppRoute.POST("list", serviceCast.ListWorkAnalysis) // 作品列表
analysisAppRoute.POST("detail", serviceCast.GetWorkAnalysis) // 作品分析详情
analysisAppRoute.POST("update-status", serviceCast.UpdateWorkAnalysisStatus) // 用户确认
analysisAppRoute.POST("check-balance", serviceCast.CheckBundleBalance) // 检查套餐余量
analysisAppRoute.POST("tobe-confirmed-list", serviceCast.TobeConfirmedList) // 待确认数据列表
analysisAppRoute.POST("work-analysis-confirm", bundle.WorkAnalysisConfirm)
}
}

View File

@ -22,10 +22,12 @@ func MediaRouter(r *gin.RouterGroup) {
media.POST("oauth-account", serviceCast.OAuthAccount) media.POST("oauth-account", serviceCast.OAuthAccount)
media.POST("refresh-token", serviceCast.RefreshToken) media.POST("refresh-token", serviceCast.RefreshToken)
media.POST("artist-info", serviceCast.ArtistInfo) media.POST("artist-info", serviceCast.ArtistInfo)
media.POST("sync-as-profile", serviceCast.SyncAsProfile)
} }
mediaNoLogin := r.Group("media") mediaNoLogin := r.Group("media")
{ {
mediaNoLogin.GET("oauth2callback", serviceCast.OAuth2Callback) mediaNoLogin.GET("oauth2callback", serviceCast.OAuth2Callback)
mediaNoLogin.GET("as-oauth2callback", serviceCast.AsOAuth2Callback)
mediaNoLogin.Any("test", serviceCast.Test) mediaNoLogin.Any("test", serviceCast.Test)
//mediaNoLogin.GET("dmoauth2callback", serviceCast.DMOAuth2Callback) //mediaNoLogin.GET("dmoauth2callback", serviceCast.DMOAuth2Callback)
} }
@ -43,7 +45,7 @@ func MediaRouter(r *gin.RouterGroup) {
work.POST("delete", serviceCast.DelWork) work.POST("delete", serviceCast.DelWork)
work.POST("remind", serviceCast.Remind) work.POST("remind", serviceCast.Remind)
work.POST("publish-info", serviceCast.PublishInfo) work.POST("publish-info", serviceCast.PublishInfo)
work.POST("import-batch", serviceCast.ImportWorkBatch)
} }
script := auth.Group("script") script := auth.Group("script")
@ -72,9 +74,15 @@ func MediaRouter(r *gin.RouterGroup) {
} }
//AI 生图 //AI 生图
ai := auth.Group("ai") aiNoAuth := noAuth.Group("ai")
{ {
ai.POST("image-generate", serviceAI.AIImageGenerate) aiNoAuth.POST("image-generate", serviceAI.AIImageGenerate)
aiNoAuth.POST("text-generate", serviceAI.AIChat)
}
aiAuth := auth.Group("ai")
{
aiAuth.POST("one-text", serviceAI.OneText)
aiAuth.POST("more-text", serviceAI.MoreText)
} }
social := noAuth.Group("social") social := noAuth.Group("social")

View File

@ -53,7 +53,6 @@ func NewRouter() *gin.Engine {
ValueAddBundleRouter(privateGroup) ValueAddBundleRouter(privateGroup)
TaskBenchRouter(privateGroup) // 新增任务台路由 TaskBenchRouter(privateGroup) // 新增任务台路由
MediaRouter(privateGroup) MediaRouter(privateGroup)
AnalysisRouter(privateGroup)
SecFilingRouter(privateGroup) SecFilingRouter(privateGroup)
app.MediaAppRouter(privateGroup) app.MediaAppRouter(privateGroup)
{ {
@ -198,6 +197,14 @@ func NewRouter() *gin.Engine {
importRoute.POST("data/publish3", imports.ImportPublishV3) importRoute.POST("data/publish3", imports.ImportPublishV3)
importRoute.POST("data/publish4", imports.ImportPublishV4) importRoute.POST("data/publish4", imports.ImportPublishV4)
importRoute.POST("data/confirm", imports.WorkConfirm) importRoute.POST("data/confirm", imports.WorkConfirm)
importRoute.POST("image-content/import", imports.ImageContentImport) // AI生成内容并导入系统
importRoute.GET("image-content/result", imports.ImageContentGetResult) // 获取导入结果
importRoute.GET("image-content/result/excel", imports.ImageContentGetResultExcel) // 导出错误的excel
importRoute.GET("generate/photo/test", imports.Test)
importRoute.GET("generate/photo/test1", imports.Test1)
importRoute.GET("generate/photo/test2", imports.Test2)
} }
//静态文件 //静态文件
r.StaticFS("/api/fiee/static", http.Dir("./runtime")) r.StaticFS("/api/fiee/static", http.Dir("./runtime"))

27
pkg/security/init.go Normal file
View File

@ -0,0 +1,27 @@
package security
import (
"github.com/fonchain/utils/security"
"go.uber.org/zap"
)
var ImageScanner *security.ImageScanner
func Init() {
config, err := security.LoadConfigFromFile("../conf/alibabacloud.env")
if err != nil {
zap.L().Error("load config fail", zap.Error(err))
panic(err)
//err = errors.New("加载黄反配置失败")
//return false, err
}
if err = config.GetSTSToken(); err != nil {
zap.L().Error("load sts token failed", zap.Error(err))
panic(err)
}
ImageScanner, err = security.NewImageScanner(config)
if err != nil {
panic(err)
}
}

28
pkg/service/ai/chat.go Normal file
View File

@ -0,0 +1,28 @@
package ai
import (
"fonchain-fiee/pkg/common/qwen"
modelQwen "fonchain-fiee/pkg/model/qwen"
"fonchain-fiee/pkg/service"
"time"
"errors"
"github.com/gin-gonic/gin"
)
func AIChat(ctx *gin.Context) {
var req modelQwen.ChatRequest
if err := ctx.ShouldBindJSON(&req); err != nil {
service.Error(ctx, errors.New("参数错误 "))
return
}
req.Seed = time.Now().Unix()
resp, err := qwen.Chat(req)
if err != nil {
service.Error(ctx, err)
return
}
service.Success(ctx, resp)
return
}

View File

@ -8,20 +8,22 @@ import (
"fonchain-fiee/pkg/model/login" "fonchain-fiee/pkg/model/login"
modelQwen "fonchain-fiee/pkg/model/qwen" modelQwen "fonchain-fiee/pkg/model/qwen"
"fonchain-fiee/pkg/service" "fonchain-fiee/pkg/service"
"regexp"
"strings" "strings"
"time" "time"
"unicode"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
// ImageGenerateRequest 文生图请求参数 // ImageGenerateRequest 文生图请求参数
type ImageGenerateRequest struct { type ImageGenerateRequest struct {
Prompt string `json:"prompt" binding:"required"` Prompt string `json:"prompt"`
Size string `json:"size,omitempty"` Size string `json:"size"`
NegativePrompt string `json:"negative_prompt,omitempty"` NegativePrompt string `json:"negative_prompt"`
Watermark bool `json:"watermark,omitempty"` Watermark bool `json:"watermark"`
PromptExtend bool `json:"prompt_extend,omitempty"` PromptExtend bool `json:"prompt_extend"`
Images []string `json:"images" binding:"required"` Images []string `json:"images"`
} }
// ImageGenerateResponse 文生图响应数据 // ImageGenerateResponse 文生图响应数据
@ -39,11 +41,29 @@ type ImageGenerateResponse struct {
func AIImageGenerate(ctx *gin.Context) { func AIImageGenerate(ctx *gin.Context) {
var req ImageGenerateRequest var req ImageGenerateRequest
if err := ctx.ShouldBindJSON(&req); err != nil { if err := ctx.ShouldBindJSON(&req); err != nil {
service.Error(ctx, errors.New("参数错误: "+err.Error())) service.Error(ctx, errors.New("参数错误: "))
return return
} }
//检测之前是否有图片生成 //检测之前是否有图片生成
loginUserInfo := login.GetUserInfoFromC(ctx) //loginUserInfo := login.GetUserInfoFromC(ctx)
loginUserInfo := login.Info{
ID: 1,
Status: 0,
Name: "",
Sex: "",
Nationality: "",
DocumentType: 0,
CertificatePicture: "",
Validity: "",
PlaceOfResidence: "",
GroupPhoto: "",
Attachment: "",
SubNum: "",
NotPassRemarks: "",
Domain: "",
TelNum: "",
SubscriberNumber: "",
}
if req.Prompt == "" { if req.Prompt == "" {
service.Error(ctx, errors.New("提示词不能为空")) service.Error(ctx, errors.New("提示词不能为空"))
return return
@ -99,3 +119,368 @@ func AIImageGenerate(ctx *gin.Context) {
cache.RedisClient.Del(lockKey) cache.RedisClient.Del(lockKey)
service.Success(ctx, result) service.Success(ctx, result)
} }
func OneText(ctx *gin.Context) {
var req ImageGenerateRequest
if err := ctx.ShouldBindJSON(&req); err != nil {
service.Error(ctx, errors.New("参数错误: "))
return
}
type ResultAll struct {
Result []struct {
Url string `json:"url"`
OrigPrompt string `json:"orig_prompt"`
ActualPrompt string `json:"actual_prompt"`
} `json:"result"`
Title string `json:"title"`
Content string `json:"content"`
Prompt string `json:"prompt"`
}
var resultAll ResultAll
if req.Prompt == "" {
service.Error(ctx, errors.New("提示词不能为空"))
return
}
if len(req.Images) > 3 {
service.Error(ctx, errors.New("最多只能上传3张图片"))
return
}
loginUserInfo := login.GetUserInfoFromC(ctx)
if req.Size == "" {
req.Size = "1024*1024"
}
var lockKey, taskID, oldVal string
if len(req.Images) == 0 {
lockKey = "generate:text_image:" + fmt.Sprint(loginUserInfo.ID)
} else {
lockKey = "generate:edit_image:" + fmt.Sprint(loginUserInfo.ID)
}
reply := cache.RedisClient.SetNX(lockKey, time.Now().Format("2006-01-02 15:04:05"), time.Minute*60)
if !reply.Val() {
oldVal = cache.RedisClient.Get(lockKey).String()
if len(oldVal) > 20 && strings.Index(oldVal, "_") != -1 {
taskID = oldVal[strings.LastIndex(oldVal, "_")+1:]
}
} else {
defer cache.RedisClient.Del(lockKey)
}
var resultTask, result *modelQwen.QwenImageResponse
var err error
if taskID == "" {
// 先进行聊天获取上下文
chatReq, err1 := ChatReqGet(req)
if err1 != nil {
service.Error(ctx, err1)
return
}
chatResp, err2 := qwen.Chat(*chatReq)
if err2 != nil {
service.Error(ctx, err2)
return
}
if len(chatResp.Choices) == 0 {
service.Error(ctx, errors.New("聊天返回结果为空"))
return
}
req.Prompt = chatResp.Choices[0].Message.Content
resultAll.Prompt = req.Prompt
if len(req.Images) == 0 {
resultTask, err = qwen.GenerateTextImage(req.Prompt, req.Size)
if err != nil {
service.Error(ctx, err)
return
}
taskID = resultTask.Output.TaskID
cache.RedisClient.Set(lockKey, time.Now().Format("2006-01-02 15:04:05")+"_"+taskID, time.Minute*5)
} else {
resultTask, err = qwen.GenerateEditImage(req.Prompt, req.Images, req.Size)
if err != nil {
service.Error(ctx, err)
return
}
if resultTask.Code != "" {
service.Error(ctx, errors.New("文生图失败: "+resultTask.Message))
return
}
taskID = resultTask.Output.TaskID
cache.RedisClient.Set(lockKey, time.Now().Format("2006-01-02 15:04:05")+"_"+taskID, time.Minute*5)
}
}
result, err = qwen.ImgTaskResult(taskID)
if err != nil {
service.Error(ctx, err)
return
}
cache.RedisClient.Del(lockKey)
if len(result.Output.Results) == 0 {
service.Error(ctx, errors.New("图片生成失败"))
return
}
resultAll.Title, resultAll.Content = extractTitleAndContent(req.Prompt)
for _, v := range result.Output.Results {
resultAll.Result = append(resultAll.Result, struct {
Url string `json:"url"`
OrigPrompt string `json:"orig_prompt"`
ActualPrompt string `json:"actual_prompt"`
}{
Url: v.URL,
OrigPrompt: v.OrigPrompt,
ActualPrompt: v.ActualPrompt,
})
}
service.Success(ctx, resultAll)
}
func MoreText(ctx *gin.Context) {
var req modelQwen.MoreTextReq
if err := ctx.ShouldBindJSON(&req); err != nil {
service.Error(ctx, errors.New("参数错误 "))
return
}
type ResultAll struct {
Title string `json:"title"`
Content string `json:"content"`
Result []struct {
Url string `json:"url"`
OrigPrompt string `json:"orig_prompt"`
ActualPrompt string `json:"actual_prompt"`
} `json:"result"`
}
var resultAll ResultAll
// 三个协程:两个去请求聊天,一个去生成图片
type chatResult struct {
title string
content string
err error
}
type imageResult struct {
result *modelQwen.QwenImageResponse
err error
}
// 协程1生成标题标题和内容加附件图片都请求聊天生成文字
titleChan := make(chan chatResult, 1)
go func() {
chatReq, err := buildChatRequest(req.TitlePrompt, req.Images)
if err != nil {
titleChan <- chatResult{err: err}
return
}
chatResp, err := qwen.Chat(*chatReq)
if err != nil {
titleChan <- chatResult{err: err}
return
}
if len(chatResp.Choices) == 0 {
titleChan <- chatResult{err: errors.New("标题聊天返回结果为空")}
return
}
titleChan <- chatResult{title: chatResp.Choices[0].Message.Content}
}()
// 协程2生成内容标题和内容加附件图片都请求聊天生成文字
contentChan := make(chan chatResult, 1)
go func() {
chatReq, err := buildChatRequest(req.ContentPrompt, req.Images)
if err != nil {
contentChan <- chatResult{err: err}
return
}
chatResp, err := qwen.Chat(*chatReq)
if err != nil {
contentChan <- chatResult{err: err}
return
}
if len(chatResp.Choices) == 0 {
contentChan <- chatResult{err: errors.New("内容聊天返回结果为空")}
return
}
contentChan <- chatResult{content: chatResp.Choices[0].Message.Content}
}()
// 协程3生成图片图片要求加附件去生成图片
imageChan := make(chan imageResult, 1)
go func() {
// 先请求聊天获取图片提示词(图片要求加附件去生成图片)
chatReq, err := buildChatRequest(req.ImagePrompt, req.Images)
if err != nil {
imageChan <- imageResult{err: err}
return
}
fmt.Println("chat" + time.Now().Format("2006-01-02 15:04:05"))
chatResp, err := qwen.Chat(*chatReq)
if err != nil {
imageChan <- imageResult{err: err}
return
}
if len(chatResp.Choices) == 0 {
imageChan <- imageResult{err: errors.New("图片提示词聊天返回结果为空")}
return
}
imagePrompt := chatResp.Choices[0].Message.Content
fmt.Println("chat ok" + time.Now().Format("2006-01-02 15:04:05"))
// 生成图片(图片要求加附件去生成图片)
size := "1024*1024"
var resultTask *modelQwen.QwenImageResponse
if len(req.Images) == 0 {
resultTask, err = qwen.GenerateTextImage(imagePrompt, size)
} else {
resultTask, err = qwen.GenerateEditImage(imagePrompt, req.Images, size)
}
if err != nil {
imageChan <- imageResult{err: err}
return
}
if resultTask.Code != "" {
imageChan <- imageResult{err: errors.New("文生图失败: " + resultTask.Message)}
return
}
fmt.Println("task" + time.Now().Format("2006-01-02 15:04:05"))
// 等待图片生成完成
result, err := qwen.ImgTaskResult(resultTask.Output.TaskID)
if err != nil {
imageChan <- imageResult{err: err}
return
}
fmt.Println("image" + time.Now().Format("2006-01-02 15:04:05"))
imageChan <- imageResult{result: result}
}()
// 等待所有协程完成(并发收集结果)
var titleRes chatResult
var contentRes chatResult
var imageRes imageResult
// 使用 select 来并发等待所有结果
completed := 0
total := 3
for completed < total {
select {
case titleRes = <-titleChan:
completed++
case contentRes = <-contentChan:
completed++
case imageRes = <-imageChan:
completed++
}
}
// 处理标题结果
if titleRes.err != nil {
service.Error(ctx, fmt.Errorf("生成标题失败: %v", titleRes.err))
return
}
resultAll.Title = titleRes.title
// 处理内容结果
if contentRes.err != nil {
service.Error(ctx, fmt.Errorf("生成内容失败: %v", contentRes.err))
return
}
resultAll.Content = contentRes.content
// 处理图片结果
if imageRes.err != nil {
service.Error(ctx, fmt.Errorf("生成图片失败: %v", imageRes.err))
return
}
if imageRes.result == nil || len(imageRes.result.Output.Results) == 0 {
service.Error(ctx, errors.New("图片生成失败"))
return
}
// 组装图片结果
for _, v := range imageRes.result.Output.Results {
resultAll.Result = append(resultAll.Result, struct {
Url string `json:"url"`
OrigPrompt string `json:"orig_prompt"`
ActualPrompt string `json:"actual_prompt"`
}{
Url: v.URL,
OrigPrompt: v.OrigPrompt,
ActualPrompt: v.ActualPrompt,
})
}
service.Success(ctx, resultAll)
}
// buildChatRequest 组装聊天的参数(用于 MoreText
func buildChatRequest(prompt string, images []string) (*modelQwen.ChatRequest, error) {
if prompt == "" {
return nil, errors.New("提示词不能为空")
}
var chatReq modelQwen.ChatRequest
chatReq.Model = "qwen3-max" //qwen3-max qwen-plus qwen-flash
var content []modelQwen.Content
content = append(content, modelQwen.Content{Type: "text", Text: prompt})
for _, v := range images {
if v != "" {
content = append(content, modelQwen.Content{Type: "image_url", ImageURL: &modelQwen.ImageURL{URL: v}})
}
}
chatReq.Messages = []modelQwen.Message{
{
Role: "user",
Content: content,
},
}
chatReq.Seed = time.Now().Unix()
return &chatReq, nil
}
// ChatReqGet 组装聊天的参数
func ChatReqGet(req ImageGenerateRequest) (*modelQwen.ChatRequest, error) {
return buildChatRequest(req.Prompt, req.Images)
}
func cleanTitle(s string) string {
var b strings.Builder
for _, r := range strings.TrimSpace(s) {
if unicode.Is(unicode.Han, r) || unicode.IsLetter(r) || unicode.IsDigit(r) ||
unicode.IsPunct(r) || unicode.IsSpace(r) {
b.WriteRune(r)
}
}
out := strings.ReplaceAll(b.String(), "*", "")
out = strings.TrimSpace(out)
return out
}
// extractTitleAndContent 从完整文本中提取 title 和 content
func extractTitleAndContent(text string) (title, content string) {
reTitleLine := regexp.MustCompile(`(?m).*标题[:]\s*(.+)$`)
if m := reTitleLine.FindStringSubmatch(text); len(m) >= 2 {
title = cleanTitle(m[1])
}
reContent := regexp.MustCompile(`(?s)标题[:].*?\n\s*\n(.*?)(?:\n\s*\n📸|\n\s*\n#|$)`)
if m := reContent.FindStringSubmatch(text); len(m) >= 2 {
content = strings.TrimSpace(m[1])
content = regexp.MustCompile(`\n+`).ReplaceAllString(content, "\n")
// 如果你希望把段内换行变成句内空格,可用下面这一行代替上面换行替换
// content = regexp.MustCompile(`\s*\n\s*`).ReplaceAllString(content, " ")
}
if content == "" && title != "" {
idx := strings.Index(text, title)
if idx >= 0 {
after := text[idx+len(title):]
after = strings.ReplaceAll(after, "**", "")
// 截到 "📸" 或 第一个 "#" 标签
if i := strings.Index(after, "📸"); i >= 0 {
after = after[:i]
} else if i := strings.Index(after, "#"); i >= 0 {
after = after[:i]
}
content = strings.TrimSpace(after)
}
}
return title, content
}

View File

@ -245,86 +245,6 @@ func WorkConfirm(c *gin.Context) { // 确认作品并扣除余量
service.Success(c, res) service.Success(c, res)
} }
func WorkAnalysisConfirm(c *gin.Context) { // 确认数据分析并扣除余量
var req bundleModel.UserWorkAnalysisConfirmReq
if err := c.ShouldBindJSON(&req); err != nil {
service.Error(c, err)
return
}
if req.ConfirmStatus == 2 { // 驳回完直接结束
res, err := service.CastProvider.UpdateWorkAnalysisStatus(c, &cast.UpdateWorkAnalysisStatusReq{
WorkAction: cast.WorkActionENUM_CONFIRM,
Uuid: req.Uuid,
ConfirmRemark: req.ConfirmRemark,
ConfirmStatus: 2,
})
if err != nil {
service.Error(c, errors.New(common.UpdateWorkStatusFailed))
return
}
service.Success(c, res)
return
}
userInfo := login.GetUserInfoFromC(c)
balanceInfoRes, err := service.BundleProvider.GetBundleBalanceByUserId(context.Background(), &bundle.GetBundleBalanceByUserIdReq{
UserId: int32(userInfo.ID),
})
if err != nil {
service.Error(c, errors.New(common.GetUserBalanceFailed))
return
}
analysisInfoRes, err := service.CastProvider.GetWorkAnalysis(c, &cast.GetWorkAnalysisDetailReq{
Uuid: req.Uuid,
})
if err != nil {
service.Error(c, errors.New(common.GetWorkDetailFailed))
return
}
if analysisInfoRes.WorkAnalysisStatus != 4 {
service.Error(c, errors.New("数据分析不是待确认状态"))
return
}
var addBalanceReq bundle.AddBundleBalanceReq
addBalanceReq.UserId = int32(userInfo.ID)
// 检查数据分析余量
if balanceInfoRes.DataAnalysisConsumptionNumber >= balanceInfoRes.DataAnalysisNumber {
service.Error(c, errors.New("数据分析余量不足"))
return
}
addBalanceReq.DataAnalysisConsumptionNumber = 1
resp, err := service.BundleProvider.AddBundleBalance(c, &addBalanceReq)
if err != nil {
service.Error(c, errors.New(common.AddBundleBalanceFailed))
return
}
res, err := service.CastProvider.UpdateWorkAnalysisStatus(c, &cast.UpdateWorkAnalysisStatusReq{
WorkAction: cast.WorkActionENUM_CONFIRM,
Uuid: req.Uuid,
ConfirmRemark: req.ConfirmRemark,
CostType: resp.UsedType,
ConfirmStatus: 1,
})
if err != nil {
service.Error(c, errors.New(common.UpdateWorkStatusFailed))
return
}
// 如果是艺人手动确认,确认操作后,自动标记为待阅读状态
_, err = service.CastProvider.UpdateWorkAnalysisStatus(c, &cast.UpdateWorkAnalysisStatusReq{
WorkAction: cast.WorkActionENUM_READ,
Uuid: req.Uuid,
})
if err != nil {
service.Error(c, errors.New(common.UpdateWorkStatusFailed))
return
}
service.Success(c, res)
}
func CastLogConfirm(ctx *gin.Context) { func CastLogConfirm(ctx *gin.Context) {
var req bundle.ConfirmWorkReq var req bundle.ConfirmWorkReq
if err := ctx.ShouldBindJSON(&req); err != nil { if err := ctx.ShouldBindJSON(&req); err != nil {

View File

@ -1,639 +0,0 @@
package cast
import (
"context"
"errors"
"fmt"
"fonchain-fiee/api/bundle"
"fonchain-fiee/api/cast"
"fonchain-fiee/pkg/cache"
"fonchain-fiee/pkg/e"
modelCast "fonchain-fiee/pkg/model/cast"
"fonchain-fiee/pkg/model/login"
"fonchain-fiee/pkg/service"
"fonchain-fiee/pkg/utils"
"strconv"
"time"
"dubbo.apache.org/dubbo-go/v3/common/constant"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
// CreateWorkAnalysis 创建作品分析
func CreateWorkAnalysis(ctx *gin.Context) {
var req *cast.CreateWorkAnalysisReq
var err error
if err = ctx.ShouldBind(&req); err != nil {
service.Error(ctx, err)
return
}
newCtx := NewCtxWithUserInfo(ctx)
artistID, _ := strconv.ParseUint(req.ArtistID, 10, 64)
if err = CheckUserBundleBalance(int32(artistID), modelCast.BalanceTypeDataValue); err != nil {
service.Error(ctx, err)
return
}
resp, err := service.CastProvider.CreateWorkAnalysis(newCtx, req)
if err != nil {
service.Error(ctx, err)
return
}
service.Success(ctx, resp)
return
}
// UpdateWorkAnalysis 更新作品分析
func UpdateWorkAnalysis(ctx *gin.Context) {
var req *cast.UpdateWorkAnalysisReq
var err error
if err = ctx.ShouldBind(&req); err != nil {
service.Error(ctx, err)
return
}
newCtx := NewCtxWithUserInfo(ctx)
_, err = service.CastProvider.UpdateWorkAnalysis(newCtx, req)
if err != nil {
service.Error(ctx, err)
return
}
service.Success(ctx, nil)
return
}
// UpdateWorkAnalysisStatus 更新作品分析状态
func UpdateWorkAnalysisStatus(ctx *gin.Context) {
var req *cast.UpdateWorkAnalysisStatusReq
var err error
if err = ctx.ShouldBind(&req); err != nil {
service.Error(ctx, err)
return
}
newCtx := NewCtxWithUserInfo(ctx)
_, err = service.CastProvider.UpdateWorkAnalysisStatus(newCtx, req)
if err != nil {
service.Error(ctx, err)
return
}
service.Success(ctx, nil)
return
}
// GetWorkAnalysis 获取作品分析详情
func GetWorkAnalysis(ctx *gin.Context) {
var req *cast.GetWorkAnalysisDetailReq
var err error
if err = ctx.ShouldBind(&req); err != nil {
service.Error(ctx, err)
return
}
newCtx := NewCtxWithUserInfo(ctx)
resp, err := service.CastProvider.GetWorkAnalysis(newCtx, req)
if err != nil {
service.Error(ctx, err)
return
}
service.Success(ctx, resp)
return
}
// GetLatestWorkAnalysis 获取最新作品分析
func GetLatestWorkAnalysis(ctx *gin.Context) {
var req *cast.GetLatestWorkAnalysisReq
var err error
if err = ctx.ShouldBind(&req); err != nil {
service.Error(ctx, err)
return
}
newCtx := NewCtxWithUserInfo(ctx)
resp, err := service.CastProvider.GetLatestWorkAnalysis(newCtx, req)
if err != nil {
service.Error(ctx, err)
return
}
service.Success(ctx, resp)
return
}
// ListWorkAnalysis 获取作品分析列表
func ListWorkAnalysis(ctx *gin.Context) {
var req *cast.ListWorkAnalysisReq
var err error
if err = ctx.ShouldBind(&req); err != nil {
service.Error(ctx, err)
return
}
newCtx := NewCtxWithUserInfo(ctx)
resp, err := service.CastProvider.ListWorkAnalysis(newCtx, req)
if err != nil {
service.Error(ctx, err)
return
}
RefreshWorkAnalysisApproval(ctx, resp.Data)
service.Success(ctx, resp)
return
}
// RefreshWorkAnalysisApproval 刷新作品分析审批状态
func RefreshWorkAnalysisApproval(ctx *gin.Context, data []*cast.WorkAnalysisInfo) {
if len(data) > 0 {
var analysisUuidApprovalIDMap = make(map[int]string)
for _, v := range data {
// 状态为2表示待审批需要获取详情来获取ApprovalID
if v.WorkAnalysisStatus == 2 && v.ApprovalID != "" {
zap.L().Info("RefreshWorkAnalysisApproval", zap.Any("approvalID", v.ApprovalID))
approvalID, _ := strconv.ParseUint(v.ApprovalID, 10, 64)
analysisUuidApprovalIDMap[int(approvalID)] = v.Uuid
}
}
if len(analysisUuidApprovalIDMap) > 0 {
_ = RefreshWorkAnalysisApprovalStatus(ctx, analysisUuidApprovalIDMap)
}
}
}
// RefreshWorkAnalysisApprovalStatus 刷新作品分析审批状态详情
func RefreshWorkAnalysisApprovalStatus(ctx *gin.Context, approvalIDAnalysisUuidMap map[int]string) (err error) {
var castS = new(CastService)
var data = make(map[int]modelCast.Item)
var approvalIDs []int
for approvalId := range approvalIDAnalysisUuidMap {
approvalIDs = append(approvalIDs, approvalId)
}
if len(approvalIDs) == 0 {
return
}
data, err = castS.ApprovalDetail(approvalIDs)
if err != nil {
return
}
// status: 1待审批 2审批通过 3审批不通过 6撤销发其中 7撤销完成
var newData = make(map[int]modelCast.Item, len(approvalIDs))
for _, v := range approvalIDs {
newData[v] = data[v]
}
newCtx := NewCtxWithUserInfo(ctx)
if len(newData) > 0 {
for approvalId, v := range newData {
if v.ID == 0 {
_, _ = service.CastProvider.UpdateWorkAnalysisStatus(newCtx, &cast.UpdateWorkAnalysisStatusReq{
WorkAction: cast.WorkActionENUM_APPROVAL_DELETE,
Uuid: approvalIDAnalysisUuidMap[approvalId],
ApprovalID: fmt.Sprint(approvalId),
ApprovalReply: "",
})
continue
}
var workAction cast.WorkActionENUM
if v.Status == 2 {
workAction = cast.WorkActionENUM_APPROVAL_PASS
} else if v.Status == 3 {
workAction = cast.WorkActionENUM_APPROVAL_REJECT
} else {
continue
}
_, _ = service.CastProvider.UpdateWorkAnalysisStatus(newCtx, &cast.UpdateWorkAnalysisStatusReq{
WorkAction: workAction,
Uuid: approvalIDAnalysisUuidMap[approvalId],
ApprovalID: fmt.Sprint(approvalId),
ApprovalReply: v.Reply,
})
}
}
return
}
// DeleteWorkAnalysis 删除作品分析
func DeleteWorkAnalysis(ctx *gin.Context) {
var req *cast.DeleteWorkAnalysisReq
var err error
if err = ctx.ShouldBind(&req); err != nil {
service.Error(ctx, err)
return
}
newCtx := NewCtxWithUserInfo(ctx)
_, err = service.CastProvider.DeleteWorkAnalysis(newCtx, req)
if err != nil {
service.Error(ctx, err)
return
}
service.Success(ctx, nil)
return
}
// getWorkAnalysisStatusText 获取作品分析状态文本
func getWorkAnalysisStatusText(status uint32) string {
switch status {
case 1:
return "待提交"
case 2:
return "审批中"
case 3:
return "审批驳回"
case 4:
return "待艺人验收"
case 5:
return "验收失败"
case 6:
return "待阅读"
case 7:
return "已阅读"
default:
return ""
}
}
// getComfirmTypeText 获取确认类型文本
func getComfirmTypeText(comfirmType int32) string {
switch comfirmType {
case 1:
return "艺人确认"
case 2:
return "系统确认"
default:
return ""
}
}
// ListWorkAnalysisExport 获取作品分析列表并导出Excel
func ListWorkAnalysisExport(ctx *gin.Context) {
var req *cast.ListWorkAnalysisReq
var err error
if err = ctx.ShouldBind(&req); err != nil {
service.Error(ctx, err)
return
}
req.Page = 1
req.PageSize = 999999999
newCtx := NewCtxWithUserInfo(ctx)
resp, err := service.CastProvider.ListWorkAnalysis(newCtx, req)
if err != nil {
service.Error(ctx, err)
return
}
titleList := []string{
"艺人", "用户编号", "手机号", "状态", "验收确认类型", "原因", "状态变更时间", "提交时间", "操作人", "pdf链接",
}
var dataList []interface{}
for _, i := range resp.Data {
data := []any{
i.ArtistName,
i.SubNum,
i.ArtistPhone,
getWorkAnalysisStatusText(i.WorkAnalysisStatus),
getComfirmTypeText(i.ComfirmType),
i.Reason,
i.StatusUpdateTime,
i.SubmitTime,
i.OperatorName,
i.PdfUrl,
}
dataList = append(dataList, &data)
}
content, err := utils.ToExcelByType(titleList, dataList, "slice", "")
if err != nil {
service.Error(ctx, err)
return
}
utils.ResponseXls(ctx, content, "数据分析列表")
return
}
// ListWorkAnalysisSingleExport 获取作品分析列表并导出Excel
func ListWorkAnalysisSingleExport(ctx *gin.Context) {
var req *cast.ListWorkAnalysisReq
var err error
if err = ctx.ShouldBind(&req); err != nil {
service.Error(ctx, err)
return
}
req.Page = 1
req.PageSize = 999999999
newCtx := NewCtxWithUserInfo(ctx)
resp, err := service.CastProvider.ListWorkAnalysis(newCtx, req)
if err != nil {
service.Error(ctx, err)
return
}
titleList := []string{
"状态", "验收确认类型", "原因", "状态变更时间", "操作人", "提交时间", "pdf链接",
}
var dataList []interface{}
for _, i := range resp.Data {
data := []any{
getWorkAnalysisStatusText(i.WorkAnalysisStatus),
getComfirmTypeText(i.ComfirmType),
i.Reason,
i.StatusUpdateTime,
i.OperatorName,
i.SubmitTime,
i.PdfUrl,
}
dataList = append(dataList, &data)
}
content, err := utils.ToExcelByType(titleList, dataList, "slice", "")
if err != nil {
service.Error(ctx, err)
return
}
utils.ResponseXls(ctx, content, "数据分析列表")
return
}
// ArtistDataList 艺人数据列表
func ArtistDataList(ctx *gin.Context) {
var req *cast.ArtistDataListReq
var err error
if err = ctx.ShouldBind(&req); err != nil {
service.Error(ctx, err)
return
}
newCtx := NewCtxWithUserInfo(ctx)
resp, err := service.CastProvider.ArtistDataList(newCtx, req)
if err != nil {
service.Error(ctx, err)
return
}
service.Success(ctx, resp)
return
}
// MediaDataList 自媒体数据列表
func MediaDataList(ctx *gin.Context) {
var req *cast.MediaDataListReq
var err error
if err = ctx.ShouldBind(&req); err != nil {
service.Error(ctx, err)
return
}
newCtx := NewCtxWithUserInfo(ctx)
resp, err := service.CastProvider.MediaDataList(newCtx, req)
if err != nil {
service.Error(ctx, err)
return
}
service.Success(ctx, resp)
return
}
// DataOverview 数据概览
func DataOverview(ctx *gin.Context) {
var req *cast.DataOverviewReq
var err error
if err = ctx.ShouldBind(&req); err != nil {
service.Error(ctx, err)
return
}
newCtx := NewCtxWithUserInfo(ctx)
resp, err := service.CastProvider.DataOverview(newCtx, req)
if err != nil {
service.Error(ctx, err)
return
}
service.Success(ctx, resp)
return
}
// ArtistMetricsSeries 艺人指标系列
func ArtistMetricsSeries(ctx *gin.Context) {
var req *cast.ArtistMetricsSeriesReq
var err error
if err = ctx.ShouldBind(&req); err != nil {
service.Error(ctx, err)
return
}
newCtx := NewCtxWithUserInfo(ctx)
resp, err := service.CastProvider.ArtistMetricsSeries(newCtx, req)
if err != nil {
service.Error(ctx, err)
return
}
service.Success(ctx, resp)
return
}
// ArtistMetricsDailyWindow 艺人指标日窗口
func ArtistMetricsDailyWindow(ctx *gin.Context) {
var req *cast.ArtistMetricsDailyWindowReq
var err error
if err = ctx.ShouldBind(&req); err != nil {
service.Error(ctx, err)
return
}
newCtx := NewCtxWithUserInfo(ctx)
resp, err := service.CastProvider.ArtistMetricsDailyWindow(newCtx, req)
if err != nil {
service.Error(ctx, err)
return
}
service.Success(ctx, resp)
return
}
// TobeConfirmedList 待确认数据列表
func TobeConfirmedList(ctx *gin.Context) {
var req *cast.TobeConfirmedListReq
var err error
if err = ctx.ShouldBind(&req); err != nil {
service.Error(ctx, err)
return
}
userInfo := login.GetUserInfoFromC(ctx)
req.ArtistUuid = strconv.Itoa(int(userInfo.ID))
newCtx := NewCtxWithUserInfo(ctx)
resp, err := service.CastProvider.TobeConfirmedList(newCtx, req)
if err != nil {
service.Error(ctx, err)
return
}
service.Success(ctx, resp)
return
}
// UpdateWorkAnalysisApprovalID 更新作品分析审批ID
func UpdateWorkAnalysisApprovalID(ctx *gin.Context) {
var req *cast.UpdateWorkAnalysisApprovalIDReq
var err error
if err = ctx.ShouldBind(&req); err != nil {
service.Error(ctx, err)
return
}
newCtx := NewCtxWithUserInfo(ctx)
_, err = service.CastProvider.UpdateWorkAnalysisApprovalID(newCtx, req)
if err != nil {
service.Error(ctx, err)
return
}
service.Success(ctx, nil)
return
}
type CheckBundleBalanceReq struct {
ArtistID string `protobuf:"bytes,4,opt,name=artistID,proto3" json:"artistID"` // 艺人ID
BalanceType modelCast.BalanceTypeEnum `json:"balanceType"` // 套餐类型
}
// CheckBundleBalance 检查套餐余量
func CheckBundleBalance(ctx *gin.Context) {
var req *CheckBundleBalanceReq
var err error
if err = ctx.ShouldBind(&req); err != nil {
service.Error(ctx, err)
return
}
artistID, err := strconv.ParseUint(req.ArtistID, 10, 64)
if err != nil {
service.Error(ctx, err)
return
}
zap.L().Info("CheckUserBundleBalance", zap.Any("userID", artistID), zap.Any("balanceType", req.BalanceType))
newCtx := NewCtxWithUserInfo(ctx)
resp, err := service.BundleProvider.GetBundleBalanceByUserId(newCtx, &bundle.GetBundleBalanceByUserIdReq{UserId: int32(artistID)})
if err != nil {
zap.L().Error("CheckUserBundleBalance", zap.Any("err", err))
service.Error(ctx, err)
return
}
zap.L().Info("CheckUserBundleBalance", zap.Any("resp", resp))
switch req.BalanceType {
case modelCast.BalanceTypeAccountValue:
if resp.AccountNumber-resp.AccountConsumptionNumber <= 0 {
err = errors.New(e.ErrorBalanceInsufficient)
service.Error(ctx, err)
return
}
case modelCast.BalanceTypeImageValue:
if resp.ImageNumber-resp.ImageConsumptionNumber <= 0 {
err = errors.New(e.ErrorBalanceInsufficient)
service.Error(ctx, err)
return
}
case modelCast.BalanceTypeVideoValue:
if resp.VideoNumber-resp.VideoConsumptionNumber <= 0 {
err = errors.New(e.ErrorBalanceInsufficient)
service.Error(ctx, err)
return
}
case modelCast.BalanceTypeDataValue:
if resp.DataAnalysisNumber-resp.DataAnalysisConsumptionNumber <= 0 {
err = errors.New(e.ErrorBalanceInsufficient)
service.Error(ctx, err)
return
}
}
service.Success(ctx, resp)
return
}
// ProcessAnalysisTask 处理数据分析自动确认任务
func ProcessAnalysisTask(ctx context.Context, analysisUuid string) {
lockKey := fmt.Sprintf(modelCast.AutoConfirmAnalysisLockKey, analysisUuid)
reply := cache.RedisClient.SetNX(lockKey, "1", 5*time.Minute)
if !reply.Val() {
zap.L().Warn("数据分析任务正在被其他实例处理", zap.String("analysisUuid", analysisUuid))
return
}
defer func() {
cache.RedisClient.Del(lockKey)
}()
err := autoConfirmAnalysis(ctx, analysisUuid)
if err != nil {
zap.L().Error("数据分析自动确认失败",
zap.String("analysisUuid", analysisUuid),
zap.Error(err))
return
}
// 从队列中移除
args := make([]interface{}, len(analysisUuid))
for i, m := range analysisUuid {
args[i] = m
}
err = cache.RedisClient.ZRem(modelCast.AutoConfirmAnalysisQueueKey, args...).Err()
if err != nil {
zap.L().Error("从队列移除数据分析任务失败",
zap.String("analysisUuid", analysisUuid),
zap.Error(err))
}
zap.L().Info("数据分析自动确认成功", zap.String("analysisUuid", analysisUuid))
}
// autoConfirmAnalysis 自动确认数据分析
func autoConfirmAnalysis(ctx context.Context, analysisUuid string) (err error) {
var confirmRemark string
var isFailed bool
var usedType uint32
infoResp, err := service.CastProvider.GetWorkAnalysis(context.Background(), &cast.GetWorkAnalysisDetailReq{
Uuid: analysisUuid,
})
if err != nil {
zap.L().Error("autoConfirmAnalysis GetWorkAnalysis", zap.Any("err", err))
return
}
if infoResp.WorkAnalysisStatus != 4 { // 4是待确认状态需要根据实际情况调整
return
}
userID, _ := strconv.ParseInt(infoResp.ArtistID, 10, 64)
balanceInfoRes, err := service.BundleProvider.GetBundleBalanceByUserId(context.Background(), &bundle.GetBundleBalanceByUserIdReq{
UserId: int32(userID),
})
if err != nil {
zap.L().Error("autoConfirmAnalysis GetBundleBalanceByUserId", zap.Any("err", err))
confirmRemark = "获取余额失败:" + err.Error()
isFailed = true
}
var addBalanceReq bundle.AddBundleBalanceReq
addBalanceReq.UserId = int32(userID)
// 检查数据分析余量
if balanceInfoRes.DataAnalysisConsumptionNumber >= balanceInfoRes.DataAnalysisNumber {
confirmRemark = "数据分析余量不足"
isFailed = true
}
addBalanceReq.DataAnalysisConsumptionNumber = 1
zap.L().Info("autoConfirmAnalysis AddBundleBalanceReq", zap.Any("addBalanceReq", &addBalanceReq))
resp, err := service.BundleProvider.AddBundleBalance(context.Background(), &addBalanceReq)
if err != nil {
zap.L().Error("autoConfirmAnalysis AddBundleBalance", zap.Any("err", err))
confirmRemark = "扣除失败:" + err.Error()
isFailed = true
}
zap.L().Info("autoConfirmAnalysis AddBundleBalanceResp", zap.Any("resp", resp))
var confirmStatus uint32 = 1
if isFailed {
usedType = 0
confirmStatus = 3
} else {
usedType = resp.UsedType
confirmRemark = "系统自动确认"
confirmStatus = 1
}
var mm = make(map[string]interface{}, 3)
mm["userid"] = 0
mm["name"] = "系统自动确定"
mm["phone"] = ""
newCtx := context.WithValue(context.Background(), constant.DubboCtxKey("attachment"), mm)
_, err = service.CastProvider.UpdateWorkAnalysisStatus(newCtx, &cast.UpdateWorkAnalysisStatusReq{
WorkAction: cast.WorkActionENUM_CONFIRM,
Uuid: analysisUuid,
ConfirmRemark: confirmRemark,
CostType: usedType,
ConfirmStatus: confirmStatus,
})
if err != nil {
return
}
return
}

View File

@ -2,9 +2,11 @@ package cast
import ( import (
"context" "context"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"fonchain-fiee/api/accountFiee" "fonchain-fiee/api/accountFiee"
"fonchain-fiee/api/aryshare"
"fonchain-fiee/api/bundle" "fonchain-fiee/api/bundle"
"fonchain-fiee/api/cast" "fonchain-fiee/api/cast"
"fonchain-fiee/cmd/config" "fonchain-fiee/cmd/config"
@ -17,7 +19,6 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"go.uber.org/zap" "go.uber.org/zap"
"google.golang.org/protobuf/types/known/emptypb"
) )
func MediaUserList(ctx *gin.Context) { func MediaUserList(ctx *gin.Context) {
@ -36,6 +37,11 @@ func MediaUserList(ctx *gin.Context) {
service.Error(ctx, err) service.Error(ctx, err)
return return
} }
go func() {
for _, v := range resp.Data {
_ = SyncAsAuth(v.ArtistUuid)
}
}()
service.Success(ctx, resp) service.Success(ctx, resp)
return return
} }
@ -106,23 +112,40 @@ func UpdateMediaAccount(ctx *gin.Context) {
return return
} }
// 查询艺人的信息 // 查询艺人的信息
// 字符串转整型 userResp, err := service.CastProvider.MediaUserList(context.Background(), &cast.MediaUserListReq{
artistID, err := strconv.ParseUint(req.ArtistUuid, 10, 64) ArtistUuid: req.ArtistUuid,
if config.AppConfig.System.AppMode != "dev" { Page: 1,
infoResp, err = service.AccountFieeProvider.Info(context.Background(), &accountFiee.InfoRequest{ PageSize: 10,
ID: artistID,
Domain: "app",
}) })
if err != nil { if err != nil {
service.Error(ctx, err) service.Error(ctx, err)
return return
} }
} else { if userResp != nil && len(userResp.Data) > 0 {
infoResp = &accountFiee.UserInfoResponse{ for _, v := range userResp.Data {
Name: "小波", if v.PlatformID == uint32(req.PlatformID) {
TelNum: "18288888888", service.Error(ctx, errors.New("账号已存在"))
return
} }
} }
}
// 字符串转整型
artistID, err := strconv.ParseUint(req.ArtistUuid, 10, 64)
infoResp, err = GetArtistAccountInfo(artistID)
if err != nil {
service.Error(ctx, err)
return
}
if infoResp.SubNum == "" {
service.Error(ctx, errors.New("用户不存在"))
return
}
//TODO 判断是否注册ay
if err = CheckAsProfile(infoResp); err != nil {
service.Error(ctx, err)
return
}
req.ArtistName = infoResp.Name req.ArtistName = infoResp.Name
req.ArtistPhone = infoResp.TelNum req.ArtistPhone = infoResp.TelNum
req.ArtistPhoneAreaCode = infoResp.TelAreaCode req.ArtistPhoneAreaCode = infoResp.TelAreaCode
@ -166,8 +189,8 @@ func UpdateMediaAccount(ctx *gin.Context) {
// 账号授权 // 账号授权
func OAuthAccount(ctx *gin.Context) { func OAuthAccount(ctx *gin.Context) {
var req *cast.OAuthAccountReq var req *cast.OAuthAccountV2Req
var resp *cast.OAuthAccountResp var resp *cast.OAuthAccountV2Resp
var err error var err error
if err = ctx.ShouldBind(&req); err != nil { if err = ctx.ShouldBind(&req); err != nil {
service.Error(ctx, err) service.Error(ctx, err)
@ -177,11 +200,55 @@ func OAuthAccount(ctx *gin.Context) {
service.Error(ctx, err) service.Error(ctx, err)
return return
} }
if resp, err = service.CastProvider.OAuthAccount(ctx, req); err != nil { mediaResp, err := service.CastProvider.MediaUserList(ctx, &cast.MediaUserListReq{
MediaUserID: req.MediaAccountUuid,
Page: 1,
PageSize: 1,
})
if err != nil {
service.Error(ctx, err) service.Error(ctx, err)
return return
} }
service.Success(ctx, resp) if mediaResp == nil || len(mediaResp.Data) == 0 {
service.Error(ctx, errors.New("未找到该账号"))
return
}
if err = SyncAsAuth(mediaResp.Data[0].ArtistUuid); err != nil {
service.Error(ctx, err)
return
}
if resp, err = service.CastProvider.OAuthAccountV2(ctx, req); err != nil {
service.Error(ctx, err)
return
}
if cast.PlatformIDENUM_DM == cast.PlatformIDENUM(resp.PlatformID) {
service.Success(ctx, map[string]interface{}{
"url": resp.AuthUrl,
})
return
}
if resp.ProfileKey == "" {
service.Error(ctx, errors.New("艺人未添加平台账号"))
return
}
jwtReq := &aryshare.GenerateJWTRequest{
Domain: "",
PrivateKey: "",
ProfileKey: resp.ProfileKey,
Logout: true,
Redirect: fmt.Sprintf("%s?artistUuid=%s&platformID=%d", config.AppConfig.System.AuthCallback, resp.ArtistUuid, resp.PlatformID),
AllowedSocial: []string{modelCast.PlatformNameKv[resp.PlatformID]},
Verify: false,
Base64: false,
ExpiresIn: 0,
Email: nil,
}
jwtResp, err := service.AyrshareProvider.GenerateJWT(context.Background(), jwtReq)
if err != nil {
service.Error(ctx, err)
return
}
service.Success(ctx, jwtResp)
return return
} }
@ -230,6 +297,89 @@ func OAuth2Callback(ctx *gin.Context) {
return return
} }
func AsOAuth2Callback(ctx *gin.Context) {
var (
//platformIds string
//userID string
)
artistUuid := ctx.Query("artistUuid")
//platformIDs := ctx.Query("platformID")
//platformID, _ := strconv.ParseInt(platformIDs, 10, 64)
// 刷新授权
var err error
var req modelCast.OAuthPlatformReq
if err = ctx.ShouldBind(&req); err != nil {
//service.Error(ctx, err)
ctx.Redirect(http.StatusFound, fmt.Sprintf("%s?status=%d&message=%s", config.AppConfig.System.AuthRedirectUrl, 1, "参数错误"))
return
}
err = SyncAsAuth(artistUuid)
if err != nil {
ctx.Redirect(http.StatusFound, fmt.Sprintf("%s?status=%d&message=%s", config.AppConfig.System.AuthRedirectUrl, 1, err.Error()))
return
}
ctx.Redirect(http.StatusFound, fmt.Sprintf("%s?status=%d&message=%s", config.AppConfig.System.AuthRedirectUrl, 0, ""))
return
}
func SyncAsAuth(artistUuid string) error {
resp, err := service.CastProvider.GetArtist(context.Background(), &cast.GetArtistReq{ArtistUuid: artistUuid})
if err != nil {
return errors.New("获取艺人信息错误")
}
if resp == nil || resp.ArtistInfo == nil || resp.ArtistInfo.ProfileKey == "" {
return errors.New("艺人未注册")
}
userResp, err := service.AyrshareProvider.GetUser(context.Background(), &aryshare.GetUserRequest{
ProfileKey: resp.ArtistInfo.ProfileKey,
InstagramDetails: true,
})
if err != nil {
return errors.New("获取艺人绑定信息错误")
}
var authReq *cast.UpdateOAuthReq
authReq = &cast.UpdateOAuthReq{Data: make([]*cast.UpdateOAuthReq_Info, 0)}
if len(userResp.DisplayNames) == 0 {
//return errors.New("没有授权信息")
authReq.Data = append(authReq.Data, &cast.UpdateOAuthReq_Info{
ArtistUuid: artistUuid,
PlatformID: cast.PlatformIDENUM_UNKNOWN,
AsID: "",
PlatformUserName: "",
AutInfo: "",
})
_, err = service.CastProvider.UpdateOAuth(context.Background(), authReq)
if err != nil {
return errors.New("同步授权信息失败")
}
return nil
}
var asInfoB []byte
for _, v := range userResp.DisplayNames {
asInfoB, _ = json.Marshal(v)
platformIDENUM := cast.PlatformIDENUM(modelCast.NamePlatformIDKv[v.Platform])
if platformIDENUM == cast.PlatformIDENUM_UNKNOWN {
continue
}
authReq.Data = append(authReq.Data, &cast.UpdateOAuthReq_Info{
ArtistUuid: artistUuid,
PlatformID: platformIDENUM,
AsID: v.Id,
PlatformUserName: v.Username,
AutInfo: string(asInfoB),
})
}
if len(authReq.Data) != 0 {
_, err = service.CastProvider.UpdateOAuth(context.Background(), authReq)
if err != nil {
return errors.New("同步授权信息失败")
}
}
return nil
}
func RefreshToken(ctx *gin.Context) { func RefreshToken(ctx *gin.Context) {
var req *cast.RefreshTokenReq var req *cast.RefreshTokenReq
var resp *cast.RefreshTokenResp var resp *cast.RefreshTokenResp
@ -270,7 +420,71 @@ func ArtistInfo(ctx *gin.Context) {
return return
} }
func Test(ctx *gin.Context) { func CheckAsProfile(infoResp *accountFiee.UserInfoResponse) error {
service.CastProvider.Test(ctx, &emptypb.Empty{}) var asArtistResp *cast.GetArtistResp
var err error
title := fmt.Sprintf("%s_%s", config.AppConfig.System.AppMode, infoResp.SubNum)
// 查询艺人的信息
if asArtistResp, err = service.CastProvider.GetArtist(context.Background(), &cast.GetArtistReq{
ArtistUuid: fmt.Sprint(infoResp.Id),
}); err != nil {
return err
}
if asArtistResp != nil && asArtistResp.ArtistInfo != nil && asArtistResp.ArtistInfo.ProfileKey != "" {
return nil
}
createProfileResp, err := service.AyrshareProvider.CreateProfile(context.Background(), &aryshare.CreateProfileRequest{
Title: title,
MessagingActive: true,
HideTopHeader: false,
TopHeader: "",
DisableSocial: nil,
Team: false,
Email: "",
SubHeader: "",
Tags: []string{config.AppConfig.System.AppMode},
})
if err != nil {
zap.L().Error("CreateProfile error", zap.Error(err))
err = errors.New("创建平台艺人账号失败")
return err
}
zap.L().Info("CreateProfile success", zap.Any("createProfileResp", createProfileResp), zap.Any("title", title))
_, err = service.CastProvider.UpdateArtist(context.Background(), &cast.UpdateArtistReq{
Uuid: asArtistResp.Uuid,
ArtistInfo: &cast.ArtistInfo{
ArtistUuid: fmt.Sprint(infoResp.Id),
RefID: createProfileResp.RefId,
ProfileKey: createProfileResp.ProfileKey,
SubNum: infoResp.SubNum,
},
})
if err != nil {
return err
}
return nil
}
func SyncAsProfile(ctx *gin.Context) {
var req *modelCast.SyncAsProfileReq
var err error
if err = ctx.ShouldBind(&req); err != nil {
service.Error(ctx, err)
return
}
infoResp, err := GetArtistAccountInfo(req.ID)
if err != nil {
service.Error(ctx, err)
return
}
if infoResp.SubNum == "" {
service.Error(ctx, errors.New("用户不存在"))
return
}
if err = CheckAsProfile(infoResp); err != nil {
service.Error(ctx, err)
return
}
service.Success(ctx, infoResp)
return return
} }

View File

@ -191,13 +191,14 @@ func ImportBatch(ctx *gin.Context) {
req.Data = append(req.Data, &temp) req.Data = append(req.Data, &temp)
continue continue
} }
subInfoResp, _err := service.AccountFieeProvider.SubNumGetInfo(context.Background(), &accountFiee.SubNumGetInfoRequest{ var subInfoResp *accountFiee.UserInfoResponse
subInfoResp, err = service.AccountFieeProvider.SubNumGetInfo(context.Background(), &accountFiee.SubNumGetInfoRequest{
SubNum: row[2], SubNum: row[2],
Domain: "app", Domain: "app",
}) })
if _err != nil { if err != nil {
zap.L().Error("AccountFieeProvider.SubNumGetInfo", zap.Error(_err)) zap.L().Error("AccountFieeProvider.SubNumGetInfo", zap.Error(err))
temp.Remark = _err.Error() temp.Remark = err.Error()
} else { } else {
temp.ArtistUuid = fmt.Sprint(subInfoResp.Id) temp.ArtistUuid = fmt.Sprint(subInfoResp.Id)
temp.ArtistName = subInfoResp.Name temp.ArtistName = subInfoResp.Name

104
pkg/service/cast/test.go Normal file
View File

@ -0,0 +1,104 @@
package cast
import (
"context"
"fmt"
"fonchain-fiee/api/aryshare"
"fonchain-fiee/api/cast"
"fonchain-fiee/cmd/config"
"fonchain-fiee/pkg/service"
"fonchain-fiee/pkg/utils"
"github.com/gin-gonic/gin"
)
func Test(ctx *gin.Context) {
action := ctx.PostForm("action")
if action == "getPost" {
id := ctx.PostForm("id")
profileKey := ctx.PostForm("profileKey")
resp, err := service.AyrshareProvider.GetPost(context.Background(), &aryshare.GetPostRequest{
Id: id,
ProfileKey: profileKey,
})
if err != nil {
service.Error(ctx, err)
return
}
service.Success(ctx, resp)
return
}
if action == "refreshPost" {
err := RefreshPublish()
if err != nil {
service.Error(ctx, err)
return
}
service.Success(ctx, "刷新成功")
return
}
if action == "updateStatus" {
resp, err := service.CastProvider.UpdateStatus(context.Background(), &cast.UpdateStatusReq{
WorkAction: cast.WorkActionENUM_CONFIRM,
WorkUuid: "00059232-3696-4c3e-a960-125955f2d881",
ApprovalID: "",
ConfirmRemark: "",
ConfirmStatus: 1,
ApprovalReply: "",
AutoPublish: 0,
CostType: 1,
})
if err != nil {
service.Error(ctx, err)
return
}
service.Success(ctx, resp)
return
}
if action == "dmGet" {
mediaUuid := ctx.PostForm("mediaUuid")
token := ctx.PostForm("token")
_, dmData, err := utils.GetUrl(fmt.Sprintf("https://api.dailymotion.com/video/%s?fields=id,title,url,published,private", mediaUuid),
map[string]string{"Authorization": "Bearer " + token}, config.AppConfig.System.ProxyUrl)
if err != nil {
service.Error(ctx, err)
return
}
service.Success(ctx, dmData)
return
}
if action == "" {
profileKey := ctx.PostForm("profileKey")
resp, err := service.AyrshareProvider.GetUser(context.Background(), &aryshare.GetUserRequest{
ProfileKey: profileKey,
InstagramDetails: true,
})
if err != nil {
service.Error(ctx, err)
return
}
service.Success(ctx, resp)
return
}
if action == "getProfile" {
//profileKey := ctx.PostForm("profileKey")
resp, err := service.AyrshareProvider.GetProfiles(context.Background(), &aryshare.GetProfilesRequest{
Title: "",
RefId: "",
HasActiveSocialAccounts: false,
IncludesActiveSocialAccounts: nil,
ActionLog: nil,
Limit: 0,
Cursor: "",
})
if err != nil {
service.Error(ctx, err)
return
}
service.Success(ctx, resp)
return
}
service.Success(ctx, "unknow")
return
}

View File

@ -3,8 +3,10 @@ package cast
import ( import (
"context" "context"
"fmt" "fmt"
"fonchain-fiee/api/accountFiee"
"fonchain-fiee/cmd/config" "fonchain-fiee/cmd/config"
"fonchain-fiee/pkg/model/login" "fonchain-fiee/pkg/model/login"
"fonchain-fiee/pkg/service"
"dubbo.apache.org/dubbo-go/v3/common/constant" "dubbo.apache.org/dubbo-go/v3/common/constant"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@ -38,3 +40,25 @@ func NewCtxWithUserInfo(ctx *gin.Context) (newCtx context.Context) {
newCtx = context.WithValue(context.Background(), constant.DubboCtxKey("attachment"), mm) newCtx = context.WithValue(context.Background(), constant.DubboCtxKey("attachment"), mm)
return return
} }
func GetArtistAccountInfo(artistID uint64) (*accountFiee.UserInfoResponse, error) {
var infoResp *accountFiee.UserInfoResponse
var err error
if config.AppConfig.System.AppMode != "dev" {
infoResp, err = service.AccountFieeProvider.Info(context.Background(), &accountFiee.InfoRequest{
ID: artistID,
Domain: "app",
})
if err != nil {
return nil, err
}
} else {
infoResp = &accountFiee.UserInfoResponse{
Id: 1234,
Name: "小波",
TelNum: "18288888888",
SubNum: "T12345",
}
}
return infoResp, nil
}

View File

@ -6,6 +6,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"fonchain-fiee/api/accountFiee" "fonchain-fiee/api/accountFiee"
"fonchain-fiee/api/aryshare"
"fonchain-fiee/api/bundle" "fonchain-fiee/api/bundle"
"fonchain-fiee/api/cast" "fonchain-fiee/api/cast"
"fonchain-fiee/cmd/config" "fonchain-fiee/cmd/config"
@ -15,13 +16,17 @@ import (
modelCast "fonchain-fiee/pkg/model/cast" modelCast "fonchain-fiee/pkg/model/cast"
"fonchain-fiee/pkg/model/login" "fonchain-fiee/pkg/model/login"
"fonchain-fiee/pkg/service" "fonchain-fiee/pkg/service"
"fonchain-fiee/pkg/service/check"
"fonchain-fiee/pkg/utils"
"fonchain-fiee/pkg/utils/stime" "fonchain-fiee/pkg/utils/stime"
"io" "io"
"path/filepath"
"strconv" "strconv"
"time" "time"
"dubbo.apache.org/dubbo-go/v3/common/constant" "dubbo.apache.org/dubbo-go/v3/common/constant"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/xuri/excelize/v2"
"go.uber.org/zap" "go.uber.org/zap"
) )
@ -273,7 +278,6 @@ func CheckUserBundleBalance(userID int32, balanceType modelCast.BalanceTypeEnum)
func Publish(ctx *gin.Context) { func Publish(ctx *gin.Context) {
var ( var (
req *cast.PublishReq req *cast.PublishReq
resp *cast.PublishResp
//workInfoResp *cast.WorkInfoResp //workInfoResp *cast.WorkInfoResp
) )
var err error var err error
@ -282,12 +286,220 @@ func Publish(ctx *gin.Context) {
return return
} }
newCtx := NewCtxWithUserInfo(ctx) newCtx := NewCtxWithUserInfo(ctx)
resp, err = service.CastProvider.Publish(newCtx, req) if err = PublishWork(newCtx, req); err != nil {
if err != nil {
service.Error(ctx, err) service.Error(ctx, err)
return return
} }
service.Success(ctx, resp) service.Success(ctx, req)
return
}
// PublishWork 统一发布
func PublishWork(ctx context.Context, req *cast.PublishReq) error {
//检测所有艺人的账号状态
if len(req.WorkUuids) == 0 {
return errors.New("请选择作品")
}
var err error
var artistUuids []string
for _, workUuid := range req.WorkUuids {
var workInfoResp *cast.WorkInfoResp
workInfoResp, err = service.CastProvider.WorkInfo(ctx, &cast.WorkInfoReq{WorkUuid: workUuid})
if err != nil {
continue
}
artistUuids = append(artistUuids, workInfoResp.ArtistUuid)
}
artistUuids = utils.UniqueT(artistUuids)
for _, artistUuid := range artistUuids {
_ = SyncAsAuth(artistUuid)
}
_, err = service.CastProvider.Publish(ctx, req)
if err != nil {
return err
}
var errs []error
errs = PostAS(req.WorkUuids)
if len(errs) > 0 {
var errMsg string
for _, v := range errs {
errMsg += v.Error() + " "
}
return errors.New(errMsg)
}
return nil
}
func PostAS(workUuids []string) (errs []error) {
for _, workUuid := range workUuids {
workDetail, _err := service.CastProvider.WorkDetail(context.Background(), &cast.WorkDetailReq{
WorkUuid: workUuid,
})
if _err != nil {
errs = append(errs, errors.New("获取作品详情失败"))
zap.L().Error("Publish WorkDetail failed", zap.String("workUuid", workUuid), zap.Error(_err))
continue
}
//if workDetail.WorkStatus != 9 { // 微服务做判断了这边不用判断
// errs = append(errs, fmt.Errorf("作品状态不正确,标题是:%s", workDetail.Title))
// continue
//}
needPlatformIDs := workDetail.NeedPlatformIDs
if len(needPlatformIDs) == 0 {
errs = append(errs, errors.New("没有平台可发布"))
continue
}
var mediaUrls []string
var isVideo bool
if workDetail.WorkCategory == 1 {
isVideo = false
mediaUrls = workDetail.Images
}
if workDetail.WorkCategory == 2 {
isVideo = true
mediaUrls = []string{workDetail.VideoUrl}
}
ArtistInfoResp, _err := service.CastProvider.GetArtist(context.Background(), &cast.GetArtistReq{
ArtistUuid: workDetail.ArtistUuid,
})
if _err != nil {
errs = append(errs, errors.New("获取艺人信息失败"))
continue
}
if ArtistInfoResp == nil || ArtistInfoResp.ArtistInfo == nil || ArtistInfoResp.ArtistInfo.ProfileKey == "" {
errs = append(errs, errors.New("艺人平台信息未配置"))
continue
}
for _, platformID := range needPlatformIDs {
var postResp *aryshare.PostResponse = &aryshare.PostResponse{}
postReq := &aryshare.PostRequest{
Post: workDetail.Title,
Platforms: []string{modelCast.PlatformNameKv[uint32(platformID)]},
MediaUrls: mediaUrls,
IsVideo: isVideo,
ScheduleDate: "",
ValidateScheduled: true,
ShortenLinks: false,
DisableComments: false,
ProfileKey: ArtistInfoResp.ArtistInfo.ProfileKey,
}
switch cast.PlatformIDENUM(platformID) {
case cast.PlatformIDENUM_INS:
postReq.InstagramOptions = &aryshare.InstagramOptions{
ShareReelsFeed: false,
AudioName: "",
ThumbNail: workDetail.CoverUrl,
ThumbNailOffset: 0,
Stories: false,
AltText: nil,
LocationId: "",
UserTags: nil,
Collaborators: nil,
AutoResize: false,
DisableComments: false,
}
case cast.PlatformIDENUM_TIKTOK:
postReq.TikTokOptions = &aryshare.TikTokOptions{
AutoAddMusic: false,
DisableComments: false,
DisableDuet: false,
DisableStitch: false,
Draft: false,
IsAIGenerated: false,
IsBrandedContent: false,
IsBrandOrganic: false,
ImageCoverIndex: int32(workDetail.CoverTimestampMs),
Title: "",
ThumbNailOffset: 0,
ThumbNail: "",
Visibility: "",
}
}
zap.L().Info("Publish Ayrshare PostReq", zap.Any("workUuid", workDetail.WorkUuid), zap.Any("postReq", postReq), zap.Any("workDetail", workDetail))
postResp, _err = service.AyrshareProvider.Post(context.Background(), postReq)
zap.L().Info("Publish Ayrshare postResp", zap.Any("workUuid", workDetail.WorkUuid), zap.Any("postResp", postResp))
if _err != nil {
go func() {
_, _ = service.CastProvider.UpdateWorkPublishLog(context.Background(), &cast.UpdateWorkPublishLogReq{
WorkUuid: workUuid,
PlatformID: cast.PlatformIDENUM(platformID),
Action: "post",
Detail: _err.Error(),
})
}()
infoReq := &cast.UpdateWorkPlatformInfoReq{PlatformInfoData: make([]*cast.PlatformInfo, 0)}
infoReq.PlatformInfoData = append(infoReq.PlatformInfoData, &cast.PlatformInfo{
WorkUuid: workDetail.WorkUuid,
MediaAccountUuid: "",
PlatformID: uint32(platformID),
PublishType: 2,
PublishResp: _err.Error(),
PublishMediaId: "",
PublishStatus: cast.PublishStatusENUM_PublishMediaStatus_EXCEPTION,
Remark: "",
})
_, _ = service.CastProvider.UpdateWorkPlatformInfo(context.Background(), infoReq)
zap.L().Error("PostAs Post err", zap.Error(_err))
errs = append(errs, errors.New(fmt.Sprintf("发布失败,标题是:%s平台是%s", workDetail.Title, modelCast.PlatformNameKv[uint32(platformID)])))
continue
}
go func() {
postBytes, _ := json.Marshal(postResp)
_, _ = service.CastProvider.UpdateWorkPublishLog(context.Background(), &cast.UpdateWorkPublishLogReq{
PlatformID: cast.PlatformIDENUM(platformID),
WorkUuid: workUuid,
Action: "post",
Detail: string(postBytes),
})
}()
zap.L().Info("PostAs", zap.Any("postResp", postResp), zap.Any("workDetail", workDetail))
infoReq := &cast.UpdateWorkPlatformInfoReq{
PlatformInfoData: make([]*cast.PlatformInfo, 0),
}
if postResp == nil || postResp.Posts == nil || len(postResp.Posts) == 0 || postResp.Posts[0].PostIds == nil {
errs = append(errs, fmt.Errorf("标题:%s请求平台失败", workDetail.Title))
continue
}
postIDs := postResp.Posts[0].PostIds
postData, _ := json.Marshal(postResp)
for _, postInfo := range postIDs {
var platformID uint32
switch postInfo.Platform {
case "tiktok":
platformID = 1
case "instagram":
platformID = 3
}
publishStatus := cast.PublishStatusENUM_PublishMediaStatus_NO
if postInfo.Status == "success" {
publishStatus = cast.PublishStatusENUM_PublishMediaStatus_ING
} else {
publishStatus = cast.PublishStatusENUM_PublishMediaStatus_FAIL
}
switch postInfo.Id {
case "pending":
publishStatus = cast.PublishStatusENUM_PublishMediaStatus_ING
}
postBytes, _ := json.Marshal(postInfo)
infoReq.PlatformInfoData = append(infoReq.PlatformInfoData, &cast.PlatformInfo{
WorkUuid: workDetail.WorkUuid,
MediaAccountUuid: "", //FIXME
PlatformID: platformID,
PublishType: 2,
PublishResp: string(postBytes),
PublishMediaId: postResp.Posts[0].Id,
PublishStatus: publishStatus,
Remark: string(postData),
})
}
_, err := service.CastProvider.UpdateWorkPlatformInfo(context.Background(), infoReq)
if err != nil {
zap.L().Error("Publish UpdateWorkPlatformInfo failed", zap.String("workUuid", workUuid), zap.Error(err))
}
zap.L().Info("Publish Ayrshare PostResp", zap.Any("postResp", postResp))
}
}
return return
} }
@ -509,8 +721,8 @@ func WorkListExport(ctx *gin.Context) {
service.Error(ctx, err) service.Error(ctx, err)
return return
} }
var loigcCastWork = new(logicCast.Work) var logicCastWork = new(logicCast.Work)
excelFile, err := loigcCastWork.ExportExcelWorkList(resp.Data) excelFile, err := logicCastWork.ExportExcelWorkList(resp.Data)
if err != nil { if err != nil {
service.Error(ctx, err) service.Error(ctx, err)
return return
@ -550,7 +762,9 @@ func ProcessTask(ctx context.Context, workUuid string) {
zap.L().Error("从队列移除任务失败", zap.L().Error("从队列移除任务失败",
zap.String("workUuid", workUuid), zap.String("workUuid", workUuid),
zap.Error(err)) zap.Error(err))
return
} }
//_ = PublishWork(context.Background(), &cast.PublishReq{WorkUuids: []string{workUuid}})
zap.L().Info("自动确认成功", zap.String("workUuid", workUuid)) zap.L().Info("自动确认成功", zap.String("workUuid", workUuid))
} }
@ -674,3 +888,445 @@ func GetBalanceLayout(ctx *gin.Context) {
json.Unmarshal([]byte(res.Data), &j) json.Unmarshal([]byte(res.Data), &j)
service.Success(ctx, j) service.Success(ctx, j)
} }
func ImportWorkBatch(ctx *gin.Context) {
excelFile, err := ctx.FormFile("file")
var (
mediaInfoResp *cast.MediaInfoResp
)
if err != nil {
service.Error(ctx, err)
return
}
loginInfo := login.GetUserInfoFromC(ctx)
lockKey := fmt.Sprintf("import_work_batch:%d", loginInfo.ID)
replay := cache.RedisClient.SetNX(lockKey, time.Now().Format("20060102150405"), 5*time.Minute)
if !replay.Val() {
service.Error(ctx, errors.New("有导入任务正在进行,请稍后再试"))
return
}
defer cache.RedisClient.Del(lockKey)
tempDir := "./runtime/work"
_, err = utils.CheckDirPath(tempDir, true)
if err != nil {
service.Error(ctx, err)
return
}
fileName := fmt.Sprintf("%d_in_work.xlsx", time.Now().UnixMicro())
excelPath := filepath.Join(tempDir, fileName)
if err = ctx.SaveUploadedFile(excelFile, excelPath); err != nil {
service.Error(ctx, err)
return
}
excelData, err := excelize.OpenFile(excelPath)
if err != nil {
service.Error(ctx, err)
return
}
defer excelData.Close()
// 解析Excel中的数据
rows, err := excelData.GetRows("Sheet1")
if err != nil {
service.Error(ctx, err)
return
}
req := cast.ImportWorkBatchReq{
ImageWorks: make([]*cast.UpdateWorkImageReq, 0),
}
for line, row := range rows {
if line == 0 {
continue
}
if len(row) == 0 {
continue
}
temp := &cast.UpdateWorkImageReq{
LineNo: uint32(line),
Source: 3,
}
var artistNum string
if len(row) > 1 && utils.CleanString(row[1]) != "" {
artistNum = utils.CleanString(row[1])
artistSubNum := utils.CleanString(row[1])
if artistNum == "" {
temp.Remark = "艺人编号不能为空"
req.ImageWorks = append(req.ImageWorks, temp)
}
var subInfoResp *accountFiee.UserInfoResponse
if config.AppConfig.System.AppMode == "dev" { //FIXME
subInfoResp = &accountFiee.UserInfoResponse{
Id: 123456,
Name: "测试用户",
TelNum: "18288888888",
TelAreaCode: "86",
}
} else {
subInfoResp, err = service.AccountFieeProvider.SubNumGetInfo(context.Background(), &accountFiee.SubNumGetInfoRequest{
SubNum: artistSubNum,
Domain: "app",
})
}
if err != nil {
temp.Remark = fmt.Sprintf("自媒体用户查询失败:%s", err.Error())
zap.L().Error("AccountFieeProvider.SubNumGetInfo", zap.Error(err))
req.ImageWorks = append(req.ImageWorks, temp)
continue
}
if subInfoResp == nil || subInfoResp.Id == 0 {
temp.Remark = "自媒体用户不存在"
zap.L().Error("AccountFieeProvider.SubNumGetInfo user not found", zap.String("subNum", artistSubNum))
req.ImageWorks = append(req.ImageWorks, temp)
continue
}
temp.ArtistUuid = fmt.Sprint(subInfoResp.Id)
temp.ArtistName = subInfoResp.Name
temp.ArtistPhone = subInfoResp.TelNum
temp.ArtistPhoneAreaCode = subInfoResp.TelAreaCode
}
if len(row) > 5 {
temp.Title = utils.CleanString(row[5])
}
if len(row) > 6 {
temp.Content = utils.CleanString(row[6])
}
// 图片
for i := 8; i <= 18; i++ {
if len(row) > i {
if utils.CleanString(row[i]) != "" {
ok, _err := check.ImageCheckUrlValid(row[i])
if _err != nil {
temp.Remark = _err.Error()
req.ImageWorks = append(req.ImageWorks, temp)
break
}
if !ok {
temp.Remark = fmt.Sprintf("图片%d黄反审核未通过", i-7)
req.ImageWorks = append(req.ImageWorks, temp)
break
}
temp.Images = append(temp.Images, utils.CleanString(row[i]))
}
}
}
if len(temp.Images) == 0 {
if temp.Remark == "" {
temp.Remark = "图片不能为空"
}
req.ImageWorks = append(req.ImageWorks, temp)
continue
}
if len(row) > 2 && utils.CleanString(row[2]) != "" {
mediaInfoResp, err = service.CastProvider.MediaInfo(context.Background(), &cast.MediaInfoReq{
ArtistUuid: temp.ArtistUuid,
PlatformID: cast.PlatformIDENUM_TIKTOK,
PlatformUserName: utils.CleanString(row[2]),
})
if err != nil || mediaInfoResp.Info.MediaAccountUuid == "" {
temp.Remark = fmt.Sprintf("TIKTOK账号名不存在")
zap.L().Error("CastProvider.MediaInfo", zap.Error(err))
req.ImageWorks = append(req.ImageWorks, temp)
continue
}
temp.PublishConfig1 = &cast.PublishConfig{
ForbidComment: 1,
PublicType: 1,
CanJoin: 1,
CanQuote: 1,
CanComment: 1,
IsAI: 1,
}
temp.PlatformIDs = append(temp.PlatformIDs, cast.PlatformIDENUM_TIKTOK)
temp.MediaAccountNames = append(temp.MediaAccountNames, utils.CleanString(row[2]))
temp.MediaAccountUuids = append(temp.MediaAccountUuids, mediaInfoResp.Info.MediaAccountUuid)
}
if len(row) > 3 && utils.CleanString(row[3]) != "" {
mediaInfoResp, err = service.CastProvider.MediaInfo(context.Background(), &cast.MediaInfoReq{
ArtistUuid: temp.ArtistUuid,
PlatformID: cast.PlatformIDENUM_INS,
PlatformUserName: utils.CleanString(row[3]),
})
if err != nil || mediaInfoResp.Info.MediaAccountUuid == "" {
temp.Remark = fmt.Sprintf("INS账号名不存在")
zap.L().Error("CastProvider.MediaInfo", zap.Error(err))
req.ImageWorks = append(req.ImageWorks, temp)
continue
}
temp.PublishConfig1 = &cast.PublishConfig{
ForbidComment: 1,
PublicType: 1,
CanJoin: 1,
CanQuote: 1,
CanComment: 1,
IsAI: 1,
}
temp.PlatformIDs = append(temp.PlatformIDs, cast.PlatformIDENUM_INS)
temp.MediaAccountNames = append(temp.MediaAccountNames, utils.CleanString(row[3]))
temp.MediaAccountUuids = append(temp.MediaAccountUuids, mediaInfoResp.Info.MediaAccountUuid)
}
if len(row) > 4 && utils.CleanString(row[4]) != "" {
temp.Remark = fmt.Sprintf("DM不能发图文")
zap.L().Error("CastProvider.MediaInfo", zap.Error(err))
req.ImageWorks = append(req.ImageWorks, temp)
continue
/*mediaInfoResp, err = service.CastProvider.MediaInfo(context.Background(), &cast.MediaInfoReq{
ArtistUuid: temp.ArtistUuid,
PlatformID: cast.PlatformIDENUM_DM,
PlatformUserName: utils.CleanString(row[4]),
})
if err != nil || mediaInfoResp.Info.MediaAccountUuid == "" {
temp.Remark = fmt.Sprintf("DM账号名不存在")
zap.L().Error("CastProvider.MediaInfo", zap.Error(err))
req.ImageWorks = append(req.ImageWorks, temp)
continue
}
temp.PlatformIDs = append(temp.PlatformIDs, cast.PlatformIDENUM_DM)
temp.PublishConfig1 = &cast.PublishConfig{
ForbidComment: 1,
PublicType: 1,
CanJoin: 1,
CanQuote: 1,
CanComment: 1,
IsAI: 1,
}
temp.MediaAccountNames = append(temp.MediaAccountNames, utils.CleanString(row[4]))
temp.MediaAccountUuids = append(temp.MediaAccountUuids, mediaInfoResp.Info.MediaAccountUuid)*/
}
if artistNum == "" {
temp.Remark = "艺人编号不能为空"
req.ImageWorks = append(req.ImageWorks, temp)
continue
}
if len(temp.MediaAccountUuids) == 0 {
temp.Remark = "账号名不能为空"
req.ImageWorks = append(req.ImageWorks, temp)
continue
}
if len(temp.PlatformIDs) == 0 {
temp.Remark = "关联平台不能为空"
req.ImageWorks = append(req.ImageWorks, temp)
continue
}
if temp.Title == "" {
temp.Remark = "标题不能为空"
req.ImageWorks = append(req.ImageWorks, temp)
continue
}
if temp.Content == "" {
temp.Remark = "内容不能为空"
req.ImageWorks = append(req.ImageWorks, temp)
continue
}
if len(temp.Images) == 0 {
temp.Remark = "图片不能为空"
req.ImageWorks = append(req.ImageWorks, temp)
continue
}
req.ImageWorks = append(req.ImageWorks, temp)
}
if len(req.ImageWorks) == 0 {
service.Error(ctx, errors.New(e.ErrNoData))
return
}
newCtx := NewCtxWithUserInfo(ctx)
resp, err := service.CastProvider.ImportWorkBatch(newCtx, &req)
if err != nil {
service.Error(ctx, err)
return
}
// 打开模板文件,如果有失败的数据,生成结果文件
var urlResult string
if resp.FailCount > 0 {
hasValueRows := make(map[int]bool, resp.FailCount)
for _, v := range resp.ImageWorks {
if !v.Success {
rowNum := int(v.LineNo) + 1
excelData.SetCellValue("Sheet1", fmt.Sprintf("H%d", rowNum), v.Remark)
hasValueRows[rowNum] = true
}
}
for i := len(rows) - 1; i >= 1; i-- { // 从最后一行开始
if !hasValueRows[i+1] {
if err = excelData.RemoveRow("Sheet1", i+1); err != nil {
continue
}
}
}
resultPath := fmt.Sprintf("./runtime/work/%s", fileName)
if err = excelData.SaveAs(resultPath); err != nil {
service.Error(ctx, err)
return
}
urlHost := config.AppConfig.System.FieeHost
urlResult = fmt.Sprintf("%s/api/fiee/static/work/%s", urlHost, fileName)
}
service.Success(ctx, map[string]interface{}{
"successCount": resp.SuccessCount,
"failCount": resp.FailCount,
"resultUrl": urlResult,
})
return
}
// RefreshPost 刷新帖子状态
func RefreshPost(ctx *gin.Context) {
err := RefreshPublish()
if err != nil {
service.Error(ctx, err)
return
}
service.Success(ctx, nil)
return
}
func RefreshPublish() error {
var resp *cast.RefreshWorkListResp
var err error
resp, err = service.CastProvider.RefreshWorkList(context.Background(), &cast.RefreshWorkListReq{PublishStatus: 1})
if err != nil {
return err
}
for _, workInfo := range resp.Data {
if workInfo.ProfileKey == "" {
continue
}
for _, platformInfo := range workInfo.PlatformInfoData {
if platformInfo.PublishMediaID == "" {
continue
}
var infoReq = &cast.UpdateWorkPlatformInfoReq{
PlatformInfoData: make([]*cast.PlatformInfo, 0),
}
//DM单独刷新
if platformInfo.PlatformID == cast.PlatformIDENUM_DM {
var dmData []byte
_, dmData, err = utils.GetUrl(fmt.Sprintf("https://api.dailymotion.com/video/%s?fields=id,title,url,published,private", platformInfo.PublishMediaID),
map[string]string{"Authorization": "Bearer " + platformInfo.Token}, config.AppConfig.System.ProxyUrl)
if err != nil {
infoReq.PlatformInfoData = append(infoReq.PlatformInfoData, &cast.PlatformInfo{
WorkUuid: workInfo.WorkUuid,
MediaAccountUuid: "",
PlatformID: uint32(platformInfo.PlatformID),
PublishType: 2,
PublishResp: err.Error(),
PublishMediaId: platformInfo.PublishMediaID,
PublishStatus: cast.PublishStatusENUM_PublishMediaStatus_EXCEPTION,
Remark: "",
PlatformUuid: platformInfo.PlatformUuid,
})
_, _ = service.CastProvider.UpdateWorkPlatformInfo(context.Background(), infoReq)
continue
}
var dmPost *modelCast.DMPost
_ = json.Unmarshal(dmData, &dmPost)
if dmPost.Published {
infoReq.PlatformInfoData = append(infoReq.PlatformInfoData, &cast.PlatformInfo{
WorkUuid: workInfo.WorkUuid,
MediaAccountUuid: "",
PlatformID: uint32(platformInfo.PlatformID),
PublishType: 2,
PublishResp: string(dmData),
PublishMediaId: platformInfo.PublishMediaID,
PublishStatus: cast.PublishStatusENUM_PublishMediaStatus_DONE,
Remark: "",
PlatformUuid: platformInfo.PlatformUuid,
})
_, _ = service.CastProvider.UpdateWorkPlatformInfo(context.Background(), infoReq)
} else {
infoReq.PlatformInfoData = append(infoReq.PlatformInfoData, &cast.PlatformInfo{
WorkUuid: workInfo.WorkUuid,
MediaAccountUuid: "",
PlatformID: uint32(platformInfo.PlatformID),
PublishType: 2,
PublishResp: string(dmData),
PublishMediaId: platformInfo.PublishMediaID,
PublishStatus: cast.PublishStatusENUM_PublishMediaStatus_FAIL,
Remark: "",
PlatformUuid: platformInfo.PlatformUuid,
})
_, _ = service.CastProvider.UpdateWorkPlatformInfo(context.Background(), infoReq)
}
continue
}
postResp, _err := service.AyrshareProvider.GetPost(context.Background(), &aryshare.GetPostRequest{
Id: platformInfo.PublishMediaID,
ProfileKey: workInfo.ProfileKey,
})
if _err != nil {
zap.L().Error("GetPost err", zap.Error(_err))
go func(v *cast.RefreshWorkListResp_Info) {
_, _ = service.CastProvider.UpdateWorkPublishLog(context.Background(), &cast.UpdateWorkPublishLogReq{
WorkUuid: v.WorkUuid,
Detail: _err.Error(),
Action: "get",
})
}(workInfo)
infoReq.PlatformInfoData = append(infoReq.PlatformInfoData, &cast.PlatformInfo{
WorkUuid: workInfo.WorkUuid,
MediaAccountUuid: "",
PlatformID: uint32(platformInfo.PlatformID),
PublishType: 2,
PublishResp: _err.Error(),
PublishMediaId: platformInfo.PublishMediaID,
PublishStatus: cast.PublishStatusENUM_PublishMediaStatus_EXCEPTION,
Remark: "",
PlatformUuid: platformInfo.PlatformUuid,
})
_, _ = service.CastProvider.UpdateWorkPlatformInfo(context.Background(), infoReq)
continue
}
if postResp.Errors != nil {
}
var postBytes []byte
postBytes, _ = json.Marshal(postResp)
if len(postResp.PostIds) == 0 || len(postResp.Errors) > 0 {
infoReq.PlatformInfoData = append(infoReq.PlatformInfoData, &cast.PlatformInfo{
WorkUuid: workInfo.WorkUuid,
MediaAccountUuid: "",
PlatformID: uint32(platformInfo.PlatformID),
PublishType: 2,
PublishResp: string(postBytes),
PublishMediaId: platformInfo.PublishMediaID,
PublishStatus: cast.PublishStatusENUM_PublishMediaStatus_FAIL,
Remark: "",
PlatformUuid: platformInfo.PlatformUuid,
})
_, _ = service.CastProvider.UpdateWorkPlatformInfo(context.Background(), infoReq)
}
if len(postResp.PostIds) > 0 {
for _, vv := range postResp.PostIds {
if vv.Status == "pending" || vv.Status == "awaiting approval" {
continue
}
var publishStatus cast.PublishStatusENUM
if vv.Status == "deleted" || vv.Status == "error" {
publishStatus = cast.PublishStatusENUM_PublishMediaStatus_FAIL
}
if vv.Status == "success" {
publishStatus = cast.PublishStatusENUM_PublishMediaStatus_DONE
}
// 不知道TikTok为什么会有这样的数据,as文档没说 特殊判断一下
if vv.Platform == "tiktok" && vv.Status == "success" && vv.Id == "pending" {
publishStatus = cast.PublishStatusENUM_PublishMediaStatus_ING
}
platformID := modelCast.NamePlatformIDKv[vv.Platform]
infoReq.PlatformInfoData = append(infoReq.PlatformInfoData, &cast.PlatformInfo{
WorkUuid: workInfo.WorkUuid,
MediaAccountUuid: "",
PlatformID: platformID,
PublishType: 2,
PublishResp: string(postBytes),
PublishMediaId: platformInfo.PublishMediaID,
PublishStatus: publishStatus,
Remark: "",
PlatformUuid: platformInfo.PlatformUuid,
})
}
_, _ = service.CastProvider.UpdateWorkPlatformInfo(context.Background(), infoReq)
}
}
}
return nil
}

View File

@ -0,0 +1,52 @@
package check
import (
"errors"
"fmt"
"mime/multipart"
"time"
pkgSecurity "fonchain-fiee/pkg/security"
"github.com/fonchain/utils/security"
)
// ImageCheckUrlValid 图片黄疸检测 true 是通过
func ImageCheckUrlValid(imgUrl string) (bool, error) {
resp, err := pkgSecurity.ImageScanner.ScanImageByURL(imgUrl, fmt.Sprint(time.Now().UnixMicro()), security.BaselineCheckGlobal)
if err != nil {
err = errors.New("图片检测请求失败")
return false, err
}
if resp.Code != 200 {
err = errors.New("图片检测失败,错误码")
return false, err
}
if len(resp.Data) == 0 || len(resp.Data[0].Results) == 0 {
return false, errors.New("图片检测结果异常")
}
riskLevel := resp.Data[0].Results[0].RiskLevel
if *riskLevel == "none" {
return true, nil
}
return false, nil
}
func ImageCheckByte(file *multipart.FileHeader) (bool, error) {
//imageScanner, err := security.NewImageScanner(&security.Config{
// RAMAccessKeyID: "LTAI5tNBzbeEbG1yCitvHsMb",
// RAMAccessKeySecret: "G1xAUB8G6WDVo0SLr6DJaJjNWIlpmO",
// RAMRoleArn: "acs:ram::5828544250383902:role/content-secret",
// Region: "ap-southeast-1",
// Endpoint: "green-cip.ap-southeast-1.aliyuncs.com",
// TempAccessKeyID: "",
// TempAccessKeySecret: "",
// SecurityToken: "",
//})
//if err != nil {
// return false, err
//}
//resp, err := imageScanner.ScanImageByFileByte(file, fmt.Sprint(time.Now().UnixMicro()), security.BaselineCheckGlobal)
//fmt.Println(resp)
return false, nil
}

View File

@ -0,0 +1,140 @@
package imports
import (
"encoding/json"
"fmt"
"net/http"
"time"
)
// 配置结构增强
type Aiconfig struct {
APIKey string
BaseURL string
Image2ImageURL string
TextGenerationURL string
TextToImageURL string
TextToImageModel string
TaskQueryURL string
ImageModel string
TextModel string
DefaultSize string
Timeout int
SaveDir string
MaxTokens int
Temperature float64
TopP float64
}
// 完整的AI生成器
type AiGenerator struct {
cfg Aiconfig
client *http.Client
}
const (
DefaultAPIKey = "sk-5ae9df5d3bcf4755ad5d12012058a2e7"
DefaultBaseURL = "https://dashscope.aliyuncs.com"
DefaultTextToImageURL = "/api/v1/services/aigc/text2image/image-synthesis" //文生图
DefaultTextToImageModel = "wan2.5-t2i-preview"
DefaultImage2ImageURL = "/api/v1/services/aigc/image2image/image-synthesis" //图生图
DefaultImageModel = "wan2.5-i2i-preview"
DefaultTextGenerationURL = "/api/v1/services/aigc/text-generation/generation" //文生文
DefaultTextModel = "qwen-turbo"
DefaultSize = "1024x1024"
DefaultTaskQueryURL = "/api/v1/tasks"
DefaultTimeout = 30
DefaultMaxTokens = 2000
DefaultTemperature = 0.8
DefaultTopP = 0.9
)
func NewAiGenerator() *AiGenerator {
cfg := &Aiconfig{
APIKey: DefaultAPIKey,
BaseURL: DefaultBaseURL,
Image2ImageURL: DefaultImage2ImageURL,
TextGenerationURL: DefaultTextGenerationURL,
TextToImageURL: DefaultTextToImageURL,
TextToImageModel: DefaultTextToImageModel,
TaskQueryURL: DefaultTaskQueryURL,
ImageModel: DefaultImageModel,
TextModel: DefaultTextModel,
DefaultSize: DefaultSize,
Timeout: DefaultTimeout,
MaxTokens: DefaultMaxTokens,
Temperature: DefaultTemperature,
TopP: DefaultTopP,
}
return &AiGenerator{
cfg: *cfg,
client: &http.Client{
Timeout: time.Duration(cfg.Timeout) * time.Second,
},
}
}
// 任务结果--------------------------------------------------------------------------
// ImageGenerationResponse 图片生成响应
type ImageGenerationResponse struct {
RequestID string `json:"request_id"`
Output Output `json:"output"`
Usage Usage `json:"usage"`
}
type Output struct {
TaskID string `json:"task_id"`
TaskStatus string `json:"task_status"`
SubmitTime string `json:"submit_time"`
ScheduledTime string `json:"scheduled_time"`
EndTime string `json:"end_time"`
Results []Result `json:"results"`
TaskMetrics TaskMetrics `json:"task_metrics"`
}
type Result struct {
URL string `json:"url,omitempty"`
Code string `json:"code,omitempty"`
Message string `json:"message,omitempty"`
}
type TaskMetrics struct {
Total int `json:"TOTAL"`
Succeeded int `json:"SUCCEEDED"`
Failed int `json:"FAILED"`
}
type Usage struct {
ImageCount int `json:"image_count"`
}
func (g *AiGenerator) GetTaskDetail(taskID string) (*ImageGenerationResponse, error) {
url := fmt.Sprintf("%s/api/v1/tasks/%s", g.cfg.BaseURL, taskID)
httpReq, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
httpReq.Header.Set("Authorization", "Bearer "+g.cfg.APIKey)
httpReq.Header.Set("Content-Type", "application/json")
resp, err := g.client.Do(httpReq)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("API错误: %d", resp.StatusCode)
}
var imageGenerationResponse ImageGenerationResponse
if err := json.NewDecoder(resp.Body).Decode(&imageGenerationResponse); err != nil {
return nil, fmt.Errorf("任务详情解析失败: %v", err)
}
return &imageGenerationResponse, nil
}

View File

@ -0,0 +1,92 @@
package imports
//
//import (
// "bytes"
// "encoding/json"
// "fmt"
// "net/http"
//)
//
////------------------------------------------图生图
//
//type Image2ImageRequest struct {
// Model string `json:"model"`
// Input ImageInput `json:"input"`
// Params ImageParams `json:"parameters"`
//}
//
//type ImageInput struct {
// Images []string `json:"images"`
// Prompt string `json:"prompt"` // 可选的条件文本
//}
//
//type ImageParams struct {
// Size string `json:"size,omitempty"` // 输出尺寸
// Strength float64 `json:"strength"` // 重绘强度0-1
// N int `json:"n,omitempty"` // 生成数量
//}
//
//type ImageResponse struct {
// Output struct {
// TaskID string `json:"task_id"`
// Results []struct {
// URL string `json:"url"`
// } `json:"results"`
// } `json:"output"`
// RequestID string `json:"request_id"`
//}
//
//// Image2image 图生图
//func (g *AiGenerator) Image2image(imagePath string, prompt string, strength float64, size string, n int) (*ImageResponse, error) {
// if g.cfg.APIKey == "" {
// return nil, fmt.Errorf("API密钥未配置")
// }
//
// // 构建请求
// req := Image2ImageRequest{
// Model: g.cfg.ImageModel,
// Input: ImageInput{
// Images: []string{imagePath},
// Prompt: prompt,
// },
// Params: ImageParams{
// Size: size,
// Strength: strength,
// N: n,
// },
// }
//
// url := g.cfg.BaseURL + g.cfg.Image2ImageURL
// jsonData, err := json.Marshal(req)
// if err != nil {
// return nil, err
// }
//
// httpReq, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
// if err != nil {
// return nil, err
// }
//
// httpReq.Header.Set("Content-Type", "application/json")
// httpReq.Header.Set("Authorization", "Bearer "+g.cfg.APIKey)
// httpReq.Header.Set("X-DashScope-Async", "enable")
//
// resp, err := g.client.Do(httpReq)
// if err != nil {
// return nil, err
// }
// defer resp.Body.Close()
//
// // 解析响应
// var result ImageResponse
// if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
// return nil, fmt.Errorf("响应解析失败: %v", err)
// }
//
// if resp.StatusCode != http.StatusOK {
// return nil, fmt.Errorf("API错误: %d", resp.StatusCode)
// }
//
// return &result, nil
//}

View File

@ -0,0 +1,346 @@
package imports
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"math/rand"
"net/http"
"strings"
"time"
)
// 使用OpenAI兼容格式
type ChatCompletionRequest struct {
Model string `json:"model"`
Messages []ChatMessage `json:"messages"`
MaxTokens int `json:"max_tokens,omitempty"`
Temperature float64 `json:"temperature,omitempty"`
TopP float64 `json:"top_p,omitempty"`
TopK float64 `json:"top_k,omitempty"`
PresencePenalty float64 `json:"presence_penalty,omitempty"`
Seed int `json:"seed,omitempty"`
}
type ChatMessage struct {
Role string `json:"role"`
Content []Content `json:"content"`
}
type Content struct {
Type string `json:"type"`
Text string `json:"text,omitempty"`
ImageURL struct {
URL string `json:"url"`
} `json:"image_url,omitempty"`
}
type ChatCompletionResponse struct {
Choices []struct {
Message struct {
Content string `json:"content"`
} `json:"message"`
} `json:"choices"`
Error struct {
Message string `json:"message"`
} `json:"error,omitempty"`
}
// 图生文:根据图片生成标题和内容
func (g *AiGenerator) GenerateTitleAndContentFromImage(imageURL, titleRequire, contentRequire string) (string, string, error) {
// 构建提示词
prompt := fmt.Sprintf(`请分析这张图片并生成内容
图片分析要求
1. 标题要求%s
2. 内容要求%s
请严格按照以下格式返回不要有任何额外文字
标题{生成的标题}
内容{生成的内容}`, titleRequire, contentRequire)
// 发送聊天请求
response, err := g.chatWithImage(imageURL, prompt)
if err != nil {
return "", "", err
}
// 解析响应
title, content := parseTitleAndContent(response)
return title, content, nil
}
func (g *AiGenerator) chatWithImage(imageURL, prompt string) (string, error) {
reqBody := ChatCompletionRequest{
Model: "qwen3-vl-plus",
Messages: []ChatMessage{
{
Role: "user",
Content: []Content{
{
Type: "image_url",
ImageURL: struct {
URL string `json:"url"`
}{
URL: imageURL,
},
},
{
Type: "text",
Text: prompt,
},
},
},
},
MaxTokens: 2000,
Temperature: 1.5,
TopP: 0.9,
TopK: 99,
PresencePenalty: 1.5,
Seed: generateSeed(),
}
// 使用兼容模式接口
url := g.cfg.BaseURL + "/compatible-mode/v1/chat/completions"
jsonData, err := json.Marshal(reqBody)
if err != nil {
return "", fmt.Errorf("JSON序列化失败: %v", err)
}
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
if err != nil {
return "", fmt.Errorf("创建请求失败: %v", err)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+g.cfg.APIKey)
req.Header.Set("X-DashScope-Session-Id", fmt.Sprintf("session-%d", time.Now().UnixNano()))
resp, err := g.client.Do(req)
if err != nil {
return "", fmt.Errorf("API请求失败: %v", err)
}
defer resp.Body.Close()
// 读取响应体
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("读取响应失败: %v", err)
}
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("API错误: %d, 响应: %s", resp.StatusCode, string(body))
}
var result ChatCompletionResponse
if err := json.Unmarshal(body, &result); err != nil {
return "", fmt.Errorf("JSON解析失败: %v, 响应: %s", err, string(body))
}
// 检查API错误
if result.Error.Message != "" {
return "", fmt.Errorf("API返回错误: %s", result.Error.Message)
}
if len(result.Choices) == 0 {
return "", errors.New("AI未生成有效响应")
}
response := strings.TrimSpace(result.Choices[0].Message.Content)
return response, nil
}
func generateSeed() int {
rand.Seed(time.Now().UnixNano()) // 使用当前时间戳作为种子
return rand.Intn(2147483647) // 生成一个在 [0, 231-1] 范围内的随机数
}
// 解析标题和内容
func parseTitleAndContent(response string) (string, string) {
var title, content string
lines := strings.Split(response, "\n")
for _, line := range lines {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "标题:") {
title = strings.TrimPrefix(line, "标题:")
} else if strings.HasPrefix(line, "内容:") {
content = strings.TrimPrefix(line, "内容:")
}
}
return strings.TrimSpace(title), strings.TrimSpace(content)
}
// 文本生成文本(聊天模式)
func (g *AiGenerator) GenerateTitleAndContentFromText(titleRequire, contentRequire string) (string, string, error) {
// 构建提示词
prompt := fmt.Sprintf(`请根据以下要求生成内容
生成要求
1. 标题要求%s
2. 内容要求%s
请严格按照以下格式返回不要有任何额外文字
标题{生成的标题}
内容{生成的内容}`,
titleRequire,
contentRequire,
)
// 发送聊天请求
response, err := g.chatWithText(prompt)
if err != nil {
return "", "", err
}
// 解析响应
title, content := parseTitleAndContent(response)
return title, content, nil
}
// 文本聊天(纯文本生成)
func (g *AiGenerator) chatWithText(prompt string) (string, error) {
reqBody := ChatCompletionRequest{
Model: "qwen-max", // 使用文本模型
Messages: []ChatMessage{
{
Role: "user",
Content: []Content{
{
Type: "text",
Text: prompt,
},
},
},
},
MaxTokens: 2000,
Temperature: 1.9,
TopP: 1.0,
TopK: 99,
PresencePenalty: 1.0,
Seed: generateSeed(),
}
url := g.cfg.BaseURL + "/compatible-mode/v1/chat/completions"
jsonData, err := json.Marshal(reqBody)
if err != nil {
return "", fmt.Errorf("JSON序列化失败: %v", err)
}
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
if err != nil {
return "", fmt.Errorf("创建请求失败: %v", err)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+g.cfg.APIKey)
req.Header.Set("X-DashScope-Session-Id", fmt.Sprintf("session-%d", time.Now().UnixNano()))
resp, err := g.client.Do(req)
if err != nil {
return "", fmt.Errorf("API请求失败: %v", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("读取响应失败: %v", err)
}
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("API错误: %d, 响应: %s", resp.StatusCode, string(body))
}
var result ChatCompletionResponse
if err := json.Unmarshal(body, &result); err != nil {
return "", fmt.Errorf("JSON解析失败: %v, 响应: %s", err, string(body))
}
if result.Error.Message != "" {
return "", fmt.Errorf("API返回错误: %s", result.Error.Message)
}
if len(result.Choices) == 0 {
return "", errors.New("AI未生成有效响应")
}
response := strings.TrimSpace(result.Choices[0].Message.Content)
return response, nil
}
//func (g *AiGenerator) GenerateImageFromText(prompt, size string, n int) (string, error) {
// // 构建图片生成提示词
// imagePrompt := fmt.Sprintf(`请根据以下描述生成图片:
//
//图片描述:%s
//生成数量:%d张
//图片尺寸:%s
//
//请直接生成图片,不要返回任何文字描述。`,
// prompt, n, size)
//
// // 使用文生图API
// result, err := g.TextToImage(imagePrompt, size, n)
// if err != nil {
// return "", err
// }
//
// return result.Output.TaskID, nil
//}
// 文本生成图像
//func (g *AiGenerator) TextToImage(prompt, size string, n int) (ImageGenerationResponse, error) {
// // 构建图像生成请求
// reqBody := map[string]interface{}{
// "prompt": prompt,
// "n": n,
// "size": size,
// "response_format": "url", // 假设返回的格式为图像 URL可以根据实际 API 调整
// }
//
// // 使用图像生成接口
// url := g.cfg.BaseURL + "/v1/images/generations"
// jsonData, err := json.Marshal(reqBody)
// if err != nil {
// return ImageGenerationResponse{}, fmt.Errorf("JSON序列化失败: %v", err)
// }
//
// req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
// if err != nil {
// return ImageGenerationResponse{}, fmt.Errorf("创建请求失败: %v", err)
// }
//
// req.Header.Set("Content-Type", "application/json")
// req.Header.Set("Authorization", "Bearer "+g.cfg.APIKey)
//
// resp, err := g.client.Do(req)
// if err != nil {
// return ImageGenerationResponse{}, fmt.Errorf("API请求失败: %v", err)
// }
// defer resp.Body.Close()
//
// // 读取响应体
// body, err := io.ReadAll(resp.Body)
// if err != nil {
// return ImageGenerationResponse{}, fmt.Errorf("读取响应失败: %v", err)
// }
//
// if resp.StatusCode != http.StatusOK {
// return ImageGenerationResponse{}, fmt.Errorf("API错误: %d, 响应: %s", resp.StatusCode, string(body))
// }
//
// // 解析图像生成响应
// var result ImageGenerationResponse
// if err := json.Unmarshal(body, &result); err != nil {
// return ImageGenerationResponse{}, fmt.Errorf("JSON解析失败: %v, 响应: %s", err, string(body))
// }
//
// if len(result.Data) == 0 {
// return ImageGenerationResponse{}, errors.New("未生成任何图像")
// }
//
// return result, nil
//}

View File

@ -0,0 +1,100 @@
package imports
//
//import (
// "bytes"
// "encoding/json"
// "fmt"
// "net/http"
//)
//
//// ----------------------------文生文
//
//// 文本生成请求结构
//type TextGenerationRequest struct {
// Model string `json:"model"`
// Input TextInput `json:"input"`
// Params TextParams `json:"parameters"`
//}
//
//type TextInput struct {
// Messages []Message `json:"messages"`
//}
//
//type TextParams struct {
// ResultFormat string `json:"result_format,omitempty"` // 结果格式
// MaxTokens int `json:"max_tokens,omitempty"` // 最大token数
// Temperature float64 `json:"temperature,omitempty"` // 温度参数
// TopP float64 `json:"top_p,omitempty"` // 核采样参数
//}
//
//type TextResponse struct {
// Output struct {
// Text string `json:"text"`
// FinishReason string `json:"finish_reason"`
// } `json:"output"`
// Usage struct {
// InputTokens int `json:"input_tokens"`
// OutputTokens int `json:"output_tokens"`
// TotalTokens int `json:"total_tokens"`
// } `json:"usage"`
// RequestID string `json:"request_id"`
//}
//
//// GenerateText 生成文本
//func (g *AiGenerator) GenerateText(prompt string) (*TextResponse, error) {
// if g.cfg.APIKey == "" {
// return nil, fmt.Errorf("API密钥未配置")
// }
//
// // 构建请求
// req := TextGenerationRequest{
// Model: g.cfg.TextModel,
// Input: TextInput{
// Messages: []Message{
// {
// Role: "user",
// Content: prompt,
// },
// },
// },
// Params: TextParams{
// ResultFormat: "message",
// MaxTokens: g.cfg.MaxTokens,
// Temperature: g.cfg.Temperature,
// TopP: g.cfg.TopP,
// },
// }
//
// url := g.cfg.BaseURL + g.cfg.TextGenerationURL
// jsonData, err := json.Marshal(req)
// if err != nil {
// return nil, err
// }
//
// httpReq, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
// if err != nil {
// return nil, err
// }
//
// httpReq.Header.Set("Content-Type", "application/json")
// httpReq.Header.Set("Authorization", "Bearer "+g.cfg.APIKey)
//
// resp, err := g.client.Do(httpReq)
// if err != nil {
// return nil, err
// }
// defer resp.Body.Close()
//
// // 解析响应
// var result TextResponse
// if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
// return nil, fmt.Errorf("文本生成响应解析失败: %v", err)
// }
//
// if resp.StatusCode != http.StatusOK {
// return nil, fmt.Errorf("API错误: %d", resp.StatusCode)
// }
//
// return &result, nil
//}

View File

@ -0,0 +1,120 @@
package imports
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
// 修正后的文生图请求结构
type TextToImageRequest struct {
Model string `json:"model"`
Input TextToImageInput `json:"input"`
Params TextToImageParameters `json:"parameters"` // 修正字段名
}
type TextToImageInput struct {
Prompt string `json:"prompt"`
NegativePrompt string `json:"negative_prompt"`
}
type TextToImageParameters struct {
Size string `json:"size,omitempty"`
N int `json:"n,omitempty"`
NegativePrompt string `json:"negative_prompt,omitempty"`
Steps int `json:"steps,omitempty"`
Scale float64 `json:"scale,omitempty"`
Style string `json:"style,omitempty"`
Seed int `json:"seed,omitempty"`
Temperature float64 `json:"temperature,omitempty"`
TopP float64 `json:"top_p,omitempty"`
}
type TextToImageResponse struct {
Output struct {
TaskID string `json:"task_id"`
TaskStatus string `json:"task_status"` // 添加任务状态字段
Results []struct {
URL string `json:"url"`
} `json:"results"`
} `json:"output"`
RequestID string `json:"request_id"`
Code string `json:"code,omitempty"` // 错误代码
Message string `json:"message,omitempty"` // 错误信息
}
// 修正后的文生图函数
func (g *AiGenerator) TextToImage(prompt string, size string, n int) (*TextToImageResponse, error) {
if g.cfg.APIKey == "" {
return nil, fmt.Errorf("API密钥未配置")
}
// 构建请求
req := TextToImageRequest{
Model: g.cfg.TextToImageModel,
Input: TextToImageInput{
Prompt: prompt,
NegativePrompt: "低质量、残缺、人物正脸、多余的手指、乱码字符和文字、比例不良丶场景以国内场景为主",
//NegativePrompt: "人物正脸",
},
Params: TextToImageParameters{
Size: size,
N: n,
Seed: generateSeed(),
NegativePrompt: "低质量、残缺、人物正脸、多余的手指、乱码字符和文字、比例不良、场景以国内场景为主",
},
}
url := g.cfg.BaseURL + g.cfg.TextToImageURL
jsonData, err := json.Marshal(req)
if err != nil {
return nil, fmt.Errorf("JSON序列化失败: %v", err)
}
// 打印请求信息用于调试
fmt.Printf("请求URL: %s\n", url)
fmt.Printf("请求体: %s\n", string(jsonData))
httpReq, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
if err != nil {
return nil, fmt.Errorf("创建请求失败: %v", err)
}
httpReq.Header.Set("Content-Type", "application/json")
httpReq.Header.Set("Authorization", "Bearer "+g.cfg.APIKey)
httpReq.Header.Set("X-DashScope-Async", "enable")
resp, err := g.client.Do(httpReq)
if err != nil {
return nil, fmt.Errorf("API请求失败: %v", err)
}
defer resp.Body.Close()
// 读取完整响应体
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("读取响应失败: %v", err)
}
fmt.Printf("响应状态: %d\n", resp.StatusCode)
fmt.Printf("响应体: %s\n", string(body))
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("API错误: %d, 响应: %s", resp.StatusCode, string(body))
}
// 解析响应
var result TextToImageResponse
if err := json.Unmarshal(body, &result); err != nil {
return nil, fmt.Errorf("响应解析失败: %v, 原始响应: %s", err, string(body))
}
// 检查任务状态
if result.Output.TaskStatus == "FAILED" {
return nil, fmt.Errorf("图片生成失败: %s", result)
}
return &result, nil
}

View File

@ -0,0 +1,116 @@
package imports
//
//import (
// "bytes"
// "encoding/json"
// "fmt"
// "io"
// "net/http"
//)
//
//// Message 结构体定义
//type Message struct {
// Role string `json:"role"`
// Content string `json:"content"`
//}
//
//// 同步文本生成请求
//type SyncTextGenerationRequest struct {
// Model string `json:"model"`
// Input SyncTextInput `json:"input"`
// Parameters SyncTextGenerationParams `json:"parameters"`
//}
//
//type SyncTextInput struct {
// Messages []Message `json:"messages"`
//}
//
//type SyncTextGenerationParams struct {
// ResultFormat string `json:"result_format,omitempty"`
// MaxTokens int `json:"max_tokens,omitempty"`
// Temperature float64 `json:"temperature,omitempty"`
// TopP float64 `json:"top_p,omitempty"`
// TopK int `json:"top_k,omitempty"`
// Seed int64 `json:"seed,omitempty"`
//}
//
//// 同步文本生成响应
//type SyncTextGenerationResponse struct {
// Output struct {
// Choices []struct {
// Message Message `json:"message"`
// } `json:"choices"`
// Text string `json:"text"`
// FinishReason string `json:"finish_reason"`
// } `json:"output"`
// Usage struct {
// InputTokens int `json:"input_tokens"`
// OutputTokens int `json:"output_tokens"`
// TotalTokens int `json:"total_tokens"`
// } `json:"usage"`
// RequestID string `json:"request_id"`
//}
//
//// 同步文本生成URL
//const DefaultSyncTextGenerationURL = "/api/v1/services/aigc/text-generation/generation"
//
//// 同步生成文本
//func (g *AiGenerator) GenerateTextSync(prompt string) (*SyncTextGenerationResponse, error) {
// if g.cfg.APIKey == "" {
// return nil, fmt.Errorf("API密钥未配置")
// }
//
// // 构建请求
// req := SyncTextGenerationRequest{
// Model: g.cfg.TextModel,
// Input: SyncTextInput{
// Messages: []Message{
// {
// Role: "user",
// Content: prompt,
// },
// },
// },
// Parameters: SyncTextGenerationParams{
// ResultFormat: "message",
// MaxTokens: g.cfg.MaxTokens,
// Temperature: g.cfg.Temperature,
// TopP: g.cfg.TopP,
// },
// }
//
// url := g.cfg.BaseURL + DefaultSyncTextGenerationURL
// jsonData, err := json.Marshal(req)
// if err != nil {
// return nil, fmt.Errorf("JSON序列化失败: %v", err)
// }
//
// httpReq, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
// if err != nil {
// return nil, fmt.Errorf("创建请求失败: %v", err)
// }
//
// httpReq.Header.Set("Content-Type", "application/json")
// httpReq.Header.Set("Authorization", "Bearer "+g.cfg.APIKey)
// // 注意:这里不设置 X-DashScope-Async 头,使用同步模式
//
// resp, err := g.client.Do(httpReq)
// if err != nil {
// return nil, fmt.Errorf("API请求失败: %v", err)
// }
// defer resp.Body.Close()
//
// if resp.StatusCode != http.StatusOK {
// body, _ := io.ReadAll(resp.Body)
// return nil, fmt.Errorf("API错误: %d, 响应: %s", resp.StatusCode, string(body))
// }
//
// // 解析响应
// var result SyncTextGenerationResponse
// if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
// return nil, fmt.Errorf("响应解析失败: %v", err)
// }
//
// return &result, nil
//}

View File

@ -0,0 +1,118 @@
package imports
import (
"errors"
"fmt"
"fonchain-fiee/pkg/service"
"fonchain-fiee/pkg/utils"
"sort"
"time"
"github.com/gin-gonic/gin"
)
func ImageContentGetResult(c *gin.Context) {
processor := GetBatchProcessorReadOnly()
if processor == nil {
service.Success(c, gin.H{
"status": 0,
"status_description": StatusMap[0],
"message": "暂无执行中的任务",
})
return
}
currentStatus := processor.getStatus()
switch currentStatus {
case StatusIdle:
service.Success(c, gin.H{
"status": currentStatus,
"status_description": StatusMap[currentStatus],
"message": "暂无执行中的任务",
})
return
case StatusProcessing:
completed, pending, total, completedTasks, failedTasks := processor.getTaskStatistics()
service.Success(c, gin.H{
"status": currentStatus,
"status_description": StatusMap[currentStatus],
"message": "任务执行中,请稍后",
"total_tasks": total,
"pending_tasks": pending,
"failed_tasks": len(failedTasks), //失败数量
"success_tasks": len(completedTasks), //成功数量
"progress": fmt.Sprintf("%.1f%%", float64(completed)/float64(total)*100),
"completed_tasks": completed,
"completed_rate": float64(completed) / float64(total) * 100,
})
return
case StatusCompleted:
completed, pending, total, completedTasks, failedTasks := processor.getTaskStatistics()
service.Success(c, gin.H{
"status": currentStatus,
"status_description": StatusMap[currentStatus],
"message": "所有任务已完成",
"total_tasks": total,
"failed_tasks": len(failedTasks), //失败数量
"success_tasks": len(completedTasks), //成功数量
"success_rate": fmt.Sprintf("%.1f%%", float64(len(completedTasks))/float64(total)*100),
"pending_tasks": pending,
"completed_tasks": completed,
"completed_rate": float64(completed) / float64(total) * 100,
}, "任务完成")
return
}
}
func ImageContentGetResultExcel(c *gin.Context) {
processor := GetBatchProcessorReadOnly()
if processor == nil {
service.Error(c, errors.New("任务未开始或者任务在处理中"))
return
}
currentStatus := processor.getStatus()
switch currentStatus {
case StatusCompleted:
_, _, _, _, failedTasks := processor.getTaskStatistics()
if len(failedTasks) > 0 {
if err := returnExcel(failedTasks, c); err != nil {
service.Error(c, errors.New("生成错误报告失败"))
return
}
return
} else {
service.Error(c, errors.New("没有错误"))
}
case StatusIdle, StatusProcessing:
service.Error(c, errors.New("任务未开始或者任务在处理中"))
return
}
}
func returnExcel(failedTasks []*Task, c *gin.Context) error {
titleList := []string{
"行数", "艺术家", "编号", "错误信息", "任务ID", "开始时间",
}
var dataList []interface{}
for _, task := range failedTasks {
data := []interface{}{
task.Data.LineNum + 1,
task.Data.ArtistName,
task.Data.SubNum,
getErrorMessage(task.Error),
task.TaskId,
task.StartTime.Format("2006-01-02 15:04:05"),
}
dataList = append(dataList, &data)
}
sort.Slice(failedTasks, func(i, j int) bool {
return failedTasks[i].Data.LineNum < failedTasks[j].Data.LineNum
})
content, err := utils.ToExcelByType(titleList, dataList, "slice", "")
if err != nil {
return fmt.Errorf("生成Excel失败: %v", err)
}
utils.ResponseXls(c, content, fmt.Sprintf("失败任务报告_%s.xlsx", time.Now().Format("20060102150405")))
return nil
}

View File

@ -0,0 +1,339 @@
package imports
import (
"errors"
"fmt"
"fonchain-fiee/pkg/config"
"fonchain-fiee/pkg/service"
"fonchain-fiee/pkg/utils"
"io"
"log"
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
"time"
"github.com/fonchain_enterprise/utils/objstorage"
"github.com/xuri/excelize/v2"
"github.com/gin-gonic/gin"
)
const (
fail string = "操作失败"
)
type excelData struct {
ArtistName string //艺人 必须字段
SubNum string //用户编号 必须字段
TikTok string
Instagram string
Youtube string
Desc string //艺人简介
TitleRequire string //标题要求 必须字段
ContentRequire string //内容要求 必须字段
PhotoRequire string //图片要求 必须字段
PhotoUrl string //画作地址
PhotoNum int //图片数量 必须字段
LineNum int
Title string //标题
Content string //内容
MediaAccountUuids []string
MediaAccountNames []string
PhotoDpi string
}
type publishImageReq struct {
ArtistName string //艺人
SubNum string //用户编号
Title string //标题
Content string //内容
TikTok string
Instagram string
GeneratePhotoUrl []string //生成图片地址
MediaAccountUuids []string
MediaAccountNames []string
}
func getErrorMessage(err error) string {
if err == nil {
return ""
}
return err.Error()
}
func ImageContentImport(c *gin.Context) {
processor := GetOrCreateBatchProcessor()
currentStatus := processor.getStatus()
switch currentStatus {
case StatusProcessing: //进行中
service.Error(c, errors.New("当前有任务正在执行中,请先查看执行进度"))
return
case StatusIdle, StatusCompleted: //空闲状态,完成可以执行下一次导入
}
defer func() {
if r := recover(); r != nil {
service.Error(c, errors.New("操作失败"))
}
}()
// 导入excel
excelFile, err := c.FormFile("excel")
if err != nil {
service.Error(c, errors.New("缺少excel文件"))
return
}
// 创建临时文件
tempDir := "tmp"
if err = os.MkdirAll(tempDir, 0755); err != nil {
service.Error(c, errors.New("创建临时目录失败"))
return
}
defer os.RemoveAll(tempDir)
// 保存excel
excelPath := filepath.Join(tempDir, "excel.xlsx")
if err = c.SaveUploadedFile(excelFile, excelPath); err != nil {
service.Error(c, errors.New("保存excel文件失败"))
return
}
// 读取excel
readExcelResult, err := readExcel(excelPath)
if err != nil {
service.Error(c, fmt.Errorf("读取excel失败: %v", err))
return
}
if len(readExcelResult) == 0 {
service.Error(c, errors.New("请检查excel文件"))
return
}
//设置全局状态为进行中
processor.setStatus(StatusProcessing)
//设置请求间隔
qps := 10
interval := time.Second / time.Duration(qps)
for i, v := range readExcelResult {
if i > 0 {
time.Sleep(interval)
}
if err := processor.submitTask(&v); err != nil {
task := &Task{
Data: &v,
TaskId: i,
Error: err,
StartTime: time.Now(),
}
processor.taskIdFindTask[v.LineNum] = task
processor.taskIdFindTaskStatus[v.LineNum] = true
}
}
//开始轮询
processor.startPolling()
service.Success(c, gin.H{
"message": "导入成功",
"total": len(readExcelResult),
})
}
func readExcel(excelPath string) ([]excelData, error) {
//打开excel
f, err := excelize.OpenFile(excelPath)
if err != nil {
return nil, err
}
defer f.Close()
//读取第一页
sheetName := f.GetSheetName(0)
if sheetName == "" {
return nil, errors.New("excel文件中没有工作表")
}
//读取数据
rows, err := f.GetRows(sheetName)
if err != nil {
return nil, fmt.Errorf("读取工作表失败: %v", err)
}
if len(rows) <= 1 {
return nil, errors.New("excel文件没有数据行只有表头或为空")
}
var result []excelData
for i := 1; i < len(rows); i++ { // 从第2行开始跳过表头
row := rows[i]
if len(row) == 0 {
continue
}
artistName := getCellValue(f, sheetName, i, 0)
if artistName == "" {
return nil, fmt.Errorf("第%d行应该有艺人名称", i+1)
}
subNum := getCellValue(f, sheetName, i, 1)
if subNum == "" {
return nil, fmt.Errorf("第%d行应该有编号", i+1)
}
tikTok := getCellValue(f, sheetName, i, 2)
if tikTok == "" {
return nil, fmt.Errorf("第%d行应该有tiktok账号昵称", i+1)
}
instagram := getCellValue(f, sheetName, i, 3)
if instagram == "" {
return nil, fmt.Errorf("第%d行应该有ins账号昵称", i+1)
}
desc := getCellValue(f, sheetName, i, 4)
titleRequire := getCellValue(f, sheetName, i, 5)
if titleRequire == "" {
return nil, fmt.Errorf("第%d行应该有标题要求", i+1)
}
contentRequire := getCellValue(f, sheetName, i, 6)
if contentRequire == "" {
return nil, fmt.Errorf("第%d行应该有内容要求", i+1)
}
photoRequire := getCellValue(f, sheetName, i, 7)
photoUrl := getCellValue(f, sheetName, i, 8)
photoNumStr := getCellValue(f, sheetName, i, 9)
photoDpi := getCellValue(f, sheetName, i, 10)
var num int
if photoUrl == "" { //如果没有关联画作,数量必须有,需求必须有
//需求必须有
if photoRequire == "" {
return nil, fmt.Errorf("第%d行应该有图片需求", i+1)
}
//转换成功
photoNum, err := strconv.Atoi(strings.TrimSpace(photoNumStr))
if err != nil {
return nil, fmt.Errorf("第%d行图片数量格式错误: '%s',必须是整数", i+1, photoNumStr)
}
// 数量大于
if photoNum <= 0 {
return nil, fmt.Errorf("第%d行图片数量必须大于0当前值: %d", i+1, photoNum)
}
num = photoNum
}
data := excelData{
ArtistName: artistName,
SubNum: subNum,
TikTok: tikTok,
Instagram: instagram,
Desc: desc,
TitleRequire: titleRequire,
ContentRequire: contentRequire,
PhotoRequire: photoRequire,
PhotoUrl: photoUrl,
PhotoNum: num,
LineNum: i, //行数
PhotoDpi: photoDpi,
}
result = append(result, data)
}
return result, nil
}
func getCellValue(f *excelize.File, sheetName string, rowIndex, colIndex int) string {
colName, _ := excelize.ColumnNumberToName(colIndex + 1)
cell := fmt.Sprintf("%s%d", colName, rowIndex+1)
value, err := f.GetCellValue(sheetName, cell)
if err != nil {
log.Printf("读取单元格 %s 失败: %v", cell, err)
return ""
}
return strings.TrimSpace(value)
}
func Test1(c *gin.Context) {
// 创建临时目录
tempDir := "tmp"
if err := os.MkdirAll(tempDir, 0755); err != nil {
}
defer os.RemoveAll(tempDir) // 程序结束时清理整个目录
// 生成唯一文件名
fileName := fmt.Sprintf("%d.jpg", time.Now().Unix())
// 构建文件路径
imgPath := filepath.Join(tempDir, fileName)
// 创建文件
file, err := os.Create(imgPath)
if err != nil {
}
defer file.Close()
log.Printf("文件创建在: %s", imgPath)
// 下载图片到文件
resp, err := http.Get("https://e-cdn.fontree.cn/fontree-fiee/tmp/unzipped/9.23-04/邬小明/90_1758873144.jpg")
if err != nil {
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
}
// 复制到文件
_, err = io.Copy(file, resp.Body)
if err != nil {
}
file.Sync()
fileBytes, err := os.ReadFile(imgPath)
if err != nil {
}
// 上传到桶
BOSClient, err := objstorage.NewOSS(
config.ConfigData.Oss.AccessKeyId,
config.ConfigData.Oss.AccessKeySecret,
config.ConfigData.Oss.Endpoint,
)
_, err = BOSClient.PutObjectFromBytes(config.ConfigData.Oss.BucketName, fileName, fileBytes)
if err != nil {
return
}
url := fmt.Sprintf("%s/%s", config.ConfigData.Oss.CdnHost, fileName)
log.Printf("图片上传成功: %s -> %s", fileName, url)
service.Success(c)
}
func Test(c *gin.Context) {
err := publishImage(publishImageReq{
ArtistName: "荣小松",
SubNum: "FE00062",
Title: "test",
Content: "test",
GeneratePhotoUrl: []string{"0221", "2"},
})
if err != nil {
return
}
service.Success(c)
}
func Test2(c *gin.Context) {
titleList := []string{
"1",
}
var dataList []interface{}
data := []string{
"123123",
}
dataList = append(dataList, &data)
content, err := utils.ToExcelByType(titleList, dataList, "slice", "")
if err != nil {
service.Error(c, err)
return
}
utils.ResponseXls(c, content, "1")
}

View File

@ -0,0 +1,746 @@
package imports
import (
"context"
"errors"
"fmt"
"fonchain-fiee/api/accountFiee"
apiCast "fonchain-fiee/api/cast"
"fonchain-fiee/pkg/config"
"fonchain-fiee/pkg/service"
"io"
"log"
"math/rand"
"net/http"
"os"
"path/filepath"
"strconv"
"sync"
"time"
"github.com/fonchain_enterprise/utils/objstorage"
"go.uber.org/zap"
)
type TaskStatus string
const (
TaskPending TaskStatus = "PENDING" //任务排队中
TaskRunning TaskStatus = "RUNNING" //任务处理中
TaskSuccessful TaskStatus = "SUCCESSFUL" //任务执行成功
TaskFailed TaskStatus = "FAILED" //任务执行失败
TaskCancelled TaskStatus = "CANCELLED" //任务已经取消
TaskCanceled TaskStatus = "UNKNOWN" //任务不存在
)
var (
batchProcessor *BatchProcessor
batchProcessorMutex sync.Mutex
)
const (
StatusIdle = 0 // 空闲中(可执行新任务)
StatusProcessing = 1 // 处理中(只能读取进度)
StatusCompleted = 2 // 已完成(可读取结果)
)
var StatusMap = map[int]string{StatusIdle: "空闲中", StatusProcessing: "处理中", StatusCompleted: "已完成"}
type BatchProcessor struct {
mu sync.RWMutex
taskIdFindTask map[int]*Task //IdFind任务
taskIdFindTaskStatus map[int]bool //IdFind任务状态
taskIdFindSubTaskStatus map[int]map[string]TaskStatus //IdFind子任务状态
pollInterval time.Duration //间隔时间
status int //全局实例状态
}
type Task struct {
StartTime time.Time //任务开始时间
EndTime time.Time //任务结束时间
RetryCount int //重试次数todo暂未使用
TaskId int //任务唯一标识
Data *excelData //导入的初始任务数据
Status TaskStatus //统一千文的错误
Error error //任务调度中的报错
//子任务
SubTaskStatus map[string]TaskStatus //子任务-状态
RequiredCount int //子任务-需要成功的数量
SuccessCount int //子任务-成功数量
FailedCount int //子任务-失败数量
//生成的内容
Title string //生成的标题
Content string //生成的内容
Urls []string //生成的地址
}
// GetBatchProcessorReadOnly 获取只读实例
func GetBatchProcessorReadOnly() *BatchProcessor {
return batchProcessor
}
// GetOrCreateBatchProcessor 获取实例
func GetOrCreateBatchProcessor() *BatchProcessor {
batchProcessorMutex.Lock()
defer batchProcessorMutex.Unlock()
if batchProcessor == nil || batchProcessor.status == StatusCompleted {
batchProcessor = &BatchProcessor{
taskIdFindTask: make(map[int]*Task),
taskIdFindTaskStatus: make(map[int]bool),
taskIdFindSubTaskStatus: make(map[int]map[string]TaskStatus),
pollInterval: 100 * time.Millisecond,
status: StatusIdle,
}
}
return batchProcessor
}
// 设置任务状态
func (p *BatchProcessor) setStatus(status int) {
p.mu.Lock()
defer p.mu.Unlock()
p.status = status
}
// GetStatus 获取当前状态
func (p *BatchProcessor) getStatus() int {
p.mu.RLock()
defer p.mu.RUnlock()
return p.status
}
// GetTaskStatistics 获取实例相关信息
func (p *BatchProcessor) getTaskStatistics() (completed, pending, total int, completedTasks, failedTasks []*Task) {
p.mu.RLock()
defer p.mu.RUnlock()
total = len(p.taskIdFindTask)
for _, task := range p.taskIdFindTask {
if p.taskIdFindTaskStatus[task.TaskId] { //是否转换成功
completed++
if task.Status == TaskSuccessful && task.Error == nil { //转换成功 并且 发布成功
completedTasks = append(completedTasks, task)
} else if task.Status == TaskFailed || task.Error != nil { //转换失败 或者 发布失败
failedTasks = append(failedTasks, task)
}
} else {
pending++
}
}
return completed, pending, total, completedTasks, failedTasks
}
func (p *BatchProcessor) recordSubTaskStatus(taskID int, subTaskID string, status TaskStatus) {
if _, exists := p.taskIdFindSubTaskStatus[taskID]; !exists {
p.taskIdFindSubTaskStatus[taskID] = make(map[string]TaskStatus)
}
p.taskIdFindSubTaskStatus[taskID][subTaskID] = status
// 更新主任务状态
if task, exists := p.taskIdFindTask[taskID]; exists {
switch status {
case TaskSuccessful:
task.SuccessCount++
task.SubTaskStatus[subTaskID] = TaskSuccessful
case TaskFailed:
task.FailedCount++
task.SubTaskStatus[subTaskID] = TaskFailed
case TaskPending:
task.SubTaskStatus[subTaskID] = TaskPending
}
// 检查任务完成状态
p.checkTaskCompletion(taskID)
}
}
// 检查任务是否完成
func (p *BatchProcessor) checkTaskCompletion(taskID int) {
task, exists := p.taskIdFindTask[taskID]
if !exists {
return
}
// 检查是否所有子任务都完成
if task.SuccessCount+task.FailedCount >= len(p.taskIdFindSubTaskStatus[taskID]) {
// 子任务都完成,判断成功数量
if task.SuccessCount >= task.RequiredCount {
// 成功数量满足要求
task.Status = TaskSuccessful
zap.L().Info("任务完成", zap.Int("taskID", taskID), zap.String("status", "success"))
} else {
// 成功数量不满足要求,但所有子任务完成
task.Status = TaskFailed
zap.L().Warn("任务部分成功", zap.Int("taskID", taskID), zap.Int("success", task.SuccessCount), zap.Int("required", task.RequiredCount))
}
task.EndTime = time.Now()
p.taskIdFindTaskStatus[taskID] = true
} else {
zap.L().Info("任务正在进行中", zap.Int("taskID", taskID), zap.String("status", "in progress"))
}
}
// IsAllCompleted 获取任务是否全部完成
func (p *BatchProcessor) IsAllCompleted() bool {
p.mu.RLock()
defer p.mu.RUnlock()
if len(p.taskIdFindTaskStatus) == 0 {
return true
}
// 检查是否所有任务都标记为完成
for _, completed := range p.taskIdFindTaskStatus {
if !completed {
return false
}
}
return true
}
// 获取未完成的任务列表
func (p *BatchProcessor) getIncompleteTasks() []int {
p.mu.RLock()
defer p.mu.RUnlock()
var incomplete []int
for taskID, completed := range p.taskIdFindTaskStatus {
if !completed {
incomplete = append(incomplete, taskID)
}
}
return incomplete
}
// 开始轮询
func (p *BatchProcessor) startPolling() {
go func() {
ticker := time.NewTicker(p.pollInterval)
defer ticker.Stop()
for range ticker.C {
if p.IsAllCompleted() {
p.setStatus(StatusCompleted)
zap.L().Info("所有任务已完成,停止轮询")
break
}
for taskId, isAccomplish := range p.taskIdFindTaskStatus { // 遍历 inProgress 中的任务
if !isAccomplish { // 如果任务未完成
for subTaskId, taskIdTaskStates := range p.taskIdFindSubTaskStatus[taskId] { // 遍历该任务的子任务状态
if taskIdTaskStates == TaskPending { // 如果子任务是待处理状态
if err := p.updateTask(taskId, subTaskId); err != nil {
zap.L().Error("批量更新任务状态失败: %v", zap.Error(err))
continue
}
}
}
}
continue
}
}
}()
}
// 提交一个任务
func (p *BatchProcessor) submitTask(req *excelData) error {
// 获取用户信息
list, err := service.AccountFieeProvider.UserList(context.Background(), &accountFiee.UserListRequest{
Name: req.ArtistName,
SubNum: req.SubNum,
})
if err != nil {
return fmt.Errorf("获取用户信息失败: %s", err.Error())
}
if len(list.UserList) == 0 {
return fmt.Errorf("未找到用户信息: %s", req.ArtistName)
}
// 获取用户详细信息
_, err = service.AccountFieeProvider.Info(context.Background(), &accountFiee.InfoRequest{
ID: list.UserList[0].Id,
Domain: "app",
})
if err != nil {
return fmt.Errorf("获取用户详细信息失败: %s", err.Error())
}
// 获取 TikTok 自媒体账号
accountListTikTok, err := service.CastProvider.MediaUserList(context.Background(), &apiCast.MediaUserListReq{
ArtistVal: req.ArtistName,
PlatformID: 1,
Page: 1,
PageSize: 10,
ArtistUuid: strconv.FormatUint(list.UserList[0].Id, 10),
})
if err != nil {
return fmt.Errorf("获取 TikTok 账号失败: %s", err.Error())
}
if accountListTikTok == nil || len(accountListTikTok.Data) == 0 {
return fmt.Errorf("tiktok自媒体账号数量为0")
}
// 查找 TikTok 账号
tiktokFound := false
for _, user := range accountListTikTok.Data {
if user.PlatformUserName == req.TikTok {
req.MediaAccountNames = append(req.MediaAccountNames, user.PlatformUserName)
req.MediaAccountUuids = append(req.MediaAccountUuids, user.MediaAccountUuid)
tiktokFound = true
break
}
}
if !tiktokFound {
return fmt.Errorf("未找到匹配的TikTok账号: %s", req.TikTok)
}
// 获取 Instagram 自媒体账号
accountListIns, err := service.CastProvider.MediaUserList(context.Background(), &apiCast.MediaUserListReq{
ArtistVal: req.ArtistName,
PlatformID: 3,
Page: 1,
PageSize: 10,
ArtistUuid: strconv.FormatUint(list.UserList[0].Id, 10),
})
if err != nil {
return fmt.Errorf("获取 Instagram 账号失败: %s", err.Error())
}
if accountListIns == nil || len(accountListIns.Data) == 0 {
return fmt.Errorf("ins自媒体账号数量为0")
}
// 查找 Instagram 账号
insFound := false
for _, user := range accountListIns.Data {
if user.PlatformUserName == req.Instagram {
req.MediaAccountNames = append(req.MediaAccountNames, user.PlatformUserName)
req.MediaAccountUuids = append(req.MediaAccountUuids, user.MediaAccountUuid)
insFound = true
break
}
}
if !insFound {
return fmt.Errorf("未找到匹配的Instagram账号: %s", req.Instagram)
}
switch {
case req.PhotoUrl == "": // 如果没有提供照片 URL生成标题和内容
// 生成标题和内容
title, content, err := p.generateTitleAndContent(req)
if err != nil {
zap.L().Error("生成标题和内容失败: %v", zap.Error(err))
return fmt.Errorf("生成标题失败")
}
req.Title = title
req.Content = content
// 请求生成图片
taskIds := make([]string, 0, req.PhotoNum)
for i := 0; i < req.PhotoNum; i++ {
taskId, err := p.generateImage(req)
if err != nil {
zap.L().Error("生成图片失败: %v", zap.Error(err))
p.recordSubTaskStatus(req.LineNum, taskId, TaskFailed)
return fmt.Errorf("生成图片失败")
}
taskIds = append(taskIds, taskId)
p.recordSubTaskStatus(req.LineNum, taskId, TaskPending)
}
// 创建并保存任务
task := &Task{
Data: req,
TaskId: req.LineNum,
Status: TaskPending,
Title: title,
Content: content,
StartTime: time.Now(),
SubTaskStatus: make(map[string]TaskStatus),
RequiredCount: req.PhotoNum, // 需要成功的图片数量
SuccessCount: 0,
FailedCount: 0,
}
p.taskIdFindTask[req.LineNum] = task
p.taskIdFindTaskStatus[req.LineNum] = false
case req.PhotoUrl != "": //如果有图片
task := &Task{
Data: req,
TaskId: req.LineNum,
Status: TaskPending,
StartTime: time.Now(),
RequiredCount: 1, // 单张图片只需要成功1次
SuccessCount: 0,
FailedCount: 0,
SubTaskStatus: make(map[string]TaskStatus),
}
p.recordSubTaskStatus(req.LineNum, strconv.Itoa(req.LineNum), TaskPending)
p.taskIdFindTask[req.LineNum] = task
p.taskIdFindTaskStatus[req.LineNum] = false
}
return nil
}
// 更新任务
func (p *BatchProcessor) updateTask(id int, taskId string) (err error) {
p.mu.Lock()
defer p.mu.Unlock()
// 任务不存在直接返回
task, exists := p.taskIdFindTask[id]
if !exists {
return fmt.Errorf("任务ID %d 不存在", id)
}
switch {
case task.Data.PhotoUrl != "": // 如果有图片 URL生成标题和内容并发布
// 生成标题和内容
title, content, err := p.generateTitleAndContent(task.Data)
if err != nil { //生成标题失败
task.Status = TaskFailed
p.taskIdFindTaskStatus[id] = true
task.EndTime = time.Now()
task.Error = fmt.Errorf("生成标题和内容失败: %s", err.Error())
p.recordSubTaskStatus(id, taskId, TaskFailed)
zap.L().Error("生成标题和内容失败: %v", zap.Error(err))
return err
}
task.Title = title
task.Content = content
// 发布内容
if err = publishImage(publishImageReq{
ArtistName: task.Data.ArtistName,
SubNum: task.Data.SubNum,
Title: task.Title,
Content: task.Content,
TikTok: task.Data.TikTok,
Instagram: task.Data.Instagram,
GeneratePhotoUrl: []string{task.Data.PhotoUrl},
MediaAccountUuids: task.Data.MediaAccountUuids,
MediaAccountNames: task.Data.MediaAccountNames,
}); err != nil { //发布失败
task.Status = TaskFailed
p.taskIdFindTaskStatus[id] = true
task.EndTime = time.Now()
task.Error = fmt.Errorf("发布内容失败: %s", err.Error())
zap.L().Error("发布内容失败: %v", zap.Error(err))
p.recordSubTaskStatus(id, taskId, TaskFailed)
return err
}
//操作成功
p.recordSubTaskStatus(id, taskId, TaskSuccessful)
case task.Data.PhotoUrl == "": // 如果没有图片 URL处理图片生成结果
getTaskDetailRes, err := NewAiGenerator().GetTaskDetail(taskId)
if err != nil { //获取图片结果失败
zap.L().Error("查看图片生成结果失败: %v", zap.Error(err))
task.Error = fmt.Errorf("查看图片生成结果失败")
//task.Status = TaskFailed
//p.inProgress[id] = true
//task.EndTime = time.Now()
p.recordSubTaskStatus(id, taskId, TaskFailed)
return err
}
switch getTaskDetailRes.Output.TaskStatus {
case "SUCCEEDED":
if task.Status != TaskSuccessful {
//上传图片
urls := make([]string, len(getTaskDetailRes.Output.Results))
for i, v := range getTaskDetailRes.Output.Results {
urls[i] = v.URL
}
uploadedURLs, err := downloadAndUploadImages(urls)
if err != nil { //图片上传失败
zap.L().Error("图片上传失败: %v", zap.Error(err))
task.Error = fmt.Errorf("图片上传失败")
//task.Status = TaskFailed
//p.inProgress[id] = true
//task.EndTime = time.Now()
p.recordSubTaskStatus(id, taskId, TaskFailed)
return err
}
task.Urls = append(task.Urls, uploadedURLs...)
p.recordSubTaskStatus(id, taskId, TaskSuccessful)
}
case "FAILED": //第三方返回失败
if task.Status != TaskFailed {
zap.L().Error("第三方生成失败: %v", zap.Error(err))
task.Error = fmt.Errorf("生成失败")
//task.Status = TaskFailed
//p.inProgress[id] = true
//task.EndTime = time.Now()
p.recordSubTaskStatus(id, taskId, TaskFailed)
return err
}
}
p.checkTaskCompletion(id)
if p.taskIdFindTaskStatus[id] == true {
// 发布图文
if err = publishImage(publishImageReq{
ArtistName: task.Data.ArtistName,
SubNum: task.Data.SubNum,
Title: task.Title,
Content: task.Content,
TikTok: task.Data.TikTok,
Instagram: task.Data.Instagram,
MediaAccountUuids: task.Data.MediaAccountUuids,
MediaAccountNames: task.Data.MediaAccountNames,
GeneratePhotoUrl: task.Urls,
}); err != nil { //发布失败
zap.L().Error("发布内容失败: %v", zap.Error(err))
task.Error = fmt.Errorf("发布内容失败")
task.Status = TaskFailed
p.taskIdFindTaskStatus[id] = true
task.EndTime = time.Now()
return err
}
//处理成功
task.Status = TaskSuccessful
p.taskIdFindTaskStatus[id] = true
task.EndTime = time.Now()
}
}
return nil
}
// 批量上传图片
func downloadAndUploadImages(urls []string) ([]string, error) {
var uploadedURLs []string
for _, result := range urls {
if result == "" {
continue
}
// 下载并直接上传到桶
bucketURL, err := downloadAndUploadToBucket(result)
if err != nil {
log.Printf("图片上传失败 [%s]: %v", result, err)
continue
}
uploadedURLs = append(uploadedURLs, bucketURL)
log.Printf("图片上传成功: %s -> %s", result, bucketURL)
}
if len(uploadedURLs) == 0 {
return nil, errors.New("所有图片上传失败")
}
return uploadedURLs, nil
}
// 上传图片到桶里面
func downloadAndUploadToBucket(imageURL string) (string, error) {
// 创建临时目录
tempDir := "tmp"
if err := os.MkdirAll(tempDir, 0755); err != nil {
return "", fmt.Errorf("创建临时目录失败: %v", err)
}
defer os.RemoveAll(tempDir) // 程序结束时清理整个目录
rand.Seed(time.Now().UnixNano())
fileName := fmt.Sprintf("%d%04d.jpg", time.Now().Unix(), rand.Intn(10000))
// 构建文件路径
imgPath := filepath.Join(tempDir, fileName)
// 创建文件
file, err := os.Create(imgPath)
if err != nil {
return "", fmt.Errorf("创建文件失败: %v", err)
}
defer file.Close()
log.Printf("文件创建在: %s", imgPath)
// 下载图片到文件
resp, err := http.Get(imageURL)
if err != nil {
return "", fmt.Errorf("下载图片失败: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("下载失败,状态码: %d", resp.StatusCode)
}
// 复制到文件
_, err = io.Copy(file, resp.Body)
if err != nil {
return "", fmt.Errorf("保存文件失败: %v", err)
}
file.Sync()
fileBytes, err := os.ReadFile(imgPath)
if err != nil {
return "", fmt.Errorf("读取本地文件失败: %v", err)
}
BOSClient, err := objstorage.NewOSS(
os.Getenv(config.ConfigData.Oss.AccessKeyId),
os.Getenv(config.ConfigData.Oss.AccessKeySecret),
os.Getenv(config.ConfigData.Oss.Endpoint),
)
if BOSClient == nil {
return "", fmt.Errorf("上传文件失败: %v", err)
}
_, err = BOSClient.PutObjectFromBytes(os.Getenv(config.ConfigData.Oss.BucketName), fileName, fileBytes)
if err != nil {
return "", fmt.Errorf("上传文件失败: %v", err)
}
url := fmt.Sprintf("%s/%s", os.Getenv(config.ConfigData.Oss.CdnHost), fileName)
//上传到桶
//BOSClient, err := objstorage.NewOSS(
// config.ConfigData.Oss.AccessKeyId,
// config.ConfigData.Oss.AccessKeySecret,
// config.ConfigData.Oss.Endpoint,
//)
//if BOSClient == nil {
// return "", fmt.Errorf("上传文件失败: %v", err)
//}
//_, err = BOSClient.PutObjectFromBytes(config.ConfigData.Oss.BucketName, fileName, fileBytes)
//if err != nil {
// return "", fmt.Errorf("上传文件失败: %v", err)
//}
//url := fmt.Sprintf("%s/%s", config.ConfigData.Oss.CdnHost, fileName)
return url, nil
}
func (p *BatchProcessor) generateTitleAndContent(req *excelData) (string, string, error) {
if req.PhotoUrl != "" {
title, content, err := NewAiGenerator().GenerateTitleAndContentFromImage(
req.PhotoUrl,
req.TitleRequire,
req.ContentRequire,
)
if err != nil {
return "", "", fmt.Errorf("图生文失败: %v", err)
}
return title, content, nil
} else {
title, content, err := NewAiGenerator().GenerateTitleAndContentFromText(
req.TitleRequire,
req.ContentRequire,
)
if err != nil {
return "", "", fmt.Errorf("生成内容失败: %v", err)
}
return title, content, nil
}
}
func (p *BatchProcessor) generateImage(req *excelData) (string, error) {
prompt := fmt.Sprintf("请根据以下要求生成内容:%s\n", req.PhotoRequire)
if req.Title != "" {
prompt += fmt.Sprintf("1标题%s\n", req.Title) // 关联标题
}
if req.Content != "" {
prompt += fmt.Sprintf("2内容%s\n", req.Content) // 关联内容
}
if req.Desc != "" {
prompt += fmt.Sprintf("3艺人简介%s艺人简介的优先级要低只依据艺人简介的风格", req.Desc)
}
prompt += "\n请基于标题和内容生成单张图片高质量高分辨率。"
prompt += "\n要求不能出现:低质量、残缺、人物正脸、多余的手指、乱码字符和文字、比例不良,场景以国内场景为主"
if req.PhotoDpi == "" {
req.PhotoDpi = "720*1280"
}
result, err := NewAiGenerator().TextToImage(
prompt,
req.PhotoDpi,
1,
)
if err != nil {
return "", err
}
return result.Output.TaskID, nil
}
//func (p *BatchProcessor) StartPolling() {
// go func() {
// ticker := time.NewTicker(p.pollInterval) // 1秒间隔
// defer ticker.Stop()
//
// // 令牌桶控制每秒最多10个请求
// tokenBucket := make(chan struct{}, 10)
//
// // 每秒补充令牌
// go func() {
// refillTicker := time.NewTicker(time.Second)
// defer refillTicker.Stop()
//
// for {
// select {
// case <-refillTicker.C:
// // 每秒补充到10个令牌
// for i := 0; i < 10-len(tokenBucket); i++ {
// select {
// case tokenBucket <- struct{}{}:
// default:
// // 桶已满,跳过
// }
// }
// }
// }
// }()
//
// for range ticker.C {
// if p.IsAllCompleted() {
// p.SetStatus(StatusCompleted)
// zap.L().Info("所有任务已完成,停止轮询")
// ticker.Stop()
// break
// }
//
// // 获取未完成的任务
// incompleteTasks := p.getIncompleteTasks()
// if len(incompleteTasks) == 0 {
// continue
// }
//
// // 处理当前可用的任务最多10个
// processedCount := 0
// for _, taskID := range incompleteTasks {
// if processedCount >= 10 {
// break // 本秒已达到10个请求限制
// }
//
// select {
// case <-tokenBucket:
// // 获取到令牌,可以发送请求
// processedCount++
// go p.updateTaskWithToken(taskID, tokenBucket)
// default:
// // 没有令牌了,跳过
// break
// }
// }
//
// zap.L().Debug("本轮处理任务数量",
// zap.Int("processed", processedCount),
// zap.Int("remaining", len(incompleteTasks)-processedCount))
// }
// }()
//}
//
//// 使用令牌更新任务状态
//func (p *BatchProcessor) updateTaskWithToken(taskID string, tokenBucket chan struct{}) {
// defer func() {
// // 任务完成后不返还令牌,由定时器统一补充
// }()
//
// if err := p.UpdateTaskStatuses(taskID); err != nil {
// zap.L().Error("更新任务状态失败",
// zap.String("task_id", taskID),
// zap.Error(err))
// }
//}

View File

@ -0,0 +1,107 @@
package imports
import (
"context"
"fmt"
"fonchain-fiee/api/accountFiee"
apiCast "fonchain-fiee/api/cast"
"fonchain-fiee/pkg/service"
"strconv"
)
func publishImage(req publishImageReq) (err error) {
var infoResp *accountFiee.UserInfoResponse
list, err := service.AccountFieeProvider.UserList(context.Background(), &accountFiee.UserListRequest{
Name: req.ArtistName,
SubNum: req.SubNum,
})
if err != nil {
return fmt.Errorf("获取用户信息失败: %s", err.Error())
}
if len(list.UserList) == 0 {
return fmt.Errorf("未找到用户信息: %s", req.ArtistName)
}
if len(list.UserList) > 0 {
infoResp, err = service.AccountFieeProvider.Info(context.Background(), &accountFiee.InfoRequest{
ID: list.UserList[0].Id,
Domain: "app",
})
if err != nil {
return fmt.Errorf("获取用户信息失败: %s", err.Error())
}
}
//-----------------------------------------------------获取自媒体账号
//accountList, err := service.CastProvider.MediaUserList(context.Background(), &apiCast.MediaUserListReq{
// ArtistUuid: strconv.FormatUint(list.UserList[0].Id, 10),
// ArtistVal: req.ArtistName,
// Page: 1,
// PageSize: 10,
//})
//if err != nil {
// return fmt.Errorf("自媒体账号数量获取失败: %s,账号数量:%d", err.Error(), len(accountList.Data))
//}
//if accountList == nil || len(accountList.Data) == 0 {
// return fmt.Errorf("自媒体账号数量为0")
//}
//var mediaAccountUuids []string
//var mediaAccountNames []string
//platformIDs 1 tiktok 2youtube 3ins
//platformIDs := []apiCast.PlatformIDENUM{}
//for _, info := range accountList.Data {
// if info.ArtistName == req.TikTok || info.ArtistName == req.Instagram {
// mediaAccountUuids = append(mediaAccountUuids, info.MediaAccountUuid)
// mediaAccountNames = append(mediaAccountNames, info.PlatformUserName)
// platformIDs = append(platformIDs, apiCast.PlatformIDENUM(info.PlatformID))
// }
//}
//---------------------------------------------------发布
_, err = service.CastProvider.UpdateWorkImage(context.Background(), &apiCast.UpdateWorkImageReq{
Title: req.Title,
Content: req.Content,
Images: req.GeneratePhotoUrl,
MediaAccountUuids: req.MediaAccountUuids,
MediaAccountNames: req.MediaAccountNames,
PlatformIDs: []apiCast.PlatformIDENUM{1, 3},
PublishConfig1: &apiCast.PublishConfig{
CanComment: 1,
CanJoin: 1,
CanQuote: 1,
ForbidComment: 1,
IsAI: 1,
PublicType: 1,
},
PublishConfig2: &apiCast.PublishConfig{
CanComment: 1,
CanJoin: 1,
CanQuote: 1,
ForbidComment: 1,
IsAI: 1,
PublicType: 1,
},
PublishConfig3: &apiCast.PublishConfig{
CanComment: 1,
CanJoin: 1,
CanQuote: 1,
ForbidComment: 1,
IsAI: 1,
PublicType: 1,
},
PublishConfig4: nil,
Action: "submit",
ArtistPhone: infoResp.TelNum,
ArtistUuid: strconv.FormatUint(list.UserList[0].Id, 10),
ArtistName: infoResp.Name,
ArtistPhoneAreaCode: infoResp.TelAreaCode,
WorkUuid: "",
Source: 2,
LineNo: 0,
Remark: "",
Success: false,
})
if err != nil {
return fmt.Errorf("发布"+req.ArtistName+"图文"+"失败: %s", err.Error())
}
return nil
}

View File

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"fonchain-fiee/api/account" "fonchain-fiee/api/account"
"fonchain-fiee/api/accountFiee" "fonchain-fiee/api/accountFiee"
"fonchain-fiee/api/aryshare"
"fonchain-fiee/api/bundle" "fonchain-fiee/api/bundle"
"fonchain-fiee/api/cast" "fonchain-fiee/api/cast"
"fonchain-fiee/api/files" "fonchain-fiee/api/files"
@ -31,6 +32,7 @@ var CastProvider = new(cast.CastClientImpl)
var GovernanceProvider = new(governance.GovernanceClientImpl) var GovernanceProvider = new(governance.GovernanceClientImpl)
var PressReleasesProvider = new(pressreleases.PressReleasesClientImpl) var PressReleasesProvider = new(pressreleases.PressReleasesClientImpl)
var SecFilingProvider = new(secFilings.SecFilingsClientImpl) var SecFilingProvider = new(secFilings.SecFilingsClientImpl)
var AyrshareProvider = new(aryshare.AyrshareClientImpl)
func init() { func init() {
config.SetConsumerService(BundleProvider) config.SetConsumerService(BundleProvider)
@ -43,6 +45,7 @@ func init() {
config.SetConsumerService(GovernanceProvider) config.SetConsumerService(GovernanceProvider)
config.SetConsumerService(PressReleasesProvider) config.SetConsumerService(PressReleasesProvider)
config.SetConsumerService(SecFilingProvider) config.SetConsumerService(SecFilingProvider)
config.SetConsumerService(AyrshareProvider)
if err := config.Load(); err != nil { if err := config.Load(); err != nil {
panic(err) panic(err)

View File

@ -6,6 +6,7 @@ import (
"io" "io"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"net/url"
"time" "time"
"go.uber.org/zap" "go.uber.org/zap"
@ -41,7 +42,7 @@ func Post(url, data string) (string, error) {
func PostBytes(url string, header map[string]interface{}, data []byte) ([]byte, error) { func PostBytes(url string, header map[string]interface{}, data []byte) ([]byte, error) {
req, err := http.NewRequest("POST", url, bytes.NewBuffer(data)) req, err := http.NewRequest("POST", url, bytes.NewBuffer(data))
if err != nil { if err != nil {
return nil, fmt.Errorf("创建请求失败: %v", err) return nil, fmt.Errorf("创建请求失败: %v", "")
} }
for k, v := range header { for k, v := range header {
req.Header.Set(k, fmt.Sprintf("%v", v)) req.Header.Set(k, fmt.Sprintf("%v", v))
@ -49,16 +50,16 @@ func PostBytes(url string, header map[string]interface{}, data []byte) ([]byte,
client := &http.Client{} client := &http.Client{}
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
return nil, fmt.Errorf("请求失败: %v", err) return nil, fmt.Errorf("请求失败: %v", "")
} }
defer resp.Body.Close() defer resp.Body.Close()
body, err := io.ReadAll(resp.Body) body, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, fmt.Errorf("读取响应失败: %v", err) return nil, fmt.Errorf("读取响应失败: %v", "")
} }
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
zap.L().Error("API返回错误", zap.Int("status", resp.StatusCode), zap.String("body", string(body))) zap.L().Error("API返回错误", zap.Int("status", resp.StatusCode), zap.String("body", string(body)))
return nil, fmt.Errorf("API返回错误: status=%d, body=%s", resp.StatusCode, string(body)) return nil, fmt.Errorf("接口返回错误")
} }
return body, nil return body, nil
} }
@ -87,3 +88,48 @@ func GetBytes(url string, header map[string]interface{}) ([]byte, error) {
} }
return body, nil return body, nil
} }
func GetUrl(apiUrl string, headerData map[string]string, proxyURL ...string) (statusCode int, body []byte, err error) {
req, err := http.NewRequest("GET", apiUrl, nil)
if err != nil {
zap.L().Error("Get", zap.Any("url", apiUrl), zap.Error(err))
err = fmt.Errorf("create request failed: %w", err)
return
}
req.Header.Set("Content-Type", "application/json")
if len(headerData) > 0 {
for k, v := range headerData {
req.Header.Set(k, v)
}
}
client := &http.Client{}
// 支持可选的代理参数
if len(proxyURL) > 0 && proxyURL[0] != "" {
proxy, _err := url.Parse(proxyURL[0])
if _err != nil {
err = _err
return
}
transport := &http.Transport{
Proxy: http.ProxyURL(proxy),
}
client.Transport = transport
}
resp, err := client.Do(req)
if err != nil {
zap.L().Error("Get", zap.Any("url", apiUrl), zap.Error(err))
err = fmt.Errorf("send request failed: %w", err)
return
}
defer resp.Body.Close()
body, err = io.ReadAll(resp.Body)
if err != nil {
zap.L().Error("Get", zap.Any("url", apiUrl), zap.Error(err))
err = fmt.Errorf("read response failed: %w", err)
return
}
statusCode = resp.StatusCode
zap.L().Info("Get", zap.Any("url", apiUrl), zap.Any("body", body))
return
}

13
pkg/utils/slice.go Normal file
View File

@ -0,0 +1,13 @@
package utils
func UniqueT[T comparable](list []T) []T {
m := make(map[T]struct{})
result := make([]T, 0, len(list))
for _, v := range list {
if _, exists := m[v]; !exists {
m[v] = struct{}{}
result = append(result, v)
}
}
return result
}