master
pan 4 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 * @property path
*/ */
enum class AccountApi(val path: String) : UrlPath { enum class AccountApi(val path: String) : UrlPath {
//注册
Register("/register"), Register("/register"),
//学号检测
CheckId("/register/checkId"), CheckId("/register/checkId"),
//登录
Login("/login"), Login("/login"),
//令牌校验
LoginToken("/login/token"), LoginToken("/login/token"),
//登出
Logout("/logout"); Logout("/logout");
@ -29,13 +38,36 @@ enum class AccountApi(val path: String) : UrlPath {
} }
} }
/**
* 主页接口
*
* @property path
*/
enum class MainApi(val path: String) : UrlPath { enum class MainApi(val path: String) : UrlPath {
//热门活动
HotActivity("/hotActivity"), HotActivity("/hotActivity"),
//留言区
LeaveMessage("/leaveMessage"), 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 { override fun build(): String {
return "/api/main/${this.path}" return "/api/association${this.path}"
} }
} }

@ -77,7 +77,7 @@ class InitViewModel : ViewModel() {
), ),
jsonParam = TokenVo( jsonParam = TokenVo(
token = currentToken.token, token = currentToken.token,
studentId = currentToken.studentId userId = currentToken.userId
) )
) )
} else if (tokenList != null && tokenList.size > 1) { } 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.SuggestionResult
import com.baidu.mapapi.search.sug.SuggestionSearch import com.baidu.mapapi.search.sug.SuggestionSearch
import com.baidu.mapapi.search.sug.SuggestionSearchOption import com.baidu.mapapi.search.sug.SuggestionSearchOption
import com.gyf.csams.BuildConfig
import com.gyf.csams.MyLocationListener import com.gyf.csams.MyLocationListener
import com.gyf.csams.NOT_IMPL_TIP import com.gyf.csams.NOT_IMPL_TIP
import com.gyf.csams.R import com.gyf.csams.R
@ -434,10 +435,14 @@ class ApplyActViewModel(application: Application) : AndroidViewModel(application
fun createMapView(context: Context): MapView { fun createMapView(context: Context): MapView {
val mapView = MapView(context).apply { val mapView = MapView(context).apply {
MyLocationListener.location.value?.let { _ -> MyLocationListener.location.value?.let { it ->
// 设置初始中心点为用户附近 if (BuildConfig.DEBUG) {
// mCenter = LatLng(it.latitude, it.longitude) //如果是测试版本地图中心设置为北京
mCenter = LatLng(39.963175, 116.400244) mCenter = LatLng(39.963175, 116.400244)
} else {
//否则设置初始中心点为用户附近
mCenter = LatLng(it.latitude, it.longitude)
}
map.setMapStatus(mapStatus(mCenter = mCenter)) map.setMapStatus(mapStatus(mCenter = mCenter))

@ -1,42 +1,135 @@
package com.gyf.csams.association.model package com.gyf.csams.association.model
import android.app.Application
import android.net.Uri import android.net.Uri
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope
import com.gyf.csams.NOT_IMPL_TIP import com.google.gson.reflect.TypeToken
import com.gyf.lib.uikit.StringForm 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(application: Application) : AndroidViewModel(application) {
class RegAssociationViewModel : ViewModel() {
val frameDesc = "社团注册资料" val frameDesc = "社团注册资料"
val name = StringForm(formDesc = "社团名称", textLength = 5) val name = ValidStringForm(formDesc = "社团名称", textLength = 5)
val desc = StringForm(formDesc = "社团简介", textLength = 30) val desc = ValidStringForm(formDesc = "社团简介", textLength = 30)
val _picture = MutableLiveData<Uri>() private val _picture = MutableLiveData<Uri?>()
val picture: LiveData<Uri> = _picture 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) { fun setPicture(uri: Uri) {
_picture.value = 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 * @param callback
*/ */
fun register(callback: (value: String) -> Unit) { 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.association.model.RegAssociationViewModel
import com.gyf.csams.uikit.Background import com.gyf.csams.uikit.Background
import com.gyf.csams.uikit.BackgroundImage import com.gyf.csams.uikit.BackgroundImage
import com.gyf.lib.uikit.BaseTextField import com.gyf.lib.uikit.*
import com.gyf.lib.uikit.BodyS
import com.gyf.lib.uikit.MainColumnFrame
import com.gyf.lib.uikit.ScaffoldModel
import com.gyf.lib.util.BottomButton import com.gyf.lib.util.BottomButton
import com.orhanobut.logger.Logger import com.orhanobut.logger.Logger
@ -61,30 +58,38 @@ class RegAssociationActivity : ComponentActivity() {
modifier = Modifier modifier = Modifier
.weight(0.1F) .weight(0.1F)
) )
Title() Title()
Name() Name()
Desc( Desc(
modifier = Modifier modifier = Modifier
.weight(0.1F) .weight(0.1F)
.fillMaxWidth() .fillMaxWidth()
) )
Spacer(modifier = Modifier.weight(0.05F)) Spacer(modifier = Modifier.weight(0.05F))
Logo( Logo(
modifier = Modifier modifier = Modifier
.weight(0.2F) .weight(0.2F)
.fillMaxWidth() .fillMaxWidth()
) )
Spacer(modifier = Modifier.weight(0.05F)) Spacer(modifier = Modifier.weight(0.05F))
val model: RegAssociationViewModel = viewModel() val model: RegAssociationViewModel = viewModel()
val scaffoldModel: ScaffoldModel = viewModel() val scaffoldModel: ScaffoldModel = viewModel()
BottomButton( val name by model.name.statusForm.observeAsState()
modifier = Modifier.fillMaxWidth(), val desc by model.name.statusForm.observeAsState()
confirmDesc = R.string.reg_btn val fileId by model.fileId.observeAsState()
) { BottomButton(
model.register { scaffoldModel.update(message = it) } modifier = Modifier.fillMaxWidth(),
} enabled = name == FormStatus.Valid && desc == FormStatus.Valid && fileId != null,
Spacer(modifier = Modifier.weight(0.05F)) 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(), scaffoldModel: ScaffoldModel = viewModel(),
modifier: Modifier modifier: Modifier
) { ) {
val photoIntent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI) val photoIntent = Intent(
photoIntent.type = "image/*" Intent.ACTION_PICK,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
)
val uri: Uri? by model.picture.observeAsState() val uri: Uri? by model.picture.observeAsState()
val resultLauncher = val resultLauncher =
rememberLauncherForActivityResult(contract = ActivityResultContracts.StartActivityForResult()) { rememberLauncherForActivityResult(contract = ActivityResultContracts.StartActivityForResult()) {
when (it.resultCode) { when (it.resultCode) {
Activity.RESULT_OK -> { 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 { } else {
uri.let { uri.let {
@ -175,17 +185,33 @@ class RegAssociationActivity : ComponentActivity() {
) )
.asImageBitmap(), contentDescription = null .asImageBitmap(), contentDescription = null
) )
IconButton(onClick = { Column(
loadPicture() modifier = Modifier.fillMaxHeight(),
}) { verticalArrangement = Arrangement.SpaceEvenly
Image( ) {
painter = painterResource(id = R.drawable.ic_exchange_rate), IconButton(onClick = {
contentDescription = null 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 { } else {
Text(text = model.errorPicture) Text(text = model.errorPicture)
} }

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

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

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

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

Loading…
Cancel
Save