Files
rider-pro-android-app/app/src/main/java/com/aiosman/ravenow/data/AccountService.kt

557 lines
16 KiB
Kotlin

package com.aiosman.ravenow.data
import com.aiosman.ravenow.AppState
import com.aiosman.ravenow.data.api.ApiClient
import com.aiosman.ravenow.data.api.AppConfig
import com.aiosman.ravenow.data.api.CaptchaInfo
import com.aiosman.ravenow.data.api.ChangePasswordRequestBody
import com.aiosman.ravenow.data.api.GoogleRegisterRequestBody
import com.aiosman.ravenow.data.api.LoginUserRequestBody
import com.aiosman.ravenow.data.api.RegisterMessageChannelRequestBody
import com.aiosman.ravenow.data.api.RegisterRequestBody
import com.aiosman.ravenow.data.api.ResetPasswordRequestBody
import com.aiosman.ravenow.data.api.TrtcSignResponseBody
import com.aiosman.ravenow.data.api.UnRegisterMessageChannelRequestBody
import com.aiosman.ravenow.data.api.UpdateNoticeRequestBody
import com.aiosman.ravenow.data.api.UpdateUserLangRequestBody
import com.aiosman.ravenow.entity.AccountFavouriteEntity
import com.aiosman.ravenow.entity.AccountLikeEntity
import com.aiosman.ravenow.entity.AccountProfileEntity
import com.aiosman.ravenow.entity.NoticeCommentEntity
import com.aiosman.ravenow.entity.NoticePostEntity
import com.aiosman.ravenow.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,
rawAvatar = avatar
)
}
}
/**
* 消息关联资料
*/
data class NoticePost(
// 动态ID
@SerializedName("id")
val id: Int,
// 动态内容
@SerializedName("textContent")
// 动态图片
val textContent: String,
// 动态图片
@SerializedName("images")
val images: List<Image>,
// 动态时间
@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,
@SerializedName("post")
val post: NoticePost?,
) {
fun toNoticeCommentEntity(): NoticeCommentEntity {
return NoticeCommentEntity(
id = id,
content = content,
postId = postId,
time = ApiClient.dateFromApiString(time),
replyComment = replyComment?.toNoticeCommentEntity(),
post = post?.toNoticePostEntity()
)
}
}
/**
* 消息关联用户
*/
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<AccountLike>
/**
* 获取我的关注通知
* @param page 页码
* @param pageSize 每页数量
*/
suspend fun getMyFollowNotice(page: Int, pageSize: Int): ListContainer<AccountFollow>
/**
* 获取我的收藏通知
* @param page 页码
* @param pageSize 每页数量
*/
suspend fun getMyFavouriteNotice(page: Int, pageSize: Int): ListContainer<AccountFavourite>
/**
* 获取我的通知信息
*/
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.isSuccessful) {
parseErrorResponse(resp.errorBody())?.let {
throw it.toServiceException()
}
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) {
val resp = ApiClient.api.changePassword(ChangePasswordRequestBody(oldPassword, newPassword))
if (!resp.isSuccessful) {
parseErrorResponse(resp.errorBody())?.let {
throw it.toServiceException()
}
throw ServiceException("Failed to change password")
}
}
override suspend fun getMyLikeNotice(page: Int, pageSize: Int): ListContainer<AccountLike> {
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<AccountFollow> {
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<AccountFavourite> {
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
}
}