- 알림 자동등록 시 memo(가맹점) 키워드로 과거에 '확정'한 내역의 분류를
찾아(가장 많이 쓴 분류) pending 내역에 미리 채운다.
- 메모 포함관계 매칭('스타벅스' ↔ '스타벅스 강남점'), 같은 type(수입/지출)만.
- 안전상 '확인 필요' 상태는 유지(분류만 선반영) — 무검토 오확정 방지.
- AccountEntryMapper.findLearnedCategory 추가.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -66,6 +66,11 @@ public interface AccountEntryMapper {
|
|||||||
/** 미확인(확인 필요) 내역 개수 */
|
/** 미확인(확인 필요) 내역 개수 */
|
||||||
int countPending(@Param("memberId") Long memberId);
|
int countPending(@Param("memberId") Long memberId);
|
||||||
|
|
||||||
|
/** 자동인식 분류 학습: 같은 메모 키워드로 과거에 가장 많이 쓴 분류 1건(없으면 null) */
|
||||||
|
String findLearnedCategory(@Param("memberId") Long memberId,
|
||||||
|
@Param("type") String type,
|
||||||
|
@Param("memo") String memo);
|
||||||
|
|
||||||
/** 미확인 내역 확정 (pending=0, 분류/계좌 보정) */
|
/** 미확인 내역 확정 (pending=0, 분류/계좌 보정) */
|
||||||
int confirm(@Param("id") Long id, @Param("memberId") Long memberId,
|
int confirm(@Param("id") Long id, @Param("memberId") Long memberId,
|
||||||
@Param("category") String category, @Param("walletId") Long walletId);
|
@Param("category") String category, @Param("walletId") Long walletId);
|
||||||
|
|||||||
@@ -231,13 +231,20 @@ public class AccountService {
|
|||||||
String walletType = p.isBankTransfer() ? "BANK" : "CARD";
|
String walletType = p.isBankTransfer() ? "BANK" : "CARD";
|
||||||
Long walletId = matchWalletId(p.getIssuer(), memberId, walletType);
|
Long walletId = matchWalletId(p.getIssuer(), memberId, walletType);
|
||||||
|
|
||||||
|
// 분류 자동학습: 같은 메모(가맹점) 키워드로 과거에 확정한 분류가 있으면 그대로 채워준다.
|
||||||
|
String type = p.isIncome() ? "INCOME" : "EXPENSE";
|
||||||
|
String merchant = p.getMerchant();
|
||||||
|
String learnedCategory = (merchant != null && !merchant.isBlank())
|
||||||
|
? mapper.findLearnedCategory(memberId, type, merchant)
|
||||||
|
: null;
|
||||||
|
|
||||||
AccountEntry entry = AccountEntry.builder()
|
AccountEntry entry = AccountEntry.builder()
|
||||||
.memberId(memberId)
|
.memberId(memberId)
|
||||||
.entryDate(p.getDate() != null ? p.getDate() : java.time.LocalDate.now())
|
.entryDate(p.getDate() != null ? p.getDate() : java.time.LocalDate.now())
|
||||||
.type(p.isIncome() ? "INCOME" : "EXPENSE")
|
.type(type)
|
||||||
.category(null)
|
.category(learnedCategory)
|
||||||
.amount(p.getAmount())
|
.amount(p.getAmount())
|
||||||
.memo(p.getMerchant())
|
.memo(merchant)
|
||||||
.walletId(walletId)
|
.walletId(walletId)
|
||||||
.toWalletId(null)
|
.toWalletId(null)
|
||||||
.installmentMonths(null)
|
.installmentMonths(null)
|
||||||
|
|||||||
@@ -160,6 +160,24 @@
|
|||||||
SELECT COUNT(*) FROM account_entry WHERE member_id = #{memberId} AND pending = 1
|
SELECT COUNT(*) FROM account_entry WHERE member_id = #{memberId} AND pending = 1
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<!-- 자동인식 분류 학습: 같은 메모 키워드로 과거에 확정된 분류 중 가장 많이 쓴 1건.
|
||||||
|
메모가 서로 포함관계여도(예: '스타벅스' ↔ '스타벅스 강남점') 매칭. -->
|
||||||
|
<select id="findLearnedCategory" resultType="string">
|
||||||
|
SELECT category
|
||||||
|
FROM account_entry
|
||||||
|
WHERE member_id = #{memberId}
|
||||||
|
AND type = #{type}
|
||||||
|
AND pending = 0
|
||||||
|
AND category IS NOT NULL AND category != ''
|
||||||
|
AND memo IS NOT NULL AND memo != ''
|
||||||
|
AND (memo = #{memo}
|
||||||
|
OR memo LIKE CONCAT('%', #{memo}, '%')
|
||||||
|
OR #{memo} LIKE CONCAT('%', memo, '%'))
|
||||||
|
GROUP BY category
|
||||||
|
ORDER BY COUNT(*) DESC, MAX(entry_date) DESC, MAX(id) DESC
|
||||||
|
LIMIT 1
|
||||||
|
</select>
|
||||||
|
|
||||||
<update id="confirm">
|
<update id="confirm">
|
||||||
UPDATE account_entry
|
UPDATE account_entry
|
||||||
SET pending = 0,
|
SET pending = 0,
|
||||||
|
|||||||
Reference in New Issue
Block a user