1. 概述
本文将为我们的 Reddit 应用 添加一个 用户个人中心(Profile)功能,让使用者可以配置个性化的默认偏好设置。
目标很明确:用户在每次发布新帖子时,不再需要重复填写相同的配置项。通过在个人中心中预设偏好,系统会自动填充这些默认值。当然,每次发帖时仍可临时修改,但至少——不用每次都从零开始。
这属于典型的“提效型”功能,长期看能显著提升用户体验,属于那种“早该有”的功能。
2. Preference 实体设计
我们将把应用中大多数可配置项,统一收归到用户的个人偏好中进行管理。
先来看核心的 Preference
实体类:
@Entity
public class Preference {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String email;
private String subreddit;
private boolean sendReplies;
// 自动重发相关配置
private int noOfAttempts;
private int timeInterval;
private int minScoreRequired;
private int minUpvoteRatio;
private boolean keepIfHasComments;
private boolean deleteAfterLastAttempt;
}
✅ 说明:
- 这个类本质上是用户级的“全局默认配置”
email
用于接收系统通知(比如发帖失败、成功等)- 后面一堆字段控制自动重发逻辑,比如最多尝试几次、间隔多久、达到多少分数就停止重发等
接下来,把 Preference
和 User
关联起来:
@Entity
public class User {
...
@OneToOne
@JoinColumn(name = "preference_id")
private Preference preference;
}
⚠️ 踩坑提醒:
这里用的是 @OneToOne
+ @JoinColumn
,表示 User
表中有一个外键指向 Preference
。别搞反了,否则初始化时容易出现 null
引用问题。
3. 简单的个人中心页面
前端页面我们不追求花里胡哨,目标是快速可用。
<form>
<input type="hidden" name="id" />
<input name="email" type="email"/>
<input name="subreddit"/>
...
<button onclick="editPref()">保存修改</button>
</form>
<script>
$(function() {
$.get("user/preference", function (data){
$.each(data, function(key, value) {
$('*[name="'+key+'"]').val(value);
});
});
});
function editPref(){
var data = {};
$('form').serializeArray().map(function(x){data[x.name] = x.value;});
$.ajax({
url: "user/preference/" + $('input[name="id"]').val(),
data: JSON.stringify(data),
type: 'PUT',
contentType: 'application/json'
})
.done(function() {
window.location.href = "./";
})
.fail(function(error) {
alert(error.responseText);
});
}
</script>
📌 关键点解析:
- 页面加载时通过
GET /user/preference
拉取当前用户的偏好 - 使用
$.each
自动填充表单字段,简单粗暴但有效 - 提交走
PUT
接口,JSON 格式提交
顺手在首页加个入口:
<h1>Welcome, <a href="profile" sec:authentication="principal.username">username</a></h1>
这样用户一眼就能点进自己的个人中心。
4. 后端接口实现
核心逻辑在 UserPreferenceController
,负责读取和更新用户偏好。
@Controller
@RequestMapping(value = "/user/preference")
public class UserPreferenceController {
@Autowired
private PreferenceRepository preferenceReopsitory;
@RequestMapping(method = RequestMethod.GET)
@ResponseBody
public Preference getCurrentUserPreference() {
return getCurrentUser().getPreference();
}
@RequestMapping(value = "/{id}", method = RequestMethod.PUT)
@ResponseStatus(HttpStatus.OK)
public void updateUserPreference(@RequestBody Preference pref) {
preferenceReopsitory.save(pref);
getCurrentUser().setPreference(pref);
}
}
✅ 注意点:
getCurrentUser()
是一个工具方法,从 Spring Security Context 中获取当前登录用户- 更新时先
save
到数据库,再同步到内存中的User
对象,避免下次读取不一致
⚠️ 初始化陷阱:
别忘了在用户首次登录/注册时,初始化其 Preference
!
public void loadAuthentication(String name, OAuth2AccessToken token) {
User user = userReopsitory.findByUsername(name);
if (user == null) {
user = new User();
user.setUsername(name);
// 初始化偏好设置
Preference pref = new Preference();
pref.setEmail(name + "@example.com"); // mock 默认邮箱
preferenceReopsitory.save(pref);
user.setPreference(pref);
userReopsitory.save(user);
}
...
}
❌ 如果漏了这一步,后续访问 /user/preference
会 NPE,线上踩过的真实坑。
5. 偏好数据的加载与使用
有了默认值,就得让它真正发挥作用。
以“发帖调度页”为例,我们在页面加载时自动填充用户偏好:
$(function() {
$.get("user/preference", function (data){
$.each(data, function(key, value) {
$('*[name="'+key+'"]').val(value);
});
});
});
📌 效果:
- 用户打开发帖页,
subreddit
、email
等字段已自动填好 - 只需微调,无需从头输入
- 修改后仍可临时覆盖,不影响默认值
这种“默认 + 可覆盖”的设计模式,在后台系统中非常常见,属于提升 UX 的基本操作。
6. 测试与总结
最后一步:加点集成测试,确保核心流程不翻车。
我们基于已有的持久层测试基类扩展即可:
@Test
public void whenUserCreated_thenHasPreference() {
User user = createUser("testuser");
assertNotNull(user.getPreference());
assertEquals("testuser@example.com", user.getPreference().getEmail());
}
@Test
public void whenUpdatePreference_thenSaved() {
Preference pref = user.getPreference();
pref.setSubreddit("java");
userPreferenceController.updateUserPreference(pref);
Preference updated = preferenceRepository.findById(pref.getId());
assertEquals("java", updated.getSubreddit());
}
✅ 总结:
- 用户个人中心功能完成
- 支持偏好设置的读取、更新、初始化
- 前后端联动,实现默认值自动填充
- 代码简洁,扩展性强,后续加新字段也很方便
这个功能看似小,但对用户来说是实实在在的便利。别小看这些“小改进”,积少成多才是好产品。