feat(statistic): add event.proto and statistic.proto with 7 RPCs + 2 event RPCs

This commit is contained in:
zerosaturation 2026-06-08 14:28:27 +08:00
parent 3707125549
commit 4212914057
6 changed files with 2327 additions and 2 deletions

View File

@ -0,0 +1,236 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v7.34.0
// source: event.proto
package event
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// 单条事件
type Event struct {
state protoimpl.MessageState `protogen:"open.v1"`
EventId string `protobuf:"bytes,1,opt,name=event_id,json=eventId,proto3" json:"event_id,omitempty"` // 事件 IDUUID客户端生成用于去重
UserId int64 `protobuf:"varint,2,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` // 用户 ID
StarId int64 `protobuf:"varint,3,opt,name=star_id,json=starId,proto3" json:"star_id,omitempty"` // 顶粉星城 ID
EventType string `protobuf:"bytes,4,opt,name=event_type,json=eventType,proto3" json:"event_type,omitempty"` // 事件类型(如 "asset.like", "exhibition.start"
OccurredAt int64 `protobuf:"varint,5,opt,name=occurred_at,json=occurredAt,proto3" json:"occurred_at,omitempty"` // 事件发生时间ms timestamp
ReceivedAt int64 `protobuf:"varint,6,opt,name=received_at,json=receivedAt,proto3" json:"received_at,omitempty"` // 服务端接收时间ms服务端填充
Properties map[string]string `protobuf:"bytes,7,rep,name=properties,proto3" json:"properties,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` // 自定义属性(扁平 key-value
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Event) Reset() {
*x = Event{}
mi := &file_event_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *Event) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Event) ProtoMessage() {}
func (x *Event) ProtoReflect() protoreflect.Message {
mi := &file_event_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Event.ProtoReflect.Descriptor instead.
func (*Event) Descriptor() ([]byte, []int) {
return file_event_proto_rawDescGZIP(), []int{0}
}
func (x *Event) GetEventId() string {
if x != nil {
return x.EventId
}
return ""
}
func (x *Event) GetUserId() int64 {
if x != nil {
return x.UserId
}
return 0
}
func (x *Event) GetStarId() int64 {
if x != nil {
return x.StarId
}
return 0
}
func (x *Event) GetEventType() string {
if x != nil {
return x.EventType
}
return ""
}
func (x *Event) GetOccurredAt() int64 {
if x != nil {
return x.OccurredAt
}
return 0
}
func (x *Event) GetReceivedAt() int64 {
if x != nil {
return x.ReceivedAt
}
return 0
}
func (x *Event) GetProperties() map[string]string {
if x != nil {
return x.Properties
}
return nil
}
// 批量事件请求
type BatchEventRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
Events []*Event `protobuf:"bytes,1,rep,name=events,proto3" json:"events,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *BatchEventRequest) Reset() {
*x = BatchEventRequest{}
mi := &file_event_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *BatchEventRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*BatchEventRequest) ProtoMessage() {}
func (x *BatchEventRequest) ProtoReflect() protoreflect.Message {
mi := &file_event_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use BatchEventRequest.ProtoReflect.Descriptor instead.
func (*BatchEventRequest) Descriptor() ([]byte, []int) {
return file_event_proto_rawDescGZIP(), []int{1}
}
func (x *BatchEventRequest) GetEvents() []*Event {
if x != nil {
return x.Events
}
return nil
}
var File_event_proto protoreflect.FileDescriptor
const file_event_proto_rawDesc = "" +
"\n" +
"\vevent.proto\x12\rtopfans.event\"\xba\x02\n" +
"\x05Event\x12\x19\n" +
"\bevent_id\x18\x01 \x01(\tR\aeventId\x12\x17\n" +
"\auser_id\x18\x02 \x01(\x03R\x06userId\x12\x17\n" +
"\astar_id\x18\x03 \x01(\x03R\x06starId\x12\x1d\n" +
"\n" +
"event_type\x18\x04 \x01(\tR\teventType\x12\x1f\n" +
"\voccurred_at\x18\x05 \x01(\x03R\n" +
"occurredAt\x12\x1f\n" +
"\vreceived_at\x18\x06 \x01(\x03R\n" +
"receivedAt\x12D\n" +
"\n" +
"properties\x18\a \x03(\v2$.topfans.event.Event.PropertiesEntryR\n" +
"properties\x1a=\n" +
"\x0fPropertiesEntry\x12\x10\n" +
"\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
"\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"A\n" +
"\x11BatchEventRequest\x12,\n" +
"\x06events\x18\x01 \x03(\v2\x14.topfans.event.EventR\x06eventsB2Z0github.com/topfans/backend/pkg/proto/event;eventb\x06proto3"
var (
file_event_proto_rawDescOnce sync.Once
file_event_proto_rawDescData []byte
)
func file_event_proto_rawDescGZIP() []byte {
file_event_proto_rawDescOnce.Do(func() {
file_event_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_event_proto_rawDesc), len(file_event_proto_rawDesc)))
})
return file_event_proto_rawDescData
}
var file_event_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
var file_event_proto_goTypes = []any{
(*Event)(nil), // 0: topfans.event.Event
(*BatchEventRequest)(nil), // 1: topfans.event.BatchEventRequest
nil, // 2: topfans.event.Event.PropertiesEntry
}
var file_event_proto_depIdxs = []int32{
2, // 0: topfans.event.Event.properties:type_name -> topfans.event.Event.PropertiesEntry
0, // 1: topfans.event.BatchEventRequest.events:type_name -> topfans.event.Event
2, // [2:2] is the sub-list for method output_type
2, // [2:2] is the sub-list for method input_type
2, // [2:2] is the sub-list for extension type_name
2, // [2:2] is the sub-list for extension extendee
0, // [0:2] is the sub-list for field type_name
}
func init() { file_event_proto_init() }
func file_event_proto_init() {
if File_event_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_event_proto_rawDesc), len(file_event_proto_rawDesc)),
NumEnums: 0,
NumMessages: 3,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_event_proto_goTypes,
DependencyIndexes: file_event_proto_depIdxs,
MessageInfos: file_event_proto_msgTypes,
}.Build()
File_event_proto = out.File
file_event_proto_goTypes = nil
file_event_proto_depIdxs = nil
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,339 @@
// Code generated by protoc-gen-triple. DO NOT EDIT.
//
// Source: statistic.proto
package statistic
import (
"context"
)
import (
"dubbo.apache.org/dubbo-go/v3"
"dubbo.apache.org/dubbo-go/v3/client"
"dubbo.apache.org/dubbo-go/v3/common"
"dubbo.apache.org/dubbo-go/v3/common/constant"
"dubbo.apache.org/dubbo-go/v3/protocol/triple/triple_protocol"
"dubbo.apache.org/dubbo-go/v3/server"
event "github.com/topfans/backend/pkg/proto/event"
)
// This is a compile-time assertion to ensure that this generated file and the Triple package
// are compatible. If you get a compiler error that this constant is not defined, this code was
// generated with a version of Triple newer than the one compiled into your binary. You can fix the
// problem by either regenerating this code with an older version of Triple or updating the Triple
// version compiled into your binary.
const _ = triple_protocol.IsAtLeastVersion0_1_0
const (
// StatisticServiceName is the fully-qualified name of the StatisticService service.
StatisticServiceName = "topfans.statistic.StatisticService"
)
// These constants are the fully-qualified names of the RPCs defined in this package. They're
// exposed at runtime as procedure and as the final two segments of the HTTP route.
//
// Note that these are different from the fully-qualified method names used by
// google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to
// reflection-formatted method names, remove the leading slash and convert the remaining slash to a
// period.
const (
// StatisticServiceGetTodayOverviewProcedure is the fully-qualified name of the StatisticService's GetTodayOverview RPC.
StatisticServiceGetTodayOverviewProcedure = "/topfans.statistic.StatisticService/GetTodayOverview"
// StatisticServiceGet7DayIncomeCurveProcedure is the fully-qualified name of the StatisticService's Get7DayIncomeCurve RPC.
StatisticServiceGet7DayIncomeCurveProcedure = "/topfans.statistic.StatisticService/Get7DayIncomeCurve"
// StatisticServiceGetExhibitionIncomeSummaryProcedure is the fully-qualified name of the StatisticService's GetExhibitionIncomeSummary RPC.
StatisticServiceGetExhibitionIncomeSummaryProcedure = "/topfans.statistic.StatisticService/GetExhibitionIncomeSummary"
// StatisticServiceGetLikeIncomeByLevelProcedure is the fully-qualified name of the StatisticService's GetLikeIncomeByLevel RPC.
StatisticServiceGetLikeIncomeByLevelProcedure = "/topfans.statistic.StatisticService/GetLikeIncomeByLevel"
// StatisticServiceGetTopAssetsByEarningProcedure is the fully-qualified name of the StatisticService's GetTopAssetsByEarning RPC.
StatisticServiceGetTopAssetsByEarningProcedure = "/topfans.statistic.StatisticService/GetTopAssetsByEarning"
// StatisticServiceGetAssetLevelDistributionProcedure is the fully-qualified name of the StatisticService's GetAssetLevelDistribution RPC.
StatisticServiceGetAssetLevelDistributionProcedure = "/topfans.statistic.StatisticService/GetAssetLevelDistribution"
// StatisticServiceGetAssetUpgradeProgressProcedure is the fully-qualified name of the StatisticService's GetAssetUpgradeProgress RPC.
StatisticServiceGetAssetUpgradeProgressProcedure = "/topfans.statistic.StatisticService/GetAssetUpgradeProgress"
// StatisticServiceTrackEventProcedure is the fully-qualified name of the StatisticService's TrackEvent RPC.
StatisticServiceTrackEventProcedure = "/topfans.statistic.StatisticService/TrackEvent"
// StatisticServiceBatchTrackEventProcedure is the fully-qualified name of the StatisticService's BatchTrackEvent RPC.
StatisticServiceBatchTrackEventProcedure = "/topfans.statistic.StatisticService/BatchTrackEvent"
)
var (
_ StatisticService = (*StatisticServiceImpl)(nil)
)
// StatisticService is a client for the topfans.statistic.StatisticService service.
type StatisticService interface {
GetTodayOverview(ctx context.Context, req *GetTodayOverviewRequest, opts ...client.CallOption) (*GetTodayOverviewResponse, error)
Get7DayIncomeCurve(ctx context.Context, req *Get7DayIncomeCurveRequest, opts ...client.CallOption) (*Get7DayIncomeCurveResponse, error)
GetExhibitionIncomeSummary(ctx context.Context, req *GetExhibitionIncomeSummaryRequest, opts ...client.CallOption) (*GetExhibitionIncomeSummaryResponse, error)
GetLikeIncomeByLevel(ctx context.Context, req *GetLikeIncomeByLevelRequest, opts ...client.CallOption) (*GetLikeIncomeByLevelResponse, error)
GetTopAssetsByEarning(ctx context.Context, req *GetTopAssetsByEarningRequest, opts ...client.CallOption) (*GetTopAssetsByEarningResponse, error)
GetAssetLevelDistribution(ctx context.Context, req *GetAssetLevelDistributionRequest, opts ...client.CallOption) (*GetAssetLevelDistributionResponse, error)
GetAssetUpgradeProgress(ctx context.Context, req *GetAssetUpgradeProgressRequest, opts ...client.CallOption) (*GetAssetUpgradeProgressResponse, error)
TrackEvent(ctx context.Context, req *event.Event, opts ...client.CallOption) (*TrackEventResponse, error)
BatchTrackEvent(ctx context.Context, req *event.BatchEventRequest, opts ...client.CallOption) (*TrackEventResponse, error)
}
// NewStatisticService constructs a client for the statistic.StatisticService service.
func NewStatisticService(cli *client.Client, opts ...client.ReferenceOption) (StatisticService, error) {
conn, err := cli.DialWithInfo("topfans.statistic.StatisticService", &StatisticService_ClientInfo, opts...)
if err != nil {
return nil, err
}
return &StatisticServiceImpl{
conn: conn,
}, nil
}
func SetConsumerStatisticService(srv common.RPCService) {
dubbo.SetConsumerServiceWithInfo(srv, &StatisticService_ClientInfo)
}
// StatisticServiceImpl implements StatisticService.
type StatisticServiceImpl struct {
conn *client.Connection
}
func (c *StatisticServiceImpl) GetTodayOverview(ctx context.Context, req *GetTodayOverviewRequest, opts ...client.CallOption) (*GetTodayOverviewResponse, error) {
resp := new(GetTodayOverviewResponse)
if err := c.conn.CallUnary(ctx, []interface{}{req}, resp, "GetTodayOverview", opts...); err != nil {
return nil, err
}
return resp, nil
}
func (c *StatisticServiceImpl) Get7DayIncomeCurve(ctx context.Context, req *Get7DayIncomeCurveRequest, opts ...client.CallOption) (*Get7DayIncomeCurveResponse, error) {
resp := new(Get7DayIncomeCurveResponse)
if err := c.conn.CallUnary(ctx, []interface{}{req}, resp, "Get7DayIncomeCurve", opts...); err != nil {
return nil, err
}
return resp, nil
}
func (c *StatisticServiceImpl) GetExhibitionIncomeSummary(ctx context.Context, req *GetExhibitionIncomeSummaryRequest, opts ...client.CallOption) (*GetExhibitionIncomeSummaryResponse, error) {
resp := new(GetExhibitionIncomeSummaryResponse)
if err := c.conn.CallUnary(ctx, []interface{}{req}, resp, "GetExhibitionIncomeSummary", opts...); err != nil {
return nil, err
}
return resp, nil
}
func (c *StatisticServiceImpl) GetLikeIncomeByLevel(ctx context.Context, req *GetLikeIncomeByLevelRequest, opts ...client.CallOption) (*GetLikeIncomeByLevelResponse, error) {
resp := new(GetLikeIncomeByLevelResponse)
if err := c.conn.CallUnary(ctx, []interface{}{req}, resp, "GetLikeIncomeByLevel", opts...); err != nil {
return nil, err
}
return resp, nil
}
func (c *StatisticServiceImpl) GetTopAssetsByEarning(ctx context.Context, req *GetTopAssetsByEarningRequest, opts ...client.CallOption) (*GetTopAssetsByEarningResponse, error) {
resp := new(GetTopAssetsByEarningResponse)
if err := c.conn.CallUnary(ctx, []interface{}{req}, resp, "GetTopAssetsByEarning", opts...); err != nil {
return nil, err
}
return resp, nil
}
func (c *StatisticServiceImpl) GetAssetLevelDistribution(ctx context.Context, req *GetAssetLevelDistributionRequest, opts ...client.CallOption) (*GetAssetLevelDistributionResponse, error) {
resp := new(GetAssetLevelDistributionResponse)
if err := c.conn.CallUnary(ctx, []interface{}{req}, resp, "GetAssetLevelDistribution", opts...); err != nil {
return nil, err
}
return resp, nil
}
func (c *StatisticServiceImpl) GetAssetUpgradeProgress(ctx context.Context, req *GetAssetUpgradeProgressRequest, opts ...client.CallOption) (*GetAssetUpgradeProgressResponse, error) {
resp := new(GetAssetUpgradeProgressResponse)
if err := c.conn.CallUnary(ctx, []interface{}{req}, resp, "GetAssetUpgradeProgress", opts...); err != nil {
return nil, err
}
return resp, nil
}
func (c *StatisticServiceImpl) TrackEvent(ctx context.Context, req *event.Event, opts ...client.CallOption) (*TrackEventResponse, error) {
resp := new(TrackEventResponse)
if err := c.conn.CallUnary(ctx, []interface{}{req}, resp, "TrackEvent", opts...); err != nil {
return nil, err
}
return resp, nil
}
func (c *StatisticServiceImpl) BatchTrackEvent(ctx context.Context, req *event.BatchEventRequest, opts ...client.CallOption) (*TrackEventResponse, error) {
resp := new(TrackEventResponse)
if err := c.conn.CallUnary(ctx, []interface{}{req}, resp, "BatchTrackEvent", opts...); err != nil {
return nil, err
}
return resp, nil
}
var StatisticService_ClientInfo = client.ClientInfo{
InterfaceName: "topfans.statistic.StatisticService",
MethodNames: []string{"GetTodayOverview", "Get7DayIncomeCurve", "GetExhibitionIncomeSummary", "GetLikeIncomeByLevel", "GetTopAssetsByEarning", "GetAssetLevelDistribution", "GetAssetUpgradeProgress", "TrackEvent", "BatchTrackEvent"},
ConnectionInjectFunc: func(dubboCliRaw interface{}, conn *client.Connection) {
dubboCli := dubboCliRaw.(*StatisticServiceImpl)
dubboCli.conn = conn
},
}
// StatisticServiceHandler is an implementation of the topfans.statistic.StatisticService service.
type StatisticServiceHandler interface {
GetTodayOverview(context.Context, *GetTodayOverviewRequest) (*GetTodayOverviewResponse, error)
Get7DayIncomeCurve(context.Context, *Get7DayIncomeCurveRequest) (*Get7DayIncomeCurveResponse, error)
GetExhibitionIncomeSummary(context.Context, *GetExhibitionIncomeSummaryRequest) (*GetExhibitionIncomeSummaryResponse, error)
GetLikeIncomeByLevel(context.Context, *GetLikeIncomeByLevelRequest) (*GetLikeIncomeByLevelResponse, error)
GetTopAssetsByEarning(context.Context, *GetTopAssetsByEarningRequest) (*GetTopAssetsByEarningResponse, error)
GetAssetLevelDistribution(context.Context, *GetAssetLevelDistributionRequest) (*GetAssetLevelDistributionResponse, error)
GetAssetUpgradeProgress(context.Context, *GetAssetUpgradeProgressRequest) (*GetAssetUpgradeProgressResponse, error)
TrackEvent(context.Context, *event.Event) (*TrackEventResponse, error)
BatchTrackEvent(context.Context, *event.BatchEventRequest) (*TrackEventResponse, error)
}
func RegisterStatisticServiceHandler(srv *server.Server, hdlr StatisticServiceHandler, opts ...server.ServiceOption) error {
return srv.Register(hdlr, &StatisticService_ServiceInfo, opts...)
}
func SetProviderStatisticService(srv common.RPCService) {
dubbo.SetProviderServiceWithInfo(srv, &StatisticService_ServiceInfo)
}
var StatisticService_ServiceInfo = server.ServiceInfo{
InterfaceName: "topfans.statistic.StatisticService",
ServiceType: (*StatisticServiceHandler)(nil),
Methods: []server.MethodInfo{
{
Name: "GetTodayOverview",
Type: constant.CallUnary,
ReqInitFunc: func() interface{} {
return new(GetTodayOverviewRequest)
},
MethodFunc: func(ctx context.Context, args []interface{}, handler interface{}) (interface{}, error) {
req := args[0].(*GetTodayOverviewRequest)
res, err := handler.(StatisticServiceHandler).GetTodayOverview(ctx, req)
if err != nil {
return nil, err
}
return triple_protocol.NewResponse(res), nil
},
},
{
Name: "Get7DayIncomeCurve",
Type: constant.CallUnary,
ReqInitFunc: func() interface{} {
return new(Get7DayIncomeCurveRequest)
},
MethodFunc: func(ctx context.Context, args []interface{}, handler interface{}) (interface{}, error) {
req := args[0].(*Get7DayIncomeCurveRequest)
res, err := handler.(StatisticServiceHandler).Get7DayIncomeCurve(ctx, req)
if err != nil {
return nil, err
}
return triple_protocol.NewResponse(res), nil
},
},
{
Name: "GetExhibitionIncomeSummary",
Type: constant.CallUnary,
ReqInitFunc: func() interface{} {
return new(GetExhibitionIncomeSummaryRequest)
},
MethodFunc: func(ctx context.Context, args []interface{}, handler interface{}) (interface{}, error) {
req := args[0].(*GetExhibitionIncomeSummaryRequest)
res, err := handler.(StatisticServiceHandler).GetExhibitionIncomeSummary(ctx, req)
if err != nil {
return nil, err
}
return triple_protocol.NewResponse(res), nil
},
},
{
Name: "GetLikeIncomeByLevel",
Type: constant.CallUnary,
ReqInitFunc: func() interface{} {
return new(GetLikeIncomeByLevelRequest)
},
MethodFunc: func(ctx context.Context, args []interface{}, handler interface{}) (interface{}, error) {
req := args[0].(*GetLikeIncomeByLevelRequest)
res, err := handler.(StatisticServiceHandler).GetLikeIncomeByLevel(ctx, req)
if err != nil {
return nil, err
}
return triple_protocol.NewResponse(res), nil
},
},
{
Name: "GetTopAssetsByEarning",
Type: constant.CallUnary,
ReqInitFunc: func() interface{} {
return new(GetTopAssetsByEarningRequest)
},
MethodFunc: func(ctx context.Context, args []interface{}, handler interface{}) (interface{}, error) {
req := args[0].(*GetTopAssetsByEarningRequest)
res, err := handler.(StatisticServiceHandler).GetTopAssetsByEarning(ctx, req)
if err != nil {
return nil, err
}
return triple_protocol.NewResponse(res), nil
},
},
{
Name: "GetAssetLevelDistribution",
Type: constant.CallUnary,
ReqInitFunc: func() interface{} {
return new(GetAssetLevelDistributionRequest)
},
MethodFunc: func(ctx context.Context, args []interface{}, handler interface{}) (interface{}, error) {
req := args[0].(*GetAssetLevelDistributionRequest)
res, err := handler.(StatisticServiceHandler).GetAssetLevelDistribution(ctx, req)
if err != nil {
return nil, err
}
return triple_protocol.NewResponse(res), nil
},
},
{
Name: "GetAssetUpgradeProgress",
Type: constant.CallUnary,
ReqInitFunc: func() interface{} {
return new(GetAssetUpgradeProgressRequest)
},
MethodFunc: func(ctx context.Context, args []interface{}, handler interface{}) (interface{}, error) {
req := args[0].(*GetAssetUpgradeProgressRequest)
res, err := handler.(StatisticServiceHandler).GetAssetUpgradeProgress(ctx, req)
if err != nil {
return nil, err
}
return triple_protocol.NewResponse(res), nil
},
},
{
Name: "TrackEvent",
Type: constant.CallUnary,
ReqInitFunc: func() interface{} {
return new(event.Event)
},
MethodFunc: func(ctx context.Context, args []interface{}, handler interface{}) (interface{}, error) {
req := args[0].(*event.Event)
res, err := handler.(StatisticServiceHandler).TrackEvent(ctx, req)
if err != nil {
return nil, err
}
return triple_protocol.NewResponse(res), nil
},
},
{
Name: "BatchTrackEvent",
Type: constant.CallUnary,
ReqInitFunc: func() interface{} {
return new(event.BatchEventRequest)
},
MethodFunc: func(ctx context.Context, args []interface{}, handler interface{}) (interface{}, error) {
req := args[0].(*event.BatchEventRequest)
res, err := handler.(StatisticServiceHandler).BatchTrackEvent(ctx, req)
if err != nil {
return nil, err
}
return triple_protocol.NewResponse(res), nil
},
},
},
}

25
backend/proto/event.proto Normal file
View File

@ -0,0 +1,25 @@
syntax = "proto3";
package topfans.event;
option go_package = "github.com/topfans/backend/pkg/proto/event;event";
// ==================== ====================
// proto socialServiceassetService
// event.proto statistic.proto
//
message Event {
string event_id = 1; // IDUUID
int64 user_id = 2; // ID
int64 star_id = 3; // ID
string event_type = 4; // "asset.like", "exhibition.start"
int64 occurred_at = 5; // ms timestamp
int64 received_at = 6; // ms
map<string, string> properties = 7; // key-value
}
//
message BatchEventRequest {
repeated Event events = 1;
}

View File

@ -0,0 +1,146 @@
syntax = "proto3";
package topfans.statistic;
option go_package = "github.com/topfans/backend/pkg/proto/statistic;statistic";
import "proto/event.proto";
// ==================== StatisticService ====================
// 7 RPC gateway + 2 RPC
// Go mobile_provider.go / internal_provider.go
service StatisticService {
// ============ 7 RPC gateway ============
rpc GetTodayOverview(GetTodayOverviewRequest) returns (GetTodayOverviewResponse);
rpc Get7DayIncomeCurve(Get7DayIncomeCurveRequest) returns (Get7DayIncomeCurveResponse);
rpc GetExhibitionIncomeSummary(GetExhibitionIncomeSummaryRequest) returns (GetExhibitionIncomeSummaryResponse);
rpc GetLikeIncomeByLevel(GetLikeIncomeByLevelRequest) returns (GetLikeIncomeByLevelResponse);
rpc GetTopAssetsByEarning(GetTopAssetsByEarningRequest) returns (GetTopAssetsByEarningResponse);
rpc GetAssetLevelDistribution(GetAssetLevelDistributionRequest) returns (GetAssetLevelDistributionResponse);
rpc GetAssetUpgradeProgress(GetAssetUpgradeProgressRequest) returns (GetAssetUpgradeProgressResponse);
// ============ RPC ============
rpc TrackEvent(topfans.event.Event) returns (TrackEventResponse);
rpc BatchTrackEvent(topfans.event.BatchEventRequest) returns (TrackEventResponse);
}
// ====== 1. ======
message GetTodayOverviewRequest {
int64 star_id = 1;
}
message GetTodayOverviewResponse {
int64 crystal_balance = 1;
int64 today_income = 2;
int32 week_rank = 3; //
int32 week_total_users = 4; // "击败 X%"
}
// ====== 2. 线 ======
message Get7DayIncomeCurveRequest {
int64 star_id = 1;
}
message DailyIncomePoint {
string date = 1; // "YYYY-MM-DD"
int64 income = 2;
bool is_today = 3;
bool is_peak = 4;
}
message Get7DayIncomeCurveResponse {
repeated DailyIncomePoint points = 1;
int64 total_income = 2;
int64 avg_income = 3;
}
// ====== 3. ======
message GetExhibitionIncomeSummaryRequest {
int64 star_id = 1;
}
message TopExhibitionItem {
int64 asset_id = 1;
string asset_name = 2;
string asset_thumb = 3;
string duration_7d = 4; // 7 "D:HH:MM:SS"
int64 earnings_7d = 5;
int32 avg_earnings = 6;
}
message GetExhibitionIncomeSummaryResponse {
int32 exhibiting_count = 1; //
int32 starbook_count = 2; //
string total_duration = 3; //
int64 total_earnings = 4; //
repeated TopExhibitionItem top5 = 5;
}
// ====== 4. ======
message GetLikeIncomeByLevelRequest {
int64 star_id = 1;
}
message LikeIncomeLevelItem {
string level = 1; // N/R/SR/SSR/UR
int32 asset_count = 2;
int64 total_income = 3;
string thumb = 4; //
}
message GetLikeIncomeByLevelResponse {
int64 total_like_count = 1;
int64 total_income = 2;
repeated LikeIncomeLevelItem levels = 3;
}
// ====== 5. TOP5 ======
message GetTopAssetsByEarningRequest {
int64 star_id = 1;
}
message TopAssetItem {
int64 asset_id = 1;
string asset_name = 2;
string asset_thumb = 3;
int64 total_earnings = 4;
int32 rank = 5;
}
message GetTopAssetsByEarningResponse {
repeated TopAssetItem items = 1;
}
// ====== 6. ======
message GetAssetLevelDistributionRequest {
int64 star_id = 1;
}
message AssetLevelItem {
string level = 1; //
int32 count = 2; //
int32 total = 3; //
}
message GetAssetLevelDistributionResponse {
repeated AssetLevelItem items = 1;
}
// ====== 7. ======
message GetAssetUpgradeProgressRequest {
int64 star_id = 1;
}
message UpcomingLevelUpItem {
int64 asset_id = 1;
string asset_name = 2;
string asset_thumb = 3;
int32 like_progress = 4; // 0-100
int32 duration_progress = 5; // 0-100
}
message RecentLevelUpItem {
int64 asset_id = 1;
string asset_name = 2;
string asset_thumb = 3;
string new_level = 4;
int64 upgrade_time = 5; // ms timestamp
}
message GetAssetUpgradeProgressResponse {
repeated UpcomingLevelUpItem upcoming = 1;
repeated RecentLevelUpItem recent = 2;
}
// ====== ======
message TrackEventResponse {
int32 accepted = 1; // channel
int32 rejected = 2; //
}

View File

@ -67,7 +67,7 @@ move_triple_files() {
# 预先创建目标目录 # 预先创建目标目录
echo "📁 创建目标目录..." echo "📁 创建目标目录..."
for name in common user social asset gallery ranking activity task starbook ai_chat; do for name in common user social asset gallery ranking activity task starbook ai_chat event statistic; do
mkdir -p "pkg/proto/$name" mkdir -p "pkg/proto/$name"
done done
echo "" echo ""
@ -208,6 +208,75 @@ move_triple_files "topfans/backend/pkg/proto/ai_chat" "pkg/proto/ai_chat"
echo "✅ ai_chat.proto 编译完成" echo "✅ ai_chat.proto 编译完成"
echo "" echo ""
# 编译 event.proto仅生成 pb.goevent.proto 没有 service生成 triple.go 会导致未使用 import 编译失败)
echo "📦 编译 event.proto ..."
protoc --proto_path=proto \
--proto_path=. \
--go_out=pkg/proto/event \
--go_opt=paths=source_relative \
event.proto
echo "✅ event.proto 编译完成"
echo ""
# 编译 statistic.proto
echo "📦 编译 statistic.proto ..."
protoc --proto_path=proto \
--proto_path=. \
--go_out=pkg/proto/statistic \
--go_opt=paths=source_relative \
--go-triple_out=pkg/proto/statistic \
--go-triple_opt=paths=source_relative \
statistic.proto
move_triple_files "topfans/backend/pkg/proto/statistic" "pkg/proto/statistic"
# 后处理protoc-gen-go-triple v3.0.0 在跨包类型作 RPC 参数时丢包前缀。
# statistic.proto 引用了 event.Event / event.BatchEventRequest
# triple 生成器把它们当作 statistic 包内类型new(Event) / *Event
# 而未导入 event 包,导致编译失败。这里补回 event 包导入和类型前缀。
TRIPLE_FILE="pkg/proto/statistic/statistic.triple.go"
if [ -f "$TRIPLE_FILE" ]; then
if ! grep -q 'github.com/topfans/backend/pkg/proto/event"' "$TRIPLE_FILE"; then
# 在第二个 import 块dubbo 块)的最末 import 行后追加 event 包导入
# 用 awk 顺序扫描,找到第二个 "import (" 之后第一个 ")" 之前的最后一行
python3 -c "
import re, sys
with open('$TRIPLE_FILE', 'r') as f:
src = f.read()
# 找所有 import 块,定位第二个 import ( ... )
pattern = re.compile(r'(import\s*\([^)]*\))', re.MULTILINE)
blocks = list(pattern.finditer(src))
if len(blocks) >= 2:
second = blocks[1]
block_text = second.group(1)
if 'pkg/proto/event' not in block_text:
new_block = block_text[:-1].rstrip() + '\n\tevent \"github.com/topfans/backend/pkg/proto/event\"\n)'
src = src[:second.start()] + new_block + src[second.end():]
with open('$TRIPLE_FILE', 'w') as f:
f.write(src)
"
# 把 RPC 方法签名 / new() / 类型断言中的裸 Event / BatchEventRequest 加上 event. 前缀
# 注意BSD sedmacOS不支持 \b改用 ([,)]) 等显式边界
sed -i '' \
-e 's/\*Event\([,)]\)/\*event.Event\1/g' \
-e 's/\*Event$/&XXX/g' \
-e 's/\*Event[^a-zA-Z._)/]/\*event.Event\&/g' \
-e 's/new(Event)/new(event.Event)/g' \
-e 's/\*BatchEventRequest\([,)]\)/\*event.BatchEventRequest\1/g' \
-e 's/new(BatchEventRequest)/new(event.BatchEventRequest)/g' \
"$TRIPLE_FILE"
# 修复 args[0].(*Event) 类型断言 — 用单独的 sed 处理方括号
sed -i '' \
-e 's/(\*Event)/(\*event.Event)/g' \
-e 's/(\*BatchEventRequest)/(\*event.BatchEventRequest)/g' \
"$TRIPLE_FILE"
echo " ✅ statistic.triple.go 已修复跨包类型引用protoc-gen-go-triple v3.0.0 bug"
fi
fi
echo "✅ statistic.proto 编译完成"
echo ""
# 清理可能存在的冗余目录和文件 # 清理可能存在的冗余目录和文件
echo "🔄 清理冗余文件..." echo "🔄 清理冗余文件..."
@ -218,7 +287,7 @@ if [ -d "github.com" ]; then
fi fi
# 删除 proto 目录下的生成文件(如果存在) # 删除 proto 目录下的生成文件(如果存在)
for name in common user social asset gallery ranking activity task starbook ai_chat; do for name in common user social asset gallery ranking activity task starbook ai_chat event statistic; do
if [ -f "proto/$name.pb.go" ]; then if [ -f "proto/$name.pb.go" ]; then
rm "proto/$name.pb.go" rm "proto/$name.pb.go"
echo " ✅ proto/$name.pb.go 已清理" echo " ✅ proto/$name.pb.go 已清理"