团队项目冲刺-day2

每天举行站立式会议

昨天已完成的工作

成员 任务
徐嘉炜 组织会议,说明项目进度,指导项目发展
陈祥意 参与会议,简要讲述应用程序测试的各个模块
林楦 参与会议,讲述有关功能界面的UI开发
陈大锴 参与会议,协调开发技术与实际需求,记录需求
蔡家显 参与会议,讲述测试时的注意事项
陈祖民 参与会议,讲述有关登录界面的UI开发
肖商 参与会议,详细讲述测试模块的测试用例

今天计划完成的工作

成员 任务
徐嘉炜 开发登录界面交互逻辑
陈祥意 测试网络API
林楦 开发注册UI界面
陈大锴 开发注册界面交互逻辑
蔡家显 汇总测试记录
陈祖民 开发登录UI界面
肖商 生成各个测试模块的测试案例

工作中遇到的困难

UI界面与交互逻辑开发需要同步,所以需要UI组付出比较大的努力。

项目燃尽图

适当的项目程序/模块的最新(运行)截图

最新模块的代码

package com.timi.music

import android.os.Bundle
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContentView(R.layout.activity_main)
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
            val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
            insets
        }
    }
}






package com.timi.music

import android.content.Intent
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.View.OnClickListener
import android.view.ViewGroup
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.NavController
import androidx.navigation.Navigation
import com.google.android.material.snackbar.Snackbar
import com.timi.music.databinding.FragmentLoginBinding
import com.timi.utils.Logger
import com.timi.utils.isClickEffective
import com.timi.utils.makeToast


class LoginFragment : Fragment(), OnClickListener {

    companion object{
        private const val TAG = "LoginFragment"
    }
    private lateinit var binding: FragmentLoginBinding
    private lateinit var navController: NavController
    private val loginViewModel: LoginViewModel by lazy {
        ViewModelProvider(this)[LoginViewModel::class.java]
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        binding = FragmentLoginBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        navController = Navigation.findNavController(view)

        observeChange()
        initListener()
        if (Logger.currentLevel < 5){
            binding.btnLoginFragmentTest.visibility = View.GONE
        }
    }

    /**
     * 检测LoginViewModel中变量的变化
     */
    private fun observeChange(){
        loginViewModel.apply {
            //验证码发送返回结果
            captchaCode.observe(viewLifecycleOwner){
                if (it != 0){
                    if (it == 200){
                        Logger.i(TAG, "登录验证码发送成功.")
                        makeToast("验证码发送成功!")
                    }else{
                        Logger.i(TAG, "验证码发送失败.")
                        makeToast("验证码发送失败!请检查你的手机号是否正确!")
                    }
                    captchaCode.value = 0   //重置变量
                }
            }
            //游客登陆返回结果
            touristData.observe(viewLifecycleOwner){
                it?.apply {
                   if (this.code == 200){
                       //登录后跳转至主程序
                       Logger.i(TAG, "游客登陆成功.")
                       makeToast("游客登陆成功!")
                       //携带cookie跳转至CentreActivity
                       startActivity(Intent(context, CentreActivity::class.java).apply {
                           //toString防止cookie为null引发空指针异常
                           putExtra("cookie", cookie.toString())
                       })
                   }else{
                       Logger.w(TAG, "游客登陆失败.")
                       makeToast("游客登陆失败")
                   }
                   touristData.value = null
                }
            }

        }
    }

    private fun initListener(){
        binding.apply {
            btnLoginFragmentLogin.setOnClickListener(this@LoginFragment)
            btnLoginFragmentRegister.setOnClickListener(this@LoginFragment)
            btnLoginFragmentSendCode.setOnClickListener(this@LoginFragment)
            btnLoginFragmentTouristLogin.setOnClickListener(this@LoginFragment)
            binding.btnLoginFragmentTest.setOnClickListener(this@LoginFragment)
        }
    }

    override fun onClick(v: View?) {
        when(v){
            binding.btnLoginFragmentLogin -> {
                if (checkInput()){
//                    if (tryLogin()){
//
//                    }
                }
            }
            binding.btnLoginFragmentRegister -> navController.navigate(R.id.action_loginFragment_to_registerFragment)
            binding.btnLoginFragmentSendCode -> getCode()
            binding.btnLoginFragmentTouristLogin -> {
                if (v.isClickEffective()) loginViewModel.touristLoginRequest()
            }
            binding.btnLoginFragmentTest -> {
                if (v.isClickEffective()){
                    startActivity(Intent(context, CentreActivity::class.java).apply {
                        putExtra("cookie", "")
                    })
                }
            }
        }
    }

    private fun getCode(){
        if (binding.edtLoginFragmentPhoneNumber.text.length != 11){
            //如果输入格式不满足要求,无法获取验证码
            makeToast("请输入11位手机号!")
        }else{
            loginViewModel.codeRequest(binding.edtLoginFragmentPhoneNumber.text.toString())
        }
    }

    private fun checkInput(): Boolean{
        return if (binding.edtLoginFragmentPhoneNumber.text.toString().isNotEmpty()){
            if (binding.edtLoginFragmentCode.text.toString().isNotEmpty()){
                true
            }else{
                Snackbar.make(requireView(), "请输入验证码!", Snackbar.LENGTH_SHORT).show()
                false
            }
        }else{
            Snackbar.make(requireView(), "请输入手机号!", Snackbar.LENGTH_SHORT).show()
            false
        }
    }

//    private fun tryLogin(): Boolean{
//
//    }
}




package com.timi.music


import com.timi.music.bean.Captcha
import com.timi.music.bean.CaptchaService
import com.timi.music.bean.Tourist
import com.timi.music.bean.TouristService
import com.timi.utils.Logger
import com.timi.utils.ServiceBuilder
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response

class LoginModel {

    private val TAG = "LoginModel"

    /**
     * 发出获取验证码请求
     */
    fun codeRequest(
        loginVM: LoginViewModel,
        phoneNumber: String
    ){
        val captchaService = ServiceBuilder.create(CaptchaService::class.java)
        captchaService.getCaptchaData(phoneNumber).enqueue(object : Callback {
            override fun onResponse(call: Call, response: Response) {
                response.body()?.apply {
                    Logger.i(TAG, "注册验证码请求:$this")
                    loginVM.captchaCode.value = this.code
                }
            }
            override fun onFailure(call: Call, t: Throwable) {
                t.printStackTrace()
            }
        })
    }

    /**
     * 进行游客登陆
     */
    fun touristLoginRequest(loginVM: LoginViewModel){
        val touristService = ServiceBuilder.create(TouristService::class.java)
        touristService.getTouristData().enqueue(object : Callback{
            override fun onResponse(call: Call, response: Response) {
                response.body()?.apply {
                    Logger.i(TAG, "游客登陆请求:$this")
                    loginVM.touristData.value = this
                }
            }
            override fun onFailure(call: Call, t: Throwable) {
                t.printStackTrace()
            }
        })
    }
}




package com.timi.music

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.timi.music.bean.Tourist

class LoginViewModel: ViewModel() {

    private val TAG = "LoginViewModel"
    private val loginModel: LoginModel by lazy { LoginModel() }

    val captchaCode = MutableLiveData()        //验证码发送返回结果
    val touristData = MutableLiveData()        //游客登陆返回结果

    init {
        captchaCode.value = 0
        touristData.value = null
    }

    /**
     * 发出获取验证码请求
     */
    fun codeRequest(phoneNumber: String){
        loginModel.codeRequest(this, phoneNumber)
    }

    fun touristLoginRequest(){
        loginModel.touristLoginRequest(this)
    }
}




package com.timi.music

import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.View.OnClickListener
import android.view.ViewGroup
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.NavController
import androidx.navigation.Navigation
import com.timi.music.databinding.FragmentRegisterBinding
import com.timi.utils.Logger
import com.timi.utils.makeToast


class RegisterFragment : Fragment(), OnClickListener{

    companion object {
        private const val TAG = "RegisterFragment"
    }

    private lateinit var binding: FragmentRegisterBinding
    private lateinit var navigationController: NavController
    private val registerViewModel: RegisterViewModel by lazy {
        ViewModelProvider(this)[RegisterViewModel::class.java]
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        binding = FragmentRegisterBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        navigationController = Navigation.findNavController(view)

        observeChange()
        initListener()
    }

    /**
     * 检测RegisterViewModel中变量的变化
     */
    private fun observeChange(){
        registerViewModel.apply {
            captchaCode.observe(viewLifecycleOwner){
                if (it != 0){
                    if (it == 200){
                        Logger.i(TAG, "注册验证码发送成功.")
                        makeToast("验证码发送成功!")
                    }else{
                        Logger.i(TAG, "验证码发送失败.")
                        makeToast("验证码发送失败!请检查你的手机号是否正确!")
                    }
                    captchaCode.value = 0   //重置变量
                }
            }
        }
    }

    private fun initListener(){
        binding.ivRegisterFragmentBack.setOnClickListener(this)
        binding.btnRegisterFragmentGetCode.setOnClickListener(this)
    }

    override fun onClick(v: View?) {
        when(v){
            binding.ivRegisterFragmentBack -> navigationController.navigateUp() //返回到LoginFragment
            binding.btnRegisterFragmentGetCode -> getCode()     //请求发送注册验证码
        }
    }

    /**
     * 获取手机验证码
     */
    private fun getCode(){
        if (binding.edtRegisterFragmentPhoneNumber.text.length != 11){
            //如果输入格式不满足要求,无法获取验证码
            makeToast("请输入11位手机号!")
        }else{
            registerViewModel.codeRequest(binding.edtRegisterFragmentPhoneNumber.text.toString())
        }
    }
}




package com.timi.music

import android.util.Log
import com.timi.music.bean.Captcha
import com.timi.music.bean.CaptchaService
import com.timi.utils.ServiceBuilder
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response

class RegisterModel {

    private val TAG = "RegisterModel"

    /**
     * 发出获取验证码请求
     */
    fun codeRequest(
        registerVM: RegisterViewModel,
        phoneNumber: String
    ){
        val captchaService = ServiceBuilder.create(CaptchaService::class.java)
        captchaService.getCaptchaData(phoneNumber).enqueue(object : Callback{
            override fun onFailure(call: Call, t: Throwable) {
                t.printStackTrace()
            }
            override fun onResponse(call: Call, response: Response) {
                response.body()?.apply {
                    Log.i(TAG, "注册验证码请求:${this}")
                    registerVM.captchaCode.value = this.code
                }
            }
        })
    }
}




package com.timi.music

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel

class RegisterViewModel: ViewModel() {
    private val registerModel: RegisterModel by lazy { RegisterModel() }

    val captchaCode = MutableLiveData()        //验证码发送返回的结果

    init {
        captchaCode.value = 0
    }

    /**
     * 发出获取验证码请求
     */
    fun codeRequest(phoneNumber: String){
        registerModel.codeRequest(this, phoneNumber)
    }
}

运行结果的截图


每日每人总结

成员 总结
徐嘉炜 登录注册模块已经做得比较完善,团队各个小组都发挥了各自的优势
陈祥意 测试模块已经全部通过,可以进行下一个模块的测试
林楦 基础的UI界面已经定好,可以在不改变数据对象的前提下继续美化UI界面
陈大锴 代码在逻辑上基本没有问题,也说是有一点成就,希望下一个模块的开发也顺顺利利。
蔡家显 在测试的方式上,可以写一些自动化脚本进行测试,但是手写测试案例还是有用的,希望能够学到更多的测试方法
陈祖民 UI界面的交互比较简洁明了,是一个有点,可以往简约风格方面发展,期待主功能界面的UI界面开发
肖商 测试用例发送和接收的报文均为有效报文,测试成功的喜悦促使着我们进一步开发下一个模块
posted @ 2024-11-17 19:17  CDucK  阅读(12)  评论(0编辑  收藏  举报