Java tutorial
/** * Copyright 2011-2012 eBusiness Information, Groupe Excilys (www.excilys.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.excilys.ebi.bank.service.impl; import static com.excilys.ebi.bank.model.entity.Operation.newOperation; import static com.excilys.ebi.bank.util.Asserts.isTrue; import static com.excilys.ebi.bank.util.Asserts.notNull; import static com.google.common.collect.Maps.newHashMap; import static com.google.common.collect.Maps.uniqueIndex; import static java.math.BigDecimal.ZERO; import static java.util.Collections.reverse; import static org.hibernate.Hibernate.initialize; import static org.joda.time.DateTime.now; import java.math.BigDecimal; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Map.Entry; import javax.validation.constraints.Min; import org.joda.time.DateMidnight; import org.joda.time.DateTime; import org.joda.time.YearMonth; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.security.access.prepost.PostAuthorize; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import com.excilys.ebi.bank.dao.AccountDao; import com.excilys.ebi.bank.dao.CardDao; import com.excilys.ebi.bank.dao.OperationDao; import com.excilys.ebi.bank.dao.OperationStatusRefDao; import com.excilys.ebi.bank.dao.OperationTypeRefDao; import com.excilys.ebi.bank.dao.UserDao; import com.excilys.ebi.bank.model.Calendar; import com.excilys.ebi.bank.model.IConstants; import com.excilys.ebi.bank.model.entity.Account; import com.excilys.ebi.bank.model.entity.Card; import com.excilys.ebi.bank.model.entity.Operation; import com.excilys.ebi.bank.model.entity.User; import com.excilys.ebi.bank.model.entity.ref.OperationSign; import com.excilys.ebi.bank.model.entity.ref.OperationStatus; import com.excilys.ebi.bank.model.entity.ref.OperationStatusRef; import com.excilys.ebi.bank.model.entity.ref.OperationType; import com.excilys.ebi.bank.model.entity.ref.OperationTypeRef; import com.excilys.ebi.bank.service.BankService; import com.excilys.ebi.bank.service.UnsufficientBalanceException; import com.google.common.base.Function; import com.googlecode.ehcache.annotations.Cacheable; import com.googlecode.ehcache.annotations.KeyGenerator; @Service @Transactional(readOnly = true) @Validated public class BankServiceImpl implements BankService { private static final int PAGE_SIZE = 20; @Autowired private UserDao userDao; @Autowired private AccountDao accountDao; @Autowired private OperationDao operationDao; @Autowired private CardDao cardDao; @Autowired private OperationStatusRefDao operationStatusDao; @Autowired private OperationTypeRefDao operationTypeDao; @Override @Cacheable(cacheName = IConstants.Cache.ENTITY_CACHE, keyGenerator = @KeyGenerator(name = IConstants.Cache.KEY_GENERATOR)) public Integer findAccountIdByNumber(String accountNumber) { Account account = accountDao.findByNumber(accountNumber); notNull(account, "account with number {} not found", accountNumber); return account.getId(); } @Override @Cacheable(cacheName = IConstants.Cache.ENTITY_CACHE, keyGenerator = @KeyGenerator(name = IConstants.Cache.KEY_GENERATOR)) public Integer findCardIdByNumber(String cardNumber) { Card card = cardDao.findByNumber(cardNumber); notNull(cardNumber, "card with number {} not found", cardNumber); return card.getId(); } @Override public List<Account> findAccountsByUser(User user) { return accountDao.findByUsersOrderByNumberAsc(user); } @Override public List<Account> findAccountsByUserFetchCardsOrderByNumberAsc(User user) { return accountDao.findByUserFetchCardsOrderByNumberAsc(user); } @Override @PostAuthorize("hasPermission(returnObject, 'read')") public Account findAccountByNumberFetchCards(String accountNumber) { Account account = accountDao.findByNumber(accountNumber); notNull(account, "account with number {} not found", accountNumber); initialize(account.getCards()); return account; } @Override public Page<Operation> findNonCardOperationsByAccountIdAndYearMonth(Integer accountId, YearMonth yearMonth, int page) { Pageable pageable = new PageRequest(page, PAGE_SIZE); return operationDao.findNonCardByAccountIdAndYearMonth(accountId, yearMonth, pageable); } @Override public Map<Card, BigDecimal[]> sumResolvedCardOperationsByAccountIdAndYearMonth(Integer accountId, YearMonth yearMonth) { Collection<Card> cards = cardDao.findByAccountIdOrderByNumberAsc(accountId); Map<Card, BigDecimal[]> sums = newHashMap(); for (Card card : cards) { sums.put(card, new BigDecimal[] { ZERO, ZERO }); } Map<Integer, Operation> creditSumsIndexedByCardId = uniqueIndex( operationDao.sumResolvedAmountByAccountIdAndYearMonthAndSignGroupByCard(accountId, yearMonth, OperationSign.CREDIT), new Function<Operation, Integer>() { @Override public Integer apply(Operation input) { return input.getCard().getId(); } }); Map<Integer, Operation> debitSumsIndexedByCardId = uniqueIndex( operationDao.sumResolvedAmountByAccountIdAndYearMonthAndSignGroupByCard(accountId, yearMonth, OperationSign.DEBIT), new Function<Operation, Integer>() { @Override public Integer apply(Operation input) { return input.getCard().getId(); } }); for (Entry<Card, BigDecimal[]> entry : sums.entrySet()) { Operation creditSum = creditSumsIndexedByCardId.get(entry.getKey().getId()); if (creditSum != null) { entry.getValue()[0] = creditSum.getAmount(); } Operation debitSum = debitSumsIndexedByCardId.get(entry.getKey().getId()); if (debitSum != null) { entry.getValue()[1] = debitSum.getAmount(); } } return sums; } @Override public BigDecimal sumResolvedAmountByAccountIdAndYearMonthAndSign(Integer accountId, YearMonth yearMonth, OperationSign sign) { return operationDao.sumResolvedAmountByAccountIdAndYearMonthAndSign(accountId, yearMonth, sign); } @Override public Page<Operation> findResolvedCardOperationsByAccountIdAndYearMonth(Integer accountId, YearMonth yearMonth, int page) { return operationDao.findCardOperationsByAccountIdAndYearMonthAndStatus(accountId, yearMonth, OperationStatus.RESOLVED, new PageRequest(page, PAGE_SIZE)); } @Override public BigDecimal sumResolvedCardAmountByAccountIdAndYearMonthAndSign(Integer accountId, YearMonth yearMonth, OperationSign sign) { return operationDao.sumCardAmountByAccountIdAndYearMonthAndSignAndStatus(accountId, yearMonth, sign, OperationStatus.RESOLVED); } @Override public Page<Operation> findResolvedCardOperationsByCardIdAndYearMonth(Integer cardId, YearMonth yearMonth, int page) { return operationDao.findCardOperationsByCardIdAndYearMonthAndStatus(cardId, yearMonth, OperationStatus.RESOLVED, new PageRequest(page, PAGE_SIZE)); } @Override public BigDecimal sumResolvedCardAmountByCardIdAndYearMonthAndSign(Integer cardId, YearMonth yearMonth, OperationSign sign) { return operationDao.sumCardAmountByCardIdAndYearMonthAndSignAndStatus(cardId, yearMonth, sign, OperationStatus.RESOLVED); } @Override public Page<Operation> findPendingCardOperationsByAccountId(Integer accountId, int page) { return operationDao.findCardOperationsByAccountIdAndYearMonthAndStatus(accountId, null, OperationStatus.PENDING, new PageRequest(page, PAGE_SIZE)); } @Override public BigDecimal sumPendingCardAmountByAccountIdAndSign(Integer accountId, OperationSign sign) { return operationDao.sumCardAmountByAccountIdAndYearMonthAndSignAndStatus(accountId, null, sign, OperationStatus.PENDING); } @Override public Page<Operation> findPendingCardOperationsByCardId(Integer cardId, int page) { return operationDao.findCardOperationsByCardIdAndYearMonthAndStatus(cardId, null, OperationStatus.PENDING, new PageRequest(page, PAGE_SIZE)); } @Override public BigDecimal sumPendingCardAmountByCardIdAndSign(Integer cardId, OperationSign sign) { return operationDao.sumCardAmountByCardIdAndYearMonthAndSignAndStatus(cardId, null, sign, OperationStatus.PENDING); } @Override public Page<Operation> findTransferOperationsByAccountId(Integer accountId, int page) { return operationDao.findTransferByAccountId(accountId, null); } @Override @Transactional(readOnly = false) public void performTransfer(Integer debitedAccountId, Integer creditedAccountId, @Min(10) BigDecimal amount) throws UnsufficientBalanceException { isTrue(!debitedAccountId.equals(creditedAccountId), "accounts must be different"); Account debitedAccount = accountDao.findOne(debitedAccountId); notNull(debitedAccount, "account with number {} not found", debitedAccount); if (debitedAccount.getBalance().compareTo(amount) < 0) { throw new UnsufficientBalanceException(); } Account creditedAccount = accountDao.findOne(creditedAccountId); notNull(creditedAccount, "account with number {} not found", creditedAccount); debitedAccount.setBalance(debitedAccount.getBalance().subtract(amount)); creditedAccount.setBalance(creditedAccount.getBalance().add(amount)); DateTime now = now(); OperationStatusRef status = operationStatusDao.findOne(OperationStatus.RESOLVED); OperationTypeRef type = operationTypeDao.findOne(OperationType.TRANSFER); Operation debitOperation = newOperation().withName("transfert -" + amount).withAccount(debitedAccount) .withAmount(amount.negate()).withDate(now).withStatus(status).withType(type).build(); Operation creditOperation = newOperation().withName("transfert +" + amount).withAccount(creditedAccount) .withAmount(amount).withDate(now).withStatus(status).withType(type).build(); operationDao.save(debitOperation); operationDao.save(creditOperation); } @Override public long countUsers() { return userDao.count(); } @Override public long countAccounts() { return accountDao.count(); } @Override public long countOperations() { return operationDao.count(); } @Override public Calendar getCalendar(Integer year, Integer month) { Calendar calendar = new Calendar(); // build months List<DateTime> months = calendar.getMonths(); DateMidnight thisMonth = getDefaultDateTime().toDateMidnight().withDayOfMonth(1); months.add(thisMonth.toDateTime()); // display last 6 months while (months.size() < 6) { thisMonth = thisMonth.minusMonths(1); months.add(thisMonth.toDateTime()); } reverse(months); // build selectedMonth if (year != null) { notNull(month, "month is required if year is specified"); DateTime selectedMonth = new DateMidnight().withDayOfMonth(1).withYear(year).withMonthOfYear(month) .toDateTime(); calendar.setSelectedMonth(selectedMonth); } return calendar; } @Override public DateTime getDefaultDateTime() { return operationDao.getLastOperationDate(); // return now(); } }