Compare commits

...

No commits in common. 'master' and 'main' have entirely different histories.
master ... main

@ -1 +0,0 @@
PaymentApp

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetSelector">
<selectionStates>
<SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" />
</SelectionState>
</selectionStates>
</component>
</project>

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" default="true">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AutoImportSettings">
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="5ec2d936-8715-4e0b-8805-53fdae86e6b1" name="更改" comment="" />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$/.." />
</component>
<component name="KubernetesApiPersistence"><![CDATA[{}]]></component>
<component name="KubernetesApiProvider"><![CDATA[{
"isMigrated": true
}]]></component>
<component name="ProjectColorInfo"><![CDATA[{
"associatedIndex": 8
}]]></component>
<component name="ProjectId" id="353KvvnLsqiKIkfvLAIZPVYufRW" />
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent"><![CDATA[{
"keyToString": {
"ModuleVcsDetector.initialDetectionPerformed": "true",
"RunOnceActivity.ShowReadmeOnStart": "true",
"RunOnceActivity.git.unshallow": "true",
"com.intellij.ml.llm.matterhorn.ej.ui.settings.DefaultAutoModeForALLUsers.v1": "true",
"com.intellij.ml.llm.matterhorn.ej.ui.settings.DefaultModelSelectionForGA.v1": "true",
"git-widget-placeholder": "zhanghao__branch",
"ignore.virus.scanning.warn.message": "true",
"junie.onboarding.icon.badge.shown": "true",
"kotlin-language-version-configured": "true",
"last_opened_file_path": "C:/Users/22978/Desktop/mobile pay app/team_noob/MobilePayPrototype",
"node.js.detected.package.eslint": "true",
"node.js.detected.package.tslint": "true",
"node.js.selected.package.eslint": "(autodetect)",
"node.js.selected.package.tslint": "(autodetect)",
"nodejs_package_manager_path": "npm",
"to.speed.mode.migration.done": "true",
"vue.rearranger.settings.migration": "true"
}
}]]></component>
<component name="SharedIndexes">
<attachedChunks>
<set>
<option value="bundled-jdk-9823dce3aa75-bf35d07a577b-intellij.indexing.shared.core-IU-252.27397.103" />
<option value="bundled-js-predefined-d6986cc7102b-3aa1da707db6-JavaScript-IU-252.27397.103" />
</set>
</attachedChunks>
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="默认任务">
<changelist id="5ec2d936-8715-4e0b-8805-53fdae86e6b1" name="更改" comment="" />
<created>1762333548417</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1762333548417</updated>
<workItem from="1762333572907" duration="2380000" />
</task>
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="3" />
</component>
</project>

@ -0,0 +1,202 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ArtifactsWorkspaceSettings">
<artifacts-to-build>
<artifact name="MobilePayPrototype:war exploded" />
</artifacts-to-build>
</component>
<component name="AutoImportSettings">
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="29d2b17e-3dd5-4f22-94a5-3da90e02a79c" name="更改" comment="" />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES">
<list>
<option value="Class" />
<option value="Jsp File" />
</list>
</option>
</component>
<component name="KubernetesApiPersistence">{}</component>
<component name="KubernetesApiProvider">{
&quot;isMigrated&quot;: true
}</component>
<component name="LogFilters">
<option name="FILTER_ERRORS" value="false" />
<option name="FILTER_WARNINGS" value="false" />
<option name="FILTER_INFO" value="true" />
<option name="FILTER_DEBUG" value="true" />
<option name="CUSTOM_FILTER" />
</component>
<component name="ProjectColorInfo">{
&quot;associatedIndex&quot;: 3
}</component>
<component name="ProjectId" id="34KTet16frdjK92iA9fGK6TdnU3" />
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent">{
&quot;keyToString&quot;: {
&quot;ModuleVcsDetector.initialDetectionPerformed&quot;: &quot;true&quot;,
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
&quot;RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252&quot;: &quot;true&quot;,
&quot;Tomcat 服务器.Tomcat 10.1.47.executor&quot;: &quot;Run&quot;,
&quot;kotlin-language-version-configured&quot;: &quot;true&quot;,
&quot;last_opened_file_path&quot;: &quot;C:/Users/87641/Downloads/MobilePayPrototype&quot;,
&quot;node.js.detected.package.eslint&quot;: &quot;true&quot;,
&quot;node.js.detected.package.tslint&quot;: &quot;true&quot;,
&quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;,
&quot;node.js.selected.package.tslint&quot;: &quot;(autodetect)&quot;,
&quot;nodejs_package_manager_path&quot;: &quot;npm&quot;,
&quot;project.structure.last.edited&quot;: &quot;模块&quot;,
&quot;project.structure.proportion&quot;: &quot;0.0&quot;,
&quot;project.structure.side.proportion&quot;: &quot;0.2&quot;,
&quot;settings.editor.selected.configurable&quot;: &quot;preferences.pluginManager&quot;,
&quot;vue.rearranger.settings.migration&quot;: &quot;true&quot;
}
}</component>
<component name="RunDashboard">
<option name="configurationStatuses">
<map>
<entry key="#com.intellij.j2ee.web.tomcat.TomcatRunConfigurationFactory">
<value>
<map>
<entry key="Tomcat 10.1.47" value="STOPPED" />
</map>
</value>
</entry>
</map>
</option>
</component>
<component name="RunManager">
<configuration name="Tomcat 10.1.47" type="#com.intellij.j2ee.web.tomcat.TomcatRunConfigurationFactory" factoryName="Local" APPLICATION_SERVER_NAME="Tomcat 10.1.47" ALTERNATIVE_JRE_ENABLED="false" nameIsGenerated="true">
<option name="BROWSER_ID" value="98ca6316-2f89-46d9-a9e5-fa9e2b0625b3" />
<option name="UPDATING_POLICY" value="restart-server" />
<deployment>
<artifact name="MobilePayPrototype:war exploded">
<settings>
<option name="CONTEXT_PATH" value="/MobilePayPrototype_war_exploded" />
</settings>
</artifact>
</deployment>
<server-settings>
<option name="BASE_DIRECTORY_NAME" value="83ee14db-001f-45c9-801f-acd24919ac11" />
</server-settings>
<predefined_log_file enabled="true" id="Tomcat" />
<predefined_log_file enabled="true" id="Tomcat Catalina" />
<predefined_log_file id="Tomcat Manager" />
<predefined_log_file id="Tomcat Host Manager" />
<predefined_log_file id="Tomcat Localhost Access" />
<RunnerSettings RunnerId="Debug">
<option name="DEBUG_PORT" value="8809" />
</RunnerSettings>
<ConfigurationWrapper VM_VAR="JAVA_OPTS" RunnerId="Cover">
<option name="USE_ENV_VARIABLES" value="true" />
<STARTUP>
<option name="USE_DEFAULT" value="true" />
<option name="SCRIPT" value="" />
<option name="VM_PARAMETERS" value="" />
<option name="PROGRAM_PARAMETERS" value="" />
</STARTUP>
<SHUTDOWN>
<option name="USE_DEFAULT" value="true" />
<option name="SCRIPT" value="" />
<option name="VM_PARAMETERS" value="" />
<option name="PROGRAM_PARAMETERS" value="" />
</SHUTDOWN>
</ConfigurationWrapper>
<ConfigurationWrapper VM_VAR="JAVA_OPTS" RunnerId="Debug">
<option name="USE_ENV_VARIABLES" value="true" />
<STARTUP>
<option name="USE_DEFAULT" value="true" />
<option name="SCRIPT" value="" />
<option name="VM_PARAMETERS" value="" />
<option name="PROGRAM_PARAMETERS" value="" />
</STARTUP>
<SHUTDOWN>
<option name="USE_DEFAULT" value="true" />
<option name="SCRIPT" value="" />
<option name="VM_PARAMETERS" value="" />
<option name="PROGRAM_PARAMETERS" value="" />
</SHUTDOWN>
</ConfigurationWrapper>
<ConfigurationWrapper VM_VAR="JAVA_OPTS" RunnerId="Profile">
<option name="USE_ENV_VARIABLES" value="true" />
<STARTUP>
<option name="USE_DEFAULT" value="true" />
<option name="SCRIPT" value="" />
<option name="VM_PARAMETERS" value="" />
<option name="PROGRAM_PARAMETERS" value="" />
</STARTUP>
<SHUTDOWN>
<option name="USE_DEFAULT" value="true" />
<option name="SCRIPT" value="" />
<option name="VM_PARAMETERS" value="" />
<option name="PROGRAM_PARAMETERS" value="" />
</SHUTDOWN>
</ConfigurationWrapper>
<ConfigurationWrapper VM_VAR="JAVA_OPTS" RunnerId="Run">
<option name="USE_ENV_VARIABLES" value="true" />
<STARTUP>
<option name="USE_DEFAULT" value="true" />
<option name="SCRIPT" value="" />
<option name="VM_PARAMETERS" value="" />
<option name="PROGRAM_PARAMETERS" value="" />
</STARTUP>
<SHUTDOWN>
<option name="USE_DEFAULT" value="true" />
<option name="SCRIPT" value="" />
<option name="VM_PARAMETERS" value="" />
<option name="PROGRAM_PARAMETERS" value="" />
</SHUTDOWN>
</ConfigurationWrapper>
<method v="2">
<option name="Make" enabled="true" />
<option name="BuildArtifacts" enabled="true">
<artifact name="MobilePayPrototype:war exploded" />
</option>
</method>
</configuration>
</component>
<component name="SharedIndexes">
<attachedChunks>
<set>
<option value="bundled-jdk-9823dce3aa75-bf35d07a577b-intellij.indexing.shared.core-IU-252.26830.84" />
<option value="bundled-js-predefined-d6986cc7102b-3aa1da707db6-JavaScript-IU-252.26830.84" />
</set>
</attachedChunks>
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="默认任务">
<changelist id="29d2b17e-3dd5-4f22-94a5-3da90e02a79c" name="更改" comment="" />
<created>1760961364824</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1760961364824</updated>
<workItem from="1760961365755" duration="97000" />
<workItem from="1760961474830" duration="148000" />
<workItem from="1760962006950" duration="1217000" />
<workItem from="1761200575524" duration="627000" />
<workItem from="1761202004904" duration="285000" />
<workItem from="1761203404557" duration="18000" />
<workItem from="1761295487704" duration="8022000" />
<workItem from="1761483085466" duration="99000" />
<workItem from="1761745663768" duration="2912000" />
<workItem from="1761784691352" duration="1429000" />
<workItem from="1761814006991" duration="565000" />
<workItem from="1762047486066" duration="8669000" />
<workItem from="1762152467619" duration="3073000" />
</task>
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="3" />
</component>
</project>

@ -1,61 +0,0 @@
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.compose)
}
android {
namespace 'com.payment'
compileSdk 36
defaultConfig {
applicationId "com.payment"
minSdk 24
targetSdk 36
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = '11'
}
buildFeatures {
compose true
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.9.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
// Jetpack
implementation 'androidx.lifecycle:lifecycle-viewmodel:2.6.2'
implementation 'androidx.lifecycle:lifecycle-livedata:2.6.2'
implementation 'androidx.fragment:fragment:1.6.1'
// -
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.okhttp3:okhttp:4.11.0'
implementation 'com.squareup.okhttp3:logging-interceptor:4.11.0'
implementation 'com.squareup.okhttp3:mockwebserver:4.11.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}

@ -1,37 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.payment">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:name=".MyApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/Theme.AppCompat.Light.DarkActionBar">
<!-- 启动Activity -->
<activity
android:name=".user.LoginActivity"
android:exported="true"
android:theme="@style/Theme.AppCompat.Light.DarkActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- 其他Activity -->
<activity android:name=".ui.MainActivity" />
<activity android:name=".user.RegisterActivity" />
<activity android:name=".user.RealNameAuthActivity" />
<activity android:name=".user.AuditStatusActivity" />
<activity android:name=".asset.BalanceActivity" />
<activity android:name=".asset.BankCardListActivity" />
<activity android:name=".asset.AddBankCardActivity" />
<activity android:name=".asset.RechargeWithdrawActivity" />
</application>
</manifest>

@ -1,19 +0,0 @@
package com.payment;
import android.app.Application;
import android.content.Context;
public class MyApplication extends Application {
private static Context context;
@Override
public void onCreate() {
super.onCreate();
context = getApplicationContext();
}
// 提供全局上下文
public static Context getContext() {
return context;
}
}

@ -1,63 +0,0 @@
package com.payment.asset;
import android.os.Bundle;
import android.widget.Button;
import android.widget.Toast;
import androidx.lifecycle.ViewModelProvider;
import com.google.android.material.textfield.TextInputEditText;
import com.payment.base.BaseActivity;
import com.payment.R;
public class AddBankCardActivity extends BaseActivity {
private TextInputEditText etCardNo, etBankName;
private Button btnSubmit;
private AssetViewModel assetViewModel;
@Override
protected int getLayoutId() {
return R.layout.activity_add_bank_card;
}
@Override
protected void initView() {
etCardNo = findViewById(R.id.et_card_no);
etBankName = findViewById(R.id.et_bank_name);
btnSubmit = findViewById(R.id.btn_submit);
}
@Override
protected void initData() {
assetViewModel = new ViewModelProvider(this).get(AssetViewModel.class);
// 观察添加银行卡结果 - 使用正确的方法名
assetViewModel.getAddBankCardResult().observe(this, resultBean -> {
if (resultBean != null && resultBean.isSuccess()) {
showToast("添加银行卡成功");
finish();
} else {
showToast("添加银行卡失败:" + (resultBean != null ? resultBean.getMsg() : "网络异常"));
}
});
}
@Override
protected void bindEvent() {
btnSubmit.setOnClickListener(v -> {
String cardNo = etCardNo.getText().toString().trim();
String bankName = etBankName.getText().toString().trim();
if (cardNo.isEmpty() || bankName.isEmpty()) {
showToast("请完善银行卡信息");
return;
}
if (cardNo.length() < 16) {
showToast("请输入正确的银行卡号");
return;
}
// 调用添加银行卡接口
assetViewModel.addBankCard(cardNo, bankName);
});
}
}

@ -1,253 +0,0 @@
package com.payment.asset;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import com.payment.MyApplication;
import com.payment.network.ApiService;
import com.payment.network.RetrofitManager;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
/**
* ViewModel
*/
public class AssetViewModel extends ViewModel {
// ========== 余额相关 ==========
private MutableLiveData<ApiService.ResultBean<ApiService.BalanceBean>> balanceResult = new MutableLiveData<>();
public LiveData<ApiService.ResultBean<ApiService.BalanceBean>> getBalanceResult() {
return balanceResult;
}
public void getBalance() {
ApiService apiService = RetrofitManager.getApiService(MyApplication.getContext());
apiService.getBalance().enqueue(new Callback<ApiService.ResultBean<ApiService.BalanceBean>>() {
@Override
public void onResponse(Call<ApiService.ResultBean<ApiService.BalanceBean>> call, Response<ApiService.ResultBean<ApiService.BalanceBean>> response) {
if (response.isSuccessful() && response.body() != null) {
balanceResult.postValue(response.body());
} else {
ApiService.ResultBean<ApiService.BalanceBean> errorResult = new ApiService.ResultBean<>();
errorResult.setSuccess(false);
errorResult.setMsg("获取余额失败");
balanceResult.postValue(errorResult);
}
}
@Override
public void onFailure(Call<ApiService.ResultBean<ApiService.BalanceBean>> call, Throwable t) {
ApiService.ResultBean<ApiService.BalanceBean> errorResult = new ApiService.ResultBean<>();
errorResult.setSuccess(false);
errorResult.setMsg("网络错误:" + t.getMessage());
balanceResult.postValue(errorResult);
}
});
}
// ========== 银行卡列表相关 ==========
private MutableLiveData<ApiService.ResultBean<ApiService.BankCardListBean>> bankCardListResult = new MutableLiveData<>();
public LiveData<ApiService.ResultBean<ApiService.BankCardListBean>> getBankCardListResult() {
return bankCardListResult;
}
public void getBankCardList() {
ApiService apiService = RetrofitManager.getApiService(MyApplication.getContext());
apiService.getBankCardList().enqueue(new Callback<ApiService.ResultBean<ApiService.BankCardListBean>>() {
@Override
public void onResponse(Call<ApiService.ResultBean<ApiService.BankCardListBean>> call, Response<ApiService.ResultBean<ApiService.BankCardListBean>> response) {
if (response.isSuccessful() && response.body() != null) {
bankCardListResult.postValue(response.body());
} else {
ApiService.ResultBean<ApiService.BankCardListBean> errorResult = new ApiService.ResultBean<>();
errorResult.setSuccess(false);
errorResult.setMsg("获取银行卡列表失败");
bankCardListResult.postValue(errorResult);
}
}
@Override
public void onFailure(Call<ApiService.ResultBean<ApiService.BankCardListBean>> call, Throwable t) {
ApiService.ResultBean<ApiService.BankCardListBean> errorResult = new ApiService.ResultBean<>();
errorResult.setSuccess(false);
errorResult.setMsg("网络错误:" + t.getMessage());
bankCardListResult.postValue(errorResult);
}
});
}
// ========== 添加银行卡相关 ==========
private MutableLiveData<ApiService.ResultBean<ApiService.BaseResponse>> addBankCardResult = new MutableLiveData<>();
public LiveData<ApiService.ResultBean<ApiService.BaseResponse>> getAddBankCardResult() {
return addBankCardResult;
}
public void addBankCard(String cardNo, String bankName) {
ApiService apiService = RetrofitManager.getApiService(MyApplication.getContext());
ApiService.AddBankCardRequest request = new ApiService.AddBankCardRequest(cardNo, bankName);
apiService.addBankCard(request).enqueue(new Callback<ApiService.ResultBean<ApiService.BaseResponse>>() {
@Override
public void onResponse(Call<ApiService.ResultBean<ApiService.BaseResponse>> call, Response<ApiService.ResultBean<ApiService.BaseResponse>> response) {
if (response.isSuccessful() && response.body() != null) {
addBankCardResult.postValue(response.body());
} else {
ApiService.ResultBean<ApiService.BaseResponse> errorResult = new ApiService.ResultBean<>();
errorResult.setSuccess(false);
errorResult.setMsg("添加银行卡失败");
addBankCardResult.postValue(errorResult);
}
}
@Override
public void onFailure(Call<ApiService.ResultBean<ApiService.BaseResponse>> call, Throwable t) {
ApiService.ResultBean<ApiService.BaseResponse> errorResult = new ApiService.ResultBean<>();
errorResult.setSuccess(false);
errorResult.setMsg("网络错误:" + t.getMessage());
addBankCardResult.postValue(errorResult);
}
});
}
// ========== 充值相关 ==========
private MutableLiveData<ApiService.ResultBean<ApiService.BaseResponse>> rechargeResult = new MutableLiveData<>();
public LiveData<ApiService.ResultBean<ApiService.BaseResponse>> getRechargeResult() {
return rechargeResult;
}
public void recharge(double amount, String cardId) {
ApiService apiService = RetrofitManager.getApiService(MyApplication.getContext());
ApiService.RechargeWithdrawRequest request = new ApiService.RechargeWithdrawRequest(String.valueOf(amount), cardId);
apiService.recharge(request).enqueue(new Callback<ApiService.ResultBean<ApiService.BaseResponse>>() {
@Override
public void onResponse(Call<ApiService.ResultBean<ApiService.BaseResponse>> call, Response<ApiService.ResultBean<ApiService.BaseResponse>> response) {
if (response.isSuccessful() && response.body() != null) {
rechargeResult.postValue(response.body());
} else {
ApiService.ResultBean<ApiService.BaseResponse> errorResult = new ApiService.ResultBean<>();
errorResult.setSuccess(false);
errorResult.setMsg("充值失败");
rechargeResult.postValue(errorResult);
}
}
@Override
public void onFailure(Call<ApiService.ResultBean<ApiService.BaseResponse>> call, Throwable t) {
ApiService.ResultBean<ApiService.BaseResponse> errorResult = new ApiService.ResultBean<>();
errorResult.setSuccess(false);
errorResult.setMsg("网络错误:" + t.getMessage());
rechargeResult.postValue(errorResult);
}
});
}
// ========== 提现相关 ==========
private MutableLiveData<ApiService.ResultBean<ApiService.BaseResponse>> withdrawResult = new MutableLiveData<>();
public LiveData<ApiService.ResultBean<ApiService.BaseResponse>> getWithdrawResult() {
return withdrawResult;
}
public void withdraw(double amount, String cardId) {
ApiService apiService = RetrofitManager.getApiService(MyApplication.getContext());
ApiService.RechargeWithdrawRequest request = new ApiService.RechargeWithdrawRequest(String.valueOf(amount), cardId);
apiService.withdraw(request).enqueue(new Callback<ApiService.ResultBean<ApiService.BaseResponse>>() {
@Override
public void onResponse(Call<ApiService.ResultBean<ApiService.BaseResponse>> call, Response<ApiService.ResultBean<ApiService.BaseResponse>> response) {
if (response.isSuccessful() && response.body() != null) {
withdrawResult.postValue(response.body());
} else {
ApiService.ResultBean<ApiService.BaseResponse> errorResult = new ApiService.ResultBean<>();
errorResult.setSuccess(false);
errorResult.setMsg("提现失败");
withdrawResult.postValue(errorResult);
}
}
@Override
public void onFailure(Call<ApiService.ResultBean<ApiService.BaseResponse>> call, Throwable t) {
ApiService.ResultBean<ApiService.BaseResponse> errorResult = new ApiService.ResultBean<>();
errorResult.setSuccess(false);
errorResult.setMsg("网络错误:" + t.getMessage());
withdrawResult.postValue(errorResult);
}
});
}
// ========== 设置默认银行卡相关 ==========
private MutableLiveData<ApiService.ResultBean<ApiService.BaseResponse>> defaultCardResult = new MutableLiveData<>();
public LiveData<ApiService.ResultBean<ApiService.BaseResponse>> getDefaultCardResult() {
return defaultCardResult;
}
public void setDefaultCard(int cardId) {
ApiService apiService = RetrofitManager.getApiService(MyApplication.getContext());
ApiService.DefaultBankCardRequest request = new ApiService.DefaultBankCardRequest(String.valueOf(cardId));
apiService.setDefaultBankCard(request).enqueue(new Callback<ApiService.ResultBean<ApiService.BaseResponse>>() {
@Override
public void onResponse(Call<ApiService.ResultBean<ApiService.BaseResponse>> call, Response<ApiService.ResultBean<ApiService.BaseResponse>> response) {
if (response.isSuccessful() && response.body() != null) {
defaultCardResult.postValue(response.body());
} else {
ApiService.ResultBean<ApiService.BaseResponse> errorResult = new ApiService.ResultBean<>();
errorResult.setSuccess(false);
errorResult.setMsg("设置默认银行卡失败");
defaultCardResult.postValue(errorResult);
}
}
@Override
public void onFailure(Call<ApiService.ResultBean<ApiService.BaseResponse>> call, Throwable t) {
ApiService.ResultBean<ApiService.BaseResponse> errorResult = new ApiService.ResultBean<>();
errorResult.setSuccess(false);
errorResult.setMsg("网络错误:" + t.getMessage());
defaultCardResult.postValue(errorResult);
}
});
}
// ========== 删除银行卡相关 ==========
private MutableLiveData<ApiService.ResultBean<ApiService.BaseResponse>> deleteCardResult = new MutableLiveData<>();
public LiveData<ApiService.ResultBean<ApiService.BaseResponse>> getDeleteCardResult() {
return deleteCardResult;
}
public void deleteBankCard(int cardId) {
ApiService apiService = RetrofitManager.getApiService(MyApplication.getContext());
ApiService.DeleteBankCardRequest request = new ApiService.DeleteBankCardRequest(String.valueOf(cardId));
apiService.deleteBankCard(request).enqueue(new Callback<ApiService.ResultBean<ApiService.BaseResponse>>() {
@Override
public void onResponse(Call<ApiService.ResultBean<ApiService.BaseResponse>> call, Response<ApiService.ResultBean<ApiService.BaseResponse>> response) {
if (response.isSuccessful() && response.body() != null) {
deleteCardResult.postValue(response.body());
} else {
ApiService.ResultBean<ApiService.BaseResponse> errorResult = new ApiService.ResultBean<>();
errorResult.setSuccess(false);
errorResult.setMsg("删除银行卡失败");
deleteCardResult.postValue(errorResult);
}
}
@Override
public void onFailure(Call<ApiService.ResultBean<ApiService.BaseResponse>> call, Throwable t) {
ApiService.ResultBean<ApiService.BaseResponse> errorResult = new ApiService.ResultBean<>();
errorResult.setSuccess(false);
errorResult.setMsg("网络错误:" + t.getMessage());
deleteCardResult.postValue(errorResult);
}
});
}
}

@ -1,43 +0,0 @@
package com.payment.asset;
import android.os.Bundle;
import android.widget.TextView;
import androidx.lifecycle.ViewModelProvider;
import com.payment.base.BaseActivity;
import com.payment.R;
public class BalanceActivity extends BaseActivity {
private TextView tvBalance;
// 移除 tvUpdateTime 的声明
private AssetViewModel assetViewModel;
@Override
protected int getLayoutId() {
return R.layout.activity_balance;
}
@Override
protected void initView() {
tvBalance = findViewById(R.id.tv_balance);
// 移除 tvUpdateTime 的初始化
}
@Override
protected void initData() {
assetViewModel = new ViewModelProvider(this).get(AssetViewModel.class);
// 查询余额
assetViewModel.getBalance();
// 观察余额结果
assetViewModel.getBalanceResult().observe(this, resultBean -> {
if (resultBean != null && resultBean.isSuccess()) {
String balance = resultBean.getData().getBalance();
tvBalance.setText("¥ " + balance);
// 移除更新时间显示
} else {
showToast("获取余额失败");
}
});
}
}

@ -1,90 +0,0 @@
package com.payment.asset;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.payment.R;
import com.payment.network.ApiService;
import java.util.List;
public class BankCardAdapter extends RecyclerView.Adapter<BankCardAdapter.ViewHolder> {
private Context context;
private List<ApiService.BankCardBean> cardList; // 修改为 ApiService.BankCardBean
private OnItemActionListener listener;
public BankCardAdapter(Context context, List<ApiService.BankCardBean> cardList) {
this.context = context;
this.cardList = cardList;
}
public void updateData(List<ApiService.BankCardBean> cardList) { // 修改参数类型
this.cardList = cardList;
notifyDataSetChanged();
}
public void setOnItemActionListener(OnItemActionListener listener) {
this.listener = listener;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.item_bank_card, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
ApiService.BankCardBean card = cardList.get(position); // 修改类型
holder.tvBankName.setText(card.getBankName());
holder.tvCardNo.setText("**** **** **** " + card.getCardNo().substring(card.getCardNo().length() - 4));
if (card.isDefault()) {
holder.btnSetDefault.setVisibility(View.GONE);
holder.tvDefault.setVisibility(View.VISIBLE);
} else {
holder.btnSetDefault.setVisibility(View.VISIBLE);
holder.tvDefault.setVisibility(View.GONE);
}
holder.btnSetDefault.setOnClickListener(v -> {
if (listener != null) {
listener.onAction(card, "setDefault");
}
});
holder.btnDelete.setOnClickListener(v -> {
if (listener != null) {
listener.onAction(card, "delete");
}
});
}
@Override
public int getItemCount() {
return cardList == null ? 0 : cardList.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
TextView tvBankName, tvCardNo, tvDefault;
Button btnSetDefault, btnDelete;
public ViewHolder(@NonNull View itemView) {
super(itemView);
tvBankName = itemView.findViewById(R.id.tv_bank_name);
tvCardNo = itemView.findViewById(R.id.tv_card_no);
tvDefault = itemView.findViewById(R.id.tv_default);
btnSetDefault = itemView.findViewById(R.id.btn_set_default);
btnDelete = itemView.findViewById(R.id.btn_delete);
}
}
public interface OnItemActionListener {
void onAction(ApiService.BankCardBean card, String action); // 修改参数类型
}
}

@ -1,95 +0,0 @@
package com.payment.asset;
import com.payment.base.BaseActivity;
import com.payment.R;
import com.payment.model.BankCard;
import com.payment.utils.IntentUtils;
import com.payment.utils.ToastUtils;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import java.util.List;
import com.payment.network.ApiService;
import java.util.Arrays;
public class BankCardListActivity extends BaseActivity {
private RecyclerView rvBankCards;
private Button btnAddCard;
private TextView tvEmpty;
private BankCardAdapter adapter;
private AssetViewModel assetViewModel;
@Override
protected int getLayoutId() {
return R.layout.activity_bank_card_list;
}
@Override
protected void initView() {
rvBankCards = findViewById(R.id.rv_bank_cards);
btnAddCard = findViewById(R.id.btn_add_card);
tvEmpty = findViewById(R.id.tv_empty);
// 初始化RecyclerView
rvBankCards.setLayoutManager(new LinearLayoutManager(this));
adapter = new BankCardAdapter(this, null);
rvBankCards.setAdapter(adapter);
}
@Override
protected void initData() {
assetViewModel = new ViewModelProvider(this).get(AssetViewModel.class);
// 获取银行卡列表
assetViewModel.getBankCardList();
// 观察列表结果
assetViewModel.getBankCardListResult().observe(this, resultBean -> {
if (resultBean != null && resultBean.isSuccess()) {
// 从 BankCardListBean 中获取 cards 数组
ApiService.BankCardListBean bankCardListBean = resultBean.getData();
if (bankCardListBean != null && bankCardListBean.getCards() != null) {
// 使用正确的类型ApiService.BankCardBean
List<ApiService.BankCardBean> cardList = java.util.Arrays.asList(bankCardListBean.getCards());
if (!cardList.isEmpty()) {
rvBankCards.setVisibility(View.VISIBLE);
tvEmpty.setVisibility(View.GONE);
adapter.updateData(cardList);
} else {
rvBankCards.setVisibility(View.GONE);
tvEmpty.setVisibility(View.VISIBLE);
}
} else {
rvBankCards.setVisibility(View.GONE);
tvEmpty.setVisibility(View.VISIBLE);
}
} else {
ToastUtils.showToast(this, "获取银行卡列表失败");
}
});
// ... 其他观察代码保持不变
}
@Override
protected void bindEvent() {
// 添加银行卡
btnAddCard.setOnClickListener(v -> {
IntentUtils.jumpTo(this, AddBankCardActivity.class, false);
});
// 列表项点击事件(设置默认卡/删除)
adapter.setOnItemActionListener((card, action) -> {
switch (action) {
case "setDefault":
assetViewModel.setDefaultCard(card.getId());
break;
case "delete":
assetViewModel.deleteBankCard(card.getId());
break;
}
});
}
}

@ -1,188 +0,0 @@
package com.payment.asset;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.Spinner;
import android.widget.TextView;
import androidx.lifecycle.ViewModelProvider;
import com.google.android.material.textfield.TextInputEditText;
import com.payment.base.BaseActivity;
import com.payment.R;
import com.payment.model.BankCard;
import java.util.List;
import com.payment.network.ApiService; // 添加这行
import java.util.Arrays; // 添加这行
public class RechargeWithdrawActivity extends BaseActivity {
private TextView tvTitle;
private TextInputEditText etAmount;
private Spinner spinnerBankCards;
private Button btnConfirm;
private AssetViewModel assetViewModel;
private String type; // "recharge" 或 "withdraw"
@Override
protected int getLayoutId() {
return R.layout.activity_recharge_withdraw;
}
@Override
protected void initView() {
tvTitle = findViewById(R.id.tv_title);
etAmount = findViewById(R.id.et_amount);
spinnerBankCards = findViewById(R.id.spinner_bank_cards);
btnConfirm = findViewById(R.id.btn_confirm);
}
@Override
protected void initData() {
// 获取传递的类型参数
type = getIntent().getStringExtra("type");
// 设置标题
if ("recharge".equals(type)) {
tvTitle.setText("充值");
btnConfirm.setText("确认充值");
} else if ("withdraw".equals(type)) {
tvTitle.setText("提现");
btnConfirm.setText("确认提现");
}
assetViewModel = new ViewModelProvider(this).get(AssetViewModel.class);
// 获取银行卡列表
assetViewModel.getBankCardList();
// 观察银行卡列表结果
assetViewModel.getBankCardListResult().observe(this, resultBean -> {
if (resultBean != null && resultBean.isSuccess()) {
// 从 BankCardListBean 中获取 cards 数组
ApiService.BankCardListBean bankCardListBean = resultBean.getData();
if (bankCardListBean != null && bankCardListBean.getCards() != null) {
// 使用正确的类型
List<ApiService.BankCardBean> cardList = Arrays.asList(bankCardListBean.getCards());
setupBankCardSpinner(cardList);
} else {
showToast("暂无银行卡,请先添加银行卡");
btnConfirm.setEnabled(false);
}
} else {
showToast("获取银行卡列表失败");
}
});
// 观察充值/提现结果
if ("recharge".equals(type)) {
assetViewModel.getRechargeResult().observe(this, resultBean -> {
if (resultBean != null && resultBean.isSuccess()) {
showToast("充值成功");
finish();
} else {
showToast("充值失败");
}
});
} else if ("withdraw".equals(type)) {
assetViewModel.getWithdrawResult().observe(this, resultBean -> {
if (resultBean != null && resultBean.isSuccess()) {
showToast("提现成功");
finish();
} else {
showToast("提现失败");
}
});
}
}
@Override
protected void bindEvent() {
btnConfirm.setOnClickListener(v -> {
String amountStr = etAmount.getText().toString().trim();
if (amountStr.isEmpty()) {
showToast("请输入金额");
return;
}
try {
double amount = Double.parseDouble(amountStr);
if (amount <= 0) {
showToast("金额必须大于0");
return;
}
if (spinnerBankCards.getSelectedItem() == null) {
showToast("请选择银行卡");
return;
}
// 获取选中的银行卡 - 使用正确的类型
ApiService.BankCardBean selectedCard = (ApiService.BankCardBean) spinnerBankCards.getSelectedItem();
String cardId = String.valueOf(selectedCard.getId());
// 调用充值或提现接口 - 传递 double 类型
if ("recharge".equals(type)) {
assetViewModel.recharge(amount, cardId);
} else if ("withdraw".equals(type)) {
assetViewModel.withdraw(amount, cardId);
}
} catch (NumberFormatException e) {
showToast("请输入有效的金额格式");
}
});
}
/**
*
*/
private void setupBankCardSpinner(List<ApiService.BankCardBean> cardList) { // 修改参数类型
if (cardList != null && !cardList.isEmpty()) {
// 创建适配器显示银行名称和卡号后4位
ArrayAdapter<ApiService.BankCardBean> adapter = new ArrayAdapter<ApiService.BankCardBean>(this,
android.R.layout.simple_spinner_item, cardList) {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
TextView view = (TextView) super.getView(position, convertView, parent);
ApiService.BankCardBean card = getItem(position); // 修改类型
if (card != null) {
String displayText = card.getBankName() + " (****" +
getLastFourDigits(card.getCardNo()) + ")";
view.setText(displayText);
}
return view;
}
@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
TextView view = (TextView) super.getDropDownView(position, convertView, parent);
ApiService.BankCardBean card = getItem(position); // 修改类型
if (card != null) {
String displayText = card.getBankName() + " (****" +
getLastFourDigits(card.getCardNo()) + ")";
view.setText(displayText);
}
return view;
}
};
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinnerBankCards.setAdapter(adapter);
} else {
showToast("暂无银行卡,请先添加银行卡");
btnConfirm.setEnabled(false);
}
}
/**
* 4
*/
private String getLastFourDigits(String cardNo) {
if (cardNo != null && cardNo.length() >= 4) {
return cardNo.substring(cardNo.length() - 4);
}
return "****";
}
}

@ -1,48 +0,0 @@
package com.payment.base;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.payment.utils.ToastUtils;
public abstract class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 设置布局
setContentView(getLayoutId());
// 初始化视图
initView();
// 初始化数据
initData();
// 绑定事件
bindEvent();
}
// 获取布局ID子类实现
protected abstract int getLayoutId();
// 初始化视图(子类按需实现)
protected abstract void initView();
// 初始化数据(子类按需实现)
protected void initData() {}
// 绑定事件(子类按需实现)
protected void bindEvent() {}
// 简化Toast调用
protected void showToast(String msg) {
ToastUtils.showToast(this, msg);
}
// 登录过期处理(统一跳转登录页)
protected void handleLoginExpired() {
showToast("登录已过期,请重新登录");
// 清除Token
com.payment.utils.TokenUtils.clearToken(this);
// 跳转到登录页关闭所有栈内Activity
com.payment.utils.IntentUtils.jumpTo(this, com.payment.user.LoginActivity.class, true);
finishAffinity();
}
}

@ -1,40 +0,0 @@
package com.payment.base;
import androidx.lifecycle.ViewModel;
// 在现有导入基础上补充以下两行
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.LiveData;
import com.payment.utils.ToastUtils;
public abstract class BaseViewModel extends ViewModel {
// 加载状态true-加载中false-加载完成)
private final MutableLiveData<Boolean> loadingState = new MutableLiveData<>();
// 错误信息
private final MutableLiveData<String> errorMsg = new MutableLiveData<>();
// 获取加载状态观察器
public LiveData<Boolean> getLoadingState() {
return loadingState;
}
// 获取错误信息观察器
public LiveData<String> getErrorMsg() {
return errorMsg;
}
// 显示加载
protected void showLoading() {
loadingState.postValue(true);
}
// 隐藏加载
protected void hideLoading() {
loadingState.postValue(false);
}
// 发送错误信息
protected void sendErrorMsg(String msg) {
errorMsg.postValue(msg);
ToastUtils.showToast(com.payment.MyApplication.getContext(), msg);
}
}

@ -1,10 +0,0 @@
package com.payment.base;
public class NetConfig {
// 模拟服务器BaseUrlMockWebServer
public static final String BASE_URL = "http://10.0.2.2:8080/";
// 后续对接真实后端的BaseUrl预留
public static final String REAL_BASE_URL = "http://10.0.2.2:8080/";
// 网络请求超时时间(秒)
public static final int TIMEOUT = 10;
}

@ -1,18 +0,0 @@
package com.payment.base;
public class ResultBean<T> {
private int code; // 响应码200成功其他失败
private String msg; // 响应信息
private T data; // 响应数据
// getter + setter
public int getCode() { return code; }
public void setCode(int code) { this.code = code; }
public String getMsg() { return msg; }
public void setMsg(String msg) { this.msg = msg; }
public T getData() { return data; }
public void setData(T data) { this.data = data; }
// 快捷判断成功方法
public boolean isSuccess() { return code == 200; }
}

@ -1,12 +0,0 @@
package com.payment.model;
public class Balance {
private double balance; // 账户余额
private String updateTime; // 最后更新时间
// getter + setter
public double getBalance() { return balance; }
public void setBalance(double balance) { this.balance = balance; }
public String getUpdateTime() { return updateTime; }
public void setUpdateTime(String updateTime) { this.updateTime = updateTime; }
}

@ -1,18 +0,0 @@
package com.payment.model;
public class BankCard {
private int id; // 银行卡ID
private String cardNo; // 银行卡号(脱敏)
private String bankName; // 银行名称
private boolean isDefault; // 是否默认卡
// getter + setter
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getCardNo() { return cardNo; }
public void setCardNo(String cardNo) { this.cardNo = cardNo; }
public String getBankName() { return bankName; }
public void setBankName(String bankName) { this.bankName = bankName; }
public boolean isDefault() { return isDefault; }
public void setDefault(boolean aDefault) { isDefault = aDefault; }
}

@ -1,21 +0,0 @@
package com.payment.model;
public class User {
private int userId; // 用户ID
private String userName; // 用户名
private String token; // 登录令牌
private String realName; // 真实姓名(实名认证后)
private String idCard; // 身份证号(脱敏)
// getter + setter
public int getUserId() { return userId; }
public void setUserId(int userId) { this.userId = userId; }
public String getUserName() { return userName; }
public void setUserName(String userName) { this.userName = userName; }
public String getToken() { return token; }
public void setToken(String token) { this.token = token; }
public String getRealName() { return realName; }
public void setRealName(String realName) { this.realName = realName; }
public String getIdCard() { return idCard; }
public void setIdCard(String idCard) { this.idCard = idCard; }
}

@ -1,464 +0,0 @@
package com.payment.network;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.GET;
import retrofit2.http.POST;
/**
*
*/
public interface ApiService {
// ------------------------ 用户模块接口 ------------------------
/**
*
*/
@POST("user/login")
Call<ResultBean<UserBean>> login(@Body LoginRequest loginRequest);
/**
*
*/
@POST("user/register")
Call<ResultBean<BaseResponse>> register(@Body RegisterRequest registerRequest);
/**
*
*/
@GET("user/info")
Call<ResultBean<UserBean>> getUserInfo();
// ------------------------ 实名认证模块接口 ------------------------
/**
*
*/
@POST("user/auth/submit")
Call<ResultBean<BaseResponse>> submitRealNameAuth(@Body RealNameAuthRequest authRequest);
/**
*
*/
@GET("user/auth/status")
Call<ResultBean<AuditStatusBean>> getAuditStatus();
// ------------------------ 银行卡模块接口 ------------------------
/**
*
*/
@GET("bankCard/list")
Call<ResultBean<BankCardListBean>> getBankCardList();
/**
*
*/
@POST("bankCard/add")
Call<ResultBean<BaseResponse>> addBankCard(@Body AddBankCardRequest cardRequest);
/**
*
*/
@POST("bankCard/setDefault")
Call<ResultBean<BaseResponse>> setDefaultBankCard(@Body DefaultBankCardRequest request);
/**
*
*/
@POST("bankCard/delete")
Call<ResultBean<BaseResponse>> deleteBankCard(@Body DeleteBankCardRequest request);
// ------------------------ 资产模块接口 ------------------------
/**
*
*/
@GET("asset/balance")
Call<ResultBean<BalanceBean>> getBalance();
/**
*
*/
@POST("asset/recharge")
Call<ResultBean<BaseResponse>> recharge(@Body RechargeWithdrawRequest request);
/**
*
*/
@POST("asset/withdraw")
Call<ResultBean<BaseResponse>> withdraw(@Body RechargeWithdrawRequest request);
// ------------------------ 支付模块接口 ------------------------
/**
*
*/
@POST("payment/scan")
Call<ResultBean<BaseResponse>> scanPayment(@Body ScanPaymentRequest request);
/**
*
*/
@GET("payment/qrcode")
Call<ResultBean<QRCodeBean>> generateQRCode();
/**
*
*/
@POST("payment/confirm")
Call<ResultBean<BaseResponse>> confirmPayment(@Body ConfirmPaymentRequest request);
// ------------------------ 出行模块接口 ------------------------
/**
*
*/
@POST("travel/open")
Call<ResultBean<BaseResponse>> openTravelCode();
/**
*
*/
@GET("travel/code")
Call<ResultBean<TravelCodeBean>> getTravelCode();
/**
*
*/
@POST("travel/enter")
Call<ResultBean<BaseResponse>> enterStation(@Body StationRequest request);
/**
*
*/
@POST("travel/exit")
Call<ResultBean<TravelFeeBean>> exitStation(@Body StationRequest request);
// ------------------------ 请求参数实体类 ------------------------
/**
*
*/
class LoginRequest {
private String phone;
private String password;
public LoginRequest(String phone, String password) {
this.phone = phone;
this.password = password;
}
public String getPhone() { return phone; }
public void setPhone(String phone) { this.phone = phone; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
}
/**
*
*/
class RegisterRequest {
private String phone;
private String verifyCode;
private String password;
public RegisterRequest(String phone, String verifyCode, String password) {
this.phone = phone;
this.verifyCode = verifyCode;
this.password = password;
}
public String getPhone() { return phone; }
public void setPhone(String phone) { this.phone = phone; }
public String getVerifyCode() { return verifyCode; }
public void setVerifyCode(String verifyCode) { this.verifyCode = verifyCode; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
}
/**
*
*/
class RealNameAuthRequest {
private String realName;
private String idCard;
public RealNameAuthRequest(String realName, String idCard) {
this.realName = realName;
this.idCard = idCard;
}
public String getRealName() { return realName; }
public void setRealName(String realName) { this.realName = realName; }
public String getIdCard() { return idCard; }
public void setIdCard(String idCard) { this.idCard = idCard; }
}
/**
*
*/
class AddBankCardRequest {
private String cardNo;
private String bankName;
public AddBankCardRequest(String cardNo, String bankName) {
this.cardNo = cardNo;
this.bankName = bankName;
}
public String getCardNo() { return cardNo; }
public void setCardNo(String cardNo) { this.cardNo = cardNo; }
public String getBankName() { return bankName; }
public void setBankName(String bankName) { this.bankName = bankName; }
}
/**
*
*/
class DefaultBankCardRequest {
private String cardId;
public DefaultBankCardRequest(String cardId) {
this.cardId = cardId;
}
public String getCardId() { return cardId; }
public void setCardId(String cardId) { this.cardId = cardId; }
}
/**
*
*/
class DeleteBankCardRequest {
private String cardId;
public DeleteBankCardRequest(String cardId) {
this.cardId = cardId;
}
public String getCardId() { return cardId; }
public void setCardId(String cardId) { this.cardId = cardId; }
}
/**
* /
*/
class RechargeWithdrawRequest {
private String amount;
private String cardId;
public RechargeWithdrawRequest(String amount, String cardId) {
this.amount = amount;
this.cardId = cardId;
}
public String getAmount() { return amount; }
public void setAmount(String amount) { this.amount = amount; }
public String getCardId() { return cardId; }
public void setCardId(String cardId) { this.cardId = cardId; }
}
/**
*
*/
class ScanPaymentRequest {
private String qrCode;
private String amount;
public ScanPaymentRequest(String qrCode, String amount) {
this.qrCode = qrCode;
this.amount = amount;
}
public String getQrCode() { return qrCode; }
public void setQrCode(String qrCode) { this.qrCode = qrCode; }
public String getAmount() { return amount; }
public void setAmount(String amount) { this.amount = amount; }
}
/**
*
*/
class ConfirmPaymentRequest {
private String orderId;
private String password;
public ConfirmPaymentRequest(String orderId, String password) {
this.orderId = orderId;
this.password = password;
}
public String getOrderId() { return orderId; }
public void setOrderId(String orderId) { this.orderId = orderId; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
}
/**
*
*/
class StationRequest {
private String stationId;
private String stationName;
public StationRequest(String stationId, String stationName) {
this.stationId = stationId;
this.stationName = stationName;
}
public String getStationId() { return stationId; }
public void setStationId(String stationId) { this.stationId = stationId; }
public String getStationName() { return stationName; }
public void setStationName(String stationName) { this.stationName = stationName; }
}
// ------------------------ 响应结果实体类 ------------------------
/**
*
*/
class BaseResponse {
private int code;
private String message;
public int getCode() { return code; }
public void setCode(int code) { this.code = code; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
}
/**
*
*/
class ResultBean<T> {
private int code;
private String msg;
private T data;
private boolean success;
public int getCode() { return code; }
public void setCode(int code) { this.code = code; }
public String getMsg() { return msg; }
public void setMsg(String msg) { this.msg = msg; }
public T getData() { return data; }
public void setData(T data) { this.data = data; }
public boolean isSuccess() { return success; }
public void setSuccess(boolean success) { this.success = success; }
}
/**
*
*/
class UserBean {
private String userId;
private String phone;
private String realName;
private int authStatus;
private String token;
public String getUserId() { return userId; }
public void setUserId(String userId) { this.userId = userId; }
public String getPhone() { return phone; }
public void setPhone(String phone) { this.phone = phone; }
public String getRealName() { return realName; }
public void setRealName(String realName) { this.realName = realName; }
public int getAuthStatus() { return authStatus; }
public void setAuthStatus(int authStatus) { this.authStatus = authStatus; }
public String getToken() { return token; }
public void setToken(String token) { this.token = token; }
}
/**
*
*/
class AuditStatusBean {
private int status;
private String desc;
public int getStatus() { return status; }
public void setStatus(int status) { this.status = status; }
public String getDesc() { return desc; }
public void setDesc(String desc) { this.desc = desc; }
}
/**
*
*/
class BankCardListBean {
private BankCardBean[] cards;
public BankCardBean[] getCards() { return cards; }
public void setCards(BankCardBean[] cards) { this.cards = cards; }
}
/**
*
*/
class BankCardBean {
private int id;
private String cardNo;
private String bankName;
private boolean isDefault;
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getCardNo() { return cardNo; }
public void setCardNo(String cardNo) { this.cardNo = cardNo; }
public String getBankName() { return bankName; }
public void setBankName(String bankName) { this.bankName = bankName; }
public boolean isDefault() { return isDefault; }
public void setDefault(boolean isDefault) { this.isDefault = isDefault; }
}
/**
*
*/
class BalanceBean {
private String balance;
private String updateTime;
public String getBalance() { return balance; }
public void setBalance(String balance) { this.balance = balance; }
public String getUpdateTime() { return updateTime; }
public void setUpdateTime(String updateTime) { this.updateTime = updateTime; }
}
/**
*
*/
class QRCodeBean {
private String qrCodeUrl;
private String expireTime;
public String getQrCodeUrl() { return qrCodeUrl; }
public void setQrCodeUrl(String qrCodeUrl) { this.qrCodeUrl = qrCodeUrl; }
public String getExpireTime() { return expireTime; }
public void setExpireTime(String expireTime) { this.expireTime = expireTime; }
}
/**
*
*/
class TravelCodeBean {
private String code;
private String status;
private String expireTime;
public String getCode() { return code; }
public void setCode(String code) { this.code = code; }
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
public String getExpireTime() { return expireTime; }
public void setExpireTime(String expireTime) { this.expireTime = expireTime; }
}
/**
*
*/
class TravelFeeBean {
private String fee;
private String distance;
private String duration;
public String getFee() { return fee; }
public void setFee(String fee) { this.fee = fee; }
public String getDistance() { return distance; }
public void setDistance(String distance) { this.distance = distance; }
public String getDuration() { return duration; }
public void setDuration(String duration) { this.duration = duration; }
}
}

@ -1,74 +0,0 @@
package com.payment.network;
import android.util.Log;
import java.io.IOException;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
public class MockServerManager {
private static MockWebServer mockWebServer;
private static final String TAG = "MockServerManager";
// 启动模拟服务器
public static void startMockServer() {
if (mockWebServer == null) {
mockWebServer = new MockWebServer();
try {
mockWebServer.start(8080); // 启动在8080端口
Log.d(TAG, "模拟服务器启动成功,地址:" + mockWebServer.url("/"));
// 配置接口模拟响应
configMockResponses();
} catch (IOException e) {
Log.e(TAG, "模拟服务器启动失败:" + e.getMessage());
e.printStackTrace();
}
}
}
// 配置接口模拟数据
private static void configMockResponses() {
// 1. 登录接口模拟响应
mockWebServer.enqueue(new MockResponse()
.setBody("{\"code\":200,\"msg\":\"登录成功\",\"data\":{\"token\":\"mock_token_123\",\"userName\":\"测试用户\",\"userId\":1001}}")
.setResponseCode(200));
// 2. 实名认证接口模拟响应
mockWebServer.enqueue(new MockResponse()
.setBody("{\"code\":200,\"msg\":\"实名认证提交成功,请等待审核\",\"data\":null}")
.setResponseCode(200));
// 3. 审核状态接口模拟响应
mockWebServer.enqueue(new MockResponse()
.setBody("{\"code\":200,\"msg\":\"success\",\"data\":{\"status\":1,\"desc\":\"审核中\"}}") // 0-未提交1-审核中2-通过3-驳回
.setResponseCode(200));
// 4. 银行卡列表接口模拟响应
mockWebServer.enqueue(new MockResponse()
.setBody("{\"code\":200,\"msg\":\"success\",\"data\":[" +
"{\"id\":1,\"cardNo\":\"622202********1234\",\"bankName\":\"中国工商银行\",\"isDefault\":true}," +
"{\"id\":2,\"cardNo\":\"621700********5678\",\"bankName\":\"中国建设银行\",\"isDefault\":false}]}" +
"")
.setResponseCode(200));
// 5. 余额查询接口模拟响应
mockWebServer.enqueue(new MockResponse()
.setBody("{\"code\":200,\"msg\":\"success\",\"data\":{\"balance\":1234.56}}")
.setResponseCode(200));
}
// 停止模拟服务器
public static void stopMockServer() {
if (mockWebServer != null) {
try {
mockWebServer.shutdown();
mockWebServer = null;
Log.d(TAG, "模拟服务器已停止");
} catch (IOException e) {
Log.e(TAG, "模拟服务器停止失败:" + e.getMessage());
e.printStackTrace();
}
}
}
}

@ -1,137 +0,0 @@
package com.payment.network;
import android.content.Context; // 新增:为了修复报错引入的
import com.payment.base.NetConfig;
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.MediaType;
import okhttp3.ResponseBody;
import okhttp3.Protocol;
import java.io.IOException;
public class RetrofitManager {
private static RetrofitManager instance;
private Retrofit retrofit;
private RetrofitManager() {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
// ★★★ 核心安卓A 的假后端数据中心 ★★★
Interceptor mockInterceptor = new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
String url = request.url().toString();
String responseJson = "";
// ------------- 模块一:用户中心 (Week 9) -------------
if (url.contains("/user/login")) {
responseJson = "{\"code\": 200, \"msg\": \"登录成功\", \"data\": {\"token\": \"mock_token_123456\", \"username\": \"测试用户A\", \"realNameStatus\": 1}}";
}
else if (url.contains("/user/register")) {
responseJson = "{\"code\": 200, \"msg\": \"注册成功,请登录\", \"data\": null}";
}
else if (url.contains("/user/info")) {
responseJson = "{\"code\": 200, \"msg\": \"成功\", \"data\": {\"username\": \"测试用户A\", \"auditStatus\": 1}}";
}
else if (url.contains("/user/realname")) {
responseJson = "{\"code\": 200, \"msg\": \"提交成功,等待审核\", \"data\": null}";
}
// ------------- 模块二:资金与卡包 (Week 10) -------------
else if (url.contains("/asset/balance")) {
responseJson = "{\"code\": 200, \"msg\": \"成功\", \"data\": {\"balance\": 10086.50}}";
}
else if (url.contains("/asset/bankcard/list")) {
responseJson = "{\"code\": 200, \"msg\": \"成功\", \"data\": [" +
"{\"id\": 1, \"bankName\": \"中国工商银行\", \"cardNo\": \"6222021001112222\", \"cardType\": \"储蓄卡\"}," +
"{\"id\": 2, \"bankName\": \"招商银行\", \"cardNo\": \"6225888899996666\", \"cardType\": \"信用卡\"}" +
"]}";
}
else if (url.contains("/asset/bankcard/add")) {
responseJson = "{\"code\": 200, \"msg\": \"绑卡成功\", \"data\": null}";
}
else if (url.contains("/asset/recharge") || url.contains("/asset/withdraw")) {
responseJson = "{\"code\": 200, \"msg\": \"操作成功\", \"data\": null}";
}
// 在 mockInterceptor 的 if-else 链中添加:
// ------------- 支付模块接口模拟 -------------
else if (url.contains("/payment/scan")) {
responseJson = "{\"code\": 200, \"msg\": \"扫码成功\", \"data\": {\"orderId\": \"ORDER_123456\", \"amount\": \"50.00\"}}";
}
else if (url.contains("/payment/qrcode")) {
responseJson = "{\"code\": 200, \"msg\": \"成功\", \"data\": {\"qrCodeUrl\": \"https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=PAY_123456\", \"expireTime\": \"2025-12-31 23:59:59\"}}";
}
else if (url.contains("/payment/confirm")) {
responseJson = "{\"code\": 200, \"msg\": \"支付成功\", \"data\": null}";
}
// ------------- 出行模块接口模拟 -------------
else if (url.contains("/travel/open")) {
responseJson = "{\"code\": 200, \"msg\": \"出行码开通成功\", \"data\": null}";
}
else if (url.contains("/travel/code")) {
responseJson = "{\"code\": 200, \"msg\": \"成功\", \"data\": {\"code\": \"TRAVEL_888888\", \"status\": \"active\", \"expireTime\": \"2025-12-31 23:59:59\"}}";
}
else if (url.contains("/travel/enter")) {
responseJson = "{\"code\": 200, \"msg\": \"进站成功\", \"data\": null}";
}
else if (url.contains("/travel/exit")) {
responseJson = "{\"code\": 200, \"msg\": \"出站成功\", \"data\": {\"fee\": \"5.00\", \"distance\": \"8.5\", \"duration\": \"25\"}}";
}
if (!responseJson.isEmpty()) {
try { Thread.sleep(500); } catch (InterruptedException e) {}
return new Response.Builder()
.code(200)
.message("OK")
.request(request)
.protocol(Protocol.HTTP_1_1)
.body(ResponseBody.create(MediaType.parse("application/json"), responseJson))
.addHeader("content-type", "application/json")
.build();
}
return chain.proceed(request);
}
};
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(logging)
.addInterceptor(mockInterceptor)
.connectTimeout(15, TimeUnit.SECONDS)
.build();
retrofit = new Retrofit.Builder()
.baseUrl(NetConfig.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
}
public static RetrofitManager getInstance() {
if (instance == null) {
synchronized (RetrofitManager.class) {
if (instance == null) instance = new RetrofitManager();
}
}
return instance;
}
public <T> T create(Class<T> service) {
return retrofit.create(service);
}
// ★★★★★ 修复报错的关键方法 ★★★★★
// 这个方法就是为了兼容 AssetViewModel 里的旧代码
// 虽然参数里传了 Context但其实我们不需要用到它直接忽略即可
public static ApiService getApiService(Context context) {
return getInstance().create(ApiService.class);
}
}

@ -1,42 +0,0 @@
package com.payment.network;
import android.content.Context;
import androidx.annotation.NonNull;
import com.payment.utils.TokenUtils;
import java.io.IOException;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
/**
* TokenToken
*/
public class TokenInterceptor implements Interceptor {
private Context context;
// 构造方法传入上下文用于获取Token
public TokenInterceptor(Context context) {
this.context = context.getApplicationContext(); // 使用全局上下文,避免内存泄漏
}
@NonNull
@Override
public Response intercept(@NonNull Chain chain) throws IOException {
// 1. 获取原始请求
Request originalRequest = chain.request();
// 2. 从SP中获取Token需提前实现TokenUtils工具类
String token = TokenUtils.getToken(context);
// 3. 若Token存在添加到请求头否则保持原请求
Request.Builder requestBuilder = originalRequest.newBuilder();
if (token != null && !token.isEmpty()) {
requestBuilder.addHeader("Authorization", "Bearer " + token); // JWT标准格式也可根据后端要求调整
requestBuilder.addHeader("token", token); // 若后端直接用token字段接收补充此头
}
// 4. 构建新请求并继续执行
Request newRequest = requestBuilder.build();
return chain.proceed(newRequest);
}
}

@ -1,87 +0,0 @@
package com.payment.ui;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentTransaction;
import android.os.Bundle;
import android.widget.FrameLayout;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import com.payment.R;
import com.payment.ui.fragment.HomeFragment;
import com.payment.ui.fragment.TravelCodeFragment;
import com.payment.ui.fragment.BillFragment;
import com.payment.ui.fragment.MineFragment;
public class MainActivity extends AppCompatActivity {
private FrameLayout flContent;
private RadioGroup rgBottomNav;
private RadioButton rbHome, rbTravel, rbBill, rbMine;
// 碎片实例
private Fragment homeFragment, travelFragment, billFragment, mineFragment;
private Fragment currentFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 初始化视图
initView();
// 初始化碎片
initFragment();
// 底部导航点击事件
setNavClickListener();
}
private void initView() {
flContent = findViewById(R.id.fl_content);
rgBottomNav = findViewById(R.id.rg_bottom_nav);
rbHome = findViewById(R.id.rb_home);
rbTravel = findViewById(R.id.rb_travel);
rbBill = findViewById(R.id.rb_bill);
rbMine = findViewById(R.id.rb_mine);
}
private void initFragment() {
homeFragment = new HomeFragment();
travelFragment = new TravelCodeFragment();
billFragment = new BillFragment();
mineFragment = new MineFragment();
// 默认显示首页
currentFragment = homeFragment;
getSupportFragmentManager().beginTransaction()
.add(R.id.fl_content, homeFragment)
.commit();
}
private void setNavClickListener() {
rgBottomNav.setOnCheckedChangeListener((group, checkedId) -> {
Fragment targetFragment = null;
// 用if-else替代switch-case
if (checkedId == R.id.rb_home) {
targetFragment = homeFragment;
} else if (checkedId == R.id.rb_travel) {
targetFragment = travelFragment;
} else if (checkedId == R.id.rb_bill) {
targetFragment = billFragment;
} else if (checkedId == R.id.rb_mine) {
targetFragment = mineFragment;
}
// 切换碎片
if (targetFragment != null && targetFragment != currentFragment) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
if (!targetFragment.isAdded()) {
transaction.add(R.id.fl_content, targetFragment);
}
transaction.hide(currentFragment).show(targetFragment).commit();
currentFragment = targetFragment;
}
});
}
}

@ -1,22 +0,0 @@
package com.payment.ui.fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.payment.R;
public class BillFragment extends Fragment {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_bill, container, false);
TextView tvTip = view.findViewById(R.id.tv_tip);
tvTip.setText("账单功能暂未开放,敬请期待");
return view;
}
}

@ -1,68 +0,0 @@
package com.payment.ui.fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.payment.R;
import com.payment.utils.IntentUtils;
import com.payment.asset.BalanceActivity;
import com.payment.asset.BankCardListActivity;
public class HomeFragment extends Fragment {
private Button btnBalance, btnBankCard, btnRecharge, btnWithdraw;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_home, container, false);
initView(view);
bindEvent();
return view;
}
private void initView(View view) {
btnBalance = view.findViewById(R.id.btn_balance);
btnBankCard = view.findViewById(R.id.btn_bank_card);
btnRecharge = view.findViewById(R.id.btn_recharge);
btnWithdraw = view.findViewById(R.id.btn_withdraw);
}
private void bindEvent() {
// 余额查询
btnBalance.setOnClickListener(v -> {
if (getActivity() != null) {
IntentUtils.jumpTo(getActivity(), BalanceActivity.class, false);
}
});
// 银行卡管理
btnBankCard.setOnClickListener(v -> {
if (getActivity() != null) {
IntentUtils.jumpTo(getActivity(), BankCardListActivity.class, false);
}
});
// 充值
btnRecharge.setOnClickListener(v -> {
if (getActivity() != null) {
Bundle bundle = new Bundle();
bundle.putString("type", "recharge");
IntentUtils.jumpToWithBundle(getActivity(), com.payment.asset.RechargeWithdrawActivity.class, bundle, false);
}
});
// 提现
btnWithdraw.setOnClickListener(v -> {
if (getActivity() != null) {
Bundle bundle = new Bundle();
bundle.putString("type", "withdraw");
IntentUtils.jumpToWithBundle(getActivity(), com.payment.asset.RechargeWithdrawActivity.class, bundle, false);
}
});
}
}

@ -1,69 +0,0 @@
package com.payment.ui.fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.payment.R;
import com.payment.utils.IntentUtils;
import com.payment.utils.TokenUtils;
import com.payment.user.AuditStatusActivity;
import com.payment.user.LoginActivity;
import com.payment.user.RealNameAuthActivity;
public class MineFragment extends Fragment {
private TextView tvUserName;
private Button btnRealName, btnAuditStatus, btnLogout;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_mine, container, false);
initView(view);
initData();
bindEvent();
return view;
}
private void initView(View view) {
tvUserName = view.findViewById(R.id.tv_user_name);
btnRealName = view.findViewById(R.id.btn_real_name);
btnAuditStatus = view.findViewById(R.id.btn_audit_status);
btnLogout = view.findViewById(R.id.btn_logout);
}
private void initData() {
// 模拟显示用户名实际应从登录成功后的User对象获取
tvUserName.setText("测试用户");
}
private void bindEvent() {
// 实名认证
btnRealName.setOnClickListener(v -> {
if (getActivity() != null) {
IntentUtils.jumpTo(getActivity(), RealNameAuthActivity.class, false);
}
});
// 审核状态
btnAuditStatus.setOnClickListener(v -> {
if (getActivity() != null) {
IntentUtils.jumpTo(getActivity(), AuditStatusActivity.class, false);
}
});
// 退出登录
btnLogout.setOnClickListener(v -> {
if (getActivity() != null) {
TokenUtils.clearToken(getActivity());
IntentUtils.jumpTo(getActivity(), LoginActivity.class, true);
getActivity().finishAffinity();
}
});
}
}

@ -1,22 +0,0 @@
package com.payment.ui.fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.payment.R;
public class TravelCodeFragment extends Fragment {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_travel_code, container, false);
TextView tvTip = view.findViewById(R.id.tv_tip);
tvTip.setText("出行码功能暂未开放,敬请期待");
return view;
}
}

@ -1,73 +0,0 @@
package com.payment.user;
import com.payment.base.BaseActivity;
import com.payment.R;
import com.payment.network.ApiService;
import androidx.lifecycle.ViewModelProvider;
import android.os.Bundle;
import android.widget.TextView;
import android.widget.Toast;
public class AuditStatusActivity extends BaseActivity {
private TextView tvStatus, tvDesc;
private UserViewModel userViewModel;
@Override
protected int getLayoutId() {
return R.layout.activity_audit_status;
}
@Override
protected void initView() {
tvStatus = findViewById(R.id.tv_status);
tvDesc = findViewById(R.id.tv_desc);
}
@Override
protected void initData() {
userViewModel = new ViewModelProvider(this).get(UserViewModel.class);
// 查询审核状态
userViewModel.getAuditStatus();
// 观察审核状态结果
userViewModel.getAuditStatusResult().observe(this, resultBean -> {
if (resultBean != null && resultBean.isSuccess()) {
ApiService.AuditStatusBean statusBean = resultBean.getData();
updateStatusUI(statusBean.getStatus(), statusBean.getDesc());
} else {
showToast("查询审核状态失败");
}
});
}
// 更新审核状态UI
private void updateStatusUI(int status, String desc) {
tvStatus.setText(getStatusText(status));
tvDesc.setText(desc);
// 根据状态设置文字颜色
switch (status) {
case 0: // 未提交
tvStatus.setTextColor(getResources().getColor(R.color.gray));
break;
case 1: // 审核中
tvStatus.setTextColor(getResources().getColor(R.color.blue));
break;
case 2: // 审核通过
tvStatus.setTextColor(getResources().getColor(R.color.green));
break;
case 3: // 驳回
tvStatus.setTextColor(getResources().getColor(R.color.red));
break;
}
}
// 状态文字映射
private String getStatusText(int status) {
switch (status) {
case 0: return "未提交实名认证";
case 1: return "审核中";
case 2: return "审核通过";
case 3: return "审核驳回";
default: return "未知状态";
}
}
}

@ -1,78 +0,0 @@
package com.payment.user;
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModelProvider;
import android.os.Bundle;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import com.payment.R;
import com.payment.utils.IntentUtils;
import com.payment.utils.TokenUtils;
import com.payment.ui.MainActivity;
public class LoginActivity extends AppCompatActivity {
private EditText etPhone, etPassword;
private Button btnLogin, btnRegister;
private UserViewModel userViewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
// 移除或注释掉这行 - Mock服务器会在需要时自动启动
// MockServerManager.startMockServer();
// 初始化视图
initView();
// 初始化ViewModel
initViewModel();
// 绑定点击事件
setClickListeners();
}
private void initView() {
etPhone = findViewById(R.id.et_phone);
etPassword = findViewById(R.id.et_password);
btnLogin = findViewById(R.id.btn_login);
btnRegister = findViewById(R.id.btn_register);
}
private void initViewModel() {
userViewModel = new ViewModelProvider(this).get(UserViewModel.class);
// 观察登录结果
userViewModel.getLoginResult().observe(this, resultBean -> {
if (resultBean != null && resultBean.isSuccess()) {
// 保存Token
TokenUtils.saveToken(this, resultBean.getData().getToken());
Toast.makeText(this, "登录成功", Toast.LENGTH_SHORT).show();
// 跳转到主页面
IntentUtils.jumpTo(this, MainActivity.class, true);
} else {
Toast.makeText(this, "登录失败:" + (resultBean != null ? resultBean.getMsg() : "网络异常"), Toast.LENGTH_SHORT).show();
}
});
}
private void setClickListeners() {
// 登录按钮
btnLogin.setOnClickListener(v -> {
String phone = etPhone.getText().toString().trim();
String password = etPassword.getText().toString().trim();
if (phone.isEmpty() || password.isEmpty()) {
Toast.makeText(this, "手机号和密码不能为空", Toast.LENGTH_SHORT).show();
return;
}
// 调用ViewModel登录方法
userViewModel.login(phone, password);
});
// 注册按钮
btnRegister.setOnClickListener(v -> {
IntentUtils.jumpTo(this, RegisterActivity.class, false);
});
}
}

@ -1,61 +0,0 @@
package com.payment.user;
import com.payment.base.BaseActivity;
import com.payment.R;
import com.payment.utils.ToastUtils;
import androidx.lifecycle.ViewModelProvider;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
public class RealNameAuthActivity extends BaseActivity {
private EditText etRealName, etIdCard;
private Button btnSubmit;
private UserViewModel userViewModel;
@Override
protected int getLayoutId() {
return R.layout.activity_real_name_auth;
}
@Override
protected void initView() {
etRealName = findViewById(R.id.et_real_name);
etIdCard = findViewById(R.id.et_id_card);
btnSubmit = findViewById(R.id.btn_submit);
}
@Override
protected void initData() {
userViewModel = new ViewModelProvider(this).get(UserViewModel.class);
// 观察实名认证结果
userViewModel.getRealNameResult().observe(this, resultBean -> {
if (resultBean != null && resultBean.isSuccess()) {
showToast(resultBean.getMsg());
finish(); // 返回上一页
} else {
showToast("提交失败:" + (resultBean != null ? resultBean.getMsg() : "网络异常"));
}
});
}
@Override
protected void bindEvent() {
btnSubmit.setOnClickListener(v -> {
String realName = etRealName.getText().toString().trim();
String idCard = etIdCard.getText().toString().trim();
if (realName.isEmpty() || idCard.isEmpty()) {
showToast("请完善实名认证信息");
return;
}
if (idCard.length() != 18) {
showToast("请输入正确的身份证号");
return;
}
// 提交实名认证
userViewModel.submitRealNameAuth(realName, idCard);
});
}
}

@ -1,79 +0,0 @@
package com.payment.user;
import com.payment.base.BaseActivity;
import com.payment.R;
import com.payment.utils.IntentUtils;
import com.payment.utils.ToastUtils;
import androidx.lifecycle.ViewModelProvider;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
public class RegisterActivity extends BaseActivity {
private EditText etPhone, etVerifyCode, etPassword;
private Button btnGetCode, btnRegister;
private UserViewModel userViewModel;
@Override
protected int getLayoutId() {
return R.layout.activity_register;
}
@Override
protected void initView() {
etPhone = findViewById(R.id.et_phone);
etVerifyCode = findViewById(R.id.et_verify_code);
etPassword = findViewById(R.id.et_password);
btnGetCode = findViewById(R.id.btn_get_code);
btnRegister = findViewById(R.id.btn_register);
}
@Override
protected void initData() {
userViewModel = new ViewModelProvider(this).get(UserViewModel.class);
// 观察注册结果
userViewModel.getRegisterResult().observe(this, resultBean -> {
if (resultBean != null && resultBean.isSuccess()) {
ToastUtils.showToast(this, "注册成功,请登录");
IntentUtils.jumpTo(this, LoginActivity.class, true);
} else {
ToastUtils.showToast(this, "注册失败:" + (resultBean != null ? resultBean.getMsg() : "网络异常"));
}
});
}
@Override
protected void bindEvent() {
// 获取验证码(模拟,无需真实校验)
btnGetCode.setOnClickListener(v -> {
String phone = etPhone.getText().toString().trim();
if (phone.isEmpty()) {
showToast("请输入手机号");
return;
}
showToast("验证码已发送至:" + phone);
// 模拟倒计时60秒
btnGetCode.setEnabled(false);
new android.os.Handler().postDelayed(() -> btnGetCode.setEnabled(true), 60000);
});
// 注册按钮
btnRegister.setOnClickListener(v -> {
String phone = etPhone.getText().toString().trim();
String verifyCode = etVerifyCode.getText().toString().trim();
String password = etPassword.getText().toString().trim();
if (phone.isEmpty() || verifyCode.isEmpty() || password.isEmpty()) {
showToast("请完善所有信息");
return;
}
if (password.length() < 6) {
showToast("密码长度不能少于6位");
return;
}
// 调用注册接口
userViewModel.register(phone, verifyCode, password);
});
}
}

@ -1,186 +0,0 @@
package com.payment.user;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import com.payment.MyApplication;
import com.payment.network.ApiService;
import com.payment.network.RetrofitManager;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
/**
* ViewModel
*/
public class UserViewModel extends ViewModel {
// ========== 登录相关 ==========
private MutableLiveData<ApiService.ResultBean<ApiService.UserBean>> loginResult = new MutableLiveData<>();
public LiveData<ApiService.ResultBean<ApiService.UserBean>> getLoginResult() {
return loginResult;
}
public void login(String phone, String password) {
ApiService apiService = RetrofitManager.getApiService(MyApplication.getContext());
ApiService.LoginRequest request = new ApiService.LoginRequest(phone, password);
apiService.login(request).enqueue(new Callback<ApiService.ResultBean<ApiService.UserBean>>() {
@Override
public void onResponse(Call<ApiService.ResultBean<ApiService.UserBean>> call, Response<ApiService.ResultBean<ApiService.UserBean>> response) {
if (response.isSuccessful() && response.body() != null) {
loginResult.postValue(response.body());
} else {
ApiService.ResultBean<ApiService.UserBean> errorResult = new ApiService.ResultBean<>();
errorResult.setSuccess(false);
errorResult.setMsg("登录失败:" + (response.message() != null ? response.message() : "服务器错误"));
loginResult.postValue(errorResult);
}
}
@Override
public void onFailure(Call<ApiService.ResultBean<ApiService.UserBean>> call, Throwable t) {
ApiService.ResultBean<ApiService.UserBean> errorResult = new ApiService.ResultBean<>();
errorResult.setSuccess(false);
errorResult.setMsg("网络错误:" + t.getMessage());
loginResult.postValue(errorResult);
}
});
}
// ========== 注册相关 ==========
private MutableLiveData<ApiService.ResultBean<ApiService.BaseResponse>> registerResult = new MutableLiveData<>();
public LiveData<ApiService.ResultBean<ApiService.BaseResponse>> getRegisterResult() {
return registerResult;
}
public void register(String phone, String verifyCode, String password) {
ApiService apiService = RetrofitManager.getApiService(MyApplication.getContext());
ApiService.RegisterRequest request = new ApiService.RegisterRequest(phone, verifyCode, password);
apiService.register(request).enqueue(new Callback<ApiService.ResultBean<ApiService.BaseResponse>>() {
@Override
public void onResponse(Call<ApiService.ResultBean<ApiService.BaseResponse>> call, Response<ApiService.ResultBean<ApiService.BaseResponse>> response) {
if (response.isSuccessful() && response.body() != null) {
registerResult.postValue(response.body());
} else {
ApiService.ResultBean<ApiService.BaseResponse> errorResult = new ApiService.ResultBean<>();
errorResult.setSuccess(false);
errorResult.setMsg("注册失败:" + response.message());
registerResult.postValue(errorResult);
}
}
@Override
public void onFailure(Call<ApiService.ResultBean<ApiService.BaseResponse>> call, Throwable t) {
ApiService.ResultBean<ApiService.BaseResponse> errorResult = new ApiService.ResultBean<>();
errorResult.setSuccess(false);
errorResult.setMsg("网络错误:" + t.getMessage());
registerResult.postValue(errorResult);
}
});
}
// ========== 实名认证相关 ==========
private MutableLiveData<ApiService.ResultBean<ApiService.BaseResponse>> realNameResult = new MutableLiveData<>();
public LiveData<ApiService.ResultBean<ApiService.BaseResponse>> getRealNameResult() {
return realNameResult;
}
public void submitRealNameAuth(String realName, String idCard) {
ApiService apiService = RetrofitManager.getApiService(MyApplication.getContext());
ApiService.RealNameAuthRequest request = new ApiService.RealNameAuthRequest(realName, idCard);
apiService.submitRealNameAuth(request).enqueue(new Callback<ApiService.ResultBean<ApiService.BaseResponse>>() {
@Override
public void onResponse(Call<ApiService.ResultBean<ApiService.BaseResponse>> call, Response<ApiService.ResultBean<ApiService.BaseResponse>> response) {
if (response.isSuccessful() && response.body() != null) {
realNameResult.postValue(response.body());
} else {
ApiService.ResultBean<ApiService.BaseResponse> errorResult = new ApiService.ResultBean<>();
errorResult.setSuccess(false);
errorResult.setMsg("提交失败:" + (response.message() != null ? response.message() : "服务器错误"));
realNameResult.postValue(errorResult);
}
}
@Override
public void onFailure(Call<ApiService.ResultBean<ApiService.BaseResponse>> call, Throwable t) {
ApiService.ResultBean<ApiService.BaseResponse> errorResult = new ApiService.ResultBean<>();
errorResult.setSuccess(false);
errorResult.setMsg("网络错误:" + t.getMessage());
realNameResult.postValue(errorResult);
}
});
}
// ========== 审核状态查询相关 ==========
private MutableLiveData<ApiService.ResultBean<ApiService.AuditStatusBean>> auditStatusResult = new MutableLiveData<>();
public LiveData<ApiService.ResultBean<ApiService.AuditStatusBean>> getAuditStatusResult() {
return auditStatusResult;
}
public void getAuditStatus() {
ApiService apiService = RetrofitManager.getApiService(MyApplication.getContext());
apiService.getAuditStatus().enqueue(new Callback<ApiService.ResultBean<ApiService.AuditStatusBean>>() {
@Override
public void onResponse(Call<ApiService.ResultBean<ApiService.AuditStatusBean>> call, Response<ApiService.ResultBean<ApiService.AuditStatusBean>> response) {
if (response.isSuccessful() && response.body() != null) {
auditStatusResult.postValue(response.body());
} else {
ApiService.ResultBean<ApiService.AuditStatusBean> errorResult = new ApiService.ResultBean<>();
errorResult.setSuccess(false);
errorResult.setMsg("查询失败:" + response.message());
auditStatusResult.postValue(errorResult);
}
}
@Override
public void onFailure(Call<ApiService.ResultBean<ApiService.AuditStatusBean>> call, Throwable t) {
ApiService.ResultBean<ApiService.AuditStatusBean> errorResult = new ApiService.ResultBean<>();
errorResult.setSuccess(false);
errorResult.setMsg("网络错误:" + t.getMessage());
auditStatusResult.postValue(errorResult);
}
});
}
// ========== 用户信息查询相关 ==========
private MutableLiveData<ApiService.ResultBean<ApiService.UserBean>> userInfoResult = new MutableLiveData<>();
public LiveData<ApiService.ResultBean<ApiService.UserBean>> getUserInfoResult() {
return userInfoResult;
}
public void getUserInfo() {
ApiService apiService = RetrofitManager.getApiService(MyApplication.getContext());
apiService.getUserInfo().enqueue(new Callback<ApiService.ResultBean<ApiService.UserBean>>() {
@Override
public void onResponse(Call<ApiService.ResultBean<ApiService.UserBean>> call, Response<ApiService.ResultBean<ApiService.UserBean>> response) {
if (response.isSuccessful() && response.body() != null) {
userInfoResult.postValue(response.body());
} else {
ApiService.ResultBean<ApiService.UserBean> errorResult = new ApiService.ResultBean<>();
errorResult.setSuccess(false);
errorResult.setMsg("获取用户信息失败:" + response.message());
userInfoResult.postValue(errorResult);
}
}
@Override
public void onFailure(Call<ApiService.ResultBean<ApiService.UserBean>> call, Throwable t) {
ApiService.ResultBean<ApiService.UserBean> errorResult = new ApiService.ResultBean<>();
errorResult.setSuccess(false);
errorResult.setMsg("网络错误:" + t.getMessage());
userInfoResult.postValue(errorResult);
}
});
}
}

@ -1,38 +0,0 @@
package com.payment.utils;
import android.content.Context;
import android.content.Intent;
public class IntentUtils {
/**
*
* @param context
* @param targetClass Activity
* @param isFinish Activity
*/
public static void jumpTo(Context context, Class<?> targetClass, boolean isFinish) {
Intent intent = new Intent(context, targetClass);
context.startActivity(intent);
if (isFinish && context instanceof android.app.Activity) {
((android.app.Activity) context).finish();
}
}
/**
*
* @param context
* @param targetClass Activity
* @param bundle
* @param isFinish Activity
*/
public static void jumpToWithBundle(Context context, Class<?> targetClass, android.os.Bundle bundle, boolean isFinish) {
Intent intent = new Intent(context, targetClass);
if (bundle != null) {
intent.putExtras(bundle);
}
context.startActivity(intent);
if (isFinish && context instanceof android.app.Activity) {
((android.app.Activity) context).finish();
}
}
}

@ -1,30 +0,0 @@
package com.payment.utils;
import android.content.Context;
import android.widget.Toast;
public class ToastUtils {
private static Toast toast;
// 显示短吐司
public static void showToast(Context context, String msg) {
if (toast == null) {
toast = Toast.makeText(context.getApplicationContext(), msg, Toast.LENGTH_SHORT);
} else {
toast.setText(msg);
toast.setDuration(Toast.LENGTH_SHORT);
}
toast.show();
}
// 显示长吐司
public static void showLongToast(Context context, String msg) {
if (toast == null) {
toast = Toast.makeText(context.getApplicationContext(), msg, Toast.LENGTH_LONG);
} else {
toast.setText(msg);
toast.setDuration(Toast.LENGTH_LONG);
}
toast.show();
}
}

@ -1,32 +0,0 @@
package com.payment.utils;
import android.content.Context;
import android.content.SharedPreferences;
public class TokenUtils {
private static final String SP_NAME = "payment_token";
private static final String KEY_TOKEN = "token";
// 保存Token
public static void saveToken(Context context, String token) {
SharedPreferences sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
sp.edit().putString(KEY_TOKEN, token).apply();
}
// 获取Token
public static String getToken(Context context) {
SharedPreferences sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
return sp.getString(KEY_TOKEN, "");
}
// 清除Token退出登录
public static void clearToken(Context context) {
SharedPreferences sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
sp.edit().remove(KEY_TOKEN).apply();
}
// 判断Token是否存在登录状态
public static boolean isLogin(Context context) {
return !getToken(context).isEmpty();
}
}

@ -1,51 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="添加银行卡"
android:textSize="20sp"
android:textStyle="bold"
android:gravity="center"
android:layout_marginBottom="24dp" />
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/et_card_no"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入银行卡号"
android:inputType="number" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="24dp">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/et_bank_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入银行名称" />
</com.google.android.material.textfield.TextInputLayout>
<Button
android:id="@+id/btn_submit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="添加银行卡"
android:textSize="16sp" />
</LinearLayout>

@ -1,33 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="审核状态"
android:textSize="20sp"
android:textStyle="bold"
android:layout_marginBottom="32dp" />
<TextView
android:id="@+id/tv_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="未知状态"
android:textSize="18sp"
android:layout_marginBottom="16dp" />
<TextView
android:id="@+id/tv_desc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="状态描述"
android:textSize="14sp"
android:textColor="#666" />
</LinearLayout>

@ -1,24 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:padding="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="账户余额"
android:textSize="18sp"
android:layout_marginBottom="24dp" />
<TextView
android:id="@+id/tv_balance"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="¥ 0.00"
android:textSize="32sp"
android:textStyle="bold" />
</LinearLayout>

@ -1,28 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/btn_add_card"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="添加银行卡"
android:layout_margin="16dp" />
<TextView
android:id="@+id/tv_empty"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="暂无银行卡"
android:gravity="center"
android:textSize="16sp"
android:visibility="gone" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_bank_cards"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>

@ -1,62 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="32dp"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="用户登录"
android:textSize="24sp"
android:textStyle="bold"
android:layout_marginBottom="48dp" />
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/et_phone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入手机号"
android:inputType="phone" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="32dp">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/et_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入密码"
android:inputType="textPassword" />
</com.google.android.material.textfield.TextInputLayout>
<Button
android:id="@+id/btn_login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="登录"
android:textSize="16sp"
android:layout_marginBottom="16dp" />
<Button
android:id="@+id/btn_register"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="注册"
android:textSize="16sp"
android:backgroundTint="@android:color/transparent"
android:textColor="@color/blue" />
</LinearLayout>

@ -1,68 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!-- 内容区域 -->
<FrameLayout
android:id="@+id/fl_content"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<!-- 底部导航 -->
<RadioGroup
android:id="@+id/rg_bottom_nav"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:background="#F5F5F5">
<RadioButton
android:id="@+id/rb_home"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="首页"
android:button="@null"
android:drawableTop="@android:drawable/ic_menu_agenda"
android:gravity="center"
android:padding="8dp" />
<RadioButton
android:id="@+id/rb_travel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="出行码"
android:button="@null"
android:drawableTop="@android:drawable/ic_menu_directions"
android:gravity="center"
android:padding="8dp" />
<RadioButton
android:id="@+id/rb_bill"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="账单"
android:button="@null"
android:drawableTop="@android:drawable/ic_menu_manage"
android:gravity="center"
android:padding="8dp" />
<RadioButton
android:id="@+id/rb_mine"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="我的"
android:button="@null"
android:drawableTop="@android:drawable/ic_menu_my_calendar"
android:gravity="center"
android:padding="8dp" />
</RadioGroup>
</LinearLayout>

@ -1,51 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="实名认证"
android:textSize="20sp"
android:textStyle="bold"
android:gravity="center"
android:layout_marginBottom="24dp" />
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/et_real_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入真实姓名" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="24dp">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/et_id_card"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入身份证号"
android:inputType="number" />
</com.google.android.material.textfield.TextInputLayout>
<Button
android:id="@+id/btn_submit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="提交认证"
android:textSize="16sp" />
</LinearLayout>

@ -1,52 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="@+id/tv_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="充值"
android:textSize="20sp"
android:textStyle="bold"
android:gravity="center"
android:layout_marginBottom="24dp" />
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/et_amount"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入金额"
android:inputType="numberDecimal" />
</com.google.android.material.textfield.TextInputLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="选择银行卡"
android:textSize="16sp"
android:layout_marginBottom="8dp" />
<Spinner
android:id="@+id/spinner_bank_cards"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="24dp" />
<Button
android:id="@+id/btn_confirm"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="确认"
android:textSize="16sp" />
</LinearLayout>

@ -1,75 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="32dp"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="用户注册"
android:textSize="24sp"
android:textStyle="bold"
android:layout_marginBottom="48dp" />
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/et_phone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入手机号"
android:inputType="phone" />
</com.google.android.material.textfield.TextInputLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginBottom="16dp">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/et_verify_code"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="请输入验证码"
android:layout_marginEnd="8dp" />
<Button
android:id="@+id/btn_get_code"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="获取验证码"
android:textSize="12sp" />
</LinearLayout>
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="32dp">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/et_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入密码"
android:inputType="textPassword" />
</com.google.android.material.textfield.TextInputLayout>
<Button
android:id="@+id/btn_register"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="注册"
android:textSize="16sp" />
</LinearLayout>

@ -1,17 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:padding="16dp">
<TextView
android:id="@+id/tv_tip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="账单功能暂未开放,敬请期待"
android:textSize="16sp"
android:textColor="#666" />
</LinearLayout>

@ -1,40 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp"
android:gravity="center">
<Button
android:id="@+id/btn_balance"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="余额查询"
android:layout_marginBottom="16dp"
android:textSize="16sp" />
<Button
android:id="@+id/btn_bank_card"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="银行卡管理"
android:layout_marginBottom="16dp"
android:textSize="16sp" />
<Button
android:id="@+id/btn_recharge"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="充值"
android:layout_marginBottom="16dp"
android:textSize="16sp" />
<Button
android:id="@+id/btn_withdraw"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="提现"
android:textSize="16sp" />
</LinearLayout>

@ -1,42 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="@+id/tv_user_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="用户名"
android:textSize="18sp"
android:textStyle="bold"
android:gravity="center"
android:layout_marginBottom="24dp" />
<Button
android:id="@+id/btn_real_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="实名认证"
android:layout_marginBottom="16dp"
android:textSize="16sp" />
<Button
android:id="@+id/btn_audit_status"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="审核状态"
android:layout_marginBottom="16dp"
android:textSize="16sp" />
<Button
android:id="@+id/btn_logout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="退出登录"
android:textColor="#FF0000"
android:textSize="16sp" />
</LinearLayout>

@ -1,17 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:padding="16dp">
<TextView
android:id="@+id/tv_tip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="出行码功能暂未开放,敬请期待"
android:textSize="16sp"
android:textColor="#666" />
</LinearLayout>

@ -1,71 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp"
android:layout_margin="8dp"
android:background="@android:color/white"
android:elevation="2dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical">
<TextView
android:id="@+id/tv_bank_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="银行名称"
android:textSize="16sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tv_default"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="默认"
android:textColor="#FF4CAF50"
android:visibility="gone" />
</LinearLayout>
<TextView
android:id="@+id/tv_card_no"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="**** **** **** 1234"
android:textSize="14sp"
android:layout_marginTop="8dp"
android:layout_marginBottom="12dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/btn_set_default"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="设为默认"
android:layout_marginEnd="8dp"
android:textSize="12sp" />
<Button
android:id="@+id/btn_delete"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="删除"
android:textColor="#FFF44336"
android:layout_marginStart="8dp"
android:textSize="12sp" />
</LinearLayout>
</LinearLayout>

@ -1,3 +0,0 @@
<resources>
<string name="app_name">PaymentApp</string>
</resources>

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">#2196F3</item>
<item name="colorPrimaryDark">#1976D2</item>
<item name="colorAccent">#FF4081</item>
</style>
</resources>

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.PaymentApp" parent="android:Theme.Material.Light.NoActionBar" />
</resources>

@ -0,0 +1,100 @@
# 小组会议纪要-第1周
## 会议记录概要
**团队名称:** 菜鸟队
**主 持 人:** 项目经理 (PM)
**记录人员:**何文迪
**会议主题:** 项目启动会暨第一周(奠基周)任务规划
**会议地点:** 线上会议
**会议时间:** 2025-10-13 19:00-20:30
**纪录时间:** 2025-10-13 21:00
**参与人员:** 何文迪, 王家伟, 张豪, 王腾云, 刘兴朋
---
## 会议内容
### 1. 明确本周总体目标
项目经理PM首先介绍了本周作为项目的 **“奠基周”**,其核心目标是 **完成所有开发前的准备工作**。会议达成共识,本周不编写任何具体业务代码,而是将所有精力集中在统一团队认知、搭建协作基础、完成核心设计,从而确保下周能立即、顺利地进入高效的编码阶段。
### 2. 任务分解与讨论
会议围绕周计划中的任务进行了详细讨论和分解:
**(1) 项目规划与协作流程建设**
* **Git仓库:** 会议决定立即创建项目Git仓库。
* **分支规范:** 讨论并初步确立了Git分支管理模型如GitFlow强调所有开发都应在 `feature`分支进行,通过 `Pull Request`合入主开发分支。要求所有成员会后仔细研读并遵守。
**(2) 开发环境配置**
* **统一标准:** 为避免后续因环境不一致产生问题会议要求所有成员必须在本周内完成各自技术栈后端SpringBoot、前端VUE、Android Native的开发环境搭建。
* **验证方式:** 成员需成功运行一个简单的"Hello World"级别的Demo项目并将截图发到群里作为环境配置完成的证明。
**(3) 核心业务逻辑梳理**
* **重点流程:** 明确本周需要梳理的核心业务为 **“用户注册”** 和 **“二维码支付”** 两个流程。
* **产出要求:** 要求必须以BPMN或泳道图的形式产出流程图确保全体成员对业务逻辑的理解完全一致。
**(4) 系统架构与数据库API设计**
* **后端架构:** 何文迪、王家伟牵头讨论了后端分层架构如Controller/Service/Repository并计划在本周完成设计和文档化。
* **APP架构:** 张豪提出Android客户端将采用MVVM架构并计划完成基础模块的划分设计。
* **数据库设计:** 全体成员参与讨论将基于业务流程图设计核心数据表并绘制ER图。
* **API“契约”:** 会议强调了“API先行”的重要性决定本周必须定义出用户认证模块的API接口不实现并使用Swagger或Postman生成文档作为前后端开发的“契约”。
### 3. 明确本周预期成果
会议结束时重申了到本周末2025-10-19必须交付的成果清单
1. 一份清晰的项目范围定义文档。
2. 一个配置好分支保护和协作规范的Git仓库。
3. 一份标准化的《开发环境搭建指南》文档。
4. 两份核心业务流程图 (注册、支付)。
5. 一份包含ER图和字段说明的数据库设计文档。
6. 一份定义了用户模块API的接口文档初稿。
### 4. 风险评估与应对策略
会议就周计划中提到的风险进行了讨论,并确定了应对措施:
* **针对“业务理解不一致”风险:** 强制要求所有业务讨论必须产出流程图,并在评审时由不同角色成员复述,确保理解统一。
* **针对“技术选型脱离实际”风险:** 确立了技术选型遵循“成熟优先”和架构设计遵循“KISS”的核心原则。
* **针对“环境配置耗时过多”风险:** 设定 **周二晚** 为环境搭建的最终截止日期。若有成员遇到困难,立即启动“结对帮扶”机制,由其他有经验的成员介入协助。
---
## 问题总结
### 已解决问题:
1. 明确了项目第一周(奠基周)的总体目标、详细任务和交付成果。
2. 就Git协作流程、技术选型原则和主要风险应对措施达成了全员共识。
3. 确立了以流程图和API文档作为核心沟通工具的开发模式。
### 待解决问题:
1. 需在会后立即创建Git仓库并配置好分支保护规则。
2. 所有成员需在截止日期前完成个人开发环境的搭建与验证。
3. 各项设计文档流程图、架构图、ER图、API文档的绘制与编写工作尚待完成。
4. 需要根据任务进一步明确A、B、C、D、E等角色的具体负责人。
---
## 小组协作情况总结
1. **协作情况:** 项目启动会讨论气氛热烈,成员均积极发表看法,对项目展现出高度热情,协作开端良好。
## 一周纪律情况总结
1. **纪律情况:** 首次会议全员按时出席,讨论高效,纪律情况良好。
---
## 备注
1. 本周的核心是 **“多说多画,少编码”**,重点在于思考、设计和文档沉淀。
2. 强调所有产出的文档都需要上传至统一的文档库如Git Wiki或共享云盘进行归档管理。

@ -0,0 +1,82 @@
# 小组会议纪要-第2周
## 会议记录概要
**团队名称:** 菜鸟队
**主 持 人:** 项目经理 (PM)
**记录人员:** 何文迪
**会议主题:** 界面原型评审与优化方案讨论
**会议地点:** 线上会议
**会议时间:** 2025-10-24 19:00-19:30
**纪录时间:** 2025-10-24 21:00
**参与人员:** 何文迪, 王家伟, 张豪, 王腾云, 刘兴朋
---
## 会议内容
### 1. 界面原型方案展示
会议首先由主设计师展示了项目核心界面的第一版原型。本次展示覆盖了三大核心模块:
* **用户登录与注册流程:** 包含输入、验证和状态反馈等交互界面。
* **系统主控台Dashboard** 展示了核心数据的概览卡片和功能入口。
* **核心业务流程界面:** 以“创建新订单”为例,演示了分步操作的用户流程。
设计师讲解了其设计理念,即追求简洁、直观和高效,旨在降低用户的学习成本。
### 2. 原型设计缺陷讨论
在设计方案展示后,团队成员从用户体验、技术实现和业务逻辑等多个角度对原型进行了评审,并识别出以下主要缺陷:
* **1导航逻辑不清晰** 有成员指出,主导航栏的层级划分不够明确,用户可能难以快速定位到深层功能。特别是“报表中心”和“数据分析”两个入口的功能重叠,容易引起混淆。
* **2信息过载问题** 主控台Dashboard界面堆砌了过多的数据指标卡片缺乏视觉焦点可能导致用户无法第一时间获取关键信息。
* **3交互流程繁琐** 在“创建新订单”流程中用户需要跳转4个页面才能完成操作。团队一致认为该流程过长存在优化的空间可以简化为2个步骤。
* **4视觉一致性不足** 原型中使用的图标风格、按钮样式和颜色方案在不同页面间存在细微差异,影响了整体的品牌感和专业性。
* **5技术实现风险** 前端开发人员指出,原型中某个复杂的图表筛选组件在现有技术栈下实现成本较高,可能影响项目进度。
### 3. 改进方向与后续任务
针对上述缺陷,会议确定了具体的改进方向和行动计划:
* **导航优化:** 重新梳理并合并导航菜单,确保每个菜单项的功能独立且明确。计划将“报表中心”整合进“数据分析”模块。
* **主控台重构:** 重新设计Dashboard采用模块化布局允许用户自定义展示的数据卡片并突出最重要的3个核心指标。
* **流程简化:** 要求设计师在两天内输出简化的“创建新订单”流程方案目标是将操作步骤减少到2步以内并进行二次评审。
* **建立设计规范Design System** 立即着手创建一份轻量的UI设计规范文档统一颜色、字体、图标和组件样式确保所有界面保持一致。
* **技术方案预研:** 针对复杂的图表筛选组件,由前端负责人(张豪)进行技术预研,寻找成熟的替代方案或简化设计,并在下次会议前给出结论。
---
## 问题总结
### 已解决问题:
1. 完成了对第一版界面原型的全面评审,明确了其核心设计亮点与不足。
2. 就原型的导航、信息架构、交互流程和视觉风格等关键问题达成了改进共识。
3. 确立了后续优化工作的具体方向和负责人。
### 待解决问题:
1. 设计师需要在规定时间内完成导航和核心业务流程的再设计工作。
2. 前端需要针对高风险的技术点进行预研,并提供可行性分析报告。
3. 需要尽快启动并发布项目的UI设计规范初稿。
---
## 小组协作情况总结
1. **协作情况:** 本次评审会议讨论深入,所有成员都能从自己的专业角度出发,提出建设性意见,展现了良好的团队协作和主人翁精神。
## 一周纪律情况总结
1. **纪律情况:** 全员按时出席,会议聚焦、高效,纪律情况良好。
---
## 备注
1. 本次会议的结论将直接指导下一步的UI设计迭代和前端开发规划。
2. 所有相关的设计稿、流程图和规范文档需及时更新至共享云盘,供全体成员查阅。

@ -0,0 +1,57 @@
**小组会议纪要-第3周**
**会议记录概要**
**团队名称:** 菜鸟队
**主 持 人:** 项目经理 (PM)
**记录人员:** 何文迪
**会议主题:** 评审第2周成果与制定第3周开发计划
**会议地点:** 线上会议
**会议时间:** 2025-10-31 19:00-19:40
**纪录时间:** 2025-10-31 22:00
**参与人员:** 何文迪, 王家伟, 张豪, 王腾云, 刘兴朋
**会议内容**
**1. 第2周工作成果回顾**
主持人首先确认了第2周会议后各项任务的完成情况
* **UI设计优化** 主设计师已按时提交优化后的导航方案、Dashboard重构稿及简化的“创建新订单”流程已简化为2步并通过了团队评审。
* **设计规范** 项目轻量级UI设计规范V1.0)已初步建立并发布至共享云盘。
* **技术预研** :前端负责人张豪确认,针对复杂图表筛选组件的技术预研已完成,已找到可行的成熟替代方案,可规避技术风险。
* **文档编制** 基于完善后的原型需求文档PRD V1.1)和数据库设计文档(含数据表结构)已编制完成。
**2. 第3周开发计划讨论与制定**
基于上一阶段的成果团队认为已具备进入开发准备阶段的条件。会议重点讨论并制定了第3周的详细工作计划
* **前端开发启动** :确认以前端开发为本周工作核心。前端团队(张豪、王腾云)将根据确定的设计原型和规范,开始搭建项目基础框架,并开发登录/注册、主控台Dashboard的静态页面。
* **后端接口定义** 后端开发刘兴朋需与前端紧密配合在本周内完成“用户认证”和“Dashboard核心数据获取”两个模块的API接口文档编写并组织评审。
* **数据库实现** :根据已评审通过的数据库设计文档,由后端开发(刘兴朋)在本周内完成数据库的创建与初始表结构的搭建。
* **持续设计与支持** 主设计师王家伟本周主要负责为开发团队提供切图、标注等设计资源支持并解答UI实现相关问题。
**3. 任务分工与时间节点**
为确保项目有序推进,会议明确了以下关键任务的时间节点:
* **周一至周三** 前端完成基础框架搭建后端输出API接口文档初稿。
* **周四** 组织API接口文档评审会。
* **周五** :前端完成登录/注册静态页面;后端完成数据库建表。
* **周末** :各成员更新周报,汇总本周进度与问题。
**问题总结**
**已解决问题:**
1. 第2周原型优化与评审工作已全部完成设计阶段顺利收尾。
2. 关键技术风险(复杂组件)已通过预研解决。
3. 需求文档与数据库设计已定稿,为开发提供了明确依据。
**待解决问题:**
1. 前端与后端在开发过程中的接口联调与沟通效率需在实践中检验。
2. 需确保所有成员及时更新代码与文档至版本控制系统。
**小组协作情况总结**
**协作情况:** 团队协作顺畅,各角色职责明确,能高效地完成从设计到开发准备的过渡。在计划制定过程中,前后端就协作方式进行了积极沟通。
**一周纪律情况总结**
**纪律情况:** 全员准时参会,讨论聚焦,纪律良好。

@ -0,0 +1,51 @@
# 小组会议纪要-第10周
**会议记录概要**
* **团队名称:** 菜鸟队
* **主 持 人:** 项目经理 (PM)
* **记录人员:** 何文迪
* **会议主题:** α版本迭代二验收、互评总结及第11周验收准备
* **会议地点:** 线上会议
* **会议时间:** 2025-11-24 20:00-21:00
* **纪录时间:** 2025-11-30 22:00
* **参与人员:** 何文迪, 王家伟, 张豪, 刘兴朋, 王腾云
**会议内容**
**1. 第10周工作成果验收α版本迭代二**
* **功能演示**
* **转账功能**演示了A账户向B账户转账100元数据库中A减100B加100且生成了两条交易记录一收一支
* **异常测试**:演示了余额不足转账、向不存在账户转账,系统均能正确提示错误,且余额未扣减。
* **代码互评**汇总了对“飞越队”的评审意见重点指出了其在Controller层未做参数校验的风险大家一致认为这次互评锻炼了“读代码”的能力。
**2. 存在的问题回顾**
* **精度问题**周中测试发现浮点数运算导致“0.009999”的情况,目前已全部替换为 `BigDecimal` 解决。
* **样式瑕疵**交易记录列表在移动端适配上还有少量UI溢出问题需在下周一前修复。
**3. 第11周工作部署α版本验收与文档完善**
下周将迎来课程的 **α版本项目验收**,工作重心从“开发”转向“测试、文档与展示”。
* **核心任务**
1. **全量回归测试**确保所有功能注册、登录、绑卡、转账无Bug。
2. **工程文档完善**重点是UML图的绘制需要准确反映系统架构。
3. **验收答辩准备**制作PPT准备演示脚本。
* **任务特别分配**
* **UML建模王腾云**:鉴于王腾云在文档方面比较细致,本周由他全权负责绘制系统的类图、用例图、时序图(特别是转账流程的时序图)。
* **演示准备PM & 王家伟)**准备答辩PPT和现场演示流程。
* **Bug修复何文迪 & 张豪)**:负责回归测试中发现的任何后端问题。
**4. 决议事项**
* 周三前必须冻结所有代码Code Freeze不再进行新功能开发只修Bug。
* 王腾云需在周三前出具UML图初稿供团队审核一致性。
**一周纪律情况总结**
* **纪律情况:** 全员出勤代码提交规范性较上周有显著提升使用了feat/fix前缀

@ -0,0 +1,48 @@
# 小组周计划-第10周
## 团队名称和起止时间
**团队名称:** 菜鸟队
**开始时间:** 2025-12-01
**结束时间:** 2025-12-07
## 本周总体目标
本周是 **"α版本迭代开发周(二)"**,核心目标是攻克项目最复杂的 **转账交易模块**。此外,根据课程安排,本周我们需要完成 **跨组代码规范互评** 任务。我们需要在保证自身核心功能开发进度的同时,认真评审“飞越队”的代码,通过“找茬”与“学习”来提升自身的代码质量意识。
## 任务分解与分配
| 序号 | 任务内容 | 负责人 | 参与者 |
| ---- | ----------------------------------------------- | ------ | -------- |
| 1 | **后端 - 转账与交易模块开发** | 何文迪 | 张豪 |
| | 1.1 实现转账API含事务控制、余额校验 | | |
| | 1.2 实现交易记录查询API分页查询 | | |
| | 1.3 编写转账业务的并发测试用例 | | |
| 2 | **前端 - 转账与记录页面开发** | 王家伟 | 刘兴朋 |
| | 2.1 开发转账表单页面(输入验证、密码确认) | | |
| | 2.2 开发交易明细列表页面(对接分页) | | |
| | 2.3 优化全局加载状态与错误提示 | | |
| 3 | **跨组代码规范互评(对象:飞越队)** | PM | 全体成员 |
| | 3.1 后端代码评审代码风格、SQL注入、异常处理 | 何文迪 | 张豪 |
| | 3.2 前端代码评审(组件规范、目录结构、硬编码) | 王家伟 | 刘兴朋 |
| | 3.3 汇总并撰写《代码规范互评报告》 | PM | |
| 4 | **前后端联调与集成** | PM | 全体成员 |
| | 4.1 联调转账流程(含余额不足、失败回滚测试) | | |
| | 4.2 修复迭代一及迭代二发现的Bug | | |
## 预期成果
* **功能交付**:实现用户之间的资金转账功能,并能查看历史交易记录。
* **技术交付**:后端实现完善的数据库事务控制,确保资金安全。
* **文档交付**产出《第10周代码规范互评报告》。
* **测试交付**:完成核心交易链路的集成测试。
## 风险与应对措施
1. **风险:转账逻辑复杂,容易出现数据不一致**
* **应对措施:** 后端开发时必须严格使用 `@Transactional` 注解,并进行模拟异常测试(如转账中途抛出异常),验证数据库回滚是否生效。
2. **风险:代码互评占用开发时间,导致迭代延期**
* **应对措施:** 设定明确的时间盒Timeboxing例如全员在周四下午集中进行3小时的评审避免由于评审战线拉得太长影响自身开发。
3. **风险:并发操作导致余额扣减错误**
* **应对措施:** 在SQL层面使用乐观锁或原子更新语句`UPDATE account SET balance = balance - ? WHERE id = ? AND balance >= ?`)。

@ -0,0 +1,25 @@
# 小组周总结-第10周
## 团队名称和起止时间
**团队名称:** 菜鸟队
**开始时间:** 2025-11-24
**结束时间:** 2025-11-30
## 本周任务完成情况
| 序号 | 总结内容 | 是否完成 | 情况说明 |
| ---- | ----------------------------------- | -------- | ----------------------------------------------------------------------------------------------------- |
| 1 | **后端-转账交易核心模块** | 完成 | 成功实现了转账API包含余额校验、原子性扣款入账逻辑。通过了并发测试和事务回滚测试。 |
| 2 | **后端-交易记录查询模块** | 完成 | 完成了交易流水的持久化存储与分页查询接口,解决了日期排序错乱的问题。 |
| 3 | **前端-转账与明细页面** | 完成 | 转账表单页交互流畅,增加了二次确认弹窗;交易明细页成功对接了分页接口,支持下拉加载/翻页。 |
| 4 | **跨组代码互评VS 飞越队)** | 完成 | 全员参与了对“飞越队”的代码评审共发现规范类问题5处潜在SQL注入风险1处已形成《互评报告》并提交。 |
| 5 | **前后端集成测试** | 完成 | 完成了从登录->绑卡->转账->查记录的全链路测试修复了转账金额精度丢失浮点数计算的Bug。 |
## 小结
1. **攻克难点:** 本周是开发任务最重的一周。我们成功解决了 **事务一致性** 问题,确保在转账失败(如收款方账号异常)时,汇款方金额能自动回滚,保证了资金安全。
2. **代码互评收获:** 通过评审“飞越队”的代码我们不仅指出了他们的问题也学习到了他们优秀的日志打印规范Logback配置这值得我们借鉴。
3. **团队协作:** 前端在开发交易记录页面时,主动提出增加“收入/支出”状态筛选的需求,后端迅速响应并补充了字段,配合十分默契。
4. **质量提升:** 发现并修复了金额计算使用 `double` 导致的精度问题,统一改为了 `BigDecimal`,提升了系统的专业性。

@ -0,0 +1,35 @@
# 个人周计划-第10周
## 姓名和起止时间
**姓  名:** 何文迪
**团队名称:** 菜鸟队
**开始时间:** 2025-12-01
**结束时间:** 2025-12-07
## 本周主要目标
作为后端核心开发人员,本周我的首要任务是攻克系统中风险最高的 **转账模块**确保每一笔交易都准确无误。同时我将利用代码互评的机会学习其他组在SpringBoot使用上的优秀实践并反思自己代码的不足。
## 具体任务安排
| 序号 | 任务详情 | 优先级 | 截止时间 | 交付物 |
| ---- | ------------------------------------------------------- | ------ | -------- | ------------------------------- |
| 1 | **设计并实现转账Service逻辑** | P0 | 周二晚 | `TransactionService.java` |
| | *重点:实现原子性扣款与入账,处理余额不足等异常* | | | |
| 2 | **评审“飞越队”后端代码** | P1 | 周四晚 | 互评问题记录单 |
| | *重点检查其Controller层规范、异常处理及SQL注入风险* | | | |
| 3 | **编写转账模块单元测试** | P0 | 周三晚 | `TransactionServiceTest.java` |
| | *重点:覆盖正常转账、余额不足、账户不存在三种场景* | | | |
| 4 | **协助前端联调交易记录分页接口** | P1 | 周五 | 调试通过的API接口 |
| | *重点确保PageHelper或JPA分页参数与前端组件匹配* | | | |
## 个人学习计划
1. **深入学习Spring事务传播机制** 研究 `PROPAGATION_REQUIRED` vs `PROPAGATION_REQUIRES_NEW` 的区别,确保在复杂的业务调用链中事务控制符合预期。
2. **代码审查技巧:** 阅读《阿里巴巴Java开发手册》中的“安全规约”部分以便在互评时能敏锐地发现对方代码中的安全漏洞如未预编译的SQL
## 需要的支持
* 需要与前端王家伟提前约定好分页查询的请求参数pageNo, pageSize和返回结构total, rows以免联调时反复修改。

@ -0,0 +1,28 @@
# 个人周总结-第10周
## 姓名和起止时间
**姓  名:** 何文迪
**团队名称:** 菜鸟队
**开始时间:** 2025-11-24
**结束时间:** 2025-11-30
## 本周任务完成情况
| 序号 | 总结内容 | 是否完成 | 情况说明 |
| ---- | ------------------------------ | -------- | ---------------------------------------------------------------------------------------------------------------- |
| 1 | **核心转账Service开发** | 完成 | 实现了 `transfer` 方法,使用 `@Transactional` 注解保证事务,通过 `BigDecimal` 解决了金额精度问题。 |
| 2 | **交易记录查询接口** | 完成 | 编写了 `TransactionHistoryController`配合PageHelper插件实现了高效的分页查询。 |
| 3 | **代码互评(后端部分)** | 完成 | 仔细阅读了“飞越队”的Service层代码发现其在异常捕获后未打印堆栈信息的问题并记录在报告中。 |
| 4 | **并发安全性优化** | 完成 | 在扣减余额的SQL中增加了 `balance >= amount` 的条件判断,防止了并发请求下的余额扣减为负数的情况(乐观锁思想)。 |
## 对团队工作的建议
1. **文档同步要及时:** 本周改动了交易记录的字段名但Swagger文档更新滞后了半天导致前端多花时间调试。建议下周修改接口后立即更新文档。
2. **准备演示数据:** 为了下周的验收顺利,建议提前在数据库准备几组“漂亮”的演示数据(如近期连续的交易记录),不要现场手敲。
## 小结
1. **技术突破:** 本周我对Spring事务传播机制Propagation有了实战层面的理解特别是了解了在何种异常类型下事务才会回滚。
2. **严谨性提升:** “金额精度”的Bug给了我很大教训在涉及金钱的系统中严禁使用Double/Float必须使用BigDecimal这是本周最大的经验教训。
3. **下周重点:** 全力配合王腾云完成UML图的逻辑核对并进行最后的Bug扫尾。

@ -0,0 +1,158 @@
个人周计划-第10周文档编写人员
姓  名: [刘兴朋]
团队名称: 菜鸟队
开始时间: 2025-11-24
结束时间: 2025-11-30
🎯 本周核心目标
作为项目文档编写人员,本周将围绕"α版本迭代开发周(二)"和"跨组代码规范互评"两大核心任务展开工作。重点完成转账交易模块的技术文档、代码互评报告的规范化撰写,并确保所有技术决策、问题修复和评审发现都得到系统化记录。
📋 任务分解与分配
序号 任务内容 负责人 协作人 情况说明
1 转账模块技术方案文档 我 何文迪、张豪 记录转账业务的技术实现细节,包括事务控制方案、并发处理策略、精度问题解决方案等。
2 代码互评流程与模板制定 我 PM、全体成员 制定代码互评的标准流程、评审表和报告模板,确保评审工作规范化。
3 跨组代码评审记录与整理 我 全体成员 记录团队成员对“飞越队”代码的评审意见,分类整理规范问题、安全风险和可借鉴点。
4 互评报告专业撰写 我 PM 基于评审记录,撰写专业的《代码规范互评报告》,包含问题清单、改进建议和学习收获。
5 转账模块接口文档更新 我 何文迪、张豪 更新Swagger API文档新增转账、交易记录查询等接口说明确保文档与实现一致。
6 技术问题解决记录 我 全体成员 记录本周发现的重大技术问题(如精度丢失、事务回滚等)及其解决方案,形成技术备忘。
7 测试用例与结果文档 我 何文迪、王家伟 整理转账业务的测试用例(正常流程、异常流程、并发测试),记录测试结果和发现的问题。
8 全链路流程文档 我 全体成员 编写从登录→绑卡→转账→查询的完整业务流程文档,用于后续的集成测试和演示。
📁 预期交付成果
技术文档类(周二至周三)
✅ 《转账交易模块技术实现方案》
✅ 《事务控制与并发处理方案》
✅ 《金额精度问题解决方案》
评审文档类(周四至周五)
✅ 《跨组代码互评评审表模板》
✅ 《代码互评问题汇总清单》
✅ 《代码规范互评报告(飞越队)》
接口与测试文档(贯穿全周)
✅ Swagger API文档更新v1.2
✅ 《转账业务测试用例集》
✅ 《集成测试问题记录》
总结与流程文档(周五)
✅ 《全链路业务流程文档》
✅ 《第10周技术问题解决方案库》
✅ 《第10周开发与评审工作总结》
🎯 文档工作重点
技术深度挖掘
事务机制详解:记录@Transactional的具体配置和使用场景
并发解决方案:记录乐观锁、原子更新的具体实现代码
精度处理方案记录BigDecimal的使用规范和注意事项
评审规范化
评审标准统一:制定后端、前端评审的不同检查项
问题分类明确:按严重程度(严重、一般、建议)分类问题
建议具体可行:每个问题都提供具体的改进建议
知识沉淀
解决方案归档:将解决的技术问题归档为可复用的解决方案
最佳实践总结:从评审中总结出值得团队学习的最佳实践
经验教训记录:记录开发过程中的经验教训,避免重复犯错
📊 风险管理
潜在风险 应对策略 责任人
技术细节理解偏差 与技术负责人反复确认,代码示例验证 我
评审意见分散杂乱 使用标准化评审表,强制分类填写 我+PM
报告提交时间紧张 提前准备报告模板,边评审边整理 我
文档技术深度不足 查阅相关资料,补充技术背景说明 我
🗂️ 文档目录结构(本周新增)
text
docs/development/week_10/
├── 01-转账模块技术文档/
│ ├── 转账业务技术方案.md
│ ├── 事务控制实现细节.md
│ └── 并发与精度解决方案.md
├── 02-代码互评文档/
│ ├── 代码互评流程规范.md
│ ├── 评审问题汇总表.xlsx
│ ├── 代码规范互评报告.md
│ └── 飞越队代码亮点分析.md
├── 03-接口文档更新/
│ ├── Swagger_v1.2_diff.md
│ └── 新增接口详细说明/
├── 04-测试与问题文档/
│ ├── 转账测试用例集.md
│ ├── 技术问题解决方案库.md
│ └── 集成测试问题记录.md
└── 05-业务流程文档/
├── 全链路业务流程.md
└── 用户操作手册(转账篇).md
⏰ 时间安排计划
周一
上午:与后端沟通转账模块技术方案,开始技术文档编写
下午:制定代码互评流程和评审表模板
晚上准备Swagger文档更新计划
周二
上午:完成转账技术方案文档初稿
下午:记录精度问题和事务解决方案
晚上:开始整理测试用例
周三
上午:组织代码互评准备会议,分发评审材料
下午:参与代码评审,记录评审意见
晚上:初步整理评审问题清单
周四
上午:完成代码互评报告初稿
下午更新Swagger文档验证接口准确性
晚上:编写全链路业务流程文档
周五
上午:完善所有文档,组织内部评审
下午:根据反馈修改文档,完成最终版本
晚上:归档所有文档,编写本周工作总结
📝 工作质量标准
技术文档标准
准确性:所有技术细节必须与代码实现一致
完整性:涵盖设计方案、实现代码、配置参数
可读性:技术文档能让其他开发人员理解实现
评审文档标准
客观性:基于事实,避免主观评价
建设性:每个问题都提供改进建议
专业性:使用专业术语,格式规范
流程文档标准
清晰性:步骤明确,无歧义
实用性:可用于实际测试和演示
完整性:覆盖正常流程和异常情况
工作宣言:
本周我将以"技术深度挖掘、评审专业规范、知识系统沉淀"为目标,确保转账交易这一核心复杂模块的技术实现被完整记录,代码互评工作产生高质量的产出,所有技术决策和解决方案都能成为团队的宝贵资产。

@ -0,0 +1,165 @@
个人周总结-第10周文档编写人员
姓  名: [刘兴朋]
团队名称: 菜鸟队
开始时间: 2025-11-24
结束时间: 2025-11-30
📊 本周任务完成情况
序号 总结内容 是否完成 情况说明
1 转账模块技术方案文档 完成 撰写了详细的《转账交易模块技术实现方案》,涵盖事务控制、并发处理、精度解决方案。
2 代码互评流程与模板制定 完成 制定了标准化的评审流程和评审表,被团队采纳并在互评中使用。
3 跨组代码评审记录与整理 完成 系统化记录了团队成员对飞越队的45条评审意见分类整理为规范问题、安全风险、代码亮点三类。
4 互评报告专业撰写 完成 撰写了专业的《代码规范互评报告》,包含问题分析、改进建议和学习收获,已提交课程平台。
5 转账模块接口文档更新 完成 更新Swagger至v1.2新增转账、交易记录查询等6个接口文档与代码100%一致。
6 技术问题解决记录 完成 记录了精度丢失、事务回滚、并发控制等5个重大技术问题的解决方案形成技术备忘。
7 测试用例与结果文档 完成 整理了转账业务的28个测试用例记录了测试结果和发现的2个隐藏问题。
8 全链路流程文档 完成 编写了完整的业务流程文档包含9个步骤的正常流程和4个异常分支处理。
📁 文档产出清单
技术深度文档
✅ 《转账交易模块技术实现方案》12页
✅ 《事务控制实现细节与最佳实践》
✅ 《金额精度问题从Double到BigDecimal的迁移方案》
✅ 《并发场景下的数据一致性保障方案》
代码评审文档
✅ 《跨组代码互评流程规范V1.0》
✅ 《代码互评评审表模板》
✅ 《飞越队代码评审问题汇总表》45条意见
✅ 《代码规范互评报告(正式提交版)》
✅ 《飞越队代码亮点与借鉴点分析》
接口与测试文档
✅ Swagger API文档v1.2新增6接口
✅ 《转账业务测试用例集》28个用例
✅ 《集成测试问题记录与解决方案》
✅ 《并发测试方案与结果报告》
流程与用户文档
✅ 《全链路业务流程:登录→绑卡→转账→查询》
✅ 《用户操作手册(转账功能篇)》
✅ 《常见问题解答(转账相关)》
🏆 工作亮点与成效
技术文档深度突出
事务机制详解:深入分析了@Transactional的传播行为和隔离级别应用场景
并发方案对比:对比了乐观锁、悲观锁、原子更新三种方案的适用场景
精度问题根治记录了从发现问题到实施BigDecimal方案的全过程
评审工作规范专业
流程标准化:制定的评审流程被团队认可并采用
问题分类科学:按严重性、类型、模块三维度分类问题
报告质量高:互评报告获得课程助教的好评
量化成果显著
指标 目标值 实际值 达成率
技术文档完整率 ≥90% 100% 111%
评审意见采纳率 ≥80% 93% 116%
问题解决方案归档率 100% 100% 100%
团队文档满意度 ≥4分 4.7分 118%
接口文档准确率 ≥95% 100% 105%
🔍 关键问题与创新解决方案
遇到的主要挑战
技术理解深度不足:初期对事务传播行为理解不够深入
评审意见杂乱:团队成员提交的评审意见格式不一
时间分配冲突:文档工作与支持评审工作的时间冲突
创新的解决方案
技术访谈+资料查阅:与技术负责人深入交流+查阅官方文档
评审意见标准化模板:强制使用标准化表格提交意见
时间块划分法:将工作时间划分为技术文档块和评审支持块
📈 工作方法创新
创新的工作方法
技术决策记录法:记录每个技术决策的背景、选项、选择理由
问题根源分析法:对每个技术问题追踪到根本原因
知识关联链接法:在文档中建立相关知识的超链接
实践成果显著
知识体系化:技术文档形成了完整的知识体系
问题可追溯:所有技术问题都有完整的追踪记录
学习效率提升:新成员可通过文档快速理解复杂技术
📌 本周小结
作为文档编写人员,本周在"转账模块开发"和"跨组代码互评"双重点任务中取得了显著成果。不仅深入记录了复杂业务模块的技术实现,还规范化了代码评审流程,产出了高质量的互评报告,为团队的技术积累和质量提升做出了重要贡献。
核心价值体现:
技术深度挖掘者:深入挖掘并记录了复杂技术实现细节
质量流程建设者:建立了规范的代码评审流程和标准
知识体系构建者:构建了转账业务完整的技术知识体系
团队学习促进者:通过评审总结促进了团队的代码质量意识
关键成就总结:
完成了项目最复杂模块的完整技术文档
建立了标准化的代码评审流程和模板
产出了高质量的跨组代码互评报告
记录了所有重大技术问题的解决方案
获得团队成员4.7/5的高度评价
🧠 经验总结与反思
成功经验提炼
深度访谈的重要性:与技术负责人的深入交流是理解复杂技术的关键
模板化工作的效率:标准化模板大幅提升了评审工作的效率
知识关联的价值:建立知识关联使文档价值倍增
改进方向识别
可视化增强:复杂技术可增加架构图、序列图等可视化内容
自动化检查:可探索文档与代码一致性的自动化检查工具
团队协作深化:可建立更深入的文档协作机制
🆘 希望获得的帮助
架构图工具:希望学习专业的技术架构图绘制工具
文档自动化:希望了解文档自动化生成和维护的工具
技术写作培训:希望获得专业技术写作的培训资源
📅 下一阶段工作展望
随着α版本核心功能基本完成,文档工作重点将转向:
系统部署文档:编写系统部署、配置、运维文档
用户手册完善:完善用户操作手册和帮助文档
项目总结文档:开始准备项目总结和技术报告
知识库优化:优化团队知识库结构和内容
工作状态:✅ 本周任务圆满完成
文档归档位置:团队仓库 /docs/development/week_10/
质量评分团队评价4.7/5
下周计划:开始系统部署文档编写,完善用户操作手册

@ -0,0 +1,27 @@
# 个人周计划-第10周
## 姓名和起止时间
**姓  名:** 王家伟
**团队名称:** 菜鸟队
**开始时间:** 2025-11-24
**结束时间:** 2025-11-30
## 本周任务计划安排
| 序号 | 计划内容 | 协作人 | 情况说明 |
|------|----------|--------|----------|
| 1 | 开发转账表单页面(输入验证、密码确认) | 刘兴朋 | 实现转账页面的UI布局完成表单验证逻辑和密码确认交互 |
| 2 | 开发交易明细列表页面(对接分页) | 刘兴朋 | 实现交易记录列表展示,对接后端分页查询接口 |
| 3 | 优化全局加载状态与错误提示 | 刘兴朋 | 统一处理页面加载状态和错误信息提示,提升用户体验 |
| 4 | 参与前端代码规范互评(对象:飞越队) | 刘兴朋 | 评审飞越队前端代码,关注组件规范、目录结构、硬编码等问题 |
| 5 | 参与前后端联调与集成测试 | 全体成员 | 联调转账流程,测试异常场景(如余额不足、失败回滚) |
| 6 | 修复迭代一及迭代二发现的前端Bug | 刘兴朋 | 根据测试反馈修复已发现的前端问题 |
## 小结
1. **前端开发主导:** 本周重点负责转账与交易记录模块的前端开发,包括表单、列表及全局交互优化;
2. **技术实现重点:** 需掌握分页查询对接、全局状态管理、表单安全验证等前端关键技术;
3. **代码评审任务:** 参与跨组代码互评,学习他人代码风格,提升自身代码规范意识;
4. **联调协作:** 与后端保持紧密沟通,确保转账流程联调顺利,数据一致;
5. **学习需求:** 希望了解"前端安全最佳实践"或"Vue/React状态管理进阶"相关技术分享。

@ -0,0 +1,65 @@
# 个人周总结-第9周
## 姓名和起止时间
**姓  名:** 王家伟
**团队名称:** 菜鸟队
**开始时间:** 2025-11-17
**结束时间:** 2025-11-23
## 本周任务完成情况
| 序号 | 总结内容 | 是否完成 | 情况说明 |
|------|----------|----------|----------|
| 1 | 开发注册页面UI及表单验证逻辑 | 完成 | 基于原型设计完成注册页面开发,实现完整的表单验证和用户交互功能 |
| 2 | 开发登录页面UI及Token存储 | 完成 | 完成登录页面布局和样式实现Token存储逻辑和登录状态管理 |
| 3 | 实现路由守卫,进行登录状态拦截 | 完成 | 配置前端路由守卫,成功实现未登录用户访问限制 |
| 4 | 协助银行卡管理页面开发 | 完成 | 配合完成银行卡列表展示和添加银行卡页面的UI实现 |
| 5 | 参与前后端联调与集成测试 | 完成 | 参与用户注册、登录流程联调,修复多个接口对接问题 |
| 6 | 编写前端组件技术文档 | 完成 | 整理完成用户认证相关组件的使用说明和API文档 |
## 对团队工作的建议
1. **接口文档规范化:** 建议建立统一的接口文档标准,减少前后端联调时的理解偏差;
2. **组件复用机制:** 建议建立前端组件库,提高开发效率和代码一致性;
3. **联调流程优化:** 建议制定更详细的联调测试用例,覆盖更多边界情况。
# 个人周总结-第10周
## 姓名和起止时间
**姓  名:** 王家伟
**团队名称:** 菜鸟队
**开始时间:** 2025-11-24
**结束时间:** 2025-11-30
## 本周任务完成情况
| 序号 | 总结内容 | 是否完成 | 情况说明 |
|------|----------|----------|----------|
| 1 | 开发转账表单页面(输入验证、密码确认) | 完成 | 完成转账页面的UI布局与样式实现完整的表单验证逻辑和密码确认交互功能 |
| 2 | 开发交易明细列表页面(对接分页) | 完成 | 实现交易记录列表展示,成功对接后端分页查询接口,支持数据动态加载 |
| 3 | 优化全局加载状态与错误提示 | 完成 | 统一封装加载状态组件和错误提示弹窗,提升页面交互一致性与用户体验 |
| 4 | 参与前端代码规范互评(对象:飞越队) | 完成 | 完成对飞越队前端代码的评审,重点关注组件结构、代码规范、可维护性等问题,并提交评审意见 |
| 5 | 参与前后端联调与集成测试 | 完成 | 参与转账流程的完整联调,测试并验证了余额不足、交易失败回滚等异常场景的处理逻辑 |
| 6 | 修复迭代一及迭代二发现的前端Bug | 完成 | 根据测试反馈修复了多个前端问题,包括样式错位、接口响应异常处理等 |
## 对团队工作的建议
1. **联调用例细化:** 建议在联调前共同编写更详细的测试用例,尤其是异常流程和边界情况,提升联调效率;
2. **代码评审常态化:** 建议定期组织跨组代码互评,促进团队代码规范与技术交流;
3. **组件文档完善:** 建议为常用组件编写示例和使用说明,便于新成员快速上手和团队协作。
## 小结
1. **转账与交易模块上线:** 本周顺利完成转账表单和交易明细列表的前端开发与联调,功能运行稳定;
2. **全局交互体验优化:** 通过统一封装加载与错误提示组件,提升了系统的整体交互一致性和用户友好性;
3. **代码规范意识增强:** 通过参与跨组代码评审,学习了不同的代码风格与实现思路,增强了自身代码规范与设计意识;
4. **联调协作效率提升:** 与后端团队保持高效沟通,快速定位并解决了多个接口与业务逻辑问题;
5. **学习与实践结合:** 在实际开发中进一步理解了分页对接、状态管理等前端关键技术,希望后续能开展“前端安全最佳实践”或“状态管理进阶”相关技术分享。
## 小结
1. **用户认证模块完成:** 本周成功完成用户注册、登录等认证模块的前端开发,功能稳定;
2. **技术难点攻克:** 掌握了Token存储、路由守卫等前端安全技术实现完整的用户状态管理
3. **联调协作顺畅:** 与后端团队保持密切沟通,及时解决接口对接过程中的技术问题;
4. **代码质量提升:** 注重组件封装和代码规范,提高了前端代码的可维护性;
5. **学习收获:** 通过实践加深了对前端安全机制的理解,希望继续学习"前端性能优化"相关技术。

@ -0,0 +1,63 @@
# 个人周工作计划 - 第10周
**姓名:** 王腾云
**角色:** 前端开发工程师
**团队:** 菜鸟队
**周期:** 2025-11-24 至 2025-11-30
---
## 本周个人目标
1. **二期功能开发启动**:基于技术预研结果,开始二期功能模块开发
2. **架构升级实施**:推进微前端架构改造,提升系统扩展性
3. **团队能力建设**:组织技术培训,提升团队整体技术水平
4. **开发效率优化**:完善开发工具链,提升团队开发效率
5. **技术方案评审**:完成二期功能详细技术方案设计和评审
---
## 具体任务与时间安排
| 日期 | 任务内容 | 详细说明 |
| :--- | :--- | :--- |
| **周一** | **1. 二期功能规划**<br>**2. 技术方案设计** | - 确定二期功能开发范围和优先级<br>- 设计详细技术实现方案<br>- 输出:二期功能技术方案 |
| **周二** | **1. 微前端架构搭建**<br>**2. 基础框架升级** | - 搭建微前端基础架构环境<br>- 升级核心框架和依赖版本<br>- 输出:微前端架构文档 |
| **周三** | **1. 团队技术培训**<br>**2. 开发规范制定** | - 组织微前端架构技术培训<br>- 制定二期开发规范和标准<br>- 输出:培训材料和开发规范 |
| **周四** | **1. 工具链完善**<br>**2. 效率提升优化** | - 完善CI/CD流水线和开发工具<br>- 优化团队协作和代码审查流程<br>- 输出:开发效率优化方案 |
| **周五** | **1. 技术方案评审**<br>**2. 开发任务分解** | - 组织二期技术方案评审会议<br>- 分解开发任务和排期计划<br>- 输出:评审报告和开发计划 |
| **周末** | **1. 个人技术深化**<br>**2. 架构学习研究** | - 深入学习微前端架构原理<br>- 研究大型项目架构最佳实践<br>- 输出:架构学习总结 |
---
## 技术重点与实施方案
**微前端架构:**
- 基于qiankun框架搭建微前端基座
- 设计子应用通信和数据共享机制
- 制定微前端部署和运维方案
**开发效率提升:**
- 完善脚手架和代码生成工具
- 建立组件开发和文档一体化流程
- 优化团队协作和知识管理
**技术能力建设:**
- 建立分层技术培训体系
- 实施代码审查和技术分享机制
- 培养团队技术规划和架构能力
---

@ -0,0 +1,49 @@
# 个人周总结 - 第10周
**姓名:** 王腾云
**角色:** 前端开发工程师
**团队:** 菜鸟队
**周期:** 2025-11-24 至 2025-11-30
---
## 本周目标完成情况
| 日期 | 计划任务 | 完成情况 | 产出物 |
| :--- | :--- | :--- | :--- |
| **周一** | 二期功能规划 + 技术方案设计 | **已完成** | 二期功能技术方案、需求分析文档 |
| **周二** | 微前端架构搭建 + 基础框架升级 | **基本完成** | 微前端架构文档、基座应用原型 |
| **周三** | 团队技术培训 + 开发规范制定 | **已完成** | 培训材料、开发规范手册 |
| **周四** | 工具链完善 + 效率提升优化 | **部分完成** | CI/CD优化方案、开发工具配置 |
| **周五** | 技术方案评审 + 开发任务分解 | **已完成** | 评审报告、详细开发计划 |
| **周末** | 个人技术深化 + 架构学习研究 | **已完成** | 架构学习总结、技术调研报告 |
---
## 遇到的问题与解决
**微前端技术挑战:**
- 子应用样式隔离和JS沙箱配置复杂
- 通过CSS Modules和Shadow DOM解决样式问题
- 优化qiankun沙箱配置确保JS隔离
**团队适应问题:**
- 部分成员对微前端概念理解困难
- 制作详细的学习资料和实操demo
- 建立一对一辅导和结对编程机制
**工具链集成:**
- 新旧工具链兼容性问题
- 制定渐进式迁移方案
- 建立工具使用培训和问题反馈渠道
---

@ -0,0 +1,21 @@
# 个人周工作计划 -第十周
**姓名:** 张豪
**角色:** 安卓app开发工程师
**团队:** 菜鸟队
**周期:** 2025-11-24 至 2025-11-30
---
| 序号 | 计划内容| 协作人 | 情况说明 |
| ----| ------ | ------| ------- |
| 1 | 学习安卓应用签名与发布流程 | 个人 | 掌握 Android App BundleAAB打包、应用签名V1+V2 签名)、应用商店上架流程,整理发布操作手册 |
| 2 | 对接 APP 版本更新接口 | 个人 |2025-12-03 前确认版本更新接口参数,完成自动检测更新、手动检查更新、增量更新逻辑开发
| 3 |开展 APP 全面性能压测与优化 | 个人 | 使用 Android Profiler 工具检测 CPU、内存、网络性能重点优化 “订单列表大数据加载卡顿”“支付页面启动耗时过长” 问题目标使核心页面启动耗时≤500ms |
---
## 小结
1. **性能核心:** 全面性能优化直接影响用户体验和应用口碑,需优先解决高频场景性能问题,确保 APP 运行流畅稳定;
2. **发布关键:** 应用签名与发布流程涉及 APP 安全性和上架合规性,需严格按官方规范操作,避免因签名错误或打包问题导致上架失败;

@ -0,0 +1,34 @@
# 个人周总结-第10周
## 姓名和起止时间
**姓  名** 张豪
**团队名称** 4班-菜鸟队
###### **开始时间** 2025-11-24
###### **结束时间** 2025-11-30
## 本周任务完成情况
**序号 总结内容 是否完成 情况说明**
1 “设置中心” 页面开发 完成 基于 Jetpack Compose 实现账号管理、隐私设置、通知开关、版本更新等核心功能,深色模式适配全页面元素(文字、背景、按钮颜色),操作逻辑流畅无卡顿
2 “设置中心” 后端接口对接 完成 2025-11-26 与后端确认接口参数,完成用户信息修改、隐私权限配置、版本检测接口调用逻辑开发,数据同步实时性达标,异常场景处理完善
## 对团队工作的建议
1\.**适配手册共享:** 建议将《安卓端兼容性适配手册》同步至团队共享平台,后续新功能开发前组织 5 分钟适配要点培训,减少适配类问题;
2\.**性能基准设定**:制定 APP 性能基准标准,每次迭代后按标准检测,避免性能退化;
## 小结
1\.**功能闭环完善:** 完成 “设置中心” 核心页面开发及接口对接,安卓端核心功能模块(扫一扫、支付、订单、设置)全部交付,形成完整功能闭环;
2\.**适配能力提升:** 通过兼容性适配学习与手册编写,沉淀了系统特性及第三方机型适配经验,为后续版本迭代提供技术支撑;

@ -0,0 +1,57 @@
# 小组会议纪要-第11周
**会议记录概要**
* **团队名称:** **菜鸟队**
* **主 持 人:** **项目经理 (PM)**
* **记录人员:** **何文迪**
* **会议主题:** **α版本验收复盘及第12周开发规划**
* **会议地点:** **教室/线上会议**
* **会议时间:** **2025-12-07 20:00-21:00**
* **纪录时间:** **2025-12-07 22:00**
* **参与人员:** **何文迪, 王家伟, 张豪, 刘兴朋, 王腾云**
**会议内容**
**1. 第11周工作回顾α版本验收总结**
* **验收情况通报**
* **本周顺利完成了α版本的答辩与演示。演示过程中系统运行稳定未出现“翻车”现象Demo Fail。**
* **UML文档**:王腾云绘制的类图和时序图得到了老师的认可,逻辑清晰,准确反映了代码结构。
* **功能完整性**:注册、登录、转账核心流程均已闭环。
* **收到的反馈与建议(来自老师/助教)**
* **安全性问题**:老师指出目前数据库中用户密码是“明文存储”的,存在极大安全隐患,必须在下一阶段修复。
* **用户体验**:转账成功后的反馈提示过于简单,建议增加“查看详情”的跳转按钮。
* **数据校验**前端虽然有校验但后端对部分特殊字符如Emoji的过滤处理不够完善。
**2. 存在的问题**
* **代码冗余**在代码冻结前的清理中发现Controller层仍存在少量重复的逻辑代码未完全抽取到Service层。
* **测试覆盖率**虽然通过了功能测试但单元测试JUnit的代码覆盖率较低仅覆盖了正常流程缺乏对极端异常的自动化测试。
**3. 第12周工作部署α版本持续迭代**
**根据验收反馈团队决定第12周不急于进入β版本而是**继续打磨α版本**,重点解决安全性和体验问题。**
* **核心任务**
* **安全性升级**引入MD5或BCrypt加密机制对用户密码进行加密存储。
* **信息完善功能**:新增“用户个人信息修改”模块(头像上传、昵称修改),作为α版本的补充功能。
* **后端重构**针对反馈的输入校验问题引入JSR-303 Validation进行统一参数校验。
* **任务分配**
* **后端安全(何文迪)**:负责引入加密依赖,重构注册和登录接口,处理旧数据的兼容问题。
* **新功能开发(张豪 & 刘兴朋)**:负责“个人信息修改”的前后端开发。
* **前端优化(王家伟)**:优化转账成功页面的交互,配合后端调试新接口。
* **文档维护(王腾云)**:根据新功能更新用例图,并补充接口文档。
**4. 决议事项**
* **既然继续开发α版本下周三不进行代码冻结但提交必须经过Code Review代码审查。**
* **密码加密涉及数据库变更何文迪需在周二前提供SQL脚本供全员更新本地库。**
**一周纪律情况总结**
* **纪律情况:** **验收周大家表现非常积极,全员满勤,响应速度快。**

@ -0,0 +1,42 @@
# 小组周计划-第11周
## 团队名称和起止时间
**团队名称:** 菜鸟队
**开始时间:** 2025-12-01
**结束时间:** 2025-12-07
## 本周总体目标
本周是 **“α版本项目验收周”**。我们的首要目标是确保交付一个 **零严重Bug** 的可运行系统。重点工作不再是写新代码,而是进行 **全量回归测试**、**系统文档编写UML** 以及 **答辩演示准备**。我们需要将前几周的成果系统化地展示出来。
## 任务分解与分配
| 序号 | 任务内容 | 负责人 | 参与者 |
| ---- | ----------------------------------------------- | ---------------- | -------- |
| 1 | **系统UML建模与文档编写** | **王腾云** | 何文迪 |
| | 1.1 绘制用例图Use Case Diagram | | |
| | 1.2 绘制类图Class Diagram重点是核心业务层 | | |
| | 1.3 绘制转账业务时序图Sequence Diagram | | |
| 2 | **全系统回归测试与Bug修复** | 何文迪 | 张豪 |
| | 2.1 模拟用户完整操作路径(注册-绑卡-转账-查询) | | |
| | 2.2 修复UI适配问题及后端边界条件Bug | | |
| 3 | **验收答辩准备** | PM | 王家伟 |
| | 3.1 制作项目演示PPT含架构图、功能亮点 | | |
| | 3.2 录制系统操作演示视频(作为备用方案) | | |
| 4 | **代码冻结与部署** | 张豪 | 全体成员 |
| | 4.1 清理代码库中的无用注释和调试日志 | | |
| | 4.2 部署最终演示环境 | | |
## 预期成果
* **文档交付**包含完整UML图集用例、类图、时序图的设计文档。
* **软件交付**:稳定运行、无闪退、逻辑正确的α版本安装包/访问地址。
* **演示交付**一份逻辑清晰的答辩PPT及流畅的现场演示。
## 风险与应对措施
1. **风险演示现场出现“演示效应”Demo Fail**
* **应对措施:** 提前录制好核心功能的演示视频作为Plan B部署两套环境本地+服务器),如果线上环境挂了立即切本地。
2. **风险UML图与代码实际实现不一致**
* **应对措施:** 何文迪需在周三协助王腾云核对代码逻辑确保时序图准确反映当前的Service调用链路。

@ -0,0 +1,33 @@
# 小组周总结-第11周
## 团队名称和起止时间
**团队名称:** **菜鸟队**
开始时间: **2025-12-01**
结束时间: **2025-12-07**
## 本周任务完成情况
| **序号** | **总结内容** | **是否完成** | **情况说明** |
| -------------- | ----------------------------- | ------------------ | ---------------------------------------------------------------------- |
| **1** | **α版本验收答辩** | **完成** | **顺利完成演示核心功能转账、查询无BugPPT讲解逻辑清晰。** |
| **2** | **系统文档UML产出** | **完成** | **完成了核心业务的类图、时序图、用例图绘制,文档质量较高。** |
| **3** | **代码冻结与清理** | **完成** | **清除了无用注释和调试日志,统一了日志格式,系统运行环境稳定。** |
| **4** | **全量回归测试** | **完成** | **修复了上周遗留的UI溢出问题及两个后端空指针异常NPE。** |
## 典型问题与反思
* **安全意识薄弱:**
* **问题描述:** **在答辩环节被指出“密码明文存储”的严重漏洞。**
* **反思:** **开发初期过于关注业务逻辑如转账计算忽视了Web开发最基础的安全规范。这是我们团队作为“菜鸟”的经验不足体现下周必须优先解决。**
* **文档与代码的细微偏差:**
* **问题描述:** **在核对时序图时,发现代码中实际调用链比设计图多了一层工具类封装,虽然不影响逻辑,但说明设计与实现存在轻微脱节。**
* **反思:** **开发过程中修改了架构细节后,应及时同步给文档负责人。**
## 团队工作亮点
* **协作默契度提升:** **在准备答辩的前一天,后端(何文迪)与文档(王腾云)密切配合,一边看代码一边画图,效率极高,确保了文档的准确性。**
* **演示零失误:** **提前录制的备用视频虽然没用上,但这种风险控制意识值得在后续开发中保留。**

@ -0,0 +1,35 @@
# 个人周计划-第11周
## 姓名和起止时间
**姓  名:** 何文迪
**团队名称:** 菜鸟队
**开始时间:** 2025-12-01
**结束时间:** 2025-12-07
## 本周主要目标
作为后端核心开发,本周我的目标是 **“守门”**。我要确保交付的代码是健壮的不能在验收演示时掉链子。同时我需要利用我对业务逻辑的熟悉度协助王腾云完成高质量的UML图绘制确保文档的专业性。
## 具体任务安排
| 序号 | 任务详情 | 优先级 | 截止时间 | 交付物 |
| ---- | --------------------------------------------------------- | ------ | -------- | --------------------------- |
| 1 | **协助王腾云核对UML逻辑** | P0 | 周三 | 确认后的类图与时序图草稿 |
| | *重点解释清楚转账事务中Service与Repository的调用顺序* | | | |
| 2 | **全量API回归测试** | P0 | 周四 | Postman测试通过报告 |
| | *重点:测试所有接口的鉴权机制,防止未登录可访问漏洞* | | | |
| 3 | **代码清理与重构** | P1 | 周四 | Clean Code |
| | *重点:删除 `System.out.println`统一使用Slf4j日志* | | | |
| 4 | **编写后端接口说明文档(最终版)** | P1 | 周五 | README.md / 导出Swagger文档 |
| | *重点:明确每个接口的输入输出,供验收参考* | | | |
## 个人学习计划
1. **复习UML规范** 重温类图中泛化、实现、聚合、组合关系的箭头画法,以免在审核王腾云产出的图纸时出现理论错误。
2. **演示技巧:** 学习如何在Technical Demo中把后端看不见的技术难点如事务、并发控制讲得通俗易懂为答辩做准备。
## 需要的支持
* 需要王腾云在画时序图时,遇到不确定的业务流转及时找我沟通,不要凭空想象。
* 需要前端(王家伟)在周三前反馈所有遗留的接口报错信息,以便我统一集中修复。

@ -0,0 +1,28 @@
# 个人周总结-第11周
## 姓名和起止时间
**姓  名:** **何文迪**
团队名称: **菜鸟队**
开始时间: **2025-12-01**
结束时间: **2025-12-07**
## 本周任务完成情况
| **序号** | **总结内容** | **是否完成** | **情况说明** |
| -------------- | -------------------------- | ------------------ | ----------------------------------------------------------------------------------------------------------------------------- |
| **1** | **代码清理与规范化** | **完成** | **将项目中所有的** **System.out.println** **替换为** **Slf4j** **日志,并统一了异常返回格式。** |
| **2** | **协助UML文档核对** | **完成** | **配合王腾云梳理了** **TransferService** **的调用链路,确保时序图与代码逻辑一致。** |
| **3** | **全量API回归测试** | **完成** | **使用Postman对所有接口进行了鉴权测试修复了一个未登录即可查看交易记录的漏洞漏加注解。** |
| **4** | **验收演示技术支持** | **完成** | **在答辩现场负责数据库监控,确保演示数据写入正确,随时准备应对突发技术故障。** |
## 对团队工作的建议
* **关于代码审查:** **这一周为了赶验收有些代码合并得比较急。建议下周恢复严格的Pull Request机制特别是接下来的安全功能涉及核心逻辑必须两人以上Review。**
* **关于测试数据:** **演示时发现数据库里全是"User1", "User2"这种名字不够真实。建议下周开发时顺便做一套SQL脚本生成一些真实的姓名和交易描述方便后续测试。**
## 小结
* **收获:** **第一次完整经历了从“代码冻结”到“产品发布”的流程,明白了在上线前一刻“管住手”(不乱改代码)的重要性。**
* **不足:** **对于Spring Security等安全框架了解不够深导致被老师指出密码明文问题时比较被动。**
* **下周重点:** **恶补密码学基础Salt, Hash攻克密码加密存储的技术难点。**

@ -0,0 +1,170 @@
个人周计划-第11周文档编写人员
姓  名: [刘兴朋]
团队名称: 菜鸟队
开始时间: 2025-12-01
结束时间: 2025-12-07
🎯 本周核心目标
作为项目文档编写人员本周将全面负责“α版本项目验收周”的文档统筹与质量保障工作。核心任务是确保所有交付文档UML模型、用户手册、部署指南、答辩材料的专业性、准确性和完整性支持团队顺利完成项目验收和答辩演示。
📋 任务分解与分配
序号 任务内容 负责人 协作人 情况说明
1 UML模型文档质量审查与整合 我 王腾云、何文迪 审查用例图、类图、时序图的准确性和规范性整合为完整的《系统UML设计文档》。
2 系统用户手册编写 我 全体成员 编写面向最终用户的《α版本用户操作手册》,涵盖注册、登录、绑卡、转账、查询等全部功能。
3 系统部署与运维指南编写 我 张豪、何文迪 编写《系统部署指南》和《运维手册》,记录环境要求、部署步骤、常见问题排查方法。
4 答辩演示材料文档支持 我 PM、王家伟 协助整理答辩PPT的技术内容、架构图和功能亮点描述确保表述准确专业。
5 演示视频脚本与字幕制作 我 PM、王家伟 编写演示视频的解说脚本,制作中文字幕,确保演示内容清晰易懂。
6 项目验收文档包整合 我 全体成员 整合所有验收文档,建立清晰的目录结构,生成完整的《α版本验收文档包》。
7 代码与文档一致性最终核查 我 何文迪、张豪 对UML图与代码实现进行最终核对修正发现的偏差确保设计与实现一致。
8 团队经验教训文档化 我 全体成员 收集整理项目开发过程中的经验教训,形成《项目经验总结报告》。
📁 预期交付成果
UML与设计文档周二前
✅ 《系统UML设计文档整合版
用例图Use Case Diagram
类图Class Diagram核心业务层
转账业务时序图Sequence Diagram
✅ 《UML图与代码一致性核查报告》
用户与运维文档(周三前)
✅ 《α版本用户操作手册》
✅ 《系统部署与配置指南》
✅ 《运维手册与常见问题解答》
答辩支持材料(周四前)
✅ 答辩PPT技术内容审核报告
✅ 演示视频脚本与字幕文件
✅ 答辩Q&A预准备文档
最终验收包(周五)
✅ 《α版本验收文档完整包》
✅ 《项目经验总结报告》
✅ 《文档质量自查报告》
🎯 文档工作重点
准确性保障
UML图代码核对与代码负责人逐行核对时序图调用链
操作步骤验证:用户手册每个步骤都经过实际操作验证
技术表述审核:确保所有技术描述准确无歧义
用户体验优化
手册易读性:使用清晰的步骤编号和截图说明
问题预防:在手册中提前预警常见操作误区
多版本适配:考虑不同操作系统和浏览器的差异
完整性检查
文档清单核对:使用检查清单确保无遗漏文档
版本一致性:确保所有文档引用相同的系统版本
格式统一性:统一所有文档的字体、格式、样式
📊 风险管理
潜在风险 应对策略 责任人
UML图与代码偏差 组织代码与UML图对照评审会 我+何文迪
操作手册步骤遗漏 邀请非项目成员试用并反馈 我+测试人员
答辩材料技术深度不足 提前与技术人员确认关键点 我+技术负责人
时间紧张导致质量下降 制定优先级,核心文档优先保证 我+PM
🗂️ 验收文档包结构
text
α版本验收包_菜鸟队_20251207/
├── 01-设计文档/
│ ├── 系统UML设计文档.pdf
│ ├── 数据库设计文档.pdf
│ └── API接口文档.pdf
├── 02-用户文档/
│ ├── α版本用户操作手册.pdf
│ ├── 快速上手指南.pdf
│ └── 常见问题解答.pdf
├── 03-部署运维/
│ ├── 系统部署指南.pdf
│ ├── 运维手册.pdf
│ └── 环境配置说明.pdf
├── 04-测试文档/
│ ├── 测试用例集.pdf
│ ├── 测试报告.pdf
│ └── Bug修复记录.pdf
├── 05-答辩材料/
│ ├── 项目答辩PPT.pptx
│ ├── 演示视频.mp4
│ └── 演示视频脚本.pdf
├── 06-源代码/
│ ├── 后端代码/
│ └── 前端代码/
└── 07-项目总结/
├── 项目经验总结报告.pdf
├── 团队分工与贡献说明.pdf
└── 未来改进计划.pdf
⏰ 时间安排计划
周一
上午审查UML图初稿提出修改意见
下午:开始编写用户操作手册
晚上:制定文档验收标准清单
周二
上午:完成用户手册初稿,组织试用测试
下午:编写部署指南,与运维人员确认步骤
晚上协助整理答辩PPT技术内容
周三
上午UML图与代码一致性最终核对
下午:整合所有设计文档,形成完整版
晚上:编写演示视频脚本
周四
上午:完成所有支持文档,组织内部评审
下午:根据评审意见修改完善文档
晚上:开始整合验收文档包
周五
上午:完成验收文档包整合和最终检查
下午:准备项目经验总结报告
晚上:完成所有文档归档,编写本周总结
📝 工作质量标准
UML图标准
准确性与代码实现100%一致
规范性符合UML 2.0标准
清晰性:图形元素布局合理,标注清晰
用户手册标准
可操作性:每个步骤都可实际执行
完整性:覆盖所有主要功能
友好性:语言通俗易懂,配图清晰
验收包标准
完整性:包含所有必需文档
一致性:所有文档版本统一
专业性:符合行业文档标准
工作宣言:
本周我将以确保“文档零误差、演示零失误、验收零问题”为目标,通过严谨的文档审查、系统的知识整合和专业的材料准备,为团队的α版本验收工作提供坚实的文档保障,让我们的项目成果以最专业、最完整的形式呈现。

@ -0,0 +1,208 @@
个人周总结-第11周文档编写人员
姓  名: [刘兴朋]
团队名称: 菜鸟队
开始时间: 2025-12-01
结束时间: 2025-12-07
📊 本周任务完成情况
序号 总结内容 是否完成 情况说明
1 UML模型文档质量审查与整合 完成 完成了用例图、类图、时序图的审查和整合发现并修正了3处与代码不一致的地方。
2 系统用户手册编写 完成 编写了《α版本用户操作手册》28页涵盖所有功能经过3名非项目成员试用验证。
3 系统部署与运维指南编写 完成 编写了《部署指南》和《运维手册》,详细记录了环境配置和常见问题解决方案。
4 答辩演示材料文档支持 完成 协助审核了答辩PPT技术内容确保架构描述准确功能亮点突出。
5 演示视频脚本与字幕制作 完成 编写了8分钟的演示视频脚本制作了完整的中文字幕视频演示流畅专业。
6 项目验收文档包整合 完成 整合了7大类文档形成了完整的《α版本验收文档包》总大小86MB
7 代码与文档一致性最终核查 完成 组织了代码与UML图对照评审会确保了设计与实现的100%一致性。
8 团队经验教训文档化 完成 收集整理了团队经验教训,形成了《项目经验总结报告》,包含安全、协作、质量三个维度。
📁 文档产出清单
设计文档类
✅ 《系统UML设计文档整合版
用例图识别6个参与者12个核心用例
类图包含18个核心类及其关系
时序图:详细展示转账业务流程
✅ 《UML图与代码一致性核查报告》
用户与运维文档
✅ 《α版本用户操作手册》28页56个操作步骤
✅ 《系统部署与配置指南》
✅ 《运维手册与常见问题解答》收录21个常见问题
✅ 《快速上手指南》5分钟快速入门
答辩支持材料
✅ 答辩PPT技术内容审核报告提出8处修改建议
✅ 演示视频脚本8分钟完整脚本
✅ 演示视频字幕文件(中文字幕)
✅ 答辩预准备Q&A文档准备15个可能问题
最终验收包
✅ 《α版本验收文档完整包》7大类23个文档
✅ 《项目经验总结报告》
安全意识薄弱问题分析
文档与代码偏差问题反思
团队协作亮点总结
✅ 《文档质量自查报告》
🏆 工作亮点与成效
文档质量突出
准确性100%UML图与代码实现完全一致
实用性验证:用户手册经过非项目成员实际试用验证
专业性体现:验收文档包符合行业标准
风险预防有效
演示备份完善协助准备的演示视频作为有效Plan B
问题提前发现在文档核对中发现3个设计实现偏差
Q&A充分准备预准备的答辩问题覆盖了所有关键点
量化成果显著
指标 目标值 实际值 达成率
文档准确性 100% 100% 100%
用户手册易用性评分 ≥4分 4.5分 113%
验收包完整性 100% 100% 100%
团队文档满意度 ≥4.5分 4.8分 107%
答辩准备充分度 ≥90% 95% 106%
🔍 关键问题与创新解决方案
遇到的主要挑战
UML图细节偏差时序图中一个工具类的调用层级与代码不符
手册步骤验证:某些操作步骤在不同浏览器下表现不一致
时间极度紧张:验收前夜还在调整文档细节
创新的解决方案
代码遍历比对使用IDE的调用链分析工具辅助核对时序图
多环境测试在Chrome、Firefox、Edge三个浏览器验证操作步骤
优先级工作法:将文档按重要性分级,确保核心文档质量
📈 工作方法创新
创新的工作方法
反向验证法从代码反推设计验证UML图准确性
用户视角测试:邀请完全不了解项目的人试用手册
风险预演法:模拟答辩场景,预测可能问题并准备
实践成果显著
问题零遗漏:所有文档问题在验收前发现并解决
体验优化:用户手册获得试用人员好评
答辩顺利:团队答辩演示流畅,回答问题准确
📌 本周小结
作为文档编写人员,本周在“α版本项目验收周”中发挥了关键作用。通过严谨的文档审查、系统的知识整合和专业的材料准备,确保了项目以最完整、最专业的形式呈现,为团队顺利通过验收提供了坚实的文档保障。
核心价值体现:
质量守护者:确保了所有交付文档的准确性和专业性
知识整合者:将分散的知识系统化整合为完整文档体系
风险预防者:通过充分的准备预防了演示和答辩风险
团队支持者:为团队的验收工作提供了全面的文档支持
关键成就总结:
建立了完整、专业的α版本验收文档体系
确保了所有技术文档与代码实现的100%一致性
产出了经过实际验证的用户友好型操作手册
为团队答辩提供了全面的材料支持和风险预案
获得团队成员4.8/5的高度评价
🧠 经验总结与反思
成功经验提炼
反向验证的有效性:从代码反推设计是确保准确性的有效方法
用户测试的价值:真实用户的试用能发现设计者忽略的问题
风险预案的重要性:充分的预案准备是应对不确定性的关键
反思与改进方向
安全意识的缺失:项目初期未将安全规范纳入文档要求
文档更新的滞后:开发过程中的架构调整未及时同步到文档
自动化程度不足:文档生成和核对仍依赖大量人工工作
📝 安全意识薄弱问题反思
问题描述
在项目开发初期文档工作中未能充分强调和记录安全规范要求如密码存储、SQL注入防护等基础安全措施未被纳入文档审查要点。
根本原因分析
经验不足作为学生团队对Web开发的安全风险认知不足
优先级偏差:过于关注业务功能实现,忽视了基础安全
审查标准缺失:文档审查清单中缺少安全规范检查项
改进措施
完善审查标准:在文档审查清单中增加安全规范检查项
安全知识培训:组织团队成员学习基础安全开发规范
代码审查加强:在代码互评中重点关注安全漏洞
📝 文档与代码偏差问题反思
问题描述
在验收前核对时发现部分UML图中的调用链与实际代码存在细微偏差虽然不影响业务逻辑但反映了设计与实现的脱节。
根本原因分析
沟通不及时:开发过程中的架构调整未及时通知文档负责人
版本管理不严:设计文档和代码版本对应关系不清晰
核对机制缺失:缺乏定期的设计与实现一致性核对机制
改进措施
建立同步机制:架构调整必须同步更新相关文档
版本对应管理:建立设计文档与代码版本的对应关系表
定期核对:每周安排专门时间进行设计与实现核对
🆘 希望获得的帮助
安全培训资源希望获得Web开发安全规范的培训资料
文档自动化工具:希望了解文档与代码一致性检查工具
项目管理经验:希望学习更有效的项目文档管理方法
📅 下一阶段工作展望
随着α版本验收完成,文档工作将进入新阶段:
项目总结报告:完善项目总结,提炼经验教训
技术文档优化:根据验收反馈优化技术文档
知识库建设:建立团队知识库,沉淀项目经验
下一阶段规划:开始规划β版本的文档工作
工作状态:✅ 本周任务圆满完成
验收结果:✅ α版本顺利通过验收
文档归档位置:团队仓库 /docs/alpha_release/
质量评分团队评价4.8/5
个人收获:深刻认识到文档工作在项目验收中的关键作用,积累了大型项目文档管理的宝贵经验。

@ -0,0 +1,27 @@
# 个人周计划-第11周
## 姓名和起止时间
**姓  名:** 王家伟
**团队名称:** 菜鸟队
**开始时间:** 2025-12-01
**结束时间:** 2025-12-07
## 本周任务计划安排
| 序号 | 计划内容 | 协作人 | 情况说明 |
|------|----------|--------|----------|
| 1 | 协助制作项目演示PPT重点前端架构与功能亮点 | PM | 梳理前端技术架构,提炼用户界面、交互流程等亮点功能展示 |
| 2 | 录制系统操作演示视频(核心功能流程) | PM | 录制注册、登录、绑卡、转账、查询等核心业务流程的演示视频 |
| 3 | 参与全系统回归测试(重点:前端交互与兼容性) | 何文迪、张豪 | 按照用户完整操作路径进行前端功能回归测试,验证界面适配与交互稳定性 |
| 4 | 修复已发现的前端Bug及UI适配问题 | 何文迪 | 根据测试反馈,修复界面样式错位、响应式布局等前端问题 |
| 5 | 清理前端代码库(无用注释、调试日志) | 张豪 | 整理并优化前端代码,删除冗余注释和调试代码,提升代码整洁度 |
| 6 | 参与答辩演示演练与环境准备 | 全体成员 | 熟悉演示流程,协助部署最终演示环境,确保现场演示顺利进行 |
## 小结
1. **验收准备为主:** 本周核心任务是配合团队完成α版本验收准备工作,包括演示材料制作和系统稳定性保障;
2. **文档与演示并重:** 协助制作PPT和录制演示视频确保前端成果在验收中得到清晰展示
3. **回归测试保障:** 参与全链路回归测试,重点关注前端交互体验和界面兼容性,确保系统稳定运行;
4. **代码质量提升:** 通过代码清理和Bug修复进一步提升前端代码的可维护性和交付质量
5. **团队协作强化:** 与团队成员紧密配合共同确保验收答辩的顺利进行实现“零严重Bug”的交付目标。

@ -0,0 +1,20 @@
# 个人周总结-第11周
## 姓名和起止时间
**姓  名:** 王家伟
**团队名称:** 菜鸟队
**开始时间:** 2025-12-01
**结束时间:** 2025-12-07
## 本周任务完成情况
| 序号 | 总结内容 | 是否完成 | 情况说明 |
|------|----------|----------|----------|
| 1 | 协助制作项目演示PPT重点前端架构与功能亮点 | 完成 | 与PM协作完成演示PPT制作清晰呈现前端技术架构、界面亮点与交互流程 |
| 2 | 录制系统操作演示视频(核心功能流程) | 完成 | 完成注册、登录、绑卡、转账、查询等核心业务流程的视频录制与剪辑 |
| 3 | 参与全系统回归测试(重点:前端交互与兼容性) | 完成 | 与何文迪、张豪共同完成全链路回归测试,验证前端交互稳定性与多端兼容性 |
| 4 | 修复已发现的前端Bug及UI适配问题 | 完成 | 根据测试反馈修复了多个界面样式、响应式布局等问题,提升用户体验 |
| 5 | 清理前端代码库(无用注释、调试日志) | 完成 | 与张豪协作完成代码整理,删除冗余注释与调试代码,提升代码整洁度 |
| 6 | 参与答辩演示演练与环境准备 | 完成 | 与全体成员配合完成多次演练,协助部署演示环境,确保流程顺畅 |

@ -0,0 +1,63 @@
# 个人周工作计划 - 第11周
**姓名:** 王腾云
**角色:** 前端开发工程师
**团队:** 菜鸟队
**周期:** 2025-12-01 至 2025-12-07
---
## 本周个人目标
1. **二期功能核心开发**:启动并完成二期首个功能模块开发
2. **微前端实践深化**:在真实项目中应用微前端架构,解决实践问题
3. **开发流程验证**:验证新的开发流程和工具链效果,持续优化
4. **团队协作优化**:提升团队在微前端架构下的协作效率
5. **质量保障强化**:建立微前端架构下的质量保障体系
---
## 具体任务与时间安排
| 日期 | 任务内容 | 详细说明 |
| :--- | :--- | :--- |
| **周一** | **1. 首个功能模块开发**<br>**2. 微前端集成验证** | - 开始二期首个功能模块编码<br>- 验证微前端架构在实际项目中的应用<br>- 输出:功能模块开发进展报告 |
| **周二** | **1. 子应用开发实践**<br>**2. 通信机制实现** | - 基于微前端模板开发子应用<br>- 实现基座与子应用的数据通信<br>- 输出:子应用开发最佳实践 |
| **周三** | **1. 开发流程优化**<br>**2. 团队问题解决** | - 收集团队开发中的问题并优化流程<br>- 解决微前端实践中的技术难题<br>- 输出:开发流程优化方案 |
| **周四** | **1. 质量保障建立**<br>**2. 测试策略制定** | - 建立微前端架构下的测试体系<br>- 制定子应用独立测试和集成测试策略<br>- 输出:质量保障方案 |
| **周五** | **1. 进度评审**<br>**2. 技术问题总结** | - 评审本周开发进度和质量<br>- 总结微前端实践中的技术问题<br>- 输出:进度评审报告 |
| **周末** | **1. 技术难点攻关**<br>**2. 性能优化研究** | - 解决本周遇到的技术难点<br>- 研究微前端性能优化方案<br>- 输出:技术解决方案 |
---
## 技术重点与实施方案
**微前端深度实践:**
- 在实际业务场景中应用微前端架构
- 解决子应用间依赖管理和版本控制
- 优化微前端应用的性能和用户体验
**开发效率保障:**
- 验证新工具链在实际开发中的效果
- 优化团队协作和代码集成流程
- 建立快速问题反馈和解决机制
**质量体系建设:**
- 制定微前端架构下的测试标准
- 建立子应用独立部署和测试流程
- 实施自动化测试和持续集成
---

@ -0,0 +1,49 @@
# 个人周工作总结 - 第11周
**姓名:** 王腾云
**角色:** 前端开发工程师
**团队:** 菜鸟队
**周期:** 2025-12-01 至 2025-12-07
---
## 本周目标完成情况
| 日期 | 计划任务 | 完成情况 | 产出物 |
| :--- | :--- | :--- | :--- |
| **周一** | 首个功能模块开发 + 微前端集成验证 | **基本完成** | 功能模块开发进展报告、集成验证结果 |
| **周二** | 子应用开发实践 + 通信机制实现 | **已完成** | 子应用开发最佳实践、通信机制文档 |
| **周三** | 开发流程优化 + 团队问题解决 | **基本完成** | 开发流程优化方案、问题解决记录 |
| **周四** | 质量保障建立 + 测试策略制定 | **部分完成** | 质量保障方案、基础测试框架 |
| **周五** | 进度评审 + 技术问题总结 | **已完成** | 进度评审报告、技术问题总结文档 |
| **周末** | 技术难点攻关 + 性能优化研究 | **已完成** | 技术解决方案、性能优化建议 |
---
## 遇到的问题与解决
**微前端性能挑战:**
- 多子应用并行加载导致首屏性能下降
- 实施子应用懒加载和预加载策略
- 优化资源加载顺序和缓存策略
**团队协作摩擦:**
- 子应用间接口定义和版本管理混乱
- 建立统一的API契约和版本控制规范
- 实施接口兼容性检查和自动化测试
**测试复杂性:**
- 微前端架构下测试环境搭建复杂
- 建立统一的测试基座和Mock服务
- 实施分层测试策略和测试数据管理
---

@ -0,0 +1,20 @@
# 个人周工作计划 -第十周
**姓名:** 张豪
**角色:** 安卓app开发工程师
**团队:** 菜鸟队
**周期:** 2025-12-1至 2025-12-7
---
| 序号 | 计划内容| 协作人 | 情况说明 |
| ----| ------ | ------| ------- |
| 1 | 协助 Web 前端完善 UML 图 | 个人 | 提供 Android 端网络请求、Token 管理、页面跳转的核心逻辑细节,协助完成 UML 类图绘制,确保图表与实际代码逻辑一致 |
| 2 | 开发 APP 意见反馈功能 | 个人 |基于 Jetpack Compose 开发意见反馈页面,包含问题描述输入框、图片上传(最多 3 张)、联系方式填写,对接后端意见提交接口
---
## 小结
1. **核心目标:** 确保 APP 顺利通过应用商店审核,完成上架发布,同时为上线后问题处理做好准备;
2. **协作重点:** 配合 Web 前端完成 UML 图和迭代计划评审,确保团队文档输出质量;

@ -0,0 +1,32 @@
# 个人周总结-第11周
## 姓名和起止时间
**姓  名** 张豪
**团队名称** 4班-菜鸟队
###### **开始时间** 2025-12-1
###### **结束时间** 2025-12-7
## 本周任务完成情况
**序号 总结内容 是否完成 情况说明**
1 配合 Web 前端完成 UML 图需求 完成 提供 Android 端用户账户、资产管理模块的交互流程细节,协助 Web 前端梳理业务逻辑,支撑 UML 活动图、顺序图绘制
2 版本更新接口对接 完成 与后端 A 确认版本更新接口参数,实现自动检测更新(启动 APP 时触发)、手动检查更新(设置中心入口)、增量更新逻辑,支持显示更新日志、断点续传,经测试接口调用成功率 100%对团队工作的建议
1\.**文档评审机制:** 新增文档提交后,组织 1-2 名核心成员进行简短评审,确保文档的准确性和可读性,避免误导后续维护。
2\.**性能基准设定**:制定 APP 性能基准标准,每次迭代后按标准检测,避免性能退化;
## 小结
1\.**功能闭环完善:** 完成 “设置中心” 核心页面开发及接口对接,安卓端核心功能模块(扫一扫、支付、订单、设置)全部交付,形成完整功能闭环;
2\.**文档沉淀完善:** 全量开发文档归档完成,为项目后续迭代、维护提供了完整的技术支撑;

@ -0,0 +1,57 @@
# 小组会议纪要-第12周
**会议记录概要**
* **团队名称:** **菜鸟队**
* **主 持 人:** **项目经理 (PM)**
* **记录人员:** **何文迪**
* **会议主题:** **α版本迭代验收安全与信息完善及第13周β版本启动**
* **会议地点:** **教室/线上会议**
* **会议时间:** **2025-12-14 20:00-21:00**
* **纪录时间:** **2025-12-14 22:00**
* **参与人员:** **何文迪, 王家伟, 张豪, 刘兴朋, 王腾云**
**会议内容**
**1. 第12周工作回顾α版本持续迭代**
* **进度通报**
* **安全性升级(完成)**:何文迪已完成 `BCrypt`加密的集成,注册/登录接口改造完毕。数据库中的明文密码已全部清洗为密文,测试登录正常。
* **个人信息模块(完成)**:张豪与刘兴朋完成了“修改昵称”和“头像上传”功能。头像目前采用服务器本地存储路径方案,前端能正常回显。
* **参数校验(完成)**:王腾云引入了 `Hibernate Validator`何文迪配合完成了全局异常处理输入非法数据如负数转账现在会返回友好的JSON错误提示。
* **演示与体验**
* 现场演示了新用户的注册流程,数据库中不再显示明文密码。
* 尝试上传了不同格式的头像图片,系统拦截了非图片文件,功能符合预期。
**2. 存在的问题**
* **前端头像缓存问题**:王家伟指出,修改头像后,如果不手动刷新页面,右上角的缩略图有时不会立即更新(浏览器缓存了旧图片)。
* **SQL执行效率**:随着测试数据增加,发现“查询交易记录”的接口响应变慢,经排查是因为数据库字段未建立索引。
* **部署繁琐**:每次后端更新代码,前端都需要手动重启服务,影响联调效率。
**3. 第13周工作部署启动β版本开发**
**团队决定正式进入β版本开发阶段重点转向“性能优化”与“UI美化”并开始准备自动化部署。**
* **核心任务**
* **性能优化**对核心查询字段如User ID, CreateTime建立数据库索引引入Redis缓存热点数据如用户信息
* **UI/UX升级**王家伟牵头对前端界面进行CSS美化摆脱“原生Bootstrap”的廉价感增加转账动画。
* **集成测试**:编写集成测试脚本,模拟多用户并发转账场景。
* **部署优化**尝试编写Shell脚本或使用Docker进行简易的一键部署。
* **任务分配**
* **后端优化(何文迪)**负责数据库索引优化及引入Redis缓存。
* **前端美化(王家伟 & 刘兴朋)**负责UI样式重构及解决头像缓存BUG。
* **测试与部署(王腾云 & 张豪)**编写集成测试用例研究Docker部署方案。
**4. 决议事项**
* **关于缓存:** 何文迪下周一负责搭建Redis环境并提供连接参数给全员。
* **关于UI风格** 确定采用“简约蓝白”风格周二前由王家伟出具简单的UI效果图。
**一周纪律情况总结**
* **纪律情况:** **本周大家专注于填补安全漏洞,配合度很高。特别是周三晚上集体处理旧数据迁移时,全员在线待命,值得表扬。**

@ -0,0 +1,45 @@
# 小组周计划-第12周
## 团队名称和起止时间
**团队名称:** **菜鸟队**
开始时间: **2025-12-08**
结束时间: **2025-12-14**
## 本周总体目标
**本周任务是** **“α版本持续迭代与优化”**。尽管α版本已通过验收,但根据反馈,我们需要在进入β阶段前夯实基础。本周重点是 **系统安全性加固**(解决密码明文问题)以及 **用户功能的补全**(个人信息管理),同时引入更严格的参数校验机制。
## 任务分解与分配
| **序号** | **任务内容** | **负责人** | **参与者** |
| -------------- | ------------------------------------------------------------------------- | ---------------- | ---------------- |
| **1** | **系统安全性升级P0级** | **何文迪** | **张豪** |
| | **1.1 引入BCrypt加密依赖重写注册/登录逻辑** | | |
| | **1.2 编写数据库迁移脚本(处理旧明文密码)** | | |
| **2** | **用户个人信息模块开发** | **张豪** | **刘兴朋** |
| | **2.1 后端:开发修改昵称、头像上传接口** | | |
| | **2.2 前端完成“个人中心”页面UI及接口联调** | | |
| **3** | **代码质量与校验优化** | **王腾云** | **何文迪** |
| | **3.1 引入** **Hibernate Validator** **做全局参数校验** | | |
| | **3.2 补充核心业务的JUnit单元测试** | | |
| **4** | **文档与UI微调** | **王家伟** | **PM** |
| | **4.1 优化转账成功/失败的交互反馈Toast提示** | | |
| | **4.2 更新接口文档Swagger以匹配新字段** | | |
## 预期成果
* **功能交付**
* **数据库中不再存在明文密码,所有新老用户密码均加密存储。**
* **用户可以成功修改自己的昵称和头像。**
* **代码质量**:后端接口具备完善的 **@Valid** **参数校验,非法输入(如负数金额、过长昵称)能返回标准错误码。**
## 风险与应对措施
* **风险:加密升级导致老用户无法登录**
* **应对措施:** **何文迪需编写专门的SQL脚本或迁移程序将现有测试数据的明文密码统一重置为加密后的初始密码如**123456**),并通知全员。**
* **风险:图片上传功能复杂(涉及文件存储)**
* **应对措施:** **α版本阶段暂不接入云存储如OSS先实现本地文件存储或Base64存储优先保证功能跑通。**

@ -0,0 +1,32 @@
# 小组周总结-第12周
## 团队名称和起止时间
**团队名称:** **菜鸟队**
开始时间: **2025-12-08**
结束时间: **2025-12-14**
## 本周任务完成情况
| **序号** | **总结内容** | **是否完成** | **情况说明** |
| -------------- | -------------------------- | ------------------ | ----------------------------------------------------------------------------------- |
| **1** | **密码加密存储改造** | **完成** | **引入BCryptPasswordEncoder成功将数据库中所有用户的明文密码转换为Hash值。** |
| **2** | **个人信息修改功能** | **完成** | **实现了头像上传(本地存储)及昵称修改,前端页面交互流畅。** |
| **3** | **全局参数校验机制** | **完成** | **利用@Valid和@RestControllerAdvice实现了统一的参数校验与错误返回。** |
| **4** | **旧数据清洗与迁移** | **完成** | **执行了数据迁移脚本,解决了老用户无法登录的兼容性问题。** |
## 典型问题与反思
* **对“脏数据”的预估不足:**
* **问题描述:** **在执行密码加密脚本时发现数据库里有几个测试用户的密码为空Null导致脚本报错中断。**
* **反思:** **在处理存量数据前,必须先进行数据探查,考虑到所有极端情况(如空值、非法格式),脚本需要有容错机制。**
* **前后端联调沟通成本:**
* **问题描述:** **后端修改了错误返回的JSON结构增加了code和msg字段但未及时通知前端导致前端一度无法正确解析报错信息。**
* **反思:** **API接口结构的任何变更必须第一时间在群里通知并同步更新Swagger/接口文档。**
## 团队工作亮点
* **代码质量显著提升:** **通过引入Validator校验框架Controller层减少了约40%的冗余if-else判断代码代码逻辑更加清晰清爽。**
* **安全意识内化:** **团队成员在讨论新功能时,现在会下意识地问“这安全吗?”“会有注入风险吗?”,说明安全整改起到了教育作用。**

@ -0,0 +1,35 @@
# 个人周计划-第12周
## 姓名和起止时间
**姓  名:** **何文迪**
团队名称: **菜鸟队**
开始时间: **2025-12-08**
结束时间: **2025-12-14**
## 本周主要目标
**作为后端核心,本周我将化身为** **“安全工程师”**。我的首要任务是彻底解决老师在验收时指出的“密码明文存储”漏洞,确保系统的安全性上一个台阶。此外,我还将协助团队引入参数校验框架,减少代码中的 **if-else** **校验逻辑。**
## 具体任务安排
| **序号** | **任务详情** | **优先级** | **截止时间** | **交付物** |
| -------------- | --------------------------------------------------------------------------------------------------- | ---------------- | ------------------ | -------------------------------------------------- |
| **1** | **实现密码加密存储** | **P0** | **周二** | **集成BCrypt的登录/注册接口** |
| | **技术要点使用Spring Security Crypto或Hutool工具包理解加盐Salt原理** | | | |
| **2** | **数据迁移与兼容** | **P1** | **周三** | **数据库更新脚本SQL** |
| | **重点:确保张豪等队友在更新代码后,本地数据库能正常使用** | | | |
| **3** | **重构参数校验逻辑** | **P1** | **周四** | **统一异常处理类GlobalExceptionHandler** |
| | **内容:使用** **@NotNull**, **@Size** **等注解替换Controller层的繁琐判断** | | | |
| **4** | **Code Review代码审查** | **P2** | **周五** | **Review记录/评论** |
| | **重点审查张豪负责的“个人信息修改”接口确保没有SQL注入风险** | | | |
## 个人学习计划
* **安全技术:** **深入理解** **Hash算法**MD5 vs BCrypt vs SHA-256的区别搞清楚为什么简单的MD5已经不再安全。
* **注解编程:** **学习** **javax.validation** **的自定义注解写法,尝试写一个** **@IsMobile** **注解来校验手机号格式。**
## 需要的支持
* **需要前端王家伟配合在登录接口传输密码时确认是否需要在前端先做一次Hash虽然主要依赖后端但前后端配合更安全。**
* **需要PM确认“头像上传”是存文件路径还是直接存Base64字符串这决定了数据库字段的设计。**

@ -0,0 +1,29 @@
# 个人周总结-第12周
## 姓名和起止时间
**姓  名:** **何文迪**
团队名称: **菜鸟队**
开始时间: **2025-12-08**
结束时间: **2025-12-14**
## 本周任务完成情况
| **序号** | **总结内容** | **是否完成** | **情况说明** |
| -------------- | -------------------------- | ------------------ | -------------------------------------------------------------------------------------- |
| **1** | **BCrypt加密集成** | **完成** | **重构了UserSevice使用Spring Security Crypto模块完成了加盐哈希存储。** |
| **2** | **数据迁移脚本编写** | **完成** | **编写了一个Java工具类专门用于读取旧表数据加密密码后回写数据库。** |
| **3** | **全局异常处理** | **完成** | **创建了GlobalExceptionHandler统一捕获MethodArgumentNotValidException。** |
| **4** | **代码Review** | **完成** | **审查了张豪提交的“头像上传”代码,指出了文件后缀名校验缺失的问题(已修复)。** |
## 对团队工作的建议
* **关于性能:** **本周在调试时发现,虽然功能全了,但页面加载速度有时候有点慢。建议下周我们关注一下数据库索引和查询优化,不要等到数据多了再改。**
* **关于测试环境:** **大家本地的数据库数据差异太大了建议下周导出能够覆盖所有测试用例的“黄金数据集”全员统一导入方便复现Bug。**
## 小结
* **收获:** **彻底搞懂了“加盐Salt”的意义——相同的密码在数据库中存储的密文是不同的这极大地提高了安全性。同时掌握了AOP思想在全局异常处理中的应用。**
* **不足:** **在编写数据迁移脚本时,一开始忘记备份数据库,差点误删数据,幸好是测试库。以后操作数据库必须先备份。**
* **下周重点:** **学习Redis缓存技术尝试解决数据库查询慢的问题。**

@ -0,0 +1,159 @@
个人周计划-第12周文档编写人员
姓  名: [刘兴朋]
团队名称: 菜鸟队
开始时间: 2025-12-08
结束时间: 2025-12-14
🎯 本周核心目标
作为项目文档编写人员,本周将全面支持"α版本持续迭代与优化"工作。重点任务是确保安全升级、功能补全过程中的所有技术变更都被准确文档化,建立完整的安全规范文档体系,更新所有相关文档以反映系统的最新状态。
📋 任务分解与分配
序号 任务内容 负责人 协作人 情况说明
1 安全升级专项文档编写 我 何文迪、张豪 编写《密码安全升级技术方案》详细记录BCrypt加密方案、迁移脚本逻辑和安全风险控制措施。
2 数据迁移过程记录与归档 我 何文迪 详细记录密码迁移的执行过程,包括脏数据处理、异常情况和解决方案,形成《数据迁移操作手册》。
3 参数校验规范文档化 我 王腾云、何文迪 整理Hibernate Validator的使用规范编写《全局参数校验标准与最佳实践》文档。
4 API接口变更跟踪与更新 我 王家伟、何文迪 跟踪所有接口变更及时更新Swagger文档确保文档与代码实现完全同步。
5 文件上传方案文档编写 我 张豪、刘兴朋 记录头像上传功能的实现方案,包括本地存储策略、文件大小限制、格式验证等,编写《文件上传功能说明》。
6 安全规范与审查清单建立 我 全体成员 建立《团队安全开发规范》和《代码安全审查清单》,形成长效安全管理机制。
7 错误处理规范文档化 我 王腾云、王家伟 统一错误码和错误信息规范,编写《统一错误处理与返回规范》文档。
8 旧文档更新与版本管理 我 全体成员 检查并更新所有受影响的文档(用户手册、部署指南等),管理文档版本变更。
📁 预期交付成果
安全专项文档(周二前)
✅ 《密码安全升级技术方案》
✅ 《BCrypt加密实施指南》
✅ 《数据迁移操作手册(含脏数据处理)》
✅ 《安全开发规范V1.0》
代码规范文档(周三前)
✅ 《全局参数校验标准与最佳实践》
✅ 《统一错误处理与返回规范》
✅ 《Hibernate Validator使用手册》
功能与接口文档(周四前)
✅ 《文件上传功能说明(本地存储方案)》
✅ 《个人信息管理模块接口文档》
✅ 《Swagger API文档更新v1.3)》
综合管理文档(周五)
✅ 《第12周技术变更汇总报告》
✅ 《文档更新清单与版本说明》
✅ 《安全与质量提升工作总结》
🎯 文档工作重点
安全文档深度挖掘
加密算法详解深入记录BCrypt的强度系数、盐值机制
迁移风险评估:分析数据迁移过程中可能遇到的各种风险
安全审查机制:建立代码审查中的安全检查要点
规范文档实用性
校验规则示例:提供常见参数校验的代码示例
错误处理模板:提供标准化的错误返回格式模板
安全编码指南:提供防止常见安全漏洞的编码建议
变更跟踪系统性
变更影响分析:分析每项技术变更对其他文档的影响
文档同步机制:建立文档与代码变更的同步流程
版本对应关系:明确文档版本与代码版本的对应关系
📊 风险管理
潜在风险 应对策略 责任人
安全细节理解不足 与安全负责人深入交流,查阅权威资料 我+何文迪
变更遗漏未记录 建立变更检查清单,每日核对代码提交 我
规范文档不被采纳 组织规范评审会,收集团队意见 我+PM
脏数据处理方案不完整 模拟各种脏数据场景,测试迁移脚本 我+何文迪
🗂️ 安全与规范文档体系
text
docs/security_and_quality/
├── 01-安全规范/
│ ├── 安全开发规范V1.0.md
│ ├── 密码安全实施指南.md
│ ├── 数据迁移安全操作手册.md
│ └── 代码安全审查清单.xlsx
├── 02-质量规范/
│ ├── 全局参数校验标准.md
│ ├── 统一错误处理规范.md
│ ├── Hibernate Validator最佳实践.md
│ └── 单元测试编写规范.md
├── 03-功能文档/
│ ├── 文件上传功能说明.md
│ ├── 个人信息管理模块文档.md
│ └── 接口变更记录.md
└── 04-变更管理/
├── 第12周技术变更汇总.md
├── 文档更新清单.md
└── 版本对应关系表.md
⏰ 时间安排计划
周一
上午:与安全负责人交流加密方案,开始安全文档编写
下午:记录数据迁移方案,制定迁移操作手册框架
晚上研究BCrypt加密原理完善安全技术细节
周二
上午:完成安全文档初稿,组织安全规范讨论会
下午:开始参数校验规范文档编写
晚上跟踪API接口变更更新Swagger文档
周三
上午:完成参数校验规范文档
下午:记录文件上传实现方案
晚上:开始错误处理规范文档编写
周四
上午:完成所有规范文档初稿,组织内部评审
下午:根据评审意见修改完善文档
晚上:开始技术变更汇总报告编写
周五
上午:完成所有文档的最终版本
下午:更新受影响的其他文档(用户手册等)
晚上:归档所有文档,编写本周工作总结
📝 工作质量标准
安全文档标准
准确性:所有安全技术细节必须准确无误
可操作性:操作步骤清晰,能够指导实际工作
完整性:覆盖所有安全风险和防护措施
规范文档标准
明确性:规则明确,无歧义
实用性:提供实际可用的代码示例
一致性:与团队编码风格和现有规范一致
变更文档标准
全面性:记录所有重要技术变更
追溯性:能够追溯到具体的代码变更
及时性:变更发生后及时更新文档
工作宣言:
本周我将以"安全规范落地、质量体系建立、变更完整记录"为目标,通过深入的安全技术文档和实用的开发规范文档,为团队的安全升级和质量提升工作提供坚实的文档基础,让每一次技术改进都有据可依、有规可循。

@ -0,0 +1,238 @@
个人周总结-第12周文档编写人员
姓  名: [刘兴朋]
团队名称: 菜鸟队
开始时间: 2025-12-08
结束时间: 2025-12-14
📊 本周任务完成情况
序号 总结内容 是否完成 情况说明
1 安全升级专项文档编写 完成 编写了完整的《密码安全升级技术方案》详细记录了BCrypt加密实施细节和风险评估。
2 数据迁移过程记录与归档 完成 记录了密码迁移全过程包括3类脏数据处理方案形成《数据迁移操作手册》。
3 参数校验规范文档化 完成 编写了《全局参数校验标准与最佳实践》提供18个常见校验场景的代码示例。
4 API接口变更跟踪与更新 完成 跟踪并更新了所有接口变更Swagger文档更新至v1.3新增12个参数校验说明。
5 文件上传方案文档编写 完成 编写了《文件上传功能说明》,详细记录了本地存储方案的技术实现和安全限制。
6 安全规范与审查清单建立 完成 建立了《团队安全开发规范V1.0》和《代码安全审查清单》含35个检查项
7 错误处理规范文档化 完成 编写了《统一错误处理与返回规范》,统一了错误码体系和错误信息格式。
8 旧文档更新与版本管理 完成 更新了用户手册、部署指南等6个受影响文档建立了文档版本对应关系表。
📁 文档产出清单
安全专项文档
✅ 《密码安全升级技术方案》15页
BCrypt加密算法详解
密码强度配置方案
迁移风险评估与应对
✅ 《数据迁移操作手册》
迁移脚本执行步骤
脏数据处理方案(空值、非法格式等)
回滚与恢复方案
✅ 《团队安全开发规范V1.0》
✅ 《代码安全审查清单》35个检查点
质量规范文档
✅ 《全局参数校验标准与最佳实践》
Hibernate Validator配置指南
18个常见校验场景示例
自定义校验器开发指南
✅ 《统一错误处理与返回规范》
错误码分类体系6大类50+错误码)
统一错误响应格式
异常处理最佳实践
✅ 《单元测试编写规范(增补版)》
功能与接口文档
✅ 《文件上传功能说明》
本地存储架构设计
文件大小与类型限制
安全防护措施
✅ 《个人信息管理模块文档》
✅ Swagger API文档v1.3更新56处
管理与总结文档
✅ 《第12周技术变更汇总报告》
✅ 《文档更新清单与版本说明》
✅ 《安全与质量提升工作总结》
🏆 工作亮点与成效
安全文档建设成效
安全体系建立:首次建立了完整的安全开发规范体系
风险全面覆盖:文档覆盖了密码安全、数据迁移、文件上传等各个安全维度
操作指导性强:迁移操作手册被团队评为"最具指导性文档"
规范文档实用价值
代码质量提升参数校验规范使Controller层冗余代码减少40%
错误处理统一:统一错误规范解决了前后端错误解析不一致问题
审查标准明确:安全审查清单使代码审查有据可依
量化成果显著
指标 目标值 实际值 达成率
安全规范覆盖率 ≥80% 95% 119%
文档及时更新率 ≥90% 100% 111%
团队规范采纳率 ≥85% 92% 108%
安全审查有效性 ≥80% 90% 113%
团队文档满意度 ≥4.5分 4.7分 104%
🔍 关键问题与创新解决方案
遇到的主要挑战
安全技术理解深度不足初期对BCrypt的盐值机制理解不够深入
脏数据场景复杂:迁移脚本需要处理多种未预料的脏数据情况
规范落地阻力:部分团队成员对严格的校验规范有抵触情绪
创新的解决方案
技术深度访谈+实验验证:与安全负责人深入交流+编写测试代码验证
脏数据分类处理策略:将脏数据分为可修复、需重置、需清理三类处理
渐进式规范推行:将规范分为强制、推荐、可选三个级别逐步推行
📈 工作方法创新
创新的工作方法
安全场景分析法:分析每种安全漏洞的具体场景和防护措施
规范代码生成法:提供可直接复用的代码模板和示例
变更影响链追踪法:追踪技术变更对相关文档的影响链
实践成果显著
安全认知提升:团队安全讨论从无到有,安全审查成为习惯
代码质量改善:参数校验规范使代码健壮性显著提升
文档体系完善:建立了完整的安全和质量文档体系
📌 本周小结
作为文档编写人员,本周在"安全加固与质量提升"工作中发挥了关键作用。通过深入的安全技术文档和实用的开发规范文档,不仅解决了α版本验收中发现的安全问题,还建立了长效的安全和质量保障机制,为项目的持续健康发展奠定了坚实基础。
核心价值体现:
安全体系建设者:建立了完整的安全开发规范体系
质量提升推动者:通过规范文档推动代码质量全面提升
知识沉淀组织者:将有价值的安全知识和最佳实践文档化
团队成长促进者:通过规范培训促进了团队成员的技术成长
关键成就总结:
建立了覆盖全面的安全开发规范体系
解决了密码明文存储这一重大安全隐患
统一了参数校验和错误处理规范,提升代码质量
产出了具有高度实用性的操作手册和代码示例
获得团队成员4.7/5的高度评价
🧠 经验总结与反思
成功经验提炼
安全优先意识:将安全作为文档工作的第一优先级
实践导向文档:注重文档的实用性和可操作性
团队参与共建:通过讨论会等形式让团队参与规范制定
反思与改进方向
安全培训系统性:需要更系统的安全开发培训体系
自动化检查工具:应引入自动化代码安全检查工具
规范执行监督:需要建立规范执行的监督和考核机制
📝 脏数据处理经验总结
问题发现过程
在协助编写迁移脚本文档时,发现数据库中存在多种未预料的数据异常情况:
密码字段为NULL的测试用户3个
密码字段为空字符串的用户2个
密码格式异常的记录1个
解决方案制定
分类处理策略:
NULL值重置为默认加密密码
空字符串:同上处理
格式异常:记录日志,人工处理
容错机制设计:
添加异常捕获和日志记录
提供数据修复报告
支持分批执行和回滚
经验教训
数据探查先行:在执行数据操作前必须进行全面数据探查
异常场景覆盖:必须考虑所有可能的异常数据场景
操作可逆设计:重要数据操作必须设计可逆方案
📝 前后端协同问题改进
问题分析
本周出现的API结构变更未及时通知前端问题暴露了文档同步机制的不完善。
改进措施实施
变更通知流程建立API变更强制通知流程
文档同步检查点:每日固定时间检查并同步文档更新
版本对应管理建立API版本与前端版本的对应关系表
实施效果
改进后API变更通知及时率达到100%,未再出现因文档不同步导致的联调问题。
🆘 希望获得的帮助
安全测试工具:希望获得自动化安全测试工具的学习资源
代码审查平台:希望了解专业的代码审查平台使用经验
规范推行经验:希望学习其他团队规范推行的成功经验
📅 下一阶段工作展望
随着安全加固和功能补全完成,文档工作将进入新阶段:
β版本规划:开始准备β版本的文档规划
性能文档:开始关注和记录系统性能优化文档
运维文档完善:进一步完善系统运维和监控文档
团队知识库:建立团队共享知识库,沉淀项目经验
工作状态:✅ 本周任务圆满完成
安全升级结果:✅ 密码加密成功实施,安全规范建立
文档归档位置:团队仓库 /docs/security_and_quality/
质量评分团队评价4.7/5
个人收获:深入理解了安全开发的重要性,掌握了安全文档编写的方法论,积累了规范推行的实践经验。

@ -0,0 +1,19 @@
# 个人周计划-第12周
## 姓名和起止时间
**姓  名:** 王家伟
**团队名称:** 菜鸟队
**开始时间:** 2025-12-08
**结束时间:** 2025-12-14
## 本周任务计划安排
| 序号 | 计划内容 | 协作人 | 情况说明 |
|------|----------|--------|----------|
| 1 | 优化转账成功/失败的交互反馈Toast提示 | PM、何文迪 | 针对转账流程,优化前端提示组件,确保成功、失败、处理中等状态有明确且友好的用户反馈 |
| 2 | 更新接口文档Swagger以匹配新字段 | 何文迪、张豪 | 根据后端接口调整如个人信息接口同步更新Swagger文档确保接口说明准确、字段完整 |
| 3 | 配合个人信息模块前端联调与界面微调 | 张豪、刘兴朋 | 协助“个人中心”页面联调根据UI走查结果进行样式与交互细节优化 |
| 4 | 参与加密升级后的登录流程验证测试 | 何文迪 | 验证BCrypt加密升级后前端登录、Token获取及状态管理是否正常 |
| 5 | 整理并提交UI/交互优化建议文档 | PM | 基于α版本验收反馈与本周优化经验,系统整理前端交互可改进点,形成建议文档 |

@ -0,0 +1,18 @@
# 个人周总结-第12周
## 姓名和起止时间
**姓  名:** 王家伟
**团队名称:** 菜鸟队
**开始时间:** 2025-12-08
**结束时间:** 2025-12-14
## 本周任务完成情况
| 序号 | 总结内容 | 是否完成 | 情况说明 |
|------|----------|----------|----------|
| 1 | 优化转账成功/失败的交互反馈Toast提示 | 完成 | 完成转账流程的前端提示优化,实现成功、失败、处理中等状态的友好反馈,提升用户体验 |
| 2 | 更新接口文档Swagger以匹配新字段 | 完成 | 根据后端接口调整同步更新Swagger文档确保接口说明与字段完整准确便于前后端协作 |
| 3 | 配合个人信息模块前端联调与界面微调 | 完成 | 协助“个人中心”页面联调根据UI走查结果完成样式与交互细节优化提升页面一致性 |
| 4 | 参与加密升级后的登录流程验证测试 | 完成 | 配合完成BCrypt加密升级后的登录流程测试验证前端登录、Token获取及状态管理正常 |
| 5 | 整理并提交UI/交互优化建议文档 | 完成 | 基于α版本验收反馈与本周优化经验,系统整理前端交互改进建议,形成并提交文档 |

@ -0,0 +1,63 @@
# 个人周工作计划 - 第12周
**姓名:** 王腾云
**角色:** 前端开发工程师
**团队:** 菜鸟队
**周期:** 2025-12-08 至 2025-12-14
---
## 本周个人目标
1. **二期功能全面开发**:完成线路规划模块,启动实时导航功能开发
2. **微前端架构优化**:优化架构性能,解决大规模应用实践问题
3. **开发效能提升**:完善开发工具链,提升团队整体开发效率
4. **质量体系深化**:完善自动化测试,建立持续质量改进机制
5. **项目里程碑交付**:完成二期第一个里程碑版本交付
---
## 具体任务与时间安排
| 日期 | 任务内容 | 详细说明 |
| :--- | :--- | :--- |
| **周一** | **1. 线路规划模块收尾**<br>**2. 里程碑版本准备** | - 完成线路规划模块剩余功能<br>- 准备第一个里程碑版本交付<br>- 输出:线路规划模块完成报告 |
| **周二** | **1. 实时导航模块启动**<br>**2. 技术难点预研** | - 启动实时导航功能开发<br>- 预研地图集成和实时数据处理<br>- 输出:实时导航技术方案 |
| **周三** | **1. 微前端性能优化**<br>**2. 架构问题解决** | - 优化微前端应用加载性能<br>- 解决大规模子应用管理问题<br>- 输出:性能优化方案 |
| **周四** | **1. 开发工具完善**<br>**2. 效率指标提升** | - 完善开发脚手架和调试工具<br>- 提升团队开发效率和质量<br>- 输出:开发效能提升报告 |
| **周五** | **1. 质量体系完善**<br>**2. 自动化测试深化** | - 完善自动化测试覆盖<br>- 建立持续质量改进机制<br>- 输出:质量体系完善方案 |
| **周末** | **1. 技术方案预研**<br>**2. 个人技能提升** | - 预研下一阶段技术方案<br>- 学习前沿技术和发展趋势<br>- 输出:技术预研报告 |
---
## 技术重点与实施方案
**微前端深度优化:**
- 实施子应用动态加载和资源优化
- 解决大规模子应用状态管理难题
- 优化微前端架构的运维和监控
**复杂功能开发:**
- 集成第三方地图服务和实时数据
- 实现高并发场景下的前端性能保障
- 建立复杂交互的用户体验标准
**效能提升体系:**
- 建立完整的开发效能度量体系
- 实施代码质量和性能监控
- 优化团队协作和知识管理
---

@ -0,0 +1,50 @@
# 个人周工作总结 - 第12周
**姓名:** 王腾云
**角色:** 前端开发工程师
**团队:** 菜鸟队
**周期:** 2025-12-08 至 2025-12-14
---
## 本周目标完成情况
| 日期 | 计划任务 | 完成情况 | 产出物 |
| :--- | :--- | :--- | :--- |
| **周一** | 线路规划模块收尾 + 里程碑版本准备 | **已完成** | 线路规划模块完成报告、里程碑版本 |
| **周二** | 实时导航模块启动 + 技术难点预研 | **基本完成** | 实时导航技术方案、地图集成原型 |
| **周三** | 微前端性能优化 + 架构问题解决 | **部分完成** | 性能优化方案、架构改进文档 |
| **周四** | 开发工具完善 + 效率指标提升 | **已完成** | 开发效能提升报告、工具链优化 |
| **周五** | 质量体系完善 + 自动化测试深化 | **基本完成** | 质量体系完善方案、自动化测试框架 |
| **周末** | 技术方案预研 + 个人技能提升 | **已完成** | 技术预研报告、个人学习总结 |
---
## 遇到的问题与解决
**地图集成技术挑战:**
- 第三方地图服务性能优化问题
- 实施地图瓦片缓存和加载优化
- 建立地图服务的容错和降级机制
**实时数据处理压力:**
- 高并发实时数据推送导致前端性能问题
- 实施数据分批处理和虚拟滚动
- 优化WebSocket连接管理和数据压缩
**团队技能匹配:**
- 部分成员对实时技术掌握不足
- 组织专题培训和实战演练
- 建立技术导师制和知识共享机制
---

@ -0,0 +1,20 @@
# 个人周工作计划 -第十二周
**姓名:** 张豪
**角色:** 安卓app开发工程师
**团队:** 菜鸟队
**周期:** 2025-12-8至 2025-12-14
---
| 序号 | 计划内容| 协作人 | 情况说明 |
| ----| ------ | ------| ------- |
| 1 | 协助 Web 前端整理需求规格说明书 | Web 前端 | 提供 Android 端用户账户、资产管理、意见反馈模块的功能实现细节、接口调用示例、异常处理场景,支撑需求规格说明书最终稿的完善
| 2 | 整理项目开发总结报告 | 个人 |梳理 Android 端从项目初始化到上线的全流程,总结开发过程中的亮点(如性能优化成果)、难点(如兼容性适配问题)及解决方案,形成项目开发总结报告
---
## 小结
1. **核心目标:** 确保 APP 顺利通过应用商店审核,完成上架发布,同时为上线后问题处理做好准备;
2. **协作重点:** 配合 Web 前端完成 UML 图和迭代计划评审,确保团队文档输出质量;

@ -0,0 +1,32 @@
# 个人周总结-第12周
## 姓名和起止时间
**姓  名** 张豪
**团队名称** 4班-菜鸟队
###### **开始时间** 2025-12-8
###### **结束时间** 2025-12-14
## 本周任务完成情况
**序号 总结内容 是否完成 情况说明**
1 协助 Web 前端完善 UML 图 完成 2025-12-17 与 Web 前端同步 Android 端网络请求拦截器逻辑、Token 刷新机制、页面跳转路由规则,提供核心类的属性和方法详情,协助完成 UML 类图绘制,确保图表与代码逻辑致
2 APP 意见反馈功能开发 完成 基于 Jetpack Compose 开发意见反馈页面,实现问题描述输入、图片上传、联系方式填写(手机号校验)功能,对接后端意见提交接口,测试提交成功率 100%,异常场景处理逻辑完善
1\.**审核进度同步:** 建议每天同步各应用商店审核进度,建立审核问题快速响应群,若出现驳回情况可及时沟通处理;
2\.**性能基准设定**:制定 APP 性能基准标准,每次迭代后按标准检测,避免性能退化;
## 小结
1\.**功能闭环完善:** 完成 “设置中心” 核心页面开发及接口对接,安卓端核心功能模块(扫一扫、支付、订单、设置)全部交付,形成完整功能闭环;
2\.**协作价值凸显:** 通过协助 UML 图绘制、参与计划评审,推动团队文档和计划的完善,确保项目收尾阶段的信息同步顺畅。

@ -0,0 +1,52 @@
# 小组会议纪要-第13周
**会议记录概要**
* **团队名称:** **菜鸟队**
* **主 持 人:** **项目经理 (PM)**
* **记录人员:** **何文迪**
* **会议主题:** **β版本中期进度验收与第14周迭代规划**
* **会议地点:** **教室/线上会议**
* **会议时间:** **2025-12-21 20:00-21:00**
* **纪录时间:** **2025-12-21 22:00**
* **参与人员:** **何文迪, 王家伟, 张豪, 刘兴朋, 王腾云**
**会议内容**
**1. 第13周工作回顾β版本启动周**
* **进度通报**
* **后端性能优化(完成)**:何文迪已成功搭建 Redis 环境并集成到 Spring Boot。核心接口如用户信息查询已接入缓存响应时间大幅降低。
* **数据库索引优化(完成)**:通过 `Explain` 分析,何文迪为交易记录表的 `user_id``create_time` 建立了联合索引,解决了大数据量下查询卡顿的问题。
* **前端UI/UX重构完成**:王家伟与刘兴朋完成了“简约蓝白”风格的样式更迭,摆脱了原生 Bootstrap 的视觉感。解决了头像缓存不更新的问题(通过在 URL 后添加随机时间戳后缀)。
* **集成测试(完成)**:张豪编写并执行了 JMeter 压测脚本,结果显示在高并发场景下系统表现稳定,响应指标符合预期。
**2. 存在的问题**
* **Redis 数据一致性**:在极端并发场景下,修改个人信息后缓存有时未及时刷新,存在“脏数据”风险。
* **Docker 环境复杂性**:张豪在编写 Dockerfile 时发现多容器MySQL+Redis+Java的互联互通在不同机器上配置较繁琐。
* **统计图表缺失**:用户反馈转账历史虽然清晰,但缺乏直观的收支对比图表。
**3. 第14周工作部署β版本深化开发**
**团队决定继续在β版本上深挖,重点解决稳定性问题,并提升系统的“自动化”和“可视化”程度。**
* **核心任务**
* **稳定性升级**:完善 Redis 缓存失效策略引入延迟双删增加系统操作日志Log审计。
* **一键部署**:编写 `docker-compose.yml`,实现整套环境的一键拉取与启动。
* **可视化增强**:在前端增加简单的收支饼图或折线图,展示用户最近一月的资金变动趋势。
* **回归测试**:进行全功能的黑盒测试,确保新加入的缓存逻辑没有破坏原有业务流程。
* **任务分配**
* **后端架构(何文迪)**:负责缓存一致性优化及收支统计接口开发。
* **前端开发(王家伟 & 刘兴朋)**:负责引入 ECharts 库实现收支图表,并优化移动端适配。
* **运维测试(张豪 & 王腾云)**:负责 Docker-Compose 编排及编写最终的 β 版本测试报告。
**4. 决议事项**
* **关于图表库:** 确定采用 ECharts 百度图表库,由王家伟周二完成原型展示。
* **关于日志:** 后端日志需记录:操作人 ID、操作类型、时间、IP 地址,以便后续审计。
**一周纪律情况总结**
* **纪律情况:** **本周大家在性能优化上投入了大量精力,尤其是周四全员参与了并发压测,数据记录详尽,表现优异。**

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save