Merge branch 'dev'
Deploy / deploy (push) Failing after 11m56s
CI / build (push) Failing after 13m46s

This commit is contained in:
ByungCheol
2026-06-06 16:42:34 +09:00
4 changed files with 62 additions and 0 deletions
@@ -23,6 +23,7 @@ import java.util.List;
* DELETE /api/account/invest/holdings/{id} 종목 삭제(매매이력 포함)
* GET /api/account/invest/holdings/{id}/trades 매매이력
* POST /api/account/invest/holdings/{id}/trades 매수/매도
* PUT /api/account/invest/trades/{id} 매매 수정
* DELETE /api/account/invest/trades/{id} 매매 삭제
*/
@RestController
@@ -93,6 +94,14 @@ public class InvestController {
return ResponseEntity.status(HttpStatus.CREATED).body(investService.addTrade(id, req, current.getId()));
}
@PutMapping("/trades/{id}")
public TradeResponse updateTrade(
@PathVariable Long id,
@Valid @RequestBody TradeRequest req,
@RequestAttribute(AuthInterceptor.CURRENT_MEMBER) SessionUser current) {
return investService.updateTrade(id, req, current.getId());
}
@DeleteMapping("/trades/{id}")
public ResponseEntity<Void> deleteTrade(
@PathVariable Long id,
@@ -38,6 +38,8 @@ public interface InvestMapper {
int insertTrade(InvestTrade trade);
int updateTrade(InvestTrade trade);
int deleteTrade(@Param("id") Long id, @Param("memberId") Long memberId);
int deleteTradesByHolding(@Param("holdingId") Long holdingId, @Param("memberId") Long memberId);
@@ -138,6 +138,50 @@ public class InvestService {
return TradeResponse.from(t);
}
@Transactional
public TradeResponse updateTrade(Long id, TradeRequest req, Long memberId) {
InvestTrade existing = investMapper.findTradeByIdAndMember(id, memberId);
if (existing == null) {
throw new ApiException(HttpStatus.NOT_FOUND, "매매 내역을 찾을 수 없습니다.");
}
Long holdingId = existing.getHoldingId();
String type = "SELL".equals(req.getTradeType()) ? "SELL" : "BUY";
long fee = req.getFee() != null ? req.getFee() : 0L;
// 편집을 반영한 시점별 시뮬레이션 — 어느 시점에도 보유수량이 음수가 되면 거부
java.util.List<InvestTrade> sim = new java.util.ArrayList<>();
for (InvestTrade t : investMapper.findTradesByHolding(holdingId, memberId)) {
if (t.getId().equals(id)) {
sim.add(InvestTrade.builder().id(id).tradeType(type).tradeDate(req.getTradeDate())
.quantity(req.getQuantity()).price(req.getPrice()).fee(fee).build());
} else {
sim.add(t);
}
}
sim.sort(java.util.Comparator.comparing(InvestTrade::getTradeDate)
.thenComparing(InvestTrade::getId));
BigDecimal running = BigDecimal.ZERO;
for (InvestTrade t : sim) {
if ("BUY".equals(t.getTradeType())) {
running = running.add(t.getQuantity());
} else {
running = running.subtract(t.getQuantity());
if (running.signum() < 0) {
throw new ApiException(HttpStatus.BAD_REQUEST,
"수정하면 보유수량이 음수가 됩니다. 매도 수량/순서를 확인하세요.");
}
}
}
existing.setTradeType(type);
existing.setTradeDate(req.getTradeDate());
existing.setQuantity(req.getQuantity());
existing.setPrice(req.getPrice());
existing.setFee(fee);
investMapper.updateTrade(existing);
return TradeResponse.from(existing);
}
@Transactional
public void deleteTrade(Long id, Long memberId) {
if (investMapper.findTradeByIdAndMember(id, memberId) == null) {
@@ -87,6 +87,13 @@
VALUES (#{memberId}, #{holdingId}, #{tradeType}, #{tradeDate}, #{quantity}, #{price}, #{fee}, NOW())
</insert>
<update id="updateTrade" parameterType="com.sb.web.account.domain.InvestTrade">
UPDATE invest_trade
SET trade_type = #{tradeType}, trade_date = #{tradeDate},
quantity = #{quantity}, price = #{price}, fee = #{fee}
WHERE id = #{id} AND member_id = #{memberId}
</update>
<delete id="deleteTrade">
DELETE FROM invest_trade WHERE id = #{id} AND member_id = #{memberId}
</delete>