feat: 新增隐藏备注记录显示开关并简化设置与日志菜单文案

- 设置页「外观设置」新增「显示已隐藏备注的记录」开关,默认关闭,切换即时保存
- 新增 GET/PUT /api/setting/show-hidden-data 接口,用户级偏好持久化于 t_user_preference
- 收支记录列表与 Excel 导出按该偏好过滤,关闭时排除已隐藏备注的记录
- 移除「备注关键字搜索时总是跳过隐藏记录」逻辑,改由该偏好统一控制,统计备注搜索同步放开
- 列表内隐藏备注后,偏好关闭时该行即时移除,当前页清空自动回退上一页
- 外观设置面板拆分为两个浅灰底分组,两个配置项区分更清晰
- 顶部用户菜单与设置/日志页标题由「系统设置/系统日志」简化为「设置/日志」,同步中英日三语
This commit is contained in:
2026-09-23 09:28:42 +08:00
parent 66088e8c7e
commit 66d6ab86b5
10 changed files with 220 additions and 25 deletions
@@ -13,6 +13,7 @@ import org.springframework.web.bind.annotation.RestController;
import cn.rosercode.finance.common.Result;
import cn.rosercode.finance.dto.BillSourceStatsRequest;
import cn.rosercode.finance.dto.ExcludedDisplayStyleRequest;
import cn.rosercode.finance.dto.ShowHiddenDataRequest;
import cn.rosercode.finance.service.UserPreferenceService;
import cn.rosercode.finance.util.UserContext;
@@ -84,4 +85,30 @@ public class UserSettingController {
userPreferenceService.saveExcludedDisplayStyle(userId, request.getStyle());
return Result.success(userPreferenceService.getExcludedDisplayStyle(userId));
}
/**
* 查询当前用户是否在收支记录列表与导出中显示已隐藏备注的记录。
*
* @return true-显示,false-不显示
*/
@GetMapping("/show-hidden-data")
@Operation(summary = "查询隐藏备注记录显示偏好", description = "读取当前用户是否在收支记录列表与导出中显示已隐藏备注的记录")
public Result<Boolean> getShowHiddenData() {
return Result.success(userPreferenceService.getShowHiddenData(UserContext.getUserId()));
}
/**
* 更新当前用户是否在收支记录列表与导出中显示已隐藏备注的记录。
*
* @param request 显示偏好更新请求
* @return 更新后的显示偏好
*/
@PutMapping("/show-hidden-data")
@Operation(summary = "更新隐藏备注记录显示偏好", description = "设置当前用户是否在收支记录列表与导出中显示已隐藏备注的记录")
public Result<Boolean> updateShowHiddenData(@RequestBody ShowHiddenDataRequest request) {
Long userId = UserContext.getUserId();
boolean show = Boolean.TRUE.equals(request.getShow());
userPreferenceService.saveShowHiddenData(userId, show);
return Result.success(userPreferenceService.getShowHiddenData(userId));
}
}
@@ -0,0 +1,15 @@
package cn.rosercode.finance.dto;
import lombok.Data;
/**
* 「显示已隐藏备注记录」偏好更新请求对象。
*
* @date 2026-09-23 09:18:55
*/
@Data
public class ShowHiddenDataRequest {
/** 是否在收支记录列表与导出中显示已隐藏备注的记录 */
private Boolean show;
}
@@ -40,4 +40,20 @@ public interface UserPreferenceService {
* @param style 展示样式
*/
void saveExcludedDisplayStyle(Long userId, String style);
/**
* 获取当前用户是否在收支记录列表与导出中显示已隐藏备注的记录,未配置时回退默认值(不显示)。
*
* @param userId 用户ID
* @return true-显示,false-不显示
*/
boolean getShowHiddenData(Long userId);
/**
* 保存当前用户是否在收支记录列表与导出中显示已隐藏备注的记录。
*
* @param userId 用户ID
* @param show 是否显示
*/
void saveShowHiddenData(Long userId, boolean show);
}
@@ -285,10 +285,9 @@ public class StatisticsServiceImpl implements StatisticsService {
if (query.getMaxAmount() != null) {
wrapper.le("amount", query.getMaxAmount());
}
// 备注关键字模糊匹配;备注已隐藏的记录不参与搜索
// 备注关键字模糊匹配
if (StringUtils.hasText(query.getRemark())) {
wrapper.like("remark", query.getRemark());
wrapper.and(w -> w.isNull("remark_hidden").or().eq("remark_hidden", false));
}
// 标签筛选(多选);同时勾选「未打标签」时与其取并集
boolean hasTagIds = query.getTagIds() != null && !query.getTagIds().isEmpty();
@@ -46,6 +46,7 @@ import cn.rosercode.finance.mapper.TransactionTagMapper;
import cn.rosercode.finance.service.ChannelService;
import cn.rosercode.finance.service.TransactionGroupService;
import cn.rosercode.finance.service.TransactionService;
import cn.rosercode.finance.service.UserPreferenceService;
import cn.rosercode.finance.util.TransactionDedupUtils;
import cn.rosercode.finance.vo.TagVO;
import cn.rosercode.finance.vo.TransactionExportVO;
@@ -70,6 +71,7 @@ public class TransactionServiceImpl implements TransactionService {
private final TransactionGroupItemMapper transactionGroupItemMapper;
private final ChannelService channelService;
private final TransactionGroupService transactionGroupService;
private final UserPreferenceService userPreferenceService;
private final VoConverter voConverter;
public TransactionServiceImpl(TransactionMapper transactionMapper,
@@ -81,6 +83,7 @@ public class TransactionServiceImpl implements TransactionService {
TransactionGroupItemMapper transactionGroupItemMapper,
ChannelService channelService,
TransactionGroupService transactionGroupService,
UserPreferenceService userPreferenceService,
VoConverter voConverter) {
this.transactionMapper = transactionMapper;
this.categoryMapper = categoryMapper;
@@ -91,6 +94,7 @@ public class TransactionServiceImpl implements TransactionService {
this.transactionGroupItemMapper = transactionGroupItemMapper;
this.channelService = channelService;
this.transactionGroupService = transactionGroupService;
this.userPreferenceService = userPreferenceService;
this.voConverter = voConverter;
}
@@ -145,11 +149,14 @@ public class TransactionServiceImpl implements TransactionService {
if (query.getMaxAmount() != null) {
wrapper.le(Transaction::getAmount, query.getMaxAmount());
}
// 按备注关键字模糊匹配;备注已隐藏的记录不参与搜索
// 按备注关键字模糊匹配
if (StringUtils.hasText(query.getRemark())) {
wrapper.like(Transaction::getRemark, query.getRemark())
.and(w -> w.isNull(Transaction::getRemarkHidden)
.or().eq(Transaction::getRemarkHidden, false));
wrapper.like(Transaction::getRemark, query.getRemark());
}
// 是否显示已隐藏备注的记录由用户偏好控制,默认不显示
if (!userPreferenceService.getShowHiddenData(userId)) {
wrapper.and(w -> w.isNull(Transaction::getRemarkHidden)
.or().eq(Transaction::getRemarkHidden, false));
}
// 按标签筛选:命中任一选中标签、有任意标签,或筛选无任何标签的记录(多条件取并集)
boolean hasTagIds = query.getTagIds() != null && !query.getTagIds().isEmpty();
@@ -802,11 +809,14 @@ public class TransactionServiceImpl implements TransactionService {
if (query.getMaxAmount() != null) {
wrapper.le(Transaction::getAmount, query.getMaxAmount());
}
// 按备注关键字模糊匹配;备注已隐藏的记录不参与搜索
// 按备注关键字模糊匹配
if (StringUtils.hasText(query.getRemark())) {
wrapper.like(Transaction::getRemark, query.getRemark())
.and(w -> w.isNull(Transaction::getRemarkHidden)
.or().eq(Transaction::getRemarkHidden, false));
wrapper.like(Transaction::getRemark, query.getRemark());
}
// 是否显示已隐藏备注的记录由用户偏好控制,默认不显示
if (!userPreferenceService.getShowHiddenData(userId)) {
wrapper.and(w -> w.isNull(Transaction::getRemarkHidden)
.or().eq(Transaction::getRemarkHidden, false));
}
// 按标签筛选:命中任一选中标签、有任意标签,或筛选无任何标签的记录(多条件取并集)
boolean hasTagIds = query.getTagIds() != null && !query.getTagIds().isEmpty();
@@ -37,6 +37,9 @@ public class UserPreferenceServiceImpl implements UserPreferenceService {
/** 偏好键:「不计入统计」记录的展示样式 */
private static final String KEY_EXCLUDED_DISPLAY_STYLE = "excludedDisplayStyle";
/** 偏好键:是否显示已隐藏备注的记录 */
private static final String KEY_SHOW_HIDDEN_DATA = "showHiddenData";
/** 默认计入统计的账单来源,其余来源默认不计入 */
private static final Set<String> DEFAULT_INCLUDED_SOURCES = Set.of("WECHAT", "ALIPAY");
@@ -120,6 +123,29 @@ public class UserPreferenceServiceImpl implements UserPreferenceService {
log.info("保存不计入统计展示样式: userId={}, style={}", userId, resolved);
}
/**
* {@inheritDoc}
*
* 结果按用户缓存,未配置时默认不显示。
*/
@Override
@Cacheable(cacheNames = CacheConfig.USER_PREFERENCE_CACHE, key = "'showHiddenData:' + #userId")
public boolean getShowHiddenData(Long userId) {
return Boolean.parseBoolean(findValue(userId, KEY_SHOW_HIDDEN_DATA));
}
/**
* {@inheritDoc}
*
* 保存后失效该用户的显示偏好缓存。
*/
@Override
@CacheEvict(cacheNames = CacheConfig.USER_PREFERENCE_CACHE, key = "'showHiddenData:' + #userId")
public void saveShowHiddenData(Long userId, boolean show) {
upsert(userId, KEY_SHOW_HIDDEN_DATA, Boolean.toString(show));
log.info("保存隐藏备注记录显示偏好: userId={}, show={}", userId, show);
}
/**
* 查询指定用户的某个偏好值。
*