← Back to Documentation Portal|📊 Coverage Quality Gates|Coverage Dashboard
Payflow API — Automated JaCoCo Code Coverage (90% Line / 73% Branch)

UserService.java

package com.payflow.service;

import java.math.BigDecimal;
import java.util.List;
import java.util.Optional;
import java.util.UUID;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.payflow.dto.request.CreateUserRequest;
import com.payflow.entity.BalanceLedgerEntry;
import com.payflow.entity.User;
import com.payflow.exception.DuplicateUpiIdException;
import com.payflow.exception.ForbiddenOperationException;
import com.payflow.exception.UserNotFoundException;
import com.payflow.repository.BalanceLedgerRepository;
import com.payflow.repository.UserRepository;
import com.payflow.security.SecurityUtils;

@Service
public class UserService {

	private static final Logger LOG = LoggerFactory.getLogger(UserService.class);

	private final UserRepository userRepository;
	private final BalanceLedgerRepository balanceLedgerRepository;
	private final UpiValidationService upiValidationService;

	public UserService(UserRepository userRepository, //
			BalanceLedgerRepository balanceLedgerRepository, //
			UpiValidationService upiValidationService) {
		this.userRepository = userRepository;
		this.balanceLedgerRepository = balanceLedgerRepository;
		this.upiValidationService = upiValidationService;
	}

	@Transactional
	@CacheEvict(value = {"users", "user_ledgers"}, allEntries = true)
	public User registerUser(CreateUserRequest request) {
		if (userRepository.findByUpiId(request.getUpiId()).isPresent()) {
			throw new DuplicateUpiIdException(request.getUpiId());
		}
		upiValidationService.validateUpi(request.getUpiId());
		LOG.info("Registering new user with UPI ID: {}", request.getUpiId());
		User user = User.builder().name(request.getName()).upiId(request.getUpiId())
				.phoneNumber(request.getPhoneNumber()).balance(request.getBalance()).build();
		User savedUser = userRepository.save(user);
		LOG.info("User registered: refId={}, upiId={}", savedUser != null ? savedUser.getReferenceId() : null,
				request.getUpiId());
		return savedUser;
	}

	@Transactional(readOnly = true)
	public List<User> getAllUsers() {
		return userRepository.findAll();
	}

	@Transactional(readOnly = true)
	public Page<User> getAllUsers(Pageable pageable) {
		return userRepository.findAll(pageable);
	}

	@Transactional(readOnly = true)
	@Cacheable(value = "users", key = "'id:' + #id", unless = "#result == null")
	public Optional<User> getUserById(Long id) {
		return userRepository.findById(id);
	}

	@Transactional(readOnly = true)
	@Cacheable(value = "users", key = "'ref:' + #referenceId", unless = "#result == null")
	public Optional<User> getUserByReferenceId(UUID referenceId) {
		return userRepository.findByReferenceId(referenceId);
	}

	@Transactional(readOnly = true)
	@Cacheable(value = "users", key = "'upi:' + #upiId", unless = "#result == null")
	public Optional<User> findByUpiId(String upiId) {
		return userRepository.findByUpiId(upiId);
	}

	@Transactional(readOnly = true)
	@Cacheable(value = "users", key = "'upi:' + #upiId")
	public User getUserByUpiId(String upiId) {
		return userRepository.findByUpiId(upiId)
				.orElseThrow(() -> new UserNotFoundException("User not found with UPI ID: " + upiId));
	}

	@Transactional(readOnly = true)
	public List<User> getUsersWithBalanceAbove(BigDecimal amount) {
		return userRepository.findUsersWithBalanceGreaterThan(amount);
	}

	@Transactional(readOnly = true)
	@Cacheable(value = "user_ledgers", key = "#userReferenceId + '_' + #pageable.pageNumber")
	public Page<BalanceLedgerEntry> getUserLedger(UUID userReferenceId, Pageable pageable) {
		User user = userRepository.findByReferenceId(userReferenceId)
				.orElseThrow(() -> new UserNotFoundException("User not found: " + userReferenceId));

		String authenticatedUpi = SecurityUtils.getAuthenticatedUpiId();
		if (authenticatedUpi != null && !authenticatedUpi.equalsIgnoreCase(user.getUpiId())) {
			throw new ForbiddenOperationException("Authenticated user '" + authenticatedUpi
					+ "' is not authorized to view ledger for: " + userReferenceId);
		}

		return balanceLedgerRepository.findByUserReferenceIdOrderByCreatedAtDesc(userReferenceId, pageable);
	}

	@Transactional(readOnly = true)
	public Page<BalanceLedgerEntry> getUserLedgerByReferenceId(UUID userReferenceId, Pageable pageable) {
		return getUserLedger(userReferenceId, pageable);
	}
}