将交易实体与关联分类转换为导出视图对象,并处理类型文案、金额格式化、 + * 渠道本地化与隐藏备注占位。
+ * + * @date 2026-10-09 10:55:53 + */ +@Component +public class TransactionExportAssembler { + + /** 备注被隐藏时在导出文件中使用的占位符 */ + private static final String REMARK_HIDDEN_PLACEHOLDER = "******"; + + /** + * 将交易实体和关联分类转换为导出视图对象。 + * + * @param t 交易实体 + * @param category 关联的分类实体(可为 null) + * @param channelNames 自定义渠道「编码 → 名称」映射 + * @return 交易导出视图对象 + */ + public TransactionExportVO toExportVO(Transaction t, Category category, Map承载批量删除、批量设置分类/计入统计与批量设置标签,均按当前用户隔离。
+ * + * @date 2026-10-09 10:55:53 + */ +@Service +@Slf4j +public class TransactionBatchService { + + private final TransactionMapper transactionMapper; + private final CategoryMapper categoryMapper; + private final TransactionTagMapper transactionTagMapper; + private final TransactionGroupService transactionGroupService; + private final TransactionTagWriter tagWriter; + + public TransactionBatchService(TransactionMapper transactionMapper, + CategoryMapper categoryMapper, + TransactionTagMapper transactionTagMapper, + TransactionGroupService transactionGroupService, + TransactionTagWriter tagWriter) { + this.transactionMapper = transactionMapper; + this.categoryMapper = categoryMapper; + this.transactionTagMapper = transactionTagMapper; + this.transactionGroupService = transactionGroupService; + this.tagWriter = tagWriter; + } + + /** + * 仅删除归属当前用户的记录,先清理标签关联再批量删除,返回实际删除数。 + * + * @param userId 用户ID + * @param ids 待删除记录ID列表 + * @return 实际删除的记录数 + */ + @Transactional + public int batchDelete(Long userId, List将分页查询与导出共用的筛选条件拼装逻辑抽取于此,避免两处重复维护: + * 包含收支类型、分类(含未分类)、时间范围、渠道、计入统计、金额区间、 + * 备注关键字、备注隐藏偏好与标签(含标签/无标签/未打标签)。
+ * + * @date 2026-10-09 10:55:53 + */ +@Component +public class TransactionQuerySupport { + + private final UserPreferenceService userPreferenceService; + + public TransactionQuerySupport(UserPreferenceService userPreferenceService) { + this.userPreferenceService = userPreferenceService; + } + + /** + * 根据查询参数构建交易记录的筛选条件。 + * + * @param userId 用户ID + * @param query 查询参数 + * @return 查询条件包装器 + */ + public LambdaQueryWrapper排序字段与方向均采用白名单,非法值回退为交易时间降序,避免注入风险。
+ * + * @param wrapper 查询条件 + * @param query 查询参数 + */ + public void applySort(LambdaQueryWrapper作为 {@link TransactionService} 的门面,内部委托查询构建、视图富化、标签写入、 + * 批量操作与导出组装等协作组件,保持对外接口不变。
*/ @Service @Slf4j public class TransactionServiceImpl implements TransactionService { - /** 备注被隐藏时在导出文件中使用的占位符 */ - private static final String REMARK_HIDDEN_PLACEHOLDER = "******"; + /** 导出记录数上限,超过时提示缩小筛选范围,避免全量加载导致 OOM */ + private static final int EXPORT_MAX_ROWS = 10000; private final TransactionMapper transactionMapper; private final CategoryMapper categoryMapper; - private final TagMapper tagMapper; - private final TransactionTagMapper transactionTagMapper; private final AiImageMapper aiImageMapper; private final AiImageTransactionMapper aiImageTransactionMapper; - private final TransactionGroupItemMapper transactionGroupItemMapper; private final ChannelService channelService; private final TransactionGroupService transactionGroupService; - private final UserPreferenceService userPreferenceService; private final VoConverter voConverter; + private final TransactionQuerySupport querySupport; + private final TransactionVoEnricher enricher; + private final TransactionTagWriter tagWriter; + private final TransactionBatchService batchService; + private final TransactionExportAssembler exportAssembler; public TransactionServiceImpl(TransactionMapper transactionMapper, CategoryMapper categoryMapper, - TagMapper tagMapper, - TransactionTagMapper transactionTagMapper, AiImageMapper aiImageMapper, AiImageTransactionMapper aiImageTransactionMapper, - TransactionGroupItemMapper transactionGroupItemMapper, ChannelService channelService, TransactionGroupService transactionGroupService, - UserPreferenceService userPreferenceService, - VoConverter voConverter) { + VoConverter voConverter, + TransactionQuerySupport querySupport, + TransactionVoEnricher enricher, + TransactionTagWriter tagWriter, + TransactionBatchService batchService, + TransactionExportAssembler exportAssembler) { this.transactionMapper = transactionMapper; this.categoryMapper = categoryMapper; - this.tagMapper = tagMapper; - this.transactionTagMapper = transactionTagMapper; this.aiImageMapper = aiImageMapper; this.aiImageTransactionMapper = aiImageTransactionMapper; - this.transactionGroupItemMapper = transactionGroupItemMapper; this.channelService = channelService; this.transactionGroupService = transactionGroupService; - this.userPreferenceService = userPreferenceService; this.voConverter = voConverter; + this.querySupport = querySupport; + this.enricher = enricher; + this.tagWriter = tagWriter; + this.batchService = batchService; + this.exportAssembler = exportAssembler; } /** @@ -106,113 +100,19 @@ public class TransactionServiceImpl implements TransactionService { */ @Override public PageResult排序字段与方向均采用白名单,非法值回退为交易时间降序,避免注入风险。
- * - * @param wrapper 查询条件 - * @param query 查询参数 - */ - private void applySort(LambdaQueryWrapper统一处理标签归属校验、单条全量替换、批量插入与关联删除,插入统一分批。
+ * + * @date 2026-10-09 10:55:53 + */ +@Service +public class TransactionTagWriter { + + /** 批量插入的分批大小 */ + private static final int BATCH_SIZE = 500; + + private final TagMapper tagMapper; + private final TransactionTagMapper transactionTagMapper; + + public TransactionTagWriter(TagMapper tagMapper, TransactionTagMapper transactionTagMapper) { + this.tagMapper = tagMapper; + this.transactionTagMapper = transactionTagMapper; + } + + /** + * 过滤出属于当前用户的标签ID。 + * + * @param userId 用户ID + * @param tagIds 待校验的标签ID列表 + * @return 有效的标签ID列表 + */ + public List标签列表按交易顺序一一对应,先统一校验标签归属再批量插入,避免逐条写库。
+ * + * @param userId 用户ID + * @param transactionIds 交易记录ID列表 + * @param tagIdsList 各交易对应的目标标签ID列表,下标与 transactionIds 对应 + */ + public void insertTags(Long userId, List为交易列表视图对象批量补充关联数据:分类映射、标签、来源图片与关系组数量, + * 均采用批量查询以避免 N+1。
+ * + * @date 2026-10-09 10:55:53 + */ +@Service +public class TransactionVoEnricher { + + private final CategoryMapper categoryMapper; + private final TagMapper tagMapper; + private final TransactionTagMapper transactionTagMapper; + private final AiImageTransactionMapper aiImageTransactionMapper; + private final TransactionGroupItemMapper transactionGroupItemMapper; + private final VoConverter voConverter; + + public TransactionVoEnricher(CategoryMapper categoryMapper, + TagMapper tagMapper, + TransactionTagMapper transactionTagMapper, + AiImageTransactionMapper aiImageTransactionMapper, + TransactionGroupItemMapper transactionGroupItemMapper, + VoConverter voConverter) { + this.categoryMapper = categoryMapper; + this.tagMapper = tagMapper; + this.transactionTagMapper = transactionTagMapper; + this.aiImageTransactionMapper = aiImageTransactionMapper; + this.transactionGroupItemMapper = transactionGroupItemMapper; + this.voConverter = voConverter; + } + + /** + * 批量加载交易关联的分类映射。 + * + * @param transactions 交易实体列表 + * @return 「分类ID → 分类实体」映射 + */ + public Map