package com.aiosman.riderpro.data import com.aiosman.riderpro.AppState import com.aiosman.riderpro.data.api.ApiClient import com.aiosman.riderpro.data.api.AppConfig import com.aiosman.riderpro.data.api.CaptchaInfo import com.aiosman.riderpro.data.api.ChangePasswordRequestBody import com.aiosman.riderpro.data.api.GoogleRegisterRequestBody import com.aiosman.riderpro.data.api.LoginUserRequestBody import com.aiosman.riderpro.data.api.RegisterMessageChannelRequestBody import com.aiosman.riderpro.data.api.RegisterRequestBody import com.aiosman.riderpro.data.api.ResetPasswordRequestBody import com.aiosman.riderpro.data.api.TrtcSignResponseBody import com.aiosman.riderpro.data.api.UnRegisterMessageChannelRequestBody import com.aiosman.riderpro.data.api.UpdateNoticeRequestBody import com.aiosman.riderpro.data.api.UpdateUserLangRequestBody import com.aiosman.riderpro.entity.AccountFavouriteEntity import com.aiosman.riderpro.entity.AccountLikeEntity import com.aiosman.riderpro.entity.AccountProfileEntity import com.aiosman.riderpro.entity.NoticeCommentEntity import com.aiosman.riderpro.entity.NoticePostEntity import com.aiosman.riderpro.entity.NoticeUserEntity import com.google.gson.annotations.SerializedName import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MultipartBody import okhttp3.RequestBody import okhttp3.RequestBody.Companion.asRequestBody import okhttp3.RequestBody.Companion.toRequestBody import java.io.File /** * 用户资料 */ data class AccountProfile( // 用户ID val id: Int, // 用户名 val username: String, // 昵称 val nickname: String, // 头像 val avatar: String, // 关注数 val followingCount: Int, // 粉丝数 val followerCount: Int, // 是否关注 val isFollowing: Boolean, // 个人简介 val bio: String, // 主页背景图 val banner: String?, // trtcUserId val trtcUserId: String, ) { /** * 转换为Entity */ fun toAccountProfileEntity(): AccountProfileEntity { return AccountProfileEntity( id = id, followerCount = followerCount, followingCount = followingCount, nickName = nickname, avatar = "${ApiClient.BASE_SERVER}$avatar", bio = bio, country = "Worldwide", isFollowing = isFollowing, banner = banner.let { if (!it.isNullOrEmpty()) { return@let "${ApiClient.BASE_SERVER}$it" } null }, trtcUserId = trtcUserId ) } } /** * 消息关联资料 */ data class NoticePost( // 动态ID @SerializedName("id") val id: Int, // 动态内容 @SerializedName("textContent") // 动态图片 val textContent: String, // 动态图片 @SerializedName("images") val images: List, // 动态时间 @SerializedName("time") val time: String, ) { /** * 转换为Entity */ fun toNoticePostEntity(): NoticePostEntity { return NoticePostEntity( id = id, textContent = textContent, images = images.map { it.copy( url = "${ApiClient.BASE_SERVER}${it.url}", thumbnail = "${ApiClient.BASE_SERVER}${it.thumbnail}", blurHash = it.blurHash, width = it.width, height = it.height ) }, time = ApiClient.dateFromApiString(time) ) } } //"comment": { // "id": 103, // "content": "ppp", // "time": "2024-09-08 15:31:37" //} data class NoticeComment( @SerializedName("id") val id: Int, @SerializedName("content") val content: String, @SerializedName("time") val time: String, @SerializedName("replyComment") val replyComment: NoticeComment?, @SerializedName("postId") val postId: Int, ) { fun toNoticeCommentEntity(): NoticeCommentEntity { return NoticeCommentEntity( id = id, content = content, postId = postId, time = ApiClient.dateFromApiString(time), replyComment = replyComment?.toNoticeCommentEntity() ) } } /** * 消息关联用户 */ data class NoticeUser( // 用户ID @SerializedName("id") val id: Int, // 昵称 @SerializedName("nickName") val nickName: String, // 头像 @SerializedName("avatar") val avatar: String, ) { /** * 转换为Entity */ fun toNoticeUserEntity(): NoticeUserEntity { return NoticeUserEntity( id = id, nickName = nickName, avatar = "${ApiClient.BASE_SERVER}$avatar", ) } } /** * 点赞消息通知 */ data class AccountLike( // 是否未读 @SerializedName("isUnread") val isUnread: Boolean, // 动态 @SerializedName("post") val post: NoticePost?, @SerializedName("comment") val comment: NoticeComment?, // 点赞用户 @SerializedName("user") val user: NoticeUser, // 点赞时间 @SerializedName("likeTime") val likeTime: String, // 动态ID @SerializedName("postId") val postId: Int, ) { fun toAccountLikeEntity(): AccountLikeEntity { return AccountLikeEntity( post = post?.toNoticePostEntity(), comment = comment?.toNoticeCommentEntity(), user = user.toNoticeUserEntity(), likeTime = ApiClient.dateFromApiString(likeTime), postId = postId ) } } data class AccountFavourite( @SerializedName("isUnread") val isUnread: Boolean, @SerializedName("post") val post: NoticePost, @SerializedName("user") val user: NoticeUser, @SerializedName("favoriteTime") val favouriteTime: String, ) { fun toAccountFavouriteEntity(): AccountFavouriteEntity { return AccountFavouriteEntity( post = post.toNoticePostEntity(), user = user.toNoticeUserEntity(), favoriteTime = ApiClient.dateFromApiString(favouriteTime) ) } } data class AccountFollow( @SerializedName("id") val id: Int, @SerializedName("username") val username: String, @SerializedName("nickname") val nickname: String, @SerializedName("avatar") val avatar: String, @SerializedName("isUnread") val isUnread: Boolean, @SerializedName("userId") val userId: Int, @SerializedName("isFollowing") val isFollowing: Boolean, ) //{ // "likeCount": 0, // "followCount": 0, // "favoriteCount": 0 //} data class AccountNotice( @SerializedName("likeCount") val likeCount: Int, @SerializedName("followCount") val followCount: Int, @SerializedName("favoriteCount") val favoriteCount: Int, @SerializedName("commentCount") val commentCount: Int, ) interface AccountService { /** * 获取登录当前用户的资料 */ suspend fun getMyAccountProfile(): AccountProfileEntity /** * 获取登录的用户认证信息 */ suspend fun getMyAccount(): UserAuth /** * 使用用户名密码登录 * @param loginName 用户名 * @param password 密码 * @param captchaInfo 验证码信息 */ suspend fun loginUserWithPassword( loginName: String, password: String, captchaInfo: CaptchaInfo? = null ): UserAuth /** * 使用google登录 * @param googleId googleId */ suspend fun loginUserWithGoogle(googleId: String): UserAuth /** * 退出登录 */ suspend fun logout() /** * 更新用户资料 * @param avatar 头像 * @param nickName 昵称 * @param bio 简介 * @param banner 主页背景图 */ suspend fun updateProfile( avatar: UploadImage?, banner: UploadImage?, nickName: String?, bio: String? ) /** * 注册用户 * @param loginName 用户名 * @param password 密码 */ suspend fun registerUserWithPassword(loginName: String, password: String) /** * 使用google账号注册 * @param idToken googleIdToken */ suspend fun regiterUserWithGoogleAccount(idToken: String) /** * 修改密码 * @param oldPassword 旧密码 * @param newPassword 新密码 */ suspend fun changeAccountPassword(oldPassword: String, newPassword: String) /** * 获取我的点赞通知 * @param page 页码 * @param pageSize 每页数量 */ suspend fun getMyLikeNotice(page: Int, pageSize: Int): ListContainer /** * 获取我的关注通知 * @param page 页码 * @param pageSize 每页数量 */ suspend fun getMyFollowNotice(page: Int, pageSize: Int): ListContainer /** * 获取我的收藏通知 * @param page 页码 * @param pageSize 每页数量 */ suspend fun getMyFavouriteNotice(page: Int, pageSize: Int): ListContainer /** * 获取我的通知信息 */ suspend fun getMyNoticeInfo(): AccountNotice /** * 更新通知信息,更新最后一次查看时间 * @param payload 通知信息 */ suspend fun updateNotice(payload: UpdateNoticeRequestBody) /** * 注册消息通道 */ suspend fun registerMessageChannel(client: String, identifier: String) /** * 取消注册消息通道 */ suspend fun unregisterMessageChannel(client: String, identifier: String) /** * 重置密码 */ suspend fun resetPassword(email: String) /** * 更新用户额外信息 */ suspend fun updateUserExtra(language: String, timeOffset: Int, timezone: String) /** * 获取腾讯云TRTC签名 */ suspend fun getMyTrtcSign(): TrtcSignResponseBody suspend fun getAppConfig(): AppConfig } class AccountServiceImpl : AccountService { override suspend fun getMyAccountProfile(): AccountProfileEntity { val resp = ApiClient.api.getMyAccount() val body = resp.body() ?: throw ServiceException("Failed to get account") return body.data.toAccountProfileEntity() } override suspend fun getMyAccount(): UserAuth { val resp = ApiClient.api.checkToken() val body = resp.body() ?: throw ServiceException("Failed to get account") AppState.UserId = body.id return UserAuth(body.id) } override suspend fun loginUserWithPassword( loginName: String, password: String, captchaInfo: CaptchaInfo? ): UserAuth { val resp = ApiClient.api.login(LoginUserRequestBody( username = loginName, password = password, captcha = captchaInfo, )) if (!resp.isSuccessful) { parseErrorResponse(resp.errorBody())?.let { throw it.toServiceException() } throw ServiceException("Failed to register") } return UserAuth(0, resp.body()?.token) } override suspend fun loginUserWithGoogle(googleId: String): UserAuth { val resp = ApiClient.api.login(LoginUserRequestBody(googleId = googleId)) val body = resp.body() ?: throw ServiceException("Failed to login") return UserAuth(0, body.token) } override suspend fun regiterUserWithGoogleAccount(idToken: String) { val resp = ApiClient.api.registerWithGoogle(GoogleRegisterRequestBody(idToken)) if (resp.code() != 200) { throw ServiceException("Failed to register") } } override suspend fun logout() { // do nothing } fun createMultipartBody(file: File, filename: String, name: String): MultipartBody.Part { val requestFile = file.asRequestBody("image/*".toMediaTypeOrNull()) return MultipartBody.Part.createFormData(name, filename, requestFile) } override suspend fun updateProfile( avatar: UploadImage?, banner: UploadImage?, nickName: String?, bio: String? ) { val nicknameField: RequestBody? = nickName?.toRequestBody("text/plain".toMediaTypeOrNull()) val bioField: RequestBody? = bio?.toRequestBody("text/plain".toMediaTypeOrNull()) val avatarField: MultipartBody.Part? = avatar?.let { createMultipartBody(it.file, it.filename, "avatar") } val bannerField: MultipartBody.Part? = banner?.let { createMultipartBody(it.file, it.filename, "banner") } ApiClient.api.updateProfile(avatarField, bannerField, nicknameField, bioField) } override suspend fun registerUserWithPassword(loginName: String, password: String) { val resp = ApiClient.api.register(RegisterRequestBody(loginName, password)) if (!resp.isSuccessful) { parseErrorResponse(resp.errorBody())?.let { throw it.toServiceException() } throw ServiceException("Failed to register") } } override suspend fun changeAccountPassword(oldPassword: String, newPassword: String) { ApiClient.api.changePassword(ChangePasswordRequestBody(oldPassword, newPassword)) } override suspend fun getMyLikeNotice(page: Int, pageSize: Int): ListContainer { val resp = ApiClient.api.getMyLikeNotices(page, pageSize) val body = resp.body() ?: throw ServiceException("Failed to get account") return body } override suspend fun getMyFollowNotice(page: Int, pageSize: Int): ListContainer { val resp = ApiClient.api.getMyFollowNotices(page, pageSize) val body = resp.body() ?: throw ServiceException("Failed to get account") return body } override suspend fun getMyFavouriteNotice( page: Int, pageSize: Int ): ListContainer { val resp = ApiClient.api.getMyFavouriteNotices(page, pageSize) val body = resp.body() ?: throw ServiceException("Failed to get account") return body } override suspend fun getMyNoticeInfo(): AccountNotice { val resp = ApiClient.api.getMyNoticeInfo() val body = resp.body() ?: throw ServiceException("Failed to get account") return body.data } override suspend fun updateNotice(payload: UpdateNoticeRequestBody) { ApiClient.api.updateNoticeInfo(payload) } override suspend fun registerMessageChannel(client: String, identifier: String) { ApiClient.api.registerMessageChannel(RegisterMessageChannelRequestBody(client, identifier)) } override suspend fun unregisterMessageChannel(client: String, identifier: String) { ApiClient.api.unRegisterMessageChannel(UnRegisterMessageChannelRequestBody(client, identifier)) } override suspend fun resetPassword(email: String) { val resp = ApiClient.api.resetPassword( ResetPasswordRequestBody( username = email ) ) if (!resp.isSuccessful) { parseErrorResponse(resp.errorBody())?.let { throw it.toServiceException() } throw ServiceException("Failed to reset password") } } override suspend fun updateUserExtra(language: String, timeOffset: Int, timezone: String) { ApiClient.api.updateUserExtra(UpdateUserLangRequestBody(language, timeOffset, timezone)) } override suspend fun getMyTrtcSign(): TrtcSignResponseBody { val resp = ApiClient.api.getChatSign() val body = resp.body() ?: throw ServiceException("Failed to get trtc sign") return body.data } override suspend fun getAppConfig(): AppConfig { val resp = ApiClient.api.getAppConfig() val body = resp.body() ?: throw ServiceException("Failed to get app config") return body.data } }