# UserInfoActivity 地区数据存取方式分析 ## 📋 概述 本文档详细说明 `UserInfoActivity` 页面中地区数据(省份、城市、区县)的**存储**和**读取**方式。 --- ## 🔄 完整数据流程 ``` 用户选择地区 ↓ AddressPicker(三级联动选择器) ↓ onAddressPicked回调 ↓ 数据格式化(省份,城市,区县) ↓ 调用updateUserProfile API保存到服务器 ↓ 服务器存储(格式:city字段 = "省份,城市,区县") ↓ 获取用户信息时,服务器返回三个独立字段 ↓ UI显示(去重处理:省份=城市时,只显示一次) ``` --- ## 💾 数据存储方式(保存地区) ### 1. 地区选择器 (`showLocation()`) ```java // 第249-287行 private void showLocation() { AddressPicker picker = new AddressPicker(getActivity()); picker.setAddressMode(AddressMode.PROVINCE_CITY_COUNTY); // 三级联动:省-市-县 picker.setOnAddressPickedListener(this); // 设置选择监听器 picker.show(); } ``` **说明**: - 使用 `AddressPicker` 库(`com.github.gzuliyujiang.wheelpicker`) - 模式:`PROVINCE_CITY_COUNTY`(省份-城市-区县三级联动) - 实现了 `OnAddressPickedListener` 接口,接收选择结果 ### 2. 选择结果处理 (`onAddressPicked()`) ```java // 第289-308行 @Override public void onAddressPicked(ProvinceEntity province, CityEntity city, CountyEntity county) { HashMap params = new HashMap(); // ⚠️ 关键:将三个字段拼接成一个字符串,使用逗号分隔 params.put("city", province.getName() + "," + city.getName() + "," + county.getName()); // 注释掉的代码(说明曾经考虑过分别存储): // params.put("province", province.getCode()); // 省份代码 // params.put("county", county.getCode()); // 区县代码 // 调用更新接口保存到服务器 updateUserInfo(params, new BooleanCallback() { @Override public void onSuccess(boolean result) { ToastUtils.showToast(getActivity(), "设置成功!"); } @Override public void onFail(int code, String msg) { ToastUtils.showToast(getActivity(), msg); } }); } ``` **数据格式**: - **存储格式**:`"省份,城市,区县"`(逗号分隔的字符串) - **示例**:`"北京市,北京市,朝阳区"` - **参数名**:`"city"`(虽然字段名是city,但实际存储的是完整的省市区信息) **注意**: - 省份代码和区县代码已被注释掉,说明目前只存储名称,不存储代码 - 使用逗号分隔的字符串格式,可能是为了兼容后端API ### 3. API调用 (`updateUserInfo()`) ```java // 第912-930行 private void updateUserInfo(HashMap params, BooleanCallback callback) { // 调用更新用户信息API AppService.Instance().updateUserProfile(params, new SimpleCallback() { @Override public void onUiSuccess(Object o) { if (callback != null) { callback.onSuccess(true); // 如果是个人信息页面(sourceType == 1),刷新用户信息 if (mSourceType == 1) { getOtherUserInfo(userInfo.uid, "1"); } } // 发送用户信息更新事件(通知其他页面刷新) LiveDataBus.get().with(Constants.USERINFO_UPDATE).postValue("1"); } @Override public void onUiFailure(int code, String msg) { ToastUtils.showToast(getActivity(), msg); } }); } ``` **API信息**: - **接口路径**:`/api/user/profile`(`Api.UPDATE_USER_PROFILE`) - **请求方法**:POST - **请求参数**:`HashMap`,包含 `"city": "省份,城市,区县"` --- ## 📖 数据读取方式(获取地区) ### 1. API调用 (`getOtherUserInfo()`) ```java // 第834-905行 private void getOtherUserInfo(String user_id, String is_frient) { // 调用获取用户信息API AppService.Instance().getUserInfo(user_id, is_frient, new SimpleCallback() { @Override public void onUiSuccess(GetUserInfoResult infoResult) { if (infoResult != null) { GetUserInfoResult data = infoResult.getData(); mUserOwnerInfo = data; // 如果是个人信息页面(sourceType == 1) if (mSourceType == 1) { // ... 其他字段处理 ... // 处理地区数据显示 StringBuffer loction = new StringBuffer(); // 1. 添加省份 loction.append(TextUtils.isEmpty(mUserOwnerInfo.getProvince()) ? "" : mUserOwnerInfo.getProvince()); // 2. 添加城市(如果省份和城市不同,才添加城市) if (!TextUtils.isEmpty(mUserOwnerInfo.getProvince()) && !TextUtils.isEmpty(mUserOwnerInfo.getCity()) && !mUserOwnerInfo.getProvince().equals(mUserOwnerInfo.getCity())) { loction.append(TextUtils.isEmpty(mUserOwnerInfo.getCity()) ? "" : mUserOwnerInfo.getCity()); } // 3. 添加区县 loction.append(TextUtils.isEmpty(mUserOwnerInfo.getDistrict()) ? "" : mUserOwnerInfo.getDistrict()); // 4. 显示地区信息 aliasLocation.setDesc(TextUtils.isEmpty(loction.toString()) ? "" : loction.toString()); } } } }); } ``` **API信息**: - **接口路径**:`/api/user/info`(通过 `AppService.getUserInfo()` 调用) - **请求参数**: - `user_id`:用户ID - `is_frient`:是否好友("1"=是好友,"0"=非好友) ### 2. 返回数据结构 (`GetUserInfoResult`) ```java // uikit/src/main/java/cn/wildfire/chat/kit/net/model/GetUserInfoResult.java public class GetUserInfoResult { private String province; // 省份 private String city; // 城市 private String district; // 区县 // ... 其他字段 ... } ``` **说明**: - 服务器返回**三个独立字段**:`province`, `city`, `district` - 这与存储时的格式不同(存储时是逗号分隔的字符串) ### 3. UI显示逻辑(去重处理) ```java // 第852-859行 StringBuffer loction = new StringBuffer(); // 1. 添加省份(如果存在) loction.append(TextUtils.isEmpty(mUserOwnerInfo.getProvince()) ? "" : mUserOwnerInfo.getProvince()); // 2. 添加城市(智能去重:如果省份和城市相同,不重复显示) // 例如:"北京市" == "北京市",则只显示一次 if (!TextUtils.isEmpty(mUserOwnerInfo.getProvince()) && !TextUtils.isEmpty(mUserOwnerInfo.getCity()) && !mUserOwnerInfo.getProvince().equals(mUserOwnerInfo.getCity())) { loction.append(TextUtils.isEmpty(mUserOwnerInfo.getCity()) ? "" : mUserOwnerInfo.getCity()); } // 3. 添加区县(如果存在) loction.append(TextUtils.isEmpty(mUserOwnerInfo.getDistrict()) ? "" : mUserOwnerInfo.getDistrict()); // 4. 显示地区信息 aliasLocation.setDesc(loction.toString()); ``` **显示逻辑**: - **省份**:如果存在,直接添加 - **城市**:如果存在且与省份不同,才添加(避免"北京市北京市"这样的重复显示) - **区县**:如果存在,直接添加 - **示例**: - 存储:`"北京市,北京市,朝阳区"` - 服务器返回:`province="北京市"`, `city="北京市"`, `district="朝阳区"` - UI显示:`"北京市朝阳区"`(城市被去重) --- ## 📊 数据格式对比 ### 存储格式(发送到服务器) ```java HashMap params = new HashMap<>(); params.put("city", "北京市,北京市,朝阳区"); // 逗号分隔的字符串 ``` ### 读取格式(服务器返回) ```java GetUserInfoResult { province: "北京市", // 独立字段 city: "北京市", // 独立字段 district: "朝阳区" // 独立字段 } ``` ### 显示格式(UI展示) ```java "北京市朝阳区" // 去重后的显示(省份=城市时,城市被省略) ``` --- ## 🔄 数据同步机制 ### 1. 保存后自动刷新 ```java // 在updateUserInfo成功后 if (mSourceType == 1) { // 如果是个人信息页面,自动刷新用户信息 getOtherUserInfo(userInfo.uid, "1"); } ``` ### 2. LiveDataBus事件通知 ```java // 保存成功后,发送用户信息更新事件 LiveDataBus.get().with(Constants.USERINFO_UPDATE).postValue("1"); ``` **说明**: - 其他页面(如 `MeFragment`)可以监听此事件,自动刷新用户信息 - 实现跨页面的数据同步 ### 3. 监听事件刷新 ```java // UserInfoFragment中监听用户信息更新事件 LiveDataBus.get().with(Constants.USERINFO_UPDATE).observe(getActivity(), new Observer() { @Override public void onChanged(Object o) { init(); // 重新初始化,刷新用户信息 } }); ``` --- ## 🛠️ 技术实现细节 ### 1. 地区选择器库 - **库名**:`com.github.gzuliyujiang.wheelpicker` - **功能**:三级联动地址选择器(省份-城市-区县) - **模式**:`AddressMode.PROVINCE_CITY_COUNTY` ### 2. 数据转换 **存储时**(选择器 → 服务器): ``` ProvinceEntity → province.getName() CityEntity → city.getName() CountyEntity → county.getName() 拼接格式:"省份,城市,区县" ``` **读取时**(服务器 → UI): ``` 服务器返回:province, city, district(三个独立字段) UI显示:去重处理,拼接显示 ``` ### 3. API端点 **保存地区**: - 接口:`POST /api/user/profile` - 参数:`{"city": "省份,城市,区县"}` **获取地区**: - 接口:`POST /api/user/info` - 参数:`{"user_id": "用户ID", "is_friend": "1"}` --- ## ⚠️ 注意事项 ### 1. 数据格式不一致 - **存储时**:使用 `"city"` 字段,值为逗号分隔的字符串 `"省份,城市,区县"` - **读取时**:服务器返回三个独立字段 `province`, `city`, `district` - **说明**:这可能是后端API的设计,存储时将三个字段合并,读取时返回三个独立字段 ### 2. 代码字段未使用 ```java // 注释掉的代码 // params.put("province", province.getCode()); // 省份代码 // params.put("county", county.getCode()); // 区县代码 ``` - 目前只存储名称,不存储代码 - 如果需要代码,需要取消注释并修改API参数 ### 3. UI显示去重逻辑 - 如果省份和城市相同(如"北京市"),UI显示时会自动去重 - 这样可以避免显示"北京市北京市朝阳区"这样的冗余信息 ### 4. 数据为空处理 ```java TextUtils.isEmpty(mUserOwnerInfo.getProvince()) ? "" : mUserOwnerInfo.getProvince() ``` - 所有字段都做了空值检查 - 如果字段为空,返回空字符串 --- ## 📝 关键代码位置 ### 存储相关 - **地区选择器显示**:`UserInfoFragment.java` 第249-287行 (`showLocation()`) - **选择结果处理**:第289-308行 (`onAddressPicked()`) - **更新用户信息**:第912-930行 (`updateUserInfo()`) ### 读取相关 - **获取用户信息**:第834-905行 (`getOtherUserInfo()`) - **地区数据显示**:第852-859行(地区拼接逻辑) - **数据结构定义**:`GetUserInfoResult.java` 第10-13行 --- ## 🔍 相关文件 - **Fragment**:`uikit/src/main/java/cn/wildfire/chat/kit/user/UserInfoFragment.java` - **API服务**:`uikit/src/main/java/cn/wildfire/chat/kit/net/AppService.java` - **API定义**:`uikit/src/main/java/cn/wildfire/chat/kit/net/Api.java` - **数据模型**:`uikit/src/main/java/cn/wildfire/chat/kit/net/model/GetUserInfoResult.java` --- ## 💡 总结 ### 存储流程 1. 用户通过三级联动选择器选择地区 2. 将省份、城市、区县拼接成 `"省份,城市,区县"` 格式 3. 通过 `updateUserProfile` API保存到服务器的 `city` 字段 ### 读取流程 1. 通过 `getUserInfo` API获取用户详细信息 2. 服务器返回三个独立字段:`province`, `city`, `district` 3. UI显示时进行去重处理,避免重复显示相同的省份和城市 ### 关键特点 - ✅ 存储格式:逗号分隔的字符串 - ✅ 读取格式:三个独立字段 - ✅ UI显示:智能去重,避免冗余信息 - ✅ 数据同步:使用LiveDataBus实现跨页面同步