# 实名认证功能及使用文档 ## 一、功能概述 实名认证功能用于验证用户身份,确保用户信息的真实性和安全性。该功能包括: - 实名认证页面(输入姓名和身份证号) - 认证结果展示页面 - 认证状态检查(在会话列表、更多操作等场景) - 认证状态存储(SharedPreferences + SDK UserInfo.extra字段) - 认证引导弹窗 --- ## 二、相关文件 ### 2.1 核心文件 #### Activity类 - `app/src/main/java/com/xunpaisoft/social/im/setting/RealNameAuthActivity.java` - 实名认证输入页面,用户输入姓名和身份证号进行认证 - `app/src/main/java/com/xunpaisoft/social/im/setting/RealNameAuthResultActivity.java` - 实名认证结果展示页面,显示认证成功信息和脱敏后的姓名、身份证号 #### 工具类 - `app/src/main/java/com/xunpaisoft/social/im/utils/UserInfoExtraHelper.java` - UserInfo扩展字段工具类,用于在SDK的UserInfo.extra字段中存储和读取realAuth - `app/src/main/java/com/xunpaisoft/social/im/dialog/RealNameAuthDialog.java` - 实名认证引导弹窗工具类,可在应用任何地方调用显示引导弹窗 #### Fragment类 - `app/src/main/java/com/xunpaisoft/social/im/main/MeFragment.java` - "我的"页面,包含实名认证入口,并在获取用户信息时同步认证状态到SDK - `uikit/src/main/java/cn/wildfire/chat/kit/conversationlist/ConversationListFragment.java` - 会话列表页面,点击会话时检查实名认证状态 #### 数据模型 - `uikit/src/main/java/cn/wildfire/chat/kit/net/model/RealNameAuthResult.java` - 实名认证接口返回结果数据模型 - `uikit/src/main/java/cn/wildfire/chat/kit/net/model/GetUserRealnameInfoResult.java` - 获取用户实名认证信息接口返回结果数据模型 - `uikit/src/main/java/cn/wildfire/chat/kit/net/model/GetUserInfoResult.java` - 获取用户信息接口返回结果数据模型(包含realauth字段) #### SDK模型 - `client/src/main/java/cn/wildfirechat/model/ModifyMyInfoType.java` - 用户信息修改类型枚举,包含Modify_Extra(8)用于扩展字段 - `client/src/main/java/cn/wildfirechat/model/ModifyMyInfoEntry.java` - 用户信息修改条目,用于通过SDK更新用户信息 ### 2.2 布局文件 - `app/src/main/res/layout/activity_real_name_auth.xml` - 实名认证输入页面布局 - `app/src/main/res/layout/activity_real_name_auth_result.xml` - 认证结果页面布局 - `app/src/main/res/layout/main_fragment_me.xml` - "我的"页面布局(包含实名认证入口) ### 2.3 网络接口 - `uikit/src/main/java/cn/wildfire/chat/kit/net/AppService.java` - `realnameAuth()` - 提交实名认证 - `getUserRealnameInfo()` - 获取用户实名认证信息 --- ## 三、数据存储 ### 3.1 SharedPreferences存储 **存储位置:** `social_app` SharedPreferences文件 **Key:** `realAuth`(定义在 `Constants.REAL_AUTH`) **值:** - `"1"` - 已认证成功 - `"0"` - 未认证(默认值) **使用工具类:** `SharedPreferencesUtils` ```java // 保存 SharedPreferencesUtils.putString(Constants.REAL_AUTH, "1"); // 读取 String realAuth = SharedPreferencesUtils.getString(Constants.REAL_AUTH, "0"); boolean isAuth = "1".equals(realAuth); ``` ### 3.2 SDK UserInfo.extra字段存储 **存储位置:** 野火SDK的 `UserInfo.extra` 字段 **数据格式:** JSON字符串 **示例:** ```json { "realAuth": "1" } ``` **使用工具类:** `UserInfoExtraHelper` ```java // 更新到SDK UserInfoExtraHelper.setRealAuthToSDK(callback); // 从UserInfo读取 UserInfo userInfo = ChatManager.Instance().getUserInfo(userId, false); boolean isAuth = UserInfoExtraHelper.isRealAuth(userInfo); ``` ### 3.3 存储时机 1. **认证成功时:** - 保存到 SharedPreferences - 更新到 SDK UserInfo.extra字段 2. **获取用户信息时:** - 从服务器获取的realauth保存到 SharedPreferences - 如果已认证但SDK中未同步,自动同步到SDK --- ## 四、API接口 ### 4.1 提交实名认证 **接口:** `POST /api/user/realnameAuth` **请求参数:** ```json { "username": "张三", "idcard": "110101199001011234" } ``` **响应示例:** ```json { "error_code": 0, "reason": "", "result": { "realname": "张*", "idcard": "110101********1234", "isok": 1, "IdCardInfor": { "name": "张三", "idcard": "110101199001011234" } }, "sn": "xxx" } ``` **调用方式:** ```java AppService.Instance().realnameAuth(name, idNumber, new SimpleCallback() { @Override public void onUiSuccess(RealNameAuthResult result) { if (result != null && result.isSuccess()) { // 认证成功处理 } } @Override public void onUiFailure(int code, String msg) { // 认证失败处理 } }); ``` ### 4.2 获取用户实名认证信息 **接口:** `POST /api/user/getUserRealnameInfo` **响应示例:** ```json { "id": 123, "user_id": "1845", "name": "张*", "idcard": "110101********1234", "phone": "", "address": "", "birthday": "", "sex": "", "status": 1, "createtime": "2025-01-01 10:00:00", "updatetime": "2025-01-01 10:00:00", "status_text": "已认证" } ``` **调用方式:** ```java AppService.Instance().getUserRealnameInfo(new SimpleCallback() { @Override public void onUiSuccess(GetUserRealnameInfoResult result) { if (result != null && result.isSuccess()) { GetUserRealnameInfoResult data = result.getData(); String name = data.getName(); String idcard = data.getIdcard(); } } }); ``` ### 4.3 获取用户信息(包含realauth字段) **接口:** `POST /api/user/getUserInfo` **响应中的realauth字段:** - `"1"` - 已认证成功 - `"0"` - 未认证 --- ## 五、功能使用 ### 5.1 实名认证入口 **位置:** "我的"页面(MeFragment) **布局:** `main_fragment_me.xml` ```xml ``` **点击逻辑:** `MeFragment.realNameAuth()` - 如果已认证:跳转到 `RealNameAuthResultActivity`(认证结果页面) - 如果未认证:跳转到 `RealNameAuthActivity`(认证输入页面) ### 5.2 实名认证流程 1. **用户点击"实名认证"入口** - 检查认证状态(`Constants.isRealAuth()`) - 根据状态跳转到相应页面 2. **输入认证信息(RealNameAuthActivity)** - 输入真实姓名 - 选择证件类型(默认:居民身份证) - 输入证件号码 - 点击"提交"按钮 3. **提交认证(submitToServer)** - 调用 `AppService.realnameAuth()` 接口 - 认证成功: - 保存到 SharedPreferences(`Constants.REAL_AUTH = "1"`) - 更新到 SDK UserInfo.extra字段(`UserInfoExtraHelper.setRealAuthToSDK()`) - 跳转到认证结果页面 4. **显示认证结果(RealNameAuthResultActivity)** - 如果Intent中有数据,直接显示 - 如果没有,调用 `AppService.getUserRealnameInfo()` 从服务器获取 - 显示脱敏后的姓名和身份证号 ### 5.3 认证状态检查 #### 5.3.1 在会话列表中检查 **位置:** `ConversationListFragment.handleConversationClick()` **检查逻辑:** 1. 优先从 `UserInfo.extra` 字段读取 2. 如果extra中没有,从 SharedPreferences 读取 3. 如果未认证,显示引导弹窗 4. 如果已认证,正常跳转到会话页面 **代码示例:** ```java private void handleConversationClick(ConversationInfo conversationInfo) { String userId = ChatManager.Instance().getUserId(); boolean isRealAuth = false; // 1. 从UserInfo.extra读取 if (userId != null) { UserInfo currentUserInfo = ChatManager.Instance().getUserInfo(userId, false); if (currentUserInfo != null && currentUserInfo.extra != null) { String realAuth = getRealAuthFromExtra(currentUserInfo.extra); isRealAuth = "1".equals(realAuth); } } // 2. 从SharedPreferences读取(兼容) if (!isRealAuth) { String realAuth = SharedPreferencesUtils.getString(Constants.REAL_AUTH, "0"); isRealAuth = "1".equals(realAuth); } // 3. 根据结果处理 if (!isRealAuth) { showRealNameAuthDialog(); // 显示引导弹窗 return; } // 已认证,正常跳转 Intent intent = new Intent(getActivity(), ConversationActivity.class); intent.putExtra("conversation", conversationInfo.conversation); startActivity(intent); } ``` #### 5.3.2 在更多操作中检查 **位置:** `MainActivity.showMoreActionMenu()` **检查逻辑:** ```java private void showMoreActionMenu() { // 检查是否已实名认证 if (!Constants.isRealAuth()) { RealNameAuthDialog.show(this); return; } // 已认证,显示更多操作菜单 // ... } ``` ### 5.4 认证引导弹窗 **工具类:** `RealNameAuthDialog` **使用方式:** 1. **简单调用(默认行为)** ```java // 在任何Activity中调用 RealNameAuthDialog.show(this); ``` 2. **带回调的调用(自定义行为)** ```java RealNameAuthDialog.show(this, new RealNameAuthDialog.OnRealNameAuthDialogListener() { @Override public void onConfirm() { // 自定义确认行为 Intent intent = new Intent(this, RealNameAuthActivity.class); startActivity(intent); } @Override public void onCancel() { // 自定义取消行为 } }); ``` **弹窗内容:** - 标题:"温馨提示" - 内容:"为保障用户信息和安全,需要进行实名认证后才能开启相关服务" - 按钮: - "取消"(关闭弹窗) - "去认证"(跳转到实名认证页面) --- ## 六、工具类使用 ### 6.1 UserInfoExtraHelper #### 6.1.1 读取认证状态 ```java // 从UserInfo读取 UserInfo userInfo = ChatManager.Instance().getUserInfo(userId, false); String realAuth = UserInfoExtraHelper.getRealAuth(userInfo); boolean isAuth = UserInfoExtraHelper.isRealAuth(userInfo); ``` #### 6.1.2 更新认证状态到SDK ```java // 设置为已认证 UserInfoExtraHelper.setRealAuthToSDK(new GeneralCallback() { @Override public void onSuccess() { Log.d("Tag", "更新成功"); } @Override public void onFail(int errorCode) { Log.e("Tag", "更新失败: " + errorCode); } }); // 设置为未认证 UserInfoExtraHelper.clearRealAuthFromSDK(callback); // 自定义值 UserInfoExtraHelper.updateRealAuthToSDK("1", callback); ``` #### 6.1.3 构建extra JSON字符串 ```java String currentExtra = userInfo.extra; // 可能为null String newExtra = UserInfoExtraHelper.buildExtraWithRealAuth(currentExtra, "1"); // 结果: {"realAuth":"1"} ``` ### 6.2 Constants工具方法 ```java // 获取认证状态字符串 String realAuth = Constants.getRealAuth(); // 返回 "1" 或 "0" // 判断是否已认证 boolean isAuth = Constants.isRealAuth(); // 返回 true 或 false ``` --- ## 七、SDK扩展字段使用 ### 7.1 Modify_Extra字段说明 `ModifyMyInfoType.Modify_Extra` 是野火SDK提供的扩展字段类型,用于在 `UserInfo.extra` 字段中存储自定义JSON数据。 **使用步骤:** 1. **构建JSON字符串** ```java JSONObject jsonObject = new JSONObject(); jsonObject.put("realAuth", "1"); String extraJson = jsonObject.toString(); ``` 2. **创建ModifyMyInfoEntry** ```java ModifyMyInfoEntry entry = new ModifyMyInfoEntry( ModifyMyInfoType.Modify_Extra, extraJson ); ``` 3. **调用SDK更新** ```java List entries = new ArrayList<>(); entries.add(entry); ChatManager.Instance().modifyMyInfo(entries, callback); ``` ### 7.2 读取extra字段 ```java UserInfo userInfo = ChatManager.Instance().getUserInfo(userId, false); if (userInfo != null && userInfo.extra != null) { try { JSONObject jsonObject = new JSONObject(userInfo.extra); String realAuth = jsonObject.optString("realAuth", "0"); } catch (JSONException e) { e.printStackTrace(); } } ``` --- ## 八、同步机制 ### 8.1 认证成功时同步 **位置:** `RealNameAuthActivity.submitToServer()` 认证成功后: 1. 保存到 SharedPreferences 2. 调用 `UserInfoExtraHelper.setRealAuthToSDK()` 更新到SDK ### 8.2 获取用户信息时同步 **位置:** `MeFragment.getOtherUserInfo()` 当从服务器获取到 `realauth = "1"` 时: 1. 保存到 SharedPreferences 2. 检查SDK的 `UserInfo.extra` 字段 3. 如果extra中没有 `realAuth` 或值不是 "1",自动同步更新 **同步逻辑:** ```java if ("1".equals(data.getRealauth())) { // 检查SDK中是否已有realAuth UserInfo currentUserInfo = ChatManager.Instance().getUserInfo(userId, false); String currentExtra = currentUserInfo != null ? currentUserInfo.extra : null; boolean needSync = true; if (currentExtra != null && !currentExtra.isEmpty()) { JSONObject jsonObject = new JSONObject(currentExtra); String existingRealAuth = jsonObject.optString("realAuth", "0"); if ("1".equals(existingRealAuth)) { needSync = false; // 已经同步过了 } } if (needSync) { UserInfoExtraHelper.setRealAuthToSDK(callback); } } ``` --- ## 九、调试日志 ### 9.1 日志标签 - `ConversationListFragment` - 会话列表认证检查日志 - `UserInfoExtraHelper` - SDK更新日志 - `MeFragment` - 同步检测日志 - `RealNameAuth` - 认证流程日志 ### 9.2 关键日志点 1. **认证状态检查** ``` D/ConversationListFragment: === 开始检查实名认证状态 === D/ConversationListFragment: 当前用户ID: 1845 D/ConversationListFragment: UserInfo.extra 值: {"realAuth":"1"} D/ConversationListFragment: 从UserInfo.extra解析的realAuth: 1 D/ConversationListFragment: ✓ 从UserInfo.extra读取到已认证状态 ``` 2. **SDK更新** ``` D/UserInfoExtraHelper: === 开始更新realAuth到SDK === D/UserInfoExtraHelper: 要设置的realAuth值: 1 D/UserInfoExtraHelper: 当前UserInfo.extra: null D/UserInfoExtraHelper: 构建后的newExtra: {"realAuth":"1"} D/UserInfoExtraHelper: ✓ 成功更新realAuth到SDK的UserInfo.extra ``` 3. **同步检测** ``` D/MeFragment: 检测到已认证但SDK中extra字段未同步,开始同步... D/MeFragment: ✓ 成功同步realAuth到SDK的UserInfo.extra ``` --- ## 十、注意事项 ### 10.1 数据一致性 - SharedPreferences 和 SDK UserInfo.extra 字段都存储认证状态 - 优先从 SDK 的 extra 字段读取(更可靠) - SharedPreferences 作为兼容和备用方案 ### 10.2 更新extra字段时 - 必须保留现有数据,只更新 `realAuth` 字段 - 使用 `UserInfoExtraHelper.buildExtraWithRealAuth()` 方法确保数据不丢失 ### 10.3 错误处理 - SDK更新失败不影响 SharedPreferences 的保存 - 即使SDK更新失败,认证状态仍可通过 SharedPreferences 读取 ### 10.4 性能考虑 - 同步操作是异步的,不会阻塞UI - 在获取用户信息时进行同步,避免频繁调用 --- ## 十一、常见问题 ### Q1: UserInfo.extra为空怎么办? **A:** 1. 检查是否调用了 `UserInfoExtraHelper.setRealAuthToSDK()` 2. 检查SDK更新是否成功(查看日志) 3. 如果之前已认证,登录时会自动同步 ### Q2: 如何手动同步认证状态到SDK? **A:** ```java // 如果SharedPreferences中有认证状态,但SDK中没有 if (Constants.isRealAuth()) { UserInfoExtraHelper.setRealAuthToSDK(new GeneralCallback() { @Override public void onSuccess() { // 同步成功 } @Override public void onFail(int errorCode) { // 同步失败 } }); } ``` ### Q3: 如何判断用户是否已认证? **A:** ```java // 方式1:使用Constants工具方法(推荐) boolean isAuth = Constants.isRealAuth(); // 方式2:从UserInfo.extra读取 UserInfo userInfo = ChatManager.Instance().getUserInfo(userId, false); boolean isAuth = UserInfoExtraHelper.isRealAuth(userInfo); // 方式3:从SharedPreferences读取 String realAuth = SharedPreferencesUtils.getString(Constants.REAL_AUTH, "0"); boolean isAuth = "1".equals(realAuth); ``` ### Q4: 认证状态在哪里检查? **A:** - 会话列表点击时:`ConversationListFragment.handleConversationClick()` - 更多操作点击时:`MainActivity.showMoreActionMenu()` - 实名认证入口点击时:`MeFragment.realNameAuth()` --- ## 十二、版本历史 - **v1.0** - 初始版本 - 实现基本的实名认证功能 - 支持SharedPreferences存储 - 支持SDK UserInfo.extra字段存储 - 实现认证状态检查和引导弹窗 - 实现自动同步机制 --- ## 十三、相关常量 **定义位置:** `uikit/src/main/java/cn/wildfire/chat/kit/net/Constants.java` ```java public static final String REAL_AUTH = "realAuth"; public static String getRealAuth() { return SharedPreferencesUtils.getString(REAL_AUTH, "0"); } public static boolean isRealAuth() { return "1".equals(getRealAuth()); } ``` --- ## 十四、总结 实名认证功能通过以下方式实现: 1. **数据存储:** SharedPreferences + SDK UserInfo.extra字段(双重存储) 2. **状态检查:** 优先从SDK读取,兼容SharedPreferences 3. **自动同步:** 获取用户信息时自动同步到SDK 4. **引导机制:** 未认证时显示引导弹窗 5. **工具类封装:** 提供便捷的读写方法 该设计确保了数据的一致性和可靠性,同时提供了良好的用户体验。