syntax = "proto3";

package hyapp.user.v1;

option go_package = "hyapp.local/api/proto/user/v1;userv1";

// RequestMeta 是 user-service 所有内部 RPC 的最小追踪元信息。
message RequestMeta {
  string request_id = 1;
  string caller = 2;
  string gateway_node_id = 3;
  int64 sent_at_ms = 4;
  string device_id = 5;
  string client_ip = 6;
  string user_agent = 7;
  string country_by_ip = 8;
  string session_id = 9;
  string app_code = 10;
  string platform = 11;
  string language = 12;
  string timezone = 13;
  // app_version/build_number 记录发起本次 RPC 的客户端构建；认证链路依赖它们把每次成功登录归档到 login_audit。
  string app_version = 14;
  string build_number = 15;
  // device_model/device_manufacturer/os_version/imei 是客户端设备资料快照，来自 gateway 的
  // X-Device-Model/X-Device-Manufacturer/X-OS-Version/X-Device-IMEI 头；认证成功后按 device_id
  // 聚合进 user_devices，支撑后台用户详情的设备 tab。非认证链路可留空，IMEI 在 Android 10+ 通常为空。
  string device_model = 16;
  string device_manufacturer = 17;
  string os_version = 18;
  string imei = 19;
}

// App 是 user-service 维护的 App 注册表投影，用于把客户端包名映射到内部租户键。
message App {
  int64 app_id = 1;
  string app_code = 2;
  string app_name = 3;
  string package_name = 4;
  string platform = 5;
  string status = 6;
  int64 created_at_ms = 7;
  int64 updated_at_ms = 8;
}

// ResolveAppRequest 允许 gateway 按包名/平台解析 app_code，也允许后台显式校验 app_code。
message ResolveAppRequest {
  RequestMeta meta = 1;
  string package_name = 2;
  string platform = 3;
  string app_code = 4;
}

message ResolveAppResponse {
  App app = 1;
}

// UserStatus 表达用户主状态，业务服务只消费稳定枚举。
enum UserStatus {
  USER_STATUS_UNSPECIFIED = 0;
  USER_STATUS_ACTIVE = 1;
  USER_STATUS_DISABLED = 2;
  USER_STATUS_BANNED = 3;
}

// User 是用户主数据投影，包含稳定身份、当前短号和基础资料。
message User {
  int64 user_id = 1;
  UserStatus status = 2;
  int64 created_at_ms = 3;
  int64 updated_at_ms = 4;
  string display_user_id = 5;
  string default_display_user_id = 6;
  string display_user_id_kind = 7;
  int64 display_user_id_expires_at_ms = 8;
  string username = 9;
  string gender = 10;
  string country = 11;
  string avatar = 12;
  string birth = 13;
  string language = 14;
  int64 country_id = 15;
  string country_name = 16;
  string country_display_name = 17;
  int64 region_id = 18;
  string region_code = 19;
  string region_name = 20;
  bool profile_completed = 21;
  int64 profile_completed_at_ms = 22;
  string onboarding_status = 23;
  string iso_numeric = 24;
  string phone_country_code = 25;
  bool country_enabled = 26;
  string app_code = 27;
  InviteOverview invite = 28;
  string pretty_id = 29;
  string pretty_display_user_id = 30;
  string profile_bg_img = 31;
  string contact_info = 32;
  string withdraw_usdt_trc20_address = 33;
  // content_blacklisted 只控制游戏列表和 Banner 等运营内容可见性，不改变登录、房间或账务状态。
  bool content_blacklisted = 34;
}

// InviteOverview 是我的页可直接展示的邀请码和邀请计数 read model。
message InviteOverview {
  string my_invite_code = 1;
  bool invite_enabled = 2;
  int64 invite_count = 3;
  int64 valid_invite_count = 4;
  int64 valid_invite_threshold_coin = 5;
}

// InviteBinding 表示注册完成请求本次是否新绑定邀请关系。
message InviteBinding {
  bool bound = 1;
  string invite_code = 2;
  int64 inviter_user_id = 3;
}

// InviteAttribution 是被邀请用户到邀请人的只读归因快照，供活动服务按充值事实累计邀请奖励。
message InviteAttribution {
  bool found = 1;
  string app_code = 2;
  int64 invited_user_id = 3;
  int64 inviter_user_id = 4;
  string invite_code = 5;
  int64 bound_at_ms = 6;
  string validity_status = 7;
  int64 accumulated_recharge_coin_amount = 8;
  int64 required_recharge_coin_amount = 9;
  int64 valid_at_ms = 10;
}

message GetInviteAttributionRequest {
  RequestMeta meta = 1;
  int64 invited_user_id = 2;
}

message GetInviteAttributionResponse {
  InviteAttribution attribution = 1;
}

// UserMicLifetimeStats 是用户维度的麦上累计基础指标，不区分主播/公会身份。
message UserMicLifetimeStats {
  int64 user_id = 1;
  int64 seat_occupied_ms = 2;
  int64 mic_online_ms = 3;
  int64 session_count = 4;
  int64 first_mic_at_ms = 5;
  int64 last_mic_at_ms = 6;
  int64 updated_at_ms = 7;
  string app_code = 8;
}

// GetUserMicLifetimeStatsRequest 查询单个用户全部历史麦位时长。
message GetUserMicLifetimeStatsRequest {
  RequestMeta meta = 1;
  int64 user_id = 2;
}

message GetUserMicLifetimeStatsResponse {
  UserMicLifetimeStats stats = 1;
}

// CronBatchRequest 是 cron-service 触发 user-service 后台批处理的统一请求。
message CronBatchRequest {
  RequestMeta meta = 1;
  string run_id = 2;
  string worker_id = 3;
  int32 batch_size = 4;
  int64 lock_ttl_ms = 5;
  int64 pending_publish_max_age_ms = 6;
  int64 publishing_session_max_age_ms = 7;
  int64 open_session_max_age_ms = 8;
}

// CronBatchResponse 返回一个批处理 run 的执行摘要。
message CronBatchResponse {
  int32 claimed_count = 1;
  int32 processed_count = 2;
  int32 success_count = 3;
  int32 failure_count = 4;
  bool has_more = 5;
}

// AuthToken 是登录、刷新和注册完成后返回给 gateway 的访问令牌投影。
message AuthToken {
  int64 user_id = 1;
  string session_id = 2;
  string access_token = 3;
  string refresh_token = 4;
  int64 expires_in_sec = 5;
  string token_type = 6;
  string display_user_id = 7;
  string default_display_user_id = 8;
  string display_user_id_kind = 9;
  int64 display_user_id_expires_at_ms = 10;
  bool profile_completed = 11;
  string onboarding_status = 12;
  string app_code = 13;
}

// GetUserRequest 查询单个用户主状态。
message GetUserRequest {
  RequestMeta meta = 1;
  int64 user_id = 2;
}

// GetUserResponse 返回单个用户主状态。
message GetUserResponse {
  User user = 1;
}

message BusinessUserLookupRequest {
  RequestMeta meta = 1;
  int64 actor_user_id = 2;
  string scene = 3;
  string keyword = 4;
  int32 page_size = 5;
}

message BusinessUserLookupItem {
  int64 user_id = 1;
  string display_user_id = 2;
  string username = 3;
  string avatar = 4;
  UserStatus status = 5;
  int64 region_id = 6;
  string region_code = 7;
  string region_name = 8;
}

message BusinessUserLookupResponse {
  repeated BusinessUserLookupItem users = 1;
}

// UserProfileStats 是我的页顶部统计 read model；不得在查询时实时扫描关系链或礼物流水。
message UserProfileStats {
  int64 user_id = 1;
  int64 visitors_count = 2;
  int64 following_count = 3;
  int64 friends_count = 4;
  int64 updated_at_ms = 5;
  string app_code = 6;
  // followers_count 是关注该用户的人数预聚合；不能在资料页请求中反向 COUNT user_follows。
  int64 followers_count = 7;
}

// GetMyProfileStatsRequest 查询我的页顶部统计计数。
message GetMyProfileStatsRequest {
  RequestMeta meta = 1;
  int64 user_id = 2;
}

message GetMyProfileStatsResponse {
  UserProfileStats stats = 1;
}

message RecordProfileVisitRequest {
  RequestMeta meta = 1;
  int64 visitor_user_id = 2;
  int64 target_user_id = 3;
}

message RecordProfileVisitResponse {
  bool recorded = 1;
  UserProfileStats target_stats = 2;
  // target_stats_hidden=true 时 target_stats 必须为空；该标志由 user-service
  // 基于目标用户当前 hide_profile_data 权益实时判定，客户端不能自行推断。
  bool target_stats_hidden = 3;
}

message ProfileVisitRecord {
  // 隐藏访客身份的记录不返回真实 visitor_user_id，字段保持 0；客户端必须使用
  // visitor_identity_hidden/anonymous_visit_id 分支渲染匿名访客，不能把 0 当作用户 ID。
  int64 visitor_user_id = 1;
  int64 target_user_id = 2;
  int64 visit_count = 3;
  int64 last_visited_at_ms = 4;
  bool visitor_identity_hidden = 5;
  // anonymous_visit_id 是首次匿名访问时随机生成、仅在同一 target 下稳定的展示 ID，
  // 不包含真实 user_id，也不能用于查询用户资料。
  string anonymous_visit_id = 6;
}

message ListProfileVisitorsRequest {
  RequestMeta meta = 1;
  int64 user_id = 2;
  int32 page = 3;
  int32 page_size = 4;
}

message ListProfileVisitorsResponse {
  repeated ProfileVisitRecord records = 1;
  int64 total = 2;
}

message FollowUserRequest {
  RequestMeta meta = 1;
  int64 follower_user_id = 2;
  int64 followee_user_id = 3;
}

message FollowUserResponse {
  bool following = 1;
  UserProfileStats follower_stats = 2;
}

message UnfollowUserRequest {
  RequestMeta meta = 1;
  int64 follower_user_id = 2;
  int64 followee_user_id = 3;
}

message UnfollowUserResponse {
  bool following = 1;
  UserProfileStats follower_stats = 2;
}

message FollowRecord {
  int64 follower_user_id = 1;
  int64 followee_user_id = 2;
  int64 followed_at_ms = 3;
}

message ListFollowingRequest {
  RequestMeta meta = 1;
  int64 user_id = 2;
  int32 page = 3;
  int32 page_size = 4;
  // cursor_updated_at_ms/cursor_user_id 用于内部关系流按时间游标扫描；公开 HTTP 列表仍可继续使用 page/page_size。
  int64 cursor_updated_at_ms = 5;
  int64 cursor_user_id = 6;
}

message ListFollowingResponse {
  repeated FollowRecord records = 1;
  int64 total = 2;
}

message ApplyFriendRequest {
  RequestMeta meta = 1;
  int64 requester_user_id = 2;
  int64 target_user_id = 3;
}

message ApplyFriendResponse {
  FriendApplication application = 1;
  bool already_friends = 2;
}

message AcceptFriendApplicationRequest {
  RequestMeta meta = 1;
  int64 accepter_user_id = 2;
  int64 requester_user_id = 3;
}

message AcceptFriendApplicationResponse {
  FriendRecord friend = 1;
}

message DeleteFriendRequest {
  RequestMeta meta = 1;
  int64 user_id = 2;
  int64 friend_user_id = 3;
}

message DeleteFriendResponse {
  bool deleted = 1;
}

message FriendRecord {
  int64 user_id = 1;
  int64 friend_user_id = 2;
  int64 friended_at_ms = 3;
}

message ListFriendsRequest {
  RequestMeta meta = 1;
  int64 user_id = 2;
  int32 page = 3;
  int32 page_size = 4;
  // cursor_updated_at_ms/cursor_user_id 用于内部关系流按时间游标扫描；公开 HTTP 列表仍可继续使用 page/page_size。
  int64 cursor_updated_at_ms = 5;
  int64 cursor_user_id = 6;
}

message ListFriendsResponse {
  repeated FriendRecord records = 1;
  int64 total = 2;
}

message FriendApplication {
  int64 requester_user_id = 1;
  int64 target_user_id = 2;
  string status = 3;
  int64 created_at_ms = 4;
  int64 updated_at_ms = 5;
}

message ListFriendApplicationsRequest {
  RequestMeta meta = 1;
  int64 user_id = 2;
  string direction = 3;
  int32 page = 4;
  int32 page_size = 5;
}

message ListFriendApplicationsResponse {
  repeated FriendApplication applications = 1;
  int64 total = 2;
}

// CPUserProfile 是 CP 关系消息里需要展示的用户快照。
message CPUserProfile {
  int64 user_id = 1;
  string display_user_id = 2;
  string username = 3;
  string avatar = 4;
}

// CPGiftSnapshot 固化触发申请或亲密值增长的礼物展示信息。
message CPGiftSnapshot {
  string gift_id = 1;
  string gift_name = 2;
  string gift_icon_url = 3;
  string gift_animation_url = 4;
  int32 gift_count = 5;
  int64 gift_value = 6;
  string billing_receipt_id = 7;
  // coin_spent 是 wallet 结算出的礼物金币价值（背包礼物也保留价值），不能用 gift_value（热度/亲密值）替代。
  int64 coin_spent = 8;
}

// CPApplication 是 A 给 B 发送 CP/兄弟/姐妹礼物后生成的待处理申请。
message CPApplication {
  string application_id = 1;
  string relation_type = 2;
  string status = 3;
  CPUserProfile requester = 4;
  CPUserProfile target = 5;
  string room_id = 6;
  int64 room_region_id = 7;
  CPGiftSnapshot gift = 8;
  int64 created_at_ms = 9;
  int64 updated_at_ms = 10;
  int64 expires_at_ms = 11;
  int64 decided_at_ms = 12;
}

// CPRelationship 是两个用户已经形成的一种关系；同一用户同一时间只能 active 一种。
message CPRelationship {
  string relationship_id = 1;
  string relation_type = 2;
  string status = 3;
  CPUserProfile me = 4;
  CPUserProfile partner = 5;
  int64 intimacy_value = 6;
  int32 level = 7;
  reserved 8;
  reserved "equipped";
  int64 formed_at_ms = 9;
  int64 updated_at_ms = 10;
  int64 current_level_threshold = 11;
  int64 next_level_threshold = 12;
  int64 needed_for_next_level = 13;
  int32 level_progress_percent = 14;
  bool max_level = 15;
  // breakup_cost_coins 是当前关系类型后台配置的解除费用；active 关系返回配置值，ended 关系返回实际支付值。
  int64 breakup_cost_coins = 16;
  int64 ended_at_ms = 17;
}

// CPAvatarFrameSnapshot 是排行榜里展示用户当前头像框的轻量快照。
message CPAvatarFrameSnapshot {
  int64 resource_id = 1;
  string resource_code = 2;
  string name = 3;
  string asset_url = 4;
  string preview_url = 5;
  string animation_url = 6;
  string metadata_json = 7;
}

// CPIntimacyLeaderboardUser 是 CP 亲密榜中一方用户的展示资料。
message CPIntimacyLeaderboardUser {
  int64 user_id = 1;
  string display_user_id = 2;
  string username = 3;
  string avatar = 4;
  CPAvatarFrameSnapshot avatar_frame = 5;
}

// CPIntimacyLeaderboardItem 是一对 active 关系在亲密值榜单里的读模型。
message CPIntimacyLeaderboardItem {
  int64 rank = 1;
  string relationship_id = 2;
  string relation_type = 3;
  int64 intimacy_value = 4;
  int32 level = 5;
  CPIntimacyLeaderboardUser user_a = 6;
  CPIntimacyLeaderboardUser user_b = 7;
  int64 formed_at_ms = 8;
  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;
  string direction = 3;
  string status = 4;
  int32 page = 5;
  int32 page_size = 6;
}

message ListCPApplicationsResponse {
  repeated CPApplication applications = 1;
  int64 total = 2;
  int64 server_time_ms = 3;
}

message AcceptCPApplicationRequest {
  RequestMeta meta = 1;
  int64 user_id = 2;
  string application_id = 3;
}

message AcceptCPApplicationResponse {
  CPApplication application = 1;
  CPRelationship relationship = 2;
}

message RejectCPApplicationRequest {
  RequestMeta meta = 1;
  int64 user_id = 2;
  string application_id = 3;
  string reason = 4;
}

message RejectCPApplicationResponse {
  CPApplication application = 1;
}

message ListCPRelationshipsRequest {
  RequestMeta meta = 1;
  int64 user_id = 2;
  string relation_type = 3;
  int32 page = 4;
  int32 page_size = 5;
}

message ListCPRelationshipsResponse {
  repeated CPRelationship relationships = 1;
  int64 total = 2;
  reserved 3;
  reserved "max_count_by_relation_type";
}

message ListCPIntimacyLeaderboardRequest {
  RequestMeta meta = 1;
  string relation_type = 2;
  int32 page = 3;
  int32 page_size = 4;
}

message ListCPIntimacyLeaderboardResponse {
  repeated CPIntimacyLeaderboardItem items = 1;
  int64 total = 2;
  int32 page = 3;
  int32 page_size = 4;
  int64 server_time_ms = 5;
}

message PrepareBreakCPRelationshipRequest {
  RequestMeta meta = 1;
  int64 user_id = 2;
  string relationship_id = 3;
  string command_id = 4;
}

message PrepareBreakCPRelationshipResponse {
  CPRelationship relationship = 1;
  string command_id = 2;
  string status = 3;
  int64 breakup_cost_coins = 4;
  string wallet_transaction_id = 5;
  int64 coin_balance_after = 6;
}

message ConfirmBreakCPRelationshipRequest {
  RequestMeta meta = 1;
  int64 user_id = 2;
  string relationship_id = 3;
  string command_id = 4;
  string wallet_transaction_id = 5;
  int64 paid_coin_amount = 6;
  int64 coin_balance_after = 7;
}

message ConfirmBreakCPRelationshipResponse {
  CPRelationship relationship = 1;
  int64 breakup_cost_coins = 2;
  string wallet_transaction_id = 3;
  int64 coin_balance_after = 4;
}

message CancelBreakCPRelationshipRequest {
  RequestMeta meta = 1;
  int64 user_id = 2;
  string relationship_id = 3;
  string command_id = 4;
  string reason = 5;
}

message CancelBreakCPRelationshipResponse {
  string command_id = 1;
  string status = 2;
}

message RoomGiftCPEvent {
  RequestMeta meta = 1;
  string event_id = 2;
  string room_id = 3;
  int64 room_version = 4;
  int64 occurred_at_ms = 5;
  int64 sender_user_id = 6;
  int64 target_user_id = 7;
  string gift_id = 8;
  int32 gift_count = 9;
  int64 gift_value = 10;
  string billing_receipt_id = 11;
  int64 visible_region_id = 12;
  string command_id = 13;
  string gift_type_code = 14;
  string cp_relation_type = 15;
  string gift_name = 16;
  string gift_icon_url = 17;
  string gift_animation_url = 18;
  // coin_spent 来自 RoomGiftSent 的 wallet 结算价值，和 gift_value 的热度口径彼此独立。
  int64 coin_spent = 19;
}

message ConsumeRoomGiftCPEventRequest {
  RequestMeta meta = 1;
  RoomGiftCPEvent event = 2;
}

message ConsumeRoomGiftCPEventResponse {
  bool consumed = 1;
  CPApplication application = 2;
  CPRelationship relationship = 3;
}

// UserReport 是 App 用户提交的治理举报单。
message UserReport {
  string report_id = 1;
  int64 reporter_user_id = 2;
  string target_type = 3;
  int64 user_id = 4;
  string room_id = 5;
  string report_type = 6;
  string reason = 7;
  repeated string image_urls = 8;
  string status = 9;
  int64 created_at_ms = 10;
}

// SubmitReportRequest 提交用户或房间举报；user_id 和 room_id 必须二选一。
message SubmitReportRequest {
  RequestMeta meta = 1;
  int64 reporter_user_id = 2;
  int64 user_id = 3;
  string room_id = 4;
  string report_type = 5;
  string reason = 6;
  repeated string image_urls = 7;
}

message SubmitReportResponse {
  UserReport report = 1;
}

// BatchGetUsersRequest 批量查询用户主状态。
message BatchGetUsersRequest {
  RequestMeta meta = 1;
  repeated int64 user_ids = 2;
}

// BatchGetUsersResponse 返回按 user_id 索引的用户主状态。
message BatchGetUsersResponse {
  map<int64, User> users = 1;
}

// ActiveUserBanSummary 是后台用户列表读取的有效封禁摘要。
// expires_at_ms=0 只在 permanent=true 时表示永久；source 区分 admin、manager 和无新事实表记录的 legacy。
message ActiveUserBanSummary {
  bool active = 1;
  string source = 2;
  string ban_id = 3;
  bool permanent = 4;
  int64 expires_at_ms = 5;
  UserStatus user_status = 6;
  string operator_type = 7;
  int64 operator_user_id = 8;
  string reason = 9;
  int64 created_at_ms = 10;
  int32 active_ban_count = 11;
}

// UserAdminProfile 聚合后台用户列表和详情需要的 user-service owner 事实。
// roles 只返回当前有效身份：host、agency、bd、bd_leader、coin_seller、manager。
message UserAdminProfile {
  User user = 1;
  string birth = 2;
  string register_device = 3;
  int64 last_success_login_at_ms = 4;
  repeated string roles = 5;
  ActiveUserBanSummary ban = 6;
  string last_operator_type = 7;
  int64 last_operator_user_id = 8;
  string last_operation_reason = 9;
  int64 last_operation_at_ms = 10;
}

message BatchGetUserAdminProfilesRequest {
  RequestMeta meta = 1;
  repeated int64 user_ids = 2;
}

message BatchGetUserAdminProfilesResponse {
  map<int64, UserAdminProfile> profiles = 1;
}

// AdminIssueUserAccessTokenRequest 供后台按用户当前最新 active session 重签 access token。
// 该链路不写 login_audit，也不修改 session 行——后台取 token 不能污染用户“最近活跃/最近登录”事实；
// 审计责任由 admin 侧操作日志承担。
message AdminIssueUserAccessTokenRequest {
  RequestMeta meta = 1;
  string app_code = 2;
  int64 user_id = 3;
}

// AdminIssueUserAccessTokenResponse 返回重签的 access token 和所属 session 的定位信息。
// 不返回 refresh token：后台没有轮换会话的理由，refresh 原文也不允许离开登录主链路。
message AdminIssueUserAccessTokenResponse {
  string access_token = 1;
  string token_type = 2;
  int64 expires_in_sec = 3;
  string session_id = 4;
  string device_id = 5;
  int64 session_last_heartbeat_at_ms = 6;
}

// UserContentBlacklistEntry 是后台“黑名单屏蔽用户”列表当前态。
// 用户展示字段随列表查询读取当前资料；operator_admin_id/reason/time 是 user-service 持有的治理事实。
message UserContentBlacklistEntry {
  int64 user_id = 1;
  string display_user_id = 2;
  string default_display_user_id = 3;
  string pretty_id = 4;
  string pretty_display_user_id = 5;
  string username = 6;
  string avatar = 7;
  UserStatus user_status = 8;
  int64 operator_admin_id = 9;
  string reason = 10;
  int64 created_at_ms = 11;
  int64 updated_at_ms = 12;
}

message ListUserContentBlacklistRequest {
  RequestMeta meta = 1;
  int32 page = 2;
  int32 page_size = 3;
}

message ListUserContentBlacklistResponse {
  repeated UserContentBlacklistEntry items = 1;
  int64 total = 2;
}

message AdminAddUserContentBlacklistRequest {
  RequestMeta meta = 1;
  int64 user_id = 2;
  int64 operator_admin_id = 3;
  string reason = 4;
}

message AdminAddUserContentBlacklistResponse {
  UserContentBlacklistEntry item = 1;
}

message AdminRemoveUserContentBlacklistRequest {
  RequestMeta meta = 1;
  int64 user_id = 2;
  int64 operator_admin_id = 3;
  string reason = 4;
}

message AdminRemoveUserContentBlacklistResponse {
  bool removed = 1;
}

// RoomBasicUser 是房间首屏只需要的用户展示基础资料。
message RoomBasicUser {
  int64 user_id = 1;
  string username = 2;
  string avatar = 3;
  string display_user_id = 4;
}

// BatchGetRoomBasicUsersRequest 为房间进房 lite 首屏批量读取最小用户资料。
message BatchGetRoomBasicUsersRequest {
  RequestMeta meta = 1;
  repeated int64 user_ids = 2;
}

// BatchGetRoomBasicUsersResponse 返回按 user_id 索引的房间首屏基础资料。
message BatchGetRoomBasicUsersResponse {
  map<int64, RoomBasicUser> users = 1;
}

// ListUserIDsRequest 给低频后台 fanout/重算任务按 user_id 游标读取目标用户。
message ListUserIDsRequest {
  RequestMeta meta = 1;
  string target_scope = 2;
  int64 cursor_user_id = 3;
  int32 page_size = 4;
  int64 region_id = 5;
  string country = 6;
}

// ListUserIDsResponse 返回一页递增 user_id，调用方必须用 next_cursor_user_id 继续。
message ListUserIDsResponse {
  repeated int64 user_ids = 1;
  int64 next_cursor_user_id = 2;
  bool done = 3;
}

// UpdateUserProfileRequest 修改用户可自助维护的基础资料。
message UpdateUserProfileRequest {
  RequestMeta meta = 1;
  int64 user_id = 2;
  optional string username = 3;
  optional string avatar = 4;
  optional string birth = 5;
  optional string gender = 6;
  // avatar_upload_id 由专用头像上传接口签发；权益制 App 修改头像时必须消费该凭证，不能提交任意 URL。
  optional string avatar_upload_id = 7;
}

// UpdateUserProfileResponse 返回更新后的用户资料投影。
message UpdateUserProfileResponse {
  User user = 1;
}

// UserAvatarMedia 是 gateway 从原始文件字节解析出的可信媒体事实。
// user-service 会持久化首次授权结果，并在完成上传和资料更新时逐字段复核。
message UserAvatarMedia {
  string url = 1;
  string object_key = 2;
  string content_type = 3;
  string format = 4;
  int64 size_bytes = 5;
  int32 width = 6;
  int32 height = 7;
  bool animated = 8;
  int32 frame_count = 9;
  int64 duration_ms = 10;
  string sha256 = 11;
  string status = 12;
}

// AuthorizeUserAvatarUploadRequest 在 COS PutObject 前占用 command_id 并执行动态头像权益校验。
message AuthorizeUserAvatarUploadRequest {
  RequestMeta meta = 1;
  int64 user_id = 2;
  string command_id = 3;
  UserAvatarMedia media = 4;
}

message AuthorizeUserAvatarUploadResponse {
  bool allowed = 1;
  string avatar_upload_id = 2;
  string object_key = 3;
  int64 server_time_ms = 4;
}

// CompleteUserAvatarUploadRequest 只接受 gateway 成功写入受信 COS 后回传的 URL 和对象键。
message CompleteUserAvatarUploadRequest {
  RequestMeta meta = 1;
  int64 user_id = 2;
  string avatar_upload_id = 3;
  UserAvatarMedia media = 4;
}

message CompleteUserAvatarUploadResponse {
  bool active = 1;
  string avatar_upload_id = 2;
  UserAvatarMedia media = 3;
  int64 server_time_ms = 4;
}

// UpdateUserProfileBackgroundRequest 只修改用户个人信息页背景图。
message UpdateUserProfileBackgroundRequest {
  RequestMeta meta = 1;
  int64 user_id = 2;
  string profile_bg_img = 3;
}

// UpdateUserProfileBackgroundResponse 返回写入后的用户资料投影。
message UpdateUserProfileBackgroundResponse {
  User user = 1;
}

// UpdateUserContactInfoRequest 只修改用户主资料上的联系方式，供不同经营身份共用。
message UpdateUserContactInfoRequest {
  RequestMeta meta = 1;
  int64 user_id = 2;
  string contact_info = 3;
}

// UpdateUserContactInfoResponse 返回写入后的用户资料投影。
message UpdateUserContactInfoResponse {
  User user = 1;
}

// UpdateUserWithdrawAddressRequest 只修改用户默认提现地址，提现订单会在 wallet-service 另行快照。
message UpdateUserWithdrawAddressRequest {
  RequestMeta meta = 1;
  int64 user_id = 2;
  string usdt_trc20_address = 3;
}

// UpdateUserWithdrawAddressResponse 返回写入后的用户资料投影。
message UpdateUserWithdrawAddressResponse {
  User user = 1;
}

// ChangeUserCountryRequest 是 App 用户自助修改国家入口；受限业务身份不允许自助修改。
message ChangeUserCountryRequest {
  RequestMeta meta = 1;
  int64 user_id = 2;
  string country = 3;
}

// AdminChangeUserCountryRequest 是后台显式覆盖用户国家入口；它绕过 App 自助身份限制。
message AdminChangeUserCountryRequest {
  RequestMeta meta = 1;
  int64 target_user_id = 2;
  string country = 3;
  int64 admin_user_id = 4;
  string reason = 5;
}

// ChangeUserCountryResponse 返回更新后的用户资料和下一次可修改时间。
message ChangeUserCountryResponse {
  User user = 1;
  int64 next_change_allowed_at_ms = 2;
  int64 old_region_id = 3;
  int64 new_region_id = 4;
  bool region_changed = 5;
}

// SetUserStatusRequest 是封禁、停用和恢复用户的通用内部入口。
// 非 active 状态会同步吊销 refresh session，并把仍在有效期内的 access session 写入 gateway denylist。
message SetUserStatusRequest {
  RequestMeta meta = 1;
  int64 target_user_id = 2;
  UserStatus status = 3;
  int64 operator_user_id = 4;
  string reason = 5;
  // operator_type 区分 admin、app_user、system，避免不同身份体系共用一个整数 ID 后无法追溯。
  string operator_type = 6;
}

// SetUserStatusResponse 返回用户当前状态和封禁相关副作用结果。
// 外部副作用字段用于后台或 App 管理入口判断是否需要重试补偿。
message SetUserStatusResponse {
  User user = 1;
  int64 revoked_session_count = 2;
  bool im_kicked = 3;
  string im_kick_error = 4;
  bool room_evicted = 5;
  string room_id = 6;
  bool rtc_kicked = 7;
  string room_evict_error = 8;
  string rtc_kick_error = 9;
  bool access_token_revoked = 10;
  string access_token_revoke_error = 11;
}

// AdminUserBan 是后台管理员封禁的独立事实；expires_at_ms=0 表示永久封禁。
message AdminUserBan {
  string ban_id = 1;
  string command_id = 2;
  int64 target_user_id = 3;
  int64 expires_at_ms = 4;
  bool permanent = 5;
  string status = 6;
  string reason = 7;
  int64 operator_admin_id = 8;
  int64 created_at_ms = 9;
  int64 updated_at_ms = 10;
  int64 released_by_admin_id = 11;
  string released_reason = 12;
  int64 released_at_ms = 13;
}

message AdminBanUserRequest {
  RequestMeta meta = 1;
  string command_id = 2;
  int64 target_user_id = 3;
  int64 expires_at_ms = 4;
  int64 operator_admin_id = 5;
  string reason = 6;
}

message AdminBanUserResponse {
  AdminUserBan ban = 1;
  SetUserStatusResponse status = 2;
}

// AdminUnbanUserRequest 由后台管理员强制释放目标用户全部管理员、经理和 legacy 封禁来源。
message AdminUnbanUserRequest {
  RequestMeta meta = 1;
  // ban_id 已废弃；保留字段号用于滚动发布兼容，强制解封始终按 target_user_id 处理全部有效事实。
  string ban_id = 2;
  int64 target_user_id = 3;
  int64 operator_admin_id = 4;
  string reason = 5;
}

message AdminUnbanUserResponse {
  // ban 仅为旧客户端保留；强制解封可能只释放经理或 legacy 来源，因此新调用方不应依赖该字段。
  AdminUserBan ban = 1;
  SetUserStatusResponse status = 2;
  int64 released_admin_ban_count = 3;
  int64 released_manager_block_count = 4;
  bool status_restored = 5;
}

message ManagerUserBlock {
  string block_id = 1;
  int64 manager_user_id = 2;
  int64 target_user_id = 3;
  string target_display_user_id = 4;
  string target_username = 5;
  string target_avatar = 6;
  string country = 7;
  int64 blocked_until_ms = 8;
  string status = 9;
  string reason = 10;
  int64 created_at_ms = 11;
  int64 updated_at_ms = 12;
}

message CreateManagerUserBlockRequest {
  RequestMeta meta = 1;
  string command_id = 2;
  int64 manager_user_id = 3;
  int64 target_user_id = 4;
  int64 blocked_until_ms = 5;
  string reason = 6;
}

message CreateManagerUserBlockResponse {
  ManagerUserBlock block = 1;
  SetUserStatusResponse status = 2;
}

message ListManagerUserBlocksRequest {
  RequestMeta meta = 1;
  int64 manager_user_id = 2;
  string status = 3;
  int32 page_size = 4;
}

message ListManagerUserBlocksResponse {
  repeated ManagerUserBlock blocks = 1;
}

message UnblockManagerUserRequest {
  RequestMeta meta = 1;
  int64 manager_user_id = 2;
  string block_id = 3;
  string reason = 4;
}

message UnblockManagerUserResponse {
  ManagerUserBlock block = 1;
  SetUserStatusResponse status = 2;
}

// CompleteOnboardingRequest 是注册页唯一资料提交入口。
message CompleteOnboardingRequest {
  RequestMeta meta = 1;
  int64 user_id = 2;
  string username = 3;
  string avatar = 4;
  string country = 5;
  string gender = 6;
  string invite_code = 7;
}

// CompleteOnboardingResponse 返回固化后的最小注册状态。
message CompleteOnboardingResponse {
  User user = 1;
  bool profile_completed = 2;
  int64 profile_completed_at_ms = 3;
  string onboarding_status = 4;
  AuthToken token = 5;
  InviteBinding invite = 6;
}

// SearchInviteReferrerRequest 用当前用户国家约束可绑定邀请码的搜索范围。
message SearchInviteReferrerRequest {
  RequestMeta meta = 1;
  int64 user_id = 2;
  string invite_code = 3;
}

message SearchInviteReferrerResponse {
  User inviter = 1;
}

// BindInviteReferrerRequest 只接受用户输入的邀请码，归属由 user-service 事务内读取。
message BindInviteReferrerRequest {
  RequestMeta meta = 1;
  int64 user_id = 2;
  string invite_code = 3;
}

message BindInviteReferrerResponse {
  InviteBinding invite = 1;
  User inviter = 2;
}

// BindPushTokenRequest 绑定当前登录用户的系统推送 token。
// push token 只服务离线触达，不作为登录凭证或设备身份凭证。
message BindPushTokenRequest {
  RequestMeta meta = 1;
  int64 user_id = 2;
  string device_id = 3;
  string push_token = 4;
  string provider = 5;
  string platform = 6;
  string app_version = 7;
  string language = 8;
  string timezone = 9;
}

message BindPushTokenResponse {
  bool bound = 1;
  int64 updated_at_ms = 2;
}

// DeletePushTokenRequest 删除或失效当前登录用户的系统推送 token。
// push_token 可为空；为空时按 user_id + device_id 失效该设备的 active token。
message DeletePushTokenRequest {
  RequestMeta meta = 1;
  int64 user_id = 2;
  string device_id = 3;
  string push_token = 4;
}

message DeletePushTokenResponse {
  bool deleted = 1;
  int64 updated_at_ms = 2;
}

// Country 是国家主数据投影，客户端和普通用户流程只提交 country_code。
message Country {
  int64 country_id = 1;
  string country_name = 2;
  string country_code = 3;
  string country_display_name = 4;
  int32 sort_order = 6;
  int64 created_at_ms = 7;
  int64 updated_at_ms = 8;
  string iso_alpha3 = 9;
  string iso_numeric = 10;
  string phone_country_code = 11;
  bool enabled = 12;
  string flag = 13;
  string app_code = 14;
}

// Region 是区域主数据投影，countries 只包含当前 active 国家码。
message Region {
  int64 region_id = 1;
  string region_code = 2;
  string name = 3;
  string status = 4;
  repeated string countries = 5;
  int32 sort_order = 6;
  int64 created_at_ms = 7;
  int64 updated_at_ms = 8;
  string app_code = 9;
}

message ListCountriesRequest {
  RequestMeta meta = 1;
  optional bool enabled = 3;
}

message ListCountriesResponse {
  repeated Country countries = 1;
}

// UpdateCountryRequest 只修改国家展示字段，不修改 country_code。
message UpdateCountryRequest {
  RequestMeta meta = 1;
  int64 country_id = 2;
  string country_name = 3;
  string country_display_name = 4;
  int32 sort_order = 5;
  int64 operator_user_id = 6;
  string iso_alpha3 = 7;
  string iso_numeric = 8;
  string phone_country_code = 9;
  string flag = 10;
}

message CountryResponse {
  Country country = 1;
}

message ListRegistrationCountriesRequest {
  RequestMeta meta = 1;
}

message ListRegistrationCountriesResponse {
  repeated Country countries = 1;
}

message LoginRiskBlockedCountry {
  string country_code = 1;
  string keyword = 2;
  int64 updated_at_ms = 3;
}

message ListLoginRiskBlockedCountriesRequest {
  RequestMeta meta = 1;
}

message ListLoginRiskBlockedCountriesResponse {
  repeated LoginRiskBlockedCountry countries = 1;
  int64 updated_at_ms = 2;
}

message ListRegionsRequest {
  RequestMeta meta = 1;
  string status = 2;
}

message ListRegionsResponse {
  repeated Region regions = 1;
}

message GetRegionRequest {
  RequestMeta meta = 1;
  int64 region_id = 2;
}

// UpdateRegionRequest 只修改区域展示字段，不修改 countries。
message UpdateRegionRequest {
  RequestMeta meta = 1;
  int64 region_id = 2;
  string region_code = 3;
  string name = 4;
  int32 sort_order = 5;
  int64 operator_user_id = 6;
}

message ReplaceRegionCountriesRequest {
  RequestMeta meta = 1;
  int64 region_id = 2;
  repeated string countries = 3;
  int64 operator_user_id = 4;
}

message RegionResponse {
  Region region = 1;
}

// UserIdentity 是 user_id 和当前 active display_user_id 的绑定投影。
message UserIdentity {
  int64 user_id = 1;
  string display_user_id = 2;
  string status = 3;
  string default_display_user_id = 4;
  string display_user_id_kind = 5;
  int64 display_user_id_expires_at_ms = 6;
  string app_code = 7;
  string pretty_id = 8;
  string pretty_display_user_id = 9;
}

// GetUserIdentityRequest 按 user_id 查询当前短号。
message GetUserIdentityRequest {
  RequestMeta meta = 1;
  int64 user_id = 2;
}

// GetUserIdentityResponse 返回当前短号绑定。
message GetUserIdentityResponse {
  UserIdentity identity = 1;
}

// ResolveDisplayUserIDRequest 把客户端展示短号解析成系统 user_id。
message ResolveDisplayUserIDRequest {
  RequestMeta meta = 1;
  string display_user_id = 2;
}

// ResolveDisplayUserIDResponse 返回 active 短号归属。
message ResolveDisplayUserIDResponse {
  UserIdentity identity = 1;
}

// ChangeDisplayUserIDRequest 修改用户短号，必须由 user-service 保证事务一致。
message ChangeDisplayUserIDRequest {
  RequestMeta meta = 1;
  int64 user_id = 2;
  string new_display_user_id = 3;
  string reason = 4;
  int64 operator_user_id = 5;
}

// ChangeDisplayUserIDResponse 返回修改后的 active 绑定。
message ChangeDisplayUserIDResponse {
  UserIdentity identity = 1;
}

// ApplyPrettyDisplayUserIDRequest 申请临时靓号并覆盖当前展示号。
message ApplyPrettyDisplayUserIDRequest {
  RequestMeta meta = 1;
  int64 user_id = 2;
  string pretty_display_user_id = 3;
  int64 lease_duration_ms = 4;
  string payment_receipt_id = 5;
}

// ApplyPrettyDisplayUserIDResponse 返回申请成功后的当前展示号和租约。
message ApplyPrettyDisplayUserIDResponse {
  UserIdentity identity = 1;
  string lease_id = 2;
}

// ExpirePrettyDisplayUserIDRequest 主动触发靓号过期恢复。
message ExpirePrettyDisplayUserIDRequest {
  RequestMeta meta = 1;
  int64 user_id = 2;
  string lease_id = 3;
}

// ExpirePrettyDisplayUserIDResponse 返回恢复后的默认短号身份。
message ExpirePrettyDisplayUserIDResponse {
  UserIdentity identity = 1;
}

// PrettyDisplayIDPool 是靓号池等级区间和生成规则配置。
message PrettyDisplayIDPool {
  string app_code = 1;
  string pool_id = 2;
  string name = 3;
  string level_track = 4;
  int32 min_level = 5;
  int32 max_level = 6;
  string rule_type = 7;
  string rule_config_json = 8;
  string status = 9;
  int32 sort_order = 10;
  int64 created_by_admin_id = 11;
  int64 updated_by_admin_id = 12;
  int64 created_at_ms = 13;
  int64 updated_at_ms = 14;
}

// PrettyDisplayID 是一个可申请或已发放的靓号记录。
message PrettyDisplayID {
  string app_code = 1;
  string pretty_id = 2;
  string pool_id = 3;
  string source = 4;
  string display_user_id = 5;
  string status = 6;
  int64 assigned_user_id = 7;
  string assigned_lease_id = 8;
  int64 assigned_at_ms = 9;
  int64 released_at_ms = 10;
  string release_reason = 11;
  string generated_batch_id = 12;
  int64 created_by_admin_id = 13;
  int64 created_at_ms = 14;
  int64 updated_at_ms = 15;
  PrettyDisplayIDPool pool = 16;
}

message PrettyDisplayIDGenerationBatch {
  string app_code = 1;
  string batch_id = 2;
  string pool_id = 3;
  string rule_type = 4;
  string rule_config_json = 5;
  int32 requested_count = 6;
  int32 generated_count = 7;
  int32 skipped_conflict_count = 8;
  string status = 9;
  int64 operator_admin_id = 10;
  string request_id = 11;
  int64 created_at_ms = 12;
  int64 updated_at_ms = 13;
}

message ListAvailablePrettyDisplayIDsRequest {
  RequestMeta meta = 1;
  int64 user_id = 2;
  int32 page = 3;
  int32 page_size = 4;
}

message ListAvailablePrettyDisplayIDsResponse {
  repeated PrettyDisplayID items = 1;
  int64 total = 2;
}

message ApplyPrettyDisplayIDFromPoolRequest {
  RequestMeta meta = 1;
  int64 user_id = 2;
  string pretty_id = 3;
}

message ApplyPrettyDisplayIDFromPoolResponse {
  UserIdentity identity = 1;
  string pretty_id = 2;
  string lease_id = 3;
}

message ListPrettyDisplayIDPoolsRequest {
  RequestMeta meta = 1;
  string status = 2;
  string level_track = 3;
  int32 page = 4;
  int32 page_size = 5;
}

message ListPrettyDisplayIDPoolsResponse {
  repeated PrettyDisplayIDPool items = 1;
  int64 total = 2;
}

message CreatePrettyDisplayIDPoolRequest {
  RequestMeta meta = 1;
  string name = 2;
  string level_track = 3;
  int32 min_level = 4;
  int32 max_level = 5;
  string rule_type = 6;
  string rule_config_json = 7;
  string status = 8;
  int32 sort_order = 9;
  int64 operator_admin_id = 10;
}

message UpdatePrettyDisplayIDPoolRequest {
  RequestMeta meta = 1;
  string pool_id = 2;
  string name = 3;
  string level_track = 4;
  int32 min_level = 5;
  int32 max_level = 6;
  string rule_type = 7;
  string rule_config_json = 8;
  string status = 9;
  int32 sort_order = 10;
  int64 operator_admin_id = 11;
}

message PrettyDisplayIDPoolResponse {
  PrettyDisplayIDPool pool = 1;
}

message GeneratePrettyDisplayIDsRequest {
  RequestMeta meta = 1;
  string pool_id = 2;
  string rule_type = 3;
  string rule_config_json = 4;
  int32 count = 5;
  int64 operator_admin_id = 6;
}

message GeneratePrettyDisplayIDsResponse {
  PrettyDisplayIDGenerationBatch batch = 1;
}

message ListPrettyDisplayIDsRequest {
  RequestMeta meta = 1;
  string pool_id = 2;
  string source = 3;
  string status = 4;
  string keyword = 5;
  int64 assigned_user_id = 6;
  int32 page = 7;
  int32 page_size = 8;
}

message ListPrettyDisplayIDsResponse {
  repeated PrettyDisplayID items = 1;
  int64 total = 2;
}

message SetPrettyDisplayIDStatusRequest {
  RequestMeta meta = 1;
  string pretty_id = 2;
  string status = 3;
  int64 operator_admin_id = 4;
  string reason = 5;
}

message RecyclePrettyDisplayIDRequest {
  RequestMeta meta = 1;
  string pretty_id = 2;
  string reason = 3;
  int64 operator_admin_id = 4;
}

message PrettyDisplayIDResponse {
  PrettyDisplayID item = 1;
}

message AdminGrantPrettyDisplayIDRequest {
  RequestMeta meta = 1;
  int64 target_user_id = 2;
  string display_user_id = 3;
  int64 duration_ms = 4;
  string reason = 5;
  int64 operator_admin_id = 6;
}

message AdminGrantPrettyDisplayIDResponse {
  UserIdentity identity = 1;
  string pretty_id = 2;
  string lease_id = 3;
}

// CPFormationGiftFeedItem 是组 CP 成立时所用礼物超过固定门槛的页面展示快照。
message CPFormationGiftFeedItem {
  string formation_id = 1;
  int64 gift_coin_value = 2;
  CPUserProfile requester = 3;
  CPUserProfile target = 4;
  int64 formed_at_ms = 5;
}

message ListCPFormationGiftFeedRequest {
  RequestMeta meta = 1;
  int32 limit = 2;
}

message ListCPFormationGiftFeedResponse {
  repeated CPFormationGiftFeedItem items = 1;
}

// UserService 提供用户主状态、基础资料和注册资料读写接口。
service UserService {
  rpc GetUser(GetUserRequest) returns (GetUserResponse);
  rpc GetInviteAttribution(GetInviteAttributionRequest) returns (GetInviteAttributionResponse);
  rpc BusinessUserLookup(BusinessUserLookupRequest) returns (BusinessUserLookupResponse);
  rpc GetMyProfileStats(GetMyProfileStatsRequest) returns (GetMyProfileStatsResponse);
  rpc BatchGetUsers(BatchGetUsersRequest) returns (BatchGetUsersResponse);
  rpc BatchGetUserAdminProfiles(BatchGetUserAdminProfilesRequest) returns (BatchGetUserAdminProfilesResponse);
  rpc AdminIssueUserAccessToken(AdminIssueUserAccessTokenRequest) returns (AdminIssueUserAccessTokenResponse);
  rpc ListUserContentBlacklist(ListUserContentBlacklistRequest) returns (ListUserContentBlacklistResponse);
  rpc AdminAddUserContentBlacklist(AdminAddUserContentBlacklistRequest) returns (AdminAddUserContentBlacklistResponse);
  rpc AdminRemoveUserContentBlacklist(AdminRemoveUserContentBlacklistRequest) returns (AdminRemoveUserContentBlacklistResponse);
  rpc BatchGetRoomBasicUsers(BatchGetRoomBasicUsersRequest) returns (BatchGetRoomBasicUsersResponse);
  rpc ListUserIDs(ListUserIDsRequest) returns (ListUserIDsResponse);
  rpc GetUserMicLifetimeStats(GetUserMicLifetimeStatsRequest) returns (GetUserMicLifetimeStatsResponse);
  rpc AuthorizeUserAvatarUpload(AuthorizeUserAvatarUploadRequest) returns (AuthorizeUserAvatarUploadResponse);
  rpc CompleteUserAvatarUpload(CompleteUserAvatarUploadRequest) returns (CompleteUserAvatarUploadResponse);
  rpc UpdateUserProfile(UpdateUserProfileRequest) returns (UpdateUserProfileResponse);
  rpc UpdateUserProfileBackground(UpdateUserProfileBackgroundRequest) returns (UpdateUserProfileBackgroundResponse);
  rpc UpdateUserContactInfo(UpdateUserContactInfoRequest) returns (UpdateUserContactInfoResponse);
  rpc UpdateUserWithdrawAddress(UpdateUserWithdrawAddressRequest) returns (UpdateUserWithdrawAddressResponse);
  rpc ChangeUserCountry(ChangeUserCountryRequest) returns (ChangeUserCountryResponse);
  rpc AdminChangeUserCountry(AdminChangeUserCountryRequest) returns (ChangeUserCountryResponse);
  rpc SetUserStatus(SetUserStatusRequest) returns (SetUserStatusResponse);
  rpc AdminBanUser(AdminBanUserRequest) returns (AdminBanUserResponse);
  rpc AdminUnbanUser(AdminUnbanUserRequest) returns (AdminUnbanUserResponse);
  rpc CreateManagerUserBlock(CreateManagerUserBlockRequest) returns (CreateManagerUserBlockResponse);
  rpc ListManagerUserBlocks(ListManagerUserBlocksRequest) returns (ListManagerUserBlocksResponse);
  rpc UnblockManagerUser(UnblockManagerUserRequest) returns (UnblockManagerUserResponse);
  rpc CompleteOnboarding(CompleteOnboardingRequest) returns (CompleteOnboardingResponse);
  rpc SearchInviteReferrer(SearchInviteReferrerRequest) returns (SearchInviteReferrerResponse);
  rpc BindInviteReferrer(BindInviteReferrerRequest) returns (BindInviteReferrerResponse);
}

// UserSocialService 承载用户访问、关注和好友关系。
service UserSocialService {
  rpc RecordProfileVisit(RecordProfileVisitRequest) returns (RecordProfileVisitResponse);
  rpc ListProfileVisitors(ListProfileVisitorsRequest) returns (ListProfileVisitorsResponse);
  rpc FollowUser(FollowUserRequest) returns (FollowUserResponse);
  rpc UnfollowUser(UnfollowUserRequest) returns (UnfollowUserResponse);
  rpc ListFollowing(ListFollowingRequest) returns (ListFollowingResponse);
  rpc ApplyFriend(ApplyFriendRequest) returns (ApplyFriendResponse);
  rpc AcceptFriendApplication(AcceptFriendApplicationRequest) returns (AcceptFriendApplicationResponse);
  rpc DeleteFriend(DeleteFriendRequest) returns (DeleteFriendResponse);
  rpc ListFriends(ListFriendsRequest) returns (ListFriendsResponse);
  rpc ListFriendApplications(ListFriendApplicationsRequest) returns (ListFriendApplicationsResponse);
  rpc SubmitReport(SubmitReportRequest) returns (SubmitReportResponse);
}

// UserCPService 承载 App 端 CP/兄弟/姐妹关系申请、处理和列表。
service UserCPService {
  rpc ListCPApplications(ListCPApplicationsRequest) returns (ListCPApplicationsResponse);
  rpc AcceptCPApplication(AcceptCPApplicationRequest) returns (AcceptCPApplicationResponse);
  rpc RejectCPApplication(RejectCPApplicationRequest) returns (RejectCPApplicationResponse);
  rpc ListCPRelationships(ListCPRelationshipsRequest) returns (ListCPRelationshipsResponse);
  rpc ListCPIntimacyLeaderboard(ListCPIntimacyLeaderboardRequest) returns (ListCPIntimacyLeaderboardResponse);
  rpc PrepareBreakCPRelationship(PrepareBreakCPRelationshipRequest) returns (PrepareBreakCPRelationshipResponse);
  rpc ConfirmBreakCPRelationship(ConfirmBreakCPRelationshipRequest) returns (ConfirmBreakCPRelationshipResponse);
  rpc CancelBreakCPRelationship(CancelBreakCPRelationshipRequest) returns (CancelBreakCPRelationshipResponse);
  rpc ListCPFormationGiftFeed(ListCPFormationGiftFeedRequest) returns (ListCPFormationGiftFeedResponse);
}

// UserCPInternalService 给 owner outbox 消费 worker 调用，不暴露给 App 或后台。
service UserCPInternalService {
  rpc ConsumeRoomGiftCPEvent(ConsumeRoomGiftCPEventRequest) returns (ConsumeRoomGiftCPEventResponse);
  rpc ListCPWeeklyRankEntries(ListCPWeeklyRankEntriesRequest) returns (ListCPWeeklyRankEntriesResponse);
}

// UserCronService 只给 cron-service 调用，业务状态仍由 user-service owner 修改。
service UserCronService {
  rpc ProcessLoginIPRiskBatch(CronBatchRequest) returns (CronBatchResponse);
  rpc ProcessRegionRebuildBatch(CronBatchRequest) returns (CronBatchResponse);
  rpc CompensateMicOpenSessions(CronBatchRequest) returns (CronBatchResponse);
  rpc CompensateRoomOpenSessions(CronBatchRequest) returns (CronBatchResponse);
  rpc ExpireManagerUserBlocks(CronBatchRequest) returns (CronBatchResponse);
  rpc ExpireAdminUserBans(CronBatchRequest) returns (CronBatchResponse);
  rpc RefreshCPIntimacyLeaderboard(CronBatchRequest) returns (CronBatchResponse);
  rpc ProcessPrivateMessageHistoryBatch(CronBatchRequest) returns (CronBatchResponse);
}

// UserDeviceService 承载 App 设备推送 token 的绑定和失效。
service UserDeviceService {
  rpc BindPushToken(BindPushTokenRequest) returns (BindPushTokenResponse);
  rpc DeletePushToken(DeletePushTokenRequest) returns (DeletePushTokenResponse);
}

// ListAppsRequest 由内部调度器读取启用租户；不接受租户过滤，避免后台任务再次退化成单 App。
message ListAppsRequest {
  RequestMeta meta = 1;
}

message ListAppsResponse {
  repeated App apps = 1;
}

// ResolveAdminUserIdentifierRequest 是后台精确查人的内部契约。与客户端展示号解析不同，
// user_identifier 可以是永久默认短号、当前有效展示号或系统长 user_id。
message ResolveAdminUserIdentifierRequest {
  RequestMeta meta = 1;
  string user_identifier = 2;
}

// ResolveAdminUserIdentifierResponse 返回匹配用户的当前有效身份投影。
message ResolveAdminUserIdentifierResponse {
  UserIdentity identity = 1;
}

// AppRegistryService 是 gateway 解析 package_name -> app_code 的内部入口。
service AppRegistryService {
  rpc ResolveApp(ResolveAppRequest) returns (ResolveAppResponse);
  rpc ListApps(ListAppsRequest) returns (ListAppsResponse);
}

// CountryAdminService 只保留 App 后端需要承接的国家查询和展示字段更新 RPC。
// 创建国家、启用/禁用国家属于 hyapp-admin-server 后台能力，不在 user-service 暴露 RPC。
service CountryAdminService {
  rpc ListCountries(ListCountriesRequest) returns (ListCountriesResponse);
  rpc UpdateCountry(UpdateCountryRequest) returns (CountryResponse);
}

// CountryQueryService 承载 App 注册页公开国家列表查询。
service CountryQueryService {
  rpc ListRegistrationCountries(ListRegistrationCountriesRequest) returns (ListRegistrationCountriesResponse);
  rpc ListLoginRiskBlockedCountries(ListLoginRiskBlockedCountriesRequest) returns (ListLoginRiskBlockedCountriesResponse);
}

// RegionAdminService 只保留 App 后端需要承接的区域查询、展示字段更新和国家归属替换 RPC。
// 创建区域、启用/禁用区域属于 hyapp-admin-server 后台能力，不在 user-service 暴露 RPC。
service RegionAdminService {
  rpc ListRegions(ListRegionsRequest) returns (ListRegionsResponse);
  rpc GetRegion(GetRegionRequest) returns (RegionResponse);
  rpc UpdateRegion(UpdateRegionRequest) returns (RegionResponse);
  rpc ReplaceRegionCountries(ReplaceRegionCountriesRequest) returns (RegionResponse);
}

// UserIdentityService 是短号解析和变更的唯一内部 owner。
service UserIdentityService {
  rpc GetUserIdentity(GetUserIdentityRequest) returns (GetUserIdentityResponse);
  rpc ResolveDisplayUserID(ResolveDisplayUserIDRequest) returns (ResolveDisplayUserIDResponse);
  // ResolveAdminUserIdentifier 仅供内部后台精确搜索；不得替代客户端 active 展示号解析语义。
  rpc ResolveAdminUserIdentifier(ResolveAdminUserIdentifierRequest) returns (ResolveAdminUserIdentifierResponse);
  rpc ChangeDisplayUserID(ChangeDisplayUserIDRequest) returns (ChangeDisplayUserIDResponse);
  rpc ApplyPrettyDisplayUserID(ApplyPrettyDisplayUserIDRequest) returns (ApplyPrettyDisplayUserIDResponse);
  rpc ListAvailablePrettyDisplayIDs(ListAvailablePrettyDisplayIDsRequest) returns (ListAvailablePrettyDisplayIDsResponse);
  rpc ApplyPrettyDisplayIDFromPool(ApplyPrettyDisplayIDFromPoolRequest) returns (ApplyPrettyDisplayIDFromPoolResponse);
  rpc ExpirePrettyDisplayUserID(ExpirePrettyDisplayUserIDRequest) returns (ExpirePrettyDisplayUserIDResponse);
}

// UserPrettyDisplayIDAdminService 承载后台靓号池和后台发放能力。
service UserPrettyDisplayIDAdminService {
  rpc ListPrettyDisplayIDPools(ListPrettyDisplayIDPoolsRequest) returns (ListPrettyDisplayIDPoolsResponse);
  rpc CreatePrettyDisplayIDPool(CreatePrettyDisplayIDPoolRequest) returns (PrettyDisplayIDPoolResponse);
  rpc UpdatePrettyDisplayIDPool(UpdatePrettyDisplayIDPoolRequest) returns (PrettyDisplayIDPoolResponse);
  rpc GeneratePrettyDisplayIDs(GeneratePrettyDisplayIDsRequest) returns (GeneratePrettyDisplayIDsResponse);
  rpc ListPrettyDisplayIDs(ListPrettyDisplayIDsRequest) returns (ListPrettyDisplayIDsResponse);
  rpc SetPrettyDisplayIDStatus(SetPrettyDisplayIDStatusRequest) returns (PrettyDisplayIDResponse);
  rpc RecyclePrettyDisplayID(RecyclePrettyDisplayIDRequest) returns (PrettyDisplayIDResponse);
  rpc AdminGrantPrettyDisplayID(AdminGrantPrettyDisplayIDRequest) returns (AdminGrantPrettyDisplayIDResponse);
}
