fix: sync host private messages from IM history
This commit is contained in:
parent
42e4adab56
commit
180d7ecd8b
@ -15143,7 +15143,7 @@ const file_proto_user_v1_user_proto_rawDesc = "" +
|
||||
"\x17ListCPFormationGiftFeed\x12-.hyapp.user.v1.ListCPFormationGiftFeedRequest\x1a..hyapp.user.v1.ListCPFormationGiftFeedResponse2\x88\x02\n" +
|
||||
"\x15UserCPInternalService\x12u\n" +
|
||||
"\x16ConsumeRoomGiftCPEvent\x12,.hyapp.user.v1.ConsumeRoomGiftCPEventRequest\x1a-.hyapp.user.v1.ConsumeRoomGiftCPEventResponse\x12x\n" +
|
||||
"\x17ListCPWeeklyRankEntries\x12-.hyapp.user.v1.ListCPWeeklyRankEntriesRequest\x1a..hyapp.user.v1.ListCPWeeklyRankEntriesResponse2\xab\x05\n" +
|
||||
"\x17ListCPWeeklyRankEntries\x12-.hyapp.user.v1.ListCPWeeklyRankEntriesRequest\x1a..hyapp.user.v1.ListCPWeeklyRankEntriesResponse2\x93\x06\n" +
|
||||
"\x0fUserCronService\x12\\\n" +
|
||||
"\x17ProcessLoginIPRiskBatch\x12\x1f.hyapp.user.v1.CronBatchRequest\x1a .hyapp.user.v1.CronBatchResponse\x12^\n" +
|
||||
"\x19ProcessRegionRebuildBatch\x12\x1f.hyapp.user.v1.CronBatchRequest\x1a .hyapp.user.v1.CronBatchResponse\x12^\n" +
|
||||
@ -15151,7 +15151,8 @@ const file_proto_user_v1_user_proto_rawDesc = "" +
|
||||
"\x1aCompensateRoomOpenSessions\x12\x1f.hyapp.user.v1.CronBatchRequest\x1a .hyapp.user.v1.CronBatchResponse\x12\\\n" +
|
||||
"\x17ExpireManagerUserBlocks\x12\x1f.hyapp.user.v1.CronBatchRequest\x1a .hyapp.user.v1.CronBatchResponse\x12X\n" +
|
||||
"\x13ExpireAdminUserBans\x12\x1f.hyapp.user.v1.CronBatchRequest\x1a .hyapp.user.v1.CronBatchResponse\x12a\n" +
|
||||
"\x1cRefreshCPIntimacyLeaderboard\x12\x1f.hyapp.user.v1.CronBatchRequest\x1a .hyapp.user.v1.CronBatchResponse2\xd1\x01\n" +
|
||||
"\x1cRefreshCPIntimacyLeaderboard\x12\x1f.hyapp.user.v1.CronBatchRequest\x1a .hyapp.user.v1.CronBatchResponse\x12f\n" +
|
||||
"!ProcessPrivateMessageHistoryBatch\x12\x1f.hyapp.user.v1.CronBatchRequest\x1a .hyapp.user.v1.CronBatchResponse2\xd1\x01\n" +
|
||||
"\x11UserDeviceService\x12Z\n" +
|
||||
"\rBindPushToken\x12#.hyapp.user.v1.BindPushTokenRequest\x1a$.hyapp.user.v1.BindPushTokenResponse\x12`\n" +
|
||||
"\x0fDeletePushToken\x12%.hyapp.user.v1.DeletePushTokenRequest\x1a&.hyapp.user.v1.DeletePushTokenResponse2\xb4\x01\n" +
|
||||
@ -15634,120 +15635,122 @@ var file_proto_user_v1_user_proto_depIdxs = []int32{
|
||||
14, // 232: hyapp.user.v1.UserCronService.ExpireManagerUserBlocks:input_type -> hyapp.user.v1.CronBatchRequest
|
||||
14, // 233: hyapp.user.v1.UserCronService.ExpireAdminUserBans:input_type -> hyapp.user.v1.CronBatchRequest
|
||||
14, // 234: hyapp.user.v1.UserCronService.RefreshCPIntimacyLeaderboard:input_type -> hyapp.user.v1.CronBatchRequest
|
||||
130, // 235: hyapp.user.v1.UserDeviceService.BindPushToken:input_type -> hyapp.user.v1.BindPushTokenRequest
|
||||
132, // 236: hyapp.user.v1.UserDeviceService.DeletePushToken:input_type -> hyapp.user.v1.DeletePushTokenRequest
|
||||
3, // 237: hyapp.user.v1.AppRegistryService.ResolveApp:input_type -> hyapp.user.v1.ResolveAppRequest
|
||||
186, // 238: hyapp.user.v1.AppRegistryService.ListApps:input_type -> hyapp.user.v1.ListAppsRequest
|
||||
136, // 239: hyapp.user.v1.CountryAdminService.ListCountries:input_type -> hyapp.user.v1.ListCountriesRequest
|
||||
138, // 240: hyapp.user.v1.CountryAdminService.UpdateCountry:input_type -> hyapp.user.v1.UpdateCountryRequest
|
||||
140, // 241: hyapp.user.v1.CountryQueryService.ListRegistrationCountries:input_type -> hyapp.user.v1.ListRegistrationCountriesRequest
|
||||
143, // 242: hyapp.user.v1.CountryQueryService.ListLoginRiskBlockedCountries:input_type -> hyapp.user.v1.ListLoginRiskBlockedCountriesRequest
|
||||
145, // 243: hyapp.user.v1.RegionAdminService.ListRegions:input_type -> hyapp.user.v1.ListRegionsRequest
|
||||
147, // 244: hyapp.user.v1.RegionAdminService.GetRegion:input_type -> hyapp.user.v1.GetRegionRequest
|
||||
148, // 245: hyapp.user.v1.RegionAdminService.UpdateRegion:input_type -> hyapp.user.v1.UpdateRegionRequest
|
||||
149, // 246: hyapp.user.v1.RegionAdminService.ReplaceRegionCountries:input_type -> hyapp.user.v1.ReplaceRegionCountriesRequest
|
||||
152, // 247: hyapp.user.v1.UserIdentityService.GetUserIdentity:input_type -> hyapp.user.v1.GetUserIdentityRequest
|
||||
154, // 248: hyapp.user.v1.UserIdentityService.ResolveDisplayUserID:input_type -> hyapp.user.v1.ResolveDisplayUserIDRequest
|
||||
188, // 249: hyapp.user.v1.UserIdentityService.ResolveAdminUserIdentifier:input_type -> hyapp.user.v1.ResolveAdminUserIdentifierRequest
|
||||
156, // 250: hyapp.user.v1.UserIdentityService.ChangeDisplayUserID:input_type -> hyapp.user.v1.ChangeDisplayUserIDRequest
|
||||
158, // 251: hyapp.user.v1.UserIdentityService.ApplyPrettyDisplayUserID:input_type -> hyapp.user.v1.ApplyPrettyDisplayUserIDRequest
|
||||
165, // 252: hyapp.user.v1.UserIdentityService.ListAvailablePrettyDisplayIDs:input_type -> hyapp.user.v1.ListAvailablePrettyDisplayIDsRequest
|
||||
167, // 253: hyapp.user.v1.UserIdentityService.ApplyPrettyDisplayIDFromPool:input_type -> hyapp.user.v1.ApplyPrettyDisplayIDFromPoolRequest
|
||||
160, // 254: hyapp.user.v1.UserIdentityService.ExpirePrettyDisplayUserID:input_type -> hyapp.user.v1.ExpirePrettyDisplayUserIDRequest
|
||||
169, // 255: hyapp.user.v1.UserPrettyDisplayIDAdminService.ListPrettyDisplayIDPools:input_type -> hyapp.user.v1.ListPrettyDisplayIDPoolsRequest
|
||||
171, // 256: hyapp.user.v1.UserPrettyDisplayIDAdminService.CreatePrettyDisplayIDPool:input_type -> hyapp.user.v1.CreatePrettyDisplayIDPoolRequest
|
||||
172, // 257: hyapp.user.v1.UserPrettyDisplayIDAdminService.UpdatePrettyDisplayIDPool:input_type -> hyapp.user.v1.UpdatePrettyDisplayIDPoolRequest
|
||||
174, // 258: hyapp.user.v1.UserPrettyDisplayIDAdminService.GeneratePrettyDisplayIDs:input_type -> hyapp.user.v1.GeneratePrettyDisplayIDsRequest
|
||||
176, // 259: hyapp.user.v1.UserPrettyDisplayIDAdminService.ListPrettyDisplayIDs:input_type -> hyapp.user.v1.ListPrettyDisplayIDsRequest
|
||||
178, // 260: hyapp.user.v1.UserPrettyDisplayIDAdminService.SetPrettyDisplayIDStatus:input_type -> hyapp.user.v1.SetPrettyDisplayIDStatusRequest
|
||||
179, // 261: hyapp.user.v1.UserPrettyDisplayIDAdminService.RecyclePrettyDisplayID:input_type -> hyapp.user.v1.RecyclePrettyDisplayIDRequest
|
||||
181, // 262: hyapp.user.v1.UserPrettyDisplayIDAdminService.AdminGrantPrettyDisplayID:input_type -> hyapp.user.v1.AdminGrantPrettyDisplayIDRequest
|
||||
18, // 263: hyapp.user.v1.UserService.GetUser:output_type -> hyapp.user.v1.GetUserResponse
|
||||
10, // 264: hyapp.user.v1.UserService.GetInviteAttribution:output_type -> hyapp.user.v1.GetInviteAttributionResponse
|
||||
21, // 265: hyapp.user.v1.UserService.BusinessUserLookup:output_type -> hyapp.user.v1.BusinessUserLookupResponse
|
||||
24, // 266: hyapp.user.v1.UserService.GetMyProfileStats:output_type -> hyapp.user.v1.GetMyProfileStatsResponse
|
||||
82, // 267: hyapp.user.v1.UserService.BatchGetUsers:output_type -> hyapp.user.v1.BatchGetUsersResponse
|
||||
86, // 268: hyapp.user.v1.UserService.BatchGetUserAdminProfiles:output_type -> hyapp.user.v1.BatchGetUserAdminProfilesResponse
|
||||
88, // 269: hyapp.user.v1.UserService.AdminIssueUserAccessToken:output_type -> hyapp.user.v1.AdminIssueUserAccessTokenResponse
|
||||
91, // 270: hyapp.user.v1.UserService.BatchGetRoomBasicUsers:output_type -> hyapp.user.v1.BatchGetRoomBasicUsersResponse
|
||||
93, // 271: hyapp.user.v1.UserService.ListUserIDs:output_type -> hyapp.user.v1.ListUserIDsResponse
|
||||
13, // 272: hyapp.user.v1.UserService.GetUserMicLifetimeStats:output_type -> hyapp.user.v1.GetUserMicLifetimeStatsResponse
|
||||
98, // 273: hyapp.user.v1.UserService.AuthorizeUserAvatarUpload:output_type -> hyapp.user.v1.AuthorizeUserAvatarUploadResponse
|
||||
100, // 274: hyapp.user.v1.UserService.CompleteUserAvatarUpload:output_type -> hyapp.user.v1.CompleteUserAvatarUploadResponse
|
||||
95, // 275: hyapp.user.v1.UserService.UpdateUserProfile:output_type -> hyapp.user.v1.UpdateUserProfileResponse
|
||||
102, // 276: hyapp.user.v1.UserService.UpdateUserProfileBackground:output_type -> hyapp.user.v1.UpdateUserProfileBackgroundResponse
|
||||
104, // 277: hyapp.user.v1.UserService.UpdateUserContactInfo:output_type -> hyapp.user.v1.UpdateUserContactInfoResponse
|
||||
106, // 278: hyapp.user.v1.UserService.UpdateUserWithdrawAddress:output_type -> hyapp.user.v1.UpdateUserWithdrawAddressResponse
|
||||
109, // 279: hyapp.user.v1.UserService.ChangeUserCountry:output_type -> hyapp.user.v1.ChangeUserCountryResponse
|
||||
109, // 280: hyapp.user.v1.UserService.AdminChangeUserCountry:output_type -> hyapp.user.v1.ChangeUserCountryResponse
|
||||
111, // 281: hyapp.user.v1.UserService.SetUserStatus:output_type -> hyapp.user.v1.SetUserStatusResponse
|
||||
114, // 282: hyapp.user.v1.UserService.AdminBanUser:output_type -> hyapp.user.v1.AdminBanUserResponse
|
||||
116, // 283: hyapp.user.v1.UserService.AdminUnbanUser:output_type -> hyapp.user.v1.AdminUnbanUserResponse
|
||||
119, // 284: hyapp.user.v1.UserService.CreateManagerUserBlock:output_type -> hyapp.user.v1.CreateManagerUserBlockResponse
|
||||
121, // 285: hyapp.user.v1.UserService.ListManagerUserBlocks:output_type -> hyapp.user.v1.ListManagerUserBlocksResponse
|
||||
123, // 286: hyapp.user.v1.UserService.UnblockManagerUser:output_type -> hyapp.user.v1.UnblockManagerUserResponse
|
||||
125, // 287: hyapp.user.v1.UserService.CompleteOnboarding:output_type -> hyapp.user.v1.CompleteOnboardingResponse
|
||||
127, // 288: hyapp.user.v1.UserService.SearchInviteReferrer:output_type -> hyapp.user.v1.SearchInviteReferrerResponse
|
||||
129, // 289: hyapp.user.v1.UserService.BindInviteReferrer:output_type -> hyapp.user.v1.BindInviteReferrerResponse
|
||||
26, // 290: hyapp.user.v1.UserSocialService.RecordProfileVisit:output_type -> hyapp.user.v1.RecordProfileVisitResponse
|
||||
29, // 291: hyapp.user.v1.UserSocialService.ListProfileVisitors:output_type -> hyapp.user.v1.ListProfileVisitorsResponse
|
||||
31, // 292: hyapp.user.v1.UserSocialService.FollowUser:output_type -> hyapp.user.v1.FollowUserResponse
|
||||
33, // 293: hyapp.user.v1.UserSocialService.UnfollowUser:output_type -> hyapp.user.v1.UnfollowUserResponse
|
||||
36, // 294: hyapp.user.v1.UserSocialService.ListFollowing:output_type -> hyapp.user.v1.ListFollowingResponse
|
||||
38, // 295: hyapp.user.v1.UserSocialService.ApplyFriend:output_type -> hyapp.user.v1.ApplyFriendResponse
|
||||
40, // 296: hyapp.user.v1.UserSocialService.AcceptFriendApplication:output_type -> hyapp.user.v1.AcceptFriendApplicationResponse
|
||||
42, // 297: hyapp.user.v1.UserSocialService.DeleteFriend:output_type -> hyapp.user.v1.DeleteFriendResponse
|
||||
45, // 298: hyapp.user.v1.UserSocialService.ListFriends:output_type -> hyapp.user.v1.ListFriendsResponse
|
||||
48, // 299: hyapp.user.v1.UserSocialService.ListFriendApplications:output_type -> hyapp.user.v1.ListFriendApplicationsResponse
|
||||
80, // 300: hyapp.user.v1.UserSocialService.SubmitReport:output_type -> hyapp.user.v1.SubmitReportResponse
|
||||
60, // 301: hyapp.user.v1.UserCPService.ListCPApplications:output_type -> hyapp.user.v1.ListCPApplicationsResponse
|
||||
62, // 302: hyapp.user.v1.UserCPService.AcceptCPApplication:output_type -> hyapp.user.v1.AcceptCPApplicationResponse
|
||||
64, // 303: hyapp.user.v1.UserCPService.RejectCPApplication:output_type -> hyapp.user.v1.RejectCPApplicationResponse
|
||||
66, // 304: hyapp.user.v1.UserCPService.ListCPRelationships:output_type -> hyapp.user.v1.ListCPRelationshipsResponse
|
||||
68, // 305: hyapp.user.v1.UserCPService.ListCPIntimacyLeaderboard:output_type -> hyapp.user.v1.ListCPIntimacyLeaderboardResponse
|
||||
70, // 306: hyapp.user.v1.UserCPService.PrepareBreakCPRelationship:output_type -> hyapp.user.v1.PrepareBreakCPRelationshipResponse
|
||||
72, // 307: hyapp.user.v1.UserCPService.ConfirmBreakCPRelationship:output_type -> hyapp.user.v1.ConfirmBreakCPRelationshipResponse
|
||||
74, // 308: hyapp.user.v1.UserCPService.CancelBreakCPRelationship:output_type -> hyapp.user.v1.CancelBreakCPRelationshipResponse
|
||||
185, // 309: hyapp.user.v1.UserCPService.ListCPFormationGiftFeed:output_type -> hyapp.user.v1.ListCPFormationGiftFeedResponse
|
||||
77, // 310: hyapp.user.v1.UserCPInternalService.ConsumeRoomGiftCPEvent:output_type -> hyapp.user.v1.ConsumeRoomGiftCPEventResponse
|
||||
58, // 311: hyapp.user.v1.UserCPInternalService.ListCPWeeklyRankEntries:output_type -> hyapp.user.v1.ListCPWeeklyRankEntriesResponse
|
||||
15, // 312: hyapp.user.v1.UserCronService.ProcessLoginIPRiskBatch:output_type -> hyapp.user.v1.CronBatchResponse
|
||||
15, // 313: hyapp.user.v1.UserCronService.ProcessRegionRebuildBatch:output_type -> hyapp.user.v1.CronBatchResponse
|
||||
15, // 314: hyapp.user.v1.UserCronService.CompensateMicOpenSessions:output_type -> hyapp.user.v1.CronBatchResponse
|
||||
15, // 315: hyapp.user.v1.UserCronService.CompensateRoomOpenSessions:output_type -> hyapp.user.v1.CronBatchResponse
|
||||
15, // 316: hyapp.user.v1.UserCronService.ExpireManagerUserBlocks:output_type -> hyapp.user.v1.CronBatchResponse
|
||||
15, // 317: hyapp.user.v1.UserCronService.ExpireAdminUserBans:output_type -> hyapp.user.v1.CronBatchResponse
|
||||
15, // 318: hyapp.user.v1.UserCronService.RefreshCPIntimacyLeaderboard:output_type -> hyapp.user.v1.CronBatchResponse
|
||||
131, // 319: hyapp.user.v1.UserDeviceService.BindPushToken:output_type -> hyapp.user.v1.BindPushTokenResponse
|
||||
133, // 320: hyapp.user.v1.UserDeviceService.DeletePushToken:output_type -> hyapp.user.v1.DeletePushTokenResponse
|
||||
4, // 321: hyapp.user.v1.AppRegistryService.ResolveApp:output_type -> hyapp.user.v1.ResolveAppResponse
|
||||
187, // 322: hyapp.user.v1.AppRegistryService.ListApps:output_type -> hyapp.user.v1.ListAppsResponse
|
||||
137, // 323: hyapp.user.v1.CountryAdminService.ListCountries:output_type -> hyapp.user.v1.ListCountriesResponse
|
||||
139, // 324: hyapp.user.v1.CountryAdminService.UpdateCountry:output_type -> hyapp.user.v1.CountryResponse
|
||||
141, // 325: hyapp.user.v1.CountryQueryService.ListRegistrationCountries:output_type -> hyapp.user.v1.ListRegistrationCountriesResponse
|
||||
144, // 326: hyapp.user.v1.CountryQueryService.ListLoginRiskBlockedCountries:output_type -> hyapp.user.v1.ListLoginRiskBlockedCountriesResponse
|
||||
146, // 327: hyapp.user.v1.RegionAdminService.ListRegions:output_type -> hyapp.user.v1.ListRegionsResponse
|
||||
150, // 328: hyapp.user.v1.RegionAdminService.GetRegion:output_type -> hyapp.user.v1.RegionResponse
|
||||
150, // 329: hyapp.user.v1.RegionAdminService.UpdateRegion:output_type -> hyapp.user.v1.RegionResponse
|
||||
150, // 330: hyapp.user.v1.RegionAdminService.ReplaceRegionCountries:output_type -> hyapp.user.v1.RegionResponse
|
||||
153, // 331: hyapp.user.v1.UserIdentityService.GetUserIdentity:output_type -> hyapp.user.v1.GetUserIdentityResponse
|
||||
155, // 332: hyapp.user.v1.UserIdentityService.ResolveDisplayUserID:output_type -> hyapp.user.v1.ResolveDisplayUserIDResponse
|
||||
189, // 333: hyapp.user.v1.UserIdentityService.ResolveAdminUserIdentifier:output_type -> hyapp.user.v1.ResolveAdminUserIdentifierResponse
|
||||
157, // 334: hyapp.user.v1.UserIdentityService.ChangeDisplayUserID:output_type -> hyapp.user.v1.ChangeDisplayUserIDResponse
|
||||
159, // 335: hyapp.user.v1.UserIdentityService.ApplyPrettyDisplayUserID:output_type -> hyapp.user.v1.ApplyPrettyDisplayUserIDResponse
|
||||
166, // 336: hyapp.user.v1.UserIdentityService.ListAvailablePrettyDisplayIDs:output_type -> hyapp.user.v1.ListAvailablePrettyDisplayIDsResponse
|
||||
168, // 337: hyapp.user.v1.UserIdentityService.ApplyPrettyDisplayIDFromPool:output_type -> hyapp.user.v1.ApplyPrettyDisplayIDFromPoolResponse
|
||||
161, // 338: hyapp.user.v1.UserIdentityService.ExpirePrettyDisplayUserID:output_type -> hyapp.user.v1.ExpirePrettyDisplayUserIDResponse
|
||||
170, // 339: hyapp.user.v1.UserPrettyDisplayIDAdminService.ListPrettyDisplayIDPools:output_type -> hyapp.user.v1.ListPrettyDisplayIDPoolsResponse
|
||||
173, // 340: hyapp.user.v1.UserPrettyDisplayIDAdminService.CreatePrettyDisplayIDPool:output_type -> hyapp.user.v1.PrettyDisplayIDPoolResponse
|
||||
173, // 341: hyapp.user.v1.UserPrettyDisplayIDAdminService.UpdatePrettyDisplayIDPool:output_type -> hyapp.user.v1.PrettyDisplayIDPoolResponse
|
||||
175, // 342: hyapp.user.v1.UserPrettyDisplayIDAdminService.GeneratePrettyDisplayIDs:output_type -> hyapp.user.v1.GeneratePrettyDisplayIDsResponse
|
||||
177, // 343: hyapp.user.v1.UserPrettyDisplayIDAdminService.ListPrettyDisplayIDs:output_type -> hyapp.user.v1.ListPrettyDisplayIDsResponse
|
||||
180, // 344: hyapp.user.v1.UserPrettyDisplayIDAdminService.SetPrettyDisplayIDStatus:output_type -> hyapp.user.v1.PrettyDisplayIDResponse
|
||||
180, // 345: hyapp.user.v1.UserPrettyDisplayIDAdminService.RecyclePrettyDisplayID:output_type -> hyapp.user.v1.PrettyDisplayIDResponse
|
||||
182, // 346: hyapp.user.v1.UserPrettyDisplayIDAdminService.AdminGrantPrettyDisplayID:output_type -> hyapp.user.v1.AdminGrantPrettyDisplayIDResponse
|
||||
263, // [263:347] is the sub-list for method output_type
|
||||
179, // [179:263] is the sub-list for method input_type
|
||||
14, // 235: hyapp.user.v1.UserCronService.ProcessPrivateMessageHistoryBatch:input_type -> hyapp.user.v1.CronBatchRequest
|
||||
130, // 236: hyapp.user.v1.UserDeviceService.BindPushToken:input_type -> hyapp.user.v1.BindPushTokenRequest
|
||||
132, // 237: hyapp.user.v1.UserDeviceService.DeletePushToken:input_type -> hyapp.user.v1.DeletePushTokenRequest
|
||||
3, // 238: hyapp.user.v1.AppRegistryService.ResolveApp:input_type -> hyapp.user.v1.ResolveAppRequest
|
||||
186, // 239: hyapp.user.v1.AppRegistryService.ListApps:input_type -> hyapp.user.v1.ListAppsRequest
|
||||
136, // 240: hyapp.user.v1.CountryAdminService.ListCountries:input_type -> hyapp.user.v1.ListCountriesRequest
|
||||
138, // 241: hyapp.user.v1.CountryAdminService.UpdateCountry:input_type -> hyapp.user.v1.UpdateCountryRequest
|
||||
140, // 242: hyapp.user.v1.CountryQueryService.ListRegistrationCountries:input_type -> hyapp.user.v1.ListRegistrationCountriesRequest
|
||||
143, // 243: hyapp.user.v1.CountryQueryService.ListLoginRiskBlockedCountries:input_type -> hyapp.user.v1.ListLoginRiskBlockedCountriesRequest
|
||||
145, // 244: hyapp.user.v1.RegionAdminService.ListRegions:input_type -> hyapp.user.v1.ListRegionsRequest
|
||||
147, // 245: hyapp.user.v1.RegionAdminService.GetRegion:input_type -> hyapp.user.v1.GetRegionRequest
|
||||
148, // 246: hyapp.user.v1.RegionAdminService.UpdateRegion:input_type -> hyapp.user.v1.UpdateRegionRequest
|
||||
149, // 247: hyapp.user.v1.RegionAdminService.ReplaceRegionCountries:input_type -> hyapp.user.v1.ReplaceRegionCountriesRequest
|
||||
152, // 248: hyapp.user.v1.UserIdentityService.GetUserIdentity:input_type -> hyapp.user.v1.GetUserIdentityRequest
|
||||
154, // 249: hyapp.user.v1.UserIdentityService.ResolveDisplayUserID:input_type -> hyapp.user.v1.ResolveDisplayUserIDRequest
|
||||
188, // 250: hyapp.user.v1.UserIdentityService.ResolveAdminUserIdentifier:input_type -> hyapp.user.v1.ResolveAdminUserIdentifierRequest
|
||||
156, // 251: hyapp.user.v1.UserIdentityService.ChangeDisplayUserID:input_type -> hyapp.user.v1.ChangeDisplayUserIDRequest
|
||||
158, // 252: hyapp.user.v1.UserIdentityService.ApplyPrettyDisplayUserID:input_type -> hyapp.user.v1.ApplyPrettyDisplayUserIDRequest
|
||||
165, // 253: hyapp.user.v1.UserIdentityService.ListAvailablePrettyDisplayIDs:input_type -> hyapp.user.v1.ListAvailablePrettyDisplayIDsRequest
|
||||
167, // 254: hyapp.user.v1.UserIdentityService.ApplyPrettyDisplayIDFromPool:input_type -> hyapp.user.v1.ApplyPrettyDisplayIDFromPoolRequest
|
||||
160, // 255: hyapp.user.v1.UserIdentityService.ExpirePrettyDisplayUserID:input_type -> hyapp.user.v1.ExpirePrettyDisplayUserIDRequest
|
||||
169, // 256: hyapp.user.v1.UserPrettyDisplayIDAdminService.ListPrettyDisplayIDPools:input_type -> hyapp.user.v1.ListPrettyDisplayIDPoolsRequest
|
||||
171, // 257: hyapp.user.v1.UserPrettyDisplayIDAdminService.CreatePrettyDisplayIDPool:input_type -> hyapp.user.v1.CreatePrettyDisplayIDPoolRequest
|
||||
172, // 258: hyapp.user.v1.UserPrettyDisplayIDAdminService.UpdatePrettyDisplayIDPool:input_type -> hyapp.user.v1.UpdatePrettyDisplayIDPoolRequest
|
||||
174, // 259: hyapp.user.v1.UserPrettyDisplayIDAdminService.GeneratePrettyDisplayIDs:input_type -> hyapp.user.v1.GeneratePrettyDisplayIDsRequest
|
||||
176, // 260: hyapp.user.v1.UserPrettyDisplayIDAdminService.ListPrettyDisplayIDs:input_type -> hyapp.user.v1.ListPrettyDisplayIDsRequest
|
||||
178, // 261: hyapp.user.v1.UserPrettyDisplayIDAdminService.SetPrettyDisplayIDStatus:input_type -> hyapp.user.v1.SetPrettyDisplayIDStatusRequest
|
||||
179, // 262: hyapp.user.v1.UserPrettyDisplayIDAdminService.RecyclePrettyDisplayID:input_type -> hyapp.user.v1.RecyclePrettyDisplayIDRequest
|
||||
181, // 263: hyapp.user.v1.UserPrettyDisplayIDAdminService.AdminGrantPrettyDisplayID:input_type -> hyapp.user.v1.AdminGrantPrettyDisplayIDRequest
|
||||
18, // 264: hyapp.user.v1.UserService.GetUser:output_type -> hyapp.user.v1.GetUserResponse
|
||||
10, // 265: hyapp.user.v1.UserService.GetInviteAttribution:output_type -> hyapp.user.v1.GetInviteAttributionResponse
|
||||
21, // 266: hyapp.user.v1.UserService.BusinessUserLookup:output_type -> hyapp.user.v1.BusinessUserLookupResponse
|
||||
24, // 267: hyapp.user.v1.UserService.GetMyProfileStats:output_type -> hyapp.user.v1.GetMyProfileStatsResponse
|
||||
82, // 268: hyapp.user.v1.UserService.BatchGetUsers:output_type -> hyapp.user.v1.BatchGetUsersResponse
|
||||
86, // 269: hyapp.user.v1.UserService.BatchGetUserAdminProfiles:output_type -> hyapp.user.v1.BatchGetUserAdminProfilesResponse
|
||||
88, // 270: hyapp.user.v1.UserService.AdminIssueUserAccessToken:output_type -> hyapp.user.v1.AdminIssueUserAccessTokenResponse
|
||||
91, // 271: hyapp.user.v1.UserService.BatchGetRoomBasicUsers:output_type -> hyapp.user.v1.BatchGetRoomBasicUsersResponse
|
||||
93, // 272: hyapp.user.v1.UserService.ListUserIDs:output_type -> hyapp.user.v1.ListUserIDsResponse
|
||||
13, // 273: hyapp.user.v1.UserService.GetUserMicLifetimeStats:output_type -> hyapp.user.v1.GetUserMicLifetimeStatsResponse
|
||||
98, // 274: hyapp.user.v1.UserService.AuthorizeUserAvatarUpload:output_type -> hyapp.user.v1.AuthorizeUserAvatarUploadResponse
|
||||
100, // 275: hyapp.user.v1.UserService.CompleteUserAvatarUpload:output_type -> hyapp.user.v1.CompleteUserAvatarUploadResponse
|
||||
95, // 276: hyapp.user.v1.UserService.UpdateUserProfile:output_type -> hyapp.user.v1.UpdateUserProfileResponse
|
||||
102, // 277: hyapp.user.v1.UserService.UpdateUserProfileBackground:output_type -> hyapp.user.v1.UpdateUserProfileBackgroundResponse
|
||||
104, // 278: hyapp.user.v1.UserService.UpdateUserContactInfo:output_type -> hyapp.user.v1.UpdateUserContactInfoResponse
|
||||
106, // 279: hyapp.user.v1.UserService.UpdateUserWithdrawAddress:output_type -> hyapp.user.v1.UpdateUserWithdrawAddressResponse
|
||||
109, // 280: hyapp.user.v1.UserService.ChangeUserCountry:output_type -> hyapp.user.v1.ChangeUserCountryResponse
|
||||
109, // 281: hyapp.user.v1.UserService.AdminChangeUserCountry:output_type -> hyapp.user.v1.ChangeUserCountryResponse
|
||||
111, // 282: hyapp.user.v1.UserService.SetUserStatus:output_type -> hyapp.user.v1.SetUserStatusResponse
|
||||
114, // 283: hyapp.user.v1.UserService.AdminBanUser:output_type -> hyapp.user.v1.AdminBanUserResponse
|
||||
116, // 284: hyapp.user.v1.UserService.AdminUnbanUser:output_type -> hyapp.user.v1.AdminUnbanUserResponse
|
||||
119, // 285: hyapp.user.v1.UserService.CreateManagerUserBlock:output_type -> hyapp.user.v1.CreateManagerUserBlockResponse
|
||||
121, // 286: hyapp.user.v1.UserService.ListManagerUserBlocks:output_type -> hyapp.user.v1.ListManagerUserBlocksResponse
|
||||
123, // 287: hyapp.user.v1.UserService.UnblockManagerUser:output_type -> hyapp.user.v1.UnblockManagerUserResponse
|
||||
125, // 288: hyapp.user.v1.UserService.CompleteOnboarding:output_type -> hyapp.user.v1.CompleteOnboardingResponse
|
||||
127, // 289: hyapp.user.v1.UserService.SearchInviteReferrer:output_type -> hyapp.user.v1.SearchInviteReferrerResponse
|
||||
129, // 290: hyapp.user.v1.UserService.BindInviteReferrer:output_type -> hyapp.user.v1.BindInviteReferrerResponse
|
||||
26, // 291: hyapp.user.v1.UserSocialService.RecordProfileVisit:output_type -> hyapp.user.v1.RecordProfileVisitResponse
|
||||
29, // 292: hyapp.user.v1.UserSocialService.ListProfileVisitors:output_type -> hyapp.user.v1.ListProfileVisitorsResponse
|
||||
31, // 293: hyapp.user.v1.UserSocialService.FollowUser:output_type -> hyapp.user.v1.FollowUserResponse
|
||||
33, // 294: hyapp.user.v1.UserSocialService.UnfollowUser:output_type -> hyapp.user.v1.UnfollowUserResponse
|
||||
36, // 295: hyapp.user.v1.UserSocialService.ListFollowing:output_type -> hyapp.user.v1.ListFollowingResponse
|
||||
38, // 296: hyapp.user.v1.UserSocialService.ApplyFriend:output_type -> hyapp.user.v1.ApplyFriendResponse
|
||||
40, // 297: hyapp.user.v1.UserSocialService.AcceptFriendApplication:output_type -> hyapp.user.v1.AcceptFriendApplicationResponse
|
||||
42, // 298: hyapp.user.v1.UserSocialService.DeleteFriend:output_type -> hyapp.user.v1.DeleteFriendResponse
|
||||
45, // 299: hyapp.user.v1.UserSocialService.ListFriends:output_type -> hyapp.user.v1.ListFriendsResponse
|
||||
48, // 300: hyapp.user.v1.UserSocialService.ListFriendApplications:output_type -> hyapp.user.v1.ListFriendApplicationsResponse
|
||||
80, // 301: hyapp.user.v1.UserSocialService.SubmitReport:output_type -> hyapp.user.v1.SubmitReportResponse
|
||||
60, // 302: hyapp.user.v1.UserCPService.ListCPApplications:output_type -> hyapp.user.v1.ListCPApplicationsResponse
|
||||
62, // 303: hyapp.user.v1.UserCPService.AcceptCPApplication:output_type -> hyapp.user.v1.AcceptCPApplicationResponse
|
||||
64, // 304: hyapp.user.v1.UserCPService.RejectCPApplication:output_type -> hyapp.user.v1.RejectCPApplicationResponse
|
||||
66, // 305: hyapp.user.v1.UserCPService.ListCPRelationships:output_type -> hyapp.user.v1.ListCPRelationshipsResponse
|
||||
68, // 306: hyapp.user.v1.UserCPService.ListCPIntimacyLeaderboard:output_type -> hyapp.user.v1.ListCPIntimacyLeaderboardResponse
|
||||
70, // 307: hyapp.user.v1.UserCPService.PrepareBreakCPRelationship:output_type -> hyapp.user.v1.PrepareBreakCPRelationshipResponse
|
||||
72, // 308: hyapp.user.v1.UserCPService.ConfirmBreakCPRelationship:output_type -> hyapp.user.v1.ConfirmBreakCPRelationshipResponse
|
||||
74, // 309: hyapp.user.v1.UserCPService.CancelBreakCPRelationship:output_type -> hyapp.user.v1.CancelBreakCPRelationshipResponse
|
||||
185, // 310: hyapp.user.v1.UserCPService.ListCPFormationGiftFeed:output_type -> hyapp.user.v1.ListCPFormationGiftFeedResponse
|
||||
77, // 311: hyapp.user.v1.UserCPInternalService.ConsumeRoomGiftCPEvent:output_type -> hyapp.user.v1.ConsumeRoomGiftCPEventResponse
|
||||
58, // 312: hyapp.user.v1.UserCPInternalService.ListCPWeeklyRankEntries:output_type -> hyapp.user.v1.ListCPWeeklyRankEntriesResponse
|
||||
15, // 313: hyapp.user.v1.UserCronService.ProcessLoginIPRiskBatch:output_type -> hyapp.user.v1.CronBatchResponse
|
||||
15, // 314: hyapp.user.v1.UserCronService.ProcessRegionRebuildBatch:output_type -> hyapp.user.v1.CronBatchResponse
|
||||
15, // 315: hyapp.user.v1.UserCronService.CompensateMicOpenSessions:output_type -> hyapp.user.v1.CronBatchResponse
|
||||
15, // 316: hyapp.user.v1.UserCronService.CompensateRoomOpenSessions:output_type -> hyapp.user.v1.CronBatchResponse
|
||||
15, // 317: hyapp.user.v1.UserCronService.ExpireManagerUserBlocks:output_type -> hyapp.user.v1.CronBatchResponse
|
||||
15, // 318: hyapp.user.v1.UserCronService.ExpireAdminUserBans:output_type -> hyapp.user.v1.CronBatchResponse
|
||||
15, // 319: hyapp.user.v1.UserCronService.RefreshCPIntimacyLeaderboard:output_type -> hyapp.user.v1.CronBatchResponse
|
||||
15, // 320: hyapp.user.v1.UserCronService.ProcessPrivateMessageHistoryBatch:output_type -> hyapp.user.v1.CronBatchResponse
|
||||
131, // 321: hyapp.user.v1.UserDeviceService.BindPushToken:output_type -> hyapp.user.v1.BindPushTokenResponse
|
||||
133, // 322: hyapp.user.v1.UserDeviceService.DeletePushToken:output_type -> hyapp.user.v1.DeletePushTokenResponse
|
||||
4, // 323: hyapp.user.v1.AppRegistryService.ResolveApp:output_type -> hyapp.user.v1.ResolveAppResponse
|
||||
187, // 324: hyapp.user.v1.AppRegistryService.ListApps:output_type -> hyapp.user.v1.ListAppsResponse
|
||||
137, // 325: hyapp.user.v1.CountryAdminService.ListCountries:output_type -> hyapp.user.v1.ListCountriesResponse
|
||||
139, // 326: hyapp.user.v1.CountryAdminService.UpdateCountry:output_type -> hyapp.user.v1.CountryResponse
|
||||
141, // 327: hyapp.user.v1.CountryQueryService.ListRegistrationCountries:output_type -> hyapp.user.v1.ListRegistrationCountriesResponse
|
||||
144, // 328: hyapp.user.v1.CountryQueryService.ListLoginRiskBlockedCountries:output_type -> hyapp.user.v1.ListLoginRiskBlockedCountriesResponse
|
||||
146, // 329: hyapp.user.v1.RegionAdminService.ListRegions:output_type -> hyapp.user.v1.ListRegionsResponse
|
||||
150, // 330: hyapp.user.v1.RegionAdminService.GetRegion:output_type -> hyapp.user.v1.RegionResponse
|
||||
150, // 331: hyapp.user.v1.RegionAdminService.UpdateRegion:output_type -> hyapp.user.v1.RegionResponse
|
||||
150, // 332: hyapp.user.v1.RegionAdminService.ReplaceRegionCountries:output_type -> hyapp.user.v1.RegionResponse
|
||||
153, // 333: hyapp.user.v1.UserIdentityService.GetUserIdentity:output_type -> hyapp.user.v1.GetUserIdentityResponse
|
||||
155, // 334: hyapp.user.v1.UserIdentityService.ResolveDisplayUserID:output_type -> hyapp.user.v1.ResolveDisplayUserIDResponse
|
||||
189, // 335: hyapp.user.v1.UserIdentityService.ResolveAdminUserIdentifier:output_type -> hyapp.user.v1.ResolveAdminUserIdentifierResponse
|
||||
157, // 336: hyapp.user.v1.UserIdentityService.ChangeDisplayUserID:output_type -> hyapp.user.v1.ChangeDisplayUserIDResponse
|
||||
159, // 337: hyapp.user.v1.UserIdentityService.ApplyPrettyDisplayUserID:output_type -> hyapp.user.v1.ApplyPrettyDisplayUserIDResponse
|
||||
166, // 338: hyapp.user.v1.UserIdentityService.ListAvailablePrettyDisplayIDs:output_type -> hyapp.user.v1.ListAvailablePrettyDisplayIDsResponse
|
||||
168, // 339: hyapp.user.v1.UserIdentityService.ApplyPrettyDisplayIDFromPool:output_type -> hyapp.user.v1.ApplyPrettyDisplayIDFromPoolResponse
|
||||
161, // 340: hyapp.user.v1.UserIdentityService.ExpirePrettyDisplayUserID:output_type -> hyapp.user.v1.ExpirePrettyDisplayUserIDResponse
|
||||
170, // 341: hyapp.user.v1.UserPrettyDisplayIDAdminService.ListPrettyDisplayIDPools:output_type -> hyapp.user.v1.ListPrettyDisplayIDPoolsResponse
|
||||
173, // 342: hyapp.user.v1.UserPrettyDisplayIDAdminService.CreatePrettyDisplayIDPool:output_type -> hyapp.user.v1.PrettyDisplayIDPoolResponse
|
||||
173, // 343: hyapp.user.v1.UserPrettyDisplayIDAdminService.UpdatePrettyDisplayIDPool:output_type -> hyapp.user.v1.PrettyDisplayIDPoolResponse
|
||||
175, // 344: hyapp.user.v1.UserPrettyDisplayIDAdminService.GeneratePrettyDisplayIDs:output_type -> hyapp.user.v1.GeneratePrettyDisplayIDsResponse
|
||||
177, // 345: hyapp.user.v1.UserPrettyDisplayIDAdminService.ListPrettyDisplayIDs:output_type -> hyapp.user.v1.ListPrettyDisplayIDsResponse
|
||||
180, // 346: hyapp.user.v1.UserPrettyDisplayIDAdminService.SetPrettyDisplayIDStatus:output_type -> hyapp.user.v1.PrettyDisplayIDResponse
|
||||
180, // 347: hyapp.user.v1.UserPrettyDisplayIDAdminService.RecyclePrettyDisplayID:output_type -> hyapp.user.v1.PrettyDisplayIDResponse
|
||||
182, // 348: hyapp.user.v1.UserPrettyDisplayIDAdminService.AdminGrantPrettyDisplayID:output_type -> hyapp.user.v1.AdminGrantPrettyDisplayIDResponse
|
||||
264, // [264:349] is the sub-list for method output_type
|
||||
179, // [179:264] is the sub-list for method input_type
|
||||
179, // [179:179] is the sub-list for extension type_name
|
||||
179, // [179:179] is the sub-list for extension extendee
|
||||
0, // [0:179] is the sub-list for field type_name
|
||||
|
||||
@ -1613,6 +1613,7 @@ service UserCronService {
|
||||
rpc ExpireManagerUserBlocks(CronBatchRequest) returns (CronBatchResponse);
|
||||
rpc ExpireAdminUserBans(CronBatchRequest) returns (CronBatchResponse);
|
||||
rpc RefreshCPIntimacyLeaderboard(CronBatchRequest) returns (CronBatchResponse);
|
||||
rpc ProcessPrivateMessageHistoryBatch(CronBatchRequest) returns (CronBatchResponse);
|
||||
}
|
||||
|
||||
// UserDeviceService 承载 App 设备推送 token 的绑定和失效。
|
||||
|
||||
@ -2153,13 +2153,14 @@ var UserCPInternalService_ServiceDesc = grpc.ServiceDesc{
|
||||
}
|
||||
|
||||
const (
|
||||
UserCronService_ProcessLoginIPRiskBatch_FullMethodName = "/hyapp.user.v1.UserCronService/ProcessLoginIPRiskBatch"
|
||||
UserCronService_ProcessRegionRebuildBatch_FullMethodName = "/hyapp.user.v1.UserCronService/ProcessRegionRebuildBatch"
|
||||
UserCronService_CompensateMicOpenSessions_FullMethodName = "/hyapp.user.v1.UserCronService/CompensateMicOpenSessions"
|
||||
UserCronService_CompensateRoomOpenSessions_FullMethodName = "/hyapp.user.v1.UserCronService/CompensateRoomOpenSessions"
|
||||
UserCronService_ExpireManagerUserBlocks_FullMethodName = "/hyapp.user.v1.UserCronService/ExpireManagerUserBlocks"
|
||||
UserCronService_ExpireAdminUserBans_FullMethodName = "/hyapp.user.v1.UserCronService/ExpireAdminUserBans"
|
||||
UserCronService_RefreshCPIntimacyLeaderboard_FullMethodName = "/hyapp.user.v1.UserCronService/RefreshCPIntimacyLeaderboard"
|
||||
UserCronService_ProcessLoginIPRiskBatch_FullMethodName = "/hyapp.user.v1.UserCronService/ProcessLoginIPRiskBatch"
|
||||
UserCronService_ProcessRegionRebuildBatch_FullMethodName = "/hyapp.user.v1.UserCronService/ProcessRegionRebuildBatch"
|
||||
UserCronService_CompensateMicOpenSessions_FullMethodName = "/hyapp.user.v1.UserCronService/CompensateMicOpenSessions"
|
||||
UserCronService_CompensateRoomOpenSessions_FullMethodName = "/hyapp.user.v1.UserCronService/CompensateRoomOpenSessions"
|
||||
UserCronService_ExpireManagerUserBlocks_FullMethodName = "/hyapp.user.v1.UserCronService/ExpireManagerUserBlocks"
|
||||
UserCronService_ExpireAdminUserBans_FullMethodName = "/hyapp.user.v1.UserCronService/ExpireAdminUserBans"
|
||||
UserCronService_RefreshCPIntimacyLeaderboard_FullMethodName = "/hyapp.user.v1.UserCronService/RefreshCPIntimacyLeaderboard"
|
||||
UserCronService_ProcessPrivateMessageHistoryBatch_FullMethodName = "/hyapp.user.v1.UserCronService/ProcessPrivateMessageHistoryBatch"
|
||||
)
|
||||
|
||||
// UserCronServiceClient is the client API for UserCronService service.
|
||||
@ -2175,6 +2176,7 @@ type UserCronServiceClient interface {
|
||||
ExpireManagerUserBlocks(ctx context.Context, in *CronBatchRequest, opts ...grpc.CallOption) (*CronBatchResponse, error)
|
||||
ExpireAdminUserBans(ctx context.Context, in *CronBatchRequest, opts ...grpc.CallOption) (*CronBatchResponse, error)
|
||||
RefreshCPIntimacyLeaderboard(ctx context.Context, in *CronBatchRequest, opts ...grpc.CallOption) (*CronBatchResponse, error)
|
||||
ProcessPrivateMessageHistoryBatch(ctx context.Context, in *CronBatchRequest, opts ...grpc.CallOption) (*CronBatchResponse, error)
|
||||
}
|
||||
|
||||
type userCronServiceClient struct {
|
||||
@ -2255,6 +2257,16 @@ func (c *userCronServiceClient) RefreshCPIntimacyLeaderboard(ctx context.Context
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *userCronServiceClient) ProcessPrivateMessageHistoryBatch(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, UserCronService_ProcessPrivateMessageHistoryBatch_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// UserCronServiceServer is the server API for UserCronService service.
|
||||
// All implementations must embed UnimplementedUserCronServiceServer
|
||||
// for forward compatibility.
|
||||
@ -2268,6 +2280,7 @@ type UserCronServiceServer interface {
|
||||
ExpireManagerUserBlocks(context.Context, *CronBatchRequest) (*CronBatchResponse, error)
|
||||
ExpireAdminUserBans(context.Context, *CronBatchRequest) (*CronBatchResponse, error)
|
||||
RefreshCPIntimacyLeaderboard(context.Context, *CronBatchRequest) (*CronBatchResponse, error)
|
||||
ProcessPrivateMessageHistoryBatch(context.Context, *CronBatchRequest) (*CronBatchResponse, error)
|
||||
mustEmbedUnimplementedUserCronServiceServer()
|
||||
}
|
||||
|
||||
@ -2299,6 +2312,9 @@ func (UnimplementedUserCronServiceServer) ExpireAdminUserBans(context.Context, *
|
||||
func (UnimplementedUserCronServiceServer) RefreshCPIntimacyLeaderboard(context.Context, *CronBatchRequest) (*CronBatchResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method RefreshCPIntimacyLeaderboard not implemented")
|
||||
}
|
||||
func (UnimplementedUserCronServiceServer) ProcessPrivateMessageHistoryBatch(context.Context, *CronBatchRequest) (*CronBatchResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method ProcessPrivateMessageHistoryBatch not implemented")
|
||||
}
|
||||
func (UnimplementedUserCronServiceServer) mustEmbedUnimplementedUserCronServiceServer() {}
|
||||
func (UnimplementedUserCronServiceServer) testEmbeddedByValue() {}
|
||||
|
||||
@ -2446,6 +2462,24 @@ func _UserCronService_RefreshCPIntimacyLeaderboard_Handler(srv interface{}, ctx
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _UserCronService_ProcessPrivateMessageHistoryBatch_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.(UserCronServiceServer).ProcessPrivateMessageHistoryBatch(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: UserCronService_ProcessPrivateMessageHistoryBatch_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(UserCronServiceServer).ProcessPrivateMessageHistoryBatch(ctx, req.(*CronBatchRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// UserCronService_ServiceDesc is the grpc.ServiceDesc for UserCronService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
@ -2481,6 +2515,10 @@ var UserCronService_ServiceDesc = grpc.ServiceDesc{
|
||||
MethodName: "RefreshCPIntimacyLeaderboard",
|
||||
Handler: _UserCronService_RefreshCPIntimacyLeaderboard_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ProcessPrivateMessageHistoryBatch",
|
||||
Handler: _UserCronService_ProcessPrivateMessageHistoryBatch_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "proto/user/v1/user.proto",
|
||||
|
||||
176
pkg/tencentim/history.go
Normal file
176
pkg/tencentim/history.go
Normal file
@ -0,0 +1,176 @@
|
||||
package tencentim
|
||||
|
||||
import (
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// historyFileCommand 获取一个自然小时的 C2C 消息归档下载地址;它是离线统计接口,
|
||||
// 不进入客户端发消息链路,也不会把业务服务延迟传播到腾讯 IM。
|
||||
historyFileCommand = "v4/open_msg_svc/get_history"
|
||||
// 小时归档通常是 MB 级文件,不能复用实时 REST 调用的 5 秒传输预算;
|
||||
// cron RPC 仍有 2 分钟总 deadline,这里只放宽单文件下载而不影响发消息接口。
|
||||
historyDownloadTimeout = 60 * time.Second
|
||||
)
|
||||
|
||||
var tencentHistoryLocation = time.FixedZone("UTC+8", 8*60*60)
|
||||
|
||||
// C2CHistoryFile 是腾讯 IM 返回的短期下载凭据。URL 含临时授权信息,只能在内存中使用,
|
||||
// 调用方不得把它写入日志、数据库或业务错误。
|
||||
type C2CHistoryFile struct {
|
||||
URL string `json:"URL"`
|
||||
FileSize int64 `json:"FileSize"`
|
||||
GzipSize int64 `json:"GzipSize"`
|
||||
}
|
||||
|
||||
// C2CHistoryMessage 只解析私聊人数统计所需字段。MsgBody 等正文由流式 decoder 跳过,
|
||||
// 避免消息内容进入 user-service 内存对象、日志或统计库。
|
||||
type C2CHistoryMessage struct {
|
||||
FromAccount string `json:"From_Account"`
|
||||
ToAccount string `json:"To_Account"`
|
||||
Timestamp int64 `json:"MsgTimestamp"`
|
||||
Sequence uint64 `json:"MsgSeq"`
|
||||
Random uint64 `json:"MsgRandom"`
|
||||
}
|
||||
|
||||
type historyFilesRequest struct {
|
||||
ChatType string `json:"ChatType"`
|
||||
MsgTime string `json:"MsgTime"`
|
||||
}
|
||||
|
||||
type historyFilesResponse struct {
|
||||
restResponse
|
||||
Files []C2CHistoryFile `json:"File"`
|
||||
}
|
||||
|
||||
// ListC2CHistoryFiles 获取指定 UTC 小时对应的 C2C 归档。
|
||||
// 腾讯接口的 MsgTime 使用 UTC+8 小时字符串,而消息本身仍携带 Unix 时间戳;在 REST
|
||||
// 边界集中转换可防止 cron 把 UTC 小时错取成前一天的数据。
|
||||
func (c *RESTClient) ListC2CHistoryFiles(ctx context.Context, hourStartUTC time.Time) ([]C2CHistoryFile, error) {
|
||||
hourStartUTC = hourStartUTC.UTC().Truncate(time.Hour)
|
||||
request := historyFilesRequest{
|
||||
ChatType: "C2C",
|
||||
MsgTime: hourStartUTC.In(tencentHistoryLocation).Format("2006010215"),
|
||||
}
|
||||
var response historyFilesResponse
|
||||
if err := c.post(ctx, historyFileCommand, request, &response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := response.err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
files := make([]C2CHistoryFile, 0, len(response.Files))
|
||||
for _, file := range response.Files {
|
||||
file.URL = strings.TrimSpace(file.URL)
|
||||
if file.URL == "" {
|
||||
continue
|
||||
}
|
||||
files = append(files, file)
|
||||
}
|
||||
return files, nil
|
||||
}
|
||||
|
||||
// StreamC2CHistory 下载并逐条解码一个 gzip 归档。decoder 只保留当前消息的统计字段,
|
||||
// 即使单小时归档很大也不会把整个 MsgList 或消息正文一次性加载进内存。
|
||||
func (c *RESTClient) StreamC2CHistory(ctx context.Context, file C2CHistoryFile, consume func(C2CHistoryMessage) error) (int, error) {
|
||||
if c == nil || c.httpClient == nil {
|
||||
return 0, fmt.Errorf("tencent im rest client is not configured")
|
||||
}
|
||||
if consume == nil {
|
||||
return 0, fmt.Errorf("tencent im history consumer is required")
|
||||
}
|
||||
downloadURL, err := url.Parse(strings.TrimSpace(file.URL))
|
||||
if err != nil || downloadURL.Scheme != "https" || downloadURL.Host == "" {
|
||||
return 0, fmt.Errorf("tencent im history download url is invalid")
|
||||
}
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodGet, downloadURL.String(), nil)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("build tencent im history request: %w", err)
|
||||
}
|
||||
downloadClient := *c.httpClient
|
||||
if downloadClient.Timeout <= 0 || downloadClient.Timeout < historyDownloadTimeout {
|
||||
downloadClient.Timeout = historyDownloadTimeout
|
||||
}
|
||||
response, err := downloadClient.Do(request)
|
||||
if err != nil {
|
||||
var transportErr *url.Error
|
||||
if errors.As(err, &transportErr) {
|
||||
// url.Error 会把带临时下载签名的完整 URL 拼进错误;只保留底层网络原因。
|
||||
return 0, fmt.Errorf("tencent im history download failed: %w", transportErr.Err)
|
||||
}
|
||||
return 0, fmt.Errorf("tencent im history download failed: %w", err)
|
||||
}
|
||||
defer response.Body.Close()
|
||||
if response.StatusCode < http.StatusOK || response.StatusCode >= http.StatusMultipleChoices {
|
||||
return 0, fmt.Errorf("tencent im history download status %d", response.StatusCode)
|
||||
}
|
||||
|
||||
archive, err := gzip.NewReader(response.Body)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("open tencent im history gzip: %w", err)
|
||||
}
|
||||
defer archive.Close()
|
||||
|
||||
decoder := json.NewDecoder(archive)
|
||||
first, err := decoder.Token()
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("decode tencent im history document: %w", err)
|
||||
}
|
||||
if delimiter, ok := first.(json.Delim); !ok || delimiter != '{' {
|
||||
return 0, fmt.Errorf("tencent im history document is not an object")
|
||||
}
|
||||
|
||||
processed := 0
|
||||
for decoder.More() {
|
||||
keyToken, err := decoder.Token()
|
||||
if err != nil {
|
||||
return processed, fmt.Errorf("decode tencent im history field: %w", err)
|
||||
}
|
||||
key, ok := keyToken.(string)
|
||||
if !ok {
|
||||
return processed, fmt.Errorf("tencent im history field name is invalid")
|
||||
}
|
||||
if key != "MsgList" {
|
||||
// 元数据字段与统计无关;RawMessage 只短暂承载单个标量/小对象,不读取消息正文。
|
||||
var ignored json.RawMessage
|
||||
if err := decoder.Decode(&ignored); err != nil {
|
||||
return processed, fmt.Errorf("skip tencent im history metadata: %w", err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
listStart, err := decoder.Token()
|
||||
if err != nil {
|
||||
return processed, fmt.Errorf("decode tencent im history list: %w", err)
|
||||
}
|
||||
if delimiter, ok := listStart.(json.Delim); !ok || delimiter != '[' {
|
||||
return processed, fmt.Errorf("tencent im history MsgList is not an array")
|
||||
}
|
||||
for decoder.More() {
|
||||
var message C2CHistoryMessage
|
||||
if err := decoder.Decode(&message); err != nil {
|
||||
return processed, fmt.Errorf("decode tencent im history message: %w", err)
|
||||
}
|
||||
if err := consume(message); err != nil {
|
||||
return processed, err
|
||||
}
|
||||
processed++
|
||||
}
|
||||
if _, err := decoder.Token(); err != nil {
|
||||
return processed, fmt.Errorf("close tencent im history list: %w", err)
|
||||
}
|
||||
}
|
||||
if _, err := decoder.Token(); err != nil {
|
||||
return processed, fmt.Errorf("close tencent im history document: %w", err)
|
||||
}
|
||||
return processed, nil
|
||||
}
|
||||
@ -81,6 +81,12 @@ tasks:
|
||||
timeout: "20s"
|
||||
lock_ttl: "5m"
|
||||
batch_size: 10000
|
||||
private_message_history:
|
||||
enabled: true
|
||||
interval: "1m"
|
||||
timeout: "2m"
|
||||
lock_ttl: "3m"
|
||||
batch_size: 500
|
||||
game_level_event_relay:
|
||||
enabled: true
|
||||
interval: "5s"
|
||||
|
||||
@ -81,6 +81,12 @@ tasks:
|
||||
timeout: "20s"
|
||||
lock_ttl: "5m"
|
||||
batch_size: 10000
|
||||
private_message_history:
|
||||
enabled: true
|
||||
interval: "1m"
|
||||
timeout: "2m"
|
||||
lock_ttl: "3m"
|
||||
batch_size: 500
|
||||
game_level_event_relay:
|
||||
enabled: true
|
||||
interval: "5s"
|
||||
|
||||
@ -81,6 +81,12 @@ tasks:
|
||||
timeout: "20s"
|
||||
lock_ttl: "5m"
|
||||
batch_size: 10000
|
||||
private_message_history:
|
||||
enabled: true
|
||||
interval: "1m"
|
||||
timeout: "2m"
|
||||
lock_ttl: "3m"
|
||||
batch_size: 500
|
||||
game_level_event_relay:
|
||||
enabled: true
|
||||
interval: "5s"
|
||||
|
||||
@ -123,6 +123,7 @@ func New(cfg config.Config) (*App, error) {
|
||||
"admin_user_ban_expiry": userCron.ExpireAdminUserBans,
|
||||
// CP 榜单任务只占 cron 租约并调用 user-service;真实 MySQL 读取、wallet 头像框补齐和 Redis 替换都在 user-service 内完成。
|
||||
"cp_intimacy_leaderboard": userCron.RefreshCPIntimacyLeaderboard,
|
||||
"private_message_history": userCron.ProcessPrivateMessageHistoryBatch,
|
||||
"message_fanout": activityCron.ProcessMessageFanoutBatch,
|
||||
"growth_level_reward": activityCron.ProcessLevelRewardBatch,
|
||||
"temporary_growth_level": activityCron.ProcessTemporaryLevelBatch,
|
||||
|
||||
@ -214,6 +214,15 @@ func defaultTasks() map[string]TaskConfig {
|
||||
LockTTL: "5m",
|
||||
BatchSize: 10000,
|
||||
},
|
||||
"private_message_history": {
|
||||
// 腾讯消息文件在小时结束后异步生成;每分钟最多认领一个小时并在 user-service
|
||||
// 内按 500 条写入,既能回补最近七天,又不会形成下载或数据库突发。
|
||||
Enabled: true,
|
||||
Interval: "1m",
|
||||
Timeout: "2m",
|
||||
LockTTL: "3m",
|
||||
BatchSize: 500,
|
||||
},
|
||||
"game_level_event_relay": {
|
||||
Enabled: true,
|
||||
Interval: "5s",
|
||||
|
||||
@ -63,6 +63,13 @@ func (c *UserCronClient) RefreshCPIntimacyLeaderboard(ctx context.Context, req s
|
||||
return userCronResult(resp), err
|
||||
}
|
||||
|
||||
// ProcessPrivateMessageHistoryBatch 只触发 user-service owner 拉取离线小时归档;
|
||||
// cron 不接触腾讯凭证、下载地址、消息内容或 user 数据库。
|
||||
func (c *UserCronClient) ProcessPrivateMessageHistoryBatch(ctx context.Context, req scheduler.BatchRequest) (scheduler.BatchResult, error) {
|
||||
resp, err := c.client.ProcessPrivateMessageHistoryBatch(ctx, userCronRequest(req))
|
||||
return userCronResult(resp), err
|
||||
}
|
||||
|
||||
func userCronRequest(req scheduler.BatchRequest) *userv1.CronBatchRequest {
|
||||
return &userv1.CronBatchRequest{
|
||||
Meta: &userv1.RequestMeta{
|
||||
|
||||
@ -211,6 +211,23 @@ CREATE TABLE IF NOT EXISTS user_private_message_events (
|
||||
KEY idx_private_message_target_time (app_code, target_user_id, occurred_at_ms, sender_user_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户私信发送事实,仅保存统计字段';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS user_private_message_history_sync (
|
||||
sdk_app_id BIGINT NOT NULL COMMENT '腾讯 IM SDKAppID',
|
||||
hour_start_ms BIGINT NOT NULL COMMENT '待同步 UTC 小时起点 epoch ms',
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'pending' COMMENT 'pending/processing/retry/completed',
|
||||
attempts INT NOT NULL DEFAULT 0 COMMENT '外部归档拉取尝试次数',
|
||||
next_retry_at_ms BIGINT NOT NULL DEFAULT 0 COMMENT '失败后最早重试时间',
|
||||
locked_by VARCHAR(96) NOT NULL DEFAULT '' COMMENT '当前 cron run ID',
|
||||
locked_until_ms BIGINT NOT NULL DEFAULT 0 COMMENT '处理租约截止时间',
|
||||
source_message_count INT NOT NULL DEFAULT 0 COMMENT '归档原始消息数',
|
||||
imported_event_count INT NOT NULL DEFAULT 0 COMMENT '新增 Host 私信事实数',
|
||||
last_error VARCHAR(512) NOT NULL DEFAULT '' COMMENT '最近一次脱敏错误',
|
||||
created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms',
|
||||
updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms',
|
||||
PRIMARY KEY (sdk_app_id, hour_start_ms),
|
||||
KEY idx_private_message_history_sync_ready (sdk_app_id, status, next_retry_at_ms, hour_start_ms, locked_until_ms)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='腾讯 IM 小时归档离线同步状态';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS user_friend_applications (
|
||||
app_code VARCHAR(32) NOT NULL DEFAULT 'lalu' COMMENT '应用编码,用于多租户隔离',
|
||||
requester_user_id BIGINT NOT NULL COMMENT '申请人用户 ID',
|
||||
|
||||
@ -0,0 +1,20 @@
|
||||
USE hyapp_user;
|
||||
|
||||
-- 归档同步表每个 SDKAppID 每小时仅一行,cron 只在该小表上 claim;
|
||||
-- 私信事实仍通过 user_id 主键校验租户后写入既有索引表,不扫描用户或消息全表。
|
||||
CREATE TABLE IF NOT EXISTS user_private_message_history_sync (
|
||||
sdk_app_id BIGINT NOT NULL COMMENT '腾讯 IM SDKAppID',
|
||||
hour_start_ms BIGINT NOT NULL COMMENT '待同步 UTC 小时起点 epoch ms',
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'pending' COMMENT 'pending/processing/retry/completed',
|
||||
attempts INT NOT NULL DEFAULT 0 COMMENT '外部归档拉取尝试次数',
|
||||
next_retry_at_ms BIGINT NOT NULL DEFAULT 0 COMMENT '失败后最早重试时间',
|
||||
locked_by VARCHAR(96) NOT NULL DEFAULT '' COMMENT '当前 cron run ID',
|
||||
locked_until_ms BIGINT NOT NULL DEFAULT 0 COMMENT '处理租约截止时间',
|
||||
source_message_count INT NOT NULL DEFAULT 0 COMMENT '归档原始消息数',
|
||||
imported_event_count INT NOT NULL DEFAULT 0 COMMENT '新增 Host 私信事实数',
|
||||
last_error VARCHAR(512) NOT NULL DEFAULT '' COMMENT '最近一次脱敏错误',
|
||||
created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms',
|
||||
updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms',
|
||||
PRIMARY KEY (sdk_app_id, hour_start_ms),
|
||||
KEY idx_private_message_history_sync_ready (sdk_app_id, status, next_retry_at_ms, hour_start_ms, locked_until_ms)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='腾讯 IM 小时归档离线同步状态';
|
||||
@ -192,6 +192,7 @@ func New(cfg config.Config) (*App, error) {
|
||||
}
|
||||
var imLoginKicker userservice.IMLoginKicker
|
||||
var imAccountImporter authservice.IMAccountImporter
|
||||
var imHistoryClient hostservice.PrivateMessageHistoryClient
|
||||
if cfg.TencentIM.Enabled {
|
||||
imClient, err := tencentim.NewRESTClient(cfg.TencentIM.RESTConfig())
|
||||
if err != nil {
|
||||
@ -207,6 +208,7 @@ func New(cfg config.Config) (*App, error) {
|
||||
}
|
||||
imLoginKicker = imClient
|
||||
imAccountImporter = tencentIMAccountImporter{client: imClient}
|
||||
imHistoryClient = imClient
|
||||
}
|
||||
var roomConn *grpc.ClientConn
|
||||
var roomEvictor userservice.RoomEvictor
|
||||
@ -323,6 +325,8 @@ func New(cfg config.Config) (*App, error) {
|
||||
)
|
||||
hostSvc := hostservice.New(hostRepo,
|
||||
hostservice.WithIDGenerator(idgen.NewInt64Generator(cfg.IDGenerator.NodeID)),
|
||||
// 私聊人数来自腾讯每小时离线归档;该依赖只暴露给 cron RPC,不进入 IM 发消息链路。
|
||||
hostservice.WithPrivateMessageHistorySource(cfg.TencentIM.SDKAppID, imHistoryClient),
|
||||
)
|
||||
// user service 负责用户主状态和 display_user_id/靓号用例,不进入房间高频流程。
|
||||
userSvc := userservice.New(userRepo,
|
||||
|
||||
@ -0,0 +1,233 @@
|
||||
package host
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"hyapp/pkg/tencentim"
|
||||
"hyapp/pkg/xerr"
|
||||
hostdomain "hyapp/services/user-service/internal/domain/host"
|
||||
)
|
||||
|
||||
const (
|
||||
privateMessageHistoryHours = 7 * 24
|
||||
privateMessageHistoryReadyDelay = 2 * time.Hour
|
||||
privateMessageHistoryRetryDelay = 10 * time.Minute
|
||||
privateMessageHistoryImportMax = 500
|
||||
privateMessageHistoryEmptyMaxWait = 4 * time.Hour
|
||||
)
|
||||
|
||||
// PrivateMessageHistoryClient 是腾讯 IM 离线归档边界;它只在 cron 批处理中调用,
|
||||
// 不参与客户端发送、接收或 UserSig 签发链路。
|
||||
type PrivateMessageHistoryClient interface {
|
||||
ListC2CHistoryFiles(ctx context.Context, hourStartUTC time.Time) ([]tencentim.C2CHistoryFile, error)
|
||||
StreamC2CHistory(ctx context.Context, file tencentim.C2CHistoryFile, consume func(tencentim.C2CHistoryMessage) error) (int, error)
|
||||
}
|
||||
|
||||
// PrivateMessageHistoryRepository 保存小时同步租约和最小私聊统计事实。
|
||||
type PrivateMessageHistoryRepository interface {
|
||||
PreparePrivateMessageHistoryHours(ctx context.Context, sdkAppID int64, fromHourMS int64, throughHourMS int64, nowMS int64) error
|
||||
ClaimPrivateMessageHistoryHour(ctx context.Context, sdkAppID int64, runID string, fromHourMS int64, throughHourMS int64, nowMS int64, lockTTL time.Duration) (hourStartMS int64, attempts int, claimed bool, err error)
|
||||
ImportPrivateMessageEvents(ctx context.Context, events []hostdomain.PrivateMessageEvent) (int, error)
|
||||
CompletePrivateMessageHistoryHour(ctx context.Context, sdkAppID int64, hourStartMS int64, runID string, sourceMessages int, importedEvents int, nowMS int64) error
|
||||
RetryPrivateMessageHistoryHour(ctx context.Context, sdkAppID int64, hourStartMS int64, runID string, retryAtMS int64, errorMessage string, nowMS int64) error
|
||||
}
|
||||
|
||||
// PrivateMessageHistoryBatchResult 汇总一次小时归档处理,不把下载地址或消息正文带到 cron 日志。
|
||||
type PrivateMessageHistoryBatchResult struct {
|
||||
Claimed bool
|
||||
SourceCount int
|
||||
ImportedCount int
|
||||
RetryPending bool
|
||||
}
|
||||
|
||||
// WithPrivateMessageHistorySource 挂载腾讯归档和 owner repository。SDKAppID 是共享归档主键,
|
||||
// 即使 cron 按多个 app_code 并发触发,同一小时也只会被认领一次。
|
||||
func WithPrivateMessageHistorySource(sdkAppID int64, client PrivateMessageHistoryClient) Option {
|
||||
return func(s *Service) {
|
||||
if sdkAppID <= 0 || client == nil {
|
||||
return
|
||||
}
|
||||
repository, ok := s.repository.(PrivateMessageHistoryRepository)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
s.privateMessageHistorySDKAppID = sdkAppID
|
||||
s.privateMessageHistoryClient = client
|
||||
s.privateMessageHistoryRepository = repository
|
||||
}
|
||||
}
|
||||
|
||||
// ProcessPrivateMessageHistoryBatch 处理最近七天中一个已完成的 UTC 小时。
|
||||
// get_history 文件由腾讯异步生成,因此统计最多延迟约两小时,但不会给 IM 发消息增加等待。
|
||||
func (s *Service) ProcessPrivateMessageHistoryBatch(ctx context.Context, runID string, lockTTL time.Duration, importBatchSize int) (PrivateMessageHistoryBatchResult, error) {
|
||||
if s == nil || s.privateMessageHistoryClient == nil || s.privateMessageHistoryRepository == nil || s.privateMessageHistorySDKAppID <= 0 {
|
||||
return PrivateMessageHistoryBatchResult{}, xerr.New(xerr.Unavailable, "private message history sync is not configured")
|
||||
}
|
||||
runID = strings.TrimSpace(runID)
|
||||
if runID == "" {
|
||||
return PrivateMessageHistoryBatchResult{}, xerr.New(xerr.InvalidArgument, "run_id is required")
|
||||
}
|
||||
if importBatchSize <= 0 || importBatchSize > privateMessageHistoryImportMax {
|
||||
importBatchSize = privateMessageHistoryImportMax
|
||||
}
|
||||
if lockTTL <= 0 {
|
||||
lockTTL = 5 * time.Minute
|
||||
}
|
||||
|
||||
now := s.now().UTC()
|
||||
throughHour := now.Truncate(time.Hour).Add(-privateMessageHistoryReadyDelay)
|
||||
fromHour := throughHour.Add(-time.Duration(privateMessageHistoryHours-1) * time.Hour)
|
||||
if err := s.privateMessageHistoryRepository.PreparePrivateMessageHistoryHours(
|
||||
ctx,
|
||||
s.privateMessageHistorySDKAppID,
|
||||
fromHour.UnixMilli(),
|
||||
throughHour.UnixMilli(),
|
||||
now.UnixMilli(),
|
||||
); err != nil {
|
||||
return PrivateMessageHistoryBatchResult{}, err
|
||||
}
|
||||
|
||||
hourStartMS, attempts, claimed, err := s.privateMessageHistoryRepository.ClaimPrivateMessageHistoryHour(
|
||||
ctx,
|
||||
s.privateMessageHistorySDKAppID,
|
||||
runID,
|
||||
fromHour.UnixMilli(),
|
||||
throughHour.UnixMilli(),
|
||||
now.UnixMilli(),
|
||||
lockTTL,
|
||||
)
|
||||
if err != nil || !claimed {
|
||||
return PrivateMessageHistoryBatchResult{}, err
|
||||
}
|
||||
result := PrivateMessageHistoryBatchResult{Claimed: true}
|
||||
hourStart := time.UnixMilli(hourStartMS).UTC()
|
||||
|
||||
files, err := s.privateMessageHistoryClient.ListC2CHistoryFiles(ctx, hourStart)
|
||||
if err != nil {
|
||||
if tencentim.IsRESTErrorCode(err, 1004) {
|
||||
// 1004 同时表示“无消息”和“文件尚未生成”。最近小时先重试;超过生成窗口后
|
||||
// 视为空小时完成,避免一个永久空小时阻塞七天回补。
|
||||
if attempts < 3 && now.Sub(hourStart) < privateMessageHistoryEmptyMaxWait {
|
||||
if retryErr := s.markPrivateMessageHistoryRetry(ctx, hourStartMS, runID, err, now); retryErr != nil {
|
||||
return result, retryErr
|
||||
}
|
||||
result.RetryPending = true
|
||||
return result, nil
|
||||
}
|
||||
if completeErr := s.privateMessageHistoryRepository.CompletePrivateMessageHistoryHour(
|
||||
ctx, s.privateMessageHistorySDKAppID, hourStartMS, runID, 0, 0, now.UnixMilli(),
|
||||
); completeErr != nil {
|
||||
return result, completeErr
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
if retryErr := s.markPrivateMessageHistoryRetry(ctx, hourStartMS, runID, err, now); retryErr != nil {
|
||||
return result, retryErr
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
pending := make([]hostdomain.PrivateMessageEvent, 0, importBatchSize)
|
||||
flush := func() error {
|
||||
if len(pending) == 0 {
|
||||
return nil
|
||||
}
|
||||
imported, importErr := s.privateMessageHistoryRepository.ImportPrivateMessageEvents(ctx, pending)
|
||||
if importErr != nil {
|
||||
return importErr
|
||||
}
|
||||
result.ImportedCount += imported
|
||||
pending = pending[:0]
|
||||
return nil
|
||||
}
|
||||
consume := func(message tencentim.C2CHistoryMessage) error {
|
||||
event, ok := privateMessageEventFromHistory(message)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
pending = append(pending, event)
|
||||
if len(pending) >= importBatchSize {
|
||||
return flush()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
processed, streamErr := s.privateMessageHistoryClient.StreamC2CHistory(ctx, file, consume)
|
||||
result.SourceCount += processed
|
||||
if streamErr != nil {
|
||||
if retryErr := s.markPrivateMessageHistoryRetry(ctx, hourStartMS, runID, streamErr, now); retryErr != nil {
|
||||
return result, retryErr
|
||||
}
|
||||
return result, streamErr
|
||||
}
|
||||
}
|
||||
if err := flush(); err != nil {
|
||||
if retryErr := s.markPrivateMessageHistoryRetry(ctx, hourStartMS, runID, err, now); retryErr != nil {
|
||||
return result, retryErr
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
if err := s.privateMessageHistoryRepository.CompletePrivateMessageHistoryHour(
|
||||
ctx,
|
||||
s.privateMessageHistorySDKAppID,
|
||||
hourStartMS,
|
||||
runID,
|
||||
result.SourceCount,
|
||||
result.ImportedCount,
|
||||
s.now().UTC().UnixMilli(),
|
||||
); err != nil {
|
||||
return result, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *Service) markPrivateMessageHistoryRetry(ctx context.Context, hourStartMS int64, runID string, cause error, now time.Time) error {
|
||||
retryErr := s.privateMessageHistoryRepository.RetryPrivateMessageHistoryHour(
|
||||
ctx,
|
||||
s.privateMessageHistorySDKAppID,
|
||||
hourStartMS,
|
||||
runID,
|
||||
now.Add(privateMessageHistoryRetryDelay).UnixMilli(),
|
||||
cause.Error(),
|
||||
now.UnixMilli(),
|
||||
)
|
||||
if retryErr == nil {
|
||||
return nil
|
||||
}
|
||||
return errors.Join(cause, retryErr)
|
||||
}
|
||||
|
||||
func privateMessageEventFromHistory(message tencentim.C2CHistoryMessage) (hostdomain.PrivateMessageEvent, bool) {
|
||||
senderText := strings.TrimSpace(message.FromAccount)
|
||||
targetText := strings.TrimSpace(message.ToAccount)
|
||||
senderUserID, senderErr := strconv.ParseInt(senderText, 10, 64)
|
||||
targetUserID, targetErr := strconv.ParseInt(targetText, 10, 64)
|
||||
if senderErr != nil || targetErr != nil || senderUserID <= 0 || targetUserID <= 0 || message.Timestamp <= 0 {
|
||||
// 共享 SDKAppID 可能包含管理员/第三方非数字 identifier;只有项目内部不可变 user_id
|
||||
// 才能安全映射 users 主键,其余消息不属于 Host Center 统计事实。
|
||||
return hostdomain.PrivateMessageEvent{}, false
|
||||
}
|
||||
identity := fmt.Sprintf(
|
||||
"%s\x00%s\x00%d\x00%d\x00%d",
|
||||
senderText,
|
||||
targetText,
|
||||
message.Timestamp,
|
||||
message.Sequence,
|
||||
message.Random,
|
||||
)
|
||||
hash := sha256.Sum256([]byte(identity))
|
||||
return hostdomain.PrivateMessageEvent{
|
||||
EventID: "im-history:" + hex.EncodeToString(hash[:]),
|
||||
SenderUserID: senderUserID,
|
||||
TargetUserID: targetUserID,
|
||||
OccurredAtMS: message.Timestamp * int64(time.Second/time.Millisecond),
|
||||
}, true
|
||||
}
|
||||
@ -70,8 +70,12 @@ type IDGenerator interface {
|
||||
type Service struct {
|
||||
repository Repository
|
||||
roleScopePolicyRepository RoleScopePolicyRepository
|
||||
idGenerator IDGenerator
|
||||
now func() time.Time
|
||||
// privateMessageHistory* 只用于 cron 离线同步,不进入任何 Host Center 查询或 IM 实时链路。
|
||||
privateMessageHistoryRepository PrivateMessageHistoryRepository
|
||||
privateMessageHistoryClient PrivateMessageHistoryClient
|
||||
privateMessageHistorySDKAppID int64
|
||||
idGenerator IDGenerator
|
||||
now func() time.Time
|
||||
}
|
||||
|
||||
// Option 调整 Host 业务层的运行时依赖,测试可以注入固定时钟和确定性发号器。
|
||||
|
||||
@ -0,0 +1,210 @@
|
||||
package host
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"hyapp/pkg/xerr"
|
||||
hostdomain "hyapp/services/user-service/internal/domain/host"
|
||||
)
|
||||
|
||||
const privateMessageHistoryMaxHours = 7 * 24
|
||||
|
||||
// PreparePrivateMessageHistoryHours 只向小时状态小表补齐最近七天的主键,不更新已完成行。
|
||||
// 多个 app_code 的 cron 并发调用会由 INSERT IGNORE 和共享 SDKAppID 主键自然收敛。
|
||||
func (r *Repository) PreparePrivateMessageHistoryHours(ctx context.Context, sdkAppID int64, fromHourMS int64, throughHourMS int64, nowMS int64) error {
|
||||
if r == nil || r.db == nil {
|
||||
return xerr.New(xerr.Unavailable, "mysql repository is not configured")
|
||||
}
|
||||
if sdkAppID <= 0 || fromHourMS <= 0 || throughHourMS < fromHourMS {
|
||||
return xerr.New(xerr.InvalidArgument, "private message history range is invalid")
|
||||
}
|
||||
const hourMS = int64(time.Hour / time.Millisecond)
|
||||
hourCount := int((throughHourMS-fromHourMS)/hourMS) + 1
|
||||
if hourCount <= 0 || hourCount > privateMessageHistoryMaxHours {
|
||||
return xerr.New(xerr.InvalidArgument, "private message history range exceeds seven days")
|
||||
}
|
||||
|
||||
values := make([]string, 0, hourCount)
|
||||
args := make([]any, 0, hourCount*4)
|
||||
for hourStartMS := fromHourMS; hourStartMS <= throughHourMS; hourStartMS += hourMS {
|
||||
values = append(values, "(?, ?, 'pending', ?, ?)")
|
||||
args = append(args, sdkAppID, hourStartMS, nowMS, nowMS)
|
||||
}
|
||||
_, err := r.db.ExecContext(ctx, `
|
||||
INSERT IGNORE INTO user_private_message_history_sync (
|
||||
sdk_app_id, hour_start_ms, status, created_at_ms, updated_at_ms
|
||||
) VALUES `+strings.Join(values, ","), args...)
|
||||
return err
|
||||
}
|
||||
|
||||
// ClaimPrivateMessageHistoryHour 原子认领最新的可处理小时,让当天 Host Center 数据优先恢复。
|
||||
// 状态表最多保留最近七天新增的小时行,ORDER BY/LIMIT 不会触碰消息事实大表。
|
||||
func (r *Repository) ClaimPrivateMessageHistoryHour(ctx context.Context, sdkAppID int64, runID string, fromHourMS int64, throughHourMS int64, nowMS int64, lockTTL time.Duration) (int64, int, bool, error) {
|
||||
if r == nil || r.db == nil {
|
||||
return 0, 0, false, xerr.New(xerr.Unavailable, "mysql repository is not configured")
|
||||
}
|
||||
runID = strings.TrimSpace(runID)
|
||||
if sdkAppID <= 0 || runID == "" || fromHourMS <= 0 || throughHourMS < fromHourMS {
|
||||
return 0, 0, false, xerr.New(xerr.InvalidArgument, "private message history claim is invalid")
|
||||
}
|
||||
if lockTTL <= 0 {
|
||||
lockTTL = 5 * time.Minute
|
||||
}
|
||||
lockedUntilMS := nowMS + lockTTL.Milliseconds()
|
||||
result, err := r.db.ExecContext(ctx, `
|
||||
UPDATE user_private_message_history_sync
|
||||
SET status = 'processing',
|
||||
attempts = attempts + 1,
|
||||
locked_by = ?,
|
||||
locked_until_ms = ?,
|
||||
updated_at_ms = ?
|
||||
WHERE sdk_app_id = ?
|
||||
AND hour_start_ms >= ? AND hour_start_ms <= ?
|
||||
AND (
|
||||
(status IN ('pending', 'retry') AND next_retry_at_ms <= ?)
|
||||
OR (status = 'processing' AND locked_until_ms <= ?)
|
||||
)
|
||||
ORDER BY hour_start_ms DESC
|
||||
LIMIT 1`,
|
||||
runID, lockedUntilMS, nowMS,
|
||||
sdkAppID, fromHourMS, throughHourMS, nowMS, nowMS,
|
||||
)
|
||||
if err != nil {
|
||||
return 0, 0, false, err
|
||||
}
|
||||
affected, err := result.RowsAffected()
|
||||
if err != nil || affected == 0 {
|
||||
return 0, 0, false, err
|
||||
}
|
||||
|
||||
var hourStartMS int64
|
||||
var attempts int
|
||||
err = r.db.QueryRowContext(ctx, `
|
||||
SELECT hour_start_ms, attempts
|
||||
FROM user_private_message_history_sync
|
||||
WHERE sdk_app_id = ? AND status = 'processing' AND locked_by = ? AND locked_until_ms = ?
|
||||
LIMIT 1`,
|
||||
sdkAppID, runID, lockedUntilMS,
|
||||
).Scan(&hourStartMS, &attempts)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return 0, 0, false, xerr.New(xerr.Conflict, "private message history claim was lost")
|
||||
}
|
||||
return 0, 0, false, err
|
||||
}
|
||||
return hourStartMS, attempts, true, nil
|
||||
}
|
||||
|
||||
// ImportPrivateMessageEvents 批量把腾讯归档转换为最小统计事实。users 主键分别验证收发双方,
|
||||
// app_code 取接收者主数据且要求双方同租户;host_profiles 联接避免保存与 Host Center 无关的普通私聊。
|
||||
func (r *Repository) ImportPrivateMessageEvents(ctx context.Context, events []hostdomain.PrivateMessageEvent) (int, error) {
|
||||
if r == nil || r.db == nil {
|
||||
return 0, xerr.New(xerr.Unavailable, "mysql repository is not configured")
|
||||
}
|
||||
if len(events) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
if len(events) > 500 {
|
||||
return 0, xerr.New(xerr.InvalidArgument, "private message history import batch exceeds 500")
|
||||
}
|
||||
|
||||
createdAtMS := time.Now().UTC().UnixMilli()
|
||||
selects := make([]string, 0, len(events))
|
||||
args := make([]any, 0, len(events)*5)
|
||||
for index, event := range events {
|
||||
prefix := "SELECT"
|
||||
if index > 0 {
|
||||
prefix = "UNION ALL SELECT"
|
||||
}
|
||||
selects = append(selects, prefix+" ? AS event_id, ? AS sender_user_id, ? AS target_user_id, ? AS occurred_at_ms, ? AS created_at_ms")
|
||||
args = append(args, event.EventID, event.SenderUserID, event.TargetUserID, event.OccurredAtMS, createdAtMS)
|
||||
}
|
||||
query := `
|
||||
INSERT IGNORE INTO user_private_message_events (
|
||||
app_code, event_id, sender_user_id, target_user_id, occurred_at_ms, created_at_ms
|
||||
)
|
||||
SELECT target.app_code,
|
||||
imported.event_id,
|
||||
sender.user_id,
|
||||
target.user_id,
|
||||
imported.occurred_at_ms,
|
||||
imported.created_at_ms
|
||||
FROM (` + strings.Join(selects, "\n") + `) imported
|
||||
INNER JOIN users target
|
||||
ON target.user_id = imported.target_user_id
|
||||
INNER JOIN users sender
|
||||
ON sender.user_id = imported.sender_user_id AND sender.app_code = target.app_code
|
||||
INNER JOIN host_profiles host
|
||||
ON host.app_code = target.app_code AND host.user_id = target.user_id`
|
||||
|
||||
result, err := r.db.ExecContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("import private message history events: %w", err)
|
||||
}
|
||||
affected, err := result.RowsAffected()
|
||||
return int(affected), err
|
||||
}
|
||||
|
||||
// CompletePrivateMessageHistoryHour 仅允许当前 run 完成自己的租约,防止超时旧 worker
|
||||
// 覆盖新 worker 的重试结果。
|
||||
func (r *Repository) CompletePrivateMessageHistoryHour(ctx context.Context, sdkAppID int64, hourStartMS int64, runID string, sourceMessages int, importedEvents int, nowMS int64) error {
|
||||
result, err := r.db.ExecContext(ctx, `
|
||||
UPDATE user_private_message_history_sync
|
||||
SET status = 'completed',
|
||||
source_message_count = ?,
|
||||
imported_event_count = ?,
|
||||
next_retry_at_ms = 0,
|
||||
locked_by = '',
|
||||
locked_until_ms = 0,
|
||||
last_error = '',
|
||||
updated_at_ms = ?
|
||||
WHERE sdk_app_id = ? AND hour_start_ms = ? AND status = 'processing' AND locked_by = ?`,
|
||||
sourceMessages, importedEvents, nowMS, sdkAppID, hourStartMS, strings.TrimSpace(runID),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
affected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if affected == 0 {
|
||||
return xerr.New(xerr.Conflict, "private message history completion lease was lost")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RetryPrivateMessageHistoryHour 记录脱敏错误并延后重试;下载 URL 和 UserSig 已在 REST
|
||||
// 边界被剥离,last_error 仍限制长度,避免外部响应放大状态表。
|
||||
func (r *Repository) RetryPrivateMessageHistoryHour(ctx context.Context, sdkAppID int64, hourStartMS int64, runID string, retryAtMS int64, errorMessage string, nowMS int64) error {
|
||||
errorMessage = strings.TrimSpace(errorMessage)
|
||||
if len(errorMessage) > 512 {
|
||||
errorMessage = errorMessage[:512]
|
||||
}
|
||||
result, err := r.db.ExecContext(ctx, `
|
||||
UPDATE user_private_message_history_sync
|
||||
SET status = 'retry',
|
||||
next_retry_at_ms = ?,
|
||||
locked_by = '',
|
||||
locked_until_ms = 0,
|
||||
last_error = ?,
|
||||
updated_at_ms = ?
|
||||
WHERE sdk_app_id = ? AND hour_start_ms = ? AND status = 'processing' AND locked_by = ?`,
|
||||
retryAtMS, errorMessage, nowMS, sdkAppID, hourStartMS, strings.TrimSpace(runID),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
affected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if affected == 0 {
|
||||
return xerr.New(xerr.Conflict, "private message history retry lease was lost")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -162,6 +162,40 @@ func (s *Server) RefreshCPIntimacyLeaderboard(ctx context.Context, req *userv1.C
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ProcessPrivateMessageHistoryBatch 从腾讯 IM 小时归档恢复 Host 私聊发送人数事实。
|
||||
// 任务只在 cron 后台运行;即使腾讯归档延迟或下载失败,也不会影响客户端私聊发送。
|
||||
func (s *Server) ProcessPrivateMessageHistoryBatch(ctx context.Context, req *userv1.CronBatchRequest) (*userv1.CronBatchResponse, error) {
|
||||
ctx = contextWithApp(ctx, req.GetMeta())
|
||||
if s.hostSvc == nil {
|
||||
return nil, xerr.ToGRPCError(xerr.New(xerr.Unavailable, "host service is not configured"))
|
||||
}
|
||||
result, err := s.hostSvc.ProcessPrivateMessageHistoryBatch(
|
||||
ctx,
|
||||
req.GetRunId(),
|
||||
durationFromMillis(req.GetLockTtlMs()),
|
||||
int(req.GetBatchSize()),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, xerr.ToGRPCError(err)
|
||||
}
|
||||
claimed := int32(0)
|
||||
if result.Claimed {
|
||||
claimed = 1
|
||||
}
|
||||
failureCount := int32(0)
|
||||
if result.RetryPending {
|
||||
failureCount = 1
|
||||
}
|
||||
return &userv1.CronBatchResponse{
|
||||
ClaimedCount: claimed,
|
||||
ProcessedCount: int32(result.SourceCount),
|
||||
SuccessCount: int32(result.ImportedCount),
|
||||
FailureCount: failureCount,
|
||||
// 小时归档有意按 cron interval 节流;回补不通过 has_more 形成无间隔下载突发。
|
||||
HasMore: false,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func durationFromMillis(value int64) time.Duration {
|
||||
if value <= 0 {
|
||||
return 0
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user