master
pan 3 years ago
parent 303f98ff04
commit 94b057d1cf
  1. 36
      foreground/src/main/java/com/gyf/csams/Api.kt
  2. 2
      foreground/src/main/java/com/gyf/csams/InitViewModel.kt
  3. 13
      foreground/src/main/java/com/gyf/csams/activity/model/ApplyActViewModel.kt
  4. 121
      foreground/src/main/java/com/gyf/csams/association/model/RegAssociationViewModel.kt
  5. 110
      foreground/src/main/java/com/gyf/csams/association/ui/RegAssociationActivity.kt
  6. 59
      foreground/src/main/java/com/gyf/csams/main/model/MainViewModel.kt
  7. 11
      foreground/src/main/java/com/gyf/csams/util/TokenUtil.kt
  8. 2
      lib/src/main/java/com/gyf/lib/util/BottomButton.kt
  9. 56
      lib/src/main/java/com/gyf/lib/util/HttpUtil.kt

@ -17,10 +17,19 @@ enum class TestApi(val path: String) : UrlPath {
* @property path
*/
enum class AccountApi(val path: String) : UrlPath {
//注册
Register("/register"),
//学号检测
CheckId("/register/checkId"),
//登录
Login("/login"),
//令牌校验
LoginToken("/login/token"),
//登出
Logout("/logout");
@ -29,13 +38,36 @@ enum class AccountApi(val path: String) : UrlPath {
}
}
/**
* 主页接口
*
* @property path
*/
enum class MainApi(val path: String) : UrlPath {
//热门活动
HotActivity("/hotActivity"),
//留言区
LeaveMessage("/leaveMessage"),
GetMessage("getMessage");
GetMessage("/getMessage");
override fun build(): String {
return "/api/main${this.path}"
}
}
/**
* 社团接口
*
* @property path
*/
enum class AssociationApi(val path: String) : UrlPath {
Logo("/uploadLogo"),
Register("/register");
override fun build(): String {
return "/api/main/${this.path}"
return "/api/association${this.path}"
}
}

@ -77,7 +77,7 @@ class InitViewModel : ViewModel() {
),
jsonParam = TokenVo(
token = currentToken.token,
studentId = currentToken.studentId
userId = currentToken.userId
)
)
} else if (tokenList != null && tokenList.size > 1) {

@ -17,6 +17,7 @@ import com.baidu.mapapi.search.poi.*
import com.baidu.mapapi.search.sug.SuggestionResult
import com.baidu.mapapi.search.sug.SuggestionSearch
import com.baidu.mapapi.search.sug.SuggestionSearchOption
import com.gyf.csams.BuildConfig
import com.gyf.csams.MyLocationListener
import com.gyf.csams.NOT_IMPL_TIP
import com.gyf.csams.R
@ -434,10 +435,14 @@ class ApplyActViewModel(application: Application) : AndroidViewModel(application
fun createMapView(context: Context): MapView {
val mapView = MapView(context).apply {
MyLocationListener.location.value?.let { _ ->
// 设置初始中心点为用户附近
// mCenter = LatLng(it.latitude, it.longitude)
mCenter = LatLng(39.963175, 116.400244)
MyLocationListener.location.value?.let { it ->
if (BuildConfig.DEBUG) {
//如果是测试版本地图中心设置为北京
mCenter = LatLng(39.963175, 116.400244)
} else {
//否则设置初始中心点为用户附近
mCenter = LatLng(it.latitude, it.longitude)
}
map.setMapStatus(mapStatus(mCenter = mCenter))

@ -1,42 +1,135 @@
package com.gyf.csams.association.model
import android.app.Application
import android.net.Uri
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.gyf.csams.NOT_IMPL_TIP
import com.gyf.lib.uikit.StringForm
import androidx.lifecycle.viewModelScope
import com.google.gson.reflect.TypeToken
import com.gyf.csams.Api
import com.gyf.csams.AssociationApi
import com.gyf.csams.MainApplication
import com.gyf.csams.UNKNOW_ERROR
import com.gyf.csams.util.SimpleCallback
import com.gyf.csams.util.Token
import com.gyf.csams.util.TokenManager
import com.gyf.lib.uikit.FormStatus
import com.gyf.lib.uikit.ValidStringForm
import com.gyf.lib.util.ApiResponse
import com.gyf.lib.util.HttpClient
import com.orhanobut.logger.Logger
import kotlinx.coroutines.launch
import java.io.File
import java.io.InputStream
data class RegAssociationVo(val name: String, val desc: String, val fileId: Int, val token: Token)
data class Image(val uri: Uri, val createTime: Long, val size: Long)
class RegAssociationViewModel : ViewModel() {
class RegAssociationViewModel(application: Application) : AndroidViewModel(application) {
val frameDesc = "社团注册资料"
val name = StringForm(formDesc = "社团名称", textLength = 5)
val desc = StringForm(formDesc = "社团简介", textLength = 30)
val name = ValidStringForm(formDesc = "社团名称", textLength = 5)
val desc = ValidStringForm(formDesc = "社团简介", textLength = 30)
val _picture = MutableLiveData<Uri>()
val picture: LiveData<Uri> = _picture
private val _picture = MutableLiveData<Uri?>()
val picture: LiveData<Uri?> = _picture
val piciurePlaceHolder = "请上传图片"
private val _fileId = MutableLiveData<Int>()
val fileId: LiveData<Int> = _fileId
val errorPicture = "图片加载失败,请联系管理员"
val picturePlaceHolder = "请上传图片"
val errorPicture = "图片加载失败,请联系管理员"
fun setPicture(uri: Uri) {
_picture.value = uri
}
fun getInputSteam(): InputStream? {
_picture.value?.let {
val resolver = getApplication<MainApplication>().contentResolver
return resolver.openInputStream(it)
}
throw IllegalArgumentException(UNKNOW_ERROR)
}
fun uploadPhoto(callback: (value: String) -> Unit) {
getInputSteam()?.readBytes()?.apply {
val token = TokenManager.token
if (token != null) {
viewModelScope.launch {
val context = getApplication<MainApplication>()
runCatching {
val cacheFile = File(context.cacheDir, "${System.currentTimeMillis()}")
cacheFile.writeBytes(this@apply)
HttpClient.uploadFile(
Api.buildUrl(AssociationApi.Logo),
SimpleCallback<List<Int>>("上传图片", onSuccess = {
Logger.i(it.message)
callback(it.message)
it.body?.let {
_fileId.postValue(it.first())
}
}, onFail = {
Logger.e(it)
callback("图片上传失败")
}, type = object : TypeToken<ApiResponse<List<Int>>>() {}.type),
id = token.userId,
token = token.token,
fileList = arrayOf(cacheFile)
)
}
}
} else {
callback(UNKNOW_ERROR)
}
}
}
/**
* TODO 注册社团
*
*
* @param callback
*/
fun register(callback: (value: String) -> Unit) {
callback(NOT_IMPL_TIP)
val nameValue = name.formValue.value
val descValue = desc.formValue.value
val token = TokenManager.token
val fileId = _fileId.value
if (token != null && nameValue != null && descValue != null && fileId != null &&
name.statusForm.value == FormStatus.Valid && desc.statusForm.value == FormStatus.Valid
) {
viewModelScope.launch {
HttpClient.post(
Api.buildUrl(AssociationApi.Register),
SimpleCallback<Boolean>("注册社团", onSuccess = {
Logger.i(it.message)
callback(it.message)
name.clean()
desc.clean()
_picture.postValue(null)
}, onFail = {
Logger.e(it)
}, object : TypeToken<ApiResponse<Boolean>>() {}.type),
jsonParam =
RegAssociationVo(
name = nameValue,
desc = descValue,
token = token,
fileId = fileId
)
)
}
} else {
callback(UNKNOW_ERROR)
}
}
}

@ -33,10 +33,7 @@ import com.gyf.csams.R
import com.gyf.csams.association.model.RegAssociationViewModel
import com.gyf.csams.uikit.Background
import com.gyf.csams.uikit.BackgroundImage
import com.gyf.lib.uikit.BaseTextField
import com.gyf.lib.uikit.BodyS
import com.gyf.lib.uikit.MainColumnFrame
import com.gyf.lib.uikit.ScaffoldModel
import com.gyf.lib.uikit.*
import com.gyf.lib.util.BottomButton
import com.orhanobut.logger.Logger
@ -61,30 +58,38 @@ class RegAssociationActivity : ComponentActivity() {
modifier = Modifier
.weight(0.1F)
)
Title()
Name()
Desc(
modifier = Modifier
.weight(0.1F)
.fillMaxWidth()
)
Spacer(modifier = Modifier.weight(0.05F))
Logo(
modifier = Modifier
.weight(0.2F)
.fillMaxWidth()
)
Spacer(modifier = Modifier.weight(0.05F))
val model: RegAssociationViewModel = viewModel()
val scaffoldModel: ScaffoldModel = viewModel()
BottomButton(
modifier = Modifier.fillMaxWidth(),
confirmDesc = R.string.reg_btn
) {
model.register { scaffoldModel.update(message = it) }
}
Spacer(modifier = Modifier.weight(0.05F))
Title()
Name()
Desc(
modifier = Modifier
.weight(0.1F)
.fillMaxWidth()
)
Spacer(modifier = Modifier.weight(0.05F))
Logo(
modifier = Modifier
.weight(0.2F)
.fillMaxWidth()
)
Spacer(modifier = Modifier.weight(0.05F))
val model: RegAssociationViewModel = viewModel()
val scaffoldModel: ScaffoldModel = viewModel()
val name by model.name.statusForm.observeAsState()
val desc by model.name.statusForm.observeAsState()
val fileId by model.fileId.observeAsState()
BottomButton(
modifier = Modifier.fillMaxWidth(),
enabled = name == FormStatus.Valid && desc == FormStatus.Valid && fileId != null,
confirmDesc = R.string.reg_btn
) {
model.register {
scaffoldModel.update(message = it, actionLabel = "返回") {
finish()
}
}
}
Spacer(modifier = Modifier.weight(0.05F))
}
}
@ -103,16 +108,21 @@ class RegAssociationActivity : ComponentActivity() {
scaffoldModel: ScaffoldModel = viewModel(),
modifier: Modifier
) {
val photoIntent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
photoIntent.type = "image/*"
val photoIntent = Intent(
Intent.ACTION_PICK,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
)
val uri: Uri? by model.picture.observeAsState()
val resultLauncher =
rememberLauncherForActivityResult(contract = ActivityResultContracts.StartActivityForResult()) {
when (it.resultCode) {
Activity.RESULT_OK -> {
Logger.i("uri=${it.data?.data}")
it.data?.data?.let { it1 -> model.setPicture(it1) }
it.data?.data?.apply {
Logger.i("uri=$this")
model.setPicture(this)
}
}
}
}
@ -161,7 +171,7 @@ class RegAssociationActivity : ComponentActivity() {
}
}
}) {
Text(text = model.piciurePlaceHolder)
Text(text = model.picturePlaceHolder)
}
} else {
uri.let {
@ -175,17 +185,33 @@ class RegAssociationActivity : ComponentActivity() {
)
.asImageBitmap(), contentDescription = null
)
IconButton(onClick = {
loadPicture()
}) {
Image(
painter = painterResource(id = R.drawable.ic_exchange_rate),
contentDescription = null
)
Column(
modifier = Modifier.fillMaxHeight(),
verticalArrangement = Arrangement.SpaceEvenly
) {
IconButton(onClick = {
loadPicture()
}) {
Image(
painter = painterResource(id = R.drawable.ic_exchange_rate),
contentDescription = null
)
}
IconButton(onClick = {
scaffoldModel.update("确认上传此图片?", actionLabel = "确定") {
Logger.i("开始上传")
model.uploadPhoto {
scaffoldModel.update(message = it)
}
}
}) {
Image(
painter = painterResource(id = R.drawable.ic_upload),
contentDescription = null
)
}
}
}
} else {
Text(text = model.errorPicture)
}

@ -8,6 +8,7 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.google.gson.reflect.TypeToken
import com.gyf.csams.*
import com.gyf.csams.account.model.UserVo
import com.gyf.csams.account.ui.AccountActivity
import com.gyf.csams.uikit.AbstractComment
import com.gyf.csams.uikit.OnlyToken
@ -29,7 +30,7 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
data class LeaveMessageFormatVo(val message: String, val studentId: String)
data class LeaveMessageFormatVo(val message: String, val user: UserVo)
/**
* 跑马灯
@ -70,17 +71,7 @@ class MarqueeViewModel : AbstractComment() {
if (it.body == true) {
callback("留言发送成功")
newContent.clean()
_marqueeTexts.value?.apply {
val c = mutableListOf<LeaveMessageFormatVo>()
c.add(
LeaveMessageFormatVo(
message = "${TokenManager.token?.name}说:${newContent.formValue.value}",
studentId = "${TokenManager.token?.studentId}"
)
)
c.addAll(this)
_marqueeTexts.postValue(c)
}
loadMessage(callback)
} else {
callback("留言发送失败")
}
@ -286,7 +277,7 @@ data class InfoVo(
override val desc: String
) : PersonInfoVo()
data class UserLogoutVo(val studentId: String)
data class UserLogoutVo(val userId: Int)
/**
* 个人中心
@ -319,35 +310,37 @@ class CenterViewModel : ViewModel() {
}
fun logout(context: Activity, callback: (message: String) -> Unit) {
val studentId = TokenManager.token?.studentId
if (studentId != null) {
Logger.i("帐号$studentId 将要退出登录")
val userId = TokenManager.token?.userId
if (userId != null) {
Logger.i("帐号$userId 将要退出登录")
viewModelScope.launch {
HttpClient.post(Api.buildUrl(AccountApi.Logout),
SimpleCallback<Boolean>(action = "登出", onSuccess = { it ->
it.body?.let {
if (it) {
viewModelScope.launch {
val db = AppDatabase.getInstance(context = context)
db?.tokenDao()?.deleteAll()
Logger.i("退出登陆成功")
context.finish()
context.startActivity(
Intent(
context,
AccountActivity::class.java
HttpClient.post(
Api.buildUrl(AccountApi.Logout),
SimpleCallback<Boolean>(
action = "登出", onSuccess = { it ->
it.body?.let {
if (it) {
viewModelScope.launch {
val db = AppDatabase.getInstance(context = context)
db?.tokenDao()?.deleteAll()
Logger.i("退出登陆成功")
context.finish()
context.startActivity(
Intent(
context,
AccountActivity::class.java
)
)
)
}
TokenManager.token = null
}
TokenManager.token = null
}
}
callback(it.message)
}, onFail = { callback("退出登陆失败") },
type = object : TypeToken<ApiResponse<Boolean>>() {}.type
),
jsonParam = UserLogoutVo(studentId = studentId)
jsonParam = UserLogoutVo(userId = userId)
)
}
} else {

@ -11,21 +11,17 @@ import androidx.room.*
*/
@Entity
data class Token(
@PrimaryKey val studentId: String,
@PrimaryKey val userId: Int,
@ColumnInfo val token: String,
@ColumnInfo val createTime: Long,
@ColumnInfo val name: String
@ColumnInfo val createTime: Long
)
//data class TokenVo(val token:String,val studentId:String)
/**
* 令牌传输
*
* @property token
*/
data class TokenVo(val token: String, val studentId: String)
data class TokenVo(val token: String, val userId: Int)
@Dao
interface TokenDao {
@ -44,6 +40,7 @@ interface TokenDao {
object TokenManager {
var token: Token? = null
}

@ -20,6 +20,7 @@ import com.gyf.lib.R
@Composable
fun BottomButton(
modifier: Modifier = Modifier,
enabled: Boolean = true,
@StringRes confirmDesc: Int = R.string.confirm_btn,
@StringRes backDesc: Int = R.string.back_btn,
onConfirm: () -> Unit
@ -28,6 +29,7 @@ fun BottomButton(
Row(modifier = modifier, horizontalArrangement = Arrangement.Center) {
OutlinedButton(
onClick = onConfirm,
enabled = enabled,
modifier = Modifier.background(color = MaterialTheme.colors.primary)
) {
Text(text = stringResource(id = confirmDesc))

@ -4,11 +4,17 @@ import com.google.gson.Gson
import com.orhanobut.logger.Logger
import okhttp3.*
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.asRequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import java.io.File
import java.io.IOException
import java.lang.reflect.Type
import java.net.SocketTimeoutException
interface TokenInterface {
fun token(): String
}
object HttpClient {
private val httpClient: OkHttpClient = OkHttpClient()
@ -59,6 +65,7 @@ object HttpClient {
* @param params
*/
fun get(url: String, callback: Callback, params: Map<String, String>? = null) {
Logger.i("request url=$url")
val request = Request.Builder()
.url(url.plus(buildQueryParams(params = params)))
.build()
@ -68,34 +75,36 @@ object HttpClient {
/**
* HTTP POST
* 发送表单
* 发送JSON
*
* @param url
* @param callback
* @param params
* @param jsonBody
*/
fun post(url: String, callback: Callback, params: Map<String, String>? = null) {
@Deprecated(
message = "",
replaceWith = ReplaceWith("com.gyf.lib.util.HttpClient.post(java.lang.String, okhttp3.Callback, java.lang.Object)")
)
fun post(url: String, callback: Callback, jsonBody: String) {
Logger.json(jsonBody)
val request = Request.Builder()
.url(url)
.post(body = buildFormBody(params))
.post(body = jsonBody.toRequestBody(contentType = JSON_CONTENT_TYPE))
.build()
val call = httpClient.newCall(request)
call.enqueue(callback)
}
/**
* HTTP POST
* 发送JSON
* 发送json表单
*
* @param url
* @param callback
* @param jsonBody
* @param jsonParam
*/
@Deprecated(
message = "",
replaceWith = ReplaceWith("com.gyf.lib.util.HttpClient.post(java.lang.String, okhttp3.Callback, java.lang.Object)")
)
fun post(url: String, callback: Callback, jsonBody: String) {
fun post(url: String, callback: Callback, jsonParam: Any) {
Logger.i("request url=$url")
val jsonBody = json.toJson(jsonParam)
Logger.json(jsonBody)
val request = Request.Builder()
.url(url)
@ -105,12 +114,27 @@ object HttpClient {
call.enqueue(callback)
}
fun post(url: String, callback: Callback, jsonParam: Any) {
val jsonBody = json.toJson(jsonParam)
Logger.json(jsonBody)
/**
* 上传文件
*
* @param url
* @param callback
*/
fun uploadFile(url: String, callback: Callback, id: Int, token: String, vararg fileList: File) {
Logger.i("request url=$url")
val body = MultipartBody.Builder()
body.addFormDataPart("id", id.toString())
body.addFormDataPart("token", token)
fileList.withIndex().forEach {
body.addFormDataPart(
"file${it.index}",
it.value.name,
it.value.asRequestBody()
)
}
val request = Request.Builder()
.url(url)
.post(body = jsonBody.toRequestBody(contentType = JSON_CONTENT_TYPE))
.post(body = body.build())
.build()
val call = httpClient.newCall(request)
call.enqueue(callback)

Loading…
Cancel
Save