Compare commits
No commits in common. "11d2388449e87229f2c3df25b727292512486c91" and "a7fb47579df66d3d6ccff1331927bba61dc9c71f" have entirely different histories.
11d2388449
...
a7fb47579d
@ -19,7 +19,6 @@
|
||||
- 腾讯云 IM 负责客户端长连接、房间群消息、公屏、单聊、离线/漫游和全球接入;
|
||||
- `wallet-service` 负责账务语义。`SendGift` 必须先扣费成功,再落房间表现。
|
||||
- `activity-service` 通过 room outbox 事件消费,不侵入 Room Cell 主链路。
|
||||
- `activity-service/internal/service/message` 是 App 系统/活动消息 inbox 的唯一业务 owner;活动发通知必须走 `CreateSystemNotice`、`CreateActivityNotice` 或 `CreateNoticeFanout`,后台和其他服务不能直接写 `user_inbox_messages` / `message_fanout_jobs`。
|
||||
- `notice-service` 负责从各 owner service 的 outbox/事件源读取事实并投递私有通知、腾讯云 IM 单聊、后续 push/inbox;钱包、活动和房间不能直接耦合私有通知投递实现。
|
||||
- `user-service` 负责用户主数据,不应该成为房间高频流程的同步中心。
|
||||
- `cron-service` 只负责后台调度、任务运行记录和通过内部 gRPC 触发 owner service 批处理;不能直接拥有房间、账务、用户或活动业务状态。
|
||||
@ -71,7 +70,6 @@
|
||||
- 修改 `api/proto` 后必须运行 `make proto`,并提交生成后的 `.pb.go` / `_grpc.pb.go`。
|
||||
- 不要在服务之间直接 import 对方 `internal` 包。跨服务只走 protobuf client 或明确的接口抽象。
|
||||
- `server/admin` 不能 import `hyapp/services/*` 或 `hyapp/pkg/*`;后台专属基础设施放在 `server/admin/internal/platform`。
|
||||
- 系统/活动消息协议只使用后端 inbox 的 `message_type=system|activity`、`title`、`summary`、`body`、`action_type`、`action_param`、`metadata_json` 等字段;后台全服通知必须创建 fanout job,不能在 admin HTTP handler 中同步遍历用户发消息。
|
||||
|
||||
## Port Rules
|
||||
|
||||
|
||||
@ -27,13 +27,6 @@ HY Voice Room 是语音房后端 monorepo。当前架构不再自研 IM 长连
|
||||
- 下游消费 owner outbox 必须使用本服务独立 RocketMQ consumer group,并在本服务库内用幂等表或唯一键记录已消费 `event_id`;消费位点不能写回或复用 owner service 的 outbox 状态。
|
||||
- 后台统计查询面:`server/admin` 调 `statistics-service` 内部 HTTP API;`gateway-service` 是 App 入口,除非有明确 App 产品需求,否则不暴露后台数据大屏统计。
|
||||
|
||||
App 系统/活动消息:
|
||||
|
||||
- Flutter 消息页固定展示腾讯云 IM 用户消息、后端系统消息和后端活动消息三个入口;系统/活动页通过 `gateway-service` 读取 `activity-service` 的 inbox,只按当前客户端能力展示标题、摘要/正文和时间。
|
||||
- `activity-service/internal/service/message` 是系统/活动消息 owner。单人消息使用 `CreateSystemNotice` / `CreateActivityNotice`,全服或范围通知使用 `CreateNoticeFanout` 创建 `message_fanout_jobs`,由 cron 触发 fanout worker 分批写入 `user_inbox_messages`。
|
||||
- 活动业务发通知时传 `producer_event_id` 或 `command_id` 作为幂等键,`message_type` 只允许 `system` 或 `activity`;跳转参数使用已有 `action_type` 白名单,例如 `activity_detail`、`room_detail`、`user_profile`、`app_h5`。
|
||||
- `server/admin` 的运营管理/全服通知只通过 `MessageInboxService.CreateFanoutJob` 创建后台 fanout 命令,不直接写 `hyapp_activity` 表,也不在 HTTP 请求里同步遍历用户。
|
||||
|
||||
## Time Model
|
||||
|
||||
项目全局时间模型固定为 UTC + epoch milliseconds。除客户端展示外,App 后端所有按天、按周、按月归档或结算的业务口径都按 UTC,不按中国时区、服务器本地时区、用户设备时区或 IP 推断时区切分。
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -2042,254 +2042,6 @@ message ListWeeklyStarHistoryResponse {
|
||||
int64 server_time_ms = 2;
|
||||
}
|
||||
|
||||
// CPWeeklyRankReward 是 CP 周榜名次对应的资源组奖励。
|
||||
message CPWeeklyRankReward {
|
||||
int32 rank_no = 1;
|
||||
int64 resource_group_id = 2;
|
||||
}
|
||||
|
||||
// CPWeeklyRankConfig 是 CP 周榜的全局启停和 Top 奖励配置。
|
||||
message CPWeeklyRankConfig {
|
||||
string activity_code = 1;
|
||||
bool enabled = 2;
|
||||
string relation_type = 3;
|
||||
int32 top_count = 4;
|
||||
repeated CPWeeklyRankReward rewards = 5;
|
||||
int64 updated_by_admin_id = 6;
|
||||
int64 created_at_ms = 7;
|
||||
int64 updated_at_ms = 8;
|
||||
string app_code = 9;
|
||||
}
|
||||
|
||||
// CPWeeklyRankUser 是 H5 周榜展示的一方用户快照。
|
||||
message CPWeeklyRankUser {
|
||||
int64 user_id = 1;
|
||||
string display_user_id = 2;
|
||||
string username = 3;
|
||||
string avatar = 4;
|
||||
}
|
||||
|
||||
// CPWeeklyRankEntry 是 activity-service 从 user-service 读取并返回 H5 的周榜条目。
|
||||
message CPWeeklyRankEntry {
|
||||
int64 rank = 1;
|
||||
string relationship_id = 2;
|
||||
string relation_type = 3;
|
||||
int64 score = 4;
|
||||
CPWeeklyRankUser user_a = 5;
|
||||
CPWeeklyRankUser user_b = 6;
|
||||
int64 formed_at_ms = 7;
|
||||
int64 updated_at_ms = 8;
|
||||
int64 first_scored_at_ms = 9;
|
||||
int64 last_scored_at_ms = 10;
|
||||
}
|
||||
|
||||
// CPWeeklyRankSettlement 是每周 cron 固化的一条用户发奖事实。
|
||||
message CPWeeklyRankSettlement {
|
||||
string settlement_id = 1;
|
||||
int64 period_start_ms = 2;
|
||||
int64 period_end_ms = 3;
|
||||
int32 rank_no = 4;
|
||||
string relationship_id = 5;
|
||||
int64 user_id = 6;
|
||||
int64 score = 7;
|
||||
int64 resource_group_id = 8;
|
||||
string wallet_command_id = 9;
|
||||
string wallet_grant_id = 10;
|
||||
string status = 11;
|
||||
string failure_reason = 12;
|
||||
int32 attempt_count = 13;
|
||||
int64 created_at_ms = 14;
|
||||
int64 updated_at_ms = 15;
|
||||
}
|
||||
|
||||
message GetCPWeeklyRankStatusRequest {
|
||||
RequestMeta meta = 1;
|
||||
int64 now_ms = 2;
|
||||
int32 limit = 3;
|
||||
}
|
||||
|
||||
message GetCPWeeklyRankStatusResponse {
|
||||
CPWeeklyRankConfig config = 1;
|
||||
repeated CPWeeklyRankEntry entries = 2;
|
||||
int64 period_start_ms = 3;
|
||||
int64 period_end_ms = 4;
|
||||
int64 server_time_ms = 5;
|
||||
}
|
||||
|
||||
message GetCPWeeklyRankConfigRequest {
|
||||
RequestMeta meta = 1;
|
||||
}
|
||||
|
||||
message GetCPWeeklyRankConfigResponse {
|
||||
CPWeeklyRankConfig config = 1;
|
||||
}
|
||||
|
||||
message UpdateCPWeeklyRankConfigRequest {
|
||||
RequestMeta meta = 1;
|
||||
CPWeeklyRankConfig config = 2;
|
||||
int64 operator_admin_id = 3;
|
||||
}
|
||||
|
||||
message UpdateCPWeeklyRankConfigResponse {
|
||||
CPWeeklyRankConfig config = 1;
|
||||
}
|
||||
|
||||
message ListCPWeeklyRankSettlementsRequest {
|
||||
RequestMeta meta = 1;
|
||||
int64 period_start_ms = 2;
|
||||
int64 period_end_ms = 3;
|
||||
}
|
||||
|
||||
message ListCPWeeklyRankSettlementsResponse {
|
||||
repeated CPWeeklyRankSettlement settlements = 1;
|
||||
}
|
||||
|
||||
// AgencyOpeningReward 是代理开业活动房间流水档位对应的金币奖励。
|
||||
message AgencyOpeningReward {
|
||||
// rank_no 保留为档位展示顺序和旧字段兼容,结算不再按榜单名次发奖。
|
||||
int32 rank_no = 1;
|
||||
int64 reward_coin_amount = 2;
|
||||
int64 threshold_coin_spent = 3;
|
||||
}
|
||||
|
||||
// AgencyOpeningCycle 是后台配置的代理开业活动周期。
|
||||
message AgencyOpeningCycle {
|
||||
string cycle_id = 1;
|
||||
string activity_code = 2;
|
||||
string title = 3;
|
||||
string status = 4;
|
||||
int64 start_ms = 5;
|
||||
int64 end_ms = 6;
|
||||
int32 min_host_count = 7;
|
||||
int32 max_agency_age_days = 8;
|
||||
repeated AgencyOpeningReward rewards = 9;
|
||||
int64 created_by_admin_id = 10;
|
||||
int64 updated_by_admin_id = 11;
|
||||
int64 settled_at_ms = 12;
|
||||
int64 created_at_ms = 13;
|
||||
int64 updated_at_ms = 14;
|
||||
string app_code = 15;
|
||||
}
|
||||
|
||||
// AgencyOpeningApplication 是代理提交开业活动申请后的流水和发奖事实。
|
||||
message AgencyOpeningApplication {
|
||||
string application_id = 1;
|
||||
string cycle_id = 2;
|
||||
int64 agency_id = 3;
|
||||
int64 agency_owner_user_id = 4;
|
||||
string agency_name = 5;
|
||||
int32 host_count = 6;
|
||||
int64 agency_created_at_ms = 7;
|
||||
string status = 8;
|
||||
int64 score_coin_amount = 9;
|
||||
int32 reward_rank_no = 10;
|
||||
int64 reward_coin_amount = 11;
|
||||
string wallet_command_id = 12;
|
||||
string wallet_transaction_id = 13;
|
||||
string failure_reason = 14;
|
||||
int64 applied_at_ms = 15;
|
||||
int64 granted_at_ms = 16;
|
||||
int64 updated_at_ms = 17;
|
||||
int64 reward_threshold_coin_spent = 18;
|
||||
}
|
||||
|
||||
// AgencyOpeningAgencySnapshot 是申请页展示和资格判断使用的代理快照。
|
||||
message AgencyOpeningAgencySnapshot {
|
||||
int64 agency_id = 1;
|
||||
int64 owner_user_id = 2;
|
||||
string name = 3;
|
||||
string status = 4;
|
||||
int32 host_count = 5;
|
||||
int64 agency_created_at_ms = 6;
|
||||
}
|
||||
|
||||
message ListAgencyOpeningCyclesRequest {
|
||||
RequestMeta meta = 1;
|
||||
string status = 2;
|
||||
int64 start_ms = 3;
|
||||
int64 end_ms = 4;
|
||||
int32 page = 5;
|
||||
int32 page_size = 6;
|
||||
}
|
||||
|
||||
message ListAgencyOpeningCyclesResponse {
|
||||
repeated AgencyOpeningCycle cycles = 1;
|
||||
int64 total = 2;
|
||||
}
|
||||
|
||||
message GetAgencyOpeningCycleRequest {
|
||||
RequestMeta meta = 1;
|
||||
string cycle_id = 2;
|
||||
}
|
||||
|
||||
message GetAgencyOpeningCycleResponse {
|
||||
AgencyOpeningCycle cycle = 1;
|
||||
}
|
||||
|
||||
message UpsertAgencyOpeningCycleRequest {
|
||||
RequestMeta meta = 1;
|
||||
AgencyOpeningCycle cycle = 2;
|
||||
int64 operator_admin_id = 3;
|
||||
}
|
||||
|
||||
message UpsertAgencyOpeningCycleResponse {
|
||||
AgencyOpeningCycle cycle = 1;
|
||||
bool created = 2;
|
||||
}
|
||||
|
||||
message SetAgencyOpeningCycleStatusRequest {
|
||||
RequestMeta meta = 1;
|
||||
string cycle_id = 2;
|
||||
string status = 3;
|
||||
int64 operator_admin_id = 4;
|
||||
}
|
||||
|
||||
message SetAgencyOpeningCycleStatusResponse {
|
||||
AgencyOpeningCycle cycle = 1;
|
||||
}
|
||||
|
||||
message ListAgencyOpeningApplicationsRequest {
|
||||
RequestMeta meta = 1;
|
||||
string cycle_id = 2;
|
||||
string status = 3;
|
||||
int64 agency_id = 4;
|
||||
int64 agency_owner_user_id = 5;
|
||||
int32 page = 6;
|
||||
int32 page_size = 7;
|
||||
}
|
||||
|
||||
message ListAgencyOpeningApplicationsResponse {
|
||||
repeated AgencyOpeningApplication applications = 1;
|
||||
int64 total = 2;
|
||||
}
|
||||
|
||||
message GetAgencyOpeningStatusRequest {
|
||||
RequestMeta meta = 1;
|
||||
int64 user_id = 2;
|
||||
}
|
||||
|
||||
message GetAgencyOpeningStatusResponse {
|
||||
AgencyOpeningCycle cycle = 1;
|
||||
AgencyOpeningApplication application = 2;
|
||||
bool enabled = 3;
|
||||
bool eligible = 4;
|
||||
bool joined = 5;
|
||||
string ineligible_reason = 6;
|
||||
int64 server_time_ms = 7;
|
||||
AgencyOpeningAgencySnapshot agency = 8;
|
||||
}
|
||||
|
||||
message ApplyAgencyOpeningRequest {
|
||||
RequestMeta meta = 1;
|
||||
int64 user_id = 2;
|
||||
string command_id = 3;
|
||||
}
|
||||
|
||||
message ApplyAgencyOpeningResponse {
|
||||
AgencyOpeningApplication application = 1;
|
||||
bool created = 2;
|
||||
}
|
||||
|
||||
// ActivityService 只暴露同步查询,不把事件消费伪装成 RPC。
|
||||
service ActivityService {
|
||||
rpc PingActivity(PingActivityRequest) returns (PingActivityResponse);
|
||||
@ -2314,8 +2066,6 @@ service ActivityCronService {
|
||||
rpc ProcessAchievementRewardBatch(CronBatchRequest) returns (CronBatchResponse);
|
||||
rpc ProcessRoomTurnoverRewardSettlementBatch(CronBatchRequest) returns (CronBatchResponse);
|
||||
rpc ProcessWeeklyStarSettlementBatch(CronBatchRequest) returns (CronBatchResponse);
|
||||
rpc ProcessCPWeeklyRankSettlementBatch(CronBatchRequest) returns (CronBatchResponse);
|
||||
rpc ProcessAgencyOpeningSettlementBatch(CronBatchRequest) returns (CronBatchResponse);
|
||||
}
|
||||
|
||||
// TaskService 拥有 App 任务查询、事件进度消费和领奖状态。
|
||||
@ -2362,18 +2112,6 @@ service WeeklyStarService {
|
||||
rpc ListWeeklyStarHistory(ListWeeklyStarHistoryRequest) returns (ListWeeklyStarHistoryResponse);
|
||||
}
|
||||
|
||||
// CPWeeklyRankService owns H5 reads for CP weekly rank and reward config.
|
||||
service CPWeeklyRankService {
|
||||
rpc GetCPWeeklyRankStatus(GetCPWeeklyRankStatusRequest) returns (GetCPWeeklyRankStatusResponse);
|
||||
}
|
||||
|
||||
// AgencyOpeningService owns App/H5 reads and application writes for agency opening activity.
|
||||
service AgencyOpeningService {
|
||||
rpc GetAgencyOpeningStatus(GetAgencyOpeningStatusRequest) returns (GetAgencyOpeningStatusResponse);
|
||||
rpc ApplyAgencyOpening(ApplyAgencyOpeningRequest) returns (ApplyAgencyOpeningResponse);
|
||||
rpc ListAgencyOpeningLeaderboard(ListAgencyOpeningApplicationsRequest) returns (ListAgencyOpeningApplicationsResponse);
|
||||
}
|
||||
|
||||
// BroadcastService owns server-side Tencent IM global/region broadcast delivery.
|
||||
service BroadcastService {
|
||||
rpc EnsureBroadcastGroups(EnsureBroadcastGroupsRequest) returns (EnsureBroadcastGroupsResponse);
|
||||
@ -2468,23 +2206,6 @@ service AdminWeeklyStarService {
|
||||
rpc ListWeeklyStarSettlements(ListWeeklyStarSettlementsRequest) returns (ListWeeklyStarSettlementsResponse);
|
||||
}
|
||||
|
||||
// AdminCPWeeklyRankService 是后台配置 CP 周榜启停、奖励和查询发奖事实的入口。
|
||||
service AdminCPWeeklyRankService {
|
||||
rpc GetCPWeeklyRankConfig(GetCPWeeklyRankConfigRequest) returns (GetCPWeeklyRankConfigResponse);
|
||||
rpc UpdateCPWeeklyRankConfig(UpdateCPWeeklyRankConfigRequest) returns (UpdateCPWeeklyRankConfigResponse);
|
||||
rpc ListCPWeeklyRankSettlements(ListCPWeeklyRankSettlementsRequest) returns (ListCPWeeklyRankSettlementsResponse);
|
||||
}
|
||||
|
||||
// AdminAgencyOpeningService 是后台配置开业周期、查看申请和榜单发奖结果的入口。
|
||||
service AdminAgencyOpeningService {
|
||||
rpc ListAgencyOpeningCycles(ListAgencyOpeningCyclesRequest) returns (ListAgencyOpeningCyclesResponse);
|
||||
rpc CreateAgencyOpeningCycle(UpsertAgencyOpeningCycleRequest) returns (UpsertAgencyOpeningCycleResponse);
|
||||
rpc GetAgencyOpeningCycle(GetAgencyOpeningCycleRequest) returns (GetAgencyOpeningCycleResponse);
|
||||
rpc UpdateAgencyOpeningCycle(UpsertAgencyOpeningCycleRequest) returns (UpsertAgencyOpeningCycleResponse);
|
||||
rpc SetAgencyOpeningCycleStatus(SetAgencyOpeningCycleStatusRequest) returns (SetAgencyOpeningCycleStatusResponse);
|
||||
rpc ListAgencyOpeningApplications(ListAgencyOpeningApplicationsRequest) returns (ListAgencyOpeningApplicationsResponse);
|
||||
}
|
||||
|
||||
// SevenDayCheckInService 拥有 App 七日签到查询和签到命令。
|
||||
service SevenDayCheckInService {
|
||||
rpc GetSevenDayCheckInStatus(GetSevenDayCheckInStatusRequest) returns (GetSevenDayCheckInStatusResponse);
|
||||
|
||||
@ -502,8 +502,6 @@ const (
|
||||
ActivityCronService_ProcessAchievementRewardBatch_FullMethodName = "/hyapp.activity.v1.ActivityCronService/ProcessAchievementRewardBatch"
|
||||
ActivityCronService_ProcessRoomTurnoverRewardSettlementBatch_FullMethodName = "/hyapp.activity.v1.ActivityCronService/ProcessRoomTurnoverRewardSettlementBatch"
|
||||
ActivityCronService_ProcessWeeklyStarSettlementBatch_FullMethodName = "/hyapp.activity.v1.ActivityCronService/ProcessWeeklyStarSettlementBatch"
|
||||
ActivityCronService_ProcessCPWeeklyRankSettlementBatch_FullMethodName = "/hyapp.activity.v1.ActivityCronService/ProcessCPWeeklyRankSettlementBatch"
|
||||
ActivityCronService_ProcessAgencyOpeningSettlementBatch_FullMethodName = "/hyapp.activity.v1.ActivityCronService/ProcessAgencyOpeningSettlementBatch"
|
||||
)
|
||||
|
||||
// ActivityCronServiceClient is the client API for ActivityCronService service.
|
||||
@ -517,8 +515,6 @@ type ActivityCronServiceClient interface {
|
||||
ProcessAchievementRewardBatch(ctx context.Context, in *CronBatchRequest, opts ...grpc.CallOption) (*CronBatchResponse, error)
|
||||
ProcessRoomTurnoverRewardSettlementBatch(ctx context.Context, in *CronBatchRequest, opts ...grpc.CallOption) (*CronBatchResponse, error)
|
||||
ProcessWeeklyStarSettlementBatch(ctx context.Context, in *CronBatchRequest, opts ...grpc.CallOption) (*CronBatchResponse, error)
|
||||
ProcessCPWeeklyRankSettlementBatch(ctx context.Context, in *CronBatchRequest, opts ...grpc.CallOption) (*CronBatchResponse, error)
|
||||
ProcessAgencyOpeningSettlementBatch(ctx context.Context, in *CronBatchRequest, opts ...grpc.CallOption) (*CronBatchResponse, error)
|
||||
}
|
||||
|
||||
type activityCronServiceClient struct {
|
||||
@ -579,26 +575,6 @@ func (c *activityCronServiceClient) ProcessWeeklyStarSettlementBatch(ctx context
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *activityCronServiceClient) ProcessCPWeeklyRankSettlementBatch(ctx context.Context, in *CronBatchRequest, opts ...grpc.CallOption) (*CronBatchResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(CronBatchResponse)
|
||||
err := c.cc.Invoke(ctx, ActivityCronService_ProcessCPWeeklyRankSettlementBatch_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *activityCronServiceClient) ProcessAgencyOpeningSettlementBatch(ctx context.Context, in *CronBatchRequest, opts ...grpc.CallOption) (*CronBatchResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(CronBatchResponse)
|
||||
err := c.cc.Invoke(ctx, ActivityCronService_ProcessAgencyOpeningSettlementBatch_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// ActivityCronServiceServer is the server API for ActivityCronService service.
|
||||
// All implementations must embed UnimplementedActivityCronServiceServer
|
||||
// for forward compatibility.
|
||||
@ -610,8 +586,6 @@ type ActivityCronServiceServer interface {
|
||||
ProcessAchievementRewardBatch(context.Context, *CronBatchRequest) (*CronBatchResponse, error)
|
||||
ProcessRoomTurnoverRewardSettlementBatch(context.Context, *CronBatchRequest) (*CronBatchResponse, error)
|
||||
ProcessWeeklyStarSettlementBatch(context.Context, *CronBatchRequest) (*CronBatchResponse, error)
|
||||
ProcessCPWeeklyRankSettlementBatch(context.Context, *CronBatchRequest) (*CronBatchResponse, error)
|
||||
ProcessAgencyOpeningSettlementBatch(context.Context, *CronBatchRequest) (*CronBatchResponse, error)
|
||||
mustEmbedUnimplementedActivityCronServiceServer()
|
||||
}
|
||||
|
||||
@ -637,12 +611,6 @@ func (UnimplementedActivityCronServiceServer) ProcessRoomTurnoverRewardSettlemen
|
||||
func (UnimplementedActivityCronServiceServer) ProcessWeeklyStarSettlementBatch(context.Context, *CronBatchRequest) (*CronBatchResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method ProcessWeeklyStarSettlementBatch not implemented")
|
||||
}
|
||||
func (UnimplementedActivityCronServiceServer) ProcessCPWeeklyRankSettlementBatch(context.Context, *CronBatchRequest) (*CronBatchResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method ProcessCPWeeklyRankSettlementBatch not implemented")
|
||||
}
|
||||
func (UnimplementedActivityCronServiceServer) ProcessAgencyOpeningSettlementBatch(context.Context, *CronBatchRequest) (*CronBatchResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method ProcessAgencyOpeningSettlementBatch not implemented")
|
||||
}
|
||||
func (UnimplementedActivityCronServiceServer) mustEmbedUnimplementedActivityCronServiceServer() {}
|
||||
func (UnimplementedActivityCronServiceServer) testEmbeddedByValue() {}
|
||||
|
||||
@ -754,42 +722,6 @@ func _ActivityCronService_ProcessWeeklyStarSettlementBatch_Handler(srv interface
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _ActivityCronService_ProcessCPWeeklyRankSettlementBatch_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(CronBatchRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ActivityCronServiceServer).ProcessCPWeeklyRankSettlementBatch(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: ActivityCronService_ProcessCPWeeklyRankSettlementBatch_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ActivityCronServiceServer).ProcessCPWeeklyRankSettlementBatch(ctx, req.(*CronBatchRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _ActivityCronService_ProcessAgencyOpeningSettlementBatch_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(CronBatchRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ActivityCronServiceServer).ProcessAgencyOpeningSettlementBatch(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: ActivityCronService_ProcessAgencyOpeningSettlementBatch_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ActivityCronServiceServer).ProcessAgencyOpeningSettlementBatch(ctx, req.(*CronBatchRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// ActivityCronService_ServiceDesc is the grpc.ServiceDesc for ActivityCronService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
@ -817,14 +749,6 @@ var ActivityCronService_ServiceDesc = grpc.ServiceDesc{
|
||||
MethodName: "ProcessWeeklyStarSettlementBatch",
|
||||
Handler: _ActivityCronService_ProcessWeeklyStarSettlementBatch_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ProcessCPWeeklyRankSettlementBatch",
|
||||
Handler: _ActivityCronService_ProcessCPWeeklyRankSettlementBatch_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ProcessAgencyOpeningSettlementBatch",
|
||||
Handler: _ActivityCronService_ProcessAgencyOpeningSettlementBatch_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "proto/activity/v1/activity.proto",
|
||||
@ -1999,294 +1923,6 @@ var WeeklyStarService_ServiceDesc = grpc.ServiceDesc{
|
||||
Metadata: "proto/activity/v1/activity.proto",
|
||||
}
|
||||
|
||||
const (
|
||||
CPWeeklyRankService_GetCPWeeklyRankStatus_FullMethodName = "/hyapp.activity.v1.CPWeeklyRankService/GetCPWeeklyRankStatus"
|
||||
)
|
||||
|
||||
// CPWeeklyRankServiceClient is the client API for CPWeeklyRankService 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.
|
||||
//
|
||||
// CPWeeklyRankService owns H5 reads for CP weekly rank and reward config.
|
||||
type CPWeeklyRankServiceClient interface {
|
||||
GetCPWeeklyRankStatus(ctx context.Context, in *GetCPWeeklyRankStatusRequest, opts ...grpc.CallOption) (*GetCPWeeklyRankStatusResponse, error)
|
||||
}
|
||||
|
||||
type cPWeeklyRankServiceClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewCPWeeklyRankServiceClient(cc grpc.ClientConnInterface) CPWeeklyRankServiceClient {
|
||||
return &cPWeeklyRankServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *cPWeeklyRankServiceClient) GetCPWeeklyRankStatus(ctx context.Context, in *GetCPWeeklyRankStatusRequest, opts ...grpc.CallOption) (*GetCPWeeklyRankStatusResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(GetCPWeeklyRankStatusResponse)
|
||||
err := c.cc.Invoke(ctx, CPWeeklyRankService_GetCPWeeklyRankStatus_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// CPWeeklyRankServiceServer is the server API for CPWeeklyRankService service.
|
||||
// All implementations must embed UnimplementedCPWeeklyRankServiceServer
|
||||
// for forward compatibility.
|
||||
//
|
||||
// CPWeeklyRankService owns H5 reads for CP weekly rank and reward config.
|
||||
type CPWeeklyRankServiceServer interface {
|
||||
GetCPWeeklyRankStatus(context.Context, *GetCPWeeklyRankStatusRequest) (*GetCPWeeklyRankStatusResponse, error)
|
||||
mustEmbedUnimplementedCPWeeklyRankServiceServer()
|
||||
}
|
||||
|
||||
// UnimplementedCPWeeklyRankServiceServer must be embedded to have
|
||||
// forward compatible implementations.
|
||||
//
|
||||
// NOTE: this should be embedded by value instead of pointer to avoid a nil
|
||||
// pointer dereference when methods are called.
|
||||
type UnimplementedCPWeeklyRankServiceServer struct{}
|
||||
|
||||
func (UnimplementedCPWeeklyRankServiceServer) GetCPWeeklyRankStatus(context.Context, *GetCPWeeklyRankStatusRequest) (*GetCPWeeklyRankStatusResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method GetCPWeeklyRankStatus not implemented")
|
||||
}
|
||||
func (UnimplementedCPWeeklyRankServiceServer) mustEmbedUnimplementedCPWeeklyRankServiceServer() {}
|
||||
func (UnimplementedCPWeeklyRankServiceServer) testEmbeddedByValue() {}
|
||||
|
||||
// UnsafeCPWeeklyRankServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to CPWeeklyRankServiceServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeCPWeeklyRankServiceServer interface {
|
||||
mustEmbedUnimplementedCPWeeklyRankServiceServer()
|
||||
}
|
||||
|
||||
func RegisterCPWeeklyRankServiceServer(s grpc.ServiceRegistrar, srv CPWeeklyRankServiceServer) {
|
||||
// If the following call panics, it indicates UnimplementedCPWeeklyRankServiceServer was
|
||||
// embedded by pointer and is nil. This will cause panics if an
|
||||
// unimplemented method is ever invoked, so we test this at initialization
|
||||
// time to prevent it from happening at runtime later due to I/O.
|
||||
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
|
||||
t.testEmbeddedByValue()
|
||||
}
|
||||
s.RegisterService(&CPWeeklyRankService_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _CPWeeklyRankService_GetCPWeeklyRankStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetCPWeeklyRankStatusRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(CPWeeklyRankServiceServer).GetCPWeeklyRankStatus(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: CPWeeklyRankService_GetCPWeeklyRankStatus_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(CPWeeklyRankServiceServer).GetCPWeeklyRankStatus(ctx, req.(*GetCPWeeklyRankStatusRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// CPWeeklyRankService_ServiceDesc is the grpc.ServiceDesc for CPWeeklyRankService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var CPWeeklyRankService_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "hyapp.activity.v1.CPWeeklyRankService",
|
||||
HandlerType: (*CPWeeklyRankServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "GetCPWeeklyRankStatus",
|
||||
Handler: _CPWeeklyRankService_GetCPWeeklyRankStatus_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "proto/activity/v1/activity.proto",
|
||||
}
|
||||
|
||||
const (
|
||||
AgencyOpeningService_GetAgencyOpeningStatus_FullMethodName = "/hyapp.activity.v1.AgencyOpeningService/GetAgencyOpeningStatus"
|
||||
AgencyOpeningService_ApplyAgencyOpening_FullMethodName = "/hyapp.activity.v1.AgencyOpeningService/ApplyAgencyOpening"
|
||||
AgencyOpeningService_ListAgencyOpeningLeaderboard_FullMethodName = "/hyapp.activity.v1.AgencyOpeningService/ListAgencyOpeningLeaderboard"
|
||||
)
|
||||
|
||||
// AgencyOpeningServiceClient is the client API for AgencyOpeningService 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.
|
||||
//
|
||||
// AgencyOpeningService owns App/H5 reads and application writes for agency opening activity.
|
||||
type AgencyOpeningServiceClient interface {
|
||||
GetAgencyOpeningStatus(ctx context.Context, in *GetAgencyOpeningStatusRequest, opts ...grpc.CallOption) (*GetAgencyOpeningStatusResponse, error)
|
||||
ApplyAgencyOpening(ctx context.Context, in *ApplyAgencyOpeningRequest, opts ...grpc.CallOption) (*ApplyAgencyOpeningResponse, error)
|
||||
ListAgencyOpeningLeaderboard(ctx context.Context, in *ListAgencyOpeningApplicationsRequest, opts ...grpc.CallOption) (*ListAgencyOpeningApplicationsResponse, error)
|
||||
}
|
||||
|
||||
type agencyOpeningServiceClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewAgencyOpeningServiceClient(cc grpc.ClientConnInterface) AgencyOpeningServiceClient {
|
||||
return &agencyOpeningServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *agencyOpeningServiceClient) GetAgencyOpeningStatus(ctx context.Context, in *GetAgencyOpeningStatusRequest, opts ...grpc.CallOption) (*GetAgencyOpeningStatusResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(GetAgencyOpeningStatusResponse)
|
||||
err := c.cc.Invoke(ctx, AgencyOpeningService_GetAgencyOpeningStatus_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *agencyOpeningServiceClient) ApplyAgencyOpening(ctx context.Context, in *ApplyAgencyOpeningRequest, opts ...grpc.CallOption) (*ApplyAgencyOpeningResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(ApplyAgencyOpeningResponse)
|
||||
err := c.cc.Invoke(ctx, AgencyOpeningService_ApplyAgencyOpening_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *agencyOpeningServiceClient) ListAgencyOpeningLeaderboard(ctx context.Context, in *ListAgencyOpeningApplicationsRequest, opts ...grpc.CallOption) (*ListAgencyOpeningApplicationsResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(ListAgencyOpeningApplicationsResponse)
|
||||
err := c.cc.Invoke(ctx, AgencyOpeningService_ListAgencyOpeningLeaderboard_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// AgencyOpeningServiceServer is the server API for AgencyOpeningService service.
|
||||
// All implementations must embed UnimplementedAgencyOpeningServiceServer
|
||||
// for forward compatibility.
|
||||
//
|
||||
// AgencyOpeningService owns App/H5 reads and application writes for agency opening activity.
|
||||
type AgencyOpeningServiceServer interface {
|
||||
GetAgencyOpeningStatus(context.Context, *GetAgencyOpeningStatusRequest) (*GetAgencyOpeningStatusResponse, error)
|
||||
ApplyAgencyOpening(context.Context, *ApplyAgencyOpeningRequest) (*ApplyAgencyOpeningResponse, error)
|
||||
ListAgencyOpeningLeaderboard(context.Context, *ListAgencyOpeningApplicationsRequest) (*ListAgencyOpeningApplicationsResponse, error)
|
||||
mustEmbedUnimplementedAgencyOpeningServiceServer()
|
||||
}
|
||||
|
||||
// UnimplementedAgencyOpeningServiceServer must be embedded to have
|
||||
// forward compatible implementations.
|
||||
//
|
||||
// NOTE: this should be embedded by value instead of pointer to avoid a nil
|
||||
// pointer dereference when methods are called.
|
||||
type UnimplementedAgencyOpeningServiceServer struct{}
|
||||
|
||||
func (UnimplementedAgencyOpeningServiceServer) GetAgencyOpeningStatus(context.Context, *GetAgencyOpeningStatusRequest) (*GetAgencyOpeningStatusResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method GetAgencyOpeningStatus not implemented")
|
||||
}
|
||||
func (UnimplementedAgencyOpeningServiceServer) ApplyAgencyOpening(context.Context, *ApplyAgencyOpeningRequest) (*ApplyAgencyOpeningResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method ApplyAgencyOpening not implemented")
|
||||
}
|
||||
func (UnimplementedAgencyOpeningServiceServer) ListAgencyOpeningLeaderboard(context.Context, *ListAgencyOpeningApplicationsRequest) (*ListAgencyOpeningApplicationsResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method ListAgencyOpeningLeaderboard not implemented")
|
||||
}
|
||||
func (UnimplementedAgencyOpeningServiceServer) mustEmbedUnimplementedAgencyOpeningServiceServer() {}
|
||||
func (UnimplementedAgencyOpeningServiceServer) testEmbeddedByValue() {}
|
||||
|
||||
// UnsafeAgencyOpeningServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to AgencyOpeningServiceServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeAgencyOpeningServiceServer interface {
|
||||
mustEmbedUnimplementedAgencyOpeningServiceServer()
|
||||
}
|
||||
|
||||
func RegisterAgencyOpeningServiceServer(s grpc.ServiceRegistrar, srv AgencyOpeningServiceServer) {
|
||||
// If the following call panics, it indicates UnimplementedAgencyOpeningServiceServer was
|
||||
// embedded by pointer and is nil. This will cause panics if an
|
||||
// unimplemented method is ever invoked, so we test this at initialization
|
||||
// time to prevent it from happening at runtime later due to I/O.
|
||||
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
|
||||
t.testEmbeddedByValue()
|
||||
}
|
||||
s.RegisterService(&AgencyOpeningService_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _AgencyOpeningService_GetAgencyOpeningStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetAgencyOpeningStatusRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AgencyOpeningServiceServer).GetAgencyOpeningStatus(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: AgencyOpeningService_GetAgencyOpeningStatus_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AgencyOpeningServiceServer).GetAgencyOpeningStatus(ctx, req.(*GetAgencyOpeningStatusRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _AgencyOpeningService_ApplyAgencyOpening_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ApplyAgencyOpeningRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AgencyOpeningServiceServer).ApplyAgencyOpening(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: AgencyOpeningService_ApplyAgencyOpening_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AgencyOpeningServiceServer).ApplyAgencyOpening(ctx, req.(*ApplyAgencyOpeningRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _AgencyOpeningService_ListAgencyOpeningLeaderboard_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ListAgencyOpeningApplicationsRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AgencyOpeningServiceServer).ListAgencyOpeningLeaderboard(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: AgencyOpeningService_ListAgencyOpeningLeaderboard_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AgencyOpeningServiceServer).ListAgencyOpeningLeaderboard(ctx, req.(*ListAgencyOpeningApplicationsRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// AgencyOpeningService_ServiceDesc is the grpc.ServiceDesc for AgencyOpeningService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var AgencyOpeningService_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "hyapp.activity.v1.AgencyOpeningService",
|
||||
HandlerType: (*AgencyOpeningServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "GetAgencyOpeningStatus",
|
||||
Handler: _AgencyOpeningService_GetAgencyOpeningStatus_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ApplyAgencyOpening",
|
||||
Handler: _AgencyOpeningService_ApplyAgencyOpening_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ListAgencyOpeningLeaderboard",
|
||||
Handler: _AgencyOpeningService_ListAgencyOpeningLeaderboard_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "proto/activity/v1/activity.proto",
|
||||
}
|
||||
|
||||
const (
|
||||
BroadcastService_EnsureBroadcastGroups_FullMethodName = "/hyapp.activity.v1.BroadcastService/EnsureBroadcastGroups"
|
||||
BroadcastService_PublishRegionBroadcast_FullMethodName = "/hyapp.activity.v1.BroadcastService/PublishRegionBroadcast"
|
||||
@ -4778,486 +4414,6 @@ var AdminWeeklyStarService_ServiceDesc = grpc.ServiceDesc{
|
||||
Metadata: "proto/activity/v1/activity.proto",
|
||||
}
|
||||
|
||||
const (
|
||||
AdminCPWeeklyRankService_GetCPWeeklyRankConfig_FullMethodName = "/hyapp.activity.v1.AdminCPWeeklyRankService/GetCPWeeklyRankConfig"
|
||||
AdminCPWeeklyRankService_UpdateCPWeeklyRankConfig_FullMethodName = "/hyapp.activity.v1.AdminCPWeeklyRankService/UpdateCPWeeklyRankConfig"
|
||||
AdminCPWeeklyRankService_ListCPWeeklyRankSettlements_FullMethodName = "/hyapp.activity.v1.AdminCPWeeklyRankService/ListCPWeeklyRankSettlements"
|
||||
)
|
||||
|
||||
// AdminCPWeeklyRankServiceClient is the client API for AdminCPWeeklyRankService 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.
|
||||
//
|
||||
// AdminCPWeeklyRankService 是后台配置 CP 周榜启停、奖励和查询发奖事实的入口。
|
||||
type AdminCPWeeklyRankServiceClient interface {
|
||||
GetCPWeeklyRankConfig(ctx context.Context, in *GetCPWeeklyRankConfigRequest, opts ...grpc.CallOption) (*GetCPWeeklyRankConfigResponse, error)
|
||||
UpdateCPWeeklyRankConfig(ctx context.Context, in *UpdateCPWeeklyRankConfigRequest, opts ...grpc.CallOption) (*UpdateCPWeeklyRankConfigResponse, error)
|
||||
ListCPWeeklyRankSettlements(ctx context.Context, in *ListCPWeeklyRankSettlementsRequest, opts ...grpc.CallOption) (*ListCPWeeklyRankSettlementsResponse, error)
|
||||
}
|
||||
|
||||
type adminCPWeeklyRankServiceClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewAdminCPWeeklyRankServiceClient(cc grpc.ClientConnInterface) AdminCPWeeklyRankServiceClient {
|
||||
return &adminCPWeeklyRankServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *adminCPWeeklyRankServiceClient) GetCPWeeklyRankConfig(ctx context.Context, in *GetCPWeeklyRankConfigRequest, opts ...grpc.CallOption) (*GetCPWeeklyRankConfigResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(GetCPWeeklyRankConfigResponse)
|
||||
err := c.cc.Invoke(ctx, AdminCPWeeklyRankService_GetCPWeeklyRankConfig_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *adminCPWeeklyRankServiceClient) UpdateCPWeeklyRankConfig(ctx context.Context, in *UpdateCPWeeklyRankConfigRequest, opts ...grpc.CallOption) (*UpdateCPWeeklyRankConfigResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(UpdateCPWeeklyRankConfigResponse)
|
||||
err := c.cc.Invoke(ctx, AdminCPWeeklyRankService_UpdateCPWeeklyRankConfig_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *adminCPWeeklyRankServiceClient) ListCPWeeklyRankSettlements(ctx context.Context, in *ListCPWeeklyRankSettlementsRequest, opts ...grpc.CallOption) (*ListCPWeeklyRankSettlementsResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(ListCPWeeklyRankSettlementsResponse)
|
||||
err := c.cc.Invoke(ctx, AdminCPWeeklyRankService_ListCPWeeklyRankSettlements_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// AdminCPWeeklyRankServiceServer is the server API for AdminCPWeeklyRankService service.
|
||||
// All implementations must embed UnimplementedAdminCPWeeklyRankServiceServer
|
||||
// for forward compatibility.
|
||||
//
|
||||
// AdminCPWeeklyRankService 是后台配置 CP 周榜启停、奖励和查询发奖事实的入口。
|
||||
type AdminCPWeeklyRankServiceServer interface {
|
||||
GetCPWeeklyRankConfig(context.Context, *GetCPWeeklyRankConfigRequest) (*GetCPWeeklyRankConfigResponse, error)
|
||||
UpdateCPWeeklyRankConfig(context.Context, *UpdateCPWeeklyRankConfigRequest) (*UpdateCPWeeklyRankConfigResponse, error)
|
||||
ListCPWeeklyRankSettlements(context.Context, *ListCPWeeklyRankSettlementsRequest) (*ListCPWeeklyRankSettlementsResponse, error)
|
||||
mustEmbedUnimplementedAdminCPWeeklyRankServiceServer()
|
||||
}
|
||||
|
||||
// UnimplementedAdminCPWeeklyRankServiceServer must be embedded to have
|
||||
// forward compatible implementations.
|
||||
//
|
||||
// NOTE: this should be embedded by value instead of pointer to avoid a nil
|
||||
// pointer dereference when methods are called.
|
||||
type UnimplementedAdminCPWeeklyRankServiceServer struct{}
|
||||
|
||||
func (UnimplementedAdminCPWeeklyRankServiceServer) GetCPWeeklyRankConfig(context.Context, *GetCPWeeklyRankConfigRequest) (*GetCPWeeklyRankConfigResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method GetCPWeeklyRankConfig not implemented")
|
||||
}
|
||||
func (UnimplementedAdminCPWeeklyRankServiceServer) UpdateCPWeeklyRankConfig(context.Context, *UpdateCPWeeklyRankConfigRequest) (*UpdateCPWeeklyRankConfigResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method UpdateCPWeeklyRankConfig not implemented")
|
||||
}
|
||||
func (UnimplementedAdminCPWeeklyRankServiceServer) ListCPWeeklyRankSettlements(context.Context, *ListCPWeeklyRankSettlementsRequest) (*ListCPWeeklyRankSettlementsResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method ListCPWeeklyRankSettlements not implemented")
|
||||
}
|
||||
func (UnimplementedAdminCPWeeklyRankServiceServer) mustEmbedUnimplementedAdminCPWeeklyRankServiceServer() {
|
||||
}
|
||||
func (UnimplementedAdminCPWeeklyRankServiceServer) testEmbeddedByValue() {}
|
||||
|
||||
// UnsafeAdminCPWeeklyRankServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to AdminCPWeeklyRankServiceServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeAdminCPWeeklyRankServiceServer interface {
|
||||
mustEmbedUnimplementedAdminCPWeeklyRankServiceServer()
|
||||
}
|
||||
|
||||
func RegisterAdminCPWeeklyRankServiceServer(s grpc.ServiceRegistrar, srv AdminCPWeeklyRankServiceServer) {
|
||||
// If the following call panics, it indicates UnimplementedAdminCPWeeklyRankServiceServer was
|
||||
// embedded by pointer and is nil. This will cause panics if an
|
||||
// unimplemented method is ever invoked, so we test this at initialization
|
||||
// time to prevent it from happening at runtime later due to I/O.
|
||||
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
|
||||
t.testEmbeddedByValue()
|
||||
}
|
||||
s.RegisterService(&AdminCPWeeklyRankService_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _AdminCPWeeklyRankService_GetCPWeeklyRankConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetCPWeeklyRankConfigRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AdminCPWeeklyRankServiceServer).GetCPWeeklyRankConfig(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: AdminCPWeeklyRankService_GetCPWeeklyRankConfig_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AdminCPWeeklyRankServiceServer).GetCPWeeklyRankConfig(ctx, req.(*GetCPWeeklyRankConfigRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _AdminCPWeeklyRankService_UpdateCPWeeklyRankConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(UpdateCPWeeklyRankConfigRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AdminCPWeeklyRankServiceServer).UpdateCPWeeklyRankConfig(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: AdminCPWeeklyRankService_UpdateCPWeeklyRankConfig_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AdminCPWeeklyRankServiceServer).UpdateCPWeeklyRankConfig(ctx, req.(*UpdateCPWeeklyRankConfigRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _AdminCPWeeklyRankService_ListCPWeeklyRankSettlements_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ListCPWeeklyRankSettlementsRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AdminCPWeeklyRankServiceServer).ListCPWeeklyRankSettlements(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: AdminCPWeeklyRankService_ListCPWeeklyRankSettlements_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AdminCPWeeklyRankServiceServer).ListCPWeeklyRankSettlements(ctx, req.(*ListCPWeeklyRankSettlementsRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// AdminCPWeeklyRankService_ServiceDesc is the grpc.ServiceDesc for AdminCPWeeklyRankService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var AdminCPWeeklyRankService_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "hyapp.activity.v1.AdminCPWeeklyRankService",
|
||||
HandlerType: (*AdminCPWeeklyRankServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "GetCPWeeklyRankConfig",
|
||||
Handler: _AdminCPWeeklyRankService_GetCPWeeklyRankConfig_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "UpdateCPWeeklyRankConfig",
|
||||
Handler: _AdminCPWeeklyRankService_UpdateCPWeeklyRankConfig_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ListCPWeeklyRankSettlements",
|
||||
Handler: _AdminCPWeeklyRankService_ListCPWeeklyRankSettlements_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "proto/activity/v1/activity.proto",
|
||||
}
|
||||
|
||||
const (
|
||||
AdminAgencyOpeningService_ListAgencyOpeningCycles_FullMethodName = "/hyapp.activity.v1.AdminAgencyOpeningService/ListAgencyOpeningCycles"
|
||||
AdminAgencyOpeningService_CreateAgencyOpeningCycle_FullMethodName = "/hyapp.activity.v1.AdminAgencyOpeningService/CreateAgencyOpeningCycle"
|
||||
AdminAgencyOpeningService_GetAgencyOpeningCycle_FullMethodName = "/hyapp.activity.v1.AdminAgencyOpeningService/GetAgencyOpeningCycle"
|
||||
AdminAgencyOpeningService_UpdateAgencyOpeningCycle_FullMethodName = "/hyapp.activity.v1.AdminAgencyOpeningService/UpdateAgencyOpeningCycle"
|
||||
AdminAgencyOpeningService_SetAgencyOpeningCycleStatus_FullMethodName = "/hyapp.activity.v1.AdminAgencyOpeningService/SetAgencyOpeningCycleStatus"
|
||||
AdminAgencyOpeningService_ListAgencyOpeningApplications_FullMethodName = "/hyapp.activity.v1.AdminAgencyOpeningService/ListAgencyOpeningApplications"
|
||||
)
|
||||
|
||||
// AdminAgencyOpeningServiceClient is the client API for AdminAgencyOpeningService 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.
|
||||
//
|
||||
// AdminAgencyOpeningService 是后台配置开业周期、查看申请和榜单发奖结果的入口。
|
||||
type AdminAgencyOpeningServiceClient interface {
|
||||
ListAgencyOpeningCycles(ctx context.Context, in *ListAgencyOpeningCyclesRequest, opts ...grpc.CallOption) (*ListAgencyOpeningCyclesResponse, error)
|
||||
CreateAgencyOpeningCycle(ctx context.Context, in *UpsertAgencyOpeningCycleRequest, opts ...grpc.CallOption) (*UpsertAgencyOpeningCycleResponse, error)
|
||||
GetAgencyOpeningCycle(ctx context.Context, in *GetAgencyOpeningCycleRequest, opts ...grpc.CallOption) (*GetAgencyOpeningCycleResponse, error)
|
||||
UpdateAgencyOpeningCycle(ctx context.Context, in *UpsertAgencyOpeningCycleRequest, opts ...grpc.CallOption) (*UpsertAgencyOpeningCycleResponse, error)
|
||||
SetAgencyOpeningCycleStatus(ctx context.Context, in *SetAgencyOpeningCycleStatusRequest, opts ...grpc.CallOption) (*SetAgencyOpeningCycleStatusResponse, error)
|
||||
ListAgencyOpeningApplications(ctx context.Context, in *ListAgencyOpeningApplicationsRequest, opts ...grpc.CallOption) (*ListAgencyOpeningApplicationsResponse, error)
|
||||
}
|
||||
|
||||
type adminAgencyOpeningServiceClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewAdminAgencyOpeningServiceClient(cc grpc.ClientConnInterface) AdminAgencyOpeningServiceClient {
|
||||
return &adminAgencyOpeningServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *adminAgencyOpeningServiceClient) ListAgencyOpeningCycles(ctx context.Context, in *ListAgencyOpeningCyclesRequest, opts ...grpc.CallOption) (*ListAgencyOpeningCyclesResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(ListAgencyOpeningCyclesResponse)
|
||||
err := c.cc.Invoke(ctx, AdminAgencyOpeningService_ListAgencyOpeningCycles_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *adminAgencyOpeningServiceClient) CreateAgencyOpeningCycle(ctx context.Context, in *UpsertAgencyOpeningCycleRequest, opts ...grpc.CallOption) (*UpsertAgencyOpeningCycleResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(UpsertAgencyOpeningCycleResponse)
|
||||
err := c.cc.Invoke(ctx, AdminAgencyOpeningService_CreateAgencyOpeningCycle_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *adminAgencyOpeningServiceClient) GetAgencyOpeningCycle(ctx context.Context, in *GetAgencyOpeningCycleRequest, opts ...grpc.CallOption) (*GetAgencyOpeningCycleResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(GetAgencyOpeningCycleResponse)
|
||||
err := c.cc.Invoke(ctx, AdminAgencyOpeningService_GetAgencyOpeningCycle_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *adminAgencyOpeningServiceClient) UpdateAgencyOpeningCycle(ctx context.Context, in *UpsertAgencyOpeningCycleRequest, opts ...grpc.CallOption) (*UpsertAgencyOpeningCycleResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(UpsertAgencyOpeningCycleResponse)
|
||||
err := c.cc.Invoke(ctx, AdminAgencyOpeningService_UpdateAgencyOpeningCycle_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *adminAgencyOpeningServiceClient) SetAgencyOpeningCycleStatus(ctx context.Context, in *SetAgencyOpeningCycleStatusRequest, opts ...grpc.CallOption) (*SetAgencyOpeningCycleStatusResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(SetAgencyOpeningCycleStatusResponse)
|
||||
err := c.cc.Invoke(ctx, AdminAgencyOpeningService_SetAgencyOpeningCycleStatus_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *adminAgencyOpeningServiceClient) ListAgencyOpeningApplications(ctx context.Context, in *ListAgencyOpeningApplicationsRequest, opts ...grpc.CallOption) (*ListAgencyOpeningApplicationsResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(ListAgencyOpeningApplicationsResponse)
|
||||
err := c.cc.Invoke(ctx, AdminAgencyOpeningService_ListAgencyOpeningApplications_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// AdminAgencyOpeningServiceServer is the server API for AdminAgencyOpeningService service.
|
||||
// All implementations must embed UnimplementedAdminAgencyOpeningServiceServer
|
||||
// for forward compatibility.
|
||||
//
|
||||
// AdminAgencyOpeningService 是后台配置开业周期、查看申请和榜单发奖结果的入口。
|
||||
type AdminAgencyOpeningServiceServer interface {
|
||||
ListAgencyOpeningCycles(context.Context, *ListAgencyOpeningCyclesRequest) (*ListAgencyOpeningCyclesResponse, error)
|
||||
CreateAgencyOpeningCycle(context.Context, *UpsertAgencyOpeningCycleRequest) (*UpsertAgencyOpeningCycleResponse, error)
|
||||
GetAgencyOpeningCycle(context.Context, *GetAgencyOpeningCycleRequest) (*GetAgencyOpeningCycleResponse, error)
|
||||
UpdateAgencyOpeningCycle(context.Context, *UpsertAgencyOpeningCycleRequest) (*UpsertAgencyOpeningCycleResponse, error)
|
||||
SetAgencyOpeningCycleStatus(context.Context, *SetAgencyOpeningCycleStatusRequest) (*SetAgencyOpeningCycleStatusResponse, error)
|
||||
ListAgencyOpeningApplications(context.Context, *ListAgencyOpeningApplicationsRequest) (*ListAgencyOpeningApplicationsResponse, error)
|
||||
mustEmbedUnimplementedAdminAgencyOpeningServiceServer()
|
||||
}
|
||||
|
||||
// UnimplementedAdminAgencyOpeningServiceServer must be embedded to have
|
||||
// forward compatible implementations.
|
||||
//
|
||||
// NOTE: this should be embedded by value instead of pointer to avoid a nil
|
||||
// pointer dereference when methods are called.
|
||||
type UnimplementedAdminAgencyOpeningServiceServer struct{}
|
||||
|
||||
func (UnimplementedAdminAgencyOpeningServiceServer) ListAgencyOpeningCycles(context.Context, *ListAgencyOpeningCyclesRequest) (*ListAgencyOpeningCyclesResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method ListAgencyOpeningCycles not implemented")
|
||||
}
|
||||
func (UnimplementedAdminAgencyOpeningServiceServer) CreateAgencyOpeningCycle(context.Context, *UpsertAgencyOpeningCycleRequest) (*UpsertAgencyOpeningCycleResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method CreateAgencyOpeningCycle not implemented")
|
||||
}
|
||||
func (UnimplementedAdminAgencyOpeningServiceServer) GetAgencyOpeningCycle(context.Context, *GetAgencyOpeningCycleRequest) (*GetAgencyOpeningCycleResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method GetAgencyOpeningCycle not implemented")
|
||||
}
|
||||
func (UnimplementedAdminAgencyOpeningServiceServer) UpdateAgencyOpeningCycle(context.Context, *UpsertAgencyOpeningCycleRequest) (*UpsertAgencyOpeningCycleResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method UpdateAgencyOpeningCycle not implemented")
|
||||
}
|
||||
func (UnimplementedAdminAgencyOpeningServiceServer) SetAgencyOpeningCycleStatus(context.Context, *SetAgencyOpeningCycleStatusRequest) (*SetAgencyOpeningCycleStatusResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method SetAgencyOpeningCycleStatus not implemented")
|
||||
}
|
||||
func (UnimplementedAdminAgencyOpeningServiceServer) ListAgencyOpeningApplications(context.Context, *ListAgencyOpeningApplicationsRequest) (*ListAgencyOpeningApplicationsResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method ListAgencyOpeningApplications not implemented")
|
||||
}
|
||||
func (UnimplementedAdminAgencyOpeningServiceServer) mustEmbedUnimplementedAdminAgencyOpeningServiceServer() {
|
||||
}
|
||||
func (UnimplementedAdminAgencyOpeningServiceServer) testEmbeddedByValue() {}
|
||||
|
||||
// UnsafeAdminAgencyOpeningServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to AdminAgencyOpeningServiceServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeAdminAgencyOpeningServiceServer interface {
|
||||
mustEmbedUnimplementedAdminAgencyOpeningServiceServer()
|
||||
}
|
||||
|
||||
func RegisterAdminAgencyOpeningServiceServer(s grpc.ServiceRegistrar, srv AdminAgencyOpeningServiceServer) {
|
||||
// If the following call panics, it indicates UnimplementedAdminAgencyOpeningServiceServer was
|
||||
// embedded by pointer and is nil. This will cause panics if an
|
||||
// unimplemented method is ever invoked, so we test this at initialization
|
||||
// time to prevent it from happening at runtime later due to I/O.
|
||||
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
|
||||
t.testEmbeddedByValue()
|
||||
}
|
||||
s.RegisterService(&AdminAgencyOpeningService_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _AdminAgencyOpeningService_ListAgencyOpeningCycles_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ListAgencyOpeningCyclesRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AdminAgencyOpeningServiceServer).ListAgencyOpeningCycles(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: AdminAgencyOpeningService_ListAgencyOpeningCycles_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AdminAgencyOpeningServiceServer).ListAgencyOpeningCycles(ctx, req.(*ListAgencyOpeningCyclesRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _AdminAgencyOpeningService_CreateAgencyOpeningCycle_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(UpsertAgencyOpeningCycleRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AdminAgencyOpeningServiceServer).CreateAgencyOpeningCycle(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: AdminAgencyOpeningService_CreateAgencyOpeningCycle_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AdminAgencyOpeningServiceServer).CreateAgencyOpeningCycle(ctx, req.(*UpsertAgencyOpeningCycleRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _AdminAgencyOpeningService_GetAgencyOpeningCycle_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetAgencyOpeningCycleRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AdminAgencyOpeningServiceServer).GetAgencyOpeningCycle(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: AdminAgencyOpeningService_GetAgencyOpeningCycle_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AdminAgencyOpeningServiceServer).GetAgencyOpeningCycle(ctx, req.(*GetAgencyOpeningCycleRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _AdminAgencyOpeningService_UpdateAgencyOpeningCycle_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(UpsertAgencyOpeningCycleRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AdminAgencyOpeningServiceServer).UpdateAgencyOpeningCycle(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: AdminAgencyOpeningService_UpdateAgencyOpeningCycle_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AdminAgencyOpeningServiceServer).UpdateAgencyOpeningCycle(ctx, req.(*UpsertAgencyOpeningCycleRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _AdminAgencyOpeningService_SetAgencyOpeningCycleStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(SetAgencyOpeningCycleStatusRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AdminAgencyOpeningServiceServer).SetAgencyOpeningCycleStatus(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: AdminAgencyOpeningService_SetAgencyOpeningCycleStatus_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AdminAgencyOpeningServiceServer).SetAgencyOpeningCycleStatus(ctx, req.(*SetAgencyOpeningCycleStatusRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _AdminAgencyOpeningService_ListAgencyOpeningApplications_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ListAgencyOpeningApplicationsRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AdminAgencyOpeningServiceServer).ListAgencyOpeningApplications(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: AdminAgencyOpeningService_ListAgencyOpeningApplications_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AdminAgencyOpeningServiceServer).ListAgencyOpeningApplications(ctx, req.(*ListAgencyOpeningApplicationsRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// AdminAgencyOpeningService_ServiceDesc is the grpc.ServiceDesc for AdminAgencyOpeningService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var AdminAgencyOpeningService_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "hyapp.activity.v1.AdminAgencyOpeningService",
|
||||
HandlerType: (*AdminAgencyOpeningServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "ListAgencyOpeningCycles",
|
||||
Handler: _AdminAgencyOpeningService_ListAgencyOpeningCycles_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "CreateAgencyOpeningCycle",
|
||||
Handler: _AdminAgencyOpeningService_CreateAgencyOpeningCycle_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetAgencyOpeningCycle",
|
||||
Handler: _AdminAgencyOpeningService_GetAgencyOpeningCycle_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "UpdateAgencyOpeningCycle",
|
||||
Handler: _AdminAgencyOpeningService_UpdateAgencyOpeningCycle_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "SetAgencyOpeningCycleStatus",
|
||||
Handler: _AdminAgencyOpeningService_SetAgencyOpeningCycleStatus_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ListAgencyOpeningApplications",
|
||||
Handler: _AdminAgencyOpeningService_ListAgencyOpeningApplications_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "proto/activity/v1/activity.proto",
|
||||
}
|
||||
|
||||
const (
|
||||
SevenDayCheckInService_GetSevenDayCheckInStatus_FullMethodName = "/hyapp.activity.v1.SevenDayCheckInService/GetSevenDayCheckInStatus"
|
||||
SevenDayCheckInService_SignSevenDayCheckIn_FullMethodName = "/hyapp.activity.v1.SevenDayCheckInService/SignSevenDayCheckIn"
|
||||
|
||||
@ -1209,12 +1209,8 @@ type RoomGiftSent struct {
|
||||
GiftAnimationUrl string `protobuf:"bytes,17,opt,name=gift_animation_url,json=giftAnimationUrl,proto3" json:"gift_animation_url,omitempty"`
|
||||
TargetGiftValue int64 `protobuf:"varint,18,opt,name=target_gift_value,json=targetGiftValue,proto3" json:"target_gift_value,omitempty"`
|
||||
GiftEffectTypes []string `protobuf:"bytes,19,rep,name=gift_effect_types,json=giftEffectTypes,proto3" json:"gift_effect_types,omitempty"`
|
||||
// is_robot_gift 标记机器人房间展示用礼物事实;下游真实统计、任务和结算消费者必须跳过。
|
||||
IsRobotGift bool `protobuf:"varint,20,opt,name=is_robot_gift,json=isRobotGift,proto3" json:"is_robot_gift,omitempty"`
|
||||
// synthetic_lucky_gift 标记幸运礼物只用于房间表现,不写真实抽奖流水、不扣真实奖池、不发真实奖励。
|
||||
SyntheticLuckyGift bool `protobuf:"varint,21,opt,name=synthetic_lucky_gift,json=syntheticLuckyGift,proto3" json:"synthetic_lucky_gift,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *RoomGiftSent) Reset() {
|
||||
@ -1380,186 +1376,6 @@ func (x *RoomGiftSent) GetGiftEffectTypes() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *RoomGiftSent) GetIsRobotGift() bool {
|
||||
if x != nil {
|
||||
return x.IsRobotGift
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *RoomGiftSent) GetSyntheticLuckyGift() bool {
|
||||
if x != nil {
|
||||
return x.SyntheticLuckyGift
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// RoomRobotLuckyGiftDrawn 是机器人房间专用幸运礼物展示事件。
|
||||
// 它复用客户端 lucky_gift_drawn 表现协议,但不代表真实奖池抽奖和钱包返奖事实。
|
||||
type RoomRobotLuckyGiftDrawn struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
DrawId string `protobuf:"bytes,1,opt,name=draw_id,json=drawId,proto3" json:"draw_id,omitempty"`
|
||||
CommandId string `protobuf:"bytes,2,opt,name=command_id,json=commandId,proto3" json:"command_id,omitempty"`
|
||||
SenderUserId int64 `protobuf:"varint,3,opt,name=sender_user_id,json=senderUserId,proto3" json:"sender_user_id,omitempty"`
|
||||
TargetUserId int64 `protobuf:"varint,4,opt,name=target_user_id,json=targetUserId,proto3" json:"target_user_id,omitempty"`
|
||||
GiftId string `protobuf:"bytes,5,opt,name=gift_id,json=giftId,proto3" json:"gift_id,omitempty"`
|
||||
GiftCount int32 `protobuf:"varint,6,opt,name=gift_count,json=giftCount,proto3" json:"gift_count,omitempty"`
|
||||
PoolId string `protobuf:"bytes,7,opt,name=pool_id,json=poolId,proto3" json:"pool_id,omitempty"`
|
||||
MultiplierPpm int64 `protobuf:"varint,8,opt,name=multiplier_ppm,json=multiplierPpm,proto3" json:"multiplier_ppm,omitempty"`
|
||||
BaseRewardCoins int64 `protobuf:"varint,9,opt,name=base_reward_coins,json=baseRewardCoins,proto3" json:"base_reward_coins,omitempty"`
|
||||
RoomAtmosphereRewardCoins int64 `protobuf:"varint,10,opt,name=room_atmosphere_reward_coins,json=roomAtmosphereRewardCoins,proto3" json:"room_atmosphere_reward_coins,omitempty"`
|
||||
ActivitySubsidyCoins int64 `protobuf:"varint,11,opt,name=activity_subsidy_coins,json=activitySubsidyCoins,proto3" json:"activity_subsidy_coins,omitempty"`
|
||||
EffectiveRewardCoins int64 `protobuf:"varint,12,opt,name=effective_reward_coins,json=effectiveRewardCoins,proto3" json:"effective_reward_coins,omitempty"`
|
||||
CreatedAtMs int64 `protobuf:"varint,13,opt,name=created_at_ms,json=createdAtMs,proto3" json:"created_at_ms,omitempty"`
|
||||
VisibleRegionId int64 `protobuf:"varint,14,opt,name=visible_region_id,json=visibleRegionId,proto3" json:"visible_region_id,omitempty"`
|
||||
IsRobot bool `protobuf:"varint,15,opt,name=is_robot,json=isRobot,proto3" json:"is_robot,omitempty"`
|
||||
Synthetic bool `protobuf:"varint,16,opt,name=synthetic,proto3" json:"synthetic,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *RoomRobotLuckyGiftDrawn) Reset() {
|
||||
*x = RoomRobotLuckyGiftDrawn{}
|
||||
mi := &file_proto_events_room_v1_events_proto_msgTypes[17]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *RoomRobotLuckyGiftDrawn) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*RoomRobotLuckyGiftDrawn) ProtoMessage() {}
|
||||
|
||||
func (x *RoomRobotLuckyGiftDrawn) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_events_room_v1_events_proto_msgTypes[17]
|
||||
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 RoomRobotLuckyGiftDrawn.ProtoReflect.Descriptor instead.
|
||||
func (*RoomRobotLuckyGiftDrawn) Descriptor() ([]byte, []int) {
|
||||
return file_proto_events_room_v1_events_proto_rawDescGZIP(), []int{17}
|
||||
}
|
||||
|
||||
func (x *RoomRobotLuckyGiftDrawn) GetDrawId() string {
|
||||
if x != nil {
|
||||
return x.DrawId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *RoomRobotLuckyGiftDrawn) GetCommandId() string {
|
||||
if x != nil {
|
||||
return x.CommandId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *RoomRobotLuckyGiftDrawn) GetSenderUserId() int64 {
|
||||
if x != nil {
|
||||
return x.SenderUserId
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *RoomRobotLuckyGiftDrawn) GetTargetUserId() int64 {
|
||||
if x != nil {
|
||||
return x.TargetUserId
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *RoomRobotLuckyGiftDrawn) GetGiftId() string {
|
||||
if x != nil {
|
||||
return x.GiftId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *RoomRobotLuckyGiftDrawn) GetGiftCount() int32 {
|
||||
if x != nil {
|
||||
return x.GiftCount
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *RoomRobotLuckyGiftDrawn) GetPoolId() string {
|
||||
if x != nil {
|
||||
return x.PoolId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *RoomRobotLuckyGiftDrawn) GetMultiplierPpm() int64 {
|
||||
if x != nil {
|
||||
return x.MultiplierPpm
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *RoomRobotLuckyGiftDrawn) GetBaseRewardCoins() int64 {
|
||||
if x != nil {
|
||||
return x.BaseRewardCoins
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *RoomRobotLuckyGiftDrawn) GetRoomAtmosphereRewardCoins() int64 {
|
||||
if x != nil {
|
||||
return x.RoomAtmosphereRewardCoins
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *RoomRobotLuckyGiftDrawn) GetActivitySubsidyCoins() int64 {
|
||||
if x != nil {
|
||||
return x.ActivitySubsidyCoins
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *RoomRobotLuckyGiftDrawn) GetEffectiveRewardCoins() int64 {
|
||||
if x != nil {
|
||||
return x.EffectiveRewardCoins
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *RoomRobotLuckyGiftDrawn) GetCreatedAtMs() int64 {
|
||||
if x != nil {
|
||||
return x.CreatedAtMs
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *RoomRobotLuckyGiftDrawn) GetVisibleRegionId() int64 {
|
||||
if x != nil {
|
||||
return x.VisibleRegionId
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *RoomRobotLuckyGiftDrawn) GetIsRobot() bool {
|
||||
if x != nil {
|
||||
return x.IsRobot
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *RoomRobotLuckyGiftDrawn) GetSynthetic() bool {
|
||||
if x != nil {
|
||||
return x.Synthetic
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// RoomHeatChanged 表达热度变化结果。
|
||||
type RoomHeatChanged struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
@ -1571,7 +1387,7 @@ type RoomHeatChanged struct {
|
||||
|
||||
func (x *RoomHeatChanged) Reset() {
|
||||
*x = RoomHeatChanged{}
|
||||
mi := &file_proto_events_room_v1_events_proto_msgTypes[18]
|
||||
mi := &file_proto_events_room_v1_events_proto_msgTypes[17]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@ -1583,7 +1399,7 @@ func (x *RoomHeatChanged) String() string {
|
||||
func (*RoomHeatChanged) ProtoMessage() {}
|
||||
|
||||
func (x *RoomHeatChanged) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_events_room_v1_events_proto_msgTypes[18]
|
||||
mi := &file_proto_events_room_v1_events_proto_msgTypes[17]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@ -1596,7 +1412,7 @@ func (x *RoomHeatChanged) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use RoomHeatChanged.ProtoReflect.Descriptor instead.
|
||||
func (*RoomHeatChanged) Descriptor() ([]byte, []int) {
|
||||
return file_proto_events_room_v1_events_proto_rawDescGZIP(), []int{18}
|
||||
return file_proto_events_room_v1_events_proto_rawDescGZIP(), []int{17}
|
||||
}
|
||||
|
||||
func (x *RoomHeatChanged) GetDelta() int64 {
|
||||
@ -1625,7 +1441,7 @@ type RoomRankChanged struct {
|
||||
|
||||
func (x *RoomRankChanged) Reset() {
|
||||
*x = RoomRankChanged{}
|
||||
mi := &file_proto_events_room_v1_events_proto_msgTypes[19]
|
||||
mi := &file_proto_events_room_v1_events_proto_msgTypes[18]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@ -1637,7 +1453,7 @@ func (x *RoomRankChanged) String() string {
|
||||
func (*RoomRankChanged) ProtoMessage() {}
|
||||
|
||||
func (x *RoomRankChanged) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_events_room_v1_events_proto_msgTypes[19]
|
||||
mi := &file_proto_events_room_v1_events_proto_msgTypes[18]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@ -1650,7 +1466,7 @@ func (x *RoomRankChanged) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use RoomRankChanged.ProtoReflect.Descriptor instead.
|
||||
func (*RoomRankChanged) Descriptor() ([]byte, []int) {
|
||||
return file_proto_events_room_v1_events_proto_rawDescGZIP(), []int{19}
|
||||
return file_proto_events_room_v1_events_proto_rawDescGZIP(), []int{18}
|
||||
}
|
||||
|
||||
func (x *RoomRankChanged) GetUserId() int64 {
|
||||
@ -1690,7 +1506,7 @@ type RoomRocketRewardGrant struct {
|
||||
|
||||
func (x *RoomRocketRewardGrant) Reset() {
|
||||
*x = RoomRocketRewardGrant{}
|
||||
mi := &file_proto_events_room_v1_events_proto_msgTypes[20]
|
||||
mi := &file_proto_events_room_v1_events_proto_msgTypes[19]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@ -1702,7 +1518,7 @@ func (x *RoomRocketRewardGrant) String() string {
|
||||
func (*RoomRocketRewardGrant) ProtoMessage() {}
|
||||
|
||||
func (x *RoomRocketRewardGrant) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_events_room_v1_events_proto_msgTypes[20]
|
||||
mi := &file_proto_events_room_v1_events_proto_msgTypes[19]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@ -1715,7 +1531,7 @@ func (x *RoomRocketRewardGrant) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use RoomRocketRewardGrant.ProtoReflect.Descriptor instead.
|
||||
func (*RoomRocketRewardGrant) Descriptor() ([]byte, []int) {
|
||||
return file_proto_events_room_v1_events_proto_rawDescGZIP(), []int{20}
|
||||
return file_proto_events_room_v1_events_proto_rawDescGZIP(), []int{19}
|
||||
}
|
||||
|
||||
func (x *RoomRocketRewardGrant) GetRewardRole() string {
|
||||
@ -1797,7 +1613,7 @@ type RoomRocketFuelChanged struct {
|
||||
|
||||
func (x *RoomRocketFuelChanged) Reset() {
|
||||
*x = RoomRocketFuelChanged{}
|
||||
mi := &file_proto_events_room_v1_events_proto_msgTypes[21]
|
||||
mi := &file_proto_events_room_v1_events_proto_msgTypes[20]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@ -1809,7 +1625,7 @@ func (x *RoomRocketFuelChanged) String() string {
|
||||
func (*RoomRocketFuelChanged) ProtoMessage() {}
|
||||
|
||||
func (x *RoomRocketFuelChanged) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_events_room_v1_events_proto_msgTypes[21]
|
||||
mi := &file_proto_events_room_v1_events_proto_msgTypes[20]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@ -1822,7 +1638,7 @@ func (x *RoomRocketFuelChanged) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use RoomRocketFuelChanged.ProtoReflect.Descriptor instead.
|
||||
func (*RoomRocketFuelChanged) Descriptor() ([]byte, []int) {
|
||||
return file_proto_events_room_v1_events_proto_rawDescGZIP(), []int{21}
|
||||
return file_proto_events_room_v1_events_proto_rawDescGZIP(), []int{20}
|
||||
}
|
||||
|
||||
func (x *RoomRocketFuelChanged) GetRocketId() string {
|
||||
@ -1946,7 +1762,7 @@ type RoomRocketIgnited struct {
|
||||
|
||||
func (x *RoomRocketIgnited) Reset() {
|
||||
*x = RoomRocketIgnited{}
|
||||
mi := &file_proto_events_room_v1_events_proto_msgTypes[22]
|
||||
mi := &file_proto_events_room_v1_events_proto_msgTypes[21]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@ -1958,7 +1774,7 @@ func (x *RoomRocketIgnited) String() string {
|
||||
func (*RoomRocketIgnited) ProtoMessage() {}
|
||||
|
||||
func (x *RoomRocketIgnited) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_events_room_v1_events_proto_msgTypes[22]
|
||||
mi := &file_proto_events_room_v1_events_proto_msgTypes[21]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@ -1971,7 +1787,7 @@ func (x *RoomRocketIgnited) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use RoomRocketIgnited.ProtoReflect.Descriptor instead.
|
||||
func (*RoomRocketIgnited) Descriptor() ([]byte, []int) {
|
||||
return file_proto_events_room_v1_events_proto_rawDescGZIP(), []int{22}
|
||||
return file_proto_events_room_v1_events_proto_rawDescGZIP(), []int{21}
|
||||
}
|
||||
|
||||
func (x *RoomRocketIgnited) GetRocketId() string {
|
||||
@ -2090,7 +1906,7 @@ type RoomRocketLaunched struct {
|
||||
|
||||
func (x *RoomRocketLaunched) Reset() {
|
||||
*x = RoomRocketLaunched{}
|
||||
mi := &file_proto_events_room_v1_events_proto_msgTypes[23]
|
||||
mi := &file_proto_events_room_v1_events_proto_msgTypes[22]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@ -2102,7 +1918,7 @@ func (x *RoomRocketLaunched) String() string {
|
||||
func (*RoomRocketLaunched) ProtoMessage() {}
|
||||
|
||||
func (x *RoomRocketLaunched) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_events_room_v1_events_proto_msgTypes[23]
|
||||
mi := &file_proto_events_room_v1_events_proto_msgTypes[22]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@ -2115,7 +1931,7 @@ func (x *RoomRocketLaunched) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use RoomRocketLaunched.ProtoReflect.Descriptor instead.
|
||||
func (*RoomRocketLaunched) Descriptor() ([]byte, []int) {
|
||||
return file_proto_events_room_v1_events_proto_rawDescGZIP(), []int{23}
|
||||
return file_proto_events_room_v1_events_proto_rawDescGZIP(), []int{22}
|
||||
}
|
||||
|
||||
func (x *RoomRocketLaunched) GetRocketId() string {
|
||||
@ -2193,7 +2009,7 @@ type RoomRocketRewardGranted struct {
|
||||
|
||||
func (x *RoomRocketRewardGranted) Reset() {
|
||||
*x = RoomRocketRewardGranted{}
|
||||
mi := &file_proto_events_room_v1_events_proto_msgTypes[24]
|
||||
mi := &file_proto_events_room_v1_events_proto_msgTypes[23]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@ -2205,7 +2021,7 @@ func (x *RoomRocketRewardGranted) String() string {
|
||||
func (*RoomRocketRewardGranted) ProtoMessage() {}
|
||||
|
||||
func (x *RoomRocketRewardGranted) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_events_room_v1_events_proto_msgTypes[24]
|
||||
mi := &file_proto_events_room_v1_events_proto_msgTypes[23]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@ -2218,7 +2034,7 @@ func (x *RoomRocketRewardGranted) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use RoomRocketRewardGranted.ProtoReflect.Descriptor instead.
|
||||
func (*RoomRocketRewardGranted) Descriptor() ([]byte, []int) {
|
||||
return file_proto_events_room_v1_events_proto_rawDescGZIP(), []int{24}
|
||||
return file_proto_events_room_v1_events_proto_rawDescGZIP(), []int{23}
|
||||
}
|
||||
|
||||
func (x *RoomRocketRewardGranted) GetRocketId() string {
|
||||
@ -2344,7 +2160,7 @@ const file_proto_events_room_v1_events_proto_rawDesc = "" +
|
||||
"\x0etarget_user_id\x18\x02 \x01(\x03R\ftargetUserId\"\\\n" +
|
||||
"\x10RoomUserUnbanned\x12\"\n" +
|
||||
"\ractor_user_id\x18\x01 \x01(\x03R\vactorUserId\x12$\n" +
|
||||
"\x0etarget_user_id\x18\x02 \x01(\x03R\ftargetUserId\"\x8b\x06\n" +
|
||||
"\x0etarget_user_id\x18\x02 \x01(\x03R\ftargetUserId\"\xb5\x05\n" +
|
||||
"\fRoomGiftSent\x12$\n" +
|
||||
"\x0esender_user_id\x18\x01 \x01(\x03R\fsenderUserId\x12$\n" +
|
||||
"\x0etarget_user_id\x18\x02 \x01(\x03R\ftargetUserId\x12\x17\n" +
|
||||
@ -2370,29 +2186,7 @@ const file_proto_events_room_v1_events_proto_rawDesc = "" +
|
||||
"\rgift_icon_url\x18\x10 \x01(\tR\vgiftIconUrl\x12,\n" +
|
||||
"\x12gift_animation_url\x18\x11 \x01(\tR\x10giftAnimationUrl\x12*\n" +
|
||||
"\x11target_gift_value\x18\x12 \x01(\x03R\x0ftargetGiftValue\x12*\n" +
|
||||
"\x11gift_effect_types\x18\x13 \x03(\tR\x0fgiftEffectTypes\x12\"\n" +
|
||||
"\ris_robot_gift\x18\x14 \x01(\bR\visRobotGift\x120\n" +
|
||||
"\x14synthetic_lucky_gift\x18\x15 \x01(\bR\x12syntheticLuckyGift\"\xf7\x04\n" +
|
||||
"\x17RoomRobotLuckyGiftDrawn\x12\x17\n" +
|
||||
"\adraw_id\x18\x01 \x01(\tR\x06drawId\x12\x1d\n" +
|
||||
"\n" +
|
||||
"command_id\x18\x02 \x01(\tR\tcommandId\x12$\n" +
|
||||
"\x0esender_user_id\x18\x03 \x01(\x03R\fsenderUserId\x12$\n" +
|
||||
"\x0etarget_user_id\x18\x04 \x01(\x03R\ftargetUserId\x12\x17\n" +
|
||||
"\agift_id\x18\x05 \x01(\tR\x06giftId\x12\x1d\n" +
|
||||
"\n" +
|
||||
"gift_count\x18\x06 \x01(\x05R\tgiftCount\x12\x17\n" +
|
||||
"\apool_id\x18\a \x01(\tR\x06poolId\x12%\n" +
|
||||
"\x0emultiplier_ppm\x18\b \x01(\x03R\rmultiplierPpm\x12*\n" +
|
||||
"\x11base_reward_coins\x18\t \x01(\x03R\x0fbaseRewardCoins\x12?\n" +
|
||||
"\x1croom_atmosphere_reward_coins\x18\n" +
|
||||
" \x01(\x03R\x19roomAtmosphereRewardCoins\x124\n" +
|
||||
"\x16activity_subsidy_coins\x18\v \x01(\x03R\x14activitySubsidyCoins\x124\n" +
|
||||
"\x16effective_reward_coins\x18\f \x01(\x03R\x14effectiveRewardCoins\x12\"\n" +
|
||||
"\rcreated_at_ms\x18\r \x01(\x03R\vcreatedAtMs\x12*\n" +
|
||||
"\x11visible_region_id\x18\x0e \x01(\x03R\x0fvisibleRegionId\x12\x19\n" +
|
||||
"\bis_robot\x18\x0f \x01(\bR\aisRobot\x12\x1c\n" +
|
||||
"\tsynthetic\x18\x10 \x01(\bR\tsynthetic\"J\n" +
|
||||
"\x11gift_effect_types\x18\x13 \x03(\tR\x0fgiftEffectTypes\"J\n" +
|
||||
"\x0fRoomHeatChanged\x12\x14\n" +
|
||||
"\x05delta\x18\x01 \x01(\x03R\x05delta\x12!\n" +
|
||||
"\fcurrent_heat\x18\x02 \x01(\x03R\vcurrentHeat\"_\n" +
|
||||
@ -2478,7 +2272,7 @@ func file_proto_events_room_v1_events_proto_rawDescGZIP() []byte {
|
||||
return file_proto_events_room_v1_events_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_proto_events_room_v1_events_proto_msgTypes = make([]protoimpl.MessageInfo, 25)
|
||||
var file_proto_events_room_v1_events_proto_msgTypes = make([]protoimpl.MessageInfo, 24)
|
||||
var file_proto_events_room_v1_events_proto_goTypes = []any{
|
||||
(*EventEnvelope)(nil), // 0: hyapp.events.room.v1.EventEnvelope
|
||||
(*RoomCreated)(nil), // 1: hyapp.events.room.v1.RoomCreated
|
||||
@ -2497,19 +2291,18 @@ var file_proto_events_room_v1_events_proto_goTypes = []any{
|
||||
(*RoomUserKicked)(nil), // 14: hyapp.events.room.v1.RoomUserKicked
|
||||
(*RoomUserUnbanned)(nil), // 15: hyapp.events.room.v1.RoomUserUnbanned
|
||||
(*RoomGiftSent)(nil), // 16: hyapp.events.room.v1.RoomGiftSent
|
||||
(*RoomRobotLuckyGiftDrawn)(nil), // 17: hyapp.events.room.v1.RoomRobotLuckyGiftDrawn
|
||||
(*RoomHeatChanged)(nil), // 18: hyapp.events.room.v1.RoomHeatChanged
|
||||
(*RoomRankChanged)(nil), // 19: hyapp.events.room.v1.RoomRankChanged
|
||||
(*RoomRocketRewardGrant)(nil), // 20: hyapp.events.room.v1.RoomRocketRewardGrant
|
||||
(*RoomRocketFuelChanged)(nil), // 21: hyapp.events.room.v1.RoomRocketFuelChanged
|
||||
(*RoomRocketIgnited)(nil), // 22: hyapp.events.room.v1.RoomRocketIgnited
|
||||
(*RoomRocketLaunched)(nil), // 23: hyapp.events.room.v1.RoomRocketLaunched
|
||||
(*RoomRocketRewardGranted)(nil), // 24: hyapp.events.room.v1.RoomRocketRewardGranted
|
||||
(*RoomHeatChanged)(nil), // 17: hyapp.events.room.v1.RoomHeatChanged
|
||||
(*RoomRankChanged)(nil), // 18: hyapp.events.room.v1.RoomRankChanged
|
||||
(*RoomRocketRewardGrant)(nil), // 19: hyapp.events.room.v1.RoomRocketRewardGrant
|
||||
(*RoomRocketFuelChanged)(nil), // 20: hyapp.events.room.v1.RoomRocketFuelChanged
|
||||
(*RoomRocketIgnited)(nil), // 21: hyapp.events.room.v1.RoomRocketIgnited
|
||||
(*RoomRocketLaunched)(nil), // 22: hyapp.events.room.v1.RoomRocketLaunched
|
||||
(*RoomRocketRewardGranted)(nil), // 23: hyapp.events.room.v1.RoomRocketRewardGranted
|
||||
}
|
||||
var file_proto_events_room_v1_events_proto_depIdxs = []int32{
|
||||
4, // 0: hyapp.events.room.v1.RoomUserJoined.entry_vehicle:type_name -> hyapp.events.room.v1.RoomEntryVehicleSnapshot
|
||||
20, // 1: hyapp.events.room.v1.RoomRocketLaunched.rewards:type_name -> hyapp.events.room.v1.RoomRocketRewardGrant
|
||||
20, // 2: hyapp.events.room.v1.RoomRocketRewardGranted.rewards:type_name -> hyapp.events.room.v1.RoomRocketRewardGrant
|
||||
19, // 1: hyapp.events.room.v1.RoomRocketLaunched.rewards:type_name -> hyapp.events.room.v1.RoomRocketRewardGrant
|
||||
19, // 2: hyapp.events.room.v1.RoomRocketRewardGranted.rewards:type_name -> hyapp.events.room.v1.RoomRocketRewardGrant
|
||||
3, // [3:3] is the sub-list for method output_type
|
||||
3, // [3:3] is the sub-list for method input_type
|
||||
3, // [3:3] is the sub-list for extension type_name
|
||||
@ -2528,7 +2321,7 @@ func file_proto_events_room_v1_events_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_proto_events_room_v1_events_proto_rawDesc), len(file_proto_events_room_v1_events_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 25,
|
||||
NumMessages: 24,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
|
||||
@ -166,31 +166,6 @@ message RoomGiftSent {
|
||||
string gift_animation_url = 17;
|
||||
int64 target_gift_value = 18;
|
||||
repeated string gift_effect_types = 19;
|
||||
// is_robot_gift 标记机器人房间展示用礼物事实;下游真实统计、任务和结算消费者必须跳过。
|
||||
bool is_robot_gift = 20;
|
||||
// synthetic_lucky_gift 标记幸运礼物只用于房间表现,不写真实抽奖流水、不扣真实奖池、不发真实奖励。
|
||||
bool synthetic_lucky_gift = 21;
|
||||
}
|
||||
|
||||
// RoomRobotLuckyGiftDrawn 是机器人房间专用幸运礼物展示事件。
|
||||
// 它复用客户端 lucky_gift_drawn 表现协议,但不代表真实奖池抽奖和钱包返奖事实。
|
||||
message RoomRobotLuckyGiftDrawn {
|
||||
string draw_id = 1;
|
||||
string command_id = 2;
|
||||
int64 sender_user_id = 3;
|
||||
int64 target_user_id = 4;
|
||||
string gift_id = 5;
|
||||
int32 gift_count = 6;
|
||||
string pool_id = 7;
|
||||
int64 multiplier_ppm = 8;
|
||||
int64 base_reward_coins = 9;
|
||||
int64 room_atmosphere_reward_coins = 10;
|
||||
int64 activity_subsidy_coins = 11;
|
||||
int64 effective_reward_coins = 12;
|
||||
int64 created_at_ms = 13;
|
||||
int64 visible_region_id = 14;
|
||||
bool is_robot = 15;
|
||||
bool synthetic = 16;
|
||||
}
|
||||
|
||||
// RoomHeatChanged 表达热度变化结果。
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -338,85 +338,6 @@ message AdminCancelRoomPinResponse {
|
||||
int64 server_time_ms = 2;
|
||||
}
|
||||
|
||||
message AdminRobotRoomGiftRule {
|
||||
repeated string gift_ids = 1;
|
||||
repeated string lucky_gift_ids = 2;
|
||||
int64 normal_gift_interval_ms = 3;
|
||||
int64 lucky_combo_min = 4;
|
||||
int64 lucky_combo_max = 5;
|
||||
int64 lucky_pause_min_ms = 6;
|
||||
int64 lucky_pause_max_ms = 7;
|
||||
}
|
||||
|
||||
message AdminRobotRoom {
|
||||
string app_code = 1;
|
||||
string room_id = 2;
|
||||
string room_short_id = 3;
|
||||
string title = 4;
|
||||
string cover_url = 5;
|
||||
int64 visible_region_id = 6;
|
||||
string status = 7;
|
||||
int64 owner_robot_user_id = 8;
|
||||
repeated int64 robot_user_ids = 9;
|
||||
AdminRobotRoomGiftRule gift_rule = 10;
|
||||
uint64 created_by_admin_id = 11;
|
||||
int64 created_at_ms = 12;
|
||||
int64 updated_at_ms = 13;
|
||||
}
|
||||
|
||||
message AdminListRobotRoomsRequest {
|
||||
RequestMeta meta = 1;
|
||||
int32 page = 2;
|
||||
int32 page_size = 3;
|
||||
string status = 4;
|
||||
}
|
||||
|
||||
message AdminListRobotRoomsResponse {
|
||||
repeated AdminRobotRoom rooms = 1;
|
||||
int64 total = 2;
|
||||
int64 server_time_ms = 3;
|
||||
}
|
||||
|
||||
message AdminCreateRobotRoomRequest {
|
||||
RequestMeta meta = 1;
|
||||
int64 owner_robot_user_id = 2;
|
||||
repeated int64 candidate_robot_user_ids = 3;
|
||||
int32 min_robot_count = 4;
|
||||
int32 max_robot_count = 5;
|
||||
string room_name = 6;
|
||||
string room_avatar = 7;
|
||||
int64 visible_region_id = 8;
|
||||
AdminRobotRoomGiftRule gift_rule = 9;
|
||||
uint64 admin_id = 10;
|
||||
}
|
||||
|
||||
message AdminCreateRobotRoomResponse {
|
||||
AdminRobotRoom room = 1;
|
||||
int64 server_time_ms = 2;
|
||||
}
|
||||
|
||||
message AdminSetRobotRoomStatusRequest {
|
||||
RequestMeta meta = 1;
|
||||
string status = 2;
|
||||
uint64 admin_id = 3;
|
||||
}
|
||||
|
||||
message AdminSetRobotRoomStatusResponse {
|
||||
AdminRobotRoom room = 1;
|
||||
int64 server_time_ms = 2;
|
||||
}
|
||||
|
||||
message AdminFilterAvailableRoomRobotsRequest {
|
||||
RequestMeta meta = 1;
|
||||
repeated int64 user_ids = 2;
|
||||
}
|
||||
|
||||
message AdminFilterAvailableRoomRobotsResponse {
|
||||
repeated int64 available_user_ids = 1;
|
||||
repeated int64 occupied_user_ids = 2;
|
||||
int64 server_time_ms = 3;
|
||||
}
|
||||
|
||||
// RoomBanState 表达单个房间 ban 的治理状态。
|
||||
message RoomBanState {
|
||||
int64 user_id = 1;
|
||||
@ -519,10 +440,6 @@ message CreateRoomRequest {
|
||||
string room_short_id = 8;
|
||||
// owner_country_code 由 gateway 按创建者 user-service 当前国家填充,用于发现页国家分类筛选。
|
||||
string owner_country_code = 9;
|
||||
// robot_room 只供内部机器人房间编排器设置;gateway 不接受客户端提交该字段。
|
||||
bool robot_room = 10;
|
||||
// robot_user_ids 是该机器人房间允许参与机器人送礼和虚拟上麦的用户集合。
|
||||
repeated int64 robot_user_ids = 11;
|
||||
}
|
||||
|
||||
// CreateRoomResponse 返回新建后的房间快照。
|
||||
@ -1232,8 +1149,6 @@ service RoomCommandService {
|
||||
rpc AdminUpdateRoomSeatConfig(AdminUpdateRoomSeatConfigRequest) returns (AdminUpdateRoomSeatConfigResponse);
|
||||
rpc AdminCreateRoomPin(AdminCreateRoomPinRequest) returns (AdminCreateRoomPinResponse);
|
||||
rpc AdminCancelRoomPin(AdminCancelRoomPinRequest) returns (AdminCancelRoomPinResponse);
|
||||
rpc AdminCreateRobotRoom(AdminCreateRobotRoomRequest) returns (AdminCreateRobotRoomResponse);
|
||||
rpc AdminSetRobotRoomStatus(AdminSetRobotRoomStatusRequest) returns (AdminSetRobotRoomStatusResponse);
|
||||
rpc MicUp(MicUpRequest) returns (MicUpResponse);
|
||||
rpc MicDown(MicDownRequest) returns (MicDownResponse);
|
||||
rpc ChangeMicSeat(ChangeMicSeatRequest) returns (ChangeMicSeatResponse);
|
||||
@ -1267,8 +1182,6 @@ service RoomQueryService {
|
||||
rpc AdminGetRoomRocketConfig(AdminGetRoomRocketConfigRequest) returns (AdminGetRoomRocketConfigResponse);
|
||||
rpc AdminGetRoomSeatConfig(AdminGetRoomSeatConfigRequest) returns (AdminGetRoomSeatConfigResponse);
|
||||
rpc AdminListRoomPins(AdminListRoomPinsRequest) returns (AdminListRoomPinsResponse);
|
||||
rpc AdminListRobotRooms(AdminListRobotRoomsRequest) returns (AdminListRobotRoomsResponse);
|
||||
rpc AdminFilterAvailableRoomRobots(AdminFilterAvailableRoomRobotsRequest) returns (AdminFilterAvailableRoomRobotsResponse);
|
||||
rpc ListRooms(ListRoomsRequest) returns (ListRoomsResponse);
|
||||
rpc ListRoomFeeds(ListRoomFeedsRequest) returns (ListRoomsResponse);
|
||||
rpc ListRoomGiftLeaderboard(ListRoomGiftLeaderboardRequest) returns (ListRoomGiftLeaderboardResponse);
|
||||
|
||||
@ -33,8 +33,6 @@ const (
|
||||
RoomCommandService_AdminUpdateRoomSeatConfig_FullMethodName = "/hyapp.room.v1.RoomCommandService/AdminUpdateRoomSeatConfig"
|
||||
RoomCommandService_AdminCreateRoomPin_FullMethodName = "/hyapp.room.v1.RoomCommandService/AdminCreateRoomPin"
|
||||
RoomCommandService_AdminCancelRoomPin_FullMethodName = "/hyapp.room.v1.RoomCommandService/AdminCancelRoomPin"
|
||||
RoomCommandService_AdminCreateRobotRoom_FullMethodName = "/hyapp.room.v1.RoomCommandService/AdminCreateRobotRoom"
|
||||
RoomCommandService_AdminSetRobotRoomStatus_FullMethodName = "/hyapp.room.v1.RoomCommandService/AdminSetRobotRoomStatus"
|
||||
RoomCommandService_MicUp_FullMethodName = "/hyapp.room.v1.RoomCommandService/MicUp"
|
||||
RoomCommandService_MicDown_FullMethodName = "/hyapp.room.v1.RoomCommandService/MicDown"
|
||||
RoomCommandService_ChangeMicSeat_FullMethodName = "/hyapp.room.v1.RoomCommandService/ChangeMicSeat"
|
||||
@ -75,8 +73,6 @@ type RoomCommandServiceClient interface {
|
||||
AdminUpdateRoomSeatConfig(ctx context.Context, in *AdminUpdateRoomSeatConfigRequest, opts ...grpc.CallOption) (*AdminUpdateRoomSeatConfigResponse, error)
|
||||
AdminCreateRoomPin(ctx context.Context, in *AdminCreateRoomPinRequest, opts ...grpc.CallOption) (*AdminCreateRoomPinResponse, error)
|
||||
AdminCancelRoomPin(ctx context.Context, in *AdminCancelRoomPinRequest, opts ...grpc.CallOption) (*AdminCancelRoomPinResponse, error)
|
||||
AdminCreateRobotRoom(ctx context.Context, in *AdminCreateRobotRoomRequest, opts ...grpc.CallOption) (*AdminCreateRobotRoomResponse, error)
|
||||
AdminSetRobotRoomStatus(ctx context.Context, in *AdminSetRobotRoomStatusRequest, opts ...grpc.CallOption) (*AdminSetRobotRoomStatusResponse, error)
|
||||
MicUp(ctx context.Context, in *MicUpRequest, opts ...grpc.CallOption) (*MicUpResponse, error)
|
||||
MicDown(ctx context.Context, in *MicDownRequest, opts ...grpc.CallOption) (*MicDownResponse, error)
|
||||
ChangeMicSeat(ctx context.Context, in *ChangeMicSeatRequest, opts ...grpc.CallOption) (*ChangeMicSeatResponse, error)
|
||||
@ -245,26 +241,6 @@ func (c *roomCommandServiceClient) AdminCancelRoomPin(ctx context.Context, in *A
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *roomCommandServiceClient) AdminCreateRobotRoom(ctx context.Context, in *AdminCreateRobotRoomRequest, opts ...grpc.CallOption) (*AdminCreateRobotRoomResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(AdminCreateRobotRoomResponse)
|
||||
err := c.cc.Invoke(ctx, RoomCommandService_AdminCreateRobotRoom_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *roomCommandServiceClient) AdminSetRobotRoomStatus(ctx context.Context, in *AdminSetRobotRoomStatusRequest, opts ...grpc.CallOption) (*AdminSetRobotRoomStatusResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(AdminSetRobotRoomStatusResponse)
|
||||
err := c.cc.Invoke(ctx, RoomCommandService_AdminSetRobotRoomStatus_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *roomCommandServiceClient) MicUp(ctx context.Context, in *MicUpRequest, opts ...grpc.CallOption) (*MicUpResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(MicUpResponse)
|
||||
@ -465,8 +441,6 @@ type RoomCommandServiceServer interface {
|
||||
AdminUpdateRoomSeatConfig(context.Context, *AdminUpdateRoomSeatConfigRequest) (*AdminUpdateRoomSeatConfigResponse, error)
|
||||
AdminCreateRoomPin(context.Context, *AdminCreateRoomPinRequest) (*AdminCreateRoomPinResponse, error)
|
||||
AdminCancelRoomPin(context.Context, *AdminCancelRoomPinRequest) (*AdminCancelRoomPinResponse, error)
|
||||
AdminCreateRobotRoom(context.Context, *AdminCreateRobotRoomRequest) (*AdminCreateRobotRoomResponse, error)
|
||||
AdminSetRobotRoomStatus(context.Context, *AdminSetRobotRoomStatusRequest) (*AdminSetRobotRoomStatusResponse, error)
|
||||
MicUp(context.Context, *MicUpRequest) (*MicUpResponse, error)
|
||||
MicDown(context.Context, *MicDownRequest) (*MicDownResponse, error)
|
||||
ChangeMicSeat(context.Context, *ChangeMicSeatRequest) (*ChangeMicSeatResponse, error)
|
||||
@ -537,12 +511,6 @@ func (UnimplementedRoomCommandServiceServer) AdminCreateRoomPin(context.Context,
|
||||
func (UnimplementedRoomCommandServiceServer) AdminCancelRoomPin(context.Context, *AdminCancelRoomPinRequest) (*AdminCancelRoomPinResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method AdminCancelRoomPin not implemented")
|
||||
}
|
||||
func (UnimplementedRoomCommandServiceServer) AdminCreateRobotRoom(context.Context, *AdminCreateRobotRoomRequest) (*AdminCreateRobotRoomResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method AdminCreateRobotRoom not implemented")
|
||||
}
|
||||
func (UnimplementedRoomCommandServiceServer) AdminSetRobotRoomStatus(context.Context, *AdminSetRobotRoomStatusRequest) (*AdminSetRobotRoomStatusResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method AdminSetRobotRoomStatus not implemented")
|
||||
}
|
||||
func (UnimplementedRoomCommandServiceServer) MicUp(context.Context, *MicUpRequest) (*MicUpResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method MicUp not implemented")
|
||||
}
|
||||
@ -870,42 +838,6 @@ func _RoomCommandService_AdminCancelRoomPin_Handler(srv interface{}, ctx context
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _RoomCommandService_AdminCreateRobotRoom_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(AdminCreateRobotRoomRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(RoomCommandServiceServer).AdminCreateRobotRoom(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: RoomCommandService_AdminCreateRobotRoom_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(RoomCommandServiceServer).AdminCreateRobotRoom(ctx, req.(*AdminCreateRobotRoomRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _RoomCommandService_AdminSetRobotRoomStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(AdminSetRobotRoomStatusRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(RoomCommandServiceServer).AdminSetRobotRoomStatus(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: RoomCommandService_AdminSetRobotRoomStatus_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(RoomCommandServiceServer).AdminSetRobotRoomStatus(ctx, req.(*AdminSetRobotRoomStatusRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _RoomCommandService_MicUp_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(MicUpRequest)
|
||||
if err := dec(in); err != nil {
|
||||
@ -1293,14 +1225,6 @@ var RoomCommandService_ServiceDesc = grpc.ServiceDesc{
|
||||
MethodName: "AdminCancelRoomPin",
|
||||
Handler: _RoomCommandService_AdminCancelRoomPin_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "AdminCreateRobotRoom",
|
||||
Handler: _RoomCommandService_AdminCreateRobotRoom_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "AdminSetRobotRoomStatus",
|
||||
Handler: _RoomCommandService_AdminSetRobotRoomStatus_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "MicUp",
|
||||
Handler: _RoomCommandService_MicUp_Handler,
|
||||
@ -1523,23 +1447,21 @@ var RoomGuardService_ServiceDesc = grpc.ServiceDesc{
|
||||
}
|
||||
|
||||
const (
|
||||
RoomQueryService_AdminListRooms_FullMethodName = "/hyapp.room.v1.RoomQueryService/AdminListRooms"
|
||||
RoomQueryService_AdminGetRoom_FullMethodName = "/hyapp.room.v1.RoomQueryService/AdminGetRoom"
|
||||
RoomQueryService_AdminGetRoomRocketConfig_FullMethodName = "/hyapp.room.v1.RoomQueryService/AdminGetRoomRocketConfig"
|
||||
RoomQueryService_AdminGetRoomSeatConfig_FullMethodName = "/hyapp.room.v1.RoomQueryService/AdminGetRoomSeatConfig"
|
||||
RoomQueryService_AdminListRoomPins_FullMethodName = "/hyapp.room.v1.RoomQueryService/AdminListRoomPins"
|
||||
RoomQueryService_AdminListRobotRooms_FullMethodName = "/hyapp.room.v1.RoomQueryService/AdminListRobotRooms"
|
||||
RoomQueryService_AdminFilterAvailableRoomRobots_FullMethodName = "/hyapp.room.v1.RoomQueryService/AdminFilterAvailableRoomRobots"
|
||||
RoomQueryService_ListRooms_FullMethodName = "/hyapp.room.v1.RoomQueryService/ListRooms"
|
||||
RoomQueryService_ListRoomFeeds_FullMethodName = "/hyapp.room.v1.RoomQueryService/ListRoomFeeds"
|
||||
RoomQueryService_ListRoomGiftLeaderboard_FullMethodName = "/hyapp.room.v1.RoomQueryService/ListRoomGiftLeaderboard"
|
||||
RoomQueryService_GetMyRoom_FullMethodName = "/hyapp.room.v1.RoomQueryService/GetMyRoom"
|
||||
RoomQueryService_GetCurrentRoom_FullMethodName = "/hyapp.room.v1.RoomQueryService/GetCurrentRoom"
|
||||
RoomQueryService_GetRoomSnapshot_FullMethodName = "/hyapp.room.v1.RoomQueryService/GetRoomSnapshot"
|
||||
RoomQueryService_ListRoomBackgrounds_FullMethodName = "/hyapp.room.v1.RoomQueryService/ListRoomBackgrounds"
|
||||
RoomQueryService_GetRoomRocket_FullMethodName = "/hyapp.room.v1.RoomQueryService/GetRoomRocket"
|
||||
RoomQueryService_ListRoomOnlineUsers_FullMethodName = "/hyapp.room.v1.RoomQueryService/ListRoomOnlineUsers"
|
||||
RoomQueryService_ListRoomBannedUsers_FullMethodName = "/hyapp.room.v1.RoomQueryService/ListRoomBannedUsers"
|
||||
RoomQueryService_AdminListRooms_FullMethodName = "/hyapp.room.v1.RoomQueryService/AdminListRooms"
|
||||
RoomQueryService_AdminGetRoom_FullMethodName = "/hyapp.room.v1.RoomQueryService/AdminGetRoom"
|
||||
RoomQueryService_AdminGetRoomRocketConfig_FullMethodName = "/hyapp.room.v1.RoomQueryService/AdminGetRoomRocketConfig"
|
||||
RoomQueryService_AdminGetRoomSeatConfig_FullMethodName = "/hyapp.room.v1.RoomQueryService/AdminGetRoomSeatConfig"
|
||||
RoomQueryService_AdminListRoomPins_FullMethodName = "/hyapp.room.v1.RoomQueryService/AdminListRoomPins"
|
||||
RoomQueryService_ListRooms_FullMethodName = "/hyapp.room.v1.RoomQueryService/ListRooms"
|
||||
RoomQueryService_ListRoomFeeds_FullMethodName = "/hyapp.room.v1.RoomQueryService/ListRoomFeeds"
|
||||
RoomQueryService_ListRoomGiftLeaderboard_FullMethodName = "/hyapp.room.v1.RoomQueryService/ListRoomGiftLeaderboard"
|
||||
RoomQueryService_GetMyRoom_FullMethodName = "/hyapp.room.v1.RoomQueryService/GetMyRoom"
|
||||
RoomQueryService_GetCurrentRoom_FullMethodName = "/hyapp.room.v1.RoomQueryService/GetCurrentRoom"
|
||||
RoomQueryService_GetRoomSnapshot_FullMethodName = "/hyapp.room.v1.RoomQueryService/GetRoomSnapshot"
|
||||
RoomQueryService_ListRoomBackgrounds_FullMethodName = "/hyapp.room.v1.RoomQueryService/ListRoomBackgrounds"
|
||||
RoomQueryService_GetRoomRocket_FullMethodName = "/hyapp.room.v1.RoomQueryService/GetRoomRocket"
|
||||
RoomQueryService_ListRoomOnlineUsers_FullMethodName = "/hyapp.room.v1.RoomQueryService/ListRoomOnlineUsers"
|
||||
RoomQueryService_ListRoomBannedUsers_FullMethodName = "/hyapp.room.v1.RoomQueryService/ListRoomBannedUsers"
|
||||
)
|
||||
|
||||
// RoomQueryServiceClient is the client API for RoomQueryService service.
|
||||
@ -1553,8 +1475,6 @@ type RoomQueryServiceClient interface {
|
||||
AdminGetRoomRocketConfig(ctx context.Context, in *AdminGetRoomRocketConfigRequest, opts ...grpc.CallOption) (*AdminGetRoomRocketConfigResponse, error)
|
||||
AdminGetRoomSeatConfig(ctx context.Context, in *AdminGetRoomSeatConfigRequest, opts ...grpc.CallOption) (*AdminGetRoomSeatConfigResponse, error)
|
||||
AdminListRoomPins(ctx context.Context, in *AdminListRoomPinsRequest, opts ...grpc.CallOption) (*AdminListRoomPinsResponse, error)
|
||||
AdminListRobotRooms(ctx context.Context, in *AdminListRobotRoomsRequest, opts ...grpc.CallOption) (*AdminListRobotRoomsResponse, error)
|
||||
AdminFilterAvailableRoomRobots(ctx context.Context, in *AdminFilterAvailableRoomRobotsRequest, opts ...grpc.CallOption) (*AdminFilterAvailableRoomRobotsResponse, error)
|
||||
ListRooms(ctx context.Context, in *ListRoomsRequest, opts ...grpc.CallOption) (*ListRoomsResponse, error)
|
||||
ListRoomFeeds(ctx context.Context, in *ListRoomFeedsRequest, opts ...grpc.CallOption) (*ListRoomsResponse, error)
|
||||
ListRoomGiftLeaderboard(ctx context.Context, in *ListRoomGiftLeaderboardRequest, opts ...grpc.CallOption) (*ListRoomGiftLeaderboardResponse, error)
|
||||
@ -1625,26 +1545,6 @@ func (c *roomQueryServiceClient) AdminListRoomPins(ctx context.Context, in *Admi
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *roomQueryServiceClient) AdminListRobotRooms(ctx context.Context, in *AdminListRobotRoomsRequest, opts ...grpc.CallOption) (*AdminListRobotRoomsResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(AdminListRobotRoomsResponse)
|
||||
err := c.cc.Invoke(ctx, RoomQueryService_AdminListRobotRooms_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *roomQueryServiceClient) AdminFilterAvailableRoomRobots(ctx context.Context, in *AdminFilterAvailableRoomRobotsRequest, opts ...grpc.CallOption) (*AdminFilterAvailableRoomRobotsResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(AdminFilterAvailableRoomRobotsResponse)
|
||||
err := c.cc.Invoke(ctx, RoomQueryService_AdminFilterAvailableRoomRobots_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *roomQueryServiceClient) ListRooms(ctx context.Context, in *ListRoomsRequest, opts ...grpc.CallOption) (*ListRoomsResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(ListRoomsResponse)
|
||||
@ -1756,8 +1656,6 @@ type RoomQueryServiceServer interface {
|
||||
AdminGetRoomRocketConfig(context.Context, *AdminGetRoomRocketConfigRequest) (*AdminGetRoomRocketConfigResponse, error)
|
||||
AdminGetRoomSeatConfig(context.Context, *AdminGetRoomSeatConfigRequest) (*AdminGetRoomSeatConfigResponse, error)
|
||||
AdminListRoomPins(context.Context, *AdminListRoomPinsRequest) (*AdminListRoomPinsResponse, error)
|
||||
AdminListRobotRooms(context.Context, *AdminListRobotRoomsRequest) (*AdminListRobotRoomsResponse, error)
|
||||
AdminFilterAvailableRoomRobots(context.Context, *AdminFilterAvailableRoomRobotsRequest) (*AdminFilterAvailableRoomRobotsResponse, error)
|
||||
ListRooms(context.Context, *ListRoomsRequest) (*ListRoomsResponse, error)
|
||||
ListRoomFeeds(context.Context, *ListRoomFeedsRequest) (*ListRoomsResponse, error)
|
||||
ListRoomGiftLeaderboard(context.Context, *ListRoomGiftLeaderboardRequest) (*ListRoomGiftLeaderboardResponse, error)
|
||||
@ -1793,12 +1691,6 @@ func (UnimplementedRoomQueryServiceServer) AdminGetRoomSeatConfig(context.Contex
|
||||
func (UnimplementedRoomQueryServiceServer) AdminListRoomPins(context.Context, *AdminListRoomPinsRequest) (*AdminListRoomPinsResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method AdminListRoomPins not implemented")
|
||||
}
|
||||
func (UnimplementedRoomQueryServiceServer) AdminListRobotRooms(context.Context, *AdminListRobotRoomsRequest) (*AdminListRobotRoomsResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method AdminListRobotRooms not implemented")
|
||||
}
|
||||
func (UnimplementedRoomQueryServiceServer) AdminFilterAvailableRoomRobots(context.Context, *AdminFilterAvailableRoomRobotsRequest) (*AdminFilterAvailableRoomRobotsResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method AdminFilterAvailableRoomRobots not implemented")
|
||||
}
|
||||
func (UnimplementedRoomQueryServiceServer) ListRooms(context.Context, *ListRoomsRequest) (*ListRoomsResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method ListRooms not implemented")
|
||||
}
|
||||
@ -1940,42 +1832,6 @@ func _RoomQueryService_AdminListRoomPins_Handler(srv interface{}, ctx context.Co
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _RoomQueryService_AdminListRobotRooms_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(AdminListRobotRoomsRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(RoomQueryServiceServer).AdminListRobotRooms(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: RoomQueryService_AdminListRobotRooms_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(RoomQueryServiceServer).AdminListRobotRooms(ctx, req.(*AdminListRobotRoomsRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _RoomQueryService_AdminFilterAvailableRoomRobots_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(AdminFilterAvailableRoomRobotsRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(RoomQueryServiceServer).AdminFilterAvailableRoomRobots(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: RoomQueryService_AdminFilterAvailableRoomRobots_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(RoomQueryServiceServer).AdminFilterAvailableRoomRobots(ctx, req.(*AdminFilterAvailableRoomRobotsRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _RoomQueryService_ListRooms_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ListRoomsRequest)
|
||||
if err := dec(in); err != nil {
|
||||
@ -2183,14 +2039,6 @@ var RoomQueryService_ServiceDesc = grpc.ServiceDesc{
|
||||
MethodName: "AdminListRoomPins",
|
||||
Handler: _RoomQueryService_AdminListRoomPins_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "AdminListRobotRooms",
|
||||
Handler: _RoomQueryService_AdminListRobotRooms_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "AdminFilterAvailableRoomRobots",
|
||||
Handler: _RoomQueryService_AdminFilterAvailableRoomRobots_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ListRooms",
|
||||
Handler: _RoomQueryService_ListRooms_Handler,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -85,7 +85,6 @@ message User {
|
||||
InviteOverview invite = 28;
|
||||
string pretty_id = 29;
|
||||
string pretty_display_user_id = 30;
|
||||
string profile_bg_img = 31;
|
||||
}
|
||||
|
||||
// InviteOverview 是我的页可直接展示的邀请码和邀请计数 read model。
|
||||
@ -477,33 +476,6 @@ message CPIntimacyLeaderboardItem {
|
||||
int64 updated_at_ms = 9;
|
||||
}
|
||||
|
||||
// CPWeeklyRankEntry 是 activity-service 结算 CP 周榜时读取的关系周积分快照。
|
||||
message CPWeeklyRankEntry {
|
||||
int64 rank = 1;
|
||||
string relationship_id = 2;
|
||||
string relation_type = 3;
|
||||
int64 score = 4;
|
||||
CPIntimacyLeaderboardUser user_a = 5;
|
||||
CPIntimacyLeaderboardUser user_b = 6;
|
||||
int64 formed_at_ms = 7;
|
||||
int64 updated_at_ms = 8;
|
||||
int64 first_scored_at_ms = 9;
|
||||
int64 last_scored_at_ms = 10;
|
||||
}
|
||||
|
||||
message ListCPWeeklyRankEntriesRequest {
|
||||
RequestMeta meta = 1;
|
||||
string relation_type = 2;
|
||||
int64 start_ms = 3;
|
||||
int64 end_ms = 4;
|
||||
int32 limit = 5;
|
||||
}
|
||||
|
||||
message ListCPWeeklyRankEntriesResponse {
|
||||
repeated CPWeeklyRankEntry entries = 1;
|
||||
int64 server_time_ms = 2;
|
||||
}
|
||||
|
||||
message ListCPApplicationsRequest {
|
||||
RequestMeta meta = 1;
|
||||
int64 user_id = 2;
|
||||
@ -721,18 +693,6 @@ message UpdateUserProfileResponse {
|
||||
User user = 1;
|
||||
}
|
||||
|
||||
// UpdateUserProfileBackgroundRequest 只修改用户个人信息页背景图。
|
||||
message UpdateUserProfileBackgroundRequest {
|
||||
RequestMeta meta = 1;
|
||||
int64 user_id = 2;
|
||||
string profile_bg_img = 3;
|
||||
}
|
||||
|
||||
// UpdateUserProfileBackgroundResponse 返回写入后的用户资料投影。
|
||||
message UpdateUserProfileBackgroundResponse {
|
||||
User user = 1;
|
||||
}
|
||||
|
||||
// ChangeUserCountryRequest 是 App 用户自助修改国家入口;受限业务身份不允许自助修改。
|
||||
message ChangeUserCountryRequest {
|
||||
RequestMeta meta = 1;
|
||||
@ -1271,7 +1231,6 @@ service UserService {
|
||||
rpc ListUserIDs(ListUserIDsRequest) returns (ListUserIDsResponse);
|
||||
rpc GetUserMicLifetimeStats(GetUserMicLifetimeStatsRequest) returns (GetUserMicLifetimeStatsResponse);
|
||||
rpc UpdateUserProfile(UpdateUserProfileRequest) returns (UpdateUserProfileResponse);
|
||||
rpc UpdateUserProfileBackground(UpdateUserProfileBackgroundRequest) returns (UpdateUserProfileBackgroundResponse);
|
||||
rpc ChangeUserCountry(ChangeUserCountryRequest) returns (ChangeUserCountryResponse);
|
||||
rpc AdminChangeUserCountry(AdminChangeUserCountryRequest) returns (ChangeUserCountryResponse);
|
||||
rpc SetUserStatus(SetUserStatusRequest) returns (SetUserStatusResponse);
|
||||
@ -1311,7 +1270,6 @@ service UserCPService {
|
||||
// UserCPInternalService 给 owner outbox 消费 worker 调用,不暴露给 App 或后台。
|
||||
service UserCPInternalService {
|
||||
rpc ConsumeRoomGiftCPEvent(ConsumeRoomGiftCPEventRequest) returns (ConsumeRoomGiftCPEventResponse);
|
||||
rpc ListCPWeeklyRankEntries(ListCPWeeklyRankEntriesRequest) returns (ListCPWeeklyRankEntriesResponse);
|
||||
}
|
||||
|
||||
// UserCronService 只给 cron-service 调用,业务状态仍由 user-service owner 修改。
|
||||
|
||||
@ -19,22 +19,21 @@ import (
|
||||
const _ = grpc.SupportPackageIsVersion9
|
||||
|
||||
const (
|
||||
UserService_GetUser_FullMethodName = "/hyapp.user.v1.UserService/GetUser"
|
||||
UserService_GetInviteAttribution_FullMethodName = "/hyapp.user.v1.UserService/GetInviteAttribution"
|
||||
UserService_BusinessUserLookup_FullMethodName = "/hyapp.user.v1.UserService/BusinessUserLookup"
|
||||
UserService_GetMyProfileStats_FullMethodName = "/hyapp.user.v1.UserService/GetMyProfileStats"
|
||||
UserService_BatchGetUsers_FullMethodName = "/hyapp.user.v1.UserService/BatchGetUsers"
|
||||
UserService_ListUserIDs_FullMethodName = "/hyapp.user.v1.UserService/ListUserIDs"
|
||||
UserService_GetUserMicLifetimeStats_FullMethodName = "/hyapp.user.v1.UserService/GetUserMicLifetimeStats"
|
||||
UserService_UpdateUserProfile_FullMethodName = "/hyapp.user.v1.UserService/UpdateUserProfile"
|
||||
UserService_UpdateUserProfileBackground_FullMethodName = "/hyapp.user.v1.UserService/UpdateUserProfileBackground"
|
||||
UserService_ChangeUserCountry_FullMethodName = "/hyapp.user.v1.UserService/ChangeUserCountry"
|
||||
UserService_AdminChangeUserCountry_FullMethodName = "/hyapp.user.v1.UserService/AdminChangeUserCountry"
|
||||
UserService_SetUserStatus_FullMethodName = "/hyapp.user.v1.UserService/SetUserStatus"
|
||||
UserService_CreateManagerUserBlock_FullMethodName = "/hyapp.user.v1.UserService/CreateManagerUserBlock"
|
||||
UserService_ListManagerUserBlocks_FullMethodName = "/hyapp.user.v1.UserService/ListManagerUserBlocks"
|
||||
UserService_UnblockManagerUser_FullMethodName = "/hyapp.user.v1.UserService/UnblockManagerUser"
|
||||
UserService_CompleteOnboarding_FullMethodName = "/hyapp.user.v1.UserService/CompleteOnboarding"
|
||||
UserService_GetUser_FullMethodName = "/hyapp.user.v1.UserService/GetUser"
|
||||
UserService_GetInviteAttribution_FullMethodName = "/hyapp.user.v1.UserService/GetInviteAttribution"
|
||||
UserService_BusinessUserLookup_FullMethodName = "/hyapp.user.v1.UserService/BusinessUserLookup"
|
||||
UserService_GetMyProfileStats_FullMethodName = "/hyapp.user.v1.UserService/GetMyProfileStats"
|
||||
UserService_BatchGetUsers_FullMethodName = "/hyapp.user.v1.UserService/BatchGetUsers"
|
||||
UserService_ListUserIDs_FullMethodName = "/hyapp.user.v1.UserService/ListUserIDs"
|
||||
UserService_GetUserMicLifetimeStats_FullMethodName = "/hyapp.user.v1.UserService/GetUserMicLifetimeStats"
|
||||
UserService_UpdateUserProfile_FullMethodName = "/hyapp.user.v1.UserService/UpdateUserProfile"
|
||||
UserService_ChangeUserCountry_FullMethodName = "/hyapp.user.v1.UserService/ChangeUserCountry"
|
||||
UserService_AdminChangeUserCountry_FullMethodName = "/hyapp.user.v1.UserService/AdminChangeUserCountry"
|
||||
UserService_SetUserStatus_FullMethodName = "/hyapp.user.v1.UserService/SetUserStatus"
|
||||
UserService_CreateManagerUserBlock_FullMethodName = "/hyapp.user.v1.UserService/CreateManagerUserBlock"
|
||||
UserService_ListManagerUserBlocks_FullMethodName = "/hyapp.user.v1.UserService/ListManagerUserBlocks"
|
||||
UserService_UnblockManagerUser_FullMethodName = "/hyapp.user.v1.UserService/UnblockManagerUser"
|
||||
UserService_CompleteOnboarding_FullMethodName = "/hyapp.user.v1.UserService/CompleteOnboarding"
|
||||
)
|
||||
|
||||
// UserServiceClient is the client API for UserService service.
|
||||
@ -51,7 +50,6 @@ type UserServiceClient interface {
|
||||
ListUserIDs(ctx context.Context, in *ListUserIDsRequest, opts ...grpc.CallOption) (*ListUserIDsResponse, error)
|
||||
GetUserMicLifetimeStats(ctx context.Context, in *GetUserMicLifetimeStatsRequest, opts ...grpc.CallOption) (*GetUserMicLifetimeStatsResponse, error)
|
||||
UpdateUserProfile(ctx context.Context, in *UpdateUserProfileRequest, opts ...grpc.CallOption) (*UpdateUserProfileResponse, error)
|
||||
UpdateUserProfileBackground(ctx context.Context, in *UpdateUserProfileBackgroundRequest, opts ...grpc.CallOption) (*UpdateUserProfileBackgroundResponse, error)
|
||||
ChangeUserCountry(ctx context.Context, in *ChangeUserCountryRequest, opts ...grpc.CallOption) (*ChangeUserCountryResponse, error)
|
||||
AdminChangeUserCountry(ctx context.Context, in *AdminChangeUserCountryRequest, opts ...grpc.CallOption) (*ChangeUserCountryResponse, error)
|
||||
SetUserStatus(ctx context.Context, in *SetUserStatusRequest, opts ...grpc.CallOption) (*SetUserStatusResponse, error)
|
||||
@ -149,16 +147,6 @@ func (c *userServiceClient) UpdateUserProfile(ctx context.Context, in *UpdateUse
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *userServiceClient) UpdateUserProfileBackground(ctx context.Context, in *UpdateUserProfileBackgroundRequest, opts ...grpc.CallOption) (*UpdateUserProfileBackgroundResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(UpdateUserProfileBackgroundResponse)
|
||||
err := c.cc.Invoke(ctx, UserService_UpdateUserProfileBackground_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *userServiceClient) ChangeUserCountry(ctx context.Context, in *ChangeUserCountryRequest, opts ...grpc.CallOption) (*ChangeUserCountryResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(ChangeUserCountryResponse)
|
||||
@ -243,7 +231,6 @@ type UserServiceServer interface {
|
||||
ListUserIDs(context.Context, *ListUserIDsRequest) (*ListUserIDsResponse, error)
|
||||
GetUserMicLifetimeStats(context.Context, *GetUserMicLifetimeStatsRequest) (*GetUserMicLifetimeStatsResponse, error)
|
||||
UpdateUserProfile(context.Context, *UpdateUserProfileRequest) (*UpdateUserProfileResponse, error)
|
||||
UpdateUserProfileBackground(context.Context, *UpdateUserProfileBackgroundRequest) (*UpdateUserProfileBackgroundResponse, error)
|
||||
ChangeUserCountry(context.Context, *ChangeUserCountryRequest) (*ChangeUserCountryResponse, error)
|
||||
AdminChangeUserCountry(context.Context, *AdminChangeUserCountryRequest) (*ChangeUserCountryResponse, error)
|
||||
SetUserStatus(context.Context, *SetUserStatusRequest) (*SetUserStatusResponse, error)
|
||||
@ -285,9 +272,6 @@ func (UnimplementedUserServiceServer) GetUserMicLifetimeStats(context.Context, *
|
||||
func (UnimplementedUserServiceServer) UpdateUserProfile(context.Context, *UpdateUserProfileRequest) (*UpdateUserProfileResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method UpdateUserProfile not implemented")
|
||||
}
|
||||
func (UnimplementedUserServiceServer) UpdateUserProfileBackground(context.Context, *UpdateUserProfileBackgroundRequest) (*UpdateUserProfileBackgroundResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method UpdateUserProfileBackground not implemented")
|
||||
}
|
||||
func (UnimplementedUserServiceServer) ChangeUserCountry(context.Context, *ChangeUserCountryRequest) (*ChangeUserCountryResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method ChangeUserCountry not implemented")
|
||||
}
|
||||
@ -474,24 +458,6 @@ func _UserService_UpdateUserProfile_Handler(srv interface{}, ctx context.Context
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _UserService_UpdateUserProfileBackground_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(UpdateUserProfileBackgroundRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(UserServiceServer).UpdateUserProfileBackground(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: UserService_UpdateUserProfileBackground_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(UserServiceServer).UpdateUserProfileBackground(ctx, req.(*UpdateUserProfileBackgroundRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _UserService_ChangeUserCountry_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ChangeUserCountryRequest)
|
||||
if err := dec(in); err != nil {
|
||||
@ -657,10 +623,6 @@ var UserService_ServiceDesc = grpc.ServiceDesc{
|
||||
MethodName: "UpdateUserProfile",
|
||||
Handler: _UserService_UpdateUserProfile_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "UpdateUserProfileBackground",
|
||||
Handler: _UserService_UpdateUserProfileBackground_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ChangeUserCountry",
|
||||
Handler: _UserService_ChangeUserCountry_Handler,
|
||||
@ -1553,8 +1515,7 @@ var UserCPService_ServiceDesc = grpc.ServiceDesc{
|
||||
}
|
||||
|
||||
const (
|
||||
UserCPInternalService_ConsumeRoomGiftCPEvent_FullMethodName = "/hyapp.user.v1.UserCPInternalService/ConsumeRoomGiftCPEvent"
|
||||
UserCPInternalService_ListCPWeeklyRankEntries_FullMethodName = "/hyapp.user.v1.UserCPInternalService/ListCPWeeklyRankEntries"
|
||||
UserCPInternalService_ConsumeRoomGiftCPEvent_FullMethodName = "/hyapp.user.v1.UserCPInternalService/ConsumeRoomGiftCPEvent"
|
||||
)
|
||||
|
||||
// UserCPInternalServiceClient is the client API for UserCPInternalService service.
|
||||
@ -1564,7 +1525,6 @@ const (
|
||||
// UserCPInternalService 给 owner outbox 消费 worker 调用,不暴露给 App 或后台。
|
||||
type UserCPInternalServiceClient interface {
|
||||
ConsumeRoomGiftCPEvent(ctx context.Context, in *ConsumeRoomGiftCPEventRequest, opts ...grpc.CallOption) (*ConsumeRoomGiftCPEventResponse, error)
|
||||
ListCPWeeklyRankEntries(ctx context.Context, in *ListCPWeeklyRankEntriesRequest, opts ...grpc.CallOption) (*ListCPWeeklyRankEntriesResponse, error)
|
||||
}
|
||||
|
||||
type userCPInternalServiceClient struct {
|
||||
@ -1585,16 +1545,6 @@ func (c *userCPInternalServiceClient) ConsumeRoomGiftCPEvent(ctx context.Context
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *userCPInternalServiceClient) ListCPWeeklyRankEntries(ctx context.Context, in *ListCPWeeklyRankEntriesRequest, opts ...grpc.CallOption) (*ListCPWeeklyRankEntriesResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(ListCPWeeklyRankEntriesResponse)
|
||||
err := c.cc.Invoke(ctx, UserCPInternalService_ListCPWeeklyRankEntries_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// UserCPInternalServiceServer is the server API for UserCPInternalService service.
|
||||
// All implementations must embed UnimplementedUserCPInternalServiceServer
|
||||
// for forward compatibility.
|
||||
@ -1602,7 +1552,6 @@ func (c *userCPInternalServiceClient) ListCPWeeklyRankEntries(ctx context.Contex
|
||||
// UserCPInternalService 给 owner outbox 消费 worker 调用,不暴露给 App 或后台。
|
||||
type UserCPInternalServiceServer interface {
|
||||
ConsumeRoomGiftCPEvent(context.Context, *ConsumeRoomGiftCPEventRequest) (*ConsumeRoomGiftCPEventResponse, error)
|
||||
ListCPWeeklyRankEntries(context.Context, *ListCPWeeklyRankEntriesRequest) (*ListCPWeeklyRankEntriesResponse, error)
|
||||
mustEmbedUnimplementedUserCPInternalServiceServer()
|
||||
}
|
||||
|
||||
@ -1616,9 +1565,6 @@ type UnimplementedUserCPInternalServiceServer struct{}
|
||||
func (UnimplementedUserCPInternalServiceServer) ConsumeRoomGiftCPEvent(context.Context, *ConsumeRoomGiftCPEventRequest) (*ConsumeRoomGiftCPEventResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method ConsumeRoomGiftCPEvent not implemented")
|
||||
}
|
||||
func (UnimplementedUserCPInternalServiceServer) ListCPWeeklyRankEntries(context.Context, *ListCPWeeklyRankEntriesRequest) (*ListCPWeeklyRankEntriesResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method ListCPWeeklyRankEntries not implemented")
|
||||
}
|
||||
func (UnimplementedUserCPInternalServiceServer) mustEmbedUnimplementedUserCPInternalServiceServer() {}
|
||||
func (UnimplementedUserCPInternalServiceServer) testEmbeddedByValue() {}
|
||||
|
||||
@ -1658,24 +1604,6 @@ func _UserCPInternalService_ConsumeRoomGiftCPEvent_Handler(srv interface{}, ctx
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _UserCPInternalService_ListCPWeeklyRankEntries_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ListCPWeeklyRankEntriesRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(UserCPInternalServiceServer).ListCPWeeklyRankEntries(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: UserCPInternalService_ListCPWeeklyRankEntries_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(UserCPInternalServiceServer).ListCPWeeklyRankEntries(ctx, req.(*ListCPWeeklyRankEntriesRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// UserCPInternalService_ServiceDesc is the grpc.ServiceDesc for UserCPInternalService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
@ -1687,10 +1615,6 @@ var UserCPInternalService_ServiceDesc = grpc.ServiceDesc{
|
||||
MethodName: "ConsumeRoomGiftCPEvent",
|
||||
Handler: _UserCPInternalService_ConsumeRoomGiftCPEvent_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ListCPWeeklyRankEntries",
|
||||
Handler: _UserCPInternalService_ListCPWeeklyRankEntries_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "proto/user/v1/user.proto",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -91,21 +91,6 @@ message BatchDebitGiftResponse {
|
||||
repeated BatchDebitGiftReceipt receipts = 2;
|
||||
}
|
||||
|
||||
// DebitRobotGiftRequest 是机器人房间内部送礼扣费命令。
|
||||
// 它只扣 ROBOT_COIN,不产生主播钻石、真实充值消费流水或普通礼物墙投影。
|
||||
message DebitRobotGiftRequest {
|
||||
string command_id = 1;
|
||||
string room_id = 2;
|
||||
int64 sender_user_id = 3;
|
||||
int64 target_user_id = 4;
|
||||
string gift_id = 5;
|
||||
int32 gift_count = 6;
|
||||
string price_version = 7;
|
||||
string app_code = 8;
|
||||
int64 region_id = 9;
|
||||
int64 sender_region_id = 10;
|
||||
}
|
||||
|
||||
// AssetBalance 是用户某类资产的余额投影。
|
||||
message AssetBalance {
|
||||
string asset_type = 1;
|
||||
@ -1291,10 +1276,6 @@ message CreateH5RechargeOrderRequest {
|
||||
string notify_url = 12;
|
||||
string client_ip = 13;
|
||||
string language = 14;
|
||||
// payer_name / payer_account 来自 gateway 重新解析的目标用户资料,不接受 H5 前端自报。
|
||||
string payer_name = 15;
|
||||
string payer_account = 16;
|
||||
string payer_email = 17;
|
||||
}
|
||||
|
||||
message SubmitH5RechargeTxRequest {
|
||||
@ -1610,28 +1591,6 @@ message CreditInviteActivityRewardResponse {
|
||||
int64 granted_at_ms = 4;
|
||||
}
|
||||
|
||||
// CreditAgencyOpeningRewardRequest 是 activity-service 代理开业流水档位结算后的 COIN 入账命令。
|
||||
message CreditAgencyOpeningRewardRequest {
|
||||
string command_id = 1;
|
||||
string app_code = 2;
|
||||
int64 target_user_id = 3;
|
||||
int64 amount = 4;
|
||||
string application_id = 5;
|
||||
string cycle_id = 6;
|
||||
int64 agency_id = 7;
|
||||
int32 rank_no = 8;
|
||||
int64 score_coins = 9;
|
||||
string reason = 10;
|
||||
}
|
||||
|
||||
// CreditAgencyOpeningRewardResponse 返回代理开业奖励入账流水和用户 COIN 账后余额。
|
||||
message CreditAgencyOpeningRewardResponse {
|
||||
string transaction_id = 1;
|
||||
AssetBalance balance = 2;
|
||||
int64 amount = 3;
|
||||
int64 granted_at_ms = 4;
|
||||
}
|
||||
|
||||
// ApplyGameCoinChangeRequest 是 game-service 唯一可以调用的钱包游戏改账入口。
|
||||
message ApplyGameCoinChangeRequest {
|
||||
string request_id = 1;
|
||||
@ -1866,7 +1825,6 @@ service WalletCronService {
|
||||
service WalletService {
|
||||
rpc DebitGift(DebitGiftRequest) returns (DebitGiftResponse);
|
||||
rpc BatchDebitGift(BatchDebitGiftRequest) returns (BatchDebitGiftResponse);
|
||||
rpc DebitRobotGift(DebitRobotGiftRequest) returns (DebitGiftResponse);
|
||||
rpc GetBalances(GetBalancesRequest) returns (GetBalancesResponse);
|
||||
rpc GetActiveHostSalaryPolicy(GetActiveHostSalaryPolicyRequest) returns (GetActiveHostSalaryPolicyResponse);
|
||||
rpc GetHostSalaryProgress(GetHostSalaryProgressRequest) returns (GetHostSalaryProgressResponse);
|
||||
@ -1935,7 +1893,6 @@ service WalletService {
|
||||
rpc CreditLuckyGiftReward(CreditLuckyGiftRewardRequest) returns (CreditLuckyGiftRewardResponse);
|
||||
rpc CreditRoomTurnoverReward(CreditRoomTurnoverRewardRequest) returns (CreditRoomTurnoverRewardResponse);
|
||||
rpc CreditInviteActivityReward(CreditInviteActivityRewardRequest) returns (CreditInviteActivityRewardResponse);
|
||||
rpc CreditAgencyOpeningReward(CreditAgencyOpeningRewardRequest) returns (CreditAgencyOpeningRewardResponse);
|
||||
rpc ApplyGameCoinChange(ApplyGameCoinChangeRequest) returns (ApplyGameCoinChangeResponse);
|
||||
rpc GetRedPacketConfig(GetRedPacketConfigRequest) returns (GetRedPacketConfigResponse);
|
||||
rpc UpdateRedPacketConfig(UpdateRedPacketConfigRequest) returns (UpdateRedPacketConfigResponse);
|
||||
|
||||
@ -203,7 +203,6 @@ var WalletCronService_ServiceDesc = grpc.ServiceDesc{
|
||||
const (
|
||||
WalletService_DebitGift_FullMethodName = "/hyapp.wallet.v1.WalletService/DebitGift"
|
||||
WalletService_BatchDebitGift_FullMethodName = "/hyapp.wallet.v1.WalletService/BatchDebitGift"
|
||||
WalletService_DebitRobotGift_FullMethodName = "/hyapp.wallet.v1.WalletService/DebitRobotGift"
|
||||
WalletService_GetBalances_FullMethodName = "/hyapp.wallet.v1.WalletService/GetBalances"
|
||||
WalletService_GetActiveHostSalaryPolicy_FullMethodName = "/hyapp.wallet.v1.WalletService/GetActiveHostSalaryPolicy"
|
||||
WalletService_GetHostSalaryProgress_FullMethodName = "/hyapp.wallet.v1.WalletService/GetHostSalaryProgress"
|
||||
@ -272,7 +271,6 @@ const (
|
||||
WalletService_CreditLuckyGiftReward_FullMethodName = "/hyapp.wallet.v1.WalletService/CreditLuckyGiftReward"
|
||||
WalletService_CreditRoomTurnoverReward_FullMethodName = "/hyapp.wallet.v1.WalletService/CreditRoomTurnoverReward"
|
||||
WalletService_CreditInviteActivityReward_FullMethodName = "/hyapp.wallet.v1.WalletService/CreditInviteActivityReward"
|
||||
WalletService_CreditAgencyOpeningReward_FullMethodName = "/hyapp.wallet.v1.WalletService/CreditAgencyOpeningReward"
|
||||
WalletService_ApplyGameCoinChange_FullMethodName = "/hyapp.wallet.v1.WalletService/ApplyGameCoinChange"
|
||||
WalletService_GetRedPacketConfig_FullMethodName = "/hyapp.wallet.v1.WalletService/GetRedPacketConfig"
|
||||
WalletService_UpdateRedPacketConfig_FullMethodName = "/hyapp.wallet.v1.WalletService/UpdateRedPacketConfig"
|
||||
@ -292,7 +290,6 @@ const (
|
||||
type WalletServiceClient interface {
|
||||
DebitGift(ctx context.Context, in *DebitGiftRequest, opts ...grpc.CallOption) (*DebitGiftResponse, error)
|
||||
BatchDebitGift(ctx context.Context, in *BatchDebitGiftRequest, opts ...grpc.CallOption) (*BatchDebitGiftResponse, error)
|
||||
DebitRobotGift(ctx context.Context, in *DebitRobotGiftRequest, opts ...grpc.CallOption) (*DebitGiftResponse, error)
|
||||
GetBalances(ctx context.Context, in *GetBalancesRequest, opts ...grpc.CallOption) (*GetBalancesResponse, error)
|
||||
GetActiveHostSalaryPolicy(ctx context.Context, in *GetActiveHostSalaryPolicyRequest, opts ...grpc.CallOption) (*GetActiveHostSalaryPolicyResponse, error)
|
||||
GetHostSalaryProgress(ctx context.Context, in *GetHostSalaryProgressRequest, opts ...grpc.CallOption) (*GetHostSalaryProgressResponse, error)
|
||||
@ -361,7 +358,6 @@ type WalletServiceClient interface {
|
||||
CreditLuckyGiftReward(ctx context.Context, in *CreditLuckyGiftRewardRequest, opts ...grpc.CallOption) (*CreditLuckyGiftRewardResponse, error)
|
||||
CreditRoomTurnoverReward(ctx context.Context, in *CreditRoomTurnoverRewardRequest, opts ...grpc.CallOption) (*CreditRoomTurnoverRewardResponse, error)
|
||||
CreditInviteActivityReward(ctx context.Context, in *CreditInviteActivityRewardRequest, opts ...grpc.CallOption) (*CreditInviteActivityRewardResponse, error)
|
||||
CreditAgencyOpeningReward(ctx context.Context, in *CreditAgencyOpeningRewardRequest, opts ...grpc.CallOption) (*CreditAgencyOpeningRewardResponse, error)
|
||||
ApplyGameCoinChange(ctx context.Context, in *ApplyGameCoinChangeRequest, opts ...grpc.CallOption) (*ApplyGameCoinChangeResponse, error)
|
||||
GetRedPacketConfig(ctx context.Context, in *GetRedPacketConfigRequest, opts ...grpc.CallOption) (*GetRedPacketConfigResponse, error)
|
||||
UpdateRedPacketConfig(ctx context.Context, in *UpdateRedPacketConfigRequest, opts ...grpc.CallOption) (*UpdateRedPacketConfigResponse, error)
|
||||
@ -401,16 +397,6 @@ func (c *walletServiceClient) BatchDebitGift(ctx context.Context, in *BatchDebit
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *walletServiceClient) DebitRobotGift(ctx context.Context, in *DebitRobotGiftRequest, opts ...grpc.CallOption) (*DebitGiftResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(DebitGiftResponse)
|
||||
err := c.cc.Invoke(ctx, WalletService_DebitRobotGift_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *walletServiceClient) GetBalances(ctx context.Context, in *GetBalancesRequest, opts ...grpc.CallOption) (*GetBalancesResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(GetBalancesResponse)
|
||||
@ -1091,16 +1077,6 @@ func (c *walletServiceClient) CreditInviteActivityReward(ctx context.Context, in
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *walletServiceClient) CreditAgencyOpeningReward(ctx context.Context, in *CreditAgencyOpeningRewardRequest, opts ...grpc.CallOption) (*CreditAgencyOpeningRewardResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(CreditAgencyOpeningRewardResponse)
|
||||
err := c.cc.Invoke(ctx, WalletService_CreditAgencyOpeningReward_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *walletServiceClient) ApplyGameCoinChange(ctx context.Context, in *ApplyGameCoinChangeRequest, opts ...grpc.CallOption) (*ApplyGameCoinChangeResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(ApplyGameCoinChangeResponse)
|
||||
@ -1199,7 +1175,6 @@ func (c *walletServiceClient) RetryRedPacketRefund(ctx context.Context, in *Retr
|
||||
type WalletServiceServer interface {
|
||||
DebitGift(context.Context, *DebitGiftRequest) (*DebitGiftResponse, error)
|
||||
BatchDebitGift(context.Context, *BatchDebitGiftRequest) (*BatchDebitGiftResponse, error)
|
||||
DebitRobotGift(context.Context, *DebitRobotGiftRequest) (*DebitGiftResponse, error)
|
||||
GetBalances(context.Context, *GetBalancesRequest) (*GetBalancesResponse, error)
|
||||
GetActiveHostSalaryPolicy(context.Context, *GetActiveHostSalaryPolicyRequest) (*GetActiveHostSalaryPolicyResponse, error)
|
||||
GetHostSalaryProgress(context.Context, *GetHostSalaryProgressRequest) (*GetHostSalaryProgressResponse, error)
|
||||
@ -1268,7 +1243,6 @@ type WalletServiceServer interface {
|
||||
CreditLuckyGiftReward(context.Context, *CreditLuckyGiftRewardRequest) (*CreditLuckyGiftRewardResponse, error)
|
||||
CreditRoomTurnoverReward(context.Context, *CreditRoomTurnoverRewardRequest) (*CreditRoomTurnoverRewardResponse, error)
|
||||
CreditInviteActivityReward(context.Context, *CreditInviteActivityRewardRequest) (*CreditInviteActivityRewardResponse, error)
|
||||
CreditAgencyOpeningReward(context.Context, *CreditAgencyOpeningRewardRequest) (*CreditAgencyOpeningRewardResponse, error)
|
||||
ApplyGameCoinChange(context.Context, *ApplyGameCoinChangeRequest) (*ApplyGameCoinChangeResponse, error)
|
||||
GetRedPacketConfig(context.Context, *GetRedPacketConfigRequest) (*GetRedPacketConfigResponse, error)
|
||||
UpdateRedPacketConfig(context.Context, *UpdateRedPacketConfigRequest) (*UpdateRedPacketConfigResponse, error)
|
||||
@ -1294,9 +1268,6 @@ func (UnimplementedWalletServiceServer) DebitGift(context.Context, *DebitGiftReq
|
||||
func (UnimplementedWalletServiceServer) BatchDebitGift(context.Context, *BatchDebitGiftRequest) (*BatchDebitGiftResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method BatchDebitGift not implemented")
|
||||
}
|
||||
func (UnimplementedWalletServiceServer) DebitRobotGift(context.Context, *DebitRobotGiftRequest) (*DebitGiftResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method DebitRobotGift not implemented")
|
||||
}
|
||||
func (UnimplementedWalletServiceServer) GetBalances(context.Context, *GetBalancesRequest) (*GetBalancesResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method GetBalances not implemented")
|
||||
}
|
||||
@ -1501,9 +1472,6 @@ func (UnimplementedWalletServiceServer) CreditRoomTurnoverReward(context.Context
|
||||
func (UnimplementedWalletServiceServer) CreditInviteActivityReward(context.Context, *CreditInviteActivityRewardRequest) (*CreditInviteActivityRewardResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method CreditInviteActivityReward not implemented")
|
||||
}
|
||||
func (UnimplementedWalletServiceServer) CreditAgencyOpeningReward(context.Context, *CreditAgencyOpeningRewardRequest) (*CreditAgencyOpeningRewardResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method CreditAgencyOpeningReward not implemented")
|
||||
}
|
||||
func (UnimplementedWalletServiceServer) ApplyGameCoinChange(context.Context, *ApplyGameCoinChangeRequest) (*ApplyGameCoinChangeResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method ApplyGameCoinChange not implemented")
|
||||
}
|
||||
@ -1588,24 +1556,6 @@ func _WalletService_BatchDebitGift_Handler(srv interface{}, ctx context.Context,
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _WalletService_DebitRobotGift_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(DebitRobotGiftRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(WalletServiceServer).DebitRobotGift(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: WalletService_DebitRobotGift_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(WalletServiceServer).DebitRobotGift(ctx, req.(*DebitRobotGiftRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _WalletService_GetBalances_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetBalancesRequest)
|
||||
if err := dec(in); err != nil {
|
||||
@ -2830,24 +2780,6 @@ func _WalletService_CreditInviteActivityReward_Handler(srv interface{}, ctx cont
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _WalletService_CreditAgencyOpeningReward_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(CreditAgencyOpeningRewardRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(WalletServiceServer).CreditAgencyOpeningReward(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: WalletService_CreditAgencyOpeningReward_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(WalletServiceServer).CreditAgencyOpeningReward(ctx, req.(*CreditAgencyOpeningRewardRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _WalletService_ApplyGameCoinChange_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ApplyGameCoinChangeRequest)
|
||||
if err := dec(in); err != nil {
|
||||
@ -3025,10 +2957,6 @@ var WalletService_ServiceDesc = grpc.ServiceDesc{
|
||||
MethodName: "BatchDebitGift",
|
||||
Handler: _WalletService_BatchDebitGift_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "DebitRobotGift",
|
||||
Handler: _WalletService_DebitRobotGift_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetBalances",
|
||||
Handler: _WalletService_GetBalances_Handler,
|
||||
@ -3301,10 +3229,6 @@ var WalletService_ServiceDesc = grpc.ServiceDesc{
|
||||
MethodName: "CreditInviteActivityReward",
|
||||
Handler: _WalletService_CreditInviteActivityReward_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "CreditAgencyOpeningReward",
|
||||
Handler: _WalletService_CreditAgencyOpeningReward_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ApplyGameCoinChange",
|
||||
Handler: _WalletService_ApplyGameCoinChange_Handler,
|
||||
|
||||
@ -1,103 +0,0 @@
|
||||
# 个人主页背景图 Flutter 对接
|
||||
|
||||
个人主页背景图是用户资料字段 `profile_bg_img`,不是背包资源,不是 `profile_card`,也不需要佩戴。App 先自行上传图片拿到 URL,再调用后端保存 URL。
|
||||
|
||||
## 保存主页背景图
|
||||
|
||||
地址:
|
||||
|
||||
```http
|
||||
POST /api/v1/users/me/profile-bg-img
|
||||
```
|
||||
|
||||
Headers:
|
||||
|
||||
| Header | 必填 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `Authorization` | 是 | `Bearer <access_token>` |
|
||||
| `Content-Type` | 是 | `application/json` |
|
||||
| `X-App-Code` | 否 | App 编码,默认 `lalu` |
|
||||
|
||||
参数:
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `url` | string | 是 | App 已上传好的图片 URL,必须是 `http` 或 `https` 绝对地址。 |
|
||||
|
||||
请求示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"url": "https://cdn.example.com/profile-bg.png"
|
||||
}
|
||||
```
|
||||
|
||||
返回值:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": "OK",
|
||||
"message": "ok",
|
||||
"request_id": "req_abc",
|
||||
"data": {
|
||||
"profile_bg_img": "https://cdn.example.com/profile-bg.png",
|
||||
"profile": {
|
||||
"user_id": "10001",
|
||||
"display_user_id": "163001",
|
||||
"username": "Alice",
|
||||
"avatar": "https://cdn.example.com/avatar.png",
|
||||
"profile_bg_img": "https://cdn.example.com/profile-bg.png",
|
||||
"profile_completed": true,
|
||||
"onboarding_status": "completed"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Flutter 处理:
|
||||
|
||||
| 场景 | 处理 |
|
||||
| --- | --- |
|
||||
| 保存成功 | 使用 `data.profile_bg_img` 刷新当前资料页背景图。 |
|
||||
| 重新打开我的资料页 | 读取个人信息接口返回的 `profile_bg_img`。 |
|
||||
| 查看别人资料卡 | 使用访问资料卡响应里的 `target_profile.profile_bg_img`。 |
|
||||
|
||||
## 个人信息返回字段
|
||||
|
||||
当前用户个人信息:
|
||||
|
||||
```http
|
||||
GET /api/v1/users/me
|
||||
```
|
||||
|
||||
我的页概览:
|
||||
|
||||
```http
|
||||
GET /api/v1/users/me/overview
|
||||
```
|
||||
|
||||
访问别人的资料卡:
|
||||
|
||||
```http
|
||||
POST /api/v1/users/{user_id}/visit
|
||||
```
|
||||
|
||||
返回字段:
|
||||
|
||||
| 字段 | 说明 |
|
||||
| --- | --- |
|
||||
| `profile_bg_img` | 用户主页背景图 URL;未设置时为空字符串。 |
|
||||
| `profile.profile_bg_img` | `/users/me/overview` 里的用户资料背景图。 |
|
||||
| `target_profile.profile_bg_img` | 查看别人资料卡时,对方的主页背景图。 |
|
||||
|
||||
## 错误处理
|
||||
|
||||
| HTTP | code | 场景 |
|
||||
| --- | --- | --- |
|
||||
| `400` | `INVALID_ARGUMENT` | `url` 为空、不是 HTTP(S) 绝对地址或长度超过限制。 |
|
||||
| `401` | `UNAUTHORIZED` | 未登录或 token 无效。 |
|
||||
| `403` | `PROFILE_REQUIRED` | 当前用户资料未完成。 |
|
||||
| `404` | `NOT_FOUND` | 当前用户不存在。 |
|
||||
| `502` | `UPSTREAM_ERROR` | user-service 不可用。 |
|
||||
|
||||
相关 IM:无。该接口只保存用户资料字段,不发送 IM,不触发背包或装扮刷新。
|
||||
@ -225,7 +225,6 @@ Body:
|
||||
| 权益校验 | 必须是当前登录用户自己的有效权益;过期、禁用、剩余数量为 0 都不能佩戴。 |
|
||||
| 发放默认佩戴 | `badge`、`mic_seat_animation` 发放成功后默认佩戴。 |
|
||||
| 徽章展示 | `badge` 是拥有即佩戴的多装备资源;资料页多徽章展示仍使用 `/api/v1/badges/me` 和 `/api/v1/badges/display`。 |
|
||||
| 资料卡背景 | `profile_card` 只用于装扮资料卡;个人主页背景图使用用户资料字段 `profile_bg_img`,不走背包佩戴。 |
|
||||
|
||||
常见错误:
|
||||
|
||||
|
||||
@ -866,7 +866,7 @@ paths:
|
||||
- users
|
||||
summary: 修改当前用户基础资料
|
||||
operationId: updateMyProfile
|
||||
description: 只修改 `username`、`avatar`、`birth`;主页背景图和国家修改都走独立接口。
|
||||
description: 只修改 `username`、`avatar`、`birth`;国家修改必须走独立国家接口。
|
||||
security:
|
||||
- BearerAuth: []
|
||||
parameters:
|
||||
@ -890,36 +890,6 @@ paths:
|
||||
$ref: "#/responses/Internal"
|
||||
"502":
|
||||
$ref: "#/responses/UpstreamError"
|
||||
/api/v1/users/me/profile-bg-img:
|
||||
post:
|
||||
tags:
|
||||
- users
|
||||
summary: 修改当前用户主页背景图
|
||||
operationId: updateMyProfileBackground
|
||||
description: App 先自行上传图片,本接口只接收图片 URL 并保存到 `profile_bg_img`,和背包、`profile_card`、佩戴无关。
|
||||
security:
|
||||
- BearerAuth: []
|
||||
parameters:
|
||||
- name: body
|
||||
in: body
|
||||
required: true
|
||||
schema:
|
||||
$ref: "#/definitions/UpdateUserProfileBackgroundRequest"
|
||||
responses:
|
||||
"200":
|
||||
description: 修改成功,`data.profile_bg_img` 返回最新背景图 URL。
|
||||
schema:
|
||||
$ref: "#/definitions/UpdateUserProfileBackgroundEnvelope"
|
||||
"400":
|
||||
$ref: "#/responses/BadRequest"
|
||||
"401":
|
||||
$ref: "#/responses/Unauthorized"
|
||||
"404":
|
||||
$ref: "#/responses/NotFound"
|
||||
"500":
|
||||
$ref: "#/responses/Internal"
|
||||
"502":
|
||||
$ref: "#/responses/UpstreamError"
|
||||
/api/v1/users/me/country/change:
|
||||
post:
|
||||
tags:
|
||||
@ -3174,9 +3144,6 @@ definitions:
|
||||
updated_at_ms:
|
||||
type: integer
|
||||
format: int64
|
||||
profile_bg_img:
|
||||
type: string
|
||||
description: 用户主页信息卡背景图片 URL,来自 users.profile_bg_img;和 `profile_card` 装扮资源无关,未设置时为空字符串。
|
||||
next_country_change_allowed_at_ms:
|
||||
type: integer
|
||||
format: int64
|
||||
@ -3627,28 +3594,6 @@ definitions:
|
||||
birth:
|
||||
type: string
|
||||
description: yyyy-mm-dd;传空字符串表示清空生日。
|
||||
UpdateUserProfileBackgroundRequest:
|
||||
type: object
|
||||
required:
|
||||
- url
|
||||
properties:
|
||||
url:
|
||||
type: string
|
||||
description: App 已上传好的个人主页背景图 URL。
|
||||
UpdateUserProfileBackgroundData:
|
||||
type: object
|
||||
properties:
|
||||
profile_bg_img:
|
||||
type: string
|
||||
profile:
|
||||
$ref: "#/definitions/UserProfileData"
|
||||
UpdateUserProfileBackgroundEnvelope:
|
||||
allOf:
|
||||
- $ref: "#/definitions/GatewayOKEnvelopeBase"
|
||||
- type: object
|
||||
properties:
|
||||
data:
|
||||
$ref: "#/definitions/UpdateUserProfileBackgroundData"
|
||||
ChangeUserCountryRequest:
|
||||
type: object
|
||||
properties:
|
||||
|
||||
@ -927,9 +927,6 @@ definitions:
|
||||
description: 用户选择的国家主数据 country_code。
|
||||
avatar:
|
||||
type: string
|
||||
profile_bg_img:
|
||||
type: string
|
||||
description: 用户主页信息卡背景图片 URL,属于用户资料字段;和 `profile_card` 装扮资源无关。
|
||||
birth:
|
||||
type: string
|
||||
description: yyyy-mm-dd。
|
||||
@ -1088,22 +1085,6 @@ definitions:
|
||||
properties:
|
||||
user:
|
||||
$ref: "#/definitions/User"
|
||||
UpdateUserProfileBackgroundRequest:
|
||||
type: object
|
||||
properties:
|
||||
meta:
|
||||
$ref: "#/definitions/RequestMeta"
|
||||
user_id:
|
||||
type: integer
|
||||
format: int64
|
||||
profile_bg_img:
|
||||
type: string
|
||||
description: 用户主页背景图 URL。
|
||||
UpdateUserProfileBackgroundResponse:
|
||||
type: object
|
||||
properties:
|
||||
user:
|
||||
$ref: "#/definitions/User"
|
||||
CompleteOnboardingRequest:
|
||||
type: object
|
||||
properties:
|
||||
|
||||
@ -27,8 +27,7 @@ Authorization: Bearer <access_token>
|
||||
"avatar": "https://cdn.example.com/avatar.png",
|
||||
"language": "en",
|
||||
"profile_completed": true,
|
||||
"onboarding_status": "completed",
|
||||
"profile_bg_img": "https://cdn.example.com/profile-bg.png"
|
||||
"onboarding_status": "completed"
|
||||
},
|
||||
"profile_stats": {
|
||||
"visitors_count": 12,
|
||||
@ -98,7 +97,6 @@ Authorization: Bearer <access_token>
|
||||
| Field | Source | Rule |
|
||||
| --- | --- | --- |
|
||||
| `profile` | `user-service.GetUser` | 只取头像、昵称、展示 ID、语言等基础资料 |
|
||||
| `profile.profile_bg_img` | `user-service.GetUser` | 取用户资料里的主页背景图;和 `profile_card` 装扮资源无关,未设置返回空字符串 |
|
||||
| `profile_stats` | `user-service.GetMyProfileStats` | 读 `user_profile_stats` 计数表,不实时扫访问、关注、好友大表 |
|
||||
| `wallet` | `wallet-service.GetWalletValueSummary` | 首屏只返回金币余额,不返回钻石和美元余额 |
|
||||
| `vip` | `wallet-service.GetMyVip` | 只取等级、名称、有效期和 active 状态 |
|
||||
|
||||
@ -1,98 +0,0 @@
|
||||
# 机器人房间产品文档
|
||||
|
||||
## 背景
|
||||
|
||||
语音房冷启动阶段,房间列表和新房首屏容易显得冷清。机器人房间用于在内部可控范围内营造房间活跃氛围:机器人账号创建房间、进入房间、静音上麦,并在房间内相互送礼,触发客户端已有的礼物动效和幸运礼物展示。
|
||||
|
||||
机器人房间不面向真人主播发放收益,不参与真实抽奖、真实奖池、榜单和结算。
|
||||
|
||||
## 产品目标
|
||||
|
||||
- 后台可创建和控制机器人房间。
|
||||
- 每个机器人房间自动拉取一批未被占用的机器人账号进房。
|
||||
- 机器人可静音上麦,让房间麦位呈现活跃状态。
|
||||
- 机器人之间自动送普通礼物和幸运礼物。
|
||||
- 幸运礼物展示复用客户端现有飘屏/房间展示协议,但不影响真实幸运礼物奖池和流水。
|
||||
- 机器人房间积分和麦位热度正常增长,用于房间内氛围展示。
|
||||
- 机器人礼物不产生真人收益,不污染充值、消费、结算和排行榜数据。
|
||||
|
||||
## 使用入口
|
||||
|
||||
后台管理系统路径:
|
||||
|
||||
`房间管理 / 机器人房间`
|
||||
|
||||
权限:
|
||||
|
||||
- `room-robot:view`:查看机器人房间。
|
||||
- `room-robot:create`:创建机器人房间。
|
||||
- `room-robot:update`:启动/停止机器人房间。
|
||||
|
||||
## 创建机器人房间
|
||||
|
||||
点击 `增加机器人房间` 后配置:
|
||||
|
||||
- 房间名称:机器人房间对外展示名称。
|
||||
- 房主机器人:从未被机器人房间占用的房间机器人账号中选择。
|
||||
- 候选机器人:从未被机器人房间占用的机器人账号中选择,默认可选择全部可用机器人。
|
||||
- 进房人数范围:例如 `6-10`,系统会从候选机器人中随机抽取一个人数。
|
||||
- 礼物合集:普通礼物池,机器人普通送礼时随机选择。
|
||||
- 幸运礼物合集:幸运礼物展示池,机器人幸运送礼时随机选择。
|
||||
- 普通礼物频次:每个机器人每隔固定毫秒数送一次普通礼物。
|
||||
- 幸运礼物连击范围:每轮幸运礼物随机生成连击次数。
|
||||
- 幸运礼物间隔范围:一轮幸运礼物结束后,随机等待一段时间再开始下一轮。
|
||||
|
||||
## 送礼规则
|
||||
|
||||
### 普通礼物
|
||||
|
||||
- 每个机器人独立执行。
|
||||
- 到达配置间隔后,从普通礼物合集中随机选择 1 个礼物。
|
||||
- 随机选择同房间另一个机器人作为收礼人。
|
||||
- 每次只送 1 个目标机器人。
|
||||
|
||||
### 幸运礼物
|
||||
|
||||
- 每个机器人独立执行。
|
||||
- 从幸运礼物合集中随机选择 1 个礼物。
|
||||
- 当前机器人向同房间所有其他机器人送礼。
|
||||
- 按配置范围随机生成连击次数,例如 `100-10000`。
|
||||
- 连击完成后,按配置间隔范围随机等待,例如 `5-20 秒`,再开始下一轮。
|
||||
|
||||
## 状态管理
|
||||
|
||||
机器人房间支持:
|
||||
|
||||
- 运行中:机器人送礼循环运行。
|
||||
- 已停止:机器人送礼循环停止,配置和房间占用保留。
|
||||
|
||||
后台列表中可使用状态开关快速启动或停止。
|
||||
|
||||
## 数据隔离
|
||||
|
||||
机器人房间遵循以下隔离规则:
|
||||
|
||||
- 机器人只给同房间机器人送礼。
|
||||
- 服务端禁止机器人给真人送礼。
|
||||
- 机器人礼物使用机器人专用金币 `ROBOT_COIN`。
|
||||
- 不消耗真人金币 `COIN`。
|
||||
- 不产生主播钻石和工资周期钻石。
|
||||
- 不写真实礼物墙。
|
||||
- 不参与真实幸运礼物奖池、抽奖、返奖和统计。
|
||||
- 不参与房间排行榜、活动排行榜和结算。
|
||||
|
||||
## 运营建议
|
||||
|
||||
- 单房机器人数量建议从 `6-10` 开始。
|
||||
- 普通礼物频次建议不要过低,避免房间消息过密。
|
||||
- 幸运礼物连击范围建议根据活动氛围配置,测试环境可用较小范围验证。
|
||||
- 礼物合集应选择低价或专门用于展示的礼物,避免用户误解真实返奖。
|
||||
- 机器人房间应优先用于冷启动、活动预热和列表氛围补足。
|
||||
|
||||
## 风险与限制
|
||||
|
||||
- 机器人房间不应和真人主播收益挂钩。
|
||||
- 不建议使用真人主播账号作为机器人账号。
|
||||
- 如果候选机器人不足,创建会失败。
|
||||
- 同一个机器人不能同时被多个 active 机器人房间占用。
|
||||
- 停止房间只停止送礼循环,不删除房间配置。
|
||||
@ -1,243 +0,0 @@
|
||||
# 机器人房间技术架构文档
|
||||
|
||||
## 目标
|
||||
|
||||
机器人房间提供后台配置驱动的内部房间编排能力,支持机器人创建房间、进房、虚拟静音上麦、普通礼物展示、幸运礼物展示,并保证与真实钱包、真实幸运礼物、真实奖池、真实榜单和结算隔离。
|
||||
|
||||
## 模块边界
|
||||
|
||||
### admin-platform
|
||||
|
||||
- 提供机器人房间配置页面。
|
||||
- 调用 admin-server HTTP 接口。
|
||||
- 不直接访问业务服务。
|
||||
|
||||
### admin-server
|
||||
|
||||
- 暴露后台 HTTP API。
|
||||
- 做权限、审计、参数校验和 DTO 转换。
|
||||
- 通过 gRPC client 调用 room-service 和 robot-service。
|
||||
- 通过 wallet-service gRPC 获取礼物配置。
|
||||
- 只读用户库补齐机器人展示资料。
|
||||
|
||||
### robot-service
|
||||
|
||||
- 只拥有机器人账号池事实。
|
||||
- 房间机器人池表:`hyapp_robot.robot_room_robots`。
|
||||
- 不处理进房、麦位、送礼和 IM。
|
||||
|
||||
### room-service
|
||||
|
||||
- 机器人房间配置 owner。
|
||||
- 创建真实房间状态。
|
||||
- 管理机器人房间配置表:`hyapp_room.room_robot_rooms`。
|
||||
- 执行机器人进房、虚拟静音上麦、送礼循环。
|
||||
- 强校验机器人送礼仅限同房间机器人。
|
||||
- 生成机器人幸运礼物展示事件。
|
||||
|
||||
### wallet-service
|
||||
|
||||
- 机器人礼物扣费入口:`DebitRobotGift`。
|
||||
- 使用专用资产 `ROBOT_COIN`。
|
||||
- 自动补足机器人专用金币后扣减。
|
||||
- 不发出真实 `WalletGiftDebited` 事件。
|
||||
- 不写主播周期钻石和真实礼物墙。
|
||||
|
||||
### activity/statistics
|
||||
|
||||
- 消费房间送礼事件时跳过 `is_robot_gift`。
|
||||
- 机器人礼物不进入真实活动、榜单、任务、成长值和统计。
|
||||
|
||||
## 核心数据表
|
||||
|
||||
### `hyapp_robot.robot_room_robots`
|
||||
|
||||
机器人账号池事实表。
|
||||
|
||||
关键字段:
|
||||
|
||||
- `app_code`
|
||||
- `room_scene`
|
||||
- `user_id`
|
||||
- `status`
|
||||
- `last_used_at_ms`
|
||||
- `used_count`
|
||||
|
||||
### `hyapp_room.room_robot_rooms`
|
||||
|
||||
机器人房间配置和占用事实表。
|
||||
|
||||
关键字段:
|
||||
|
||||
- `room_id`
|
||||
- `room_short_id`
|
||||
- `status`
|
||||
- `owner_robot_user_id`
|
||||
- `robot_user_ids_json`
|
||||
- `gift_ids_json`
|
||||
- `lucky_gift_ids_json`
|
||||
- `normal_gift_interval_ms`
|
||||
- `lucky_combo_min`
|
||||
- `lucky_combo_max`
|
||||
- `lucky_pause_min_ms`
|
||||
- `lucky_pause_max_ms`
|
||||
|
||||
## 后台 API
|
||||
|
||||
后台 HTTP:
|
||||
|
||||
- `GET /api/v1/admin/rooms/robot-rooms`
|
||||
- `GET /api/v1/admin/rooms/robot-rooms/available-robots`
|
||||
- `POST /api/v1/admin/rooms/robot-rooms`
|
||||
- `POST /api/v1/admin/rooms/robot-rooms/{room_id}/start`
|
||||
- `POST /api/v1/admin/rooms/robot-rooms/{room_id}/stop`
|
||||
|
||||
room-service gRPC:
|
||||
|
||||
- `AdminListRobotRooms`
|
||||
- `AdminFilterAvailableRoomRobots`
|
||||
- `AdminCreateRobotRoom`
|
||||
- `AdminSetRobotRoomStatus`
|
||||
|
||||
wallet-service gRPC:
|
||||
|
||||
- `DebitRobotGift`
|
||||
|
||||
## 创建流程
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Admin as admin-platform
|
||||
participant AS as admin-server
|
||||
participant RS as room-service
|
||||
participant RB as robot-service
|
||||
participant DB as MySQL
|
||||
|
||||
Admin->>AS: POST /admin/rooms/robot-rooms
|
||||
AS->>RS: AdminCreateRobotRoom
|
||||
RS->>DB: 查询 active 机器人房占用
|
||||
RS->>RS: 从候选机器人随机抽取人数
|
||||
RS->>RS: CreateRoom(robot_room=true)
|
||||
RS->>RS: JoinRoom for robots
|
||||
RS->>RS: RobotVirtualMicUp for robots
|
||||
RS->>DB: 写 room_robot_rooms
|
||||
RS->>RS: start runtime loops
|
||||
RS-->>AS: AdminRobotRoom
|
||||
AS-->>Admin: 机器人房间详情
|
||||
```
|
||||
|
||||
## 运行时循环
|
||||
|
||||
room-service 启动时会扫描所有 active 机器人房间并恢复运行时循环。
|
||||
|
||||
每个 active 机器人房间:
|
||||
|
||||
- 为每个机器人启动普通礼物 loop。
|
||||
- 为每个机器人启动幸运礼物 loop。
|
||||
- stop 后取消对应房间的 context。
|
||||
|
||||
普通礼物 loop:
|
||||
|
||||
1. 等待 `normal_gift_interval_ms`。
|
||||
2. 从 `gift_ids_json` 随机选择礼物。
|
||||
3. 从同房间其他机器人随机选择 1 个目标。
|
||||
4. 调用 `RobotSendGift`。
|
||||
|
||||
幸运礼物 loop:
|
||||
|
||||
1. 从 `lucky_gift_ids_json` 随机选择礼物。
|
||||
2. 随机生成 `lucky_combo_min-lucky_combo_max` 连击次数。
|
||||
3. 当前机器人向同房间其他机器人逐个送礼。
|
||||
4. 使用 `synthetic_lucky_gift=true`,跳过真实抽奖。
|
||||
5. 等待 `lucky_pause_min_ms-lucky_pause_max_ms` 后继续。
|
||||
|
||||
## 服务端安全校验
|
||||
|
||||
room-service 在机器人送礼时强校验:
|
||||
|
||||
- 当前房间必须是机器人房间。
|
||||
- sender 必须在该机器人房间 `robot_user_ids_json` 内。
|
||||
- target 必须在同一机器人房间 `robot_user_ids_json` 内。
|
||||
- sender 不能给真人送礼。
|
||||
- 当前机器人礼物只允许单目标调用。
|
||||
|
||||
## 钱包隔离
|
||||
|
||||
机器人礼物使用 `DebitRobotGift`:
|
||||
|
||||
- 资产类型固定为 `ROBOT_COIN`。
|
||||
- 如果机器人 `ROBOT_COIN` 不足,同一事务内自动补足。
|
||||
- 立即扣减本次礼物价格。
|
||||
- 交易类型为 `robot_gift_debit`。
|
||||
- 不写真实 `gift_debit`。
|
||||
- 不发 `WalletGiftDebited`。
|
||||
- 不写 `user_gift_wall`。
|
||||
- 不写 `host_period_diamond_accounts`。
|
||||
|
||||
## 幸运礼物隔离
|
||||
|
||||
机器人幸运礼物不会调用真实幸运抽奖链路。
|
||||
|
||||
room-service 只生成展示事件:
|
||||
|
||||
- `RoomRobotLuckyGiftDrawn`
|
||||
- 对 IM 客户端映射为 `lucky_gift_drawn`
|
||||
- 标记 `synthetic=true`
|
||||
- 携带机器人房间、送礼人、收礼人、礼物和展示金额字段
|
||||
|
||||
真实奖池、真实流水、真实返奖、真实统计完全不写入。
|
||||
|
||||
## 排行榜与统计隔离
|
||||
|
||||
`RoomGiftSent` 增加:
|
||||
|
||||
- `is_robot_gift`
|
||||
- `synthetic_lucky_gift`
|
||||
|
||||
下游服务看到 `is_robot_gift=true` 时跳过:
|
||||
|
||||
- 活动播报统计
|
||||
- 周星
|
||||
- 房间流水奖励
|
||||
- 每日任务
|
||||
- 成长值
|
||||
- statistics 礼物消费与幸运礼物统计
|
||||
|
||||
room-service 本地仍允许机器人房内:
|
||||
|
||||
- 房间热度增长
|
||||
- 麦位热度增长
|
||||
- 房内展示事件发送
|
||||
|
||||
## 本地接口测试
|
||||
|
||||
测试工具:
|
||||
|
||||
```bash
|
||||
go run ./tools/robot-room-api-test
|
||||
```
|
||||
|
||||
默认依赖:
|
||||
|
||||
- admin-server: `http://127.0.0.1:13100/api/v1`
|
||||
- MySQL: `127.0.0.1:23306`
|
||||
- 管理员账号: `admin / admin123`
|
||||
|
||||
工具会:
|
||||
|
||||
1. 种 12 个房间机器人账号。
|
||||
2. 种 2 个礼物配置和价格。
|
||||
3. 登录 admin-server。
|
||||
4. 调用可用机器人列表接口。
|
||||
5. 创建机器人房间。
|
||||
6. 查询 active 列表。
|
||||
7. 停止、启动、再次停止机器人房间。
|
||||
8. 校验数据库中机器人房间配置和真实 `gift_debit` 隔离。
|
||||
|
||||
## 运维注意事项
|
||||
|
||||
- 多实例 room-service 下,机器人房间 runtime 是进程内循环;重启会从 MySQL active 配置恢复。
|
||||
- 机器人房间配置以 MySQL 为准。
|
||||
- 同一机器人账号不能被多个 active 机器人房间占用。
|
||||
- 停止机器人房间只停止 runtime,不删除房间。
|
||||
- 如果需要彻底释放机器人账号,应增加删除/归档能力后再开放。
|
||||
@ -62,18 +62,6 @@ GET /api/v1/users/me/appearance
|
||||
"entitlement_id": "ent-vehicle",
|
||||
"expires_at_ms": 1999999999999
|
||||
},
|
||||
"mic_seat_animation": {
|
||||
"resource_id": 8106,
|
||||
"resource_code": "mic_seat_wave_gold",
|
||||
"resource_type": "mic_seat_animation",
|
||||
"name": "Gold Mic Wave",
|
||||
"asset_url": "https://cdn.example/mic-seat.png",
|
||||
"preview_url": "",
|
||||
"animation_url": "https://cdn.example/mic-seat.svga",
|
||||
"metadata_json": "{}",
|
||||
"entitlement_id": "ent-mic-seat-wave",
|
||||
"expires_at_ms": 1999999999999
|
||||
},
|
||||
"equipped_badges": [],
|
||||
"badges": [
|
||||
{
|
||||
@ -106,20 +94,7 @@ GET /api/v1/users/me/appearance
|
||||
"honor_badges": [],
|
||||
"server_time_ms": 1778256000000
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"resource_id": 8106,
|
||||
"resource_code": "mic_seat_wave_gold",
|
||||
"resource_type": "mic_seat_animation",
|
||||
"name": "Gold Mic Wave",
|
||||
"asset_url": "https://cdn.example/mic-seat.png",
|
||||
"preview_url": "",
|
||||
"animation_url": "https://cdn.example/mic-seat.svga",
|
||||
"metadata_json": "{}",
|
||||
"entitlement_id": "ent-mic-seat-wave",
|
||||
"expires_at_ms": 1999999999999
|
||||
}
|
||||
],
|
||||
"resources": [],
|
||||
"server_time_ms": 1778256000000
|
||||
}
|
||||
```
|
||||
@ -132,7 +107,6 @@ GET /api/v1/users/me/appearance
|
||||
| avatar_frame | 当前佩戴头像框,未佩戴时为 `{}` |
|
||||
| profile_card | 当前佩戴背景卡,未佩戴时为 `{}` |
|
||||
| vehicle | 当前佩戴入场座驾,未佩戴时为 `{}` |
|
||||
| mic_seat_animation | 当前佩戴麦位声波,未佩戴时为 `{}` |
|
||||
| equipped_badges | wallet 当前佩戴的 badge 权益 |
|
||||
| badges | 房间、公屏等高频展示用徽章,取 `strip_badges` |
|
||||
| badge_profile | 完整徽章展示配置 |
|
||||
@ -142,7 +116,7 @@ GET /api/v1/users/me/appearance
|
||||
说明:
|
||||
|
||||
- 过期佩戴不会返回。
|
||||
- 头像框、背景卡、入场座驾、麦位声波来自 wallet 当前佩戴。
|
||||
- 头像框、背景卡、入场座驾来自 wallet 当前佩戴。
|
||||
- 徽章展示配置来自 activity。
|
||||
- 客户端渲染动画优先使用 `animation_url`,没有动画时用 `asset_url` 或 `preview_url` 兜底。
|
||||
|
||||
@ -371,18 +345,6 @@ GET /api/v1/users/room-display-profiles:batch?user_ids=42,43
|
||||
"entitlement_id": "ent-vehicle",
|
||||
"expires_at_ms": 1999999999999
|
||||
},
|
||||
"mic_seat_animation": {
|
||||
"resource_id": 8106,
|
||||
"resource_code": "mic_seat_wave_gold",
|
||||
"resource_type": "mic_seat_animation",
|
||||
"name": "Gold Mic Wave",
|
||||
"asset_url": "https://cdn.example/mic-seat.png",
|
||||
"preview_url": "",
|
||||
"animation_url": "https://cdn.example/mic-seat.svga",
|
||||
"metadata_json": "{}",
|
||||
"entitlement_id": "ent-mic-seat-wave",
|
||||
"expires_at_ms": 1999999999999
|
||||
},
|
||||
"charm": 0
|
||||
}
|
||||
]
|
||||
@ -394,7 +356,6 @@ GET /api/v1/users/room-display-profiles:batch?user_ids=42,43
|
||||
- 房间、公屏、在线列表展示头像框用 `avatar_frame`。
|
||||
- 房间资料卡展示用 `profile_card`,从 `metadata_json.profile_card_layout` 解析 `content_top` 和 `content_height`。
|
||||
- 入场座驾展示用 `vehicle`。
|
||||
- 麦位声波展示用 `mic_seat_animation`。
|
||||
- 徽章条展示用 `badges`。
|
||||
- 过期佩戴不会返回。
|
||||
|
||||
@ -523,6 +484,5 @@ Flutter 处理:
|
||||
2. 背包页调 `/api/v1/users/me/resources?resource_type=xxx`。
|
||||
3. 点击佩戴调 `POST /api/v1/users/me/resources/{resource_id}/equip`,成功后刷新 `/appearance`。
|
||||
4. 点击取消佩戴调 `DELETE /api/v1/users/me/resources/{resource_type}/unequip`,成功后刷新 `/appearance`。
|
||||
5. 个人主页信息卡背景使用个人资料返回的 `profile_bg_img`;该字段是用户资料字段,和 `profile_card` 装扮资源无关。
|
||||
6. 房间、公屏、在线用户资料优先使用 `room-display-profiles:batch` 的 `avatar_frame`、`profile_card`、`vehicle`、`badges`。
|
||||
7. 收到 `room_user_joined` IM 时,只按 `entry_vehicle` 播放入场座驾。
|
||||
5. 房间、公屏、在线用户资料优先使用 `room-display-profiles:batch` 的 `avatar_frame`、`profile_card`、`vehicle`、`badges`。
|
||||
6. 收到 `room_user_joined` IM 时,只按 `entry_vehicle` 播放入场座驾。
|
||||
|
||||
@ -52,27 +52,6 @@ sequenceDiagram
|
||||
|
||||
## Response Examples
|
||||
|
||||
访问资料卡:
|
||||
|
||||
```json
|
||||
{
|
||||
"recorded": true,
|
||||
"target_stats": {
|
||||
"visitors_count": 1,
|
||||
"following_count": 0,
|
||||
"friends_count": 0,
|
||||
"updated_at_ms": 1777996800000
|
||||
},
|
||||
"target_profile": {
|
||||
"user_id": "10002",
|
||||
"display_user_id": "163002",
|
||||
"username": "Bob",
|
||||
"avatar": "https://cdn.example.com/avatar.png",
|
||||
"profile_bg_img": "https://cdn.example.com/profile-bg.png"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
申请好友:
|
||||
|
||||
```json
|
||||
|
||||
@ -78,9 +78,7 @@ func codeFromGRPC(code codes.Code) Code {
|
||||
return Unauthorized
|
||||
case codes.PermissionDenied:
|
||||
return PermissionDenied
|
||||
case codes.Unavailable, codes.DeadlineExceeded:
|
||||
// 下游服务未启动、网络不可达或 gRPC deadline 到期都属于依赖暂不可用;gateway 应返回 UPSTREAM_ERROR,
|
||||
// 不能降级为 INTERNAL_ERROR,否则本地联调和线上排障都会误判成业务代码内部崩溃。
|
||||
case codes.Unavailable:
|
||||
return Unavailable
|
||||
case codes.AlreadyExists, codes.FailedPrecondition, codes.Aborted:
|
||||
return Conflict
|
||||
|
||||
@ -127,66 +127,3 @@ INSERT IGNORE INTO third_party_payment_methods (
|
||||
('lalu', 'mifapay', 'PH', 'Philippines', 'PHP', 'Ewallet', 'Gcash', 'GCash', '', 'active', 1, 1310, @now_ms, @now_ms),
|
||||
('lalu', 'mifapay', 'PH', 'Philippines', 'PHP', 'Ewallet', 'PayMaya', 'PayMaya', '', 'active', 1, 1320, @now_ms, @now_ms),
|
||||
('lalu', 'mifapay', 'PH', 'Philippines', 'PHP', 'QR', 'QRPH', 'QRPH', '', 'active', 1, 1330, @now_ms, @now_ms);
|
||||
|
||||
-- MiFaPay logo 使用已上传 COS 的固定对象地址;仅覆盖空值、早期占位图和 Apifox 临时预览图,避免误改运营手工替换的正式图。
|
||||
UPDATE third_party_payment_methods m
|
||||
JOIN (
|
||||
SELECT 'AE' AS country_code, 'Card' AS pay_way, 'CreditCard' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/ae/card-creditcard.png' AS logo_url
|
||||
UNION ALL SELECT 'BD' AS country_code, 'Ewallet' AS pay_way, 'Nagad' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/bd/ewallet-nagad.png' AS logo_url
|
||||
UNION ALL SELECT 'BD' AS country_code, 'Ewallet' AS pay_way, 'bKash' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/bd/ewallet-bkash.png' AS logo_url
|
||||
UNION ALL SELECT 'BH' AS country_code, 'Card' AS pay_way, 'CreditCard' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/bh/card-creditcard.png' AS logo_url
|
||||
UNION ALL SELECT 'EG' AS country_code, 'Card' AS pay_way, 'CreditCard' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/eg/card-creditcard.png' AS logo_url
|
||||
UNION ALL SELECT 'EG' AS country_code, 'Ewallet' AS pay_way, 'FawryPay' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/eg/ewallet-fawrypay.png' AS logo_url
|
||||
UNION ALL SELECT 'EG' AS country_code, 'Ewallet' AS pay_way, 'Meeza' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/eg/ewallet-meeza.png' AS logo_url
|
||||
UNION ALL SELECT 'EG' AS country_code, 'Ewallet' AS pay_way, 'Opay' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/eg/ewallet-opay.png' AS logo_url
|
||||
UNION ALL SELECT 'EG' AS country_code, 'Ewallet' AS pay_way, 'Orange' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/eg/ewallet-orange.png' AS logo_url
|
||||
UNION ALL SELECT 'EG' AS country_code, 'Ewallet' AS pay_way, 'Vodafone' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/eg/ewallet-vodafone.png' AS logo_url
|
||||
UNION ALL SELECT 'GLOBAL' AS country_code, 'BankTransfer' AS pay_way, 'UnionPay' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/global/banktransfer-unionpay.png' AS logo_url
|
||||
UNION ALL SELECT 'GLOBAL' AS country_code, 'Card' AS pay_way, 'CreditCard' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/global/card-creditcard.png' AS logo_url
|
||||
UNION ALL SELECT 'GLOBAL' AS country_code, 'Ewallet' AS pay_way, 'ApplePay' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/global/ewallet-applepay.png' AS logo_url
|
||||
UNION ALL SELECT 'GLOBAL' AS country_code, 'Ewallet' AS pay_way, 'GooglePay' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/global/ewallet-googlepay.png' AS logo_url
|
||||
UNION ALL SELECT 'ID' AS country_code, 'BankTransfer' AS pay_way, 'BCA' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/id/banktransfer-bca.png' AS logo_url
|
||||
UNION ALL SELECT 'ID' AS country_code, 'BankTransfer' AS pay_way, 'BNI' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/id/banktransfer-bni.png' AS logo_url
|
||||
UNION ALL SELECT 'ID' AS country_code, 'BankTransfer' AS pay_way, 'BRI' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/id/banktransfer-bri.png' AS logo_url
|
||||
UNION ALL SELECT 'ID' AS country_code, 'BankTransfer' AS pay_way, 'Mandiri' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/id/banktransfer-mandiri.png' AS logo_url
|
||||
UNION ALL SELECT 'ID' AS country_code, 'BankTransfer' AS pay_way, 'Permata' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/id/banktransfer-permata.png' AS logo_url
|
||||
UNION ALL SELECT 'ID' AS country_code, 'BankTransfer' AS pay_way, 'QRIS' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/id/banktransfer-qris.png' AS logo_url
|
||||
UNION ALL SELECT 'ID' AS country_code, 'Ewallet' AS pay_way, 'DANA' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/id/ewallet-dana.png' AS logo_url
|
||||
UNION ALL SELECT 'ID' AS country_code, 'Ewallet' AS pay_way, 'LinkAja' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/id/ewallet-linkaja.png' AS logo_url
|
||||
UNION ALL SELECT 'ID' AS country_code, 'Ewallet' AS pay_way, 'OVO' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/id/ewallet-ovo.png' AS logo_url
|
||||
UNION ALL SELECT 'ID' AS country_code, 'Ewallet' AS pay_way, 'ShopeePay' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/id/ewallet-shopeepay.png' AS logo_url
|
||||
UNION ALL SELECT 'ID' AS country_code, 'QR' AS pay_way, 'QRIS' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/id/qr-qris.png' AS logo_url
|
||||
UNION ALL SELECT 'IN' AS country_code, 'Ewallet' AS pay_way, 'BHIM' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/in/ewallet-bhim.png' AS logo_url
|
||||
UNION ALL SELECT 'IN' AS country_code, 'Ewallet' AS pay_way, 'GooglePay' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/in/ewallet-googlepay.png' AS logo_url
|
||||
UNION ALL SELECT 'IN' AS country_code, 'Ewallet' AS pay_way, 'Mobikwik' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/in/ewallet-mobikwik.png' AS logo_url
|
||||
UNION ALL SELECT 'IN' AS country_code, 'Ewallet' AS pay_way, 'Paytm' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/in/ewallet-paytm.png' AS logo_url
|
||||
UNION ALL SELECT 'IN' AS country_code, 'Ewallet' AS pay_way, 'PhonePe' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/in/ewallet-phonepe.png' AS logo_url
|
||||
UNION ALL SELECT 'IN' AS country_code, 'Ewallet' AS pay_way, 'UPI' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/in/ewallet-upi.png' AS logo_url
|
||||
UNION ALL SELECT 'KW' AS country_code, 'Card' AS pay_way, 'KENT' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/kw/card-kent.png' AS logo_url
|
||||
UNION ALL SELECT 'KW' AS country_code, 'Card' AS pay_way, 'Knet' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/kw/card-knet.png' AS logo_url
|
||||
UNION ALL SELECT 'MY' AS country_code, 'BankTransfer' AS pay_way, 'FPX' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/my/banktransfer-fpx.png' AS logo_url
|
||||
UNION ALL SELECT 'MY' AS country_code, 'Ewallet' AS pay_way, 'TNG' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/my/ewallet-tng.png' AS logo_url
|
||||
UNION ALL SELECT 'OM' AS country_code, 'Card' AS pay_way, 'CreditCard' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/om/card-creditcard.png' AS logo_url
|
||||
UNION ALL SELECT 'PH' AS country_code, 'BankTransfer' AS pay_way, 'QRPH' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/ph/banktransfer-qrph.png' AS logo_url
|
||||
UNION ALL SELECT 'PH' AS country_code, 'Ewallet' AS pay_way, 'Gcash' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/ph/ewallet-gcash.png' AS logo_url
|
||||
UNION ALL SELECT 'PH' AS country_code, 'Ewallet' AS pay_way, 'PayMaya' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/ph/ewallet-paymaya.png' AS logo_url
|
||||
UNION ALL SELECT 'PH' AS country_code, 'QR' AS pay_way, 'QRPH' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/ph/qr-qrph.png' AS logo_url
|
||||
UNION ALL SELECT 'PK' AS country_code, 'Ewallet' AS pay_way, 'Easypaisa' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/pk/ewallet-easypaisa.png' AS logo_url
|
||||
UNION ALL SELECT 'PK' AS country_code, 'Ewallet' AS pay_way, 'JazzCash' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/pk/ewallet-jazzcash.png' AS logo_url
|
||||
UNION ALL SELECT 'QA' AS country_code, 'Card' AS pay_way, 'CreditCard' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/qa/card-creditcard.png' AS logo_url
|
||||
UNION ALL SELECT 'SA' AS country_code, 'Card' AS pay_way, 'MADA' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/sa/card-mada.png' AS logo_url
|
||||
UNION ALL SELECT 'SA' AS country_code, 'Ewallet' AS pay_way, 'ApplePay' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/sa/ewallet-applepay.png' AS logo_url
|
||||
UNION ALL SELECT 'SA' AS country_code, 'Ewallet' AS pay_way, 'STCPay' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/sa/ewallet-stcpay.png' AS logo_url
|
||||
UNION ALL SELECT 'TH' AS country_code, 'BankTransfer' AS pay_way, 'PromptPay' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/th/banktransfer-promptpay.png' AS logo_url
|
||||
UNION ALL SELECT 'TH' AS country_code, 'Ewallet' AS pay_way, 'TrueMoney' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/th/ewallet-truemoney.png' AS logo_url
|
||||
UNION ALL SELECT 'VN' AS country_code, 'BankTransfer' AS pay_way, 'VN_BankTransfer' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/vn/banktransfer-vn-banktransfer.png' AS logo_url
|
||||
UNION ALL SELECT 'VN' AS country_code, 'QR' AS pay_way, 'VietQR' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/vn/qr-vietqr.png' AS logo_url
|
||||
) logos
|
||||
ON UPPER(m.country_code) = logos.country_code
|
||||
AND LOWER(m.pay_way) = LOWER(logos.pay_way)
|
||||
AND LOWER(m.pay_type) = LOWER(logos.pay_type)
|
||||
SET m.logo_url = logos.logo_url,
|
||||
m.updated_at_ms = @now_ms
|
||||
WHERE m.app_code = 'lalu'
|
||||
AND m.provider_code = 'mifapay'
|
||||
AND (m.logo_url = '' OR m.logo_url LIKE 'https://placehold.co/%' OR m.logo_url LIKE '%/image-preview');
|
||||
|
||||
@ -105,66 +105,3 @@ ON DUPLICATE KEY UPDATE
|
||||
usd_to_currency_rate = IF(usd_to_currency_rate > 0, usd_to_currency_rate, VALUES(usd_to_currency_rate)),
|
||||
sort_order = VALUES(sort_order),
|
||||
updated_at_ms = VALUES(updated_at_ms);
|
||||
|
||||
-- MiFaPay logo 使用已上传 COS 的固定对象地址;仅覆盖空值、早期占位图和 Apifox 临时预览图,避免误改运营手工替换的正式图。
|
||||
UPDATE third_party_payment_methods m
|
||||
JOIN (
|
||||
SELECT 'AE' AS country_code, 'Card' AS pay_way, 'CreditCard' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/ae/card-creditcard.png' AS logo_url
|
||||
UNION ALL SELECT 'BD' AS country_code, 'Ewallet' AS pay_way, 'Nagad' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/bd/ewallet-nagad.png' AS logo_url
|
||||
UNION ALL SELECT 'BD' AS country_code, 'Ewallet' AS pay_way, 'bKash' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/bd/ewallet-bkash.png' AS logo_url
|
||||
UNION ALL SELECT 'BH' AS country_code, 'Card' AS pay_way, 'CreditCard' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/bh/card-creditcard.png' AS logo_url
|
||||
UNION ALL SELECT 'EG' AS country_code, 'Card' AS pay_way, 'CreditCard' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/eg/card-creditcard.png' AS logo_url
|
||||
UNION ALL SELECT 'EG' AS country_code, 'Ewallet' AS pay_way, 'FawryPay' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/eg/ewallet-fawrypay.png' AS logo_url
|
||||
UNION ALL SELECT 'EG' AS country_code, 'Ewallet' AS pay_way, 'Meeza' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/eg/ewallet-meeza.png' AS logo_url
|
||||
UNION ALL SELECT 'EG' AS country_code, 'Ewallet' AS pay_way, 'Opay' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/eg/ewallet-opay.png' AS logo_url
|
||||
UNION ALL SELECT 'EG' AS country_code, 'Ewallet' AS pay_way, 'Orange' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/eg/ewallet-orange.png' AS logo_url
|
||||
UNION ALL SELECT 'EG' AS country_code, 'Ewallet' AS pay_way, 'Vodafone' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/eg/ewallet-vodafone.png' AS logo_url
|
||||
UNION ALL SELECT 'GLOBAL' AS country_code, 'BankTransfer' AS pay_way, 'UnionPay' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/global/banktransfer-unionpay.png' AS logo_url
|
||||
UNION ALL SELECT 'GLOBAL' AS country_code, 'Card' AS pay_way, 'CreditCard' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/global/card-creditcard.png' AS logo_url
|
||||
UNION ALL SELECT 'GLOBAL' AS country_code, 'Ewallet' AS pay_way, 'ApplePay' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/global/ewallet-applepay.png' AS logo_url
|
||||
UNION ALL SELECT 'GLOBAL' AS country_code, 'Ewallet' AS pay_way, 'GooglePay' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/global/ewallet-googlepay.png' AS logo_url
|
||||
UNION ALL SELECT 'ID' AS country_code, 'BankTransfer' AS pay_way, 'BCA' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/id/banktransfer-bca.png' AS logo_url
|
||||
UNION ALL SELECT 'ID' AS country_code, 'BankTransfer' AS pay_way, 'BNI' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/id/banktransfer-bni.png' AS logo_url
|
||||
UNION ALL SELECT 'ID' AS country_code, 'BankTransfer' AS pay_way, 'BRI' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/id/banktransfer-bri.png' AS logo_url
|
||||
UNION ALL SELECT 'ID' AS country_code, 'BankTransfer' AS pay_way, 'Mandiri' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/id/banktransfer-mandiri.png' AS logo_url
|
||||
UNION ALL SELECT 'ID' AS country_code, 'BankTransfer' AS pay_way, 'Permata' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/id/banktransfer-permata.png' AS logo_url
|
||||
UNION ALL SELECT 'ID' AS country_code, 'BankTransfer' AS pay_way, 'QRIS' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/id/banktransfer-qris.png' AS logo_url
|
||||
UNION ALL SELECT 'ID' AS country_code, 'Ewallet' AS pay_way, 'DANA' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/id/ewallet-dana.png' AS logo_url
|
||||
UNION ALL SELECT 'ID' AS country_code, 'Ewallet' AS pay_way, 'LinkAja' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/id/ewallet-linkaja.png' AS logo_url
|
||||
UNION ALL SELECT 'ID' AS country_code, 'Ewallet' AS pay_way, 'OVO' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/id/ewallet-ovo.png' AS logo_url
|
||||
UNION ALL SELECT 'ID' AS country_code, 'Ewallet' AS pay_way, 'ShopeePay' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/id/ewallet-shopeepay.png' AS logo_url
|
||||
UNION ALL SELECT 'ID' AS country_code, 'QR' AS pay_way, 'QRIS' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/id/qr-qris.png' AS logo_url
|
||||
UNION ALL SELECT 'IN' AS country_code, 'Ewallet' AS pay_way, 'BHIM' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/in/ewallet-bhim.png' AS logo_url
|
||||
UNION ALL SELECT 'IN' AS country_code, 'Ewallet' AS pay_way, 'GooglePay' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/in/ewallet-googlepay.png' AS logo_url
|
||||
UNION ALL SELECT 'IN' AS country_code, 'Ewallet' AS pay_way, 'Mobikwik' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/in/ewallet-mobikwik.png' AS logo_url
|
||||
UNION ALL SELECT 'IN' AS country_code, 'Ewallet' AS pay_way, 'Paytm' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/in/ewallet-paytm.png' AS logo_url
|
||||
UNION ALL SELECT 'IN' AS country_code, 'Ewallet' AS pay_way, 'PhonePe' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/in/ewallet-phonepe.png' AS logo_url
|
||||
UNION ALL SELECT 'IN' AS country_code, 'Ewallet' AS pay_way, 'UPI' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/in/ewallet-upi.png' AS logo_url
|
||||
UNION ALL SELECT 'KW' AS country_code, 'Card' AS pay_way, 'KENT' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/kw/card-kent.png' AS logo_url
|
||||
UNION ALL SELECT 'KW' AS country_code, 'Card' AS pay_way, 'Knet' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/kw/card-knet.png' AS logo_url
|
||||
UNION ALL SELECT 'MY' AS country_code, 'BankTransfer' AS pay_way, 'FPX' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/my/banktransfer-fpx.png' AS logo_url
|
||||
UNION ALL SELECT 'MY' AS country_code, 'Ewallet' AS pay_way, 'TNG' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/my/ewallet-tng.png' AS logo_url
|
||||
UNION ALL SELECT 'OM' AS country_code, 'Card' AS pay_way, 'CreditCard' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/om/card-creditcard.png' AS logo_url
|
||||
UNION ALL SELECT 'PH' AS country_code, 'BankTransfer' AS pay_way, 'QRPH' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/ph/banktransfer-qrph.png' AS logo_url
|
||||
UNION ALL SELECT 'PH' AS country_code, 'Ewallet' AS pay_way, 'Gcash' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/ph/ewallet-gcash.png' AS logo_url
|
||||
UNION ALL SELECT 'PH' AS country_code, 'Ewallet' AS pay_way, 'PayMaya' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/ph/ewallet-paymaya.png' AS logo_url
|
||||
UNION ALL SELECT 'PH' AS country_code, 'QR' AS pay_way, 'QRPH' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/ph/qr-qrph.png' AS logo_url
|
||||
UNION ALL SELECT 'PK' AS country_code, 'Ewallet' AS pay_way, 'Easypaisa' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/pk/ewallet-easypaisa.png' AS logo_url
|
||||
UNION ALL SELECT 'PK' AS country_code, 'Ewallet' AS pay_way, 'JazzCash' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/pk/ewallet-jazzcash.png' AS logo_url
|
||||
UNION ALL SELECT 'QA' AS country_code, 'Card' AS pay_way, 'CreditCard' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/qa/card-creditcard.png' AS logo_url
|
||||
UNION ALL SELECT 'SA' AS country_code, 'Card' AS pay_way, 'MADA' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/sa/card-mada.png' AS logo_url
|
||||
UNION ALL SELECT 'SA' AS country_code, 'Ewallet' AS pay_way, 'ApplePay' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/sa/ewallet-applepay.png' AS logo_url
|
||||
UNION ALL SELECT 'SA' AS country_code, 'Ewallet' AS pay_way, 'STCPay' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/sa/ewallet-stcpay.png' AS logo_url
|
||||
UNION ALL SELECT 'TH' AS country_code, 'BankTransfer' AS pay_way, 'PromptPay' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/th/banktransfer-promptpay.png' AS logo_url
|
||||
UNION ALL SELECT 'TH' AS country_code, 'Ewallet' AS pay_way, 'TrueMoney' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/th/ewallet-truemoney.png' AS logo_url
|
||||
UNION ALL SELECT 'VN' AS country_code, 'BankTransfer' AS pay_way, 'VN_BankTransfer' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/vn/banktransfer-vn-banktransfer.png' AS logo_url
|
||||
UNION ALL SELECT 'VN' AS country_code, 'QR' AS pay_way, 'VietQR' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/vn/qr-vietqr.png' AS logo_url
|
||||
) logos
|
||||
ON UPPER(m.country_code) = logos.country_code
|
||||
AND LOWER(m.pay_way) = LOWER(logos.pay_way)
|
||||
AND LOWER(m.pay_type) = LOWER(logos.pay_type)
|
||||
SET m.logo_url = logos.logo_url,
|
||||
m.updated_at_ms = @now_ms
|
||||
WHERE m.app_code = 'lalu'
|
||||
AND m.provider_code = 'mifapay'
|
||||
AND (m.logo_url = '' OR m.logo_url LIKE 'https://placehold.co/%' OR m.logo_url LIKE '%/image-preview');
|
||||
|
||||
@ -1,44 +0,0 @@
|
||||
SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
USE hyapp_user;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS manager_profiles (
|
||||
app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码,用于多租户隔离',
|
||||
user_id BIGINT NOT NULL PRIMARY KEY COMMENT '经理用户 ID',
|
||||
status VARCHAR(32) NOT NULL COMMENT '业务状态',
|
||||
contact_info VARCHAR(128) NOT NULL DEFAULT '' COMMENT '联系信息',
|
||||
created_by_admin_id BIGINT NOT NULL DEFAULT 0 COMMENT '创建后台管理员 ID',
|
||||
created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms',
|
||||
updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms',
|
||||
KEY idx_manager_profiles_status (app_code, status, updated_at_ms),
|
||||
KEY idx_manager_profiles_created_by (app_code, created_by_admin_id, created_at_ms)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='经理资料表';
|
||||
|
||||
SET @ddl := IF(
|
||||
(SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'manager_profiles' AND COLUMN_NAME = 'contact_info') = 0,
|
||||
'ALTER TABLE manager_profiles ADD COLUMN contact_info VARCHAR(128) NOT NULL DEFAULT '''' COMMENT ''联系信息'' AFTER status',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
INSERT INTO manager_profiles (
|
||||
app_code, user_id, status, contact_info, created_by_admin_id, created_at_ms, updated_at_ms
|
||||
)
|
||||
SELECT
|
||||
bl.app_code,
|
||||
bl.created_by_user_id,
|
||||
'active',
|
||||
'',
|
||||
0,
|
||||
MIN(bl.created_at_ms),
|
||||
MAX(bl.updated_at_ms)
|
||||
FROM bd_leader_profiles bl
|
||||
JOIN users u
|
||||
ON u.app_code = bl.app_code
|
||||
AND u.user_id = bl.created_by_user_id
|
||||
WHERE bl.created_by_user_id > 0
|
||||
GROUP BY bl.app_code, bl.created_by_user_id
|
||||
ON DUPLICATE KEY UPDATE
|
||||
updated_at_ms = GREATEST(manager_profiles.updated_at_ms, VALUES(updated_at_ms));
|
||||
@ -1,66 +0,0 @@
|
||||
SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
SET @now_ms = CAST(UNIX_TIMESTAMP(UTC_TIMESTAMP(3)) * 1000 AS UNSIGNED);
|
||||
|
||||
-- MiFaPay logo 使用已上传 COS 的固定对象地址;仅覆盖空值、早期占位图和 Apifox 临时预览图,避免误改运营手工替换的正式图。
|
||||
UPDATE third_party_payment_methods m
|
||||
JOIN (
|
||||
SELECT 'AE' AS country_code, 'Card' AS pay_way, 'CreditCard' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/ae/card-creditcard.png' AS logo_url
|
||||
UNION ALL SELECT 'BD' AS country_code, 'Ewallet' AS pay_way, 'Nagad' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/bd/ewallet-nagad.png' AS logo_url
|
||||
UNION ALL SELECT 'BD' AS country_code, 'Ewallet' AS pay_way, 'bKash' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/bd/ewallet-bkash.png' AS logo_url
|
||||
UNION ALL SELECT 'BH' AS country_code, 'Card' AS pay_way, 'CreditCard' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/bh/card-creditcard.png' AS logo_url
|
||||
UNION ALL SELECT 'EG' AS country_code, 'Card' AS pay_way, 'CreditCard' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/eg/card-creditcard.png' AS logo_url
|
||||
UNION ALL SELECT 'EG' AS country_code, 'Ewallet' AS pay_way, 'FawryPay' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/eg/ewallet-fawrypay.png' AS logo_url
|
||||
UNION ALL SELECT 'EG' AS country_code, 'Ewallet' AS pay_way, 'Meeza' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/eg/ewallet-meeza.png' AS logo_url
|
||||
UNION ALL SELECT 'EG' AS country_code, 'Ewallet' AS pay_way, 'Opay' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/eg/ewallet-opay.png' AS logo_url
|
||||
UNION ALL SELECT 'EG' AS country_code, 'Ewallet' AS pay_way, 'Orange' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/eg/ewallet-orange.png' AS logo_url
|
||||
UNION ALL SELECT 'EG' AS country_code, 'Ewallet' AS pay_way, 'Vodafone' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/eg/ewallet-vodafone.png' AS logo_url
|
||||
UNION ALL SELECT 'GLOBAL' AS country_code, 'BankTransfer' AS pay_way, 'UnionPay' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/global/banktransfer-unionpay.png' AS logo_url
|
||||
UNION ALL SELECT 'GLOBAL' AS country_code, 'Card' AS pay_way, 'CreditCard' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/global/card-creditcard.png' AS logo_url
|
||||
UNION ALL SELECT 'GLOBAL' AS country_code, 'Ewallet' AS pay_way, 'ApplePay' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/global/ewallet-applepay.png' AS logo_url
|
||||
UNION ALL SELECT 'GLOBAL' AS country_code, 'Ewallet' AS pay_way, 'GooglePay' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/global/ewallet-googlepay.png' AS logo_url
|
||||
UNION ALL SELECT 'ID' AS country_code, 'BankTransfer' AS pay_way, 'BCA' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/id/banktransfer-bca.png' AS logo_url
|
||||
UNION ALL SELECT 'ID' AS country_code, 'BankTransfer' AS pay_way, 'BNI' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/id/banktransfer-bni.png' AS logo_url
|
||||
UNION ALL SELECT 'ID' AS country_code, 'BankTransfer' AS pay_way, 'BRI' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/id/banktransfer-bri.png' AS logo_url
|
||||
UNION ALL SELECT 'ID' AS country_code, 'BankTransfer' AS pay_way, 'Mandiri' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/id/banktransfer-mandiri.png' AS logo_url
|
||||
UNION ALL SELECT 'ID' AS country_code, 'BankTransfer' AS pay_way, 'Permata' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/id/banktransfer-permata.png' AS logo_url
|
||||
UNION ALL SELECT 'ID' AS country_code, 'BankTransfer' AS pay_way, 'QRIS' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/id/banktransfer-qris.png' AS logo_url
|
||||
UNION ALL SELECT 'ID' AS country_code, 'Ewallet' AS pay_way, 'DANA' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/id/ewallet-dana.png' AS logo_url
|
||||
UNION ALL SELECT 'ID' AS country_code, 'Ewallet' AS pay_way, 'LinkAja' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/id/ewallet-linkaja.png' AS logo_url
|
||||
UNION ALL SELECT 'ID' AS country_code, 'Ewallet' AS pay_way, 'OVO' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/id/ewallet-ovo.png' AS logo_url
|
||||
UNION ALL SELECT 'ID' AS country_code, 'Ewallet' AS pay_way, 'ShopeePay' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/id/ewallet-shopeepay.png' AS logo_url
|
||||
UNION ALL SELECT 'ID' AS country_code, 'QR' AS pay_way, 'QRIS' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/id/qr-qris.png' AS logo_url
|
||||
UNION ALL SELECT 'IN' AS country_code, 'Ewallet' AS pay_way, 'BHIM' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/in/ewallet-bhim.png' AS logo_url
|
||||
UNION ALL SELECT 'IN' AS country_code, 'Ewallet' AS pay_way, 'GooglePay' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/in/ewallet-googlepay.png' AS logo_url
|
||||
UNION ALL SELECT 'IN' AS country_code, 'Ewallet' AS pay_way, 'Mobikwik' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/in/ewallet-mobikwik.png' AS logo_url
|
||||
UNION ALL SELECT 'IN' AS country_code, 'Ewallet' AS pay_way, 'Paytm' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/in/ewallet-paytm.png' AS logo_url
|
||||
UNION ALL SELECT 'IN' AS country_code, 'Ewallet' AS pay_way, 'PhonePe' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/in/ewallet-phonepe.png' AS logo_url
|
||||
UNION ALL SELECT 'IN' AS country_code, 'Ewallet' AS pay_way, 'UPI' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/in/ewallet-upi.png' AS logo_url
|
||||
UNION ALL SELECT 'KW' AS country_code, 'Card' AS pay_way, 'KENT' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/kw/card-kent.png' AS logo_url
|
||||
UNION ALL SELECT 'KW' AS country_code, 'Card' AS pay_way, 'Knet' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/kw/card-knet.png' AS logo_url
|
||||
UNION ALL SELECT 'MY' AS country_code, 'BankTransfer' AS pay_way, 'FPX' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/my/banktransfer-fpx.png' AS logo_url
|
||||
UNION ALL SELECT 'MY' AS country_code, 'Ewallet' AS pay_way, 'TNG' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/my/ewallet-tng.png' AS logo_url
|
||||
UNION ALL SELECT 'OM' AS country_code, 'Card' AS pay_way, 'CreditCard' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/om/card-creditcard.png' AS logo_url
|
||||
UNION ALL SELECT 'PH' AS country_code, 'BankTransfer' AS pay_way, 'QRPH' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/ph/banktransfer-qrph.png' AS logo_url
|
||||
UNION ALL SELECT 'PH' AS country_code, 'Ewallet' AS pay_way, 'Gcash' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/ph/ewallet-gcash.png' AS logo_url
|
||||
UNION ALL SELECT 'PH' AS country_code, 'Ewallet' AS pay_way, 'PayMaya' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/ph/ewallet-paymaya.png' AS logo_url
|
||||
UNION ALL SELECT 'PH' AS country_code, 'QR' AS pay_way, 'QRPH' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/ph/qr-qrph.png' AS logo_url
|
||||
UNION ALL SELECT 'PK' AS country_code, 'Ewallet' AS pay_way, 'Easypaisa' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/pk/ewallet-easypaisa.png' AS logo_url
|
||||
UNION ALL SELECT 'PK' AS country_code, 'Ewallet' AS pay_way, 'JazzCash' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/pk/ewallet-jazzcash.png' AS logo_url
|
||||
UNION ALL SELECT 'QA' AS country_code, 'Card' AS pay_way, 'CreditCard' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/qa/card-creditcard.png' AS logo_url
|
||||
UNION ALL SELECT 'SA' AS country_code, 'Card' AS pay_way, 'MADA' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/sa/card-mada.png' AS logo_url
|
||||
UNION ALL SELECT 'SA' AS country_code, 'Ewallet' AS pay_way, 'ApplePay' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/sa/ewallet-applepay.png' AS logo_url
|
||||
UNION ALL SELECT 'SA' AS country_code, 'Ewallet' AS pay_way, 'STCPay' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/sa/ewallet-stcpay.png' AS logo_url
|
||||
UNION ALL SELECT 'TH' AS country_code, 'BankTransfer' AS pay_way, 'PromptPay' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/th/banktransfer-promptpay.png' AS logo_url
|
||||
UNION ALL SELECT 'TH' AS country_code, 'Ewallet' AS pay_way, 'TrueMoney' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/th/ewallet-truemoney.png' AS logo_url
|
||||
UNION ALL SELECT 'VN' AS country_code, 'BankTransfer' AS pay_way, 'VN_BankTransfer' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/vn/banktransfer-vn-banktransfer.png' AS logo_url
|
||||
UNION ALL SELECT 'VN' AS country_code, 'QR' AS pay_way, 'VietQR' AS pay_type, 'https://media.haiyihy.com/admin/payment/mifapay/logos/vn/qr-vietqr.png' AS logo_url
|
||||
) logos
|
||||
ON UPPER(m.country_code) = logos.country_code
|
||||
AND LOWER(m.pay_way) = LOWER(logos.pay_way)
|
||||
AND LOWER(m.pay_type) = LOWER(logos.pay_type)
|
||||
SET m.logo_url = logos.logo_url,
|
||||
m.updated_at_ms = @now_ms
|
||||
WHERE m.app_code = 'lalu'
|
||||
AND m.provider_code = 'mifapay'
|
||||
AND (m.logo_url = '' OR m.logo_url LIKE 'https://placehold.co/%' OR m.logo_url LIKE '%/image-preview');
|
||||
@ -1,15 +0,0 @@
|
||||
SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
USE hyapp_user;
|
||||
|
||||
SET @now_ms = CAST(UNIX_TIMESTAMP(UTC_TIMESTAMP(3)) * 1000 AS UNSIGNED);
|
||||
|
||||
-- App 注册表是 gateway 包名解析的事实来源;Huwaa 使用独立 app_code 隔离用户、钱包、房间和统计数据。
|
||||
INSERT INTO apps (app_code, app_name, package_name, platform, status, created_at_ms, updated_at_ms)
|
||||
VALUES ('huwaa', 'Huwaa', 'com.app.huwaa', '', 'active', @now_ms, @now_ms)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
app_name = VALUES(app_name),
|
||||
package_name = VALUES(package_name),
|
||||
platform = VALUES(platform),
|
||||
status = VALUES(status),
|
||||
updated_at_ms = VALUES(updated_at_ms);
|
||||
@ -87,8 +87,6 @@ go run ./cmd/server -config configs/config.yaml -bootstrap
|
||||
7. 国家创建和国家启用/禁用是后台管理后端能力,实现在 `hyapp-admin-server`,不要为这些后台动作新增 user-service RPC 或 user-service 业务逻辑。
|
||||
8. 后台创建 BD Leader 按目标用户当前 `users.region_id` 创建;admin-server 只负责权限和审计,不传 `regionId`,user-service 在关系创建事务里读取目标用户区域并写入 `bd_leader_profiles.region_id`,不要在创建 BD Leader 时顺手修改用户区域。
|
||||
9. `hyapp.local/api` 是唯一允许直接依赖的 app 后端契约 module;不要 import `hyapp/services/*`、`hyapp/internal/*` 或 `hyapp/pkg/*`。
|
||||
10. 运营管理/全服通知只能调用 `activityv1.MessageInboxService.CreateFanoutJob` 创建异步 fanout 命令;admin-server 不直接写 activity-service 数据表,也不能在 HTTP 请求里同步遍历用户发系统/活动消息。
|
||||
11. 全服通知菜单 code 固定为 `operation-full-server-notice`,权限固定为 `full-server-notice:view` 和 `full-server-notice:send`;新增 UI、OpenAPI、种子权限和路由时必须保持这些 code 一致。
|
||||
|
||||
## 验证
|
||||
|
||||
|
||||
@ -64,27 +64,6 @@ go run ./cmd/server -config configs/config.yaml -bootstrap
|
||||
- 数据权限通过角色的 `data-scopes` 保存,并在后台用户列表/导出查询中执行。
|
||||
- 任务产物默认写入 `storage/exports`,通过 `/api/v1/jobs/:id/artifact` 下载。
|
||||
|
||||
## Full Server Notice
|
||||
|
||||
菜单入口:运营管理 / 全服通知,菜单 code 为 `operation-full-server-notice`,页面路径为 `/operations/full-server-notices`。
|
||||
|
||||
接口:
|
||||
|
||||
```text
|
||||
POST /api/v1/admin/operations/full-server-notices/fanout
|
||||
```
|
||||
|
||||
参数只传后台需要的命令字段:`message_type` 为 `system` 或 `activity`,`target_scope` 支持 `all_active_users`、`single_user`、`user_ids`、`region`、`country`,内容字段为 `title`、`summary`、`body`,跳转字段为 `action_type`、`action_param`,扩展字段为 `metadata_json`。`command_id` 是幂等键,不传时由 admin-server 按管理员和当前时间生成。
|
||||
|
||||
返回值包含 `job_id`、`status`、`created`、`command_id`、`message_type`、`target_scope`。接口只创建 activity-service 的 `message_fanout_jobs`,实际逐用户写入由 message fanout worker 分批完成。
|
||||
|
||||
权限:
|
||||
|
||||
- 查看菜单:`full-server-notice:view`
|
||||
- 发送通知:`full-server-notice:send`
|
||||
|
||||
相关 IM:Flutter 消息页的系统/活动标签读取后端 inbox;当前客户端只展示标题、摘要/正文和时间。腾讯云 IM 单聊和房间群消息不走这个后台入口。
|
||||
|
||||
## Verify
|
||||
|
||||
```bash
|
||||
|
||||
@ -18,7 +18,6 @@ import (
|
||||
"hyapp-admin-server/internal/config"
|
||||
"hyapp-admin-server/internal/integration/activityclient"
|
||||
"hyapp-admin-server/internal/integration/gameclient"
|
||||
"hyapp-admin-server/internal/integration/robotclient"
|
||||
"hyapp-admin-server/internal/integration/roomclient"
|
||||
"hyapp-admin-server/internal/integration/userclient"
|
||||
"hyapp-admin-server/internal/integration/walletclient"
|
||||
@ -26,7 +25,6 @@ import (
|
||||
"hyapp-admin-server/internal/migration"
|
||||
achievementconfigmodule "hyapp-admin-server/internal/modules/achievementconfig"
|
||||
adminusermodule "hyapp-admin-server/internal/modules/adminuser"
|
||||
agencyopeningmodule "hyapp-admin-server/internal/modules/agencyopening"
|
||||
appconfigmodule "hyapp-admin-server/internal/modules/appconfig"
|
||||
appregistrymodule "hyapp-admin-server/internal/modules/appregistry"
|
||||
appusermodule "hyapp-admin-server/internal/modules/appuser"
|
||||
@ -39,7 +37,6 @@ import (
|
||||
dailytaskmodule "hyapp-admin-server/internal/modules/dailytask"
|
||||
dashboardmodule "hyapp-admin-server/internal/modules/dashboard"
|
||||
firstrechargerewardmodule "hyapp-admin-server/internal/modules/firstrechargereward"
|
||||
fullservernoticemodule "hyapp-admin-server/internal/modules/fullservernotice"
|
||||
gamemanagementmodule "hyapp-admin-server/internal/modules/gamemanagement"
|
||||
giftdiamondmodule "hyapp-admin-server/internal/modules/giftdiamond"
|
||||
healthmodule "hyapp-admin-server/internal/modules/health"
|
||||
@ -200,13 +197,6 @@ func main() {
|
||||
defer roomConn.Close()
|
||||
roomClient := roomclient.NewGRPC(roomConn)
|
||||
|
||||
robotConn, err := dialBackendGRPC(cfg.RobotService.Addr)
|
||||
if err != nil {
|
||||
fatalRuntime("connect_robot_service_failed", err)
|
||||
}
|
||||
defer robotConn.Close()
|
||||
robotClient := robotclient.NewGRPC(robotConn)
|
||||
|
||||
activityConn, err := dialBackendGRPC(cfg.ActivityService.Addr)
|
||||
if err != nil {
|
||||
fatalRuntime("connect_activity_service_failed", err)
|
||||
@ -254,19 +244,17 @@ func main() {
|
||||
Audit: auditHandler,
|
||||
Auth: authmodule.New(store, auth, auditHandler, cfg),
|
||||
AdminUser: adminusermodule.New(store, cfg, auditHandler),
|
||||
AgencyOpening: agencyopeningmodule.New(activityclient.NewGRPC(activityConn), auditHandler),
|
||||
AchievementConfig: achievementconfigmodule.New(activityclient.NewGRPC(activityConn), auditHandler),
|
||||
AppConfig: appconfigmodule.New(store, auditHandler),
|
||||
AppRegistry: appregistrymodule.New(userDB),
|
||||
AppUser: appusermodule.New(userclient.NewGRPC(userConn), activityclient.NewGRPC(activityConn), sqlDB, userDB, walletDB, cfg, auditHandler),
|
||||
CoinLedger: coinledgermodule.New(userDB, walletDB, sqlDB, walletclient.NewGRPC(walletConn), auditHandler),
|
||||
CountryRegion: countryregionmodule.New(userclient.NewGRPC(userConn), userDB, cfg, auditHandler),
|
||||
CPRelation: cprelationmodule.NewWithActivity(userDB, activityclient.NewGRPC(activityConn), auditHandler),
|
||||
CPRelation: cprelationmodule.New(userDB, auditHandler),
|
||||
CumulativeRecharge: cumulativerechargerewardmodule.New(activityclient.NewGRPC(activityConn), userDB, auditHandler),
|
||||
DailyTask: dailytaskmodule.New(activityclient.NewGRPC(activityConn), auditHandler),
|
||||
Dashboard: dashboardmodule.New(store, cfg, userclient.NewGRPC(userConn)),
|
||||
FirstRechargeReward: firstrechargerewardmodule.New(activityclient.NewGRPC(activityConn), userDB, auditHandler),
|
||||
FullServerNotice: fullservernoticemodule.New(activityclient.NewGRPC(activityConn), auditHandler),
|
||||
Game: gamemanagementmodule.New(
|
||||
gameclient.NewGRPC(gameConn),
|
||||
userclient.NewGRPC(userConn),
|
||||
@ -291,7 +279,7 @@ func main() {
|
||||
RegistrationReward: registrationrewardmodule.New(activityclient.NewGRPC(activityConn), userDB, auditHandler),
|
||||
RegionBlock: regionblockmodule.New(userDB, auditHandler),
|
||||
Resource: resourcemodule.New(walletclient.NewGRPC(walletConn), store, userDB, cfg.WalletService.RequestTimeout, auditHandler),
|
||||
RoomAdmin: roomadminmodule.New(userDB, roomClient, robotClient, auditHandler),
|
||||
RoomAdmin: roomadminmodule.New(userDB, roomClient, auditHandler),
|
||||
RoomRocket: roomrocketmodule.New(roomClient, auditHandler),
|
||||
RoomTurnoverReward: roomturnoverrewardmodule.New(activityclient.NewGRPC(activityConn), auditHandler),
|
||||
Search: searchmodule.New(store),
|
||||
|
||||
@ -47,9 +47,6 @@ wallet_service:
|
||||
room_service:
|
||||
addr: "10.2.1.16:13001"
|
||||
request_timeout: "3s"
|
||||
robot_service:
|
||||
addr: "10.2.1.16:13011"
|
||||
request_timeout: "3s"
|
||||
activity_service:
|
||||
addr: "10.2.1.16:13006"
|
||||
request_timeout: "3s"
|
||||
|
||||
@ -46,9 +46,6 @@ wallet_service:
|
||||
room_service:
|
||||
addr: "127.0.0.1:13001"
|
||||
request_timeout: "3s"
|
||||
robot_service:
|
||||
addr: "127.0.0.1:13011"
|
||||
request_timeout: "3s"
|
||||
activity_service:
|
||||
addr: "127.0.0.1:13006"
|
||||
request_timeout: "3s"
|
||||
|
||||
@ -38,7 +38,6 @@ type Config struct {
|
||||
Jobs JobsConfig `yaml:"jobs"`
|
||||
WalletService WalletServiceConfig `yaml:"wallet_service"`
|
||||
RoomService RoomServiceConfig `yaml:"room_service"`
|
||||
RobotService RobotServiceConfig `yaml:"robot_service"`
|
||||
ActivityService ActivityServiceConfig `yaml:"activity_service"`
|
||||
GameService GameServiceConfig `yaml:"game_service"`
|
||||
StatisticsService StatisticsServiceConfig `yaml:"statistics_service"`
|
||||
@ -65,11 +64,6 @@ type RoomServiceConfig struct {
|
||||
RequestTimeout time.Duration `yaml:"request_timeout"`
|
||||
}
|
||||
|
||||
type RobotServiceConfig struct {
|
||||
Addr string `yaml:"addr"`
|
||||
RequestTimeout time.Duration `yaml:"request_timeout"`
|
||||
}
|
||||
|
||||
type ActivityServiceConfig struct {
|
||||
Addr string `yaml:"addr"`
|
||||
RequestTimeout time.Duration `yaml:"request_timeout"`
|
||||
@ -174,10 +168,6 @@ func Default() Config {
|
||||
Addr: "127.0.0.1:13001",
|
||||
RequestTimeout: 3 * time.Second,
|
||||
},
|
||||
RobotService: RobotServiceConfig{
|
||||
Addr: "127.0.0.1:13011",
|
||||
RequestTimeout: 3 * time.Second,
|
||||
},
|
||||
ActivityService: ActivityServiceConfig{
|
||||
Addr: "127.0.0.1:13006",
|
||||
RequestTimeout: 3 * time.Second,
|
||||
@ -291,13 +281,6 @@ func (cfg *Config) Normalize() {
|
||||
if cfg.RoomService.RequestTimeout <= 0 {
|
||||
cfg.RoomService.RequestTimeout = 3 * time.Second
|
||||
}
|
||||
cfg.RobotService.Addr = strings.TrimSpace(cfg.RobotService.Addr)
|
||||
if cfg.RobotService.Addr == "" {
|
||||
cfg.RobotService.Addr = "127.0.0.1:13011"
|
||||
}
|
||||
if cfg.RobotService.RequestTimeout <= 0 {
|
||||
cfg.RobotService.RequestTimeout = 3 * time.Second
|
||||
}
|
||||
cfg.ActivityService.Addr = strings.TrimSpace(cfg.ActivityService.Addr)
|
||||
if cfg.ActivityService.RequestTimeout <= 0 {
|
||||
cfg.ActivityService.RequestTimeout = 3 * time.Second
|
||||
@ -411,12 +394,6 @@ func (cfg Config) Validate() error {
|
||||
if cfg.RoomService.RequestTimeout <= 0 {
|
||||
return errors.New("room_service.request_timeout must be greater than 0")
|
||||
}
|
||||
if strings.TrimSpace(cfg.RobotService.Addr) == "" {
|
||||
return errors.New("robot_service.addr is required")
|
||||
}
|
||||
if cfg.RobotService.RequestTimeout <= 0 {
|
||||
return errors.New("robot_service.request_timeout must be greater than 0")
|
||||
}
|
||||
if strings.TrimSpace(cfg.ActivityService.Addr) == "" {
|
||||
return errors.New("activity_service.addr is required")
|
||||
}
|
||||
|
||||
@ -42,15 +42,6 @@ type Client interface {
|
||||
SetWeeklyStarCycleStatus(ctx context.Context, req *activityv1.SetWeeklyStarCycleStatusRequest) (*activityv1.SetWeeklyStarCycleStatusResponse, error)
|
||||
ListWeeklyStarLeaderboard(ctx context.Context, req *activityv1.ListWeeklyStarLeaderboardRequest) (*activityv1.ListWeeklyStarLeaderboardResponse, error)
|
||||
ListWeeklyStarSettlements(ctx context.Context, req *activityv1.ListWeeklyStarSettlementsRequest) (*activityv1.ListWeeklyStarSettlementsResponse, error)
|
||||
ListAgencyOpeningCycles(ctx context.Context, req *activityv1.ListAgencyOpeningCyclesRequest) (*activityv1.ListAgencyOpeningCyclesResponse, error)
|
||||
CreateAgencyOpeningCycle(ctx context.Context, req *activityv1.UpsertAgencyOpeningCycleRequest) (*activityv1.UpsertAgencyOpeningCycleResponse, error)
|
||||
GetAgencyOpeningCycle(ctx context.Context, req *activityv1.GetAgencyOpeningCycleRequest) (*activityv1.GetAgencyOpeningCycleResponse, error)
|
||||
UpdateAgencyOpeningCycle(ctx context.Context, req *activityv1.UpsertAgencyOpeningCycleRequest) (*activityv1.UpsertAgencyOpeningCycleResponse, error)
|
||||
SetAgencyOpeningCycleStatus(ctx context.Context, req *activityv1.SetAgencyOpeningCycleStatusRequest) (*activityv1.SetAgencyOpeningCycleStatusResponse, error)
|
||||
ListAgencyOpeningApplications(ctx context.Context, req *activityv1.ListAgencyOpeningApplicationsRequest) (*activityv1.ListAgencyOpeningApplicationsResponse, error)
|
||||
GetCPWeeklyRankConfig(ctx context.Context, req *activityv1.GetCPWeeklyRankConfigRequest) (*activityv1.GetCPWeeklyRankConfigResponse, error)
|
||||
UpdateCPWeeklyRankConfig(ctx context.Context, req *activityv1.UpdateCPWeeklyRankConfigRequest) (*activityv1.UpdateCPWeeklyRankConfigResponse, error)
|
||||
ListCPWeeklyRankSettlements(ctx context.Context, req *activityv1.ListCPWeeklyRankSettlementsRequest) (*activityv1.ListCPWeeklyRankSettlementsResponse, error)
|
||||
GetLuckyGiftConfig(ctx context.Context, req *activityv1.GetLuckyGiftConfigRequest) (*activityv1.GetLuckyGiftConfigResponse, error)
|
||||
UpsertLuckyGiftConfig(ctx context.Context, req *activityv1.UpsertLuckyGiftConfigRequest) (*activityv1.UpsertLuckyGiftConfigResponse, error)
|
||||
ListLuckyGiftConfigs(ctx context.Context, req *activityv1.ListLuckyGiftConfigsRequest) (*activityv1.ListLuckyGiftConfigsResponse, error)
|
||||
@ -61,8 +52,6 @@ type Client interface {
|
||||
UpsertLevelTrack(ctx context.Context, req *activityv1.UpsertLevelTrackRequest) (*activityv1.UpsertLevelTrackResponse, error)
|
||||
UpsertLevelRule(ctx context.Context, req *activityv1.UpsertLevelRuleRequest) (*activityv1.UpsertLevelRuleResponse, error)
|
||||
UpsertLevelTier(ctx context.Context, req *activityv1.UpsertLevelTierRequest) (*activityv1.UpsertLevelTierResponse, error)
|
||||
CreateInboxMessage(ctx context.Context, req *activityv1.CreateInboxMessageRequest) (*activityv1.CreateInboxMessageResponse, error)
|
||||
CreateFanoutJob(ctx context.Context, req *activityv1.CreateFanoutJobRequest) (*activityv1.CreateFanoutJobResponse, error)
|
||||
}
|
||||
|
||||
type GRPCClient struct {
|
||||
@ -75,12 +64,9 @@ type GRPCClient struct {
|
||||
sevenDayCheckInClient activityv1.AdminSevenDayCheckInServiceClient
|
||||
roomTurnoverRewardClient activityv1.AdminRoomTurnoverRewardServiceClient
|
||||
weeklyStarClient activityv1.AdminWeeklyStarServiceClient
|
||||
agencyOpeningClient activityv1.AdminAgencyOpeningServiceClient
|
||||
cpWeeklyRankClient activityv1.AdminCPWeeklyRankServiceClient
|
||||
luckyGiftClient activityv1.AdminLuckyGiftServiceClient
|
||||
broadcastClient activityv1.BroadcastServiceClient
|
||||
growthClient activityv1.AdminGrowthLevelServiceClient
|
||||
messageClient activityv1.MessageInboxServiceClient
|
||||
}
|
||||
|
||||
func NewGRPC(conn grpc.ClientConnInterface) *GRPCClient {
|
||||
@ -94,12 +80,9 @@ func NewGRPC(conn grpc.ClientConnInterface) *GRPCClient {
|
||||
sevenDayCheckInClient: activityv1.NewAdminSevenDayCheckInServiceClient(conn),
|
||||
roomTurnoverRewardClient: activityv1.NewAdminRoomTurnoverRewardServiceClient(conn),
|
||||
weeklyStarClient: activityv1.NewAdminWeeklyStarServiceClient(conn),
|
||||
agencyOpeningClient: activityv1.NewAdminAgencyOpeningServiceClient(conn),
|
||||
cpWeeklyRankClient: activityv1.NewAdminCPWeeklyRankServiceClient(conn),
|
||||
luckyGiftClient: activityv1.NewAdminLuckyGiftServiceClient(conn),
|
||||
broadcastClient: activityv1.NewBroadcastServiceClient(conn),
|
||||
growthClient: activityv1.NewAdminGrowthLevelServiceClient(conn),
|
||||
messageClient: activityv1.NewMessageInboxServiceClient(conn),
|
||||
}
|
||||
}
|
||||
|
||||
@ -231,42 +214,6 @@ func (c *GRPCClient) ListWeeklyStarSettlements(ctx context.Context, req *activit
|
||||
return c.weeklyStarClient.ListWeeklyStarSettlements(ctx, req)
|
||||
}
|
||||
|
||||
func (c *GRPCClient) ListAgencyOpeningCycles(ctx context.Context, req *activityv1.ListAgencyOpeningCyclesRequest) (*activityv1.ListAgencyOpeningCyclesResponse, error) {
|
||||
return c.agencyOpeningClient.ListAgencyOpeningCycles(ctx, req)
|
||||
}
|
||||
|
||||
func (c *GRPCClient) CreateAgencyOpeningCycle(ctx context.Context, req *activityv1.UpsertAgencyOpeningCycleRequest) (*activityv1.UpsertAgencyOpeningCycleResponse, error) {
|
||||
return c.agencyOpeningClient.CreateAgencyOpeningCycle(ctx, req)
|
||||
}
|
||||
|
||||
func (c *GRPCClient) GetAgencyOpeningCycle(ctx context.Context, req *activityv1.GetAgencyOpeningCycleRequest) (*activityv1.GetAgencyOpeningCycleResponse, error) {
|
||||
return c.agencyOpeningClient.GetAgencyOpeningCycle(ctx, req)
|
||||
}
|
||||
|
||||
func (c *GRPCClient) UpdateAgencyOpeningCycle(ctx context.Context, req *activityv1.UpsertAgencyOpeningCycleRequest) (*activityv1.UpsertAgencyOpeningCycleResponse, error) {
|
||||
return c.agencyOpeningClient.UpdateAgencyOpeningCycle(ctx, req)
|
||||
}
|
||||
|
||||
func (c *GRPCClient) SetAgencyOpeningCycleStatus(ctx context.Context, req *activityv1.SetAgencyOpeningCycleStatusRequest) (*activityv1.SetAgencyOpeningCycleStatusResponse, error) {
|
||||
return c.agencyOpeningClient.SetAgencyOpeningCycleStatus(ctx, req)
|
||||
}
|
||||
|
||||
func (c *GRPCClient) ListAgencyOpeningApplications(ctx context.Context, req *activityv1.ListAgencyOpeningApplicationsRequest) (*activityv1.ListAgencyOpeningApplicationsResponse, error) {
|
||||
return c.agencyOpeningClient.ListAgencyOpeningApplications(ctx, req)
|
||||
}
|
||||
|
||||
func (c *GRPCClient) GetCPWeeklyRankConfig(ctx context.Context, req *activityv1.GetCPWeeklyRankConfigRequest) (*activityv1.GetCPWeeklyRankConfigResponse, error) {
|
||||
return c.cpWeeklyRankClient.GetCPWeeklyRankConfig(ctx, req)
|
||||
}
|
||||
|
||||
func (c *GRPCClient) UpdateCPWeeklyRankConfig(ctx context.Context, req *activityv1.UpdateCPWeeklyRankConfigRequest) (*activityv1.UpdateCPWeeklyRankConfigResponse, error) {
|
||||
return c.cpWeeklyRankClient.UpdateCPWeeklyRankConfig(ctx, req)
|
||||
}
|
||||
|
||||
func (c *GRPCClient) ListCPWeeklyRankSettlements(ctx context.Context, req *activityv1.ListCPWeeklyRankSettlementsRequest) (*activityv1.ListCPWeeklyRankSettlementsResponse, error) {
|
||||
return c.cpWeeklyRankClient.ListCPWeeklyRankSettlements(ctx, req)
|
||||
}
|
||||
|
||||
func (c *GRPCClient) GetLuckyGiftConfig(ctx context.Context, req *activityv1.GetLuckyGiftConfigRequest) (*activityv1.GetLuckyGiftConfigResponse, error) {
|
||||
return c.luckyGiftClient.GetLuckyGiftConfig(ctx, req)
|
||||
}
|
||||
@ -306,11 +253,3 @@ func (c *GRPCClient) UpsertLevelRule(ctx context.Context, req *activityv1.Upsert
|
||||
func (c *GRPCClient) UpsertLevelTier(ctx context.Context, req *activityv1.UpsertLevelTierRequest) (*activityv1.UpsertLevelTierResponse, error) {
|
||||
return c.growthClient.UpsertLevelTier(ctx, req)
|
||||
}
|
||||
|
||||
func (c *GRPCClient) CreateInboxMessage(ctx context.Context, req *activityv1.CreateInboxMessageRequest) (*activityv1.CreateInboxMessageResponse, error) {
|
||||
return c.messageClient.CreateInboxMessage(ctx, req)
|
||||
}
|
||||
|
||||
func (c *GRPCClient) CreateFanoutJob(ctx context.Context, req *activityv1.CreateFanoutJobRequest) (*activityv1.CreateFanoutJobResponse, error) {
|
||||
return c.messageClient.CreateFanoutJob(ctx, req)
|
||||
}
|
||||
|
||||
@ -1,83 +0,0 @@
|
||||
package robotclient
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"hyapp-admin-server/internal/appctx"
|
||||
robotv1 "hyapp.local/api/proto/robot/v1"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
type Client interface {
|
||||
ListRoomRobots(ctx context.Context, req ListRoomRobotsRequest) (ListRoomRobotsResult, error)
|
||||
}
|
||||
|
||||
type Robot struct {
|
||||
UserID int64
|
||||
Status string
|
||||
RoomScene string
|
||||
CreatedAtMS int64
|
||||
UpdatedAtMS int64
|
||||
LastUsedAtMS int64
|
||||
UsedCount int64
|
||||
}
|
||||
|
||||
type ListRoomRobotsRequest struct {
|
||||
RoomScene string
|
||||
Status string
|
||||
PageSize int32
|
||||
Cursor string
|
||||
}
|
||||
|
||||
type ListRoomRobotsResult struct {
|
||||
Robots []Robot
|
||||
NextCursor string
|
||||
ServerTimeMS int64
|
||||
}
|
||||
|
||||
type GRPCClient struct {
|
||||
client robotv1.RoomRobotServiceClient
|
||||
}
|
||||
|
||||
func NewGRPC(conn grpc.ClientConnInterface) *GRPCClient {
|
||||
return &GRPCClient{client: robotv1.NewRoomRobotServiceClient(conn)}
|
||||
}
|
||||
|
||||
func (c *GRPCClient) ListRoomRobots(ctx context.Context, req ListRoomRobotsRequest) (ListRoomRobotsResult, error) {
|
||||
if c == nil || c.client == nil {
|
||||
return ListRoomRobotsResult{}, fmt.Errorf("robot service client is not configured")
|
||||
}
|
||||
resp, err := c.client.ListRoomRobots(ctx, &robotv1.ListRoomRobotsRequest{
|
||||
Meta: &robotv1.RequestMeta{
|
||||
RequestId: fmt.Sprintf("admin-room-robots-%d", time.Now().UnixMilli()),
|
||||
Caller: "admin-server",
|
||||
AppCode: appctx.FromContext(ctx),
|
||||
SentAtMs: time.Now().UnixMilli(),
|
||||
ActorUserId: 0,
|
||||
},
|
||||
RoomScene: strings.TrimSpace(req.RoomScene),
|
||||
Status: strings.TrimSpace(req.Status),
|
||||
PageSize: req.PageSize,
|
||||
Cursor: strings.TrimSpace(req.Cursor),
|
||||
})
|
||||
if err != nil {
|
||||
return ListRoomRobotsResult{}, err
|
||||
}
|
||||
items := make([]Robot, 0, len(resp.GetRobots()))
|
||||
for _, item := range resp.GetRobots() {
|
||||
items = append(items, Robot{
|
||||
UserID: item.GetUserId(),
|
||||
Status: item.GetStatus(),
|
||||
RoomScene: item.GetRoomScene(),
|
||||
CreatedAtMS: item.GetCreatedAtMs(),
|
||||
UpdatedAtMS: item.GetUpdatedAtMs(),
|
||||
LastUsedAtMS: item.GetLastUsedAtMs(),
|
||||
UsedCount: item.GetUsedCount(),
|
||||
})
|
||||
}
|
||||
return ListRoomRobotsResult{Robots: items, NextCursor: resp.GetNextCursor(), ServerTimeMS: resp.GetServerTimeMs()}, nil
|
||||
}
|
||||
@ -24,10 +24,6 @@ type Client interface {
|
||||
ListRoomPins(ctx context.Context, req ListRoomPinsRequest) (ListRoomPinsResult, error)
|
||||
CreateRoomPin(ctx context.Context, req CreateRoomPinRequest) (RoomPin, error)
|
||||
CancelRoomPin(ctx context.Context, req CancelRoomPinRequest) (RoomPin, error)
|
||||
ListRobotRooms(ctx context.Context, req ListRobotRoomsRequest) (ListRobotRoomsResult, error)
|
||||
CreateRobotRoom(ctx context.Context, req CreateRobotRoomRequest) (RobotRoom, error)
|
||||
SetRobotRoomStatus(ctx context.Context, req SetRobotRoomStatusRequest) (RobotRoom, error)
|
||||
FilterAvailableRoomRobots(ctx context.Context, userIDs []int64) (FilterAvailableRoomRobotsResult, error)
|
||||
CloseRoom(ctx context.Context, req CloseRoomRequest) (*CloseRoomResult, error)
|
||||
ReopenRoom(ctx context.Context, req ReopenRoomRequest) (*CloseRoomResult, error)
|
||||
}
|
||||
@ -218,66 +214,6 @@ type CancelRoomPinRequest struct {
|
||||
AdminID uint64
|
||||
}
|
||||
|
||||
type RobotRoomGiftRule struct {
|
||||
GiftIDs []string
|
||||
LuckyGiftIDs []string
|
||||
NormalGiftIntervalMS int64
|
||||
LuckyComboMin int64
|
||||
LuckyComboMax int64
|
||||
LuckyPauseMinMS int64
|
||||
LuckyPauseMaxMS int64
|
||||
}
|
||||
|
||||
type RobotRoom struct {
|
||||
AppCode string
|
||||
RoomID string
|
||||
RoomShortID string
|
||||
Title string
|
||||
CoverURL string
|
||||
VisibleRegionID int64
|
||||
Status string
|
||||
OwnerRobotUserID int64
|
||||
RobotUserIDs []int64
|
||||
GiftRule RobotRoomGiftRule
|
||||
CreatedByAdminID uint64
|
||||
CreatedAtMS int64
|
||||
UpdatedAtMS int64
|
||||
}
|
||||
|
||||
type ListRobotRoomsRequest struct {
|
||||
Page int
|
||||
PageSize int
|
||||
Status string
|
||||
}
|
||||
|
||||
type ListRobotRoomsResult struct {
|
||||
Rooms []RobotRoom
|
||||
Total int64
|
||||
}
|
||||
|
||||
type CreateRobotRoomRequest struct {
|
||||
OwnerRobotUserID int64
|
||||
CandidateRobotUserIDs []int64
|
||||
MinRobotCount int32
|
||||
MaxRobotCount int32
|
||||
RoomName string
|
||||
RoomAvatar string
|
||||
VisibleRegionID int64
|
||||
GiftRule RobotRoomGiftRule
|
||||
AdminID uint64
|
||||
}
|
||||
|
||||
type SetRobotRoomStatusRequest struct {
|
||||
RoomID string
|
||||
Status string
|
||||
AdminID uint64
|
||||
}
|
||||
|
||||
type FilterAvailableRoomRobotsResult struct {
|
||||
AvailableUserIDs []int64
|
||||
OccupiedUserIDs []int64
|
||||
}
|
||||
|
||||
type CloseRoomRequest struct {
|
||||
RequestID string
|
||||
RoomID string
|
||||
@ -464,68 +400,6 @@ func (c *GRPCClient) CancelRoomPin(ctx context.Context, req CancelRoomPinRequest
|
||||
return roomPinFromProto(resp.GetPin()), nil
|
||||
}
|
||||
|
||||
func (c *GRPCClient) ListRobotRooms(ctx context.Context, req ListRobotRoomsRequest) (ListRobotRoomsResult, error) {
|
||||
resp, err := c.queryClient.AdminListRobotRooms(ctx, &roomv1.AdminListRobotRoomsRequest{
|
||||
Meta: requestMeta(ctx, "", 0, "admin-list-robot-rooms"),
|
||||
Page: int32(req.Page),
|
||||
PageSize: int32(req.PageSize),
|
||||
Status: strings.TrimSpace(req.Status),
|
||||
})
|
||||
if err != nil {
|
||||
return ListRobotRoomsResult{}, err
|
||||
}
|
||||
items := make([]RobotRoom, 0, len(resp.GetRooms()))
|
||||
for _, item := range resp.GetRooms() {
|
||||
items = append(items, robotRoomFromProto(item))
|
||||
}
|
||||
return ListRobotRoomsResult{Rooms: items, Total: resp.GetTotal()}, nil
|
||||
}
|
||||
|
||||
func (c *GRPCClient) CreateRobotRoom(ctx context.Context, req CreateRobotRoomRequest) (RobotRoom, error) {
|
||||
resp, err := c.client.AdminCreateRobotRoom(ctx, &roomv1.AdminCreateRobotRoomRequest{
|
||||
Meta: requestMeta(ctx, "", int64(req.AdminID), "admin-create-robot-room"),
|
||||
OwnerRobotUserId: req.OwnerRobotUserID,
|
||||
CandidateRobotUserIds: append([]int64(nil), req.CandidateRobotUserIDs...),
|
||||
MinRobotCount: req.MinRobotCount,
|
||||
MaxRobotCount: req.MaxRobotCount,
|
||||
RoomName: strings.TrimSpace(req.RoomName),
|
||||
RoomAvatar: strings.TrimSpace(req.RoomAvatar),
|
||||
VisibleRegionId: req.VisibleRegionID,
|
||||
GiftRule: robotRoomGiftRuleToProto(req.GiftRule),
|
||||
AdminId: req.AdminID,
|
||||
})
|
||||
if err != nil {
|
||||
return RobotRoom{}, err
|
||||
}
|
||||
return robotRoomFromProto(resp.GetRoom()), nil
|
||||
}
|
||||
|
||||
func (c *GRPCClient) SetRobotRoomStatus(ctx context.Context, req SetRobotRoomStatusRequest) (RobotRoom, error) {
|
||||
resp, err := c.client.AdminSetRobotRoomStatus(ctx, &roomv1.AdminSetRobotRoomStatusRequest{
|
||||
Meta: requestMeta(ctx, strings.TrimSpace(req.RoomID), int64(req.AdminID), "admin-set-robot-room-status"),
|
||||
Status: strings.TrimSpace(req.Status),
|
||||
AdminId: req.AdminID,
|
||||
})
|
||||
if err != nil {
|
||||
return RobotRoom{}, err
|
||||
}
|
||||
return robotRoomFromProto(resp.GetRoom()), nil
|
||||
}
|
||||
|
||||
func (c *GRPCClient) FilterAvailableRoomRobots(ctx context.Context, userIDs []int64) (FilterAvailableRoomRobotsResult, error) {
|
||||
resp, err := c.queryClient.AdminFilterAvailableRoomRobots(ctx, &roomv1.AdminFilterAvailableRoomRobotsRequest{
|
||||
Meta: requestMeta(ctx, "", 0, "admin-filter-available-room-robots"),
|
||||
UserIds: append([]int64(nil), userIDs...),
|
||||
})
|
||||
if err != nil {
|
||||
return FilterAvailableRoomRobotsResult{}, err
|
||||
}
|
||||
return FilterAvailableRoomRobotsResult{
|
||||
AvailableUserIDs: append([]int64(nil), resp.GetAvailableUserIds()...),
|
||||
OccupiedUserIDs: append([]int64(nil), resp.GetOccupiedUserIds()...),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *GRPCClient) CloseRoom(ctx context.Context, req CloseRoomRequest) (*CloseRoomResult, error) {
|
||||
return c.setRoomLifecycle(ctx, req.RequestID, req.RoomID, req.ActorUserID, firstNonEmpty(req.Reason, "admin_closed"))
|
||||
}
|
||||
@ -686,54 +560,6 @@ func roomPinFromProto(input *roomv1.AdminRoomPin) RoomPin {
|
||||
}
|
||||
}
|
||||
|
||||
func robotRoomFromProto(input *roomv1.AdminRobotRoom) RobotRoom {
|
||||
if input == nil {
|
||||
return RobotRoom{}
|
||||
}
|
||||
return RobotRoom{
|
||||
AppCode: input.GetAppCode(),
|
||||
RoomID: input.GetRoomId(),
|
||||
RoomShortID: input.GetRoomShortId(),
|
||||
Title: input.GetTitle(),
|
||||
CoverURL: input.GetCoverUrl(),
|
||||
VisibleRegionID: input.GetVisibleRegionId(),
|
||||
Status: input.GetStatus(),
|
||||
OwnerRobotUserID: input.GetOwnerRobotUserId(),
|
||||
RobotUserIDs: append([]int64(nil), input.GetRobotUserIds()...),
|
||||
GiftRule: robotRoomGiftRuleFromProto(input.GetGiftRule()),
|
||||
CreatedByAdminID: input.GetCreatedByAdminId(),
|
||||
CreatedAtMS: input.GetCreatedAtMs(),
|
||||
UpdatedAtMS: input.GetUpdatedAtMs(),
|
||||
}
|
||||
}
|
||||
|
||||
func robotRoomGiftRuleFromProto(input *roomv1.AdminRobotRoomGiftRule) RobotRoomGiftRule {
|
||||
if input == nil {
|
||||
return RobotRoomGiftRule{}
|
||||
}
|
||||
return RobotRoomGiftRule{
|
||||
GiftIDs: append([]string(nil), input.GetGiftIds()...),
|
||||
LuckyGiftIDs: append([]string(nil), input.GetLuckyGiftIds()...),
|
||||
NormalGiftIntervalMS: input.GetNormalGiftIntervalMs(),
|
||||
LuckyComboMin: input.GetLuckyComboMin(),
|
||||
LuckyComboMax: input.GetLuckyComboMax(),
|
||||
LuckyPauseMinMS: input.GetLuckyPauseMinMs(),
|
||||
LuckyPauseMaxMS: input.GetLuckyPauseMaxMs(),
|
||||
}
|
||||
}
|
||||
|
||||
func robotRoomGiftRuleToProto(input RobotRoomGiftRule) *roomv1.AdminRobotRoomGiftRule {
|
||||
return &roomv1.AdminRobotRoomGiftRule{
|
||||
GiftIds: append([]string(nil), input.GiftIDs...),
|
||||
LuckyGiftIds: append([]string(nil), input.LuckyGiftIDs...),
|
||||
NormalGiftIntervalMs: input.NormalGiftIntervalMS,
|
||||
LuckyComboMin: input.LuckyComboMin,
|
||||
LuckyComboMax: input.LuckyComboMax,
|
||||
LuckyPauseMinMs: input.LuckyPauseMinMS,
|
||||
LuckyPauseMaxMs: input.LuckyPauseMaxMS,
|
||||
}
|
||||
}
|
||||
|
||||
func roomRocketConfigToProto(input RoomRocketConfig) *roomv1.AdminRoomRocketConfig {
|
||||
return &roomv1.AdminRoomRocketConfig{
|
||||
AppCode: input.AppCode,
|
||||
|
||||
@ -1,299 +0,0 @@
|
||||
package agencyopening
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"hyapp-admin-server/internal/appctx"
|
||||
"hyapp-admin-server/internal/integration/activityclient"
|
||||
"hyapp-admin-server/internal/middleware"
|
||||
"hyapp-admin-server/internal/modules/shared"
|
||||
"hyapp-admin-server/internal/response"
|
||||
activityv1 "hyapp.local/api/proto/activity/v1"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type Handler struct {
|
||||
activity activityclient.Client
|
||||
audit shared.OperationLogger
|
||||
}
|
||||
|
||||
func New(activity activityclient.Client, audit shared.OperationLogger) *Handler {
|
||||
return &Handler{activity: activity, audit: audit}
|
||||
}
|
||||
|
||||
type cycleRequest struct {
|
||||
CycleID string `json:"cycle_id"`
|
||||
Title string `json:"title"`
|
||||
Status string `json:"status"`
|
||||
StartMS int64 `json:"start_ms"`
|
||||
EndMS int64 `json:"end_ms"`
|
||||
MinHostCount int32 `json:"min_host_count"`
|
||||
MaxAgencyAgeDays int32 `json:"max_agency_age_days"`
|
||||
Rewards []rewardDTO `json:"rewards"`
|
||||
}
|
||||
|
||||
type statusRequest struct {
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
type cycleDTO struct {
|
||||
AppCode string `json:"app_code,omitempty"`
|
||||
CycleID string `json:"cycle_id"`
|
||||
ActivityCode string `json:"activity_code"`
|
||||
Title string `json:"title"`
|
||||
Status string `json:"status"`
|
||||
StartMS int64 `json:"start_ms"`
|
||||
EndMS int64 `json:"end_ms"`
|
||||
MinHostCount int32 `json:"min_host_count"`
|
||||
MaxAgencyAgeDays int32 `json:"max_agency_age_days"`
|
||||
Rewards []rewardDTO `json:"rewards"`
|
||||
CreatedByAdminID int64 `json:"created_by_admin_id"`
|
||||
UpdatedByAdminID int64 `json:"updated_by_admin_id"`
|
||||
SettledAtMS int64 `json:"settled_at_ms"`
|
||||
CreatedAtMS int64 `json:"created_at_ms"`
|
||||
UpdatedAtMS int64 `json:"updated_at_ms"`
|
||||
}
|
||||
|
||||
type rewardDTO struct {
|
||||
RankNo int32 `json:"rank_no"`
|
||||
TierNo int32 `json:"tier_no,omitempty"`
|
||||
ThresholdCoinSpent int64 `json:"threshold_coin_spent"`
|
||||
RewardCoinAmount int64 `json:"reward_coin_amount"`
|
||||
}
|
||||
|
||||
type applicationDTO struct {
|
||||
ApplicationID string `json:"application_id"`
|
||||
CycleID string `json:"cycle_id"`
|
||||
AgencyID int64 `json:"agency_id,string"`
|
||||
AgencyOwnerUserID int64 `json:"agency_owner_user_id,string"`
|
||||
AgencyName string `json:"agency_name"`
|
||||
HostCount int32 `json:"host_count"`
|
||||
AgencyCreatedAtMS int64 `json:"agency_created_at_ms"`
|
||||
Status string `json:"status"`
|
||||
ScoreCoinAmount int64 `json:"score_coin_amount"`
|
||||
RewardRankNo int32 `json:"reward_rank_no"`
|
||||
RewardThresholdCoin int64 `json:"reward_threshold_coin_spent"`
|
||||
RewardCoinAmount int64 `json:"reward_coin_amount"`
|
||||
WalletCommandID string `json:"wallet_command_id"`
|
||||
WalletTransactionID string `json:"wallet_transaction_id"`
|
||||
FailureReason string `json:"failure_reason"`
|
||||
AppliedAtMS int64 `json:"applied_at_ms"`
|
||||
GrantedAtMS int64 `json:"granted_at_ms"`
|
||||
UpdatedAtMS int64 `json:"updated_at_ms"`
|
||||
}
|
||||
|
||||
func (h *Handler) ListCycles(c *gin.Context) {
|
||||
options := shared.ListOptions(c)
|
||||
resp, err := h.activity.ListAgencyOpeningCycles(c.Request.Context(), &activityv1.ListAgencyOpeningCyclesRequest{
|
||||
Meta: h.meta(c),
|
||||
Status: strings.TrimSpace(options.Status),
|
||||
StartMs: parseInt64Query(c, "start_ms"),
|
||||
EndMs: parseInt64Query(c, "end_ms"),
|
||||
Page: int32(options.Page),
|
||||
PageSize: int32(options.PageSize),
|
||||
})
|
||||
if err != nil {
|
||||
response.ServerError(c, "获取代理开业周期失败")
|
||||
return
|
||||
}
|
||||
items := make([]cycleDTO, 0, len(resp.GetCycles()))
|
||||
for _, cycle := range resp.GetCycles() {
|
||||
items = append(items, cycleFromProto(cycle))
|
||||
}
|
||||
response.OK(c, response.Page{Items: items, Page: options.Page, PageSize: options.PageSize, Total: resp.GetTotal()})
|
||||
}
|
||||
|
||||
func (h *Handler) CreateCycle(c *gin.Context) {
|
||||
var req cycleRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "代理开业周期参数不正确")
|
||||
return
|
||||
}
|
||||
resp, err := h.activity.CreateAgencyOpeningCycle(c.Request.Context(), &activityv1.UpsertAgencyOpeningCycleRequest{
|
||||
Meta: h.meta(c),
|
||||
Cycle: req.toProto(),
|
||||
OperatorAdminId: int64(middleware.CurrentUserID(c)),
|
||||
})
|
||||
if err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
item := cycleFromProto(resp.GetCycle())
|
||||
shared.OperationLogWithResourceID(c, h.audit, "create-agency-opening-cycle", "agency_opening_cycles", item.CycleID, "success", "")
|
||||
response.OK(c, item)
|
||||
}
|
||||
|
||||
func (h *Handler) GetCycle(c *gin.Context) {
|
||||
cycleID := strings.TrimSpace(c.Param("cycle_id"))
|
||||
resp, err := h.activity.GetAgencyOpeningCycle(c.Request.Context(), &activityv1.GetAgencyOpeningCycleRequest{Meta: h.meta(c), CycleId: cycleID})
|
||||
if err != nil {
|
||||
response.ServerError(c, "获取代理开业周期详情失败")
|
||||
return
|
||||
}
|
||||
response.OK(c, cycleFromProto(resp.GetCycle()))
|
||||
}
|
||||
|
||||
func (h *Handler) UpdateCycle(c *gin.Context) {
|
||||
var req cycleRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "代理开业周期参数不正确")
|
||||
return
|
||||
}
|
||||
req.CycleID = strings.TrimSpace(c.Param("cycle_id"))
|
||||
resp, err := h.activity.UpdateAgencyOpeningCycle(c.Request.Context(), &activityv1.UpsertAgencyOpeningCycleRequest{
|
||||
Meta: h.meta(c),
|
||||
Cycle: req.toProto(),
|
||||
OperatorAdminId: int64(middleware.CurrentUserID(c)),
|
||||
})
|
||||
if err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
item := cycleFromProto(resp.GetCycle())
|
||||
shared.OperationLogWithResourceID(c, h.audit, "update-agency-opening-cycle", "agency_opening_cycles", item.CycleID, "success", "")
|
||||
response.OK(c, item)
|
||||
}
|
||||
|
||||
func (h *Handler) SetCycleStatus(c *gin.Context) {
|
||||
var req statusRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "代理开业周期状态参数不正确")
|
||||
return
|
||||
}
|
||||
cycleID := strings.TrimSpace(c.Param("cycle_id"))
|
||||
resp, err := h.activity.SetAgencyOpeningCycleStatus(c.Request.Context(), &activityv1.SetAgencyOpeningCycleStatusRequest{
|
||||
Meta: h.meta(c),
|
||||
CycleId: cycleID,
|
||||
Status: strings.TrimSpace(req.Status),
|
||||
OperatorAdminId: int64(middleware.CurrentUserID(c)),
|
||||
})
|
||||
if err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
item := cycleFromProto(resp.GetCycle())
|
||||
shared.OperationLogWithResourceID(c, h.audit, "set-agency-opening-cycle-status", "agency_opening_cycles", item.CycleID, "success", "")
|
||||
response.OK(c, item)
|
||||
}
|
||||
|
||||
func (h *Handler) ListApplications(c *gin.Context) {
|
||||
options := shared.ListOptions(c)
|
||||
resp, err := h.activity.ListAgencyOpeningApplications(c.Request.Context(), &activityv1.ListAgencyOpeningApplicationsRequest{
|
||||
Meta: h.meta(c),
|
||||
CycleId: strings.TrimSpace(c.Query("cycle_id")),
|
||||
Status: strings.TrimSpace(options.Status),
|
||||
AgencyId: parseInt64Query(c, "agency_id"),
|
||||
AgencyOwnerUserId: parseInt64Query(c, "agency_owner_user_id"),
|
||||
Page: int32(options.Page),
|
||||
PageSize: int32(options.PageSize),
|
||||
})
|
||||
if err != nil {
|
||||
response.ServerError(c, "获取代理开业申请记录失败")
|
||||
return
|
||||
}
|
||||
items := make([]applicationDTO, 0, len(resp.GetApplications()))
|
||||
for _, application := range resp.GetApplications() {
|
||||
items = append(items, applicationFromProto(application))
|
||||
}
|
||||
response.OK(c, response.Page{Items: items, Page: options.Page, PageSize: options.PageSize, Total: resp.GetTotal()})
|
||||
}
|
||||
|
||||
func (h *Handler) meta(c *gin.Context) *activityv1.RequestMeta {
|
||||
return &activityv1.RequestMeta{
|
||||
RequestId: middleware.CurrentRequestID(c),
|
||||
Caller: "admin-server",
|
||||
AppCode: appctx.FromContext(c.Request.Context()),
|
||||
SentAtMs: time.Now().UnixMilli(),
|
||||
}
|
||||
}
|
||||
|
||||
func (r cycleRequest) toProto() *activityv1.AgencyOpeningCycle {
|
||||
rewards := make([]*activityv1.AgencyOpeningReward, 0, len(r.Rewards))
|
||||
for _, reward := range r.Rewards {
|
||||
rankNo := reward.RankNo
|
||||
if rankNo <= 0 {
|
||||
rankNo = reward.TierNo
|
||||
}
|
||||
rewards = append(rewards, &activityv1.AgencyOpeningReward{
|
||||
RankNo: rankNo,
|
||||
ThresholdCoinSpent: reward.ThresholdCoinSpent,
|
||||
RewardCoinAmount: reward.RewardCoinAmount,
|
||||
})
|
||||
}
|
||||
return &activityv1.AgencyOpeningCycle{
|
||||
CycleId: strings.TrimSpace(r.CycleID),
|
||||
Title: strings.TrimSpace(r.Title),
|
||||
Status: strings.TrimSpace(r.Status),
|
||||
StartMs: r.StartMS,
|
||||
EndMs: r.EndMS,
|
||||
MinHostCount: r.MinHostCount,
|
||||
MaxAgencyAgeDays: r.MaxAgencyAgeDays,
|
||||
Rewards: rewards,
|
||||
}
|
||||
}
|
||||
|
||||
func cycleFromProto(cycle *activityv1.AgencyOpeningCycle) cycleDTO {
|
||||
if cycle == nil {
|
||||
return cycleDTO{Rewards: []rewardDTO{}}
|
||||
}
|
||||
rewards := make([]rewardDTO, 0, len(cycle.GetRewards()))
|
||||
for _, reward := range cycle.GetRewards() {
|
||||
rewards = append(rewards, rewardDTO{
|
||||
RankNo: reward.GetRankNo(),
|
||||
TierNo: reward.GetRankNo(),
|
||||
ThresholdCoinSpent: reward.GetThresholdCoinSpent(),
|
||||
RewardCoinAmount: reward.GetRewardCoinAmount(),
|
||||
})
|
||||
}
|
||||
return cycleDTO{
|
||||
AppCode: cycle.GetAppCode(),
|
||||
CycleID: cycle.GetCycleId(),
|
||||
ActivityCode: cycle.GetActivityCode(),
|
||||
Title: cycle.GetTitle(),
|
||||
Status: cycle.GetStatus(),
|
||||
StartMS: cycle.GetStartMs(),
|
||||
EndMS: cycle.GetEndMs(),
|
||||
MinHostCount: cycle.GetMinHostCount(),
|
||||
MaxAgencyAgeDays: cycle.GetMaxAgencyAgeDays(),
|
||||
Rewards: rewards,
|
||||
CreatedByAdminID: cycle.GetCreatedByAdminId(),
|
||||
UpdatedByAdminID: cycle.GetUpdatedByAdminId(),
|
||||
SettledAtMS: cycle.GetSettledAtMs(),
|
||||
CreatedAtMS: cycle.GetCreatedAtMs(),
|
||||
UpdatedAtMS: cycle.GetUpdatedAtMs(),
|
||||
}
|
||||
}
|
||||
|
||||
func applicationFromProto(item *activityv1.AgencyOpeningApplication) applicationDTO {
|
||||
if item == nil {
|
||||
return applicationDTO{}
|
||||
}
|
||||
return applicationDTO{
|
||||
ApplicationID: item.GetApplicationId(),
|
||||
CycleID: item.GetCycleId(),
|
||||
AgencyID: item.GetAgencyId(),
|
||||
AgencyOwnerUserID: item.GetAgencyOwnerUserId(),
|
||||
AgencyName: item.GetAgencyName(),
|
||||
HostCount: item.GetHostCount(),
|
||||
AgencyCreatedAtMS: item.GetAgencyCreatedAtMs(),
|
||||
Status: item.GetStatus(),
|
||||
ScoreCoinAmount: item.GetScoreCoinAmount(),
|
||||
RewardRankNo: item.GetRewardRankNo(),
|
||||
RewardThresholdCoin: item.GetRewardThresholdCoinSpent(),
|
||||
RewardCoinAmount: item.GetRewardCoinAmount(),
|
||||
WalletCommandID: item.GetWalletCommandId(),
|
||||
WalletTransactionID: item.GetWalletTransactionId(),
|
||||
FailureReason: item.GetFailureReason(),
|
||||
AppliedAtMS: item.GetAppliedAtMs(),
|
||||
GrantedAtMS: item.GetGrantedAtMs(),
|
||||
UpdatedAtMS: item.GetUpdatedAtMs(),
|
||||
}
|
||||
}
|
||||
|
||||
func parseInt64Query(c *gin.Context, key string) int64 {
|
||||
value, _ := strconv.ParseInt(strings.TrimSpace(c.Query(key)), 10, 64)
|
||||
return value
|
||||
}
|
||||
@ -1,19 +0,0 @@
|
||||
package agencyopening
|
||||
|
||||
import (
|
||||
"hyapp-admin-server/internal/middleware"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func RegisterRoutes(protected *gin.RouterGroup, h *Handler) {
|
||||
if h == nil {
|
||||
return
|
||||
}
|
||||
protected.GET("/admin/activity/agency-opening/cycles", middleware.RequirePermission("agency-opening:view"), h.ListCycles)
|
||||
protected.POST("/admin/activity/agency-opening/cycles", middleware.RequirePermission("agency-opening:create"), h.CreateCycle)
|
||||
protected.GET("/admin/activity/agency-opening/cycles/:cycle_id", middleware.RequirePermission("agency-opening:view"), h.GetCycle)
|
||||
protected.PUT("/admin/activity/agency-opening/cycles/:cycle_id", middleware.RequirePermission("agency-opening:update"), h.UpdateCycle)
|
||||
protected.PATCH("/admin/activity/agency-opening/cycles/:cycle_id/status", middleware.RequirePermission("agency-opening:update"), h.SetCycleStatus)
|
||||
protected.GET("/admin/activity/agency-opening/applications", middleware.RequirePermission("agency-opening:view"), h.ListApplications)
|
||||
}
|
||||
@ -5,8 +5,6 @@ import (
|
||||
"fmt"
|
||||
|
||||
"hyapp-admin-server/internal/appctx"
|
||||
"hyapp-admin-server/internal/integration/activityclient"
|
||||
"hyapp-admin-server/internal/middleware"
|
||||
"hyapp-admin-server/internal/modules/shared"
|
||||
"hyapp-admin-server/internal/response"
|
||||
|
||||
@ -19,11 +17,7 @@ type Handler struct {
|
||||
}
|
||||
|
||||
func New(userDB DB, audit shared.OperationLogger) *Handler {
|
||||
return NewWithActivity(userDB, nil, audit)
|
||||
}
|
||||
|
||||
func NewWithActivity(userDB DB, activity activityclient.Client, audit shared.OperationLogger) *Handler {
|
||||
return &Handler{service: NewService(userDB, activity), audit: audit}
|
||||
return &Handler{service: NewService(userDB), audit: audit}
|
||||
}
|
||||
|
||||
func (h *Handler) GetConfig(c *gin.Context) {
|
||||
@ -41,9 +35,6 @@ func (h *Handler) UpdateConfig(c *gin.Context) {
|
||||
response.BadRequest(c, "CP配置参数不正确")
|
||||
return
|
||||
}
|
||||
if req.WeeklyRank != nil {
|
||||
req.WeeklyRank.UpdatedByAdminID = int64(middleware.CurrentUserID(c))
|
||||
}
|
||||
config, err := h.service.UpdateConfig(c.Request.Context(), appctx.FromContext(c.Request.Context()), req)
|
||||
if err != nil {
|
||||
if errors.Is(err, ErrInvalidArgument) {
|
||||
|
||||
@ -8,9 +8,6 @@ import (
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"hyapp-admin-server/internal/integration/activityclient"
|
||||
activityv1 "hyapp.local/api/proto/activity/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -41,14 +38,12 @@ type DB interface {
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
db DB
|
||||
activity activityclient.Client
|
||||
db DB
|
||||
}
|
||||
|
||||
type configDTO struct {
|
||||
AppCode string `json:"appCode"`
|
||||
Relations []relationDTO `json:"relations"`
|
||||
WeeklyRank weeklyRankDTO `json:"weeklyRank"`
|
||||
ServerTimeMS int64 `json:"serverTimeMs"`
|
||||
}
|
||||
|
||||
@ -72,28 +67,12 @@ type levelDTO struct {
|
||||
UpdatedAtMS int64 `json:"updatedAtMs"`
|
||||
}
|
||||
|
||||
type weeklyRankDTO struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
RelationType string `json:"relationType"`
|
||||
TopCount int32 `json:"topCount"`
|
||||
Rewards []weeklyRankRewardDTO `json:"rewards"`
|
||||
UpdatedByAdminID int64 `json:"updatedByAdminId"`
|
||||
CreatedAtMS int64 `json:"createdAtMs"`
|
||||
UpdatedAtMS int64 `json:"updatedAtMs"`
|
||||
}
|
||||
|
||||
type weeklyRankRewardDTO struct {
|
||||
RankNo int32 `json:"rankNo"`
|
||||
ResourceGroupID int64 `json:"resourceGroupId"`
|
||||
}
|
||||
|
||||
type updateRequest struct {
|
||||
Relations []relationDTO `json:"relations"`
|
||||
WeeklyRank *weeklyRankDTO `json:"weeklyRank"`
|
||||
Relations []relationDTO `json:"relations"`
|
||||
}
|
||||
|
||||
func NewService(db DB, activity activityclient.Client) *Service {
|
||||
return &Service{db: db, activity: activity}
|
||||
func NewService(db DB) *Service {
|
||||
return &Service{db: db}
|
||||
}
|
||||
|
||||
// GetConfig 读取当前 App 的关系容量和等级阈值;缺配置时先落默认值,保证后台首次打开就是可保存状态。
|
||||
@ -109,11 +88,7 @@ func (s *Service) GetConfig(ctx context.Context, appCode string) (configDTO, err
|
||||
if err != nil {
|
||||
return configDTO{}, err
|
||||
}
|
||||
weeklyRank, err := s.getWeeklyRankConfig(ctx, appCode)
|
||||
if err != nil {
|
||||
return configDTO{}, err
|
||||
}
|
||||
return configDTO{AppCode: appCode, Relations: relations, WeeklyRank: weeklyRank, ServerTimeMS: nowMs}, nil
|
||||
return configDTO{AppCode: appCode, Relations: relations, ServerTimeMS: nowMs}, nil
|
||||
}
|
||||
|
||||
// UpdateConfig 覆盖低频配置;写入时按 relation_type + level_no upsert,运行中消费逻辑每次读取最新阈值。
|
||||
@ -171,80 +146,9 @@ func (s *Service) UpdateConfig(ctx context.Context, appCode string, req updateRe
|
||||
if err := tx.Commit(); err != nil {
|
||||
return configDTO{}, err
|
||||
}
|
||||
if req.WeeklyRank != nil {
|
||||
// CP 关系配置归 user DB,周榜启停和发奖事实归 activity-service;这里在 user 配置提交后再写 activity,
|
||||
// 失败时返回错误让后台明确重试,避免误以为奖励配置已经生效。
|
||||
if err := s.updateWeeklyRankConfig(ctx, appCode, *req.WeeklyRank); err != nil {
|
||||
return configDTO{}, err
|
||||
}
|
||||
}
|
||||
return s.GetConfig(ctx, appCode)
|
||||
}
|
||||
|
||||
func (s *Service) getWeeklyRankConfig(ctx context.Context, appCode string) (weeklyRankDTO, error) {
|
||||
if s.activity == nil {
|
||||
return weeklyRankDTO{Enabled: true, RelationType: relationTypeCP, TopCount: 3}, nil
|
||||
}
|
||||
resp, err := s.activity.GetCPWeeklyRankConfig(ctx, &activityv1.GetCPWeeklyRankConfigRequest{Meta: s.activityMeta(appCode)})
|
||||
if err != nil {
|
||||
return weeklyRankDTO{}, err
|
||||
}
|
||||
return weeklyRankFromProto(resp.GetConfig()), nil
|
||||
}
|
||||
|
||||
func (s *Service) updateWeeklyRankConfig(ctx context.Context, appCode string, req weeklyRankDTO) error {
|
||||
if s.activity == nil {
|
||||
return errors.New("activity client is not configured")
|
||||
}
|
||||
_, err := s.activity.UpdateCPWeeklyRankConfig(ctx, &activityv1.UpdateCPWeeklyRankConfigRequest{
|
||||
Meta: s.activityMeta(appCode),
|
||||
Config: weeklyRankToProto(req),
|
||||
OperatorAdminId: req.UpdatedByAdminID,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Service) activityMeta(appCode string) *activityv1.RequestMeta {
|
||||
return &activityv1.RequestMeta{
|
||||
RequestId: "admin-cp-config",
|
||||
Caller: "admin-server",
|
||||
AppCode: appCode,
|
||||
SentAtMs: time.Now().UTC().UnixMilli(),
|
||||
}
|
||||
}
|
||||
|
||||
func weeklyRankFromProto(item *activityv1.CPWeeklyRankConfig) weeklyRankDTO {
|
||||
if item == nil {
|
||||
return weeklyRankDTO{Enabled: true, RelationType: relationTypeCP, TopCount: 3}
|
||||
}
|
||||
rewards := make([]weeklyRankRewardDTO, 0, len(item.GetRewards()))
|
||||
for _, reward := range item.GetRewards() {
|
||||
rewards = append(rewards, weeklyRankRewardDTO{RankNo: reward.GetRankNo(), ResourceGroupID: reward.GetResourceGroupId()})
|
||||
}
|
||||
return weeklyRankDTO{
|
||||
Enabled: item.GetEnabled(),
|
||||
RelationType: item.GetRelationType(),
|
||||
TopCount: item.GetTopCount(),
|
||||
Rewards: rewards,
|
||||
UpdatedByAdminID: item.GetUpdatedByAdminId(),
|
||||
CreatedAtMS: item.GetCreatedAtMs(),
|
||||
UpdatedAtMS: item.GetUpdatedAtMs(),
|
||||
}
|
||||
}
|
||||
|
||||
func weeklyRankToProto(item weeklyRankDTO) *activityv1.CPWeeklyRankConfig {
|
||||
rewards := make([]*activityv1.CPWeeklyRankReward, 0, len(item.Rewards))
|
||||
for _, reward := range item.Rewards {
|
||||
rewards = append(rewards, &activityv1.CPWeeklyRankReward{RankNo: reward.RankNo, ResourceGroupId: reward.ResourceGroupID})
|
||||
}
|
||||
return &activityv1.CPWeeklyRankConfig{
|
||||
Enabled: item.Enabled,
|
||||
RelationType: relationTypeCP,
|
||||
TopCount: item.TopCount,
|
||||
Rewards: rewards,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) listRelations(ctx context.Context, appCode string) ([]relationDTO, error) {
|
||||
rows, err := s.db.QueryContext(ctx, `
|
||||
SELECT relation_type, max_count_per_user, application_expire_hours, breakup_cost_coins, status, updated_at_ms
|
||||
|
||||
@ -1,301 +0,0 @@
|
||||
package fullservernotice
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"hyapp-admin-server/internal/appctx"
|
||||
"hyapp-admin-server/internal/integration/activityclient"
|
||||
"hyapp-admin-server/internal/middleware"
|
||||
"hyapp-admin-server/internal/modules/shared"
|
||||
"hyapp-admin-server/internal/response"
|
||||
activityv1 "hyapp.local/api/proto/activity/v1"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
const (
|
||||
messageTypeSystem = "system"
|
||||
messageTypeActivity = "activity"
|
||||
|
||||
targetScopeSingleUser = "single_user"
|
||||
targetScopeUserIDs = "user_ids"
|
||||
targetScopeRegion = "region"
|
||||
targetScopeCountry = "country"
|
||||
targetScopeAllActiveUsers = "all_active_users"
|
||||
|
||||
defaultProducerEventType = "admin_full_server_notice"
|
||||
defaultBatchSize = 500
|
||||
)
|
||||
|
||||
type Handler struct {
|
||||
activity activityclient.Client
|
||||
requestTimeout time.Duration
|
||||
audit shared.OperationLogger
|
||||
}
|
||||
|
||||
func New(activity activityclient.Client, audit shared.OperationLogger) *Handler {
|
||||
return &Handler{activity: activity, requestTimeout: 3 * time.Second, audit: audit}
|
||||
}
|
||||
|
||||
type fanoutRequest struct {
|
||||
CommandID string `json:"command_id"`
|
||||
MessageType string `json:"message_type"`
|
||||
TargetScope string `json:"target_scope"`
|
||||
TargetUserID int64 `json:"target_user_id"`
|
||||
UserIDs []int64 `json:"user_ids"`
|
||||
RegionID int64 `json:"region_id"`
|
||||
Country string `json:"country"`
|
||||
ProducerEventType string `json:"producer_event_type"`
|
||||
AggregateType string `json:"aggregate_type"`
|
||||
AggregateID string `json:"aggregate_id"`
|
||||
TemplateID string `json:"template_id"`
|
||||
TemplateVersion string `json:"template_version"`
|
||||
Title string `json:"title"`
|
||||
Summary string `json:"summary"`
|
||||
Body string `json:"body"`
|
||||
IconURL string `json:"icon_url"`
|
||||
ImageURL string `json:"image_url"`
|
||||
ActionType string `json:"action_type"`
|
||||
ActionParam string `json:"action_param"`
|
||||
Priority int32 `json:"priority"`
|
||||
SentAtMS int64 `json:"sent_at_ms"`
|
||||
ExpireAtMS int64 `json:"expire_at_ms"`
|
||||
MetadataJSON string `json:"metadata_json"`
|
||||
BatchSize int32 `json:"batch_size"`
|
||||
}
|
||||
|
||||
type fanoutDTO struct {
|
||||
JobID string `json:"job_id"`
|
||||
Status string `json:"status"`
|
||||
Created bool `json:"created"`
|
||||
CommandID string `json:"command_id"`
|
||||
MessageType string `json:"message_type"`
|
||||
TargetScope string `json:"target_scope"`
|
||||
}
|
||||
|
||||
func (h *Handler) CreateFanout(c *gin.Context) {
|
||||
var req fanoutRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "全服通知参数不正确")
|
||||
return
|
||||
}
|
||||
normalized, err := normalizeFanoutRequest(req, int64(middleware.CurrentUserID(c)))
|
||||
if err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
targetFilterJSON, err := buildTargetFilterJSON(normalized)
|
||||
if err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
templateSnapshotJSON, err := buildTemplateSnapshotJSON(normalized)
|
||||
if err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
ctx, cancel := h.activityContext(c)
|
||||
defer cancel()
|
||||
// 全服通知只向 activity-service 创建 fanout 命令;真正逐用户写入由 message fanout worker 执行。
|
||||
// command_id 作为后台操作幂等键,重复提交同一 payload 会返回同一个 job,不会重复刷用户收件箱。
|
||||
resp, err := h.activity.CreateFanoutJob(ctx, &activityv1.CreateFanoutJobRequest{
|
||||
Meta: h.meta(c),
|
||||
CommandId: normalized.CommandID,
|
||||
MessageType: normalized.MessageType,
|
||||
TargetScope: normalized.TargetScope,
|
||||
TargetFilterJson: targetFilterJSON,
|
||||
TemplateSnapshotJson: templateSnapshotJSON,
|
||||
BatchSize: normalized.BatchSize,
|
||||
CreatedBy: "admin:" + strconv.FormatInt(int64(middleware.CurrentUserID(c)), 10),
|
||||
})
|
||||
if err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
item := fanoutDTO{
|
||||
JobID: resp.GetJobId(),
|
||||
Status: resp.GetStatus(),
|
||||
Created: resp.GetCreated(),
|
||||
CommandID: normalized.CommandID,
|
||||
MessageType: normalized.MessageType,
|
||||
TargetScope: normalized.TargetScope,
|
||||
}
|
||||
shared.OperationLogWithResourceID(c, h.audit, "create-full-server-notice-fanout", "message_fanout_jobs", item.JobID, "success", normalized.Title)
|
||||
response.Created(c, item)
|
||||
}
|
||||
|
||||
func (h *Handler) meta(c *gin.Context) *activityv1.RequestMeta {
|
||||
return &activityv1.RequestMeta{
|
||||
RequestId: middleware.CurrentRequestID(c),
|
||||
Caller: "admin-server",
|
||||
AppCode: appctx.FromContext(c.Request.Context()),
|
||||
SentAtMs: time.Now().UTC().UnixMilli(),
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handler) activityContext(c *gin.Context) (context.Context, context.CancelFunc) {
|
||||
timeout := h.requestTimeout
|
||||
if timeout <= 0 {
|
||||
timeout = 3 * time.Second
|
||||
}
|
||||
return context.WithTimeout(c.Request.Context(), timeout)
|
||||
}
|
||||
|
||||
func normalizeFanoutRequest(req fanoutRequest, adminID int64) (fanoutRequest, error) {
|
||||
req.MessageType = strings.ToLower(strings.TrimSpace(req.MessageType))
|
||||
if req.MessageType == "" {
|
||||
req.MessageType = messageTypeSystem
|
||||
}
|
||||
if req.MessageType != messageTypeSystem && req.MessageType != messageTypeActivity {
|
||||
return fanoutRequest{}, fmt.Errorf("message_type 只支持 system 或 activity")
|
||||
}
|
||||
req.TargetScope = strings.TrimSpace(req.TargetScope)
|
||||
if req.TargetScope == "" {
|
||||
req.TargetScope = targetScopeAllActiveUsers
|
||||
}
|
||||
if !allowedTargetScope(req.TargetScope) {
|
||||
return fanoutRequest{}, fmt.Errorf("target_scope 不正确")
|
||||
}
|
||||
req.Title = strings.TrimSpace(req.Title)
|
||||
req.Summary = strings.TrimSpace(req.Summary)
|
||||
req.Body = strings.TrimSpace(req.Body)
|
||||
req.IconURL = strings.TrimSpace(req.IconURL)
|
||||
req.ImageURL = strings.TrimSpace(req.ImageURL)
|
||||
req.ActionType = strings.TrimSpace(req.ActionType)
|
||||
req.ActionParam = strings.TrimSpace(req.ActionParam)
|
||||
req.ProducerEventType = firstText(req.ProducerEventType, defaultProducerEventType)
|
||||
req.AggregateType = firstText(req.AggregateType, "admin_notice")
|
||||
req.AggregateID = strings.TrimSpace(req.AggregateID)
|
||||
req.TemplateID = strings.TrimSpace(req.TemplateID)
|
||||
req.TemplateVersion = strings.TrimSpace(req.TemplateVersion)
|
||||
req.Country = strings.TrimSpace(req.Country)
|
||||
req.MetadataJSON = strings.TrimSpace(req.MetadataJSON)
|
||||
if req.Title == "" || req.Summary == "" {
|
||||
return fanoutRequest{}, fmt.Errorf("title 和 summary 不能为空")
|
||||
}
|
||||
if req.ExpireAtMS > 0 && req.SentAtMS > 0 && req.ExpireAtMS <= req.SentAtMS {
|
||||
return fanoutRequest{}, fmt.Errorf("expire_at_ms 必须晚于 sent_at_ms")
|
||||
}
|
||||
if req.MetadataJSON != "" && !json.Valid([]byte(req.MetadataJSON)) {
|
||||
return fanoutRequest{}, fmt.Errorf("metadata_json 不是合法 JSON")
|
||||
}
|
||||
if req.BatchSize <= 0 {
|
||||
req.BatchSize = defaultBatchSize
|
||||
}
|
||||
if req.CommandID == "" {
|
||||
req.CommandID = fmt.Sprintf("admin_notice_%d_%d", adminID, time.Now().UTC().UnixMilli())
|
||||
}
|
||||
req.CommandID = strings.TrimSpace(req.CommandID)
|
||||
if req.AggregateID == "" {
|
||||
req.AggregateID = req.CommandID
|
||||
}
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func buildTargetFilterJSON(req fanoutRequest) (string, error) {
|
||||
var payload map[string]any
|
||||
switch req.TargetScope {
|
||||
case targetScopeAllActiveUsers:
|
||||
payload = map[string]any{}
|
||||
case targetScopeSingleUser:
|
||||
if req.TargetUserID <= 0 {
|
||||
return "", fmt.Errorf("target_user_id 不能为空")
|
||||
}
|
||||
payload = map[string]any{"target_user_id": req.TargetUserID}
|
||||
case targetScopeUserIDs:
|
||||
userIDs := positiveUniqueInt64s(req.UserIDs)
|
||||
if len(userIDs) == 0 {
|
||||
return "", fmt.Errorf("user_ids 不能为空")
|
||||
}
|
||||
payload = map[string]any{"user_ids": userIDs}
|
||||
case targetScopeRegion:
|
||||
if req.RegionID <= 0 {
|
||||
return "", fmt.Errorf("region_id 不能为空")
|
||||
}
|
||||
payload = map[string]any{"region_id": req.RegionID}
|
||||
case targetScopeCountry:
|
||||
if req.Country == "" {
|
||||
return "", fmt.Errorf("country 不能为空")
|
||||
}
|
||||
payload = map[string]any{"country": req.Country}
|
||||
default:
|
||||
return "", fmt.Errorf("target_scope 不正确")
|
||||
}
|
||||
encoded, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(encoded), nil
|
||||
}
|
||||
|
||||
func buildTemplateSnapshotJSON(req fanoutRequest) (string, error) {
|
||||
payload := map[string]any{
|
||||
"producer_event_type": req.ProducerEventType,
|
||||
"aggregate_type": req.AggregateType,
|
||||
"aggregate_id": req.AggregateID,
|
||||
"template_id": req.TemplateID,
|
||||
"template_version": req.TemplateVersion,
|
||||
"title": req.Title,
|
||||
"summary": req.Summary,
|
||||
"body": req.Body,
|
||||
"icon_url": req.IconURL,
|
||||
"image_url": req.ImageURL,
|
||||
"action_type": req.ActionType,
|
||||
"action_param": req.ActionParam,
|
||||
"priority": req.Priority,
|
||||
"sent_at_ms": req.SentAtMS,
|
||||
"expire_at_ms": req.ExpireAtMS,
|
||||
}
|
||||
if req.MetadataJSON != "" {
|
||||
var parsed any
|
||||
if err := json.Unmarshal([]byte(req.MetadataJSON), &parsed); err != nil {
|
||||
return "", fmt.Errorf("metadata_json 不是合法 JSON")
|
||||
}
|
||||
payload["metadata_json"] = parsed
|
||||
}
|
||||
encoded, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(encoded), nil
|
||||
}
|
||||
|
||||
func allowedTargetScope(scope string) bool {
|
||||
switch scope {
|
||||
case targetScopeSingleUser, targetScopeUserIDs, targetScopeRegion, targetScopeCountry, targetScopeAllActiveUsers:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func positiveUniqueInt64s(values []int64) []int64 {
|
||||
seen := make(map[int64]struct{}, len(values))
|
||||
out := make([]int64, 0, len(values))
|
||||
for _, value := range values {
|
||||
if value <= 0 {
|
||||
continue
|
||||
}
|
||||
if _, exists := seen[value]; exists {
|
||||
continue
|
||||
}
|
||||
seen[value] = struct{}{}
|
||||
out = append(out, value)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func firstText(values ...string) string {
|
||||
for _, value := range values {
|
||||
if trimmed := strings.TrimSpace(value); trimmed != "" {
|
||||
return trimmed
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@ -1,55 +0,0 @@
|
||||
package fullservernotice
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBuildFanoutJSONForUserIDScope(t *testing.T) {
|
||||
req, err := normalizeFanoutRequest(fanoutRequest{
|
||||
MessageType: "activity",
|
||||
TargetScope: "user_ids",
|
||||
UserIDs: []int64{42, 43, 42, 0},
|
||||
Title: "活动上线",
|
||||
Summary: "新活动已上线。",
|
||||
ActionType: "activity_detail",
|
||||
ActionParam: "activity_id=act_01",
|
||||
MetadataJSON: `{"source":"admin"}`,
|
||||
BatchSize: 500,
|
||||
}, 1)
|
||||
if err != nil {
|
||||
t.Fatalf("normalizeFanoutRequest failed: %v", err)
|
||||
}
|
||||
targetFilterJSON, err := buildTargetFilterJSON(req)
|
||||
if err != nil {
|
||||
t.Fatalf("buildTargetFilterJSON failed: %v", err)
|
||||
}
|
||||
if targetFilterJSON != `{"user_ids":[42,43]}` {
|
||||
t.Fatalf("target filter mismatch: %s", targetFilterJSON)
|
||||
}
|
||||
templateSnapshotJSON, err := buildTemplateSnapshotJSON(req)
|
||||
if err != nil {
|
||||
t.Fatalf("buildTemplateSnapshotJSON failed: %v", err)
|
||||
}
|
||||
for _, required := range []string{`"title":"活动上线"`, `"action_type":"activity_detail"`, `"metadata_json":{"source":"admin"}`} {
|
||||
if !strings.Contains(templateSnapshotJSON, required) {
|
||||
t.Fatalf("template snapshot %s missing %s", templateSnapshotJSON, required)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeFanoutRequestRequiresTargetParameters(t *testing.T) {
|
||||
req, err := normalizeFanoutRequest(fanoutRequest{
|
||||
MessageType: "system",
|
||||
TargetScope: "country",
|
||||
Title: "维护通知",
|
||||
Summary: "今晚维护。",
|
||||
BatchSize: 500,
|
||||
}, 1)
|
||||
if err != nil {
|
||||
t.Fatalf("normalizeFanoutRequest failed: %v", err)
|
||||
}
|
||||
if _, err := buildTargetFilterJSON(req); err == nil {
|
||||
t.Fatalf("country target without country should fail")
|
||||
}
|
||||
}
|
||||
@ -1,14 +0,0 @@
|
||||
package fullservernotice
|
||||
|
||||
import (
|
||||
"hyapp-admin-server/internal/middleware"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func RegisterRoutes(protected *gin.RouterGroup, h *Handler) {
|
||||
if h == nil {
|
||||
return
|
||||
}
|
||||
protected.POST("/admin/operations/full-server-notices/fanout", middleware.RequirePermission("full-server-notice:send"), h.CreateFanout)
|
||||
}
|
||||
@ -63,35 +63,6 @@ func (h *Handler) ListAgencies(c *gin.Context) {
|
||||
response.OK(c, response.Page{Items: items, Page: query.Page, PageSize: query.PageSize, Total: total})
|
||||
}
|
||||
|
||||
func (h *Handler) ListManagers(c *gin.Context) {
|
||||
query, ok := parseListQuery(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
items, total, err := h.service.ListManagers(c.Request.Context(), query)
|
||||
if err != nil {
|
||||
response.ServerError(c, "获取经理列表失败")
|
||||
return
|
||||
}
|
||||
response.OK(c, response.Page{Items: items, Page: query.Page, PageSize: query.PageSize, Total: total})
|
||||
}
|
||||
|
||||
func (h *Handler) CreateManager(c *gin.Context) {
|
||||
var req createManagerRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "经理参数不正确")
|
||||
return
|
||||
}
|
||||
item, err := h.service.CreateManager(c.Request.Context(), adminActorID(c), middleware.CurrentRequestID(c), req)
|
||||
if err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
writeHostOrgAuditLog(c, h.audit, "create-manager", "manager_profiles", item.UserID,
|
||||
fmt.Sprintf("user_id=%d contact=%q", item.UserID, strings.TrimSpace(req.Contact)))
|
||||
response.Created(c, item)
|
||||
}
|
||||
|
||||
func (h *Handler) ListHosts(c *gin.Context) {
|
||||
query, ok := parseListQuery(c)
|
||||
if !ok {
|
||||
|
||||
@ -5,7 +5,6 @@ import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@ -42,21 +41,6 @@ type CoinSellerListItem struct {
|
||||
UpdatedAtMs int64 `json:"updatedAtMs"`
|
||||
}
|
||||
|
||||
type ManagerListItem struct {
|
||||
UserID int64 `json:"userId,string"`
|
||||
DisplayUserID string `json:"displayUserId"`
|
||||
Username string `json:"username"`
|
||||
Avatar string `json:"avatar"`
|
||||
RegionID int64 `json:"regionId"`
|
||||
RegionName string `json:"regionName"`
|
||||
Status string `json:"status"`
|
||||
Contact string `json:"contact"`
|
||||
BDLeaderCount int64 `json:"bdLeaderCount"`
|
||||
LastInvitedAtMs int64 `json:"lastInvitedAtMs"`
|
||||
CreatedAtMs int64 `json:"createdAtMs"`
|
||||
UpdatedAtMs int64 `json:"updatedAtMs"`
|
||||
}
|
||||
|
||||
type CoinSellerSalaryRateTier struct {
|
||||
RegionID int64 `json:"regionId,string"`
|
||||
MinUSDMinor int64 `json:"minUsdMinor"`
|
||||
@ -573,118 +557,6 @@ func (r *Reader) DeleteBDLeader(ctx context.Context, userID int64) (*userclient.
|
||||
return item, nil
|
||||
}
|
||||
|
||||
// CreateManagerProfile 创建或恢复经理身份。
|
||||
// Manager 是 manager_profiles 表里的独立身份;这里只写经理事实和联系方式,不改 Host/Agency/BD 任一关系。
|
||||
func (r *Reader) CreateManagerProfile(ctx context.Context, userID int64, actorID int64, contact string) (*ManagerListItem, error) {
|
||||
if r == nil || r.db == nil {
|
||||
return nil, errUserDBNotConfigured()
|
||||
}
|
||||
if userID <= 0 {
|
||||
return nil, fmt.Errorf("target_user_id is required")
|
||||
}
|
||||
contact = strings.TrimSpace(contact)
|
||||
if len([]rune(contact)) > 128 {
|
||||
return nil, fmt.Errorf("contact is too long")
|
||||
}
|
||||
nowMs := time.Now().UnixMilli()
|
||||
appCode := appctx.FromContext(ctx)
|
||||
// ON DUPLICATE KEY 用于后台重复添加同一经理时刷新联系方式并恢复启用状态,
|
||||
// 不新增第二条身份,也不触碰该经理已经邀请出的 BD Leader/Admin 归属统计。
|
||||
if _, err := r.db.ExecContext(ctx, `
|
||||
INSERT INTO manager_profiles (
|
||||
app_code, user_id, status, contact_info, created_by_admin_id, created_at_ms, updated_at_ms
|
||||
) VALUES (?, ?, 'active', ?, ?, ?, ?)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
status = 'active',
|
||||
contact_info = VALUES(contact_info),
|
||||
updated_at_ms = VALUES(updated_at_ms)
|
||||
`, appCode, userID, contact, actorID, nowMs, nowMs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items, _, err := r.ListManagers(ctx, listQuery{Keyword: strconv.FormatInt(userID, 10), Page: 1, PageSize: 1})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(items) == 0 {
|
||||
return nil, fmt.Errorf("manager not found")
|
||||
}
|
||||
return items[0], nil
|
||||
}
|
||||
|
||||
// ListManagers 按 manager_profiles 的独立身份事实读取经理列表。
|
||||
// 经理是 BD Leader/Admin 的上级,不属于 Host/Agency/BD 链路;下级 BD Leader 只作为统计信息左连汇总。
|
||||
func (r *Reader) ListManagers(ctx context.Context, query listQuery) ([]*ManagerListItem, int64, error) {
|
||||
if r == nil || r.db == nil {
|
||||
return nil, 0, errUserDBNotConfigured()
|
||||
}
|
||||
appCode := appctx.FromContext(ctx)
|
||||
whereSQL := `
|
||||
FROM manager_profiles mp
|
||||
JOIN users manager ON manager.app_code = mp.app_code AND manager.user_id = mp.user_id
|
||||
LEFT JOIN bd_leader_profiles bl ON bl.app_code = mp.app_code AND bl.created_by_user_id = mp.user_id
|
||||
` + userCountryRegionJoin("manager", "manager_country_region", "manager_region") + `
|
||||
WHERE mp.app_code = ?`
|
||||
args := []any{appCode}
|
||||
if query.Status != "" {
|
||||
whereSQL += " AND mp.status = ?"
|
||||
args = append(args, query.Status)
|
||||
}
|
||||
if query.RegionID > 0 {
|
||||
whereSQL += " AND manager_region.region_id = ?"
|
||||
args = append(args, query.RegionID)
|
||||
}
|
||||
if keyword := strings.TrimSpace(query.Keyword); keyword != "" {
|
||||
like := "%" + keyword + "%"
|
||||
managerSQL, managerArgs := shared.UserIdentityKeywordSQL("manager", "mp.user_id", keyword, time.Now().UnixMilli())
|
||||
whereSQL += " AND (" + managerSQL + " OR manager_region.name LIKE ?)"
|
||||
args = append(args, managerArgs...)
|
||||
args = append(args, like)
|
||||
}
|
||||
|
||||
var total int64
|
||||
if err := r.db.QueryRowContext(ctx, `SELECT COUNT(DISTINCT mp.user_id) `+whereSQL, args...).Scan(&total); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
rows, err := r.db.QueryContext(ctx, fmt.Sprintf(`
|
||||
SELECT mp.user_id, COALESCE(manager.current_display_user_id, ''), COALESCE(manager.username, ''),
|
||||
COALESCE(manager.avatar, ''), `+userCountryRegionIDSQL("manager_region")+`, `+userCountryRegionNameSQL("manager_region")+`,
|
||||
mp.status, COALESCE(mp.contact_info, ''), COUNT(DISTINCT bl.user_id), COALESCE(MAX(bl.created_at_ms), 0),
|
||||
mp.created_at_ms, mp.updated_at_ms
|
||||
%s
|
||||
GROUP BY mp.user_id, manager.current_display_user_id, manager.username, manager.avatar,
|
||||
manager_region.region_id, manager_region.name, mp.status, mp.contact_info, mp.created_at_ms, mp.updated_at_ms
|
||||
ORDER BY mp.updated_at_ms DESC, mp.user_id DESC
|
||||
LIMIT ? OFFSET ?
|
||||
`, whereSQL), append(args, query.PageSize, offset(query.Page, query.PageSize))...)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
items := make([]*ManagerListItem, 0, query.PageSize)
|
||||
for rows.Next() {
|
||||
item := &ManagerListItem{}
|
||||
if err := rows.Scan(
|
||||
&item.UserID,
|
||||
&item.DisplayUserID,
|
||||
&item.Username,
|
||||
&item.Avatar,
|
||||
&item.RegionID,
|
||||
&item.RegionName,
|
||||
&item.Status,
|
||||
&item.Contact,
|
||||
&item.BDLeaderCount,
|
||||
&item.LastInvitedAtMs,
|
||||
&item.CreatedAtMs,
|
||||
&item.UpdatedAtMs,
|
||||
); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
items = append(items, item)
|
||||
}
|
||||
return items, total, rows.Err()
|
||||
}
|
||||
|
||||
// ListAgencies 只读 user-service 的 Agency 事实表;后台关闭和入会开关仍走写命令。
|
||||
func (r *Reader) ListAgencies(ctx context.Context, query listQuery) ([]*userclient.Agency, int64, error) {
|
||||
if r == nil || r.db == nil {
|
||||
|
||||
@ -60,11 +60,6 @@ type createBDLeaderRequest struct {
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
|
||||
type createManagerRequest struct {
|
||||
Contact string `json:"contact"`
|
||||
TargetUserID flexibleInt64 `json:"targetUserId" binding:"required"`
|
||||
}
|
||||
|
||||
type createBDRequest struct {
|
||||
CommandID string `json:"commandId" binding:"required"`
|
||||
TargetUserID flexibleInt64 `json:"targetUserId" binding:"required"`
|
||||
|
||||
@ -15,8 +15,6 @@ func RegisterRoutes(protected *gin.RouterGroup, h *Handler) {
|
||||
protected.GET("/admin/bds", middleware.RequirePermission("bd:view"), h.ListBDs)
|
||||
protected.POST("/admin/bds", middleware.RequirePermission("bd:create"), h.CreateBD)
|
||||
protected.PATCH("/admin/bds/:user_id/status", middleware.RequirePermission("bd:update"), h.SetBDStatus)
|
||||
protected.GET("/admin/managers", middleware.RequirePermission("bd:view"), h.ListManagers)
|
||||
protected.POST("/admin/managers", middleware.RequirePermission("bd:create"), h.CreateManager)
|
||||
protected.GET("/admin/agencies", middleware.RequirePermission("agency:view"), h.ListAgencies)
|
||||
protected.POST("/admin/agencies", middleware.RequirePermission("agency:create"), h.CreateAgency)
|
||||
protected.POST("/admin/agencies/:agency_id/close", middleware.RequirePermission("agency:status"), h.CloseAgency)
|
||||
|
||||
@ -50,20 +50,6 @@ func (s *Service) ListAgencies(ctx context.Context, query listQuery) ([]*usercli
|
||||
return s.reader.ListAgencies(ctx, query)
|
||||
}
|
||||
|
||||
func (s *Service) ListManagers(ctx context.Context, query listQuery) ([]*ManagerListItem, int64, error) {
|
||||
query = normalizeListQuery(query)
|
||||
return s.reader.ListManagers(ctx, query)
|
||||
}
|
||||
|
||||
func (s *Service) CreateManager(ctx context.Context, actorID int64, requestID string, req createManagerRequest) (*ManagerListItem, error) {
|
||||
targetUserID, err := s.resolveDisplayUserID(ctx, requestID, req.TargetUserID.Int64())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 经理身份只落 manager_profiles 表;联系方式作为后台运营资料保存,不参与经理中心授权。
|
||||
return s.reader.CreateManagerProfile(ctx, targetUserID, actorID, req.Contact)
|
||||
}
|
||||
|
||||
func (s *Service) ListHosts(ctx context.Context, query listQuery) ([]*userclient.HostProfile, int64, error) {
|
||||
query = normalizeListQuery(query)
|
||||
return s.reader.ListHostProfiles(ctx, query)
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package hostorg
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@ -18,38 +17,6 @@ func TestNormalizeListQueryAllowsComputedSorts(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestManagerListItemJSONHasNoAgencyOrParentBD 锁定后台经理列表的响应口径:经理是 BD Leader/Admin 的上级,不暴露 Agency 或上级 BD 字段。
|
||||
func TestManagerListItemJSONHasNoAgencyOrParentBD(t *testing.T) {
|
||||
item := ManagerListItem{
|
||||
UserID: 316033326332776448,
|
||||
DisplayUserID: "165549",
|
||||
Username: "Kitty",
|
||||
Status: "active",
|
||||
Contact: "+63",
|
||||
BDLeaderCount: 3,
|
||||
LastInvitedAtMs: 1720000000000,
|
||||
}
|
||||
payload, err := json.Marshal(item)
|
||||
if err != nil {
|
||||
t.Fatalf("marshal manager list item: %v", err)
|
||||
}
|
||||
body := string(payload)
|
||||
for _, want := range []string{
|
||||
`"userId":"316033326332776448"`,
|
||||
`"displayUserId":"165549"`,
|
||||
`"contact":"+63"`,
|
||||
`"bdLeaderCount":3`,
|
||||
`"lastInvitedAtMs":1720000000000`,
|
||||
} {
|
||||
if !strings.Contains(body, want) {
|
||||
t.Fatalf("manager json should contain %s, got %s", want, body)
|
||||
}
|
||||
}
|
||||
if strings.Contains(body, "parentBd") || strings.Contains(body, "ParentBD") || strings.Contains(body, "agency") || strings.Contains(body, "Agency") {
|
||||
t.Fatalf("manager json must not expose agency or parent bd fields: %s", body)
|
||||
}
|
||||
}
|
||||
|
||||
// TestBDRoleFromStatusPathUsesLeaderTableWithAPIPrefix 锁定线上真实 FullPath 带 /api/v1 前缀时,BD Leader 状态仍然写独立负责人表。
|
||||
func TestBDRoleFromStatusPathUsesLeaderTableWithAPIPrefix(t *testing.T) {
|
||||
role, target := bdRoleFromStatusPath("/api/v1/admin/bd-leaders/:user_id/status")
|
||||
|
||||
@ -1,274 +0,0 @@
|
||||
package payment
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
walletv1 "hyapp.local/api/proto/wallet/v1"
|
||||
)
|
||||
|
||||
type exchangeRateClient interface {
|
||||
LatestUSDRates(ctx context.Context, currencies []string) (exchangeRateResult, error)
|
||||
}
|
||||
|
||||
type exchangeRateResult struct {
|
||||
Rates map[string]string `json:"rates"`
|
||||
SourceNames []string `json:"sourceNames"`
|
||||
Sources []exchangeRateSourceStatusDTO `json:"sources"`
|
||||
}
|
||||
|
||||
type exchangeRateSourceStatusDTO struct {
|
||||
Name string `json:"name"`
|
||||
URL string `json:"url"`
|
||||
Success bool `json:"success"`
|
||||
Currencies []string `json:"currencies,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
type thirdPartyPaymentRateSyncDTO struct {
|
||||
UpdatedCount int `json:"updatedCount"`
|
||||
SkippedCount int `json:"skippedCount"`
|
||||
MissingCurrencies []string `json:"missingCurrencies"`
|
||||
SourceNames []string `json:"sourceNames"`
|
||||
Sources []exchangeRateSourceStatusDTO `json:"sources"`
|
||||
Rates map[string]string `json:"rates"`
|
||||
}
|
||||
|
||||
type httpExchangeRateClient struct {
|
||||
client *http.Client
|
||||
sources []exchangeRateSource
|
||||
}
|
||||
|
||||
type exchangeRateSource struct {
|
||||
name string
|
||||
url string
|
||||
parse func([]byte) (map[string]float64, error)
|
||||
}
|
||||
|
||||
func newDefaultExchangeRateClient() exchangeRateClient {
|
||||
return &httpExchangeRateClient{
|
||||
client: &http.Client{Timeout: 8 * time.Second},
|
||||
sources: []exchangeRateSource{
|
||||
{name: "open.er-api.com", url: "https://open.er-api.com/v6/latest/USD", parse: parseOpenExchangeRateAPI},
|
||||
{name: "fawazahmed0-currency-api", url: "https://cdn.jsdelivr.net/npm/@fawazahmed0/currency-api@latest/v1/currencies/usd.json", parse: parseFawazCurrencyAPI},
|
||||
{name: "FloatRates", url: "https://www.floatrates.com/daily/usd.json", parse: parseFloatRates},
|
||||
{name: "Frankfurter", url: "https://api.frankfurter.dev/v1/latest?base=USD", parse: parseFrankfurterRates},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *httpExchangeRateClient) LatestUSDRates(ctx context.Context, currencies []string) (exchangeRateResult, error) {
|
||||
required, rates := splitRequiredCurrencies(currencies)
|
||||
if len(required) == 0 {
|
||||
return exchangeRateResult{Rates: rates}, nil
|
||||
}
|
||||
sourceNames := make([]string, 0, len(c.sources))
|
||||
sourceStatuses := make([]exchangeRateSourceStatusDTO, 0, len(c.sources))
|
||||
for _, source := range c.sources {
|
||||
// 每个源独立失败,不阻断后续源;同一个币种只取最先成功源,避免不同源的更新时间混在同一个币种上来回抖动。
|
||||
sourceRates, err := c.fetchSource(ctx, source)
|
||||
if err != nil {
|
||||
sourceStatuses = append(sourceStatuses, exchangeRateSourceStatusDTO{Name: source.name, URL: source.url, Success: false, Error: err.Error()})
|
||||
continue
|
||||
}
|
||||
matched := make([]string, 0, len(required))
|
||||
for currency := range required {
|
||||
if _, exists := rates[currency]; exists {
|
||||
continue
|
||||
}
|
||||
value := sourceRates[currency]
|
||||
if value <= 0 {
|
||||
continue
|
||||
}
|
||||
rates[currency] = formatExchangeRate(value)
|
||||
matched = append(matched, currency)
|
||||
}
|
||||
sort.Strings(matched)
|
||||
if len(matched) > 0 {
|
||||
sourceNames = append(sourceNames, source.name)
|
||||
}
|
||||
sourceStatuses = append(sourceStatuses, exchangeRateSourceStatusDTO{Name: source.name, URL: source.url, Success: true, Currencies: matched})
|
||||
if len(rates)-usdRateCount(rates) == len(required) {
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(rates)-usdRateCount(rates) == 0 {
|
||||
return exchangeRateResult{}, errors.New("no exchange rate source returned requested currencies")
|
||||
}
|
||||
return exchangeRateResult{Rates: rates, SourceNames: sourceNames, Sources: sourceStatuses}, nil
|
||||
}
|
||||
|
||||
func (c *httpExchangeRateClient) fetchSource(ctx context.Context, source exchangeRateSource) (map[string]float64, error) {
|
||||
client := c.client
|
||||
if client == nil {
|
||||
client = &http.Client{Timeout: 8 * time.Second}
|
||||
}
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, source.url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("Accept", "application/json")
|
||||
req.Header.Set("User-Agent", "HYAppAdmin/1.0 exchange-rate-sync")
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
return nil, fmt.Errorf("http %d", resp.StatusCode)
|
||||
}
|
||||
body, err := io.ReadAll(io.LimitReader(resp.Body, 4<<20))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return source.parse(body)
|
||||
}
|
||||
|
||||
func parseOpenExchangeRateAPI(body []byte) (map[string]float64, error) {
|
||||
var payload struct {
|
||||
Result string `json:"result"`
|
||||
Rates map[string]float64 `json:"rates"`
|
||||
}
|
||||
if err := json.Unmarshal(body, &payload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if payload.Result != "" && payload.Result != "success" {
|
||||
return nil, fmt.Errorf("result %s", payload.Result)
|
||||
}
|
||||
return normalizeFloatRateMap(payload.Rates), nil
|
||||
}
|
||||
|
||||
func parseFrankfurterRates(body []byte) (map[string]float64, error) {
|
||||
var payload struct {
|
||||
Rates map[string]float64 `json:"rates"`
|
||||
}
|
||||
if err := json.Unmarshal(body, &payload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return normalizeFloatRateMap(payload.Rates), nil
|
||||
}
|
||||
|
||||
func parseFawazCurrencyAPI(body []byte) (map[string]float64, error) {
|
||||
var payload struct {
|
||||
Rates map[string]float64 `json:"usd"`
|
||||
}
|
||||
if err := json.Unmarshal(body, &payload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return normalizeFloatRateMap(payload.Rates), nil
|
||||
}
|
||||
|
||||
func parseFloatRates(body []byte) (map[string]float64, error) {
|
||||
var payload map[string]struct {
|
||||
Code string `json:"code"`
|
||||
AlphaCode string `json:"alphaCode"`
|
||||
Rate string `json:"rate"`
|
||||
}
|
||||
if err := json.Unmarshal(body, &payload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rates := make(map[string]float64, len(payload))
|
||||
for key, item := range payload {
|
||||
currency := strings.ToUpper(firstNonEmptyString(item.AlphaCode, item.Code, key))
|
||||
value, err := strconv.ParseFloat(strings.TrimSpace(item.Rate), 64)
|
||||
if currency == "" || err != nil || value <= 0 {
|
||||
continue
|
||||
}
|
||||
rates[currency] = value
|
||||
}
|
||||
return rates, nil
|
||||
}
|
||||
|
||||
func normalizeFloatRateMap(input map[string]float64) map[string]float64 {
|
||||
rates := make(map[string]float64, len(input))
|
||||
for currency, value := range input {
|
||||
currency = strings.ToUpper(strings.TrimSpace(currency))
|
||||
if currency == "" || value <= 0 {
|
||||
continue
|
||||
}
|
||||
rates[currency] = value
|
||||
}
|
||||
return rates
|
||||
}
|
||||
|
||||
func firstNonEmptyString(values ...string) string {
|
||||
for _, value := range values {
|
||||
if trimmed := strings.TrimSpace(value); trimmed != "" {
|
||||
return trimmed
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func collectThirdPartyPaymentMethods(channels []*walletv1.ThirdPartyPaymentChannel) []*walletv1.ThirdPartyPaymentMethod {
|
||||
seen := make(map[int64]struct{})
|
||||
methods := make([]*walletv1.ThirdPartyPaymentMethod, 0)
|
||||
for _, channel := range channels {
|
||||
for _, method := range channel.GetMethods() {
|
||||
if method.GetMethodId() <= 0 {
|
||||
continue
|
||||
}
|
||||
if _, exists := seen[method.GetMethodId()]; exists {
|
||||
continue
|
||||
}
|
||||
seen[method.GetMethodId()] = struct{}{}
|
||||
methods = append(methods, method)
|
||||
}
|
||||
}
|
||||
return methods
|
||||
}
|
||||
|
||||
func collectThirdPartyPaymentCurrencies(methods []*walletv1.ThirdPartyPaymentMethod) []string {
|
||||
currencies := make(map[string]struct{})
|
||||
for _, method := range methods {
|
||||
if currency := strings.ToUpper(strings.TrimSpace(method.GetCurrencyCode())); currency != "" {
|
||||
currencies[currency] = struct{}{}
|
||||
}
|
||||
}
|
||||
return sortedMapKeys(currencies)
|
||||
}
|
||||
|
||||
func splitRequiredCurrencies(currencies []string) (map[string]struct{}, map[string]string) {
|
||||
required := make(map[string]struct{})
|
||||
rates := make(map[string]string)
|
||||
for _, currency := range currencies {
|
||||
currency = strings.ToUpper(strings.TrimSpace(currency))
|
||||
if currency == "" {
|
||||
continue
|
||||
}
|
||||
if currency == "USD" {
|
||||
rates[currency] = "1.00000000"
|
||||
continue
|
||||
}
|
||||
required[currency] = struct{}{}
|
||||
}
|
||||
return required, rates
|
||||
}
|
||||
|
||||
func usdRateCount(rates map[string]string) int {
|
||||
if rates["USD"] == "" {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func sortedMapKeys(values map[string]struct{}) []string {
|
||||
keys := make([]string, 0, len(values))
|
||||
for key := range values {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
return keys
|
||||
}
|
||||
|
||||
func formatExchangeRate(value float64) string {
|
||||
return strconv.FormatFloat(value, 'f', 8, 64)
|
||||
}
|
||||
@ -23,15 +23,14 @@ import (
|
||||
const usdtMicroUnit int64 = 1_000_000
|
||||
|
||||
type Handler struct {
|
||||
wallet walletclient.Client
|
||||
userDB *sql.DB
|
||||
audit shared.OperationLogger
|
||||
exchangeRates exchangeRateClient
|
||||
wallet walletclient.Client
|
||||
userDB *sql.DB
|
||||
audit shared.OperationLogger
|
||||
}
|
||||
|
||||
// New 组装支付后台 handler;wallet 负责账单和商品事实,userDB 只补后台列表需要的用户展示资料。
|
||||
func New(wallet walletclient.Client, userDB *sql.DB, audit shared.OperationLogger) *Handler {
|
||||
return &Handler{wallet: wallet, userDB: userDB, audit: audit, exchangeRates: newDefaultExchangeRateClient()}
|
||||
return &Handler{wallet: wallet, userDB: userDB, audit: audit}
|
||||
}
|
||||
|
||||
func (h *Handler) ListRechargeBills(c *gin.Context) {
|
||||
@ -315,66 +314,6 @@ func (h *Handler) UpdateThirdPartyPaymentRate(c *gin.Context) {
|
||||
response.OK(c, item)
|
||||
}
|
||||
|
||||
func (h *Handler) SyncThirdPartyPaymentRates(c *gin.Context) {
|
||||
resp, err := h.wallet.ListThirdPartyPaymentChannels(c.Request.Context(), &walletv1.ListThirdPartyPaymentChannelsRequest{
|
||||
RequestId: middleware.CurrentRequestID(c),
|
||||
AppCode: appctx.FromContext(c.Request.Context()),
|
||||
ProviderCode: strings.TrimSpace(firstQuery(c, "provider_code", "providerCode")),
|
||||
IncludeDisabledMethods: true,
|
||||
})
|
||||
if err != nil {
|
||||
writeWalletError(c, err, "获取三方支付方式失败")
|
||||
return
|
||||
}
|
||||
methods := collectThirdPartyPaymentMethods(resp.GetChannels())
|
||||
currencies := collectThirdPartyPaymentCurrencies(methods)
|
||||
if len(currencies) == 0 {
|
||||
response.OK(c, thirdPartyPaymentRateSyncDTO{UpdatedCount: 0})
|
||||
return
|
||||
}
|
||||
// 汇率同步必须以服务端实时拉取结果为准:前端按钮只触发动作,不传任何汇率,避免浏览器篡改本币金额。
|
||||
rateClient := h.exchangeRates
|
||||
if rateClient == nil {
|
||||
rateClient = newDefaultExchangeRateClient()
|
||||
}
|
||||
rateResult, err := rateClient.LatestUSDRates(c.Request.Context(), currencies)
|
||||
if err != nil {
|
||||
response.ServerError(c, "同步支付汇率失败")
|
||||
return
|
||||
}
|
||||
updated := 0
|
||||
skipped := make(map[string]struct{})
|
||||
for _, method := range methods {
|
||||
currencyCode := strings.ToUpper(strings.TrimSpace(method.GetCurrencyCode()))
|
||||
rate := rateResult.Rates[currencyCode]
|
||||
if rate == "" {
|
||||
skipped[currencyCode] = struct{}{}
|
||||
continue
|
||||
}
|
||||
if _, err := h.wallet.UpdateThirdPartyPaymentRate(c.Request.Context(), &walletv1.UpdateThirdPartyPaymentRateRequest{
|
||||
RequestId: middleware.CurrentRequestID(c),
|
||||
AppCode: appctx.FromContext(c.Request.Context()),
|
||||
MethodId: method.GetMethodId(),
|
||||
UsdToCurrencyRate: rate,
|
||||
OperatorUserId: actorID(c),
|
||||
}); err != nil {
|
||||
writeWalletError(c, err, "写入支付汇率失败")
|
||||
return
|
||||
}
|
||||
updated++
|
||||
}
|
||||
skippedCurrencies := sortedMapKeys(skipped)
|
||||
shared.OperationLog(c, h.audit, "sync-third-party-payment-rates", "third_party_payment_methods", "success", strings.Join(rateResult.SourceNames, ","))
|
||||
response.OK(c, thirdPartyPaymentRateSyncDTO{
|
||||
UpdatedCount: updated,
|
||||
SkippedCount: len(skippedCurrencies),
|
||||
MissingCurrencies: skippedCurrencies,
|
||||
SourceNames: rateResult.SourceNames,
|
||||
Sources: rateResult.Sources,
|
||||
Rates: rateResult.Rates,
|
||||
})
|
||||
}
|
||||
|
||||
type rechargeProductRequest struct {
|
||||
AmountUSDT string `json:"amountUsdt"`
|
||||
AmountUSDTMicro int64 `json:"amountUsdtMicro"`
|
||||
|
||||
@ -108,58 +108,6 @@ func TestUpdateThirdPartyPaymentRateTrimsRateAndForwardsOperator(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSyncThirdPartyPaymentRatesUpdatesAllMethodsFromFetchedCurrencyRates(t *testing.T) {
|
||||
wallet := &mockPaymentWallet{thirdPartyChannelsResp: &walletv1.ListThirdPartyPaymentChannelsResponse{
|
||||
Channels: []*walletv1.ThirdPartyPaymentChannel{{
|
||||
AppCode: "lalu",
|
||||
ProviderCode: "mifapay",
|
||||
ProviderName: "MiFaPay",
|
||||
Status: "active",
|
||||
Methods: []*walletv1.ThirdPartyPaymentMethod{
|
||||
{MethodId: 810, CurrencyCode: "SAR", Status: "active"},
|
||||
{MethodId: 811, CurrencyCode: "BHD", Status: "disabled"},
|
||||
},
|
||||
}},
|
||||
}}
|
||||
handler := New(wallet, nil, nil)
|
||||
handler.exchangeRates = fakeExchangeRateClient{result: exchangeRateResult{
|
||||
Rates: map[string]string{"SAR": "3.75000000", "BHD": "0.37600000"},
|
||||
SourceNames: []string{"open.er-api.com"},
|
||||
}}
|
||||
router := newPaymentHandlerTestRouter(handler)
|
||||
request := httptest.NewRequest(http.MethodPost, "/admin/payment/third-party-rates/sync", nil)
|
||||
recorder := httptest.NewRecorder()
|
||||
|
||||
router.ServeHTTP(recorder, request)
|
||||
|
||||
if recorder.Code != http.StatusOK {
|
||||
t.Fatalf("status mismatch: got %d body=%s", recorder.Code, recorder.Body.String())
|
||||
}
|
||||
if wallet.lastThirdPartyChannels == nil || !wallet.lastThirdPartyChannels.GetIncludeDisabledMethods() {
|
||||
t.Fatalf("sync must read active and disabled methods: %+v", wallet.lastThirdPartyChannels)
|
||||
}
|
||||
if len(wallet.updateRateRequests) != 2 {
|
||||
t.Fatalf("sync should update every method with a fetched currency rate: %+v", wallet.updateRateRequests)
|
||||
}
|
||||
got := map[int64]string{}
|
||||
for _, req := range wallet.updateRateRequests {
|
||||
if req.GetOperatorUserId() != 7 {
|
||||
t.Fatalf("sync operator mismatch: %+v", req)
|
||||
}
|
||||
got[req.GetMethodId()] = req.GetUsdToCurrencyRate()
|
||||
}
|
||||
if got[810] != "3.75000000" || got[811] != "0.37600000" {
|
||||
t.Fatalf("synced rates mismatch: %+v", got)
|
||||
}
|
||||
var response adminPaymentTestResponse
|
||||
if err := json.NewDecoder(recorder.Body).Decode(&response); err != nil {
|
||||
t.Fatalf("decode response failed: %v", err)
|
||||
}
|
||||
if response.Code != 0 || response.Data["updatedCount"].(float64) != 2 {
|
||||
t.Fatalf("sync response mismatch: %+v", response)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateRechargeProductForwardsWebCoinSellerAudience(t *testing.T) {
|
||||
wallet := &mockPaymentWallet{}
|
||||
router := newPaymentHandlerTestRouter(New(wallet, nil, nil))
|
||||
@ -200,7 +148,6 @@ func newPaymentHandlerTestRouter(handler *Handler) *gin.Engine {
|
||||
router.GET("/admin/payment/third-party-channels", handler.ListThirdPartyPaymentChannels)
|
||||
router.PATCH("/admin/payment/third-party-methods/:method_id/status", handler.SetThirdPartyPaymentMethodStatus)
|
||||
router.PATCH("/admin/payment/third-party-rates/:method_id", handler.UpdateThirdPartyPaymentRate)
|
||||
router.POST("/admin/payment/third-party-rates/sync", handler.SyncThirdPartyPaymentRates)
|
||||
router.POST("/admin/payment/recharge-products", handler.CreateRechargeProduct)
|
||||
return router
|
||||
}
|
||||
@ -217,7 +164,6 @@ type mockPaymentWallet struct {
|
||||
thirdPartyChannelsResp *walletv1.ListThirdPartyPaymentChannelsResponse
|
||||
lastSetMethodStatus *walletv1.SetThirdPartyPaymentMethodStatusRequest
|
||||
lastUpdateRate *walletv1.UpdateThirdPartyPaymentRateRequest
|
||||
updateRateRequests []*walletv1.UpdateThirdPartyPaymentRateRequest
|
||||
lastCreateProduct *walletv1.CreateRechargeProductRequest
|
||||
}
|
||||
|
||||
@ -249,7 +195,6 @@ func (m *mockPaymentWallet) SetThirdPartyPaymentMethodStatus(_ context.Context,
|
||||
|
||||
func (m *mockPaymentWallet) UpdateThirdPartyPaymentRate(_ context.Context, req *walletv1.UpdateThirdPartyPaymentRateRequest) (*walletv1.ThirdPartyPaymentMethodResponse, error) {
|
||||
m.lastUpdateRate = req
|
||||
m.updateRateRequests = append(m.updateRateRequests, req)
|
||||
return &walletv1.ThirdPartyPaymentMethodResponse{Method: &walletv1.ThirdPartyPaymentMethod{
|
||||
MethodId: req.GetMethodId(),
|
||||
AppCode: req.GetAppCode(),
|
||||
@ -263,15 +208,6 @@ func (m *mockPaymentWallet) UpdateThirdPartyPaymentRate(_ context.Context, req *
|
||||
}}, nil
|
||||
}
|
||||
|
||||
type fakeExchangeRateClient struct {
|
||||
result exchangeRateResult
|
||||
err error
|
||||
}
|
||||
|
||||
func (f fakeExchangeRateClient) LatestUSDRates(context.Context, []string) (exchangeRateResult, error) {
|
||||
return f.result, f.err
|
||||
}
|
||||
|
||||
func (m *mockPaymentWallet) CreateRechargeProduct(_ context.Context, req *walletv1.CreateRechargeProductRequest) (*walletv1.RechargeProductResponse, error) {
|
||||
m.lastCreateProduct = req
|
||||
return &walletv1.RechargeProductResponse{Product: &walletv1.RechargeProduct{
|
||||
|
||||
@ -15,7 +15,6 @@ func RegisterRoutes(protected *gin.RouterGroup, h *Handler) {
|
||||
protected.GET("/admin/payment/third-party-channels", middleware.RequirePermission("payment-third-party:view"), h.ListThirdPartyPaymentChannels)
|
||||
protected.PATCH("/admin/payment/third-party-methods/:method_id/status", middleware.RequirePermission("payment-third-party:update"), h.SetThirdPartyPaymentMethodStatus)
|
||||
protected.PATCH("/admin/payment/third-party-rates/:method_id", middleware.RequirePermission("payment-third-party:update"), h.UpdateThirdPartyPaymentRate)
|
||||
protected.POST("/admin/payment/third-party-rates/sync", middleware.RequirePermission("payment-third-party:update"), h.SyncThirdPartyPaymentRates)
|
||||
protected.GET("/admin/payment/recharge-products", middleware.RequirePermission("payment-product:view"), h.ListRechargeProducts)
|
||||
protected.POST("/admin/payment/recharge-products", middleware.RequirePermission("payment-product:create"), h.CreateRechargeProduct)
|
||||
protected.PUT("/admin/payment/recharge-products/:product_id", middleware.RequirePermission("payment-product:update"), h.UpdateRechargeProduct)
|
||||
|
||||
@ -6,7 +6,6 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"hyapp-admin-server/internal/integration/robotclient"
|
||||
"hyapp-admin-server/internal/integration/roomclient"
|
||||
"hyapp-admin-server/internal/middleware"
|
||||
"hyapp-admin-server/internal/modules/shared"
|
||||
@ -20,8 +19,8 @@ type Handler struct {
|
||||
audit shared.OperationLogger
|
||||
}
|
||||
|
||||
func New(userDB *sql.DB, roomClient roomclient.Client, robotClient robotclient.Client, audit shared.OperationLogger) *Handler {
|
||||
return &Handler{service: NewService(userDB, roomClient, robotClient), audit: audit}
|
||||
func New(userDB *sql.DB, roomClient roomclient.Client, audit shared.OperationLogger) *Handler {
|
||||
return &Handler{service: NewService(userDB, roomClient), audit: audit}
|
||||
}
|
||||
|
||||
func (h *Handler) ListRooms(c *gin.Context) {
|
||||
@ -79,48 +78,6 @@ func (h *Handler) CancelRoomPin(c *gin.Context) {
|
||||
response.OK(c, pin)
|
||||
}
|
||||
|
||||
func (h *Handler) ListRobotRooms(c *gin.Context) {
|
||||
query := parseRobotRoomListQuery(c)
|
||||
items, total, err := h.service.ListRobotRooms(c.Request.Context(), query)
|
||||
if err != nil {
|
||||
response.ServerError(c, "获取机器人房间列表失败")
|
||||
return
|
||||
}
|
||||
response.OK(c, response.Page{Items: items, Page: query.Page, PageSize: query.PageSize, Total: total})
|
||||
}
|
||||
|
||||
func (h *Handler) ListAvailableRoomRobots(c *gin.Context) {
|
||||
items, err := h.service.ListAvailableRoomRobots(c.Request.Context())
|
||||
if err != nil {
|
||||
response.ServerError(c, "获取可用机器人失败")
|
||||
return
|
||||
}
|
||||
response.OK(c, items)
|
||||
}
|
||||
|
||||
func (h *Handler) CreateRobotRoom(c *gin.Context) {
|
||||
var req createRobotRoomRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "机器人房间参数不正确")
|
||||
return
|
||||
}
|
||||
room, err := h.service.CreateRobotRoom(c.Request.Context(), req, shared.ActorFromContext(c))
|
||||
if err != nil {
|
||||
writeMutationError(c, err, "创建机器人房间失败")
|
||||
return
|
||||
}
|
||||
writeRoomAuditLog(c, h.audit, "create-robot-room", "room_robot_rooms", room.RoomID, "success", "create robot room")
|
||||
response.Created(c, room)
|
||||
}
|
||||
|
||||
func (h *Handler) StartRobotRoom(c *gin.Context) {
|
||||
h.setRobotRoomStatus(c, "active", "start-robot-room", "启动机器人房间失败")
|
||||
}
|
||||
|
||||
func (h *Handler) StopRobotRoom(c *gin.Context) {
|
||||
h.setRobotRoomStatus(c, "stopped", "stop-robot-room", "停止机器人房间失败")
|
||||
}
|
||||
|
||||
func (h *Handler) GetRoomConfig(c *gin.Context) {
|
||||
config, err := h.service.GetRoomConfig(c.Request.Context())
|
||||
if err != nil {
|
||||
@ -177,20 +134,6 @@ func (h *Handler) DeleteRoom(c *gin.Context) {
|
||||
response.OK(c, gin.H{"deleted": true})
|
||||
}
|
||||
|
||||
func (h *Handler) setRobotRoomStatus(c *gin.Context, status string, action string, fallback string) {
|
||||
roomID, ok := parseRoomID(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
room, err := h.service.SetRobotRoomStatus(c.Request.Context(), roomID, status, shared.ActorFromContext(c))
|
||||
if err != nil {
|
||||
writeMutationError(c, err, fallback)
|
||||
return
|
||||
}
|
||||
writeRoomAuditLog(c, h.audit, action, "room_robot_rooms", roomID, "success", action)
|
||||
response.OK(c, room)
|
||||
}
|
||||
|
||||
func parseListQuery(c *gin.Context) (listQuery, bool) {
|
||||
options := shared.ListOptions(c)
|
||||
query := listQuery{
|
||||
@ -225,15 +168,6 @@ func parseRoomPinListQuery(c *gin.Context) (roomPinListQuery, bool) {
|
||||
}), true
|
||||
}
|
||||
|
||||
func parseRobotRoomListQuery(c *gin.Context) robotRoomListQuery {
|
||||
options := shared.ListOptions(c)
|
||||
return robotRoomListQuery{
|
||||
Page: options.Page,
|
||||
PageSize: options.PageSize,
|
||||
Status: options.Status,
|
||||
}
|
||||
}
|
||||
|
||||
func firstQueryValue(c *gin.Context, names ...string) string {
|
||||
for _, name := range names {
|
||||
if value := strings.TrimSpace(c.Query(name)); value != "" {
|
||||
|
||||
@ -19,29 +19,6 @@ type roomPinListQuery struct {
|
||||
Status string
|
||||
}
|
||||
|
||||
type robotRoomListQuery struct {
|
||||
Page int
|
||||
PageSize int
|
||||
Status string
|
||||
}
|
||||
|
||||
type createRobotRoomRequest struct {
|
||||
OwnerRobotUserID int64 `json:"ownerRobotUserId"`
|
||||
CandidateRobotUserIDs []int64 `json:"candidateRobotUserIds"`
|
||||
MinRobotCount int32 `json:"minRobotCount"`
|
||||
MaxRobotCount int32 `json:"maxRobotCount"`
|
||||
RoomName string `json:"roomName"`
|
||||
RoomAvatar string `json:"roomAvatar"`
|
||||
VisibleRegionID int64 `json:"visibleRegionId"`
|
||||
GiftIDs []string `json:"giftIds"`
|
||||
LuckyGiftIDs []string `json:"luckyGiftIds"`
|
||||
NormalGiftIntervalMS int64 `json:"normalGiftIntervalMs"`
|
||||
LuckyComboMin int64 `json:"luckyComboMin"`
|
||||
LuckyComboMax int64 `json:"luckyComboMax"`
|
||||
LuckyPauseMinMS int64 `json:"luckyPauseMinMs"`
|
||||
LuckyPauseMaxMS int64 `json:"luckyPauseMaxMs"`
|
||||
}
|
||||
|
||||
type createRoomPinRequest struct {
|
||||
DurationDays int64 `json:"durationDays"`
|
||||
ExpiresAtMS int64 `json:"expiresAtMs"`
|
||||
|
||||
@ -1,300 +0,0 @@
|
||||
package roomadmin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"hyapp-admin-server/internal/integration/robotclient"
|
||||
"hyapp-admin-server/internal/integration/roomclient"
|
||||
"hyapp-admin-server/internal/modules/shared"
|
||||
)
|
||||
|
||||
const defaultRoomRobotScene = "voice"
|
||||
|
||||
type RobotRoom struct {
|
||||
AppCode string `json:"appCode"`
|
||||
RoomID string `json:"roomId"`
|
||||
RoomShortID string `json:"roomShortId"`
|
||||
Title string `json:"title"`
|
||||
CoverURL string `json:"coverUrl"`
|
||||
VisibleRegionID int64 `json:"visibleRegionId"`
|
||||
RegionName string `json:"regionName"`
|
||||
Status string `json:"status"`
|
||||
OwnerRobotUserID string `json:"ownerRobotUserId"`
|
||||
Owner RoomOwner `json:"owner"`
|
||||
RobotUserIDs []string `json:"robotUserIds"`
|
||||
Robots []RoomOwner `json:"robots"`
|
||||
GiftRule RobotGiftRule `json:"giftRule"`
|
||||
CreatedByAdminID uint64 `json:"createdByAdminId"`
|
||||
CreatedAtMS int64 `json:"createdAtMs"`
|
||||
UpdatedAtMS int64 `json:"updatedAtMs"`
|
||||
}
|
||||
|
||||
type RobotGiftRule struct {
|
||||
GiftIDs []string `json:"giftIds"`
|
||||
LuckyGiftIDs []string `json:"luckyGiftIds"`
|
||||
NormalGiftIntervalMS int64 `json:"normalGiftIntervalMs"`
|
||||
LuckyComboMin int64 `json:"luckyComboMin"`
|
||||
LuckyComboMax int64 `json:"luckyComboMax"`
|
||||
LuckyPauseMinMS int64 `json:"luckyPauseMinMs"`
|
||||
LuckyPauseMaxMS int64 `json:"luckyPauseMaxMs"`
|
||||
}
|
||||
|
||||
type AvailableRoomRobot struct {
|
||||
UserID string `json:"userId"`
|
||||
Status string `json:"status"`
|
||||
RoomScene string `json:"roomScene"`
|
||||
LastUsedAtMS int64 `json:"lastUsedAtMs"`
|
||||
UsedCount int64 `json:"usedCount"`
|
||||
User RoomOwner `json:"user"`
|
||||
}
|
||||
|
||||
func (s *Service) ListRobotRooms(ctx context.Context, query robotRoomListQuery) ([]RobotRoom, int64, error) {
|
||||
if s.roomClient == nil {
|
||||
return nil, 0, fmt.Errorf("room service client is not configured")
|
||||
}
|
||||
if query.Page <= 0 {
|
||||
query.Page = 1
|
||||
}
|
||||
if query.PageSize <= 0 || query.PageSize > 100 {
|
||||
query.PageSize = 50
|
||||
}
|
||||
result, err := s.roomClient.ListRobotRooms(ctx, roomclient.ListRobotRoomsRequest{
|
||||
Page: query.Page,
|
||||
PageSize: query.PageSize,
|
||||
Status: normalizeRobotRoomListStatus(query.Status),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
items := robotRoomsFromClient(result.Rooms)
|
||||
if err := s.fillRobotRoomDetails(ctx, items); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
return items, result.Total, nil
|
||||
}
|
||||
|
||||
func normalizeRobotRoomListStatus(status string) string {
|
||||
switch strings.TrimSpace(status) {
|
||||
case "active", "stopped":
|
||||
return strings.TrimSpace(status)
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) ListAvailableRoomRobots(ctx context.Context) ([]AvailableRoomRobot, error) {
|
||||
if s.robotClient == nil || s.roomClient == nil {
|
||||
return nil, fmt.Errorf("robot or room service client is not configured")
|
||||
}
|
||||
result, err := s.robotClient.ListRoomRobots(ctx, robotclient.ListRoomRobotsRequest{
|
||||
RoomScene: defaultRoomRobotScene,
|
||||
Status: "active",
|
||||
PageSize: 200,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userIDs := make([]int64, 0, len(result.Robots))
|
||||
for _, item := range result.Robots {
|
||||
if item.UserID > 0 {
|
||||
userIDs = append(userIDs, item.UserID)
|
||||
}
|
||||
}
|
||||
available, err := s.roomClient.FilterAvailableRoomRobots(ctx, userIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
allowed := make(map[int64]bool, len(available.AvailableUserIDs))
|
||||
for _, userID := range available.AvailableUserIDs {
|
||||
allowed[userID] = true
|
||||
}
|
||||
owners, err := s.queryRoomOwners(ctx, available.AvailableUserIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items := make([]AvailableRoomRobot, 0, len(available.AvailableUserIDs))
|
||||
for _, robot := range result.Robots {
|
||||
if !allowed[robot.UserID] {
|
||||
continue
|
||||
}
|
||||
items = append(items, AvailableRoomRobot{
|
||||
UserID: strconv.FormatInt(robot.UserID, 10),
|
||||
Status: robot.Status,
|
||||
RoomScene: robot.RoomScene,
|
||||
LastUsedAtMS: robot.LastUsedAtMS,
|
||||
UsedCount: robot.UsedCount,
|
||||
User: owners[robot.UserID],
|
||||
})
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
func (s *Service) CreateRobotRoom(ctx context.Context, req createRobotRoomRequest, actor shared.Actor) (RobotRoom, error) {
|
||||
if s.roomClient == nil {
|
||||
return RobotRoom{}, fmt.Errorf("room service client is not configured")
|
||||
}
|
||||
if _, err := normalizeCreateRobotRoomRequest(req); err != nil {
|
||||
return RobotRoom{}, err
|
||||
}
|
||||
created, err := s.roomClient.CreateRobotRoom(ctx, roomclient.CreateRobotRoomRequest{
|
||||
OwnerRobotUserID: req.OwnerRobotUserID,
|
||||
CandidateRobotUserIDs: req.CandidateRobotUserIDs,
|
||||
MinRobotCount: req.MinRobotCount,
|
||||
MaxRobotCount: req.MaxRobotCount,
|
||||
RoomName: req.RoomName,
|
||||
RoomAvatar: req.RoomAvatar,
|
||||
VisibleRegionID: req.VisibleRegionID,
|
||||
GiftRule: roomclient.RobotRoomGiftRule{
|
||||
GiftIDs: req.GiftIDs,
|
||||
LuckyGiftIDs: req.LuckyGiftIDs,
|
||||
NormalGiftIntervalMS: req.NormalGiftIntervalMS,
|
||||
LuckyComboMin: req.LuckyComboMin,
|
||||
LuckyComboMax: req.LuckyComboMax,
|
||||
LuckyPauseMinMS: req.LuckyPauseMinMS,
|
||||
LuckyPauseMaxMS: req.LuckyPauseMaxMS,
|
||||
},
|
||||
AdminID: uint64(actor.UserID),
|
||||
})
|
||||
if err != nil {
|
||||
return RobotRoom{}, mapRoomClientError(err)
|
||||
}
|
||||
items := []RobotRoom{robotRoomFromClient(created)}
|
||||
if err := s.fillRobotRoomDetails(ctx, items); err != nil {
|
||||
return RobotRoom{}, err
|
||||
}
|
||||
return items[0], nil
|
||||
}
|
||||
|
||||
func (s *Service) SetRobotRoomStatus(ctx context.Context, roomID string, status string, actor shared.Actor) (RobotRoom, error) {
|
||||
if s.roomClient == nil {
|
||||
return RobotRoom{}, fmt.Errorf("room service client is not configured")
|
||||
}
|
||||
roomID = strings.TrimSpace(roomID)
|
||||
status = strings.TrimSpace(status)
|
||||
if roomID == "" || (status != "active" && status != "stopped") {
|
||||
return RobotRoom{}, fmt.Errorf("%w: 机器人房间状态参数不正确", ErrInvalidArgument)
|
||||
}
|
||||
item, err := s.roomClient.SetRobotRoomStatus(ctx, roomclient.SetRobotRoomStatusRequest{
|
||||
RoomID: roomID,
|
||||
Status: status,
|
||||
AdminID: uint64(actor.UserID),
|
||||
})
|
||||
if err != nil {
|
||||
return RobotRoom{}, mapRoomClientError(err)
|
||||
}
|
||||
items := []RobotRoom{robotRoomFromClient(item)}
|
||||
if err := s.fillRobotRoomDetails(ctx, items); err != nil {
|
||||
return RobotRoom{}, err
|
||||
}
|
||||
return items[0], nil
|
||||
}
|
||||
|
||||
func normalizeCreateRobotRoomRequest(req createRobotRoomRequest) (createRobotRoomRequest, error) {
|
||||
if req.OwnerRobotUserID <= 0 {
|
||||
return req, fmt.Errorf("%w: 请选择房主机器人", ErrInvalidArgument)
|
||||
}
|
||||
if req.MinRobotCount <= 0 || req.MaxRobotCount < req.MinRobotCount {
|
||||
return req, fmt.Errorf("%w: 机器人数量范围不正确", ErrInvalidArgument)
|
||||
}
|
||||
if len(req.CandidateRobotUserIDs) == 0 {
|
||||
return req, fmt.Errorf("%w: 请选择候选机器人", ErrInvalidArgument)
|
||||
}
|
||||
if len(req.GiftIDs) == 0 {
|
||||
return req, fmt.Errorf("%w: 请选择普通礼物合集", ErrInvalidArgument)
|
||||
}
|
||||
if len(req.LuckyGiftIDs) == 0 {
|
||||
return req, fmt.Errorf("%w: 请选择幸运礼物合集", ErrInvalidArgument)
|
||||
}
|
||||
if req.NormalGiftIntervalMS <= 0 {
|
||||
return req, fmt.Errorf("%w: 普通礼物频次不正确", ErrInvalidArgument)
|
||||
}
|
||||
if req.LuckyComboMin <= 0 || req.LuckyComboMax < req.LuckyComboMin {
|
||||
return req, fmt.Errorf("%w: 幸运礼物连击范围不正确", ErrInvalidArgument)
|
||||
}
|
||||
if req.LuckyPauseMinMS <= 0 || req.LuckyPauseMaxMS < req.LuckyPauseMinMS {
|
||||
return req, fmt.Errorf("%w: 幸运礼物间隔范围不正确", ErrInvalidArgument)
|
||||
}
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func robotRoomsFromClient(items []roomclient.RobotRoom) []RobotRoom {
|
||||
out := make([]RobotRoom, 0, len(items))
|
||||
for _, item := range items {
|
||||
out = append(out, robotRoomFromClient(item))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func robotRoomFromClient(item roomclient.RobotRoom) RobotRoom {
|
||||
robotUserIDs := make([]string, 0, len(item.RobotUserIDs))
|
||||
for _, userID := range item.RobotUserIDs {
|
||||
robotUserIDs = append(robotUserIDs, strconv.FormatInt(userID, 10))
|
||||
}
|
||||
return RobotRoom{
|
||||
AppCode: item.AppCode,
|
||||
RoomID: item.RoomID,
|
||||
RoomShortID: item.RoomShortID,
|
||||
Title: item.Title,
|
||||
CoverURL: item.CoverURL,
|
||||
VisibleRegionID: item.VisibleRegionID,
|
||||
Status: item.Status,
|
||||
OwnerRobotUserID: strconv.FormatInt(item.OwnerRobotUserID, 10),
|
||||
RobotUserIDs: robotUserIDs,
|
||||
GiftRule: RobotGiftRule{
|
||||
GiftIDs: item.GiftRule.GiftIDs,
|
||||
LuckyGiftIDs: item.GiftRule.LuckyGiftIDs,
|
||||
NormalGiftIntervalMS: item.GiftRule.NormalGiftIntervalMS,
|
||||
LuckyComboMin: item.GiftRule.LuckyComboMin,
|
||||
LuckyComboMax: item.GiftRule.LuckyComboMax,
|
||||
LuckyPauseMinMS: item.GiftRule.LuckyPauseMinMS,
|
||||
LuckyPauseMaxMS: item.GiftRule.LuckyPauseMaxMS,
|
||||
},
|
||||
CreatedByAdminID: item.CreatedByAdminID,
|
||||
CreatedAtMS: item.CreatedAtMS,
|
||||
UpdatedAtMS: item.UpdatedAtMS,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) fillRobotRoomDetails(ctx context.Context, items []RobotRoom) error {
|
||||
if s.userDB == nil || len(items) == 0 {
|
||||
return nil
|
||||
}
|
||||
userIDs := make([]int64, 0)
|
||||
regionIDs := make([]int64, 0)
|
||||
for _, item := range items {
|
||||
if ownerID, err := strconv.ParseInt(item.OwnerRobotUserID, 10, 64); err == nil && ownerID > 0 {
|
||||
userIDs = append(userIDs, ownerID)
|
||||
}
|
||||
for _, rawID := range item.RobotUserIDs {
|
||||
if userID, err := strconv.ParseInt(rawID, 10, 64); err == nil && userID > 0 {
|
||||
userIDs = append(userIDs, userID)
|
||||
}
|
||||
}
|
||||
if item.VisibleRegionID > 0 {
|
||||
regionIDs = append(regionIDs, item.VisibleRegionID)
|
||||
}
|
||||
}
|
||||
owners, err := s.queryRoomOwners(ctx, uniqueInt64s(userIDs))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
regions, err := s.queryRegionNames(ctx, uniqueInt64s(regionIDs))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for index := range items {
|
||||
if ownerID, err := strconv.ParseInt(items[index].OwnerRobotUserID, 10, 64); err == nil {
|
||||
items[index].Owner = owners[ownerID]
|
||||
}
|
||||
for _, rawID := range items[index].RobotUserIDs {
|
||||
if userID, err := strconv.ParseInt(rawID, 10, 64); err == nil {
|
||||
items[index].Robots = append(items[index].Robots, owners[userID])
|
||||
}
|
||||
}
|
||||
items[index].RegionName = regions[items[index].VisibleRegionID]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -15,11 +15,6 @@ func RegisterRoutes(protected *gin.RouterGroup, h *Handler) {
|
||||
protected.GET("/admin/rooms/pins", middleware.RequirePermission("room-pin:view"), h.ListRoomPins)
|
||||
protected.POST("/admin/rooms/pins", middleware.RequirePermission("room-pin:create"), h.CreateRoomPin)
|
||||
protected.DELETE("/admin/rooms/pins/:pin_id", middleware.RequirePermission("room-pin:cancel"), h.CancelRoomPin)
|
||||
protected.GET("/admin/rooms/robot-rooms", middleware.RequirePermission("room-robot:view"), h.ListRobotRooms)
|
||||
protected.GET("/admin/rooms/robot-rooms/available-robots", middleware.RequirePermission("room-robot:view"), h.ListAvailableRoomRobots)
|
||||
protected.POST("/admin/rooms/robot-rooms", middleware.RequirePermission("room-robot:create"), h.CreateRobotRoom)
|
||||
protected.POST("/admin/rooms/robot-rooms/:room_id/start", middleware.RequirePermission("room-robot:update"), h.StartRobotRoom)
|
||||
protected.POST("/admin/rooms/robot-rooms/:room_id/stop", middleware.RequirePermission("room-robot:update"), h.StopRobotRoom)
|
||||
protected.GET("/admin/rooms/config", middleware.RequirePermission("room-config:view"), h.GetRoomConfig)
|
||||
protected.PUT("/admin/rooms/config", middleware.RequirePermission("room-config:update"), h.UpdateRoomConfig)
|
||||
protected.PATCH("/admin/rooms/:room_id", middleware.RequirePermission("room:update"), h.UpdateRoom)
|
||||
|
||||
@ -12,7 +12,6 @@ import (
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"hyapp-admin-server/internal/appctx"
|
||||
"hyapp-admin-server/internal/integration/robotclient"
|
||||
"hyapp-admin-server/internal/integration/roomclient"
|
||||
"hyapp-admin-server/internal/modules/shared"
|
||||
)
|
||||
@ -23,9 +22,8 @@ var (
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
userDB *sql.DB
|
||||
roomClient roomclient.Client
|
||||
robotClient robotclient.Client
|
||||
userDB *sql.DB
|
||||
roomClient roomclient.Client
|
||||
}
|
||||
|
||||
type RoomOwner struct {
|
||||
@ -60,8 +58,8 @@ type Room struct {
|
||||
VisibleRegionID int64 `json:"visibleRegionId"`
|
||||
}
|
||||
|
||||
func NewService(userDB *sql.DB, roomClient roomclient.Client, robotClient robotclient.Client) *Service {
|
||||
return &Service{userDB: userDB, roomClient: roomClient, robotClient: robotClient}
|
||||
func NewService(userDB *sql.DB, roomClient roomclient.Client) *Service {
|
||||
return &Service{userDB: userDB, roomClient: roomClient}
|
||||
}
|
||||
|
||||
func (s *Service) ListRooms(ctx context.Context, query listQuery) ([]Room, int64, error) {
|
||||
|
||||
@ -36,9 +36,6 @@ var defaultPermissions = []model.Permission{
|
||||
{Name: "房间置顶取消", Code: "room-pin:cancel", Kind: "button"},
|
||||
{Name: "房间配置查看", Code: "room-config:view", Kind: "menu"},
|
||||
{Name: "房间配置更新", Code: "room-config:update", Kind: "button"},
|
||||
{Name: "机器人房间查看", Code: "room-robot:view", Kind: "menu"},
|
||||
{Name: "机器人房间创建", Code: "room-robot:create", Kind: "button"},
|
||||
{Name: "机器人房间更新", Code: "room-robot:update", Kind: "button"},
|
||||
{Name: "APP 配置查看", Code: "app-config:view", Kind: "menu"},
|
||||
{Name: "APP 配置更新", Code: "app-config:update", Kind: "button"},
|
||||
{Name: "版本管理查看", Code: "app-version:view", Kind: "menu"},
|
||||
@ -101,8 +98,6 @@ var defaultPermissions = []model.Permission{
|
||||
{Name: "举报列表查看", Code: "report:view", Kind: "menu"},
|
||||
{Name: "礼物钻石查看", Code: "gift-diamond:view", Kind: "menu"},
|
||||
{Name: "礼物钻石更新", Code: "gift-diamond:update", Kind: "button"},
|
||||
{Name: "全服通知查看", Code: "full-server-notice:view", Kind: "menu"},
|
||||
{Name: "全服通知发送", Code: "full-server-notice:send", Kind: "button"},
|
||||
{Name: "支付账单查看", Code: "payment-bill:view", Kind: "menu"},
|
||||
{Name: "三方支付查看", Code: "payment-third-party:view", Kind: "menu"},
|
||||
{Name: "三方支付更新", Code: "payment-third-party:update", Kind: "button"},
|
||||
@ -142,9 +137,6 @@ var defaultPermissions = []model.Permission{
|
||||
{Name: "周星配置创建", Code: "weekly-star:create", Kind: "button"},
|
||||
{Name: "周星配置更新", Code: "weekly-star:update", Kind: "button"},
|
||||
{Name: "周星结算查看", Code: "weekly-star:settle", Kind: "button"},
|
||||
{Name: "代理开业活动查看", Code: "agency-opening:view", Kind: "menu"},
|
||||
{Name: "代理开业活动创建", Code: "agency-opening:create", Kind: "button"},
|
||||
{Name: "代理开业活动更新", Code: "agency-opening:update", Kind: "button"},
|
||||
{Name: "角色查看", Code: "role:view", Kind: "menu"},
|
||||
{Name: "角色创建", Code: "role:create", Kind: "button"},
|
||||
{Name: "角色更新", Code: "role:update", Kind: "button"},
|
||||
@ -263,7 +255,6 @@ func (s *Store) seedMenus() error {
|
||||
{ParentID: &roomsID, Title: "房间列表", Code: "room-list", Path: "/rooms", Icon: "room", PermissionCode: "room:view", Sort: 65, Visible: true},
|
||||
{ParentID: &roomsID, Title: "房间置顶", Code: "room-pins", Path: "/rooms/pins", Icon: "push_pin", PermissionCode: "room-pin:view", Sort: 66, Visible: true},
|
||||
{ParentID: &roomsID, Title: "房间配置", Code: "room-config", Path: "/rooms/config", Icon: "settings", PermissionCode: "room-config:view", Sort: 67, Visible: true},
|
||||
{ParentID: &roomsID, Title: "机器人房间", Code: "room-robots", Path: "/rooms/robots", Icon: "team", PermissionCode: "room-robot:view", Sort: 68, Visible: true},
|
||||
{ParentID: &appConfigID, Title: "H5配置", Code: "app-config-h5", Path: "/app-config/h5", Icon: "settings", PermissionCode: "app-config:view", Sort: 66, Visible: true},
|
||||
{ParentID: &appConfigID, Title: "BANNER配置", Code: "app-config-banners", Path: "/app-config/banners", Icon: "image", PermissionCode: "app-config:view", Sort: 67, Visible: true},
|
||||
{ParentID: &appConfigID, Title: "开屏配置", Code: "app-config-splash-screens", Path: "/app-config/splash-screens", Icon: "image", PermissionCode: "app-config:view", Sort: 68, Visible: true},
|
||||
@ -282,10 +273,9 @@ func (s *Store) seedMenus() error {
|
||||
{ParentID: &operationsID, Title: "幸运礼物", Code: "lucky-gift", Path: "/operations/lucky-gift", Icon: "redeem", PermissionCode: "lucky-gift:view", Sort: 71, Visible: true},
|
||||
{ParentID: &operationsID, Title: "举报列表", Code: "operation-reports", Path: "/operations/reports", Icon: "flag", PermissionCode: "report:view", Sort: 72, Visible: true},
|
||||
{ParentID: &operationsID, Title: "礼物钻石", Code: "operation-gift-diamond", Path: "/operations/gift-diamonds", Icon: "diamond", PermissionCode: "gift-diamond:view", Sort: 73, Visible: true},
|
||||
{ParentID: &operationsID, Title: "全服通知", Code: "operation-full-server-notice", Path: "/operations/full-server-notices", Icon: "campaign", PermissionCode: "full-server-notice:view", Sort: 74, Visible: true},
|
||||
{ParentID: &paymentID, Title: "账单列表", Code: "payment-bill-list", Path: "/payment/bills", Icon: "receipt", PermissionCode: "payment-bill:view", Sort: 68, Visible: true},
|
||||
{ParentID: &paymentID, Title: "三方支付", Code: "payment-third-party", Path: "/payment/third-party", Icon: "wallet", PermissionCode: "payment-third-party:view", Sort: 69, Visible: true},
|
||||
{ParentID: &paymentID, Title: "支付内购商品", Code: "payment-recharge-products", Path: "/payment/recharge-products", Icon: "wallet", PermissionCode: "payment-product:view", Sort: 70, Visible: true},
|
||||
{ParentID: &paymentID, Title: "内购配置", Code: "payment-recharge-products", Path: "/payment/recharge-products", Icon: "wallet", PermissionCode: "payment-product:view", Sort: 70, Visible: true},
|
||||
{ParentID: &activityID, Title: "每日任务", Code: "daily-task-list", Path: "/activities/daily-tasks", Icon: "task", PermissionCode: "daily-task:view", Sort: 69, Visible: true},
|
||||
{ParentID: &activityID, Title: "注册奖励", Code: "registration-reward", Path: "/activities/registration-reward", Icon: "gift", PermissionCode: "registration-reward:view", Sort: 70, Visible: true},
|
||||
{ParentID: &activityID, Title: "成就配置", Code: "achievement-config", Path: "/activities/achievements", Icon: "military_tech", PermissionCode: "achievement:view", Sort: 71, Visible: true},
|
||||
@ -296,13 +286,12 @@ func (s *Store) seedMenus() error {
|
||||
{ParentID: &activityID, Title: "CP配置", Code: "cp-config", Path: "/activities/cp-config", Icon: "favorite", PermissionCode: "cp-config:view", Sort: 76, Visible: true},
|
||||
{ParentID: &activityID, Title: "VIP配置", Code: "vip-config", Path: "/activities/vip-config", Icon: "workspace_premium", PermissionCode: "vip-config:view", Sort: 77, Visible: true},
|
||||
{ParentID: &activityID, Title: "周星配置", Code: "weekly-star", Path: "/activities/weekly-star", Icon: "star", PermissionCode: "weekly-star:view", Sort: 78, Visible: true},
|
||||
{ParentID: &activityID, Title: "代理开业活动", Code: "agency-opening", Path: "/activities/agency-opening", Icon: "workspace_premium", PermissionCode: "agency-opening:view", Sort: 79, Visible: true},
|
||||
{ParentID: &gameID, Title: "游戏列表", Code: "game-list", Path: "/games", Icon: "sports_esports", PermissionCode: "game:view", Sort: 70, Visible: true},
|
||||
{ParentID: &gameID, Title: "自研游戏", Code: "self-games", Path: "/games/self-games", Icon: "settings", PermissionCode: "game:view", Sort: 80, Visible: true},
|
||||
{ParentID: &gameID, Title: "全站机器人", Code: "game-robots", Path: "/games/robots", Icon: "team", PermissionCode: "game:view", Sort: 90, Visible: true},
|
||||
{ParentID: &geoID, Title: "国家管理", Code: "host-org-countries", Path: "/host/countries", Icon: "public", PermissionCode: "country:view", Sort: 70, Visible: true},
|
||||
{ParentID: &geoID, Title: "区域管理", Code: "host-org-regions", Path: "/host/regions", Icon: "map", PermissionCode: "region:view", Sort: 71, Visible: true},
|
||||
{ParentID: &hostOrgID, Title: "经理列表", Code: "host-org-managers", Path: "/host/managers", Icon: "users", PermissionCode: "bd:view", Sort: 80, Visible: true},
|
||||
{ParentID: &hostOrgID, Title: "Manager列表", Code: "host-org-managers", Path: "/host/managers", Icon: "users", PermissionCode: "agency:view", Sort: 80, Visible: true},
|
||||
{ParentID: &hostOrgID, Title: "Agency 列表", Code: "host-org-agencies", Path: "/host/agencies", Icon: "apartment", PermissionCode: "agency:view", Sort: 81, Visible: true},
|
||||
{ParentID: &hostOrgID, Title: "BD Leader 列表", Code: "host-org-bd-leaders", Path: "/host/bd-leaders", Icon: "shield", PermissionCode: "bd:view", Sort: 82, Visible: true},
|
||||
{ParentID: &hostOrgID, Title: "BD 列表", Code: "host-org-bds", Path: "/host/bds", Icon: "team", PermissionCode: "bd:view", Sort: 83, Visible: true},
|
||||
@ -522,7 +511,7 @@ func defaultRolePermissionCodes(code string) []string {
|
||||
"level-config:view", "level-config:update",
|
||||
"pretty-id:view", "pretty-id:update", "pretty-id:generate", "pretty-id:grant",
|
||||
"region-block:view", "region-block:update",
|
||||
"room:view", "room:update", "room:delete", "room-pin:view", "room-pin:create", "room-pin:cancel", "room-config:view", "room-config:update", "room-robot:view", "room-robot:create", "room-robot:update",
|
||||
"room:view", "room:update", "room:delete", "room-pin:view", "room-pin:create", "room-pin:cancel", "room-config:view", "room-config:update",
|
||||
"app-config:view", "app-config:update",
|
||||
"app-version:view", "app-version:create", "app-version:update", "app-version:delete",
|
||||
"resource:view", "resource:create", "resource:update",
|
||||
@ -537,7 +526,7 @@ func defaultRolePermissionCodes(code string) []string {
|
||||
"agency:view", "agency:create", "agency:status", "agency:delete",
|
||||
"bd:view", "bd:create", "bd:update",
|
||||
"coin-seller:view", "coin-seller:create", "coin-seller:update", "coin-seller:stock-credit", "coin-seller:exchange-rate",
|
||||
"coin-ledger:view", "coin-seller-ledger:view", "coin-adjustment:view", "coin-adjustment:create", "report:view", "gift-diamond:view", "gift-diamond:update", "full-server-notice:view", "full-server-notice:send", "payment-bill:view", "payment-third-party:view", "payment-third-party:update", "payment-product:view", "payment-product:create", "payment-product:update", "payment-product:delete",
|
||||
"coin-ledger:view", "coin-seller-ledger:view", "coin-adjustment:view", "coin-adjustment:create", "report:view", "gift-diamond:view", "gift-diamond:update", "payment-bill:view", "payment-third-party:view", "payment-third-party:update", "payment-product:view", "payment-product:create", "payment-product:update", "payment-product:delete",
|
||||
"lucky-gift:view", "lucky-gift:update",
|
||||
"game:view", "game:create", "game:update", "game:status", "game:delete",
|
||||
"daily-task:view", "daily-task:create", "daily-task:update", "daily-task:status",
|
||||
@ -548,13 +537,12 @@ func defaultRolePermissionCodes(code string) []string {
|
||||
"cp-config:view", "cp-config:update",
|
||||
"vip-config:view", "vip-config:update", "vip-config:grant",
|
||||
"weekly-star:view", "weekly-star:create", "weekly-star:update", "weekly-star:settle",
|
||||
"agency-opening:view", "agency-opening:create", "agency-opening:update",
|
||||
"log:view",
|
||||
"job:view", "job:cancel", "export:create",
|
||||
"upload:create",
|
||||
}
|
||||
case "auditor":
|
||||
return []string{"overview:view", "log:view", "user:view", "app-user:view", "level-config:view", "pretty-id:view", "region-block:view", "room:view", "room-pin:view", "room-config:view", "room-robot:view", "app-config:view", "app-version:view", "resource:view", "resource-shop:view", "resource-group:view", "resource-grant:view", "gift:view", "emoji-pack:view", "host-agency-policy:view", "team-salary-policy:view", "host-salary-settlement:view", "coin-ledger:view", "coin-seller-ledger:view", "coin-adjustment:view", "report:view", "gift-diamond:view", "full-server-notice:view", "lucky-gift:view", "payment-bill:view", "payment-third-party:view", "payment-product:view", "game:view", "daily-task:view", "achievement:view", "seven-day-checkin:view", "room-rocket:view", "red-packet:view", "cp-config:view", "vip-config:view", "weekly-star:view", "agency-opening:view", "role:view", "permission:view", "job:view"}
|
||||
return []string{"overview:view", "log:view", "user:view", "app-user:view", "level-config:view", "pretty-id:view", "region-block:view", "room:view", "room-pin:view", "room-config:view", "app-config:view", "app-version:view", "resource:view", "resource-shop:view", "resource-group:view", "resource-grant:view", "gift:view", "emoji-pack:view", "host-agency-policy:view", "team-salary-policy:view", "host-salary-settlement:view", "coin-ledger:view", "coin-seller-ledger:view", "coin-adjustment:view", "report:view", "gift-diamond:view", "lucky-gift:view", "payment-bill:view", "payment-third-party:view", "payment-product:view", "game:view", "daily-task:view", "achievement:view", "seven-day-checkin:view", "room-rocket:view", "red-packet:view", "cp-config:view", "vip-config:view", "weekly-star:view", "role:view", "permission:view", "job:view"}
|
||||
case "readonly":
|
||||
return []string{
|
||||
"overview:view",
|
||||
@ -566,7 +554,6 @@ func defaultRolePermissionCodes(code string) []string {
|
||||
"room:view",
|
||||
"room-pin:view",
|
||||
"room-config:view",
|
||||
"room-robot:view",
|
||||
"app-config:view",
|
||||
"app-version:view",
|
||||
"resource:view",
|
||||
@ -589,7 +576,6 @@ func defaultRolePermissionCodes(code string) []string {
|
||||
"coin-adjustment:view",
|
||||
"report:view",
|
||||
"gift-diamond:view",
|
||||
"full-server-notice:view",
|
||||
"lucky-gift:view",
|
||||
"payment-bill:view",
|
||||
"payment-third-party:view",
|
||||
@ -603,7 +589,6 @@ func defaultRolePermissionCodes(code string) []string {
|
||||
"cp-config:view",
|
||||
"vip-config:view",
|
||||
"weekly-star:view",
|
||||
"agency-opening:view",
|
||||
"role:view",
|
||||
"permission:view",
|
||||
"menu:view",
|
||||
@ -620,7 +605,7 @@ func defaultRolePermissionMigrationCodes(code string) []string {
|
||||
case "ops-admin":
|
||||
return []string{
|
||||
"app-user:update", "app-user:status", "app-user:password",
|
||||
"room:view", "room:update", "room:delete", "room-pin:view", "room-pin:create", "room-pin:cancel", "room-config:view", "room-config:update", "room-robot:view", "room-robot:create", "room-robot:update",
|
||||
"room:view", "room:update", "room:delete", "room-pin:view", "room-pin:create", "room-pin:cancel", "room-config:view", "room-config:update",
|
||||
"app-config:view", "app-config:update",
|
||||
"app-version:view", "app-version:create", "app-version:update", "app-version:delete",
|
||||
"level-config:view", "level-config:update",
|
||||
@ -637,7 +622,7 @@ func defaultRolePermissionMigrationCodes(code string) []string {
|
||||
"region:view", "region:create", "region:update", "region:status",
|
||||
"host-agency-policy:view", "host-agency-policy:create", "host-agency-policy:update", "host-agency-policy:delete", "host-agency-policy:publish", "team-salary-policy:view", "team-salary-policy:create", "team-salary-policy:update", "team-salary-policy:delete", "host-salary-settlement:view", "host-salary-settlement:settle",
|
||||
"coin-seller:view", "coin-seller:create", "coin-seller:update", "coin-seller:stock-credit", "coin-seller:exchange-rate",
|
||||
"coin-ledger:view", "coin-seller-ledger:view", "coin-adjustment:view", "coin-adjustment:create", "report:view", "gift-diamond:view", "gift-diamond:update", "full-server-notice:view", "full-server-notice:send", "payment-bill:view", "payment-third-party:view", "payment-third-party:update", "payment-product:view", "payment-product:create", "payment-product:update", "payment-product:delete",
|
||||
"coin-ledger:view", "coin-seller-ledger:view", "coin-adjustment:view", "coin-adjustment:create", "report:view", "gift-diamond:view", "gift-diamond:update", "payment-bill:view", "payment-third-party:view", "payment-third-party:update", "payment-product:view", "payment-product:create", "payment-product:update", "payment-product:delete",
|
||||
"lucky-gift:view", "lucky-gift:update",
|
||||
"game:view", "game:create", "game:update", "game:status", "game:delete",
|
||||
"daily-task:view", "daily-task:create", "daily-task:update", "daily-task:status",
|
||||
@ -648,10 +633,9 @@ func defaultRolePermissionMigrationCodes(code string) []string {
|
||||
"cp-config:view", "cp-config:update",
|
||||
"vip-config:view", "vip-config:update", "vip-config:grant",
|
||||
"weekly-star:view", "weekly-star:create", "weekly-star:update", "weekly-star:settle",
|
||||
"agency-opening:view", "agency-opening:create", "agency-opening:update",
|
||||
}
|
||||
case "auditor", "readonly":
|
||||
return []string{"level-config:view", "pretty-id:view", "region-block:view", "room:view", "room-pin:view", "room-config:view", "room-robot:view", "app-config:view", "app-version:view", "resource:view", "resource-shop:view", "resource-group:view", "resource-grant:view", "gift:view", "emoji-pack:view", "host-agency-policy:view", "team-salary-policy:view", "host-salary-settlement:view", "coin-seller:view", "coin-ledger:view", "coin-seller-ledger:view", "coin-adjustment:view", "report:view", "gift-diamond:view", "full-server-notice:view", "lucky-gift:view", "payment-bill:view", "payment-third-party:view", "payment-product:view", "game:view", "daily-task:view", "achievement:view", "seven-day-checkin:view", "room-rocket:view", "red-packet:view", "cp-config:view", "vip-config:view", "weekly-star:view", "agency-opening:view"}
|
||||
return []string{"level-config:view", "pretty-id:view", "region-block:view", "room:view", "room-pin:view", "room-config:view", "app-config:view", "app-version:view", "resource:view", "resource-shop:view", "resource-group:view", "resource-grant:view", "gift:view", "emoji-pack:view", "host-agency-policy:view", "team-salary-policy:view", "host-salary-settlement:view", "coin-seller:view", "coin-ledger:view", "coin-seller-ledger:view", "coin-adjustment:view", "report:view", "gift-diamond:view", "lucky-gift:view", "payment-bill:view", "payment-third-party:view", "payment-product:view", "game:view", "daily-task:view", "achievement:view", "seven-day-checkin:view", "room-rocket:view", "red-packet:view", "cp-config:view", "vip-config:view", "weekly-star:view"}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -5,7 +5,6 @@ import (
|
||||
"hyapp-admin-server/internal/middleware"
|
||||
"hyapp-admin-server/internal/modules/achievementconfig"
|
||||
"hyapp-admin-server/internal/modules/adminuser"
|
||||
"hyapp-admin-server/internal/modules/agencyopening"
|
||||
"hyapp-admin-server/internal/modules/appconfig"
|
||||
"hyapp-admin-server/internal/modules/appregistry"
|
||||
"hyapp-admin-server/internal/modules/appuser"
|
||||
@ -18,7 +17,6 @@ import (
|
||||
"hyapp-admin-server/internal/modules/dailytask"
|
||||
"hyapp-admin-server/internal/modules/dashboard"
|
||||
"hyapp-admin-server/internal/modules/firstrechargereward"
|
||||
"hyapp-admin-server/internal/modules/fullservernotice"
|
||||
gamemanagement "hyapp-admin-server/internal/modules/gamemanagement"
|
||||
"hyapp-admin-server/internal/modules/giftdiamond"
|
||||
"hyapp-admin-server/internal/modules/health"
|
||||
@ -57,7 +55,6 @@ import (
|
||||
|
||||
type Handlers struct {
|
||||
AdminUser *adminuser.Handler
|
||||
AgencyOpening *agencyopening.Handler
|
||||
AchievementConfig *achievementconfig.Handler
|
||||
AppConfig *appconfig.Handler
|
||||
AppRegistry *appregistry.Handler
|
||||
@ -71,7 +68,6 @@ type Handlers struct {
|
||||
DailyTask *dailytask.Handler
|
||||
Dashboard *dashboard.Handler
|
||||
FirstRechargeReward *firstrechargereward.Handler
|
||||
FullServerNotice *fullservernotice.Handler
|
||||
Game *gamemanagement.Handler
|
||||
GiftDiamond *giftdiamond.Handler
|
||||
Health *health.Handler
|
||||
@ -116,7 +112,6 @@ func New(cfg config.Config, auth *service.AuthService, h Handlers) *gin.Engine {
|
||||
protected.Use(middleware.AuthRequired(auth), middleware.Audit(h.Audit))
|
||||
|
||||
authroutes.RegisterRoutes(api, protected, h.Auth)
|
||||
agencyopening.RegisterRoutes(protected, h.AgencyOpening)
|
||||
achievementconfig.RegisterRoutes(protected, h.AchievementConfig)
|
||||
coinledger.RegisterRoutes(protected, h.CoinLedger)
|
||||
menu.RegisterRoutes(protected, h.Menu)
|
||||
@ -133,7 +128,6 @@ func New(cfg config.Config, auth *service.AuthService, h Handlers) *gin.Engine {
|
||||
cumulativerechargereward.RegisterRoutes(protected, h.CumulativeRecharge)
|
||||
dailytask.RegisterRoutes(protected, h.DailyTask)
|
||||
firstrechargereward.RegisterRoutes(protected, h.FirstRechargeReward)
|
||||
fullservernotice.RegisterRoutes(protected, h.FullServerNotice)
|
||||
registrationreward.RegisterRoutes(protected, h.RegistrationReward)
|
||||
regionblock.RegisterRoutes(protected, h.RegionBlock)
|
||||
gamemanagement.RegisterRoutes(protected, h.Game)
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
SET @now_ms = UNIX_TIMESTAMP(CURRENT_TIMESTAMP(3)) * 1000;
|
||||
|
||||
UPDATE admin_menus
|
||||
SET title = '支付内购商品', updated_at_ms = @now_ms
|
||||
WHERE code = 'payment-recharge-products';
|
||||
@ -1,41 +0,0 @@
|
||||
SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
SET @now_ms = CAST(UNIX_TIMESTAMP(UTC_TIMESTAMP(3)) * 1000 AS UNSIGNED);
|
||||
|
||||
INSERT INTO admin_permissions (name, code, kind, description, created_at_ms, updated_at_ms) VALUES
|
||||
('机器人房间查看', 'room-robot:view', 'menu', '', @now_ms, @now_ms),
|
||||
('机器人房间创建', 'room-robot:create', 'button', '', @now_ms, @now_ms),
|
||||
('机器人房间更新', 'room-robot:update', 'button', '', @now_ms, @now_ms)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
name = VALUES(name),
|
||||
kind = VALUES(kind),
|
||||
description = VALUES(description),
|
||||
updated_at_ms = @now_ms;
|
||||
|
||||
INSERT INTO admin_menus (parent_id, title, code, path, icon, permission_code, sort, visible, created_at_ms, updated_at_ms)
|
||||
SELECT parent.id, '机器人房间', 'room-robots', '/rooms/robots', 'team', 'room-robot:view', 68, TRUE, @now_ms, @now_ms
|
||||
FROM admin_menus parent
|
||||
WHERE parent.code = 'rooms'
|
||||
ON DUPLICATE KEY UPDATE
|
||||
parent_id = VALUES(parent_id),
|
||||
title = VALUES(title),
|
||||
path = VALUES(path),
|
||||
icon = VALUES(icon),
|
||||
permission_code = VALUES(permission_code),
|
||||
sort = VALUES(sort),
|
||||
visible = VALUES(visible),
|
||||
updated_at_ms = @now_ms;
|
||||
|
||||
INSERT IGNORE INTO admin_role_permissions (role_id, permission_id)
|
||||
SELECT admin_role.id, admin_permission.id
|
||||
FROM admin_roles admin_role
|
||||
JOIN admin_permissions admin_permission
|
||||
WHERE admin_role.code IN ('platform-admin', 'ops-admin')
|
||||
AND admin_permission.code IN ('room-robot:view', 'room-robot:create', 'room-robot:update');
|
||||
|
||||
INSERT IGNORE INTO admin_role_permissions (role_id, permission_id)
|
||||
SELECT admin_role.id, admin_permission.id
|
||||
FROM admin_roles admin_role
|
||||
JOIN admin_permissions admin_permission
|
||||
WHERE admin_role.code IN ('auditor', 'readonly')
|
||||
AND admin_permission.code = 'room-robot:view';
|
||||
@ -1283,125 +1283,6 @@ CREATE TABLE IF NOT EXISTS weekly_star_settlements (
|
||||
KEY idx_weekly_star_settlement_cycle (app_code, cycle_id, status)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='周星 Top 奖励结算';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS cp_weekly_rank_configs (
|
||||
app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码',
|
||||
activity_code VARCHAR(64) NOT NULL DEFAULT 'cp_weekly_rank' COMMENT '活动编码',
|
||||
enabled TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否启用',
|
||||
relation_type VARCHAR(32) NOT NULL DEFAULT 'cp' COMMENT '关系类型,当前固定 cp',
|
||||
top_count INT NOT NULL DEFAULT 3 COMMENT '结算 Top 数量',
|
||||
updated_by_admin_id BIGINT NOT NULL DEFAULT 0 COMMENT '最后更新管理员',
|
||||
created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms',
|
||||
updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms',
|
||||
PRIMARY KEY (app_code)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='CP 周榜配置表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS cp_weekly_rank_rewards (
|
||||
app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码',
|
||||
rank_no INT NOT NULL COMMENT '名次',
|
||||
resource_group_id BIGINT NOT NULL COMMENT '奖励资源组 ID',
|
||||
created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms',
|
||||
updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms',
|
||||
PRIMARY KEY (app_code, rank_no)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='CP 周榜名次奖励资源组表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS cp_weekly_rank_settlements (
|
||||
app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码',
|
||||
settlement_id VARCHAR(96) NOT NULL COMMENT '发奖记录 ID',
|
||||
period_start_ms BIGINT NOT NULL COMMENT '周周期开始,UTC epoch ms',
|
||||
period_end_ms BIGINT NOT NULL COMMENT '周周期结束,UTC epoch ms',
|
||||
rank_no INT NOT NULL COMMENT '名次',
|
||||
relationship_id VARCHAR(128) NOT NULL COMMENT '获奖 CP 关系 ID',
|
||||
user_id BIGINT NOT NULL COMMENT '获奖用户 ID',
|
||||
score BIGINT NOT NULL DEFAULT 0 COMMENT '关系周榜分数',
|
||||
resource_group_id BIGINT NOT NULL COMMENT '发放资源组 ID',
|
||||
wallet_command_id VARCHAR(192) NOT NULL COMMENT 'wallet 发奖幂等键',
|
||||
wallet_grant_id VARCHAR(128) NOT NULL DEFAULT '' COMMENT 'wallet grant ID',
|
||||
status VARCHAR(32) NOT NULL COMMENT 'pending/running/granted/failed',
|
||||
failure_reason VARCHAR(512) NOT NULL DEFAULT '' COMMENT '失败原因',
|
||||
attempt_count INT NOT NULL DEFAULT 0 COMMENT '尝试次数',
|
||||
created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms',
|
||||
updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms',
|
||||
PRIMARY KEY (app_code, settlement_id),
|
||||
UNIQUE KEY uk_cp_weekly_rank_period_user (app_code, period_start_ms, rank_no, relationship_id, user_id),
|
||||
UNIQUE KEY uk_cp_weekly_rank_wallet_command (app_code, wallet_command_id),
|
||||
KEY idx_cp_weekly_rank_pending (app_code, period_start_ms, period_end_ms, status, updated_at_ms)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='CP 周榜发奖事实表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS agency_opening_cycles (
|
||||
app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码',
|
||||
cycle_id VARCHAR(96) NOT NULL COMMENT '代理开业周期 ID',
|
||||
activity_code VARCHAR(64) NOT NULL DEFAULT 'agency_opening' COMMENT '活动编码',
|
||||
title VARCHAR(128) NOT NULL DEFAULT '' COMMENT '周期标题',
|
||||
status VARCHAR(32) NOT NULL DEFAULT 'draft' COMMENT 'draft/active/disabled/settling/settled',
|
||||
start_ms BIGINT NOT NULL COMMENT 'UTC epoch ms 开始,包含',
|
||||
end_ms BIGINT NOT NULL COMMENT 'UTC epoch ms 结束,不包含',
|
||||
min_host_count INT NOT NULL DEFAULT 10 COMMENT '申请要求的有效主播数',
|
||||
max_agency_age_days INT NOT NULL DEFAULT 7 COMMENT '代理创建后允许申请的最大天数,0 表示不限制',
|
||||
created_by_admin_id BIGINT NOT NULL DEFAULT 0 COMMENT '创建管理员',
|
||||
updated_by_admin_id BIGINT NOT NULL DEFAULT 0 COMMENT '更新管理员',
|
||||
settled_at_ms BIGINT NOT NULL DEFAULT 0 COMMENT '结算完成时间',
|
||||
locked_by VARCHAR(96) NOT NULL DEFAULT '' COMMENT '结算 worker 锁',
|
||||
lock_until_ms BIGINT NOT NULL DEFAULT 0 COMMENT '结算锁过期时间',
|
||||
created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms',
|
||||
updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms',
|
||||
PRIMARY KEY (app_code, cycle_id),
|
||||
KEY idx_agency_opening_cycle_current (app_code, activity_code, status, start_ms, end_ms),
|
||||
KEY idx_agency_opening_cycle_due (app_code, activity_code, status, end_ms, lock_until_ms)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='代理开业活动周期';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS agency_opening_cycle_rewards (
|
||||
app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码',
|
||||
cycle_id VARCHAR(96) NOT NULL COMMENT '代理开业周期 ID',
|
||||
rank_no INT NOT NULL COMMENT '榜单名次',
|
||||
reward_coin_amount BIGINT NOT NULL DEFAULT 0 COMMENT '奖励金币',
|
||||
created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms',
|
||||
updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms',
|
||||
PRIMARY KEY (app_code, cycle_id, rank_no)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='代理开业榜单奖励';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS agency_opening_applications (
|
||||
app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码',
|
||||
application_id VARCHAR(96) NOT NULL COMMENT '申请 ID',
|
||||
cycle_id VARCHAR(96) NOT NULL COMMENT '代理开业周期 ID',
|
||||
agency_id BIGINT NOT NULL COMMENT '代理 ID',
|
||||
agency_owner_user_id BIGINT NOT NULL COMMENT '代理 owner 用户 ID',
|
||||
agency_name VARCHAR(128) NOT NULL DEFAULT '' COMMENT '代理名称快照',
|
||||
host_count INT NOT NULL DEFAULT 0 COMMENT '申请时有效主播数快照',
|
||||
agency_created_at_ms BIGINT NOT NULL DEFAULT 0 COMMENT '代理创建时间快照',
|
||||
status VARCHAR(32) NOT NULL DEFAULT 'applied' COMMENT 'applied/pending/granted/failed',
|
||||
score_coin_amount BIGINT NOT NULL DEFAULT 0 COMMENT '周期内开业流水金币',
|
||||
reward_rank_no INT NOT NULL DEFAULT 0 COMMENT '结算榜单名次',
|
||||
reward_coin_amount BIGINT NOT NULL DEFAULT 0 COMMENT '结算奖励金币',
|
||||
wallet_command_id VARCHAR(128) NOT NULL DEFAULT '' COMMENT '钱包幂等命令',
|
||||
wallet_transaction_id VARCHAR(128) NOT NULL DEFAULT '' COMMENT '钱包交易 ID',
|
||||
failure_reason VARCHAR(512) NOT NULL DEFAULT '' COMMENT '失败原因',
|
||||
applied_at_ms BIGINT NOT NULL COMMENT '申请时间',
|
||||
granted_at_ms BIGINT NOT NULL DEFAULT 0 COMMENT '发奖时间',
|
||||
created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms',
|
||||
updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms',
|
||||
PRIMARY KEY (app_code, application_id),
|
||||
UNIQUE KEY uk_agency_opening_owner_once (app_code, agency_owner_user_id),
|
||||
KEY idx_agency_opening_wallet_command (app_code, wallet_command_id),
|
||||
KEY idx_agency_opening_application_owner (app_code, agency_owner_user_id, applied_at_ms),
|
||||
KEY idx_agency_opening_agency_cycle (app_code, cycle_id, agency_id),
|
||||
KEY idx_agency_opening_application_rank (app_code, cycle_id, score_coin_amount, applied_at_ms, agency_id),
|
||||
KEY idx_agency_opening_application_status (app_code, cycle_id, status, updated_at_ms)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='代理开业申请和结算事实';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS agency_opening_score_events (
|
||||
app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码',
|
||||
source_event_id VARCHAR(128) NOT NULL COMMENT 'room-service outbox event_id',
|
||||
cycle_id VARCHAR(96) NOT NULL COMMENT '代理开业周期 ID',
|
||||
application_id VARCHAR(96) NOT NULL COMMENT '命中的申请 ID',
|
||||
agency_id BIGINT NOT NULL COMMENT '代理 ID',
|
||||
target_user_id BIGINT NOT NULL COMMENT '收礼主播用户 ID',
|
||||
score_delta BIGINT NOT NULL COMMENT '本次流水金币',
|
||||
occurred_at_ms BIGINT NOT NULL COMMENT '事件发生时间,UTC epoch ms',
|
||||
created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms',
|
||||
PRIMARY KEY (app_code, source_event_id),
|
||||
KEY idx_agency_opening_score_event_application (app_code, application_id, occurred_at_ms)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='代理开业流水事件幂等表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS invite_activity_reward_configs (
|
||||
app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码',
|
||||
enabled BOOLEAN NOT NULL DEFAULT TRUE COMMENT '是否启用邀请活动',
|
||||
|
||||
@ -1,43 +0,0 @@
|
||||
CREATE TABLE IF NOT EXISTS cp_weekly_rank_configs (
|
||||
app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码',
|
||||
activity_code VARCHAR(64) NOT NULL DEFAULT 'cp_weekly_rank' COMMENT '活动编码',
|
||||
enabled TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否启用',
|
||||
relation_type VARCHAR(32) NOT NULL DEFAULT 'cp' COMMENT '关系类型,当前固定 cp',
|
||||
top_count INT NOT NULL DEFAULT 3 COMMENT '结算 Top 数量',
|
||||
updated_by_admin_id BIGINT NOT NULL DEFAULT 0 COMMENT '最后更新管理员',
|
||||
created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms',
|
||||
updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms',
|
||||
PRIMARY KEY (app_code)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='CP 周榜配置表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS cp_weekly_rank_rewards (
|
||||
app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码',
|
||||
rank_no INT NOT NULL COMMENT '名次',
|
||||
resource_group_id BIGINT NOT NULL COMMENT '奖励资源组 ID',
|
||||
created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms',
|
||||
updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms',
|
||||
PRIMARY KEY (app_code, rank_no)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='CP 周榜名次奖励资源组表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS cp_weekly_rank_settlements (
|
||||
app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码',
|
||||
settlement_id VARCHAR(96) NOT NULL COMMENT '发奖记录 ID',
|
||||
period_start_ms BIGINT NOT NULL COMMENT '周周期开始,UTC epoch ms',
|
||||
period_end_ms BIGINT NOT NULL COMMENT '周周期结束,UTC epoch ms',
|
||||
rank_no INT NOT NULL COMMENT '名次',
|
||||
relationship_id VARCHAR(128) NOT NULL COMMENT '获奖 CP 关系 ID',
|
||||
user_id BIGINT NOT NULL COMMENT '获奖用户 ID',
|
||||
score BIGINT NOT NULL DEFAULT 0 COMMENT '关系周榜分数',
|
||||
resource_group_id BIGINT NOT NULL COMMENT '发放资源组 ID',
|
||||
wallet_command_id VARCHAR(192) NOT NULL COMMENT 'wallet 发奖幂等键',
|
||||
wallet_grant_id VARCHAR(128) NOT NULL DEFAULT '' COMMENT 'wallet grant ID',
|
||||
status VARCHAR(32) NOT NULL COMMENT 'pending/running/granted/failed',
|
||||
failure_reason VARCHAR(512) NOT NULL DEFAULT '' COMMENT '失败原因',
|
||||
attempt_count INT NOT NULL DEFAULT 0 COMMENT '尝试次数',
|
||||
created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms',
|
||||
updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms',
|
||||
PRIMARY KEY (app_code, settlement_id),
|
||||
UNIQUE KEY uk_cp_weekly_rank_period_user (app_code, period_start_ms, rank_no, relationship_id, user_id),
|
||||
UNIQUE KEY uk_cp_weekly_rank_wallet_command (app_code, wallet_command_id),
|
||||
KEY idx_cp_weekly_rank_pending (app_code, period_start_ms, period_end_ms, status, updated_at_ms)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='CP 周榜发奖事实表';
|
||||
@ -1,76 +0,0 @@
|
||||
CREATE TABLE IF NOT EXISTS agency_opening_cycles (
|
||||
app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码',
|
||||
cycle_id VARCHAR(96) NOT NULL COMMENT '代理开业周期 ID',
|
||||
activity_code VARCHAR(64) NOT NULL DEFAULT 'agency_opening' COMMENT '活动编码',
|
||||
title VARCHAR(128) NOT NULL DEFAULT '' COMMENT '周期标题',
|
||||
status VARCHAR(32) NOT NULL DEFAULT 'draft' COMMENT 'draft/active/disabled/settling/settled',
|
||||
start_ms BIGINT NOT NULL COMMENT 'UTC epoch ms 开始,包含',
|
||||
end_ms BIGINT NOT NULL COMMENT 'UTC epoch ms 结束,不包含',
|
||||
min_host_count INT NOT NULL DEFAULT 10 COMMENT '申请要求的有效主播数',
|
||||
max_agency_age_days INT NOT NULL DEFAULT 7 COMMENT '代理创建后允许申请的最大天数,0 表示不限制',
|
||||
created_by_admin_id BIGINT NOT NULL DEFAULT 0 COMMENT '创建管理员',
|
||||
updated_by_admin_id BIGINT NOT NULL DEFAULT 0 COMMENT '更新管理员',
|
||||
settled_at_ms BIGINT NOT NULL DEFAULT 0 COMMENT '结算完成时间',
|
||||
locked_by VARCHAR(96) NOT NULL DEFAULT '' COMMENT '结算 worker 锁',
|
||||
lock_until_ms BIGINT NOT NULL DEFAULT 0 COMMENT '结算锁过期时间',
|
||||
created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms',
|
||||
updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms',
|
||||
PRIMARY KEY (app_code, cycle_id),
|
||||
KEY idx_agency_opening_cycle_current (app_code, activity_code, status, start_ms, end_ms),
|
||||
KEY idx_agency_opening_cycle_due (app_code, activity_code, status, end_ms, lock_until_ms)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='代理开业活动周期';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS agency_opening_cycle_rewards (
|
||||
app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码',
|
||||
cycle_id VARCHAR(96) NOT NULL COMMENT '代理开业周期 ID',
|
||||
rank_no INT NOT NULL COMMENT '档位序号',
|
||||
threshold_coin_spent BIGINT NOT NULL DEFAULT 0 COMMENT '命中该档需要的代理房间流水金币',
|
||||
reward_coin_amount BIGINT NOT NULL DEFAULT 0 COMMENT '奖励金币',
|
||||
created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms',
|
||||
updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms',
|
||||
PRIMARY KEY (app_code, cycle_id, rank_no)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='代理开业流水奖励档位';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS agency_opening_applications (
|
||||
app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码',
|
||||
application_id VARCHAR(96) NOT NULL COMMENT '申请 ID',
|
||||
cycle_id VARCHAR(96) NOT NULL COMMENT '代理开业周期 ID',
|
||||
agency_id BIGINT NOT NULL COMMENT '代理 ID',
|
||||
agency_owner_user_id BIGINT NOT NULL COMMENT '代理 owner 用户 ID',
|
||||
agency_name VARCHAR(128) NOT NULL DEFAULT '' COMMENT '代理名称快照',
|
||||
host_count INT NOT NULL DEFAULT 0 COMMENT '申请时有效主播数快照',
|
||||
agency_created_at_ms BIGINT NOT NULL DEFAULT 0 COMMENT '代理创建时间快照',
|
||||
status VARCHAR(32) NOT NULL DEFAULT 'applied' COMMENT 'applied/pending/granted/failed',
|
||||
score_coin_amount BIGINT NOT NULL DEFAULT 0 COMMENT '周期内开业流水金币',
|
||||
reward_rank_no INT NOT NULL DEFAULT 0 COMMENT '结算命中的档位序号',
|
||||
reward_threshold_coin_spent BIGINT NOT NULL DEFAULT 0 COMMENT '结算命中的流水阈值',
|
||||
reward_coin_amount BIGINT NOT NULL DEFAULT 0 COMMENT '结算奖励金币',
|
||||
wallet_command_id VARCHAR(128) NOT NULL DEFAULT '' COMMENT '钱包幂等命令',
|
||||
wallet_transaction_id VARCHAR(128) NOT NULL DEFAULT '' COMMENT '钱包交易 ID',
|
||||
failure_reason VARCHAR(512) NOT NULL DEFAULT '' COMMENT '失败原因',
|
||||
applied_at_ms BIGINT NOT NULL COMMENT '申请时间',
|
||||
granted_at_ms BIGINT NOT NULL DEFAULT 0 COMMENT '发奖时间',
|
||||
created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms',
|
||||
updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms',
|
||||
PRIMARY KEY (app_code, application_id),
|
||||
UNIQUE KEY uk_agency_opening_owner_once (app_code, agency_owner_user_id),
|
||||
KEY idx_agency_opening_wallet_command (app_code, wallet_command_id),
|
||||
KEY idx_agency_opening_application_owner (app_code, agency_owner_user_id, applied_at_ms),
|
||||
KEY idx_agency_opening_agency_cycle (app_code, cycle_id, agency_id),
|
||||
KEY idx_agency_opening_application_rank (app_code, cycle_id, score_coin_amount, applied_at_ms, agency_id),
|
||||
KEY idx_agency_opening_application_status (app_code, cycle_id, status, updated_at_ms)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='代理开业申请和结算事实';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS agency_opening_score_events (
|
||||
app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码',
|
||||
source_event_id VARCHAR(128) NOT NULL COMMENT 'room-service outbox event_id',
|
||||
cycle_id VARCHAR(96) NOT NULL COMMENT '代理开业周期 ID',
|
||||
application_id VARCHAR(96) NOT NULL COMMENT '命中的申请 ID',
|
||||
agency_id BIGINT NOT NULL COMMENT '代理 ID',
|
||||
target_user_id BIGINT NOT NULL COMMENT '收礼主播用户 ID',
|
||||
score_delta BIGINT NOT NULL COMMENT '本次流水金币',
|
||||
occurred_at_ms BIGINT NOT NULL COMMENT '事件发生时间,UTC epoch ms',
|
||||
created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms',
|
||||
PRIMARY KEY (app_code, source_event_id),
|
||||
KEY idx_agency_opening_score_event_application (app_code, application_id, occurred_at_ms)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='代理开业流水事件幂等表';
|
||||
@ -11,7 +11,7 @@ func registerGRPCServers(server *grpc.Server, services *serviceBundle) {
|
||||
activityv1.RegisterMessageInboxServiceServer(server, grpcserver.NewMessageServer(services.message))
|
||||
// cron 入口只做调度适配;具体状态机仍在各 activity service 内部。
|
||||
// weekly-star 和 room-turnover 都依赖 wallet 发奖,所以这里必须把同一批已装配的 service 注入 cron server。
|
||||
activityv1.RegisterActivityCronServiceServer(server, grpcserver.NewCronServer(services.message, services.growth, services.achievement, services.weeklyStar, services.roomTurnoverReward, services.agencyOpening, services.cpWeeklyRank))
|
||||
activityv1.RegisterActivityCronServiceServer(server, grpcserver.NewCronServer(services.message, services.growth, services.achievement, services.weeklyStar, services.roomTurnoverReward))
|
||||
activityv1.RegisterTaskServiceServer(server, grpcserver.NewTaskServer(services.task))
|
||||
activityv1.RegisterAdminTaskServiceServer(server, grpcserver.NewAdminTaskServer(services.task))
|
||||
activityv1.RegisterGrowthLevelServiceServer(server, grpcserver.NewGrowthLevelServer(services.growth))
|
||||
@ -26,16 +26,11 @@ func registerGRPCServers(server *grpc.Server, services *serviceBundle) {
|
||||
activityv1.RegisterAdminSevenDayCheckInServiceServer(server, grpcserver.NewAdminSevenDayCheckInServer(services.sevenDayCheckIn))
|
||||
activityv1.RegisterWeeklyStarServiceServer(server, grpcserver.NewWeeklyStarServer(services.weeklyStar))
|
||||
activityv1.RegisterAdminWeeklyStarServiceServer(server, grpcserver.NewAdminWeeklyStarServer(services.weeklyStar))
|
||||
cpWeeklyRankServer := grpcserver.NewCPWeeklyRankServer(services.cpWeeklyRank)
|
||||
activityv1.RegisterCPWeeklyRankServiceServer(server, cpWeeklyRankServer)
|
||||
activityv1.RegisterAdminCPWeeklyRankServiceServer(server, cpWeeklyRankServer)
|
||||
activityv1.RegisterAgencyOpeningServiceServer(server, grpcserver.NewAgencyOpeningServer(services.agencyOpening))
|
||||
activityv1.RegisterAdminAgencyOpeningServiceServer(server, grpcserver.NewAdminAgencyOpeningServer(services.agencyOpening))
|
||||
activityv1.RegisterRoomTurnoverRewardServiceServer(server, grpcserver.NewRoomTurnoverRewardServer(services.roomTurnoverReward))
|
||||
activityv1.RegisterAdminRoomTurnoverRewardServiceServer(server, grpcserver.NewAdminRoomTurnoverRewardServer(services.roomTurnoverReward))
|
||||
// BroadcastServer 同时承载 BroadcastService 和 RoomEventConsumerService。
|
||||
// gRPC 直连投递和 MQ 消费都必须经过相同的 room event fanout 顺序,否则本地验证会通过但线上 MQ 路径漏积分。
|
||||
broadcastServer := grpcserver.NewBroadcastServer(services.broadcast, services.growth, services.weeklyStar, services.task, services.roomTurnoverReward, services.agencyOpening)
|
||||
broadcastServer := grpcserver.NewBroadcastServer(services.broadcast, services.growth, services.weeklyStar, services.task, services.roomTurnoverReward)
|
||||
activityv1.RegisterBroadcastServiceServer(server, broadcastServer)
|
||||
activityv1.RegisterRoomEventConsumerServiceServer(server, broadcastServer)
|
||||
activityv1.RegisterFirstRechargeRewardServiceServer(server, grpcserver.NewFirstRechargeRewardServer(services.firstRechargeReward))
|
||||
|
||||
@ -120,9 +120,6 @@ func newRoomOutboxConsumer(cfg config.Config, services *serviceBundle) (*rocketm
|
||||
if _, err := services.weeklyStar.HandleRoomEvent(eventCtx, envelope); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := services.agencyOpening.HandleRoomEvent(eventCtx, envelope); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := services.task.HandleRoomEvent(eventCtx, envelope); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -10,9 +10,7 @@ import (
|
||||
"hyapp/services/activity-service/internal/config"
|
||||
achievementservice "hyapp/services/activity-service/internal/service/achievement"
|
||||
activityservice "hyapp/services/activity-service/internal/service/activity"
|
||||
agencyopeningservice "hyapp/services/activity-service/internal/service/agencyopening"
|
||||
broadcastservice "hyapp/services/activity-service/internal/service/broadcast"
|
||||
cpweeklyrankservice "hyapp/services/activity-service/internal/service/cpweeklyrank"
|
||||
cumulativerechargeservice "hyapp/services/activity-service/internal/service/cumulativerecharge"
|
||||
firstrechargeservice "hyapp/services/activity-service/internal/service/firstrecharge"
|
||||
growthservice "hyapp/services/activity-service/internal/service/growth"
|
||||
@ -35,9 +33,7 @@ type serviceBundle struct {
|
||||
sevenDayCheckIn *sevendaycheckinservice.Service
|
||||
growth *growthservice.Service
|
||||
achievement *achievementservice.Service
|
||||
agencyOpening *agencyopeningservice.Service
|
||||
weeklyStar *weeklystarservice.Service
|
||||
cpWeeklyRank *cpweeklyrankservice.Service
|
||||
roomTurnoverReward *roomturnoverrewardservice.Service
|
||||
broadcast *broadcastservice.Service
|
||||
luckyGift *luckygiftservice.Service
|
||||
@ -58,9 +54,7 @@ func buildServiceBundle(cfg config.Config, repository *mysqlstorage.Repository,
|
||||
sevenDayCheckInSvc := sevendaycheckinservice.New(repository, walletClient)
|
||||
growthSvc := growthservice.New(repository, walletClient)
|
||||
achievementSvc := achievementservice.New(repository, walletClient)
|
||||
agencyOpeningSvc := agencyopeningservice.New(repository, walletClient, activityclient.NewGRPCAgencyOpeningSource(clients.userConn), roomClient)
|
||||
weeklyStarSvc := weeklystarservice.New(repository, walletClient)
|
||||
cpWeeklyRankSvc := cpweeklyrankservice.New(repository, activityclient.NewGRPCCPWeeklyRankSource(clients.userConn), walletClient)
|
||||
roomTurnoverRewardSvc := roomturnoverrewardservice.New(repository, walletClient, roomClient)
|
||||
|
||||
// Tencent IM 是区域广播、房间礼物播报和红包播报的外部发布器。
|
||||
@ -108,9 +102,7 @@ func buildServiceBundle(cfg config.Config, repository *mysqlstorage.Repository,
|
||||
sevenDayCheckIn: sevenDayCheckInSvc,
|
||||
growth: growthSvc,
|
||||
achievement: achievementSvc,
|
||||
agencyOpening: agencyOpeningSvc,
|
||||
weeklyStar: weeklyStarSvc,
|
||||
cpWeeklyRank: cpWeeklyRankSvc,
|
||||
roomTurnoverReward: roomTurnoverRewardSvc,
|
||||
broadcast: broadcastSvc,
|
||||
luckyGift: luckyGiftSvc,
|
||||
|
||||
@ -1,78 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
userv1 "hyapp.local/api/proto/user/v1"
|
||||
"hyapp/pkg/appcode"
|
||||
domain "hyapp/services/activity-service/internal/domain/agencyopening"
|
||||
)
|
||||
|
||||
// GRPCAgencyOpeningSource reads current Agency facts from user-service for activity qualification and scoring.
|
||||
type GRPCAgencyOpeningSource struct {
|
||||
client userv1.UserHostServiceClient
|
||||
}
|
||||
|
||||
func NewGRPCAgencyOpeningSource(conn *grpc.ClientConn) *GRPCAgencyOpeningSource {
|
||||
return &GRPCAgencyOpeningSource{client: userv1.NewUserHostServiceClient(conn)}
|
||||
}
|
||||
|
||||
func (s *GRPCAgencyOpeningSource) ResolveOwnerAgency(ctx context.Context, userID int64) (domain.AgencySnapshot, bool, error) {
|
||||
summaryResp, err := s.client.GetUserRoleSummary(ctx, &userv1.GetUserRoleSummaryRequest{
|
||||
Meta: meta(ctx, "agency-opening-owner"),
|
||||
UserId: userID,
|
||||
})
|
||||
if err != nil {
|
||||
return domain.AgencySnapshot{}, false, err
|
||||
}
|
||||
summary := summaryResp.GetSummary()
|
||||
if summary == nil || !summary.GetIsAgency() || summary.GetAgencyId() <= 0 {
|
||||
return domain.AgencySnapshot{}, false, nil
|
||||
}
|
||||
agencyResp, err := s.client.GetAgency(ctx, &userv1.GetAgencyRequest{
|
||||
Meta: meta(ctx, "agency-opening-agency"),
|
||||
AgencyId: summary.GetAgencyId(),
|
||||
})
|
||||
if err != nil {
|
||||
return domain.AgencySnapshot{}, false, err
|
||||
}
|
||||
agency := agencyResp.GetAgency()
|
||||
if agency == nil || agency.GetOwnerUserId() != userID {
|
||||
return domain.AgencySnapshot{}, false, nil
|
||||
}
|
||||
snapshot := agencySnapshotFromProto(agency)
|
||||
membersResp, err := s.client.GetAgencyMembers(ctx, &userv1.GetAgencyMembersRequest{
|
||||
Meta: meta(ctx, "agency-opening-agency-members"),
|
||||
AgencyId: snapshot.AgencyID,
|
||||
Status: "active",
|
||||
})
|
||||
if err != nil {
|
||||
return domain.AgencySnapshot{}, false, err
|
||||
}
|
||||
// GetAgency 的基础事实不总是携带聚合人数;资格判断必须使用当前 active membership 事实,避免把有效 agency 误判成 0 主播。
|
||||
snapshot.HostCount = int32(len(membersResp.GetMemberships()))
|
||||
return snapshot, true, nil
|
||||
}
|
||||
|
||||
func agencySnapshotFromProto(agency *userv1.Agency) domain.AgencySnapshot {
|
||||
if agency == nil {
|
||||
return domain.AgencySnapshot{}
|
||||
}
|
||||
return domain.AgencySnapshot{
|
||||
AgencyID: agency.GetAgencyId(),
|
||||
OwnerUserID: agency.GetOwnerUserId(),
|
||||
Name: agency.GetName(),
|
||||
Status: agency.GetStatus(),
|
||||
HostCount: agency.GetHostCount(),
|
||||
AgencyCreatedAtMS: agency.GetCreatedAtMs(),
|
||||
}
|
||||
}
|
||||
|
||||
func meta(ctx context.Context, requestID string) *userv1.RequestMeta {
|
||||
return &userv1.RequestMeta{
|
||||
RequestId: requestID,
|
||||
Caller: "activity-service",
|
||||
AppCode: appcode.FromContext(ctx),
|
||||
}
|
||||
}
|
||||
@ -1,64 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
userv1 "hyapp.local/api/proto/user/v1"
|
||||
"hyapp/pkg/appcode"
|
||||
domain "hyapp/services/activity-service/internal/domain/cpweeklyrank"
|
||||
)
|
||||
|
||||
// GRPCCPWeeklyRankSource reads CP weekly rank snapshots from user-service, the owner of CP relationship facts.
|
||||
type GRPCCPWeeklyRankSource struct {
|
||||
client userv1.UserCPInternalServiceClient
|
||||
}
|
||||
|
||||
func NewGRPCCPWeeklyRankSource(conn *grpc.ClientConn) *GRPCCPWeeklyRankSource {
|
||||
return &GRPCCPWeeklyRankSource{client: userv1.NewUserCPInternalServiceClient(conn)}
|
||||
}
|
||||
|
||||
func (s *GRPCCPWeeklyRankSource) ListCPWeeklyRankEntries(ctx context.Context, relationType string, startMS int64, endMS int64, limit int32) ([]domain.Entry, error) {
|
||||
resp, err := s.client.ListCPWeeklyRankEntries(ctx, &userv1.ListCPWeeklyRankEntriesRequest{
|
||||
Meta: &userv1.RequestMeta{
|
||||
RequestId: "cp-weekly-rank",
|
||||
Caller: "activity-service",
|
||||
AppCode: appcode.FromContext(ctx),
|
||||
},
|
||||
RelationType: relationType,
|
||||
StartMs: startMS,
|
||||
EndMs: endMS,
|
||||
Limit: limit,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
entries := make([]domain.Entry, 0, len(resp.GetEntries()))
|
||||
for _, item := range resp.GetEntries() {
|
||||
entries = append(entries, domain.Entry{
|
||||
Rank: item.GetRank(),
|
||||
RelationshipID: item.GetRelationshipId(),
|
||||
RelationType: item.GetRelationType(),
|
||||
Score: item.GetScore(),
|
||||
UserA: cpWeeklyRankUserFromProto(item.GetUserA()),
|
||||
UserB: cpWeeklyRankUserFromProto(item.GetUserB()),
|
||||
FormedAtMS: item.GetFormedAtMs(),
|
||||
UpdatedAtMS: item.GetUpdatedAtMs(),
|
||||
FirstScoredAtMS: item.GetFirstScoredAtMs(),
|
||||
LastScoredAtMS: item.GetLastScoredAtMs(),
|
||||
})
|
||||
}
|
||||
return entries, nil
|
||||
}
|
||||
|
||||
func cpWeeklyRankUserFromProto(user *userv1.CPIntimacyLeaderboardUser) domain.User {
|
||||
if user == nil {
|
||||
return domain.User{}
|
||||
}
|
||||
return domain.User{
|
||||
UserID: user.GetUserId(),
|
||||
DisplayUserID: user.GetDisplayUserId(),
|
||||
Username: user.GetUsername(),
|
||||
Avatar: user.GetAvatar(),
|
||||
}
|
||||
}
|
||||
@ -1,140 +0,0 @@
|
||||
package agencyopening
|
||||
|
||||
const (
|
||||
ActivityCode = "agency_opening"
|
||||
|
||||
StatusDraft = "draft"
|
||||
StatusActive = "active"
|
||||
StatusDisabled = "disabled"
|
||||
StatusSettling = "settling"
|
||||
StatusSettled = "settled"
|
||||
|
||||
ApplicationStatusApplied = "applied"
|
||||
ApplicationStatusPending = "pending"
|
||||
ApplicationStatusGranted = "granted"
|
||||
ApplicationStatusFailed = "failed"
|
||||
|
||||
EventStatusConsumed = "consumed"
|
||||
EventStatusDuplicate = "duplicate"
|
||||
EventStatusSkipped = "skipped"
|
||||
|
||||
GrantSourceAgencyOpening = "agency_opening"
|
||||
)
|
||||
|
||||
// Reward 是代理开业流水档位;RankNo 只保留为档位展示顺序和旧字段兼容,结算命中只看 ThresholdCoinSpent。
|
||||
type Reward struct {
|
||||
AppCode string
|
||||
CycleID string
|
||||
RankNo int32
|
||||
ThresholdCoinSpent int64
|
||||
RewardCoinAmount int64
|
||||
CreatedAtMS int64
|
||||
UpdatedAtMS int64
|
||||
}
|
||||
|
||||
// Cycle is the UTC activity window configured by admin.
|
||||
type Cycle struct {
|
||||
AppCode string
|
||||
CycleID string
|
||||
ActivityCode string
|
||||
Title string
|
||||
Status string
|
||||
StartMS int64
|
||||
EndMS int64
|
||||
MinHostCount int32
|
||||
MaxAgencyAgeDays int32
|
||||
CreatedByAdminID int64
|
||||
UpdatedByAdminID int64
|
||||
SettledAtMS int64
|
||||
CreatedAtMS int64
|
||||
UpdatedAtMS int64
|
||||
Rewards []Reward
|
||||
}
|
||||
|
||||
// CycleCommand carries an admin mutation after transport normalization.
|
||||
type CycleCommand struct {
|
||||
Cycle Cycle
|
||||
OperatorAdminID int64
|
||||
RequireNewRecord bool
|
||||
}
|
||||
|
||||
// ListQuery filters admin list reads.
|
||||
type ListQuery struct {
|
||||
Status string
|
||||
StartMS int64
|
||||
EndMS int64
|
||||
Page int32
|
||||
PageSize int32
|
||||
}
|
||||
|
||||
// Application is both the submitted application record and the settlement fact.
|
||||
type Application struct {
|
||||
AppCode string
|
||||
ApplicationID string
|
||||
CycleID string
|
||||
AgencyID int64
|
||||
AgencyOwnerUserID int64
|
||||
AgencyName string
|
||||
HostCount int32
|
||||
AgencyCreatedAtMS int64
|
||||
Status string
|
||||
ScoreCoinAmount int64
|
||||
RewardRankNo int32
|
||||
RewardThresholdCoin int64
|
||||
RewardCoinAmount int64
|
||||
WalletCommandID string
|
||||
WalletTransactionID string
|
||||
FailureReason string
|
||||
AppliedAtMS int64
|
||||
GrantedAtMS int64
|
||||
CreatedAtMS int64
|
||||
UpdatedAtMS int64
|
||||
}
|
||||
|
||||
// ApplicationQuery filters admin and H5 leaderboard reads.
|
||||
type ApplicationQuery struct {
|
||||
CycleID string
|
||||
Status string
|
||||
AgencyID int64
|
||||
AgencyOwnerUserID int64
|
||||
Page int32
|
||||
PageSize int32
|
||||
}
|
||||
|
||||
// AgencySnapshot is read from user-service at apply time so the activity record remains auditable.
|
||||
type AgencySnapshot struct {
|
||||
AgencyID int64
|
||||
OwnerUserID int64
|
||||
Name string
|
||||
Status string
|
||||
HostCount int32
|
||||
AgencyCreatedAtMS int64
|
||||
}
|
||||
|
||||
// Qualification combines the current cycle, agency snapshot and application state for H5.
|
||||
type Qualification struct {
|
||||
Cycle Cycle
|
||||
Application Application
|
||||
Agency AgencySnapshot
|
||||
Enabled bool
|
||||
Eligible bool
|
||||
Joined bool
|
||||
IneligibleReason string
|
||||
ServerTimeMS int64
|
||||
}
|
||||
|
||||
// GiftEvent is the room-service gift fact projected into the applicant room owner's opening revenue.
|
||||
type GiftEvent struct {
|
||||
EventID string
|
||||
RoomID string
|
||||
RoomOwnerUserID int64
|
||||
TargetUserID int64
|
||||
CoinSpent int64
|
||||
OccurredAtMS int64
|
||||
}
|
||||
|
||||
// EventResult describes idempotent revenue consumption for one room event.
|
||||
type EventResult struct {
|
||||
EventID string
|
||||
Status string
|
||||
}
|
||||
@ -1,85 +0,0 @@
|
||||
package cpweeklyrank
|
||||
|
||||
const (
|
||||
ActivityCode = "cp_weekly_rank"
|
||||
|
||||
SettlementStatusPending = "pending"
|
||||
SettlementStatusRunning = "running"
|
||||
SettlementStatusGranted = "granted"
|
||||
SettlementStatusFailed = "failed"
|
||||
|
||||
GrantSourceCPWeeklyRank = "cp_weekly_rank"
|
||||
)
|
||||
|
||||
// Reward maps one CP weekly rank to the resource group granted to both relationship users.
|
||||
type Reward struct {
|
||||
AppCode string
|
||||
RankNo int32
|
||||
ResourceGroupID int64
|
||||
CreatedAtMS int64
|
||||
UpdatedAtMS int64
|
||||
}
|
||||
|
||||
// Config is the activity-service owned switch and reward definition for CP weekly rank.
|
||||
type Config struct {
|
||||
AppCode string
|
||||
ActivityCode string
|
||||
Enabled bool
|
||||
RelationType string
|
||||
TopCount int32
|
||||
UpdatedByAdminID int64
|
||||
CreatedAtMS int64
|
||||
UpdatedAtMS int64
|
||||
Rewards []Reward
|
||||
}
|
||||
|
||||
// User is a user-service profile snapshot included in leaderboard reads.
|
||||
type User struct {
|
||||
UserID int64
|
||||
DisplayUserID string
|
||||
Username string
|
||||
Avatar string
|
||||
}
|
||||
|
||||
// Entry is one relationship score inside a concrete UTC week window.
|
||||
type Entry struct {
|
||||
Rank int64
|
||||
RelationshipID string
|
||||
RelationType string
|
||||
Score int64
|
||||
UserA User
|
||||
UserB User
|
||||
FormedAtMS int64
|
||||
UpdatedAtMS int64
|
||||
FirstScoredAtMS int64
|
||||
LastScoredAtMS int64
|
||||
}
|
||||
|
||||
// Settlement is one resource-group grant job; every winning relationship creates two rows, one for each user.
|
||||
type Settlement struct {
|
||||
AppCode string
|
||||
SettlementID string
|
||||
PeriodStartMS int64
|
||||
PeriodEndMS int64
|
||||
RankNo int32
|
||||
RelationshipID string
|
||||
UserID int64
|
||||
Score int64
|
||||
ResourceGroupID int64
|
||||
WalletCommandID string
|
||||
WalletGrantID string
|
||||
Status string
|
||||
FailureReason string
|
||||
AttemptCount int32
|
||||
CreatedAtMS int64
|
||||
UpdatedAtMS int64
|
||||
}
|
||||
|
||||
// Status is the H5 read model for current week leaderboard and configured rewards.
|
||||
type Status struct {
|
||||
Config Config
|
||||
Entries []Entry
|
||||
PeriodStartMS int64
|
||||
PeriodEndMS int64
|
||||
ServerTimeMS int64
|
||||
}
|
||||
@ -1,423 +0,0 @@
|
||||
package agencyopening
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/protobuf/proto"
|
||||
roomeventsv1 "hyapp.local/api/proto/events/room/v1"
|
||||
roomv1 "hyapp.local/api/proto/room/v1"
|
||||
walletv1 "hyapp.local/api/proto/wallet/v1"
|
||||
"hyapp/pkg/appcode"
|
||||
"hyapp/pkg/logx"
|
||||
"hyapp/pkg/xerr"
|
||||
domain "hyapp/services/activity-service/internal/domain/agencyopening"
|
||||
)
|
||||
|
||||
const rewardReason = "agency opening reward"
|
||||
|
||||
type Repository interface {
|
||||
ListAgencyOpeningCycles(ctx context.Context, query domain.ListQuery) ([]domain.Cycle, int64, error)
|
||||
GetAgencyOpeningCycle(ctx context.Context, cycleID string) (domain.Cycle, error)
|
||||
UpsertAgencyOpeningCycle(ctx context.Context, command domain.CycleCommand, nowMS int64) (domain.Cycle, bool, error)
|
||||
SetAgencyOpeningCycleStatus(ctx context.Context, cycleID string, status string, operatorAdminID int64, nowMS int64) (domain.Cycle, error)
|
||||
FindCurrentAgencyOpeningCycle(ctx context.Context, nowMS int64) (domain.Cycle, bool, error)
|
||||
GetAgencyOpeningApplicationByOwner(ctx context.Context, ownerUserID int64) (domain.Application, bool, error)
|
||||
CreateAgencyOpeningApplication(ctx context.Context, cycle domain.Cycle, agency domain.AgencySnapshot, nowMS int64) (domain.Application, bool, error)
|
||||
ListAgencyOpeningApplications(ctx context.Context, query domain.ApplicationQuery) ([]domain.Application, int64, error)
|
||||
ConsumeAgencyOpeningGiftEvent(ctx context.Context, event domain.GiftEvent, nowMS int64) (domain.EventResult, error)
|
||||
ClaimDueAgencyOpeningCycles(ctx context.Context, workerID string, nowMS int64, lockTTL time.Duration, batchSize int32) ([]domain.Cycle, error)
|
||||
PrepareAgencyOpeningSettlements(ctx context.Context, cycle domain.Cycle, nowMS int64) ([]domain.Application, error)
|
||||
MarkAgencyOpeningApplicationGranted(ctx context.Context, applicationID string, walletTransactionID string, grantedAtMS int64) (domain.Application, error)
|
||||
MarkAgencyOpeningApplicationFailed(ctx context.Context, applicationID string, reason string, nowMS int64) error
|
||||
FinishAgencyOpeningCycleIfComplete(ctx context.Context, cycleID string, nowMS int64) (bool, error)
|
||||
}
|
||||
|
||||
type AgencySource interface {
|
||||
ResolveOwnerAgency(ctx context.Context, userID int64) (domain.AgencySnapshot, bool, error)
|
||||
}
|
||||
|
||||
type WalletClient interface {
|
||||
CreditAgencyOpeningReward(ctx context.Context, req *walletv1.CreditAgencyOpeningRewardRequest, opts ...grpc.CallOption) (*walletv1.CreditAgencyOpeningRewardResponse, error)
|
||||
}
|
||||
|
||||
type RoomQueryClient interface {
|
||||
AdminGetRoom(ctx context.Context, req *roomv1.AdminGetRoomRequest, opts ...grpc.CallOption) (*roomv1.AdminGetRoomResponse, error)
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
repository Repository
|
||||
agency AgencySource
|
||||
room RoomQueryClient
|
||||
wallet WalletClient
|
||||
now func() time.Time
|
||||
}
|
||||
|
||||
func New(repository Repository, wallet WalletClient, agency AgencySource, room RoomQueryClient) *Service {
|
||||
return &Service{repository: repository, wallet: wallet, agency: agency, room: room, now: time.Now}
|
||||
}
|
||||
|
||||
func (s *Service) ListCycles(ctx context.Context, query domain.ListQuery) ([]domain.Cycle, int64, error) {
|
||||
if err := s.requireRepository(); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
query.Status = normalizeStatus(query.Status)
|
||||
return s.repository.ListAgencyOpeningCycles(ctx, query)
|
||||
}
|
||||
|
||||
func (s *Service) GetCycle(ctx context.Context, cycleID string) (domain.Cycle, error) {
|
||||
if err := s.requireRepository(); err != nil {
|
||||
return domain.Cycle{}, err
|
||||
}
|
||||
cycleID = strings.TrimSpace(cycleID)
|
||||
if cycleID == "" {
|
||||
return domain.Cycle{}, xerr.New(xerr.InvalidArgument, "cycle_id is required")
|
||||
}
|
||||
return s.repository.GetAgencyOpeningCycle(ctx, cycleID)
|
||||
}
|
||||
|
||||
func (s *Service) CreateCycle(ctx context.Context, command domain.CycleCommand) (domain.Cycle, bool, error) {
|
||||
command.RequireNewRecord = true
|
||||
return s.upsertCycle(ctx, command)
|
||||
}
|
||||
|
||||
func (s *Service) UpdateCycle(ctx context.Context, command domain.CycleCommand) (domain.Cycle, bool, error) {
|
||||
command.RequireNewRecord = false
|
||||
return s.upsertCycle(ctx, command)
|
||||
}
|
||||
|
||||
func (s *Service) SetCycleStatus(ctx context.Context, cycleID string, status string, operatorAdminID int64) (domain.Cycle, error) {
|
||||
if err := s.requireRepository(); err != nil {
|
||||
return domain.Cycle{}, err
|
||||
}
|
||||
status = normalizeStatus(status)
|
||||
if !validStatus(status) || status == domain.StatusSettling {
|
||||
return domain.Cycle{}, xerr.New(xerr.InvalidArgument, "status is invalid")
|
||||
}
|
||||
if operatorAdminID <= 0 {
|
||||
return domain.Cycle{}, xerr.New(xerr.InvalidArgument, "operator_admin_id is required")
|
||||
}
|
||||
return s.repository.SetAgencyOpeningCycleStatus(ctx, strings.TrimSpace(cycleID), status, operatorAdminID, s.now().UTC().UnixMilli())
|
||||
}
|
||||
|
||||
func (s *Service) GetStatus(ctx context.Context, userID int64) (domain.Qualification, error) {
|
||||
if err := s.requireRepository(); err != nil {
|
||||
return domain.Qualification{}, err
|
||||
}
|
||||
nowMS := s.now().UTC().UnixMilli()
|
||||
cycle, exists, err := s.repository.FindCurrentAgencyOpeningCycle(ctx, nowMS)
|
||||
if err != nil {
|
||||
return domain.Qualification{}, err
|
||||
}
|
||||
result := domain.Qualification{Cycle: cycle, Enabled: exists, ServerTimeMS: nowMS}
|
||||
if !exists {
|
||||
result.IneligibleReason = "activity_disabled"
|
||||
return result, nil
|
||||
}
|
||||
agency, ok, err := s.resolveOwnerAgency(ctx, userID)
|
||||
if err != nil {
|
||||
return domain.Qualification{}, err
|
||||
}
|
||||
result.Agency = agency
|
||||
if !ok {
|
||||
result.IneligibleReason = "active_agency_owner_required"
|
||||
return result, nil
|
||||
}
|
||||
application, joined, err := s.repository.GetAgencyOpeningApplicationByOwner(ctx, agency.OwnerUserID)
|
||||
if err != nil {
|
||||
return domain.Qualification{}, err
|
||||
}
|
||||
result.Application = application
|
||||
result.Joined = joined
|
||||
result.Eligible, result.IneligibleReason = eligible(cycle, agency, nowMS)
|
||||
if joined {
|
||||
result.Eligible = false
|
||||
result.IneligibleReason = "already_applied"
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *Service) Apply(ctx context.Context, userID int64, commandID string) (domain.Application, bool, error) {
|
||||
if err := s.requireRepository(); err != nil {
|
||||
return domain.Application{}, false, err
|
||||
}
|
||||
if strings.TrimSpace(commandID) == "" {
|
||||
return domain.Application{}, false, xerr.New(xerr.InvalidArgument, "command_id is required")
|
||||
}
|
||||
qualification, err := s.GetStatus(ctx, userID)
|
||||
if err != nil {
|
||||
return domain.Application{}, false, err
|
||||
}
|
||||
if !qualification.Enabled {
|
||||
return domain.Application{}, false, xerr.New(xerr.Conflict, "activity is not active")
|
||||
}
|
||||
if qualification.Joined {
|
||||
return qualification.Application, false, nil
|
||||
}
|
||||
if !qualification.Eligible {
|
||||
return domain.Application{}, false, xerr.New(xerr.Conflict, qualification.IneligibleReason)
|
||||
}
|
||||
return s.repository.CreateAgencyOpeningApplication(ctx, qualification.Cycle, qualification.Agency, s.now().UTC().UnixMilli())
|
||||
}
|
||||
|
||||
func (s *Service) ListApplications(ctx context.Context, query domain.ApplicationQuery) ([]domain.Application, int64, error) {
|
||||
if err := s.requireRepository(); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
query.Status = normalizeApplicationStatus(query.Status)
|
||||
return s.repository.ListAgencyOpeningApplications(ctx, query)
|
||||
}
|
||||
|
||||
func (s *Service) HandleGiftEvent(ctx context.Context, event domain.GiftEvent) (int32, error) {
|
||||
if err := s.requireRepository(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if event.EventID == "" || event.RoomOwnerUserID <= 0 || event.CoinSpent <= 0 {
|
||||
return 0, nil
|
||||
}
|
||||
result, err := s.repository.ConsumeAgencyOpeningGiftEvent(ctx, event, s.now().UTC().UnixMilli())
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if result.Status == domain.EventStatusConsumed {
|
||||
return 1, nil
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// HandleRoomEvent consumes committed room gift facts and projects revenue to the applicant room owner.
|
||||
// 机器人礼物、非礼物事件和无扣费金额事件都跳过;房间 owner 从 room-service 当前事实解析,保证“开业流水”只统计申请人自己房间内发生的真实扣费。
|
||||
func (s *Service) HandleRoomEvent(ctx context.Context, envelope *roomeventsv1.EventEnvelope) (int32, error) {
|
||||
if s == nil || envelope == nil || envelope.GetEventType() != "RoomGiftSent" {
|
||||
return 0, nil
|
||||
}
|
||||
var gift roomeventsv1.RoomGiftSent
|
||||
if err := proto.Unmarshal(envelope.GetBody(), &gift); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
roomID := strings.TrimSpace(envelope.GetRoomId())
|
||||
if gift.GetIsRobotGift() || roomID == "" || gift.GetCoinSpent() <= 0 {
|
||||
return 0, nil
|
||||
}
|
||||
eventCtx := appcode.WithContext(ctx, envelope.GetAppCode())
|
||||
ownerUserID, err := s.fetchRoomOwner(eventCtx, roomID)
|
||||
if err != nil || ownerUserID <= 0 {
|
||||
return 0, err
|
||||
}
|
||||
return s.HandleGiftEvent(eventCtx, domain.GiftEvent{
|
||||
EventID: strings.TrimSpace(envelope.GetEventId()),
|
||||
RoomID: roomID,
|
||||
RoomOwnerUserID: ownerUserID,
|
||||
TargetUserID: gift.GetTargetUserId(),
|
||||
CoinSpent: gift.GetCoinSpent(),
|
||||
OccurredAtMS: envelope.GetOccurredAtMs(),
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Service) ProcessSettlementBatch(ctx context.Context, runID string, workerID string, batchSize int32, lockTTL time.Duration) (claimed int32, processed int32, success int32, failure int32, hasMore bool, err error) {
|
||||
if err := s.requireRepository(); err != nil {
|
||||
return 0, 0, 0, 0, false, err
|
||||
}
|
||||
if batchSize <= 0 {
|
||||
batchSize = 50
|
||||
}
|
||||
if lockTTL <= 0 {
|
||||
lockTTL = 30 * time.Second
|
||||
}
|
||||
if strings.TrimSpace(workerID) == "" {
|
||||
workerID = "activity-agency-opening"
|
||||
}
|
||||
cycles, err := s.repository.ClaimDueAgencyOpeningCycles(ctx, workerID, s.now().UTC().UnixMilli(), lockTTL, batchSize)
|
||||
if err != nil {
|
||||
return 0, 0, 0, 0, false, err
|
||||
}
|
||||
for _, cycle := range cycles {
|
||||
settlements, prepareErr := s.repository.PrepareAgencyOpeningSettlements(ctx, cycle, s.now().UTC().UnixMilli())
|
||||
if prepareErr != nil {
|
||||
logx.Error(ctx, "agency_opening_prepare_settlements_failed", prepareErr, slog.String("cycle_id", cycle.CycleID), slog.String("run_id", runID))
|
||||
failure++
|
||||
continue
|
||||
}
|
||||
if len(settlements) == 0 {
|
||||
if _, finishErr := s.repository.FinishAgencyOpeningCycleIfComplete(ctx, cycle.CycleID, s.now().UTC().UnixMilli()); finishErr != nil {
|
||||
failure++
|
||||
continue
|
||||
}
|
||||
success++
|
||||
continue
|
||||
}
|
||||
for _, settlement := range settlements {
|
||||
processed++
|
||||
if _, grantErr := s.grantSettlement(ctx, settlement); grantErr != nil {
|
||||
logx.Error(ctx, "agency_opening_grant_failed", grantErr, slog.String("application_id", settlement.ApplicationID), slog.String("cycle_id", settlement.CycleID))
|
||||
failure++
|
||||
continue
|
||||
}
|
||||
success++
|
||||
}
|
||||
if _, finishErr := s.repository.FinishAgencyOpeningCycleIfComplete(ctx, cycle.CycleID, s.now().UTC().UnixMilli()); finishErr != nil {
|
||||
failure++
|
||||
}
|
||||
}
|
||||
return int32(len(cycles)), processed, success, failure, len(cycles) >= int(batchSize), nil
|
||||
}
|
||||
|
||||
func (s *Service) grantSettlement(ctx context.Context, settlement domain.Application) (domain.Application, error) {
|
||||
if settlement.Status == domain.ApplicationStatusGranted {
|
||||
return settlement, nil
|
||||
}
|
||||
if settlement.RewardCoinAmount <= 0 {
|
||||
nowMS := s.now().UTC().UnixMilli()
|
||||
return s.repository.MarkAgencyOpeningApplicationGranted(ctx, settlement.ApplicationID, "", nowMS)
|
||||
}
|
||||
if s.wallet == nil {
|
||||
err := xerr.New(xerr.Unavailable, "wallet client is not configured")
|
||||
_ = s.repository.MarkAgencyOpeningApplicationFailed(ctx, settlement.ApplicationID, xerr.MessageOf(err), s.now().UTC().UnixMilli())
|
||||
return domain.Application{}, err
|
||||
}
|
||||
resp, err := s.wallet.CreditAgencyOpeningReward(ctx, &walletv1.CreditAgencyOpeningRewardRequest{
|
||||
CommandId: settlement.WalletCommandID,
|
||||
AppCode: appcode.FromContext(ctx),
|
||||
TargetUserId: settlement.AgencyOwnerUserID,
|
||||
Amount: settlement.RewardCoinAmount,
|
||||
ApplicationId: settlement.ApplicationID,
|
||||
CycleId: settlement.CycleID,
|
||||
AgencyId: settlement.AgencyID,
|
||||
RankNo: settlement.RewardRankNo,
|
||||
ScoreCoins: settlement.ScoreCoinAmount,
|
||||
Reason: rewardReason,
|
||||
})
|
||||
if err != nil {
|
||||
_ = s.repository.MarkAgencyOpeningApplicationFailed(ctx, settlement.ApplicationID, xerr.MessageOf(err), s.now().UTC().UnixMilli())
|
||||
return domain.Application{}, err
|
||||
}
|
||||
return s.repository.MarkAgencyOpeningApplicationGranted(ctx, settlement.ApplicationID, resp.GetTransactionId(), resp.GetGrantedAtMs())
|
||||
}
|
||||
|
||||
func (s *Service) upsertCycle(ctx context.Context, command domain.CycleCommand) (domain.Cycle, bool, error) {
|
||||
if err := s.requireRepository(); err != nil {
|
||||
return domain.Cycle{}, false, err
|
||||
}
|
||||
command.Cycle.Status = normalizeStatus(command.Cycle.Status)
|
||||
if err := validateCycle(command.Cycle, command.OperatorAdminID); err != nil {
|
||||
return domain.Cycle{}, false, err
|
||||
}
|
||||
return s.repository.UpsertAgencyOpeningCycle(ctx, command, s.now().UTC().UnixMilli())
|
||||
}
|
||||
|
||||
func (s *Service) resolveOwnerAgency(ctx context.Context, userID int64) (domain.AgencySnapshot, bool, error) {
|
||||
if s.agency == nil {
|
||||
return domain.AgencySnapshot{}, false, xerr.New(xerr.Unavailable, "agency source is not configured")
|
||||
}
|
||||
return s.agency.ResolveOwnerAgency(ctx, userID)
|
||||
}
|
||||
|
||||
func (s *Service) fetchRoomOwner(ctx context.Context, roomID string) (int64, error) {
|
||||
if s.room == nil {
|
||||
return 0, xerr.New(xerr.Unavailable, "room query client is not configured")
|
||||
}
|
||||
roomID = strings.TrimSpace(roomID)
|
||||
if roomID == "" {
|
||||
return 0, xerr.New(xerr.InvalidArgument, "room_id is required")
|
||||
}
|
||||
resp, err := s.room.AdminGetRoom(ctx, &roomv1.AdminGetRoomRequest{
|
||||
Meta: &roomv1.RequestMeta{
|
||||
// 事件没有携带 owner 快照,按 room_id 生成稳定 request_id,方便追踪某次流水为何命中或跳过申请人。
|
||||
RequestId: "agency-opening-room-owner:" + roomID,
|
||||
AppCode: appcode.FromContext(ctx),
|
||||
SentAtMs: s.now().UTC().UnixMilli(),
|
||||
},
|
||||
RoomId: roomID,
|
||||
})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return resp.GetRoom().GetOwnerUserId(), nil
|
||||
}
|
||||
|
||||
func (s *Service) requireRepository() error {
|
||||
if s == nil || s.repository == nil {
|
||||
return xerr.New(xerr.Unavailable, "agency opening repository is not configured")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateCycle(cycle domain.Cycle, operatorAdminID int64) error {
|
||||
if strings.TrimSpace(cycle.Title) == "" {
|
||||
return xerr.New(xerr.InvalidArgument, "title is required")
|
||||
}
|
||||
if !validStatus(cycle.Status) || cycle.Status == domain.StatusSettling || cycle.Status == domain.StatusSettled {
|
||||
return xerr.New(xerr.InvalidArgument, "status is invalid")
|
||||
}
|
||||
if cycle.StartMS <= 0 || cycle.EndMS <= cycle.StartMS {
|
||||
return xerr.New(xerr.InvalidArgument, "cycle period is invalid")
|
||||
}
|
||||
if cycle.MinHostCount <= 0 {
|
||||
return xerr.New(xerr.InvalidArgument, "min_host_count must be positive")
|
||||
}
|
||||
if cycle.MaxAgencyAgeDays < 0 {
|
||||
return xerr.New(xerr.InvalidArgument, "max_agency_age_days must not be negative")
|
||||
}
|
||||
if len(cycle.Rewards) == 0 {
|
||||
return xerr.New(xerr.InvalidArgument, "reward tiers are required")
|
||||
}
|
||||
seen := map[int32]bool{}
|
||||
previousThreshold := int64(0)
|
||||
for _, reward := range cycle.Rewards {
|
||||
if reward.RankNo <= 0 || reward.ThresholdCoinSpent <= 0 || reward.RewardCoinAmount < 0 {
|
||||
return xerr.New(xerr.InvalidArgument, "reward tier threshold or amount is invalid")
|
||||
}
|
||||
if seen[reward.RankNo] {
|
||||
return xerr.New(xerr.InvalidArgument, "reward tier duplicated")
|
||||
}
|
||||
if previousThreshold > 0 && reward.ThresholdCoinSpent <= previousThreshold {
|
||||
return xerr.New(xerr.InvalidArgument, "reward tier threshold must increase")
|
||||
}
|
||||
seen[reward.RankNo] = true
|
||||
previousThreshold = reward.ThresholdCoinSpent
|
||||
}
|
||||
if operatorAdminID <= 0 {
|
||||
return xerr.New(xerr.InvalidArgument, "operator_admin_id is required")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func eligible(cycle domain.Cycle, agency domain.AgencySnapshot, nowMS int64) (bool, string) {
|
||||
if agency.Status != "active" {
|
||||
return false, "active_agency_required"
|
||||
}
|
||||
if agency.HostCount <= cycle.MinHostCount {
|
||||
return false, "host_count_not_enough"
|
||||
}
|
||||
if cycle.MaxAgencyAgeDays > 0 && agency.AgencyCreatedAtMS > 0 {
|
||||
maxAgeMS := int64(cycle.MaxAgencyAgeDays) * int64(24*time.Hour/time.Millisecond)
|
||||
if nowMS-agency.AgencyCreatedAtMS > maxAgeMS {
|
||||
return false, "agency_too_old"
|
||||
}
|
||||
}
|
||||
return true, ""
|
||||
}
|
||||
|
||||
func normalizeStatus(value string) string {
|
||||
value = strings.ToLower(strings.TrimSpace(value))
|
||||
if value == "" {
|
||||
return domain.StatusDraft
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func normalizeApplicationStatus(value string) string {
|
||||
return strings.ToLower(strings.TrimSpace(value))
|
||||
}
|
||||
|
||||
func validStatus(value string) bool {
|
||||
switch value {
|
||||
case domain.StatusDraft, domain.StatusActive, domain.StatusDisabled, domain.StatusSettling, domain.StatusSettled:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
@ -1,191 +0,0 @@
|
||||
package agencyopening
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/protobuf/proto"
|
||||
roomeventsv1 "hyapp.local/api/proto/events/room/v1"
|
||||
roomv1 "hyapp.local/api/proto/room/v1"
|
||||
walletv1 "hyapp.local/api/proto/wallet/v1"
|
||||
"hyapp/pkg/appcode"
|
||||
domain "hyapp/services/activity-service/internal/domain/agencyopening"
|
||||
)
|
||||
|
||||
func TestEligibleRequiresHostCountGreaterThanConfigured(t *testing.T) {
|
||||
cycle := domain.Cycle{MinHostCount: 10}
|
||||
agency := domain.AgencySnapshot{Status: "active", HostCount: 10}
|
||||
if ok, reason := eligible(cycle, agency, 1000); ok || reason != "host_count_not_enough" {
|
||||
t.Fatalf("host_count equal to threshold must be rejected, got ok=%v reason=%q", ok, reason)
|
||||
}
|
||||
agency.HostCount = 11
|
||||
if ok, reason := eligible(cycle, agency, 1000); !ok || reason != "" {
|
||||
t.Fatalf("host_count above threshold must be eligible, got ok=%v reason=%q", ok, reason)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetStatusRejectsOwnerAlreadyAppliedInAnyCycle(t *testing.T) {
|
||||
repo := &agencyOpeningFakeRepo{
|
||||
currentCycle: domain.Cycle{CycleID: "current", Status: domain.StatusActive, StartMS: 1, EndMS: 10000, MinHostCount: 10},
|
||||
hasCurrent: true,
|
||||
application: domain.Application{
|
||||
ApplicationID: "old-application",
|
||||
CycleID: "previous",
|
||||
AgencyOwnerUserID: 42,
|
||||
Status: domain.ApplicationStatusApplied,
|
||||
},
|
||||
hasApplication: true,
|
||||
}
|
||||
svc := New(repo, nil, agencyOpeningFakeAgencySource{
|
||||
snapshot: domain.AgencySnapshot{AgencyID: 7, OwnerUserID: 42, Status: "active", HostCount: 11},
|
||||
ok: true,
|
||||
}, nil)
|
||||
svc.now = func() time.Time { return time.UnixMilli(5000).UTC() }
|
||||
|
||||
status, err := svc.GetStatus(appcode.WithContext(context.Background(), "lalu"), 42)
|
||||
if err != nil {
|
||||
t.Fatalf("GetStatus returned error: %v", err)
|
||||
}
|
||||
if !status.Joined || status.Eligible || status.IneligibleReason != "already_applied" {
|
||||
t.Fatalf("expected already-applied owner to be blocked, got joined=%v eligible=%v reason=%q", status.Joined, status.Eligible, status.IneligibleReason)
|
||||
}
|
||||
if repo.ownerLookup != 42 {
|
||||
t.Fatalf("expected repository lookup by owner_user_id, got %d", repo.ownerLookup)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetStatusRequiresAgencyOwner(t *testing.T) {
|
||||
repo := &agencyOpeningFakeRepo{
|
||||
currentCycle: domain.Cycle{CycleID: "current", Status: domain.StatusActive, StartMS: 1, EndMS: 10000, MinHostCount: 10},
|
||||
hasCurrent: true,
|
||||
}
|
||||
svc := New(repo, nil, agencyOpeningFakeAgencySource{ok: false}, nil)
|
||||
svc.now = func() time.Time { return time.UnixMilli(5000).UTC() }
|
||||
|
||||
status, err := svc.GetStatus(appcode.WithContext(context.Background(), "lalu"), 42)
|
||||
if err != nil {
|
||||
t.Fatalf("GetStatus returned error: %v", err)
|
||||
}
|
||||
if status.Joined || status.Eligible || status.IneligibleReason != "active_agency_owner_required" {
|
||||
t.Fatalf("expected non-agency user to be blocked, got joined=%v eligible=%v reason=%q", status.Joined, status.Eligible, status.IneligibleReason)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleRoomEventScoresApplicantRoomOwner(t *testing.T) {
|
||||
repo := &agencyOpeningFakeRepo{consumeStatus: domain.EventStatusConsumed}
|
||||
room := &agencyOpeningFakeRoomClient{ownerUserID: 42}
|
||||
svc := New(repo, nil, nil, room)
|
||||
svc.now = func() time.Time { return time.UnixMilli(8000).UTC() }
|
||||
body, err := proto.Marshal(&roomeventsv1.RoomGiftSent{
|
||||
SenderUserId: 9,
|
||||
TargetUserId: 88,
|
||||
CoinSpent: 1200,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("marshal gift: %v", err)
|
||||
}
|
||||
count, err := svc.HandleRoomEvent(context.Background(), &roomeventsv1.EventEnvelope{
|
||||
EventId: "gift-event-1",
|
||||
RoomId: "room-1",
|
||||
EventType: "RoomGiftSent",
|
||||
OccurredAtMs: 7000,
|
||||
AppCode: "lalu",
|
||||
Body: body,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("HandleRoomEvent returned error: %v", err)
|
||||
}
|
||||
if count != 1 {
|
||||
t.Fatalf("expected one consumed event, got %d", count)
|
||||
}
|
||||
if room.roomID != "room-1" {
|
||||
t.Fatalf("expected room lookup by event room_id, got %q", room.roomID)
|
||||
}
|
||||
if repo.consumed.RoomOwnerUserID != 42 || repo.consumed.TargetUserID != 88 || repo.consumed.CoinSpent != 1200 {
|
||||
t.Fatalf("unexpected consumed event: %+v", repo.consumed)
|
||||
}
|
||||
}
|
||||
|
||||
type agencyOpeningFakeRepo struct {
|
||||
currentCycle domain.Cycle
|
||||
hasCurrent bool
|
||||
application domain.Application
|
||||
hasApplication bool
|
||||
ownerLookup int64
|
||||
consumeStatus string
|
||||
consumed domain.GiftEvent
|
||||
}
|
||||
|
||||
func (r *agencyOpeningFakeRepo) ListAgencyOpeningCycles(context.Context, domain.ListQuery) ([]domain.Cycle, int64, error) {
|
||||
return nil, 0, nil
|
||||
}
|
||||
func (r *agencyOpeningFakeRepo) GetAgencyOpeningCycle(context.Context, string) (domain.Cycle, error) {
|
||||
return domain.Cycle{}, nil
|
||||
}
|
||||
func (r *agencyOpeningFakeRepo) UpsertAgencyOpeningCycle(context.Context, domain.CycleCommand, int64) (domain.Cycle, bool, error) {
|
||||
return domain.Cycle{}, false, nil
|
||||
}
|
||||
func (r *agencyOpeningFakeRepo) SetAgencyOpeningCycleStatus(context.Context, string, string, int64, int64) (domain.Cycle, error) {
|
||||
return domain.Cycle{}, nil
|
||||
}
|
||||
func (r *agencyOpeningFakeRepo) FindCurrentAgencyOpeningCycle(context.Context, int64) (domain.Cycle, bool, error) {
|
||||
return r.currentCycle, r.hasCurrent, nil
|
||||
}
|
||||
func (r *agencyOpeningFakeRepo) GetAgencyOpeningApplicationByOwner(_ context.Context, ownerUserID int64) (domain.Application, bool, error) {
|
||||
r.ownerLookup = ownerUserID
|
||||
return r.application, r.hasApplication, nil
|
||||
}
|
||||
func (r *agencyOpeningFakeRepo) CreateAgencyOpeningApplication(context.Context, domain.Cycle, domain.AgencySnapshot, int64) (domain.Application, bool, error) {
|
||||
return domain.Application{}, false, nil
|
||||
}
|
||||
func (r *agencyOpeningFakeRepo) ListAgencyOpeningApplications(context.Context, domain.ApplicationQuery) ([]domain.Application, int64, error) {
|
||||
return nil, 0, nil
|
||||
}
|
||||
func (r *agencyOpeningFakeRepo) ConsumeAgencyOpeningGiftEvent(_ context.Context, event domain.GiftEvent, _ int64) (domain.EventResult, error) {
|
||||
r.consumed = event
|
||||
return domain.EventResult{EventID: event.EventID, Status: r.consumeStatus}, nil
|
||||
}
|
||||
func (r *agencyOpeningFakeRepo) ClaimDueAgencyOpeningCycles(context.Context, string, int64, time.Duration, int32) ([]domain.Cycle, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (r *agencyOpeningFakeRepo) PrepareAgencyOpeningSettlements(context.Context, domain.Cycle, int64) ([]domain.Application, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (r *agencyOpeningFakeRepo) MarkAgencyOpeningApplicationGranted(context.Context, string, string, int64) (domain.Application, error) {
|
||||
return domain.Application{}, nil
|
||||
}
|
||||
func (r *agencyOpeningFakeRepo) MarkAgencyOpeningApplicationFailed(context.Context, string, string, int64) error {
|
||||
return nil
|
||||
}
|
||||
func (r *agencyOpeningFakeRepo) FinishAgencyOpeningCycleIfComplete(context.Context, string, int64) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
type agencyOpeningFakeAgencySource struct {
|
||||
snapshot domain.AgencySnapshot
|
||||
ok bool
|
||||
}
|
||||
|
||||
func (s agencyOpeningFakeAgencySource) ResolveOwnerAgency(context.Context, int64) (domain.AgencySnapshot, bool, error) {
|
||||
return s.snapshot, s.ok, nil
|
||||
}
|
||||
|
||||
type agencyOpeningFakeRoomClient struct {
|
||||
ownerUserID int64
|
||||
roomID string
|
||||
}
|
||||
|
||||
func (c *agencyOpeningFakeRoomClient) AdminGetRoom(_ context.Context, req *roomv1.AdminGetRoomRequest, _ ...grpc.CallOption) (*roomv1.AdminGetRoomResponse, error) {
|
||||
c.roomID = req.GetRoomId()
|
||||
return &roomv1.AdminGetRoomResponse{
|
||||
Room: &roomv1.AdminRoomListItem{OwnerUserId: c.ownerUserID},
|
||||
}, nil
|
||||
}
|
||||
|
||||
type agencyOpeningFakeWalletClient struct{}
|
||||
|
||||
func (agencyOpeningFakeWalletClient) CreditAgencyOpeningReward(context.Context, *walletv1.CreditAgencyOpeningRewardRequest, ...grpc.CallOption) (*walletv1.CreditAgencyOpeningRewardResponse, error) {
|
||||
return &walletv1.CreditAgencyOpeningRewardResponse{}, nil
|
||||
}
|
||||
@ -323,9 +323,6 @@ func (s *Service) HandleRoomEvent(ctx context.Context, envelope *roomeventsv1.Ev
|
||||
if err := proto.Unmarshal(envelope.GetBody(), &gift); err != nil {
|
||||
return broadcastdomain.ConsumeRoomEventResult{}, err
|
||||
}
|
||||
if gift.GetIsRobotGift() {
|
||||
return result, nil
|
||||
}
|
||||
if gift.GetVisibleRegionId() <= 0 || !s.shouldBroadcastGift(&gift) {
|
||||
// visible_region_id 来自房间/主播可见区域,不能用客户端当前 IP 或本地时区推断。
|
||||
return result, nil
|
||||
|
||||
@ -1,297 +0,0 @@
|
||||
package cpweeklyrank
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
walletv1 "hyapp.local/api/proto/wallet/v1"
|
||||
"hyapp/pkg/appcode"
|
||||
"hyapp/pkg/logx"
|
||||
"hyapp/pkg/xerr"
|
||||
domain "hyapp/services/activity-service/internal/domain/cpweeklyrank"
|
||||
)
|
||||
|
||||
const cpWeeklyRankRewardReason = "cp weekly rank reward"
|
||||
|
||||
// Repository is the activity-service storage boundary for CP weekly rank config and settlement facts.
|
||||
type Repository interface {
|
||||
GetCPWeeklyRankConfig(ctx context.Context) (domain.Config, bool, error)
|
||||
UpdateCPWeeklyRankConfig(ctx context.Context, config domain.Config, nowMS int64) (domain.Config, error)
|
||||
ListCPWeeklyRankSettlements(ctx context.Context, periodStartMS int64, periodEndMS int64) ([]domain.Settlement, error)
|
||||
PrepareCPWeeklyRankSettlements(ctx context.Context, config domain.Config, entries []domain.Entry, periodStartMS int64, periodEndMS int64, nowMS int64) ([]domain.Settlement, error)
|
||||
ListPendingCPWeeklyRankSettlements(ctx context.Context, periodStartMS int64, periodEndMS int64, limit int) ([]domain.Settlement, error)
|
||||
MarkCPWeeklyRankSettlementGranted(ctx context.Context, settlementID string, walletGrantID string, nowMS int64) error
|
||||
MarkCPWeeklyRankSettlementFailed(ctx context.Context, settlementID string, reason string, nowMS int64) error
|
||||
}
|
||||
|
||||
// RankSource reads user-service owned CP weekly score snapshots; activity-service never scans user DB directly.
|
||||
type RankSource interface {
|
||||
ListCPWeeklyRankEntries(ctx context.Context, relationType string, startMS int64, endMS int64, limit int32) ([]domain.Entry, error)
|
||||
}
|
||||
|
||||
// WalletClient owns the external resource-group grant side effect.
|
||||
type WalletClient interface {
|
||||
GrantResourceGroup(ctx context.Context, req *walletv1.GrantResourceGroupRequest, opts ...grpc.CallOption) (*walletv1.ResourceGrantResponse, error)
|
||||
}
|
||||
|
||||
// Service owns CP weekly rank config, H5 reads and weekly reward settlement.
|
||||
type Service struct {
|
||||
repository Repository
|
||||
rankSource RankSource
|
||||
wallet WalletClient
|
||||
now func() time.Time
|
||||
}
|
||||
|
||||
func New(repository Repository, rankSource RankSource, wallet WalletClient) *Service {
|
||||
return &Service{repository: repository, rankSource: rankSource, wallet: wallet, now: time.Now}
|
||||
}
|
||||
|
||||
func (s *Service) SetClock(now func() time.Time) {
|
||||
if now != nil {
|
||||
s.now = now
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) GetConfig(ctx context.Context) (domain.Config, error) {
|
||||
if err := s.requireRepository(); err != nil {
|
||||
return domain.Config{}, err
|
||||
}
|
||||
config, exists, err := s.repository.GetCPWeeklyRankConfig(ctx)
|
||||
if err != nil {
|
||||
return domain.Config{}, err
|
||||
}
|
||||
if !exists {
|
||||
return defaultConfig(ctx), nil
|
||||
}
|
||||
return normalizeConfigForRead(config), nil
|
||||
}
|
||||
|
||||
func (s *Service) UpdateConfig(ctx context.Context, config domain.Config, operatorAdminID int64) (domain.Config, error) {
|
||||
if err := s.requireRepository(); err != nil {
|
||||
return domain.Config{}, err
|
||||
}
|
||||
if operatorAdminID <= 0 {
|
||||
return domain.Config{}, xerr.New(xerr.InvalidArgument, "operator_admin_id is required")
|
||||
}
|
||||
config.AppCode = appcode.FromContext(ctx)
|
||||
config.ActivityCode = domain.ActivityCode
|
||||
config.RelationType = "cp"
|
||||
config.UpdatedByAdminID = operatorAdminID
|
||||
config = normalizeConfigForWrite(config)
|
||||
if err := validateConfig(config); err != nil {
|
||||
return domain.Config{}, err
|
||||
}
|
||||
return s.repository.UpdateCPWeeklyRankConfig(ctx, config, s.now().UTC().UnixMilli())
|
||||
}
|
||||
|
||||
func (s *Service) GetStatus(ctx context.Context, limit int32, nowMS int64) (domain.Status, error) {
|
||||
if err := s.requireRepository(); err != nil {
|
||||
return domain.Status{}, err
|
||||
}
|
||||
if s.rankSource == nil {
|
||||
return domain.Status{}, xerr.New(xerr.Unavailable, "cp weekly rank source is not configured")
|
||||
}
|
||||
now := s.now().UTC()
|
||||
if nowMS > 0 {
|
||||
now = time.UnixMilli(nowMS).UTC()
|
||||
}
|
||||
start, end := WeekWindow(now)
|
||||
config, err := s.GetConfig(ctx)
|
||||
if err != nil {
|
||||
return domain.Status{}, err
|
||||
}
|
||||
result := domain.Status{
|
||||
Config: config,
|
||||
PeriodStartMS: start.UnixMilli(),
|
||||
PeriodEndMS: end.UnixMilli(),
|
||||
ServerTimeMS: now.UnixMilli(),
|
||||
}
|
||||
if !config.Enabled {
|
||||
return result, nil
|
||||
}
|
||||
if limit <= 0 || limit > config.TopCount {
|
||||
limit = config.TopCount
|
||||
}
|
||||
if limit <= 0 {
|
||||
limit = 3
|
||||
}
|
||||
entries, err := s.rankSource.ListCPWeeklyRankEntries(ctx, config.RelationType, result.PeriodStartMS, result.PeriodEndMS, limit)
|
||||
if err != nil {
|
||||
return domain.Status{}, err
|
||||
}
|
||||
result.Entries = entries
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *Service) ListSettlements(ctx context.Context, periodStartMS int64, periodEndMS int64) ([]domain.Settlement, error) {
|
||||
if err := s.requireRepository(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.repository.ListCPWeeklyRankSettlements(ctx, periodStartMS, periodEndMS)
|
||||
}
|
||||
|
||||
// ProcessSettlementBatch freezes the previous complete UTC week and grants each Top relationship reward to both users.
|
||||
func (s *Service) ProcessSettlementBatch(ctx context.Context, runID string, workerID string, batchSize int32) (claimed int32, processed int32, success int32, failure int32, hasMore bool, err error) {
|
||||
if err := s.requireRepository(); err != nil {
|
||||
return 0, 0, 0, 0, false, err
|
||||
}
|
||||
if s.rankSource == nil {
|
||||
return 0, 0, 0, 0, false, xerr.New(xerr.Unavailable, "cp weekly rank source is not configured")
|
||||
}
|
||||
if s.wallet == nil {
|
||||
return 0, 0, 0, 0, false, xerr.New(xerr.Unavailable, "wallet client is not configured")
|
||||
}
|
||||
if batchSize <= 0 {
|
||||
batchSize = 50
|
||||
}
|
||||
config, err := s.GetConfig(ctx)
|
||||
if err != nil {
|
||||
return 0, 0, 0, 0, false, err
|
||||
}
|
||||
if !config.Enabled || len(config.Rewards) == 0 {
|
||||
return 0, 0, 0, 0, false, nil
|
||||
}
|
||||
periodStart, periodEnd := PreviousWeekWindow(s.now().UTC())
|
||||
entries, err := s.rankSource.ListCPWeeklyRankEntries(ctx, config.RelationType, periodStart.UnixMilli(), periodEnd.UnixMilli(), config.TopCount)
|
||||
if err != nil {
|
||||
return 0, 0, 0, 0, false, err
|
||||
}
|
||||
settlements, err := s.repository.PrepareCPWeeklyRankSettlements(ctx, config, entries, periodStart.UnixMilli(), periodEnd.UnixMilli(), s.now().UTC().UnixMilli())
|
||||
if err != nil {
|
||||
return 0, 0, 0, 0, false, err
|
||||
}
|
||||
claimed = int32(len(settlements))
|
||||
pending, err := s.repository.ListPendingCPWeeklyRankSettlements(ctx, periodStart.UnixMilli(), periodEnd.UnixMilli(), int(batchSize))
|
||||
if err != nil {
|
||||
return claimed, 0, 0, 0, false, err
|
||||
}
|
||||
hasMore = len(pending) >= int(batchSize)
|
||||
for _, settlement := range pending {
|
||||
processed++
|
||||
// activity-service 只决定 CP 周榜获奖关系和资源组,wallet-service 负责展开资源组和写 grant 流水。
|
||||
// 失败时保持同一个 wallet_command_id 回到 failed,下一轮 cron 继续重试,防止网络抖动造成重复发奖。
|
||||
resp, grantErr := s.wallet.GrantResourceGroup(ctx, &walletv1.GrantResourceGroupRequest{
|
||||
CommandId: settlement.WalletCommandID,
|
||||
AppCode: appcode.FromContext(ctx),
|
||||
TargetUserId: settlement.UserID,
|
||||
GroupId: settlement.ResourceGroupID,
|
||||
Reason: cpWeeklyRankRewardReason,
|
||||
OperatorUserId: settlement.UserID,
|
||||
GrantSource: domain.GrantSourceCPWeeklyRank,
|
||||
})
|
||||
if grantErr != nil {
|
||||
logx.Error(ctx, "cp_weekly_rank_grant_resource_group_failed", grantErr, slog.String("settlement_id", settlement.SettlementID), slog.String("run_id", runID), slog.String("worker_id", workerID))
|
||||
failure++
|
||||
_ = s.repository.MarkCPWeeklyRankSettlementFailed(ctx, settlement.SettlementID, xerr.MessageOf(grantErr), s.now().UTC().UnixMilli())
|
||||
continue
|
||||
}
|
||||
walletGrantID := ""
|
||||
if resp.GetGrant() != nil {
|
||||
walletGrantID = resp.GetGrant().GetGrantId()
|
||||
}
|
||||
if err := s.repository.MarkCPWeeklyRankSettlementGranted(ctx, settlement.SettlementID, walletGrantID, s.now().UTC().UnixMilli()); err != nil {
|
||||
logx.Error(ctx, "cp_weekly_rank_mark_granted_failed", err, slog.String("settlement_id", settlement.SettlementID), slog.String("wallet_grant_id", walletGrantID))
|
||||
failure++
|
||||
continue
|
||||
}
|
||||
success++
|
||||
}
|
||||
return claimed, processed, success, failure, hasMore, nil
|
||||
}
|
||||
|
||||
func (s *Service) requireRepository() error {
|
||||
if s == nil || s.repository == nil {
|
||||
return xerr.New(xerr.Unavailable, "cp weekly rank repository is not configured")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func defaultConfig(ctx context.Context) domain.Config {
|
||||
return domain.Config{
|
||||
AppCode: appcode.FromContext(ctx),
|
||||
ActivityCode: domain.ActivityCode,
|
||||
Enabled: true,
|
||||
RelationType: "cp",
|
||||
TopCount: 3,
|
||||
}
|
||||
}
|
||||
|
||||
func normalizeConfigForRead(config domain.Config) domain.Config {
|
||||
config.ActivityCode = domain.ActivityCode
|
||||
config.RelationType = "cp"
|
||||
if config.TopCount <= 0 {
|
||||
config.TopCount = 3
|
||||
}
|
||||
config.Rewards = normalizeRewards(config.Rewards, config.TopCount)
|
||||
return config
|
||||
}
|
||||
|
||||
func normalizeConfigForWrite(config domain.Config) domain.Config {
|
||||
if config.TopCount <= 0 {
|
||||
config.TopCount = 3
|
||||
}
|
||||
if config.TopCount > 20 {
|
||||
config.TopCount = 20
|
||||
}
|
||||
config.Rewards = normalizeRewards(config.Rewards, config.TopCount)
|
||||
return config
|
||||
}
|
||||
|
||||
func normalizeRewards(rewards []domain.Reward, topCount int32) []domain.Reward {
|
||||
seen := make(map[int32]domain.Reward, len(rewards))
|
||||
for _, reward := range rewards {
|
||||
if reward.RankNo <= 0 || reward.RankNo > topCount || reward.ResourceGroupID <= 0 {
|
||||
continue
|
||||
}
|
||||
reward.RankNo = int32(reward.RankNo)
|
||||
seen[reward.RankNo] = reward
|
||||
}
|
||||
normalized := make([]domain.Reward, 0, len(seen))
|
||||
for _, reward := range seen {
|
||||
normalized = append(normalized, reward)
|
||||
}
|
||||
sort.Slice(normalized, func(i, j int) bool { return normalized[i].RankNo < normalized[j].RankNo })
|
||||
return normalized
|
||||
}
|
||||
|
||||
func validateConfig(config domain.Config) error {
|
||||
if config.Enabled && len(config.Rewards) == 0 {
|
||||
return xerr.New(xerr.InvalidArgument, "cp weekly rank rewards are required when enabled")
|
||||
}
|
||||
for _, reward := range config.Rewards {
|
||||
if reward.RankNo <= 0 || reward.RankNo > config.TopCount {
|
||||
return xerr.New(xerr.InvalidArgument, "cp weekly rank reward rank_no is invalid")
|
||||
}
|
||||
if reward.ResourceGroupID <= 0 {
|
||||
return xerr.New(xerr.InvalidArgument, "cp weekly rank reward resource_group_id is required")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WeekWindow returns the current UTC week [Monday 00:00, next Monday 00:00).
|
||||
func WeekWindow(now time.Time) (time.Time, time.Time) {
|
||||
dayStart := time.Date(now.UTC().Year(), now.UTC().Month(), now.UTC().Day(), 0, 0, 0, 0, time.UTC)
|
||||
weekday := int(dayStart.Weekday())
|
||||
if weekday == 0 {
|
||||
weekday = 7
|
||||
}
|
||||
start := dayStart.AddDate(0, 0, 1-weekday)
|
||||
return start, start.AddDate(0, 0, 7)
|
||||
}
|
||||
|
||||
// PreviousWeekWindow returns the complete UTC week immediately before now.
|
||||
func PreviousWeekWindow(now time.Time) (time.Time, time.Time) {
|
||||
currentStart, _ := WeekWindow(now)
|
||||
previousStart := currentStart.AddDate(0, 0, -7)
|
||||
return previousStart, currentStart
|
||||
}
|
||||
|
||||
func WalletCommandID(periodStartMS int64, rankNo int32, relationshipID string, userID int64) string {
|
||||
return fmt.Sprintf("cp_weekly_rank:%d:%d:%s:%d", periodStartMS, rankNo, strings.TrimSpace(relationshipID), userID)
|
||||
}
|
||||
@ -257,9 +257,6 @@ func (s *Service) HandleRoomEvent(ctx context.Context, envelope *roomeventsv1.Ev
|
||||
if err := proto.Unmarshal(envelope.GetBody(), &gift); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if gift.GetIsRobotGift() {
|
||||
return 0, nil
|
||||
}
|
||||
if gift.GetSenderUserId() <= 0 || gift.GetTargetUserId() <= 0 || gift.GetGiftValue() <= 0 {
|
||||
return 0, xerr.New(xerr.InvalidArgument, "room gift event is incomplete")
|
||||
}
|
||||
|
||||
@ -1,265 +0,0 @@
|
||||
package message
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"hyapp/pkg/xerr"
|
||||
messagedomain "hyapp/services/activity-service/internal/domain/message"
|
||||
)
|
||||
|
||||
const defaultNoticeProducer = "activity-service"
|
||||
|
||||
// NoticeCommand is the common producer-facing command for one user-visible system/activity message.
|
||||
type NoticeCommand struct {
|
||||
TargetUserID int64
|
||||
Producer string
|
||||
ProducerEventID string
|
||||
ProducerEventType string
|
||||
AggregateType string
|
||||
AggregateID string
|
||||
TemplateID string
|
||||
TemplateVersion string
|
||||
Title string
|
||||
Summary string
|
||||
Body string
|
||||
IconURL string
|
||||
ImageURL string
|
||||
ActionType string
|
||||
ActionParam string
|
||||
Priority int32
|
||||
SentAtMS int64
|
||||
ExpireAtMS int64
|
||||
Metadata map[string]any
|
||||
MetadataJSON string
|
||||
}
|
||||
|
||||
// NoticeResult keeps the inbox row and the idempotent create flag together.
|
||||
type NoticeResult struct {
|
||||
Message messagedomain.InboxMessage
|
||||
Created bool
|
||||
}
|
||||
|
||||
// NoticeFanoutCommand is the common command for background system/activity broadcasts.
|
||||
type NoticeFanoutCommand struct {
|
||||
CommandID string
|
||||
MessageType string
|
||||
TargetScope string
|
||||
TargetUserID int64
|
||||
UserIDs []int64
|
||||
RegionID int64
|
||||
Country string
|
||||
ProducerEventType string
|
||||
AggregateType string
|
||||
AggregateID string
|
||||
TemplateID string
|
||||
TemplateVersion string
|
||||
Title string
|
||||
Summary string
|
||||
Body string
|
||||
IconURL string
|
||||
ImageURL string
|
||||
ActionType string
|
||||
ActionParam string
|
||||
Priority int32
|
||||
SentAtMS int64
|
||||
ExpireAtMS int64
|
||||
Metadata map[string]any
|
||||
MetadataJSON string
|
||||
BatchSize int32
|
||||
CreatedBy string
|
||||
}
|
||||
|
||||
// CreateSystemNotice is the small public helper for account, wallet, room and application messages.
|
||||
func (s *Service) CreateSystemNotice(ctx context.Context, cmd NoticeCommand) (NoticeResult, error) {
|
||||
return s.CreateNotice(ctx, messagedomain.SectionSystem, cmd)
|
||||
}
|
||||
|
||||
// CreateActivityNotice is the small public helper for activity, reward and campaign messages.
|
||||
func (s *Service) CreateActivityNotice(ctx context.Context, cmd NoticeCommand) (NoticeResult, error) {
|
||||
return s.CreateNotice(ctx, messagedomain.SectionActivity, cmd)
|
||||
}
|
||||
|
||||
// CreateNotice normalizes producer defaults and metadata before delegating to the inbox owner.
|
||||
func (s *Service) CreateNotice(ctx context.Context, messageType string, cmd NoticeCommand) (NoticeResult, error) {
|
||||
metadataJSON, err := noticeMetadataJSON(cmd.Metadata, cmd.MetadataJSON)
|
||||
if err != nil {
|
||||
return NoticeResult{}, err
|
||||
}
|
||||
// The existing CreateInboxMessage method is still the only place that writes user_inbox_messages.
|
||||
// This helper only narrows the public call shape so producers use the same idempotency and content rules.
|
||||
message, created, err := s.CreateInboxMessage(ctx, messagedomain.CreateInput{
|
||||
TargetUserID: cmd.TargetUserID,
|
||||
Producer: firstNoticeText(cmd.Producer, defaultNoticeProducer),
|
||||
ProducerEventID: cmd.ProducerEventID,
|
||||
ProducerEventType: cmd.ProducerEventType,
|
||||
MessageType: messageType,
|
||||
AggregateType: cmd.AggregateType,
|
||||
AggregateID: cmd.AggregateID,
|
||||
TemplateID: cmd.TemplateID,
|
||||
TemplateVersion: cmd.TemplateVersion,
|
||||
Title: cmd.Title,
|
||||
Summary: cmd.Summary,
|
||||
Body: cmd.Body,
|
||||
IconURL: cmd.IconURL,
|
||||
ImageURL: cmd.ImageURL,
|
||||
ActionType: cmd.ActionType,
|
||||
ActionParam: cmd.ActionParam,
|
||||
Priority: cmd.Priority,
|
||||
SentAtMS: cmd.SentAtMS,
|
||||
ExpireAtMS: cmd.ExpireAtMS,
|
||||
MetadataJSON: metadataJSON,
|
||||
})
|
||||
if err != nil {
|
||||
return NoticeResult{}, err
|
||||
}
|
||||
return NoticeResult{Message: message, Created: created}, nil
|
||||
}
|
||||
|
||||
// CreateNoticeFanout records a broadcast command for the fanout worker instead of looping in the caller.
|
||||
func (s *Service) CreateNoticeFanout(ctx context.Context, cmd NoticeFanoutCommand) (messagedomain.FanoutJob, bool, error) {
|
||||
messageType := normalizeSection(cmd.MessageType)
|
||||
if messageType == "" {
|
||||
messageType = messagedomain.SectionSystem
|
||||
}
|
||||
targetFilterJSON, err := noticeTargetFilterJSON(cmd)
|
||||
if err != nil {
|
||||
return messagedomain.FanoutJob{}, false, err
|
||||
}
|
||||
templateSnapshotJSON, err := noticeTemplateSnapshotJSON(cmd)
|
||||
if err != nil {
|
||||
return messagedomain.FanoutJob{}, false, err
|
||||
}
|
||||
// command_id is the broadcast idempotency key. Retrying the same admin/activity command returns the
|
||||
// existing job; changing payload under the same command_id is rejected by CreateFanoutJob.
|
||||
return s.CreateFanoutJob(ctx, messagedomain.CreateFanoutInput{
|
||||
CommandID: strings.TrimSpace(cmd.CommandID),
|
||||
MessageType: messageType,
|
||||
TargetScope: strings.TrimSpace(cmd.TargetScope),
|
||||
TargetFilterJSON: targetFilterJSON,
|
||||
TemplateSnapshotJSON: templateSnapshotJSON,
|
||||
BatchSize: cmd.BatchSize,
|
||||
CreatedBy: strings.TrimSpace(cmd.CreatedBy),
|
||||
})
|
||||
}
|
||||
|
||||
func noticeTargetFilterJSON(cmd NoticeFanoutCommand) (string, error) {
|
||||
scope := strings.TrimSpace(cmd.TargetScope)
|
||||
var payload map[string]any
|
||||
switch scope {
|
||||
case messagedomain.TargetScopeSingleUser:
|
||||
if cmd.TargetUserID <= 0 {
|
||||
return "", xerr.New(xerr.InvalidArgument, "target_user_id is required")
|
||||
}
|
||||
payload = map[string]any{"target_user_id": cmd.TargetUserID}
|
||||
case messagedomain.TargetScopeUserIDs:
|
||||
userIDs := positiveUniqueInt64s(cmd.UserIDs)
|
||||
if len(userIDs) == 0 {
|
||||
return "", xerr.New(xerr.InvalidArgument, "user_ids is required")
|
||||
}
|
||||
payload = map[string]any{"user_ids": userIDs}
|
||||
case messagedomain.TargetScopeRegion:
|
||||
if cmd.RegionID <= 0 {
|
||||
return "", xerr.New(xerr.InvalidArgument, "region_id is required")
|
||||
}
|
||||
payload = map[string]any{"region_id": cmd.RegionID}
|
||||
case messagedomain.TargetScopeCountry:
|
||||
country := strings.TrimSpace(cmd.Country)
|
||||
if country == "" {
|
||||
return "", xerr.New(xerr.InvalidArgument, "country is required")
|
||||
}
|
||||
payload = map[string]any{"country": country}
|
||||
case messagedomain.TargetScopeAllActiveUsers:
|
||||
payload = map[string]any{}
|
||||
default:
|
||||
return "", xerr.New(xerr.InvalidArgument, "target_scope is invalid")
|
||||
}
|
||||
encoded, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(encoded), nil
|
||||
}
|
||||
|
||||
func noticeTemplateSnapshotJSON(cmd NoticeFanoutCommand) (string, error) {
|
||||
metadataJSON, err := noticeMetadataJSON(cmd.Metadata, cmd.MetadataJSON)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
payload := map[string]any{
|
||||
"producer_event_type": strings.TrimSpace(cmd.ProducerEventType),
|
||||
"aggregate_type": strings.TrimSpace(cmd.AggregateType),
|
||||
"aggregate_id": strings.TrimSpace(cmd.AggregateID),
|
||||
"template_id": strings.TrimSpace(cmd.TemplateID),
|
||||
"template_version": strings.TrimSpace(cmd.TemplateVersion),
|
||||
"title": strings.TrimSpace(cmd.Title),
|
||||
"summary": strings.TrimSpace(cmd.Summary),
|
||||
"body": strings.TrimSpace(cmd.Body),
|
||||
"icon_url": strings.TrimSpace(cmd.IconURL),
|
||||
"image_url": strings.TrimSpace(cmd.ImageURL),
|
||||
"action_type": strings.TrimSpace(cmd.ActionType),
|
||||
"action_param": strings.TrimSpace(cmd.ActionParam),
|
||||
"priority": cmd.Priority,
|
||||
"sent_at_ms": cmd.SentAtMS,
|
||||
"expire_at_ms": cmd.ExpireAtMS,
|
||||
}
|
||||
if metadataJSON != "" {
|
||||
var parsed any
|
||||
if err := json.Unmarshal([]byte(metadataJSON), &parsed); err != nil {
|
||||
return "", xerr.New(xerr.InvalidArgument, "metadata_json is invalid")
|
||||
}
|
||||
payload["metadata_json"] = parsed
|
||||
}
|
||||
encoded, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(encoded), nil
|
||||
}
|
||||
|
||||
func noticeMetadataJSON(metadata map[string]any, metadataJSON string) (string, error) {
|
||||
metadataJSON = strings.TrimSpace(metadataJSON)
|
||||
if len(metadata) > 0 && metadataJSON != "" {
|
||||
return "", xerr.New(xerr.InvalidArgument, "metadata and metadata_json cannot both be set")
|
||||
}
|
||||
if metadataJSON != "" {
|
||||
if !json.Valid([]byte(metadataJSON)) {
|
||||
return "", xerr.New(xerr.InvalidArgument, "metadata_json is invalid")
|
||||
}
|
||||
return metadataJSON, nil
|
||||
}
|
||||
if len(metadata) == 0 {
|
||||
return "", nil
|
||||
}
|
||||
encoded, err := json.Marshal(metadata)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(encoded), nil
|
||||
}
|
||||
|
||||
func positiveUniqueInt64s(values []int64) []int64 {
|
||||
seen := make(map[int64]struct{}, len(values))
|
||||
out := make([]int64, 0, len(values))
|
||||
for _, value := range values {
|
||||
if value <= 0 {
|
||||
continue
|
||||
}
|
||||
if _, exists := seen[value]; exists {
|
||||
continue
|
||||
}
|
||||
seen[value] = struct{}{}
|
||||
out = append(out, value)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func firstNoticeText(values ...string) string {
|
||||
for _, value := range values {
|
||||
if trimmed := strings.TrimSpace(value); trimmed != "" {
|
||||
return trimmed
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@ -178,72 +178,6 @@ func TestCreateFanoutJobIsIdempotentAndDetectsCommandConflict(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoticeHelpersCreateInboxAndFanoutPayloads(t *testing.T) {
|
||||
ctx := appcode.WithContext(context.Background(), "lalu")
|
||||
repository := mysqltest.NewRepository(t)
|
||||
svc := messageservice.New(messageservice.Config{NodeID: "activity-a"}, repository)
|
||||
svc.SetClock(func() time.Time { return time.UnixMilli(1_800_000_025_000) })
|
||||
|
||||
notice, err := svc.CreateActivityNotice(ctx, messageservice.NoticeCommand{
|
||||
TargetUserID: 42,
|
||||
ProducerEventID: "activity-reward-42-1",
|
||||
ProducerEventType: "activity_reward",
|
||||
AggregateType: "activity",
|
||||
AggregateID: "act_01",
|
||||
Title: "活动奖励到账",
|
||||
Summary: "你的活动奖励已发放。",
|
||||
ActionType: "activity_detail",
|
||||
ActionParam: "activity_id=act_01",
|
||||
Metadata: map[string]any{"activity_id": "act_01"},
|
||||
})
|
||||
if err != nil || !notice.Created || notice.Message.MessageType != messagedomain.SectionActivity || notice.Message.Producer != "activity-service" {
|
||||
t.Fatalf("CreateActivityNotice failed: notice=%+v err=%v", notice, err)
|
||||
}
|
||||
|
||||
job, created, err := svc.CreateNoticeFanout(ctx, messageservice.NoticeFanoutCommand{
|
||||
CommandID: "admin-full-server-notice-1",
|
||||
MessageType: messagedomain.SectionSystem,
|
||||
TargetScope: messagedomain.TargetScopeUserIDs,
|
||||
UserIDs: []int64{42, 43, 42, 0},
|
||||
ProducerEventType: "admin_full_server_notice",
|
||||
AggregateType: "admin_notice",
|
||||
AggregateID: "notice_1",
|
||||
Title: "全服维护通知",
|
||||
Summary: "今晚 23:00 进行短暂维护。",
|
||||
Body: "维护期间部分功能可能短暂不可用。",
|
||||
ActionType: "app_h5",
|
||||
ActionParam: "https://example.com/notice",
|
||||
MetadataJSON: `{"source":"admin"}`,
|
||||
BatchSize: 500,
|
||||
CreatedBy: "admin:1",
|
||||
})
|
||||
if err != nil || !created || job.MessageType != messagedomain.SectionSystem || job.TargetScope != messagedomain.TargetScopeUserIDs {
|
||||
t.Fatalf("CreateNoticeFanout failed: created=%v job=%+v err=%v", created, job, err)
|
||||
}
|
||||
if job.TargetFilterJSON != `{"user_ids":[42,43]}` {
|
||||
t.Fatalf("fanout target filter should deduplicate user_ids, got %s", job.TargetFilterJSON)
|
||||
}
|
||||
if _, created, err := svc.CreateNoticeFanout(ctx, messageservice.NoticeFanoutCommand{
|
||||
CommandID: "admin-full-server-notice-1",
|
||||
MessageType: messagedomain.SectionSystem,
|
||||
TargetScope: messagedomain.TargetScopeUserIDs,
|
||||
UserIDs: []int64{42, 43, 42, 0},
|
||||
ProducerEventType: "admin_full_server_notice",
|
||||
AggregateType: "admin_notice",
|
||||
AggregateID: "notice_1",
|
||||
Title: "全服维护通知",
|
||||
Summary: "今晚 23:00 进行短暂维护。",
|
||||
Body: "维护期间部分功能可能短暂不可用。",
|
||||
ActionType: "app_h5",
|
||||
ActionParam: "https://example.com/notice",
|
||||
MetadataJSON: `{"source":"admin"}`,
|
||||
BatchSize: 500,
|
||||
CreatedBy: "admin:1",
|
||||
}); err != nil || created {
|
||||
t.Fatalf("notice fanout duplicate should be idempotent: created=%v err=%v", created, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFanoutWorkerMaterializesUserIDTargetsByCursor(t *testing.T) {
|
||||
ctx := appcode.WithContext(context.Background(), "lalu")
|
||||
repository := mysqltest.NewRepository(t)
|
||||
|
||||
@ -157,9 +157,6 @@ func (s *Service) HandleRoomEvent(ctx context.Context, envelope *roomeventsv1.Ev
|
||||
if err := proto.Unmarshal(envelope.GetBody(), &gift); err != nil {
|
||||
return domain.EventResult{}, err
|
||||
}
|
||||
if gift.GetIsRobotGift() {
|
||||
return domain.EventResult{EventID: envelope.GetEventId(), Status: domain.EventStatusSkipped}, nil
|
||||
}
|
||||
if strings.TrimSpace(envelope.GetRoomId()) == "" || gift.GetCoinSpent() <= 0 {
|
||||
// 缺房间或 0 金币不会形成有效房间贡献;这类事件本身没有可补偿数据,跳过比失败重放更安全。
|
||||
return domain.EventResult{EventID: envelope.GetEventId(), Status: domain.EventStatusSkipped}, nil
|
||||
|
||||
@ -207,9 +207,6 @@ func (s *Service) HandleRoomEvent(ctx context.Context, envelope *roomeventsv1.Ev
|
||||
if err := proto.Unmarshal(envelope.GetBody(), &gift); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if gift.GetIsRobotGift() {
|
||||
return 0, nil
|
||||
}
|
||||
if gift.GetSenderUserId() <= 0 || gift.GetGiftId() == "" || gift.GetGiftCount() <= 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
@ -163,9 +163,6 @@ func (s *Service) HandleRoomEvent(ctx context.Context, envelope *roomeventsv1.Ev
|
||||
if err := proto.Unmarshal(envelope.GetBody(), &gift); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if gift.GetIsRobotGift() {
|
||||
return 0, nil
|
||||
}
|
||||
if gift.GetSenderUserId() <= 0 || gift.GetGiftId() == "" || gift.GetCoinSpent() <= 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
@ -1,142 +0,0 @@
|
||||
package mysql
|
||||
|
||||
import "context"
|
||||
|
||||
func (r *Repository) ensureAgencyOpeningTables(ctx context.Context) error {
|
||||
statements := []string{
|
||||
`CREATE TABLE IF NOT EXISTS agency_opening_cycles (
|
||||
app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码',
|
||||
cycle_id VARCHAR(96) NOT NULL COMMENT '代理开业周期 ID',
|
||||
activity_code VARCHAR(64) NOT NULL DEFAULT 'agency_opening' COMMENT '活动编码',
|
||||
title VARCHAR(128) NOT NULL DEFAULT '' COMMENT '周期标题',
|
||||
status VARCHAR(32) NOT NULL DEFAULT 'draft' COMMENT 'draft/active/disabled/settling/settled',
|
||||
start_ms BIGINT NOT NULL COMMENT 'UTC epoch ms 开始,包含',
|
||||
end_ms BIGINT NOT NULL COMMENT 'UTC epoch ms 结束,不包含',
|
||||
min_host_count INT NOT NULL DEFAULT 10 COMMENT '申请要求的有效主播数',
|
||||
max_agency_age_days INT NOT NULL DEFAULT 7 COMMENT '代理创建后允许申请的最大天数,0 表示不限制',
|
||||
created_by_admin_id BIGINT NOT NULL DEFAULT 0 COMMENT '创建管理员',
|
||||
updated_by_admin_id BIGINT NOT NULL DEFAULT 0 COMMENT '更新管理员',
|
||||
settled_at_ms BIGINT NOT NULL DEFAULT 0 COMMENT '结算完成时间',
|
||||
locked_by VARCHAR(96) NOT NULL DEFAULT '' COMMENT '结算 worker 锁',
|
||||
lock_until_ms BIGINT NOT NULL DEFAULT 0 COMMENT '结算锁过期时间',
|
||||
created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms',
|
||||
updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms',
|
||||
PRIMARY KEY (app_code, cycle_id),
|
||||
KEY idx_agency_opening_cycle_current (app_code, activity_code, status, start_ms, end_ms),
|
||||
KEY idx_agency_opening_cycle_due (app_code, activity_code, status, end_ms, lock_until_ms)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='代理开业活动周期'`,
|
||||
`CREATE TABLE IF NOT EXISTS agency_opening_cycle_rewards (
|
||||
app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码',
|
||||
cycle_id VARCHAR(96) NOT NULL COMMENT '代理开业周期 ID',
|
||||
rank_no INT NOT NULL COMMENT '档位序号',
|
||||
threshold_coin_spent BIGINT NOT NULL DEFAULT 0 COMMENT '命中该档需要的代理房间流水金币',
|
||||
reward_coin_amount BIGINT NOT NULL DEFAULT 0 COMMENT '奖励金币',
|
||||
created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms',
|
||||
updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms',
|
||||
PRIMARY KEY (app_code, cycle_id, rank_no)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='代理开业流水奖励档位'`,
|
||||
`CREATE TABLE IF NOT EXISTS agency_opening_applications (
|
||||
app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码',
|
||||
application_id VARCHAR(96) NOT NULL COMMENT '申请 ID',
|
||||
cycle_id VARCHAR(96) NOT NULL COMMENT '代理开业周期 ID',
|
||||
agency_id BIGINT NOT NULL COMMENT '代理 ID',
|
||||
agency_owner_user_id BIGINT NOT NULL COMMENT '代理 owner 用户 ID',
|
||||
agency_name VARCHAR(128) NOT NULL DEFAULT '' COMMENT '代理名称快照',
|
||||
host_count INT NOT NULL DEFAULT 0 COMMENT '申请时有效主播数快照',
|
||||
agency_created_at_ms BIGINT NOT NULL DEFAULT 0 COMMENT '代理创建时间快照',
|
||||
status VARCHAR(32) NOT NULL DEFAULT 'applied' COMMENT 'applied/pending/granted/failed',
|
||||
score_coin_amount BIGINT NOT NULL DEFAULT 0 COMMENT '周期内开业流水金币',
|
||||
reward_rank_no INT NOT NULL DEFAULT 0 COMMENT '结算命中的档位序号',
|
||||
reward_threshold_coin_spent BIGINT NOT NULL DEFAULT 0 COMMENT '结算命中的流水阈值',
|
||||
reward_coin_amount BIGINT NOT NULL DEFAULT 0 COMMENT '结算奖励金币',
|
||||
wallet_command_id VARCHAR(128) NOT NULL DEFAULT '' COMMENT '钱包幂等命令',
|
||||
wallet_transaction_id VARCHAR(128) NOT NULL DEFAULT '' COMMENT '钱包交易 ID',
|
||||
failure_reason VARCHAR(512) NOT NULL DEFAULT '' COMMENT '失败原因',
|
||||
applied_at_ms BIGINT NOT NULL COMMENT '申请时间',
|
||||
granted_at_ms BIGINT NOT NULL DEFAULT 0 COMMENT '发奖时间',
|
||||
created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms',
|
||||
updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms',
|
||||
PRIMARY KEY (app_code, application_id),
|
||||
UNIQUE KEY uk_agency_opening_owner_once (app_code, agency_owner_user_id),
|
||||
KEY idx_agency_opening_wallet_command (app_code, wallet_command_id),
|
||||
KEY idx_agency_opening_application_owner (app_code, agency_owner_user_id, applied_at_ms),
|
||||
KEY idx_agency_opening_agency_cycle (app_code, cycle_id, agency_id),
|
||||
KEY idx_agency_opening_application_rank (app_code, cycle_id, score_coin_amount, applied_at_ms, agency_id),
|
||||
KEY idx_agency_opening_application_status (app_code, cycle_id, status, updated_at_ms)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='代理开业申请和结算事实'`,
|
||||
`CREATE TABLE IF NOT EXISTS agency_opening_score_events (
|
||||
app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码',
|
||||
source_event_id VARCHAR(128) NOT NULL COMMENT 'room-service outbox event_id',
|
||||
cycle_id VARCHAR(96) NOT NULL COMMENT '代理开业周期 ID',
|
||||
application_id VARCHAR(96) NOT NULL COMMENT '命中的申请 ID',
|
||||
agency_id BIGINT NOT NULL COMMENT '代理 ID',
|
||||
target_user_id BIGINT NOT NULL COMMENT '收礼主播用户 ID',
|
||||
score_delta BIGINT NOT NULL COMMENT '本次流水金币',
|
||||
occurred_at_ms BIGINT NOT NULL COMMENT '事件发生时间,UTC epoch ms',
|
||||
created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms',
|
||||
PRIMARY KEY (app_code, source_event_id),
|
||||
KEY idx_agency_opening_score_event_application (app_code, application_id, occurred_at_ms)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='代理开业流水事件幂等表'`,
|
||||
}
|
||||
for _, statement := range statements {
|
||||
if _, err := r.db.ExecContext(ctx, statement); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := r.ensureAgencyOpeningRewardTierColumns(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
return r.ensureAgencyOpeningApplicationOwnerOnceIndex(ctx)
|
||||
}
|
||||
|
||||
func (r *Repository) ensureAgencyOpeningRewardTierColumns(ctx context.Context) error {
|
||||
if exists, err := r.columnExists(ctx, "agency_opening_cycle_rewards", "threshold_coin_spent"); err != nil {
|
||||
return err
|
||||
} else if !exists {
|
||||
// 旧本地表按 Top 名次发奖;新增阈值列后,rank_no 只作为档位序号保留,实际结算按 threshold_coin_spent 命中。
|
||||
if _, err := r.db.ExecContext(ctx, `ALTER TABLE agency_opening_cycle_rewards ADD COLUMN threshold_coin_spent BIGINT NOT NULL DEFAULT 0 COMMENT '命中该档需要的代理房间流水金币' AFTER rank_no`); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if exists, err := r.columnExists(ctx, "agency_opening_applications", "reward_threshold_coin_spent"); err != nil {
|
||||
return err
|
||||
} else if !exists {
|
||||
if _, err := r.db.ExecContext(ctx, `ALTER TABLE agency_opening_applications ADD COLUMN reward_threshold_coin_spent BIGINT NOT NULL DEFAULT 0 COMMENT '结算命中的流水阈值' AFTER reward_rank_no`); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Repository) ensureAgencyOpeningApplicationOwnerOnceIndex(ctx context.Context) error {
|
||||
ownerOnceExists, err := r.indexExists(ctx, "agency_opening_applications", "uk_agency_opening_owner_once")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !ownerOnceExists {
|
||||
// 申请资格是用户维度的一次性活动,唯一键先落在 owner_user_id 上,避免新周期或更换 agency 后重复参与。
|
||||
if _, err := r.db.ExecContext(ctx, `ALTER TABLE agency_opening_applications ADD UNIQUE KEY uk_agency_opening_owner_once (app_code, agency_owner_user_id)`); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
oldAgencyCycleUniqueExists, err := r.indexExists(ctx, "agency_opening_applications", "uk_agency_opening_agency_cycle")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if oldAgencyCycleUniqueExists {
|
||||
// 旧唯一键只限制同一周期同一 agency,不能表达“每个用户只能参与一次”,降级成普通查询索引即可。
|
||||
if _, err := r.db.ExecContext(ctx, `ALTER TABLE agency_opening_applications DROP INDEX uk_agency_opening_agency_cycle`); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
agencyCycleIndexExists, err := r.indexExists(ctx, "agency_opening_applications", "idx_agency_opening_agency_cycle")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !agencyCycleIndexExists {
|
||||
if _, err := r.db.ExecContext(ctx, `ALTER TABLE agency_opening_applications ADD KEY idx_agency_opening_agency_cycle (app_code, cycle_id, agency_id)`); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -1,628 +0,0 @@
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"hyapp/pkg/appcode"
|
||||
"hyapp/pkg/idgen"
|
||||
"hyapp/pkg/xerr"
|
||||
domain "hyapp/services/activity-service/internal/domain/agencyopening"
|
||||
)
|
||||
|
||||
// ListAgencyOpeningCycles returns admin configured cycles with reward rows attached.
|
||||
func (r *Repository) ListAgencyOpeningCycles(ctx context.Context, query domain.ListQuery) ([]domain.Cycle, int64, error) {
|
||||
if r == nil || r.db == nil {
|
||||
return nil, 0, xerr.New(xerr.Unavailable, "mysql repository is not configured")
|
||||
}
|
||||
where, args := agencyOpeningCycleWhere(ctx, query)
|
||||
var total int64
|
||||
if err := r.db.QueryRowContext(ctx, `SELECT COUNT(*) FROM agency_opening_cycles `+where, args...).Scan(&total); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
pageSize := normalizePageSize(query.PageSize)
|
||||
page := query.Page
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
rows, err := r.db.QueryContext(ctx, `
|
||||
SELECT app_code, cycle_id, activity_code, title, status, start_ms, end_ms, min_host_count, max_agency_age_days,
|
||||
created_by_admin_id, updated_by_admin_id, settled_at_ms, created_at_ms, updated_at_ms
|
||||
FROM agency_opening_cycles `+where+`
|
||||
ORDER BY start_ms DESC, cycle_id DESC
|
||||
LIMIT ? OFFSET ?`, append(args, int(pageSize), int((page-1)*pageSize))...)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
defer rows.Close()
|
||||
cycles := make([]domain.Cycle, 0)
|
||||
for rows.Next() {
|
||||
cycle, scanErr := scanAgencyOpeningCycle(rows)
|
||||
if scanErr != nil {
|
||||
return nil, 0, scanErr
|
||||
}
|
||||
cycles = append(cycles, cycle)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
if err := r.attachAgencyOpeningRewards(ctx, cycles); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
return cycles, total, nil
|
||||
}
|
||||
|
||||
func (r *Repository) GetAgencyOpeningCycle(ctx context.Context, cycleID string) (domain.Cycle, error) {
|
||||
if r == nil || r.db == nil {
|
||||
return domain.Cycle{}, xerr.New(xerr.Unavailable, "mysql repository is not configured")
|
||||
}
|
||||
cycle, err := r.getAgencyOpeningCycle(ctx, r.db, cycleID)
|
||||
if err != nil {
|
||||
return domain.Cycle{}, err
|
||||
}
|
||||
cycles := []domain.Cycle{cycle}
|
||||
if err := r.attachAgencyOpeningRewards(ctx, cycles); err != nil {
|
||||
return domain.Cycle{}, err
|
||||
}
|
||||
return cycles[0], nil
|
||||
}
|
||||
|
||||
func (r *Repository) UpsertAgencyOpeningCycle(ctx context.Context, command domain.CycleCommand, nowMS int64) (domain.Cycle, bool, error) {
|
||||
if r == nil || r.db == nil {
|
||||
return domain.Cycle{}, false, xerr.New(xerr.Unavailable, "mysql repository is not configured")
|
||||
}
|
||||
tx, err := r.db.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
return domain.Cycle{}, false, err
|
||||
}
|
||||
defer tx.Rollback()
|
||||
cycle := command.Cycle
|
||||
cycle.AppCode = appcode.FromContext(ctx)
|
||||
cycle.ActivityCode = domain.ActivityCode
|
||||
if cycle.Status == "" {
|
||||
cycle.Status = domain.StatusDraft
|
||||
}
|
||||
created := strings.TrimSpace(cycle.CycleID) == ""
|
||||
if created {
|
||||
cycle.CycleID = idgen.New("aopen")
|
||||
cycle.CreatedAtMS = nowMS
|
||||
cycle.CreatedByAdminID = command.OperatorAdminID
|
||||
} else if _, err := r.getAgencyOpeningCycle(ctx, tx, cycle.CycleID); err != nil {
|
||||
return domain.Cycle{}, false, err
|
||||
}
|
||||
if !created && command.RequireNewRecord {
|
||||
return domain.Cycle{}, false, xerr.New(xerr.InvalidArgument, "cycle_id already exists")
|
||||
}
|
||||
cycle.UpdatedAtMS = nowMS
|
||||
cycle.UpdatedByAdminID = command.OperatorAdminID
|
||||
if err := r.checkAgencyOpeningCycleOverlap(ctx, tx, cycle); err != nil {
|
||||
return domain.Cycle{}, false, err
|
||||
}
|
||||
if created {
|
||||
_, err = tx.ExecContext(ctx, `
|
||||
INSERT INTO agency_opening_cycles (
|
||||
app_code, cycle_id, activity_code, title, status, start_ms, end_ms, min_host_count, max_agency_age_days,
|
||||
created_by_admin_id, updated_by_admin_id, settled_at_ms, created_at_ms, updated_at_ms
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, ?, ?)`,
|
||||
cycle.AppCode, cycle.CycleID, cycle.ActivityCode, cycle.Title, cycle.Status, cycle.StartMS, cycle.EndMS, cycle.MinHostCount, cycle.MaxAgencyAgeDays,
|
||||
cycle.CreatedByAdminID, cycle.UpdatedByAdminID, cycle.CreatedAtMS, cycle.UpdatedAtMS)
|
||||
} else {
|
||||
_, err = tx.ExecContext(ctx, `
|
||||
UPDATE agency_opening_cycles
|
||||
SET title = ?, status = ?, start_ms = ?, end_ms = ?, min_host_count = ?, max_agency_age_days = ?,
|
||||
updated_by_admin_id = ?, updated_at_ms = ?
|
||||
WHERE app_code = ? AND cycle_id = ?`,
|
||||
cycle.Title, cycle.Status, cycle.StartMS, cycle.EndMS, cycle.MinHostCount, cycle.MaxAgencyAgeDays,
|
||||
cycle.UpdatedByAdminID, cycle.UpdatedAtMS, cycle.AppCode, cycle.CycleID)
|
||||
}
|
||||
if err != nil {
|
||||
return domain.Cycle{}, false, err
|
||||
}
|
||||
if _, err := tx.ExecContext(ctx, `DELETE FROM agency_opening_cycle_rewards WHERE app_code = ? AND cycle_id = ?`, cycle.AppCode, cycle.CycleID); err != nil {
|
||||
return domain.Cycle{}, false, err
|
||||
}
|
||||
for _, reward := range cycle.Rewards {
|
||||
if _, err := tx.ExecContext(ctx, `
|
||||
INSERT INTO agency_opening_cycle_rewards (app_code, cycle_id, rank_no, threshold_coin_spent, reward_coin_amount, created_at_ms, updated_at_ms)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
||||
cycle.AppCode, cycle.CycleID, reward.RankNo, reward.ThresholdCoinSpent, reward.RewardCoinAmount, nowMS, nowMS); err != nil {
|
||||
return domain.Cycle{}, false, err
|
||||
}
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
return domain.Cycle{}, false, err
|
||||
}
|
||||
stored, err := r.GetAgencyOpeningCycle(ctx, cycle.CycleID)
|
||||
return stored, created, err
|
||||
}
|
||||
|
||||
func (r *Repository) SetAgencyOpeningCycleStatus(ctx context.Context, cycleID string, status string, operatorAdminID int64, nowMS int64) (domain.Cycle, error) {
|
||||
if r == nil || r.db == nil {
|
||||
return domain.Cycle{}, xerr.New(xerr.Unavailable, "mysql repository is not configured")
|
||||
}
|
||||
tx, err := r.db.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
return domain.Cycle{}, err
|
||||
}
|
||||
defer tx.Rollback()
|
||||
cycle, err := r.getAgencyOpeningCycle(ctx, tx, cycleID)
|
||||
if err != nil {
|
||||
return domain.Cycle{}, err
|
||||
}
|
||||
cycle.Status = status
|
||||
if err := r.checkAgencyOpeningCycleOverlap(ctx, tx, cycle); err != nil {
|
||||
return domain.Cycle{}, err
|
||||
}
|
||||
if _, err := tx.ExecContext(ctx, `
|
||||
UPDATE agency_opening_cycles
|
||||
SET status = ?, updated_by_admin_id = ?, updated_at_ms = ?
|
||||
WHERE app_code = ? AND cycle_id = ?`,
|
||||
status, operatorAdminID, nowMS, appcode.FromContext(ctx), cycleID); err != nil {
|
||||
return domain.Cycle{}, err
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
return domain.Cycle{}, err
|
||||
}
|
||||
return r.GetAgencyOpeningCycle(ctx, cycleID)
|
||||
}
|
||||
|
||||
func (r *Repository) FindCurrentAgencyOpeningCycle(ctx context.Context, nowMS int64) (domain.Cycle, bool, error) {
|
||||
if r == nil || r.db == nil {
|
||||
return domain.Cycle{}, false, xerr.New(xerr.Unavailable, "mysql repository is not configured")
|
||||
}
|
||||
cycle, err := scanAgencyOpeningCycle(r.db.QueryRowContext(ctx, `
|
||||
SELECT app_code, cycle_id, activity_code, title, status, start_ms, end_ms, min_host_count, max_agency_age_days,
|
||||
created_by_admin_id, updated_by_admin_id, settled_at_ms, created_at_ms, updated_at_ms
|
||||
FROM agency_opening_cycles
|
||||
WHERE app_code = ? AND activity_code = ? AND status = ? AND start_ms <= ? AND end_ms > ?
|
||||
ORDER BY start_ms DESC, updated_at_ms DESC
|
||||
LIMIT 1`, appcode.FromContext(ctx), domain.ActivityCode, domain.StatusActive, nowMS, nowMS))
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return domain.Cycle{}, false, nil
|
||||
}
|
||||
return domain.Cycle{}, false, err
|
||||
}
|
||||
cycles := []domain.Cycle{cycle}
|
||||
if err := r.attachAgencyOpeningRewards(ctx, cycles); err != nil {
|
||||
return domain.Cycle{}, false, err
|
||||
}
|
||||
return cycles[0], true, nil
|
||||
}
|
||||
|
||||
func (r *Repository) GetAgencyOpeningApplicationByOwner(ctx context.Context, ownerUserID int64) (domain.Application, bool, error) {
|
||||
item, err := scanAgencyOpeningApplication(r.db.QueryRowContext(ctx, agencyOpeningApplicationSelect()+`
|
||||
WHERE app_code = ? AND agency_owner_user_id = ?
|
||||
ORDER BY applied_at_ms DESC
|
||||
LIMIT 1`, appcode.FromContext(ctx), ownerUserID))
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return domain.Application{}, false, nil
|
||||
}
|
||||
return domain.Application{}, false, err
|
||||
}
|
||||
return item, true, nil
|
||||
}
|
||||
|
||||
func (r *Repository) CreateAgencyOpeningApplication(ctx context.Context, cycle domain.Cycle, agency domain.AgencySnapshot, nowMS int64) (domain.Application, bool, error) {
|
||||
if r == nil || r.db == nil {
|
||||
return domain.Application{}, false, xerr.New(xerr.Unavailable, "mysql repository is not configured")
|
||||
}
|
||||
applicationID := idgen.New("aopenapp")
|
||||
result, err := r.db.ExecContext(ctx, `
|
||||
INSERT IGNORE INTO agency_opening_applications (
|
||||
app_code, application_id, cycle_id, agency_id, agency_owner_user_id, agency_name, host_count, agency_created_at_ms,
|
||||
status, score_coin_amount, reward_rank_no, reward_threshold_coin_spent, reward_coin_amount, wallet_command_id, wallet_transaction_id, failure_reason,
|
||||
applied_at_ms, granted_at_ms, created_at_ms, updated_at_ms
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 0, 0, 0, 0, '', '', '', ?, 0, ?, ?)`,
|
||||
appcode.FromContext(ctx), applicationID, cycle.CycleID, agency.AgencyID, agency.OwnerUserID, agency.Name, agency.HostCount, agency.AgencyCreatedAtMS,
|
||||
domain.ApplicationStatusApplied, nowMS, nowMS, nowMS)
|
||||
if err != nil {
|
||||
return domain.Application{}, false, err
|
||||
}
|
||||
affected, _ := result.RowsAffected()
|
||||
item, ok, err := r.GetAgencyOpeningApplicationByOwner(ctx, agency.OwnerUserID)
|
||||
return item, affected > 0 && ok, err
|
||||
}
|
||||
|
||||
func (r *Repository) ListAgencyOpeningApplications(ctx context.Context, query domain.ApplicationQuery) ([]domain.Application, int64, error) {
|
||||
if r == nil || r.db == nil {
|
||||
return nil, 0, xerr.New(xerr.Unavailable, "mysql repository is not configured")
|
||||
}
|
||||
where, args := agencyOpeningApplicationWhere(ctx, query)
|
||||
var total int64
|
||||
if err := r.db.QueryRowContext(ctx, `SELECT COUNT(*) FROM agency_opening_applications `+where, args...).Scan(&total); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
pageSize := normalizePageSize(query.PageSize)
|
||||
page := query.Page
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
rows, err := r.db.QueryContext(ctx, agencyOpeningApplicationSelect()+` `+where+`
|
||||
ORDER BY score_coin_amount DESC, applied_at_ms ASC, agency_id ASC
|
||||
LIMIT ? OFFSET ?`, append(args, int(pageSize), int((page-1)*pageSize))...)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
defer rows.Close()
|
||||
items := make([]domain.Application, 0)
|
||||
for rows.Next() {
|
||||
item, scanErr := scanAgencyOpeningApplication(rows)
|
||||
if scanErr != nil {
|
||||
return nil, 0, scanErr
|
||||
}
|
||||
items = append(items, item)
|
||||
}
|
||||
return items, total, rows.Err()
|
||||
}
|
||||
|
||||
func (r *Repository) ConsumeAgencyOpeningGiftEvent(ctx context.Context, event domain.GiftEvent, nowMS int64) (domain.EventResult, error) {
|
||||
if r == nil || r.db == nil {
|
||||
return domain.EventResult{}, xerr.New(xerr.Unavailable, "mysql repository is not configured")
|
||||
}
|
||||
tx, err := r.db.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
return domain.EventResult{}, err
|
||||
}
|
||||
defer tx.Rollback()
|
||||
var app domain.Application
|
||||
err = tx.QueryRowContext(ctx, `
|
||||
SELECT app.app_code, app.application_id, app.cycle_id, app.agency_id, app.agency_owner_user_id, app.agency_name, app.host_count, app.agency_created_at_ms,
|
||||
app.status, app.score_coin_amount, app.reward_rank_no, app.reward_threshold_coin_spent, app.reward_coin_amount, app.wallet_command_id, app.wallet_transaction_id, app.failure_reason,
|
||||
app.applied_at_ms, app.granted_at_ms, app.created_at_ms, app.updated_at_ms
|
||||
FROM agency_opening_applications app
|
||||
INNER JOIN agency_opening_cycles cycle ON cycle.app_code = app.app_code AND cycle.cycle_id = app.cycle_id
|
||||
WHERE app.app_code = ? AND app.agency_owner_user_id = ? AND app.status IN (?, ?, ?) AND app.applied_at_ms <= ?
|
||||
AND cycle.activity_code = ? AND cycle.status IN (?, ?) AND cycle.start_ms <= ? AND cycle.end_ms > ?
|
||||
ORDER BY app.applied_at_ms DESC
|
||||
LIMIT 1`, appcode.FromContext(ctx), event.RoomOwnerUserID, domain.ApplicationStatusApplied, domain.ApplicationStatusPending, domain.ApplicationStatusFailed, event.OccurredAtMS,
|
||||
domain.ActivityCode, domain.StatusActive, domain.StatusSettling, event.OccurredAtMS, event.OccurredAtMS).Scan(
|
||||
&app.AppCode, &app.ApplicationID, &app.CycleID, &app.AgencyID, &app.AgencyOwnerUserID, &app.AgencyName,
|
||||
&app.HostCount, &app.AgencyCreatedAtMS, &app.Status, &app.ScoreCoinAmount, &app.RewardRankNo, &app.RewardThresholdCoin, &app.RewardCoinAmount,
|
||||
&app.WalletCommandID, &app.WalletTransactionID, &app.FailureReason, &app.AppliedAtMS, &app.GrantedAtMS, &app.CreatedAtMS, &app.UpdatedAtMS)
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return domain.EventResult{EventID: event.EventID, Status: domain.EventStatusSkipped}, tx.Commit()
|
||||
}
|
||||
return domain.EventResult{}, err
|
||||
}
|
||||
result, err := tx.ExecContext(ctx, `
|
||||
INSERT IGNORE INTO agency_opening_score_events (
|
||||
app_code, source_event_id, cycle_id, application_id, agency_id, target_user_id, score_delta, occurred_at_ms, created_at_ms
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
appcode.FromContext(ctx), event.EventID, app.CycleID, app.ApplicationID, app.AgencyID, event.TargetUserID, event.CoinSpent, event.OccurredAtMS, nowMS)
|
||||
if err != nil {
|
||||
return domain.EventResult{}, err
|
||||
}
|
||||
affected, _ := result.RowsAffected()
|
||||
if affected == 0 {
|
||||
return domain.EventResult{EventID: event.EventID, Status: domain.EventStatusDuplicate}, tx.Commit()
|
||||
}
|
||||
if _, err := tx.ExecContext(ctx, `
|
||||
UPDATE agency_opening_applications
|
||||
SET score_coin_amount = score_coin_amount + ?, updated_at_ms = ?
|
||||
WHERE app_code = ? AND application_id = ?`,
|
||||
event.CoinSpent, nowMS, appcode.FromContext(ctx), app.ApplicationID); err != nil {
|
||||
return domain.EventResult{}, err
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
return domain.EventResult{}, err
|
||||
}
|
||||
return domain.EventResult{EventID: event.EventID, Status: domain.EventStatusConsumed}, nil
|
||||
}
|
||||
|
||||
func (r *Repository) ClaimDueAgencyOpeningCycles(ctx context.Context, workerID string, nowMS int64, lockTTL time.Duration, batchSize int32) ([]domain.Cycle, error) {
|
||||
if batchSize <= 0 {
|
||||
batchSize = 50
|
||||
}
|
||||
lockUntil := nowMS + lockTTL.Milliseconds()
|
||||
if lockTTL <= 0 {
|
||||
lockUntil = nowMS + int64((30 * time.Second).Milliseconds())
|
||||
}
|
||||
_, err := r.db.ExecContext(ctx, `
|
||||
UPDATE agency_opening_cycles
|
||||
SET status = ?, locked_by = ?, lock_until_ms = ?, updated_at_ms = ?
|
||||
WHERE app_code = ? AND activity_code = ? AND status IN (?, ?) AND end_ms <= ? AND (lock_until_ms = 0 OR lock_until_ms <= ?)
|
||||
ORDER BY end_ms ASC, cycle_id ASC
|
||||
LIMIT ?`,
|
||||
domain.StatusSettling, strings.TrimSpace(workerID), lockUntil, nowMS,
|
||||
appcode.FromContext(ctx), domain.ActivityCode, domain.StatusActive, domain.StatusSettling, nowMS, nowMS, int(batchSize))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rows, err := r.db.QueryContext(ctx, `
|
||||
SELECT app_code, cycle_id, activity_code, title, status, start_ms, end_ms, min_host_count, max_agency_age_days,
|
||||
created_by_admin_id, updated_by_admin_id, settled_at_ms, created_at_ms, updated_at_ms
|
||||
FROM agency_opening_cycles
|
||||
WHERE app_code = ? AND activity_code = ? AND status = ? AND locked_by = ? AND lock_until_ms > ?
|
||||
ORDER BY end_ms ASC, cycle_id ASC
|
||||
LIMIT ?`, appcode.FromContext(ctx), domain.ActivityCode, domain.StatusSettling, strings.TrimSpace(workerID), nowMS, int(batchSize))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
cycles := make([]domain.Cycle, 0)
|
||||
for rows.Next() {
|
||||
cycle, scanErr := scanAgencyOpeningCycle(rows)
|
||||
if scanErr != nil {
|
||||
return nil, scanErr
|
||||
}
|
||||
cycles = append(cycles, cycle)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cycles, r.attachAgencyOpeningRewards(ctx, cycles)
|
||||
}
|
||||
|
||||
func (r *Repository) PrepareAgencyOpeningSettlements(ctx context.Context, cycle domain.Cycle, nowMS int64) ([]domain.Application, error) {
|
||||
rewards := activeAgencyOpeningRewardTiers(cycle.Rewards)
|
||||
if len(rewards) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
tx, err := r.db.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer tx.Rollback()
|
||||
rows, err := tx.QueryContext(ctx, agencyOpeningApplicationSelect()+`
|
||||
WHERE app_code = ? AND cycle_id = ? AND status IN (?, ?)
|
||||
ORDER BY score_coin_amount DESC, applied_at_ms ASC, agency_id ASC`,
|
||||
appcode.FromContext(ctx), cycle.CycleID, domain.ApplicationStatusApplied, domain.ApplicationStatusFailed)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
applications := make([]domain.Application, 0)
|
||||
for rows.Next() {
|
||||
item, scanErr := scanAgencyOpeningApplication(rows)
|
||||
if scanErr != nil {
|
||||
rows.Close()
|
||||
return nil, scanErr
|
||||
}
|
||||
applications = append(applications, item)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, application := range applications {
|
||||
reward, matched := matchAgencyOpeningRewardTier(rewards, application.ScoreCoinAmount)
|
||||
if !matched {
|
||||
reward = domain.Reward{}
|
||||
}
|
||||
walletCommandID := fmt.Sprintf("agency_opening:%s:%s", cycle.CycleID, application.ApplicationID)
|
||||
if _, err := tx.ExecContext(ctx, `
|
||||
UPDATE agency_opening_applications
|
||||
SET status = CASE WHEN status = ? THEN status ELSE ? END,
|
||||
reward_rank_no = ?, reward_threshold_coin_spent = ?, reward_coin_amount = ?, wallet_command_id = ?, updated_at_ms = ?
|
||||
WHERE app_code = ? AND application_id = ? AND status IN (?, ?)`,
|
||||
domain.ApplicationStatusGranted, domain.ApplicationStatusPending,
|
||||
reward.RankNo, reward.ThresholdCoinSpent, reward.RewardCoinAmount, walletCommandID, nowMS,
|
||||
appcode.FromContext(ctx), application.ApplicationID, domain.ApplicationStatusApplied, domain.ApplicationStatusFailed); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pendingApplications, _, err := r.ListAgencyOpeningApplications(ctx, domain.ApplicationQuery{CycleID: cycle.CycleID, Status: domain.ApplicationStatusPending, Page: 1, PageSize: 100})
|
||||
return pendingApplications, err
|
||||
}
|
||||
|
||||
func (r *Repository) MarkAgencyOpeningApplicationGranted(ctx context.Context, applicationID string, walletTransactionID string, grantedAtMS int64) (domain.Application, error) {
|
||||
if _, err := r.db.ExecContext(ctx, `
|
||||
UPDATE agency_opening_applications
|
||||
SET status = ?, wallet_transaction_id = ?, failure_reason = '', granted_at_ms = ?, updated_at_ms = ?
|
||||
WHERE app_code = ? AND application_id = ?`,
|
||||
domain.ApplicationStatusGranted, strings.TrimSpace(walletTransactionID), grantedAtMS, grantedAtMS, appcode.FromContext(ctx), strings.TrimSpace(applicationID)); err != nil {
|
||||
return domain.Application{}, err
|
||||
}
|
||||
return r.getAgencyOpeningApplication(ctx, applicationID)
|
||||
}
|
||||
|
||||
func (r *Repository) MarkAgencyOpeningApplicationFailed(ctx context.Context, applicationID string, reason string, nowMS int64) error {
|
||||
_, err := r.db.ExecContext(ctx, `
|
||||
UPDATE agency_opening_applications
|
||||
SET status = ?, failure_reason = ?, updated_at_ms = ?
|
||||
WHERE app_code = ? AND application_id = ?`,
|
||||
domain.ApplicationStatusFailed, truncateAgencyOpeningFailure(reason), nowMS, appcode.FromContext(ctx), strings.TrimSpace(applicationID))
|
||||
return err
|
||||
}
|
||||
|
||||
func truncateString(value string, limit int) string {
|
||||
value = strings.TrimSpace(value)
|
||||
if limit <= 0 || len(value) <= limit {
|
||||
return value
|
||||
}
|
||||
return value[:limit]
|
||||
}
|
||||
|
||||
func (r *Repository) FinishAgencyOpeningCycleIfComplete(ctx context.Context, cycleID string, nowMS int64) (bool, error) {
|
||||
var pending int64
|
||||
if err := r.db.QueryRowContext(ctx, `
|
||||
SELECT COUNT(*) FROM agency_opening_applications
|
||||
WHERE app_code = ? AND cycle_id = ? AND status IN (?, ?)`,
|
||||
appcode.FromContext(ctx), strings.TrimSpace(cycleID), domain.ApplicationStatusPending, domain.ApplicationStatusFailed).Scan(&pending); err != nil {
|
||||
return false, err
|
||||
}
|
||||
if pending > 0 {
|
||||
return false, nil
|
||||
}
|
||||
_, err := r.db.ExecContext(ctx, `
|
||||
UPDATE agency_opening_cycles
|
||||
SET status = ?, settled_at_ms = ?, locked_by = '', lock_until_ms = 0, updated_at_ms = ?
|
||||
WHERE app_code = ? AND cycle_id = ?`,
|
||||
domain.StatusSettled, nowMS, nowMS, appcode.FromContext(ctx), strings.TrimSpace(cycleID))
|
||||
return err == nil, err
|
||||
}
|
||||
|
||||
func (r *Repository) getAgencyOpeningApplication(ctx context.Context, applicationID string) (domain.Application, error) {
|
||||
return scanAgencyOpeningApplication(r.db.QueryRowContext(ctx, agencyOpeningApplicationSelect()+` WHERE app_code = ? AND application_id = ?`, appcode.FromContext(ctx), strings.TrimSpace(applicationID)))
|
||||
}
|
||||
|
||||
func (r *Repository) getAgencyOpeningCycle(ctx context.Context, q interface {
|
||||
QueryRowContext(context.Context, string, ...any) *sql.Row
|
||||
}, cycleID string) (domain.Cycle, error) {
|
||||
cycleID = strings.TrimSpace(cycleID)
|
||||
if cycleID == "" {
|
||||
return domain.Cycle{}, xerr.New(xerr.InvalidArgument, "cycle_id is required")
|
||||
}
|
||||
return scanAgencyOpeningCycle(q.QueryRowContext(ctx, `
|
||||
SELECT app_code, cycle_id, activity_code, title, status, start_ms, end_ms, min_host_count, max_agency_age_days,
|
||||
created_by_admin_id, updated_by_admin_id, settled_at_ms, created_at_ms, updated_at_ms
|
||||
FROM agency_opening_cycles
|
||||
WHERE app_code = ? AND cycle_id = ?`, appcode.FromContext(ctx), cycleID))
|
||||
}
|
||||
|
||||
func (r *Repository) attachAgencyOpeningRewards(ctx context.Context, cycles []domain.Cycle) error {
|
||||
if len(cycles) == 0 {
|
||||
return nil
|
||||
}
|
||||
ids := make([]string, 0, len(cycles))
|
||||
index := make(map[string]int, len(cycles))
|
||||
for i, cycle := range cycles {
|
||||
ids = append(ids, cycle.CycleID)
|
||||
index[cycle.CycleID] = i
|
||||
}
|
||||
args := []any{appcode.FromContext(ctx)}
|
||||
for _, id := range ids {
|
||||
args = append(args, id)
|
||||
}
|
||||
rows, err := r.db.QueryContext(ctx, `
|
||||
SELECT app_code, cycle_id, rank_no, threshold_coin_spent, reward_coin_amount, created_at_ms, updated_at_ms
|
||||
FROM agency_opening_cycle_rewards
|
||||
WHERE app_code = ? AND cycle_id IN (`+placeholders(len(ids))+`)
|
||||
ORDER BY rank_no ASC`, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var reward domain.Reward
|
||||
if err := rows.Scan(&reward.AppCode, &reward.CycleID, &reward.RankNo, &reward.ThresholdCoinSpent, &reward.RewardCoinAmount, &reward.CreatedAtMS, &reward.UpdatedAtMS); err != nil {
|
||||
return err
|
||||
}
|
||||
if i, ok := index[reward.CycleID]; ok {
|
||||
cycles[i].Rewards = append(cycles[i].Rewards, reward)
|
||||
}
|
||||
}
|
||||
return rows.Err()
|
||||
}
|
||||
|
||||
func (r *Repository) checkAgencyOpeningCycleOverlap(ctx context.Context, q interface {
|
||||
QueryRowContext(context.Context, string, ...any) *sql.Row
|
||||
}, cycle domain.Cycle) error {
|
||||
if cycle.Status != domain.StatusActive {
|
||||
return nil
|
||||
}
|
||||
var count int64
|
||||
if err := q.QueryRowContext(ctx, `
|
||||
SELECT COUNT(*) FROM agency_opening_cycles
|
||||
WHERE app_code = ? AND activity_code = ? AND status = ? AND cycle_id <> ? AND start_ms < ? AND end_ms > ?`,
|
||||
appcode.FromContext(ctx), domain.ActivityCode, domain.StatusActive, cycle.CycleID, cycle.EndMS, cycle.StartMS).Scan(&count); err != nil {
|
||||
return err
|
||||
}
|
||||
if count > 0 {
|
||||
return xerr.New(xerr.Conflict, "active agency opening cycle overlaps")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func agencyOpeningCycleWhere(ctx context.Context, query domain.ListQuery) (string, []any) {
|
||||
where := []string{"app_code = ?", "activity_code = ?"}
|
||||
args := []any{appcode.FromContext(ctx), domain.ActivityCode}
|
||||
if strings.TrimSpace(query.Status) != "" {
|
||||
where = append(where, "status = ?")
|
||||
args = append(args, strings.TrimSpace(query.Status))
|
||||
}
|
||||
if query.StartMS > 0 {
|
||||
where = append(where, "end_ms > ?")
|
||||
args = append(args, query.StartMS)
|
||||
}
|
||||
if query.EndMS > 0 {
|
||||
where = append(where, "start_ms < ?")
|
||||
args = append(args, query.EndMS)
|
||||
}
|
||||
return "WHERE " + strings.Join(where, " AND "), args
|
||||
}
|
||||
|
||||
func agencyOpeningApplicationWhere(ctx context.Context, query domain.ApplicationQuery) (string, []any) {
|
||||
where := []string{"app_code = ?"}
|
||||
args := []any{appcode.FromContext(ctx)}
|
||||
if strings.TrimSpace(query.CycleID) != "" {
|
||||
where = append(where, "cycle_id = ?")
|
||||
args = append(args, strings.TrimSpace(query.CycleID))
|
||||
}
|
||||
if strings.TrimSpace(query.Status) != "" {
|
||||
where = append(where, "status = ?")
|
||||
args = append(args, strings.TrimSpace(query.Status))
|
||||
}
|
||||
if query.AgencyID > 0 {
|
||||
where = append(where, "agency_id = ?")
|
||||
args = append(args, query.AgencyID)
|
||||
}
|
||||
if query.AgencyOwnerUserID > 0 {
|
||||
where = append(where, "agency_owner_user_id = ?")
|
||||
args = append(args, query.AgencyOwnerUserID)
|
||||
}
|
||||
return "WHERE " + strings.Join(where, " AND "), args
|
||||
}
|
||||
|
||||
func agencyOpeningApplicationSelect() string {
|
||||
return `SELECT app_code, application_id, cycle_id, agency_id, agency_owner_user_id, agency_name, host_count, agency_created_at_ms,
|
||||
status, score_coin_amount, reward_rank_no, reward_threshold_coin_spent, reward_coin_amount, wallet_command_id, wallet_transaction_id, failure_reason,
|
||||
applied_at_ms, granted_at_ms, created_at_ms, updated_at_ms FROM agency_opening_applications`
|
||||
}
|
||||
|
||||
func scanAgencyOpeningCycle(row interface{ Scan(...any) error }) (domain.Cycle, error) {
|
||||
var item domain.Cycle
|
||||
err := row.Scan(&item.AppCode, &item.CycleID, &item.ActivityCode, &item.Title, &item.Status, &item.StartMS, &item.EndMS,
|
||||
&item.MinHostCount, &item.MaxAgencyAgeDays, &item.CreatedByAdminID, &item.UpdatedByAdminID, &item.SettledAtMS,
|
||||
&item.CreatedAtMS, &item.UpdatedAtMS)
|
||||
return item, err
|
||||
}
|
||||
|
||||
func scanAgencyOpeningApplication(row interface{ Scan(...any) error }) (domain.Application, error) {
|
||||
var item domain.Application
|
||||
err := row.Scan(&item.AppCode, &item.ApplicationID, &item.CycleID, &item.AgencyID, &item.AgencyOwnerUserID, &item.AgencyName,
|
||||
&item.HostCount, &item.AgencyCreatedAtMS, &item.Status, &item.ScoreCoinAmount, &item.RewardRankNo, &item.RewardThresholdCoin, &item.RewardCoinAmount,
|
||||
&item.WalletCommandID, &item.WalletTransactionID, &item.FailureReason, &item.AppliedAtMS, &item.GrantedAtMS, &item.CreatedAtMS, &item.UpdatedAtMS)
|
||||
return item, err
|
||||
}
|
||||
|
||||
func activeAgencyOpeningRewardTiers(rewards []domain.Reward) []domain.Reward {
|
||||
out := make([]domain.Reward, 0, len(rewards))
|
||||
for _, reward := range rewards {
|
||||
if reward.RankNo > 0 && reward.ThresholdCoinSpent > 0 && reward.RewardCoinAmount >= 0 {
|
||||
out = append(out, reward)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func matchAgencyOpeningRewardTier(rewards []domain.Reward, scoreCoinAmount int64) (domain.Reward, bool) {
|
||||
var matched domain.Reward
|
||||
found := false
|
||||
for _, reward := range rewards {
|
||||
if scoreCoinAmount >= reward.ThresholdCoinSpent && (!found || reward.ThresholdCoinSpent > matched.ThresholdCoinSpent) {
|
||||
matched = reward
|
||||
found = true
|
||||
}
|
||||
}
|
||||
return matched, found
|
||||
}
|
||||
|
||||
func truncateAgencyOpeningFailure(value string) string {
|
||||
value = strings.TrimSpace(value)
|
||||
if len(value) <= 512 {
|
||||
return value
|
||||
}
|
||||
return value[:512]
|
||||
}
|
||||
@ -1,55 +0,0 @@
|
||||
package mysql
|
||||
|
||||
import "context"
|
||||
|
||||
func (r *Repository) ensureCPWeeklyRankTables(ctx context.Context) error {
|
||||
statements := []string{
|
||||
`CREATE TABLE IF NOT EXISTS cp_weekly_rank_configs (
|
||||
app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码',
|
||||
activity_code VARCHAR(64) NOT NULL DEFAULT 'cp_weekly_rank' COMMENT '活动编码',
|
||||
enabled TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否启用',
|
||||
relation_type VARCHAR(32) NOT NULL DEFAULT 'cp' COMMENT '关系类型,当前固定 cp',
|
||||
top_count INT NOT NULL DEFAULT 3 COMMENT '结算 Top 数量',
|
||||
updated_by_admin_id BIGINT NOT NULL DEFAULT 0 COMMENT '最后更新管理员',
|
||||
created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms',
|
||||
updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms',
|
||||
PRIMARY KEY (app_code)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='CP 周榜配置表'`,
|
||||
`CREATE TABLE IF NOT EXISTS cp_weekly_rank_rewards (
|
||||
app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码',
|
||||
rank_no INT NOT NULL COMMENT '名次',
|
||||
resource_group_id BIGINT NOT NULL COMMENT '奖励资源组 ID',
|
||||
created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms',
|
||||
updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms',
|
||||
PRIMARY KEY (app_code, rank_no)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='CP 周榜名次奖励资源组表'`,
|
||||
`CREATE TABLE IF NOT EXISTS cp_weekly_rank_settlements (
|
||||
app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码',
|
||||
settlement_id VARCHAR(96) NOT NULL COMMENT '发奖记录 ID',
|
||||
period_start_ms BIGINT NOT NULL COMMENT '周周期开始,UTC epoch ms',
|
||||
period_end_ms BIGINT NOT NULL COMMENT '周周期结束,UTC epoch ms',
|
||||
rank_no INT NOT NULL COMMENT '名次',
|
||||
relationship_id VARCHAR(128) NOT NULL COMMENT '获奖 CP 关系 ID',
|
||||
user_id BIGINT NOT NULL COMMENT '获奖用户 ID',
|
||||
score BIGINT NOT NULL DEFAULT 0 COMMENT '关系周榜分数',
|
||||
resource_group_id BIGINT NOT NULL COMMENT '发放资源组 ID',
|
||||
wallet_command_id VARCHAR(192) NOT NULL COMMENT 'wallet 发奖幂等键',
|
||||
wallet_grant_id VARCHAR(128) NOT NULL DEFAULT '' COMMENT 'wallet grant ID',
|
||||
status VARCHAR(32) NOT NULL COMMENT 'pending/running/granted/failed',
|
||||
failure_reason VARCHAR(512) NOT NULL DEFAULT '' COMMENT '失败原因',
|
||||
attempt_count INT NOT NULL DEFAULT 0 COMMENT '尝试次数',
|
||||
created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms',
|
||||
updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms',
|
||||
PRIMARY KEY (app_code, settlement_id),
|
||||
UNIQUE KEY uk_cp_weekly_rank_period_user (app_code, period_start_ms, rank_no, relationship_id, user_id),
|
||||
UNIQUE KEY uk_cp_weekly_rank_wallet_command (app_code, wallet_command_id),
|
||||
KEY idx_cp_weekly_rank_pending (app_code, period_start_ms, period_end_ms, status, updated_at_ms)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='CP 周榜发奖事实表'`,
|
||||
}
|
||||
for _, statement := range statements {
|
||||
if _, err := r.db.ExecContext(ctx, statement); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -1,259 +0,0 @@
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"hyapp/pkg/appcode"
|
||||
"hyapp/pkg/idgen"
|
||||
"hyapp/pkg/xerr"
|
||||
domain "hyapp/services/activity-service/internal/domain/cpweeklyrank"
|
||||
)
|
||||
|
||||
func (r *Repository) GetCPWeeklyRankConfig(ctx context.Context) (domain.Config, bool, error) {
|
||||
if r == nil || r.db == nil {
|
||||
return domain.Config{}, false, xerr.New(xerr.Unavailable, "mysql repository is not configured")
|
||||
}
|
||||
appCode := appcode.FromContext(ctx)
|
||||
config, err := scanCPWeeklyRankConfig(r.db.QueryRowContext(ctx, `
|
||||
SELECT app_code, activity_code, enabled, relation_type, top_count, updated_by_admin_id, created_at_ms, updated_at_ms
|
||||
FROM cp_weekly_rank_configs
|
||||
WHERE app_code = ?`, appCode))
|
||||
if err == sql.ErrNoRows {
|
||||
return domain.Config{}, false, nil
|
||||
}
|
||||
if err != nil {
|
||||
return domain.Config{}, false, err
|
||||
}
|
||||
rewards, err := r.listCPWeeklyRankRewards(ctx, appCode)
|
||||
if err != nil {
|
||||
return domain.Config{}, false, err
|
||||
}
|
||||
config.Rewards = rewards
|
||||
return config, true, nil
|
||||
}
|
||||
|
||||
func (r *Repository) UpdateCPWeeklyRankConfig(ctx context.Context, config domain.Config, nowMS int64) (domain.Config, error) {
|
||||
if r == nil || r.db == nil {
|
||||
return domain.Config{}, xerr.New(xerr.Unavailable, "mysql repository is not configured")
|
||||
}
|
||||
appCode := appcode.FromContext(ctx)
|
||||
tx, err := r.db.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
return domain.Config{}, err
|
||||
}
|
||||
defer func() { _ = tx.Rollback() }()
|
||||
// 配置行和奖励行必须一起替换;否则后台保存成功但 cron 读到半套奖励会导致榜单部分名次无法发奖。
|
||||
if _, err := tx.ExecContext(ctx, `
|
||||
INSERT INTO cp_weekly_rank_configs (
|
||||
app_code, activity_code, enabled, relation_type, top_count, updated_by_admin_id, created_at_ms, updated_at_ms
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
activity_code = VALUES(activity_code),
|
||||
enabled = VALUES(enabled),
|
||||
relation_type = VALUES(relation_type),
|
||||
top_count = VALUES(top_count),
|
||||
updated_by_admin_id = VALUES(updated_by_admin_id),
|
||||
updated_at_ms = VALUES(updated_at_ms)`,
|
||||
appCode, domain.ActivityCode, config.Enabled, "cp", config.TopCount, config.UpdatedByAdminID, nowMS, nowMS,
|
||||
); err != nil {
|
||||
return domain.Config{}, err
|
||||
}
|
||||
if _, err := tx.ExecContext(ctx, `DELETE FROM cp_weekly_rank_rewards WHERE app_code = ?`, appCode); err != nil {
|
||||
return domain.Config{}, err
|
||||
}
|
||||
for _, reward := range config.Rewards {
|
||||
if _, err := tx.ExecContext(ctx, `
|
||||
INSERT INTO cp_weekly_rank_rewards (
|
||||
app_code, rank_no, resource_group_id, created_at_ms, updated_at_ms
|
||||
) VALUES (?, ?, ?, ?, ?)`,
|
||||
appCode, reward.RankNo, reward.ResourceGroupID, nowMS, nowMS,
|
||||
); err != nil {
|
||||
return domain.Config{}, err
|
||||
}
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
return domain.Config{}, err
|
||||
}
|
||||
stored, _, err := r.GetCPWeeklyRankConfig(ctx)
|
||||
return stored, err
|
||||
}
|
||||
|
||||
func (r *Repository) ListCPWeeklyRankSettlements(ctx context.Context, periodStartMS int64, periodEndMS int64) ([]domain.Settlement, error) {
|
||||
if r == nil || r.db == nil {
|
||||
return nil, xerr.New(xerr.Unavailable, "mysql repository is not configured")
|
||||
}
|
||||
where := []string{"app_code = ?"}
|
||||
args := []any{appcode.FromContext(ctx)}
|
||||
if periodStartMS > 0 {
|
||||
where = append(where, "period_start_ms = ?")
|
||||
args = append(args, periodStartMS)
|
||||
}
|
||||
if periodEndMS > 0 {
|
||||
where = append(where, "period_end_ms = ?")
|
||||
args = append(args, periodEndMS)
|
||||
}
|
||||
rows, err := r.db.QueryContext(ctx, `
|
||||
SELECT app_code, settlement_id, period_start_ms, period_end_ms, rank_no, relationship_id, user_id, score,
|
||||
resource_group_id, wallet_command_id, wallet_grant_id, status, failure_reason, attempt_count, created_at_ms, updated_at_ms
|
||||
FROM cp_weekly_rank_settlements
|
||||
WHERE `+strings.Join(where, " AND ")+`
|
||||
ORDER BY period_start_ms DESC, rank_no ASC, relationship_id ASC, user_id ASC`, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
return scanCPWeeklyRankSettlements(rows)
|
||||
}
|
||||
|
||||
func (r *Repository) PrepareCPWeeklyRankSettlements(ctx context.Context, config domain.Config, entries []domain.Entry, periodStartMS int64, periodEndMS int64, nowMS int64) ([]domain.Settlement, error) {
|
||||
if r == nil || r.db == nil {
|
||||
return nil, xerr.New(xerr.Unavailable, "mysql repository is not configured")
|
||||
}
|
||||
appCode := appcode.FromContext(ctx)
|
||||
tx, err := r.db.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() { _ = tx.Rollback() }()
|
||||
var existing int
|
||||
if err := tx.QueryRowContext(ctx, `
|
||||
SELECT COUNT(*)
|
||||
FROM cp_weekly_rank_settlements
|
||||
WHERE app_code = ? AND period_start_ms = ? AND period_end_ms = ?`,
|
||||
appCode, periodStartMS, periodEndMS,
|
||||
).Scan(&existing); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if existing == 0 {
|
||||
rewardsByRank := make(map[int32]int64, len(config.Rewards))
|
||||
for _, reward := range config.Rewards {
|
||||
rewardsByRank[reward.RankNo] = reward.ResourceGroupID
|
||||
}
|
||||
for _, entry := range entries {
|
||||
rankNo := int32(entry.Rank)
|
||||
resourceGroupID := rewardsByRank[rankNo]
|
||||
if resourceGroupID <= 0 {
|
||||
continue
|
||||
}
|
||||
for _, userID := range []int64{entry.UserA.UserID, entry.UserB.UserID} {
|
||||
if userID <= 0 {
|
||||
continue
|
||||
}
|
||||
commandID := fmt.Sprintf("cp_weekly_rank:%d:%d:%s:%d", periodStartMS, rankNo, entry.RelationshipID, userID)
|
||||
// 每个获奖关系拆成两个用户发奖记录;INSERT IGNORE 配合唯一键保证同一周期并发 cron 不会重复固化。
|
||||
if _, err := tx.ExecContext(ctx, `
|
||||
INSERT IGNORE INTO cp_weekly_rank_settlements (
|
||||
app_code, settlement_id, period_start_ms, period_end_ms, rank_no, relationship_id, user_id, score,
|
||||
resource_group_id, wallet_command_id, status, created_at_ms, updated_at_ms
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
appCode, idgen.New("cpwrs"), periodStartMS, periodEndMS, rankNo, entry.RelationshipID, userID, entry.Score,
|
||||
resourceGroupID, commandID, domain.SettlementStatusPending, nowMS, nowMS,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return r.ListPendingCPWeeklyRankSettlements(ctx, periodStartMS, periodEndMS, 1000)
|
||||
}
|
||||
|
||||
func (r *Repository) ListPendingCPWeeklyRankSettlements(ctx context.Context, periodStartMS int64, periodEndMS int64, limit int) ([]domain.Settlement, error) {
|
||||
if r == nil || r.db == nil {
|
||||
return nil, xerr.New(xerr.Unavailable, "mysql repository is not configured")
|
||||
}
|
||||
if limit <= 0 {
|
||||
limit = 50
|
||||
}
|
||||
rows, err := r.db.QueryContext(ctx, `
|
||||
SELECT app_code, settlement_id, period_start_ms, period_end_ms, rank_no, relationship_id, user_id, score,
|
||||
resource_group_id, wallet_command_id, wallet_grant_id, status, failure_reason, attempt_count, created_at_ms, updated_at_ms
|
||||
FROM cp_weekly_rank_settlements
|
||||
WHERE app_code = ? AND period_start_ms = ? AND period_end_ms = ? AND status IN (?, ?)
|
||||
ORDER BY rank_no ASC, relationship_id ASC, user_id ASC
|
||||
LIMIT ?`,
|
||||
appcode.FromContext(ctx), periodStartMS, periodEndMS, domain.SettlementStatusPending, domain.SettlementStatusFailed, limit,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
return scanCPWeeklyRankSettlements(rows)
|
||||
}
|
||||
|
||||
func (r *Repository) MarkCPWeeklyRankSettlementGranted(ctx context.Context, settlementID string, walletGrantID string, nowMS int64) error {
|
||||
_, err := r.db.ExecContext(ctx, `
|
||||
UPDATE cp_weekly_rank_settlements
|
||||
SET status = ?, wallet_grant_id = ?, failure_reason = '', updated_at_ms = ?
|
||||
WHERE app_code = ? AND settlement_id = ?`,
|
||||
domain.SettlementStatusGranted, walletGrantID, nowMS, appcode.FromContext(ctx), settlementID,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *Repository) MarkCPWeeklyRankSettlementFailed(ctx context.Context, settlementID string, reason string, nowMS int64) error {
|
||||
_, err := r.db.ExecContext(ctx, `
|
||||
UPDATE cp_weekly_rank_settlements
|
||||
SET status = ?, failure_reason = ?, attempt_count = attempt_count + 1, updated_at_ms = ?
|
||||
WHERE app_code = ? AND settlement_id = ?`,
|
||||
domain.SettlementStatusFailed, truncateCPWeeklyRankFailure(reason), nowMS, appcode.FromContext(ctx), settlementID,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *Repository) listCPWeeklyRankRewards(ctx context.Context, appCode string) ([]domain.Reward, error) {
|
||||
rows, err := r.db.QueryContext(ctx, `
|
||||
SELECT app_code, rank_no, resource_group_id, created_at_ms, updated_at_ms
|
||||
FROM cp_weekly_rank_rewards
|
||||
WHERE app_code = ?
|
||||
ORDER BY rank_no ASC`, appCode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
rewards := make([]domain.Reward, 0)
|
||||
for rows.Next() {
|
||||
var reward domain.Reward
|
||||
if err := rows.Scan(&reward.AppCode, &reward.RankNo, &reward.ResourceGroupID, &reward.CreatedAtMS, &reward.UpdatedAtMS); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rewards = append(rewards, reward)
|
||||
}
|
||||
return rewards, rows.Err()
|
||||
}
|
||||
|
||||
func scanCPWeeklyRankConfig(row interface{ Scan(dest ...any) error }) (domain.Config, error) {
|
||||
var config domain.Config
|
||||
var enabled bool
|
||||
err := row.Scan(&config.AppCode, &config.ActivityCode, &enabled, &config.RelationType, &config.TopCount, &config.UpdatedByAdminID, &config.CreatedAtMS, &config.UpdatedAtMS)
|
||||
config.Enabled = enabled
|
||||
return config, err
|
||||
}
|
||||
|
||||
func scanCPWeeklyRankSettlements(rows *sql.Rows) ([]domain.Settlement, error) {
|
||||
items := make([]domain.Settlement, 0)
|
||||
for rows.Next() {
|
||||
var item domain.Settlement
|
||||
if err := rows.Scan(
|
||||
&item.AppCode, &item.SettlementID, &item.PeriodStartMS, &item.PeriodEndMS, &item.RankNo, &item.RelationshipID, &item.UserID, &item.Score,
|
||||
&item.ResourceGroupID, &item.WalletCommandID, &item.WalletGrantID, &item.Status, &item.FailureReason, &item.AttemptCount, &item.CreatedAtMS, &item.UpdatedAtMS,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, item)
|
||||
}
|
||||
return items, rows.Err()
|
||||
}
|
||||
|
||||
func truncateCPWeeklyRankFailure(value string) string {
|
||||
value = strings.TrimSpace(value)
|
||||
if len(value) > 512 {
|
||||
return value[:512]
|
||||
}
|
||||
return value
|
||||
}
|
||||
@ -69,9 +69,6 @@ func (r *Repository) Migrate(ctx context.Context) error {
|
||||
if err := r.ensureWeeklyStarTables(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := r.ensureAgencyOpeningTables(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := r.ensureLuckyUserStateFlowColumns(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -87,9 +84,6 @@ func (r *Repository) Migrate(ctx context.Context) error {
|
||||
if err := r.ensureInviteActivityRewardTables(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := r.ensureCPWeeklyRankTables(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := r.ensureTaskDefinitionMetadataColumns(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -1,239 +0,0 @@
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
activityv1 "hyapp.local/api/proto/activity/v1"
|
||||
"hyapp/pkg/appcode"
|
||||
"hyapp/pkg/xerr"
|
||||
domain "hyapp/services/activity-service/internal/domain/agencyopening"
|
||||
service "hyapp/services/activity-service/internal/service/agencyopening"
|
||||
)
|
||||
|
||||
type AgencyOpeningServer struct {
|
||||
activityv1.UnimplementedAgencyOpeningServiceServer
|
||||
svc *service.Service
|
||||
}
|
||||
|
||||
func NewAgencyOpeningServer(svc *service.Service) *AgencyOpeningServer {
|
||||
return &AgencyOpeningServer{svc: svc}
|
||||
}
|
||||
|
||||
func (s *AgencyOpeningServer) GetAgencyOpeningStatus(ctx context.Context, req *activityv1.GetAgencyOpeningStatusRequest) (*activityv1.GetAgencyOpeningStatusResponse, error) {
|
||||
ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
|
||||
status, err := s.svc.GetStatus(ctx, req.GetUserId())
|
||||
if err != nil {
|
||||
return nil, xerr.ToGRPCError(err)
|
||||
}
|
||||
return &activityv1.GetAgencyOpeningStatusResponse{
|
||||
Cycle: agencyOpeningCycleToProto(status.Cycle),
|
||||
Application: agencyOpeningApplicationToProto(status.Application),
|
||||
Enabled: status.Enabled,
|
||||
Eligible: status.Eligible,
|
||||
Joined: status.Joined,
|
||||
IneligibleReason: status.IneligibleReason,
|
||||
ServerTimeMs: status.ServerTimeMS,
|
||||
Agency: agencyOpeningAgencyToProto(status.Agency),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *AgencyOpeningServer) ApplyAgencyOpening(ctx context.Context, req *activityv1.ApplyAgencyOpeningRequest) (*activityv1.ApplyAgencyOpeningResponse, error) {
|
||||
ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
|
||||
application, created, err := s.svc.Apply(ctx, req.GetUserId(), req.GetCommandId())
|
||||
if err != nil {
|
||||
return nil, xerr.ToGRPCError(err)
|
||||
}
|
||||
return &activityv1.ApplyAgencyOpeningResponse{Application: agencyOpeningApplicationToProto(application), Created: created}, nil
|
||||
}
|
||||
|
||||
func (s *AgencyOpeningServer) ListAgencyOpeningLeaderboard(ctx context.Context, req *activityv1.ListAgencyOpeningApplicationsRequest) (*activityv1.ListAgencyOpeningApplicationsResponse, error) {
|
||||
ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
|
||||
items, total, err := s.svc.ListApplications(ctx, agencyOpeningApplicationQueryFromProto(req))
|
||||
if err != nil {
|
||||
return nil, xerr.ToGRPCError(err)
|
||||
}
|
||||
return agencyOpeningApplicationsResponse(items, total), nil
|
||||
}
|
||||
|
||||
type AdminAgencyOpeningServer struct {
|
||||
activityv1.UnimplementedAdminAgencyOpeningServiceServer
|
||||
svc *service.Service
|
||||
}
|
||||
|
||||
func NewAdminAgencyOpeningServer(svc *service.Service) *AdminAgencyOpeningServer {
|
||||
return &AdminAgencyOpeningServer{svc: svc}
|
||||
}
|
||||
|
||||
func (s *AdminAgencyOpeningServer) ListAgencyOpeningCycles(ctx context.Context, req *activityv1.ListAgencyOpeningCyclesRequest) (*activityv1.ListAgencyOpeningCyclesResponse, error) {
|
||||
ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
|
||||
cycles, total, err := s.svc.ListCycles(ctx, domain.ListQuery{Status: req.GetStatus(), StartMS: req.GetStartMs(), EndMS: req.GetEndMs(), Page: req.GetPage(), PageSize: req.GetPageSize()})
|
||||
if err != nil {
|
||||
return nil, xerr.ToGRPCError(err)
|
||||
}
|
||||
resp := &activityv1.ListAgencyOpeningCyclesResponse{Cycles: make([]*activityv1.AgencyOpeningCycle, 0, len(cycles)), Total: total}
|
||||
for _, cycle := range cycles {
|
||||
resp.Cycles = append(resp.Cycles, agencyOpeningCycleToProto(cycle))
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (s *AdminAgencyOpeningServer) CreateAgencyOpeningCycle(ctx context.Context, req *activityv1.UpsertAgencyOpeningCycleRequest) (*activityv1.UpsertAgencyOpeningCycleResponse, error) {
|
||||
ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
|
||||
cycle, created, err := s.svc.CreateCycle(ctx, domain.CycleCommand{Cycle: agencyOpeningCycleFromProto(req.GetCycle()), OperatorAdminID: req.GetOperatorAdminId()})
|
||||
if err != nil {
|
||||
return nil, xerr.ToGRPCError(err)
|
||||
}
|
||||
return &activityv1.UpsertAgencyOpeningCycleResponse{Cycle: agencyOpeningCycleToProto(cycle), Created: created}, nil
|
||||
}
|
||||
|
||||
func (s *AdminAgencyOpeningServer) GetAgencyOpeningCycle(ctx context.Context, req *activityv1.GetAgencyOpeningCycleRequest) (*activityv1.GetAgencyOpeningCycleResponse, error) {
|
||||
ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
|
||||
cycle, err := s.svc.GetCycle(ctx, req.GetCycleId())
|
||||
if err != nil {
|
||||
return nil, xerr.ToGRPCError(err)
|
||||
}
|
||||
return &activityv1.GetAgencyOpeningCycleResponse{Cycle: agencyOpeningCycleToProto(cycle)}, nil
|
||||
}
|
||||
|
||||
func (s *AdminAgencyOpeningServer) UpdateAgencyOpeningCycle(ctx context.Context, req *activityv1.UpsertAgencyOpeningCycleRequest) (*activityv1.UpsertAgencyOpeningCycleResponse, error) {
|
||||
ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
|
||||
cycle, created, err := s.svc.UpdateCycle(ctx, domain.CycleCommand{Cycle: agencyOpeningCycleFromProto(req.GetCycle()), OperatorAdminID: req.GetOperatorAdminId()})
|
||||
if err != nil {
|
||||
return nil, xerr.ToGRPCError(err)
|
||||
}
|
||||
return &activityv1.UpsertAgencyOpeningCycleResponse{Cycle: agencyOpeningCycleToProto(cycle), Created: created}, nil
|
||||
}
|
||||
|
||||
func (s *AdminAgencyOpeningServer) SetAgencyOpeningCycleStatus(ctx context.Context, req *activityv1.SetAgencyOpeningCycleStatusRequest) (*activityv1.SetAgencyOpeningCycleStatusResponse, error) {
|
||||
ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
|
||||
cycle, err := s.svc.SetCycleStatus(ctx, req.GetCycleId(), req.GetStatus(), req.GetOperatorAdminId())
|
||||
if err != nil {
|
||||
return nil, xerr.ToGRPCError(err)
|
||||
}
|
||||
return &activityv1.SetAgencyOpeningCycleStatusResponse{Cycle: agencyOpeningCycleToProto(cycle)}, nil
|
||||
}
|
||||
|
||||
func (s *AdminAgencyOpeningServer) ListAgencyOpeningApplications(ctx context.Context, req *activityv1.ListAgencyOpeningApplicationsRequest) (*activityv1.ListAgencyOpeningApplicationsResponse, error) {
|
||||
ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
|
||||
items, total, err := s.svc.ListApplications(ctx, agencyOpeningApplicationQueryFromProto(req))
|
||||
if err != nil {
|
||||
return nil, xerr.ToGRPCError(err)
|
||||
}
|
||||
return agencyOpeningApplicationsResponse(items, total), nil
|
||||
}
|
||||
|
||||
func agencyOpeningCycleFromProto(item *activityv1.AgencyOpeningCycle) domain.Cycle {
|
||||
if item == nil {
|
||||
return domain.Cycle{}
|
||||
}
|
||||
cycle := domain.Cycle{
|
||||
CycleID: item.GetCycleId(),
|
||||
ActivityCode: item.GetActivityCode(),
|
||||
Title: item.GetTitle(),
|
||||
Status: item.GetStatus(),
|
||||
StartMS: item.GetStartMs(),
|
||||
EndMS: item.GetEndMs(),
|
||||
MinHostCount: item.GetMinHostCount(),
|
||||
MaxAgencyAgeDays: item.GetMaxAgencyAgeDays(),
|
||||
CreatedByAdminID: item.GetCreatedByAdminId(),
|
||||
UpdatedByAdminID: item.GetUpdatedByAdminId(),
|
||||
}
|
||||
for _, reward := range item.GetRewards() {
|
||||
cycle.Rewards = append(cycle.Rewards, domain.Reward{
|
||||
RankNo: reward.GetRankNo(),
|
||||
ThresholdCoinSpent: reward.GetThresholdCoinSpent(),
|
||||
RewardCoinAmount: reward.GetRewardCoinAmount(),
|
||||
})
|
||||
}
|
||||
return cycle
|
||||
}
|
||||
|
||||
func agencyOpeningCycleToProto(item domain.Cycle) *activityv1.AgencyOpeningCycle {
|
||||
if item.CycleID == "" {
|
||||
return nil
|
||||
}
|
||||
resp := &activityv1.AgencyOpeningCycle{
|
||||
AppCode: item.AppCode,
|
||||
CycleId: item.CycleID,
|
||||
ActivityCode: item.ActivityCode,
|
||||
Title: item.Title,
|
||||
Status: item.Status,
|
||||
StartMs: item.StartMS,
|
||||
EndMs: item.EndMS,
|
||||
MinHostCount: item.MinHostCount,
|
||||
MaxAgencyAgeDays: item.MaxAgencyAgeDays,
|
||||
CreatedByAdminId: item.CreatedByAdminID,
|
||||
UpdatedByAdminId: item.UpdatedByAdminID,
|
||||
SettledAtMs: item.SettledAtMS,
|
||||
CreatedAtMs: item.CreatedAtMS,
|
||||
UpdatedAtMs: item.UpdatedAtMS,
|
||||
Rewards: make([]*activityv1.AgencyOpeningReward, 0, len(item.Rewards)),
|
||||
}
|
||||
for _, reward := range item.Rewards {
|
||||
resp.Rewards = append(resp.Rewards, &activityv1.AgencyOpeningReward{
|
||||
RankNo: reward.RankNo,
|
||||
ThresholdCoinSpent: reward.ThresholdCoinSpent,
|
||||
RewardCoinAmount: reward.RewardCoinAmount,
|
||||
})
|
||||
}
|
||||
return resp
|
||||
}
|
||||
|
||||
func agencyOpeningApplicationToProto(item domain.Application) *activityv1.AgencyOpeningApplication {
|
||||
if item.ApplicationID == "" {
|
||||
return nil
|
||||
}
|
||||
return &activityv1.AgencyOpeningApplication{
|
||||
ApplicationId: item.ApplicationID,
|
||||
CycleId: item.CycleID,
|
||||
AgencyId: item.AgencyID,
|
||||
AgencyOwnerUserId: item.AgencyOwnerUserID,
|
||||
AgencyName: item.AgencyName,
|
||||
HostCount: item.HostCount,
|
||||
AgencyCreatedAtMs: item.AgencyCreatedAtMS,
|
||||
Status: item.Status,
|
||||
ScoreCoinAmount: item.ScoreCoinAmount,
|
||||
RewardRankNo: item.RewardRankNo,
|
||||
RewardThresholdCoinSpent: item.RewardThresholdCoin,
|
||||
RewardCoinAmount: item.RewardCoinAmount,
|
||||
WalletCommandId: item.WalletCommandID,
|
||||
WalletTransactionId: item.WalletTransactionID,
|
||||
FailureReason: item.FailureReason,
|
||||
AppliedAtMs: item.AppliedAtMS,
|
||||
GrantedAtMs: item.GrantedAtMS,
|
||||
UpdatedAtMs: item.UpdatedAtMS,
|
||||
}
|
||||
}
|
||||
|
||||
func agencyOpeningAgencyToProto(item domain.AgencySnapshot) *activityv1.AgencyOpeningAgencySnapshot {
|
||||
if item.AgencyID <= 0 {
|
||||
return nil
|
||||
}
|
||||
return &activityv1.AgencyOpeningAgencySnapshot{
|
||||
AgencyId: item.AgencyID,
|
||||
OwnerUserId: item.OwnerUserID,
|
||||
Name: item.Name,
|
||||
Status: item.Status,
|
||||
HostCount: item.HostCount,
|
||||
AgencyCreatedAtMs: item.AgencyCreatedAtMS,
|
||||
}
|
||||
}
|
||||
|
||||
func agencyOpeningApplicationsResponse(items []domain.Application, total int64) *activityv1.ListAgencyOpeningApplicationsResponse {
|
||||
resp := &activityv1.ListAgencyOpeningApplicationsResponse{Applications: make([]*activityv1.AgencyOpeningApplication, 0, len(items)), Total: total}
|
||||
for _, item := range items {
|
||||
resp.Applications = append(resp.Applications, agencyOpeningApplicationToProto(item))
|
||||
}
|
||||
return resp
|
||||
}
|
||||
|
||||
func agencyOpeningApplicationQueryFromProto(req *activityv1.ListAgencyOpeningApplicationsRequest) domain.ApplicationQuery {
|
||||
return domain.ApplicationQuery{
|
||||
CycleID: req.GetCycleId(),
|
||||
Status: req.GetStatus(),
|
||||
AgencyID: req.GetAgencyId(),
|
||||
AgencyOwnerUserID: req.GetAgencyOwnerUserId(),
|
||||
Page: req.GetPage(),
|
||||
PageSize: req.GetPageSize(),
|
||||
}
|
||||
}
|
||||
@ -7,7 +7,6 @@ import (
|
||||
activityv1 "hyapp.local/api/proto/activity/v1"
|
||||
"hyapp/pkg/appcode"
|
||||
"hyapp/pkg/xerr"
|
||||
agencyopeningservice "hyapp/services/activity-service/internal/service/agencyopening"
|
||||
broadcastservice "hyapp/services/activity-service/internal/service/broadcast"
|
||||
growthservice "hyapp/services/activity-service/internal/service/growth"
|
||||
roomturnoverrewardservice "hyapp/services/activity-service/internal/service/roomturnoverreward"
|
||||
@ -26,20 +25,20 @@ type BroadcastServer struct {
|
||||
task *taskservice.Service
|
||||
roomReward *roomturnoverrewardservice.Service
|
||||
weeklyStar *weeklystarservice.Service
|
||||
agencyOpen *agencyopeningservice.Service
|
||||
}
|
||||
|
||||
// NewBroadcastServer 创建播报和 room event 消费共用的 gRPC adapter。
|
||||
// room-service 的 outbox 事件只投递一次,但 activity-service 内部有多条独立投影:
|
||||
// 播报负责 IM 展示,growth 负责等级进度,weekly-star 负责指定礼物榜单,roomReward 负责房间流水。
|
||||
// 这里把这些模块都挂到同一个 gRPC 入口,保证本地直连投递和 MQ 投递使用同一组业务副作用。
|
||||
func NewBroadcastServer(svc *broadcastservice.Service, growthSvc *growthservice.Service, weeklyStarSvc *weeklystarservice.Service, taskSvc *taskservice.Service, roomRewardSvc *roomturnoverrewardservice.Service, agencyOpeningSvc *agencyopeningservice.Service) *BroadcastServer {
|
||||
func NewBroadcastServer(svc *broadcastservice.Service, growthSvc *growthservice.Service, weeklyStarSvc *weeklystarservice.Service, taskSvc *taskservice.Service, roomRewardSvc ...*roomturnoverrewardservice.Service) *BroadcastServer {
|
||||
server := &BroadcastServer{svc: svc}
|
||||
server.growth = growthSvc
|
||||
server.weeklyStar = weeklyStarSvc
|
||||
server.task = taskSvc
|
||||
server.roomReward = roomRewardSvc
|
||||
server.agencyOpen = agencyOpeningSvc
|
||||
if len(roomRewardSvc) > 0 {
|
||||
server.roomReward = roomRewardSvc[0]
|
||||
}
|
||||
return server
|
||||
}
|
||||
|
||||
@ -130,11 +129,6 @@ func (s *BroadcastServer) ConsumeRoomEvent(ctx context.Context, req *activityv1.
|
||||
return nil, xerr.ToGRPCError(err)
|
||||
}
|
||||
}
|
||||
if s.agencyOpen != nil {
|
||||
if _, err := s.agencyOpen.HandleRoomEvent(ctx, req.GetEnvelope()); err != nil {
|
||||
return nil, xerr.ToGRPCError(err)
|
||||
}
|
||||
}
|
||||
if s.task != nil {
|
||||
if _, err := s.task.HandleRoomEvent(ctx, req.GetEnvelope()); err != nil {
|
||||
return nil, xerr.ToGRPCError(err)
|
||||
|
||||
@ -1,176 +0,0 @@
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
activityv1 "hyapp.local/api/proto/activity/v1"
|
||||
"hyapp/pkg/appcode"
|
||||
"hyapp/pkg/xerr"
|
||||
domain "hyapp/services/activity-service/internal/domain/cpweeklyrank"
|
||||
service "hyapp/services/activity-service/internal/service/cpweeklyrank"
|
||||
)
|
||||
|
||||
type CPWeeklyRankServer struct {
|
||||
activityv1.UnimplementedCPWeeklyRankServiceServer
|
||||
activityv1.UnimplementedAdminCPWeeklyRankServiceServer
|
||||
service *service.Service
|
||||
}
|
||||
|
||||
func NewCPWeeklyRankServer(service *service.Service) *CPWeeklyRankServer {
|
||||
return &CPWeeklyRankServer{service: service}
|
||||
}
|
||||
|
||||
func (s *CPWeeklyRankServer) GetCPWeeklyRankStatus(ctx context.Context, req *activityv1.GetCPWeeklyRankStatusRequest) (*activityv1.GetCPWeeklyRankStatusResponse, error) {
|
||||
ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
|
||||
if s.service == nil {
|
||||
return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "cp weekly rank service is not configured"))
|
||||
}
|
||||
status, err := s.service.GetStatus(ctx, req.GetLimit(), req.GetNowMs())
|
||||
if err != nil {
|
||||
return nil, xerr.ToGRPCError(err)
|
||||
}
|
||||
return &activityv1.GetCPWeeklyRankStatusResponse{
|
||||
Config: cpWeeklyRankConfigToProto(status.Config),
|
||||
Entries: cpWeeklyRankEntriesToProto(status.Entries),
|
||||
PeriodStartMs: status.PeriodStartMS,
|
||||
PeriodEndMs: status.PeriodEndMS,
|
||||
ServerTimeMs: status.ServerTimeMS,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *CPWeeklyRankServer) GetCPWeeklyRankConfig(ctx context.Context, req *activityv1.GetCPWeeklyRankConfigRequest) (*activityv1.GetCPWeeklyRankConfigResponse, error) {
|
||||
ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
|
||||
if s.service == nil {
|
||||
return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "cp weekly rank service is not configured"))
|
||||
}
|
||||
config, err := s.service.GetConfig(ctx)
|
||||
if err != nil {
|
||||
return nil, xerr.ToGRPCError(err)
|
||||
}
|
||||
return &activityv1.GetCPWeeklyRankConfigResponse{Config: cpWeeklyRankConfigToProto(config)}, nil
|
||||
}
|
||||
|
||||
func (s *CPWeeklyRankServer) UpdateCPWeeklyRankConfig(ctx context.Context, req *activityv1.UpdateCPWeeklyRankConfigRequest) (*activityv1.UpdateCPWeeklyRankConfigResponse, error) {
|
||||
ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
|
||||
if s.service == nil {
|
||||
return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "cp weekly rank service is not configured"))
|
||||
}
|
||||
config, err := s.service.UpdateConfig(ctx, cpWeeklyRankConfigFromProto(req.GetConfig()), req.GetOperatorAdminId())
|
||||
if err != nil {
|
||||
return nil, xerr.ToGRPCError(err)
|
||||
}
|
||||
return &activityv1.UpdateCPWeeklyRankConfigResponse{Config: cpWeeklyRankConfigToProto(config)}, nil
|
||||
}
|
||||
|
||||
func (s *CPWeeklyRankServer) ListCPWeeklyRankSettlements(ctx context.Context, req *activityv1.ListCPWeeklyRankSettlementsRequest) (*activityv1.ListCPWeeklyRankSettlementsResponse, error) {
|
||||
ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
|
||||
if s.service == nil {
|
||||
return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "cp weekly rank service is not configured"))
|
||||
}
|
||||
items, err := s.service.ListSettlements(ctx, req.GetPeriodStartMs(), req.GetPeriodEndMs())
|
||||
if err != nil {
|
||||
return nil, xerr.ToGRPCError(err)
|
||||
}
|
||||
return &activityv1.ListCPWeeklyRankSettlementsResponse{Settlements: cpWeeklyRankSettlementsToProto(items)}, nil
|
||||
}
|
||||
|
||||
func cpWeeklyRankConfigFromProto(item *activityv1.CPWeeklyRankConfig) domain.Config {
|
||||
if item == nil {
|
||||
return domain.Config{}
|
||||
}
|
||||
config := domain.Config{
|
||||
AppCode: item.GetAppCode(),
|
||||
ActivityCode: item.GetActivityCode(),
|
||||
Enabled: item.GetEnabled(),
|
||||
RelationType: item.GetRelationType(),
|
||||
TopCount: item.GetTopCount(),
|
||||
UpdatedByAdminID: item.GetUpdatedByAdminId(),
|
||||
CreatedAtMS: item.GetCreatedAtMs(),
|
||||
UpdatedAtMS: item.GetUpdatedAtMs(),
|
||||
Rewards: make([]domain.Reward, 0, len(item.GetRewards())),
|
||||
}
|
||||
for _, reward := range item.GetRewards() {
|
||||
config.Rewards = append(config.Rewards, domain.Reward{
|
||||
RankNo: reward.GetRankNo(),
|
||||
ResourceGroupID: reward.GetResourceGroupId(),
|
||||
})
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
func cpWeeklyRankConfigToProto(item domain.Config) *activityv1.CPWeeklyRankConfig {
|
||||
return &activityv1.CPWeeklyRankConfig{
|
||||
ActivityCode: item.ActivityCode,
|
||||
Enabled: item.Enabled,
|
||||
RelationType: item.RelationType,
|
||||
TopCount: item.TopCount,
|
||||
Rewards: cpWeeklyRankRewardsToProto(item.Rewards),
|
||||
UpdatedByAdminId: item.UpdatedByAdminID,
|
||||
CreatedAtMs: item.CreatedAtMS,
|
||||
UpdatedAtMs: item.UpdatedAtMS,
|
||||
AppCode: item.AppCode,
|
||||
}
|
||||
}
|
||||
|
||||
func cpWeeklyRankRewardsToProto(items []domain.Reward) []*activityv1.CPWeeklyRankReward {
|
||||
resp := make([]*activityv1.CPWeeklyRankReward, 0, len(items))
|
||||
for _, item := range items {
|
||||
resp = append(resp, &activityv1.CPWeeklyRankReward{RankNo: item.RankNo, ResourceGroupId: item.ResourceGroupID})
|
||||
}
|
||||
return resp
|
||||
}
|
||||
|
||||
func cpWeeklyRankEntriesToProto(items []domain.Entry) []*activityv1.CPWeeklyRankEntry {
|
||||
resp := make([]*activityv1.CPWeeklyRankEntry, 0, len(items))
|
||||
for _, item := range items {
|
||||
resp = append(resp, &activityv1.CPWeeklyRankEntry{
|
||||
Rank: item.Rank,
|
||||
RelationshipId: item.RelationshipID,
|
||||
RelationType: item.RelationType,
|
||||
Score: item.Score,
|
||||
UserA: cpWeeklyRankUserToProto(item.UserA),
|
||||
UserB: cpWeeklyRankUserToProto(item.UserB),
|
||||
FormedAtMs: item.FormedAtMS,
|
||||
UpdatedAtMs: item.UpdatedAtMS,
|
||||
FirstScoredAtMs: item.FirstScoredAtMS,
|
||||
LastScoredAtMs: item.LastScoredAtMS,
|
||||
})
|
||||
}
|
||||
return resp
|
||||
}
|
||||
|
||||
func cpWeeklyRankUserToProto(item domain.User) *activityv1.CPWeeklyRankUser {
|
||||
if item.UserID <= 0 {
|
||||
return nil
|
||||
}
|
||||
return &activityv1.CPWeeklyRankUser{
|
||||
UserId: item.UserID,
|
||||
DisplayUserId: item.DisplayUserID,
|
||||
Username: item.Username,
|
||||
Avatar: item.Avatar,
|
||||
}
|
||||
}
|
||||
|
||||
func cpWeeklyRankSettlementsToProto(items []domain.Settlement) []*activityv1.CPWeeklyRankSettlement {
|
||||
resp := make([]*activityv1.CPWeeklyRankSettlement, 0, len(items))
|
||||
for _, item := range items {
|
||||
resp = append(resp, &activityv1.CPWeeklyRankSettlement{
|
||||
SettlementId: item.SettlementID,
|
||||
PeriodStartMs: item.PeriodStartMS,
|
||||
PeriodEndMs: item.PeriodEndMS,
|
||||
RankNo: item.RankNo,
|
||||
RelationshipId: item.RelationshipID,
|
||||
UserId: item.UserID,
|
||||
Score: item.Score,
|
||||
ResourceGroupId: item.ResourceGroupID,
|
||||
WalletCommandId: item.WalletCommandID,
|
||||
WalletGrantId: item.WalletGrantID,
|
||||
Status: item.Status,
|
||||
FailureReason: item.FailureReason,
|
||||
AttemptCount: item.AttemptCount,
|
||||
CreatedAtMs: item.CreatedAtMS,
|
||||
UpdatedAtMs: item.UpdatedAtMS,
|
||||
})
|
||||
}
|
||||
return resp
|
||||
}
|
||||
@ -8,8 +8,6 @@ import (
|
||||
"hyapp/pkg/appcode"
|
||||
"hyapp/pkg/xerr"
|
||||
achievementservice "hyapp/services/activity-service/internal/service/achievement"
|
||||
agencyopeningservice "hyapp/services/activity-service/internal/service/agencyopening"
|
||||
cpweeklyrankservice "hyapp/services/activity-service/internal/service/cpweeklyrank"
|
||||
growthservice "hyapp/services/activity-service/internal/service/growth"
|
||||
messageservice "hyapp/services/activity-service/internal/service/message"
|
||||
roomturnoverrewardservice "hyapp/services/activity-service/internal/service/roomturnoverreward"
|
||||
@ -25,19 +23,17 @@ type CronServer struct {
|
||||
achievement *achievementservice.Service
|
||||
weeklyStar *weeklystarservice.Service
|
||||
roomReward *roomturnoverrewardservice.Service
|
||||
agencyOpen *agencyopeningservice.Service
|
||||
cpWeekly *cpweeklyrankservice.Service
|
||||
}
|
||||
|
||||
// NewCronServer creates the internal cron adapter without exposing message storage.
|
||||
func NewCronServer(messageSvc *messageservice.Service, growthSvc *growthservice.Service, achievementSvc *achievementservice.Service, weeklyStarSvc *weeklystarservice.Service, roomRewardSvc *roomturnoverrewardservice.Service, agencyOpeningSvc *agencyopeningservice.Service, cpWeeklyRankSvc *cpweeklyrankservice.Service) *CronServer {
|
||||
func NewCronServer(messageSvc *messageservice.Service, growthSvc *growthservice.Service, achievementSvc *achievementservice.Service, weeklyStarSvc *weeklystarservice.Service, roomRewardSvc ...*roomturnoverrewardservice.Service) *CronServer {
|
||||
server := &CronServer{message: messageSvc}
|
||||
server.growth = growthSvc
|
||||
server.achievement = achievementSvc
|
||||
server.weeklyStar = weeklyStarSvc
|
||||
server.roomReward = roomRewardSvc
|
||||
server.agencyOpen = agencyOpeningSvc
|
||||
server.cpWeekly = cpWeeklyRankSvc
|
||||
if len(roomRewardSvc) > 0 {
|
||||
server.roomReward = roomRewardSvc[0]
|
||||
}
|
||||
return server
|
||||
}
|
||||
|
||||
@ -123,25 +119,6 @@ func (s *CronServer) ProcessWeeklyStarSettlementBatch(ctx context.Context, req *
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ProcessCPWeeklyRankSettlementBatch closes the previous UTC CP week and grants configured Top rewards.
|
||||
func (s *CronServer) ProcessCPWeeklyRankSettlementBatch(ctx context.Context, req *activityv1.CronBatchRequest) (*activityv1.CronBatchResponse, error) {
|
||||
ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
|
||||
if s.cpWeekly == nil {
|
||||
return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "cp weekly rank service is not configured"))
|
||||
}
|
||||
claimed, processed, success, failure, hasMore, err := s.cpWeekly.ProcessSettlementBatch(ctx, req.GetRunId(), req.GetWorkerId(), req.GetBatchSize())
|
||||
if err != nil {
|
||||
return nil, xerr.ToGRPCError(err)
|
||||
}
|
||||
return &activityv1.CronBatchResponse{
|
||||
ClaimedCount: claimed,
|
||||
ProcessedCount: processed,
|
||||
SuccessCount: success,
|
||||
FailureCount: failure,
|
||||
HasMore: hasMore,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ProcessRoomTurnoverRewardSettlementBatch closes the previous UTC week and grants pending room rewards.
|
||||
func (s *CronServer) ProcessRoomTurnoverRewardSettlementBatch(ctx context.Context, req *activityv1.CronBatchRequest) (*activityv1.CronBatchResponse, error) {
|
||||
// cron-service 只负责调度,activity-service 才拥有上一 UTC 周期认领 settlement 和发奖状态机。
|
||||
@ -162,25 +139,6 @@ func (s *CronServer) ProcessRoomTurnoverRewardSettlementBatch(ctx context.Contex
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ProcessAgencyOpeningSettlementBatch claims ended agency-opening cycles and grants leaderboard COIN rewards.
|
||||
func (s *CronServer) ProcessAgencyOpeningSettlementBatch(ctx context.Context, req *activityv1.CronBatchRequest) (*activityv1.CronBatchResponse, error) {
|
||||
ctx = appcode.WithContext(ctx, req.GetMeta().GetAppCode())
|
||||
if s.agencyOpen == nil {
|
||||
return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "agency opening service is not configured"))
|
||||
}
|
||||
claimed, processed, success, failure, hasMore, err := s.agencyOpen.ProcessSettlementBatch(ctx, req.GetRunId(), req.GetWorkerId(), req.GetBatchSize(), durationFromMillis(req.GetLockTtlMs()))
|
||||
if err != nil {
|
||||
return nil, xerr.ToGRPCError(err)
|
||||
}
|
||||
return &activityv1.CronBatchResponse{
|
||||
ClaimedCount: claimed,
|
||||
ProcessedCount: processed,
|
||||
SuccessCount: success,
|
||||
FailureCount: failure,
|
||||
HasMore: hasMore,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func durationFromMillis(value int64) time.Duration {
|
||||
if value <= 0 {
|
||||
return 0
|
||||
|
||||
@ -50,13 +50,6 @@ tasks:
|
||||
lock_ttl: "1m"
|
||||
batch_size: 50
|
||||
app_codes: ["lalu"]
|
||||
agency_opening_settlement:
|
||||
enabled: true
|
||||
interval: "5s"
|
||||
timeout: "20s"
|
||||
lock_ttl: "1m"
|
||||
batch_size: 50
|
||||
app_codes: ["lalu"]
|
||||
room_turnover_reward_settlement:
|
||||
enabled: true
|
||||
interval: "5m"
|
||||
|
||||
@ -50,13 +50,6 @@ tasks:
|
||||
lock_ttl: "1m"
|
||||
batch_size: 50
|
||||
app_codes: ["lalu"]
|
||||
agency_opening_settlement:
|
||||
enabled: true
|
||||
interval: "5s"
|
||||
timeout: "20s"
|
||||
lock_ttl: "1m"
|
||||
batch_size: 50
|
||||
app_codes: ["lalu"]
|
||||
room_turnover_reward_settlement:
|
||||
enabled: true
|
||||
interval: "5m"
|
||||
|
||||
@ -50,13 +50,6 @@ tasks:
|
||||
lock_ttl: "1m"
|
||||
batch_size: 50
|
||||
app_codes: ["lalu"]
|
||||
agency_opening_settlement:
|
||||
enabled: true
|
||||
interval: "5s"
|
||||
timeout: "20s"
|
||||
lock_ttl: "1m"
|
||||
batch_size: 50
|
||||
app_codes: ["lalu"]
|
||||
room_turnover_reward_settlement:
|
||||
enabled: true
|
||||
interval: "5m"
|
||||
|
||||
@ -123,8 +123,6 @@ func New(cfg config.Config) (*App, error) {
|
||||
"growth_level_reward": activityCron.ProcessLevelRewardBatch,
|
||||
"achievement_reward": activityCron.ProcessAchievementRewardBatch,
|
||||
"weekly_star_settlement": activityCron.ProcessWeeklyStarSettlementBatch,
|
||||
"agency_opening_settlement": activityCron.ProcessAgencyOpeningSettlementBatch,
|
||||
"cp_weekly_rank_settlement": activityCron.ProcessCPWeeklyRankSettlementBatch,
|
||||
"room_turnover_reward_settlement": activityCron.ProcessRoomTurnoverRewardSettlementBatch,
|
||||
"game_level_event_relay": gameCron.ProcessLevelEventOutboxBatch,
|
||||
"host_salary_daily_settlement": walletCron.ProcessHostSalaryDailySettlementBatch,
|
||||
|
||||
@ -172,22 +172,6 @@ func defaultTasks() map[string]TaskConfig {
|
||||
BatchSize: 50,
|
||||
AppCodes: []string{defaultAppCode},
|
||||
},
|
||||
"agency_opening_settlement": {
|
||||
Enabled: true,
|
||||
Interval: "5s",
|
||||
Timeout: "20s",
|
||||
LockTTL: "1m",
|
||||
BatchSize: 50,
|
||||
AppCodes: []string{defaultAppCode},
|
||||
},
|
||||
"cp_weekly_rank_settlement": {
|
||||
Enabled: true,
|
||||
Interval: "5m",
|
||||
Timeout: "30s",
|
||||
LockTTL: "5m",
|
||||
BatchSize: 50,
|
||||
AppCodes: []string{defaultAppCode},
|
||||
},
|
||||
"room_turnover_reward_settlement": {
|
||||
Enabled: true,
|
||||
Interval: "5m",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user