PayCell
1. Ilkin olaraq ConfigurationController controllerinin ne ish gorduyunu arashdiririq:
package com.azercell.paycell.paycellconsumer.controller;
import com.azercell.paycell.paycellconsumer.model.ShortNumberConfiguration;
import com.azercell.paycell.paycellconsumer.model.dto.ShortNumberConfigurationResponseDto;
import com.azercell.paycell.paycellconsumer.service.ShortNumberConfigurationService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.stream.Collectors;
@RestController
@RequiredArgsConstructor
@Tag(name = "ConfigurationController")
@RequestMapping("/configurations")
public class ConfigurationController {
private final ShortNumberConfigurationService shortNumberConfigurationService;
@GetMapping
@Operation(
summary = "Retrieve paycell configuration data",
description = "Retrieves the current paycell configuration data from the database."
)
public List<ShortNumberConfigurationResponseDto> getShortNumberConfiguration() {
List<ShortNumberConfiguration> configurations = shortNumberConfigurationService.getShortNumberConfigurations();
return configurations.stream().map(c -> {
Long fullPrice = c.getAmountInCents() + c.getFeeAmountInCents();
return new ShortNumberConfigurationResponseDto(c.getServiceType(), c.getAmountInCents().toString(), fullPrice.toString());
}).collect(Collectors.toList());
}
}
GET /configurations endpoint-ini addım-addım izah edirəm. Service layer-ə də baxırıq:
[5 tools called]
Mapper-ə baxırıq:
[3 tools called]
GET /configurations endpoint-ini addım-addım izah edirəm:
## GET /configurations — addım-addım analiz
### Controller (ConfigurationController.java)
#### Sətir 16-19: Class declaration və dependencies
```java
@RestController // REST controller (HTTP endpoint-lər üçün)
@RequiredArgsConstructor // Lombok: constructor injection
@Tag(name = "ConfigurationController") // Swagger UI-da görünəcək ad
@RequestMapping("/configurations") // Base path: /configurations
public class ConfigurationController {
private final ShortNumberConfigurationService shortNumberConfigurationService;
}
```
Nə edir:
- `@RestController` — Spring-ə REST controller olduğunu bildirir
- `@RequiredArgsConstructor` — Lombok constructor yaradır
- `@RequestMapping("/configurations")` — Base path `/configurations`
---
#### Sətir 24-28: Endpoint annotation
```java
@GetMapping // HTTP GET metodu
@Operation( // Swagger documentation
summary = "Retrieve paycell configuration data",
description = "Retrieves the current paycell configuration data from the database."
)
```
Nə edir:
- `@GetMapping` — GET request qəbul edir
- `@Operation` — Swagger UI-da görünəcək açıqlama
Endpoint URL: `GET http://localhost:8080/configurations`
---
#### Sətir 29: Metod signature
```java
public List<ShortNumberConfigurationResponseDto> getShortNumberConfiguration() {
```
Nə edir:
- `List<ShortNumberConfigurationResponseDto>` — Response tipi (list)
- Parametr yoxdur — bütün konfiqurasiyaları qaytarır
---
#### Sətir 30: Service çağırışı
```java
List<ShortNumberConfiguration> configurations =
shortNumberConfigurationService.getShortNumberConfigurations();
```
Nə edir:
- Service-dən bütün konfiqurasiyaları alır
- `ShortNumberConfiguration` list qaytarır
---
#### Sətir 31-34: Response mapping
```java
return configurations.stream().map(c -> {
Long fullPrice = c.getAmountInCents() + c.getFeeAmountInCents();
return new ShortNumberConfigurationResponseDto(
c.getServiceType(), // Məsələn: 9204
c.getAmountInCents().toString(), // Məsələn: "20"
fullPrice.toString() // Məsələn: "22"
);
}).collect(Collectors.toList());
```
Nə edir:
- Stream ilə hər konfiqurasiyanı map edir
- `fullPrice` hesablayır: `amountInCents + feeAmountInCents`
- `ShortNumberConfigurationResponseDto` yaradır
- List-ə toplayır
---
### Service layer (ShortNumberConfigurationServiceImpl.java)
#### Sətir 23-24: Service metodu
```java
@Override
public List<ShortNumberConfiguration> getShortNumberConfigurations() {
return configurationCache.getShortNumberConfigurations();
}
```
Nə edir:
- Cache service-ə yönləndirir
- Cache service DB-dən oxuyur
---
### Cache layer (ShortNumberConfigurationRedisCache.java)
#### Sətir 43-50: Cache metodu
```java
@Override
public List<ShortNumberConfiguration> getShortNumberConfigurations() {
log.info("Get ShortNumberConfigurations from DB");
List<ShortNumberConfigurationEntity> entities = valueMapper.getShortNumberConfigurations();
return entities.stream()
.map(this::mapToShortNumberConfiguration) // Entity → Model
.peek(this::cacheShortNumberConfiguration) // Redis-ə yazır
.collect(Collectors.toList());
}
```
Nə edir:
- DB-dən entity-ləri alır
- Entity-ləri model-ə çevirir
- Hər birini Redis-ə yazır (cache)
- List qaytarır
---
#### Sətir 53-69: Entity → Model mapping
```java
private ShortNumberConfiguration mapToShortNumberConfiguration(ShortNumberConfigurationEntity entity) {
if (entity == null) {
return null;
}
Long price = toCents(entity.getPrice()); // BigDecimal → Long (cents)
Long priceFull = toCents(entity.getPriceFull()); // BigDecimal → Long (cents)
return new ShortNumberConfiguration(
entity.getDiameterChargingFeeNumber(), // Məsələn: "123456"
entity.getDiameterChargingFullNumber(), // Məsələn: "123457"
entity.getDiameterRefundWithoutFeeNumber(), // Məsələn: "123458"
entity.getDiameterRefundFullNumber(), // Məsələn: "123459"
entity.getRefillProfileId(), // Məsələn: "PROFILE_123"
price, // Məsələn: 2000 (20 manat = 2000 qəpik)
Math.max(0, priceFull - price), // Fee: priceFull - price
entity.getServiceType() // Məsələn: 9204
);
}
```
Nə edir:
- DB entity-sini model-ə çevirir
- `toCents()` — manatı qəpikə çevirir (məs. 20.00 → 2000)
- `feeAmountInCents` hesablayır: `priceFull - price`
---
### Database layer (value-mapper.xml)
#### Sətir 42-45: SQL query
```xml
<select id="getShortNumberConfigurations" resultMap="shortNumberConfigResultMap">
SELECT PRICE, PRICE_FULL, CHARGING_DIAMETER_FEE, CHARGING_DIAMETER_FULL,
DIAMETER_NUM, DIAMETER_NUM_FULL, PAYMENT_PROFILE, SERVICE_TYPE
FROM SUBDEALER.PAYCELL_VALUES
WHERE sysdate BETWEEN valid_from AND valid_to
</select>
```
Nə edir:
- `SUBDEALER.PAYCELL_VALUES` cədvəlindən oxuyur
- Yalnız aktiv (valid_from və valid_to arasında) qeydləri gətirir
- Bütün konfiqurasiyaları qaytarır
---
## Tam axın diagramı
```
1. Client → GET /configurations
↓
2. ConfigurationController.getShortNumberConfiguration() (sətir 29)
↓
3. ShortNumberConfigurationService.getShortNumberConfigurations() (sətir 30)
↓
4. ShortNumberConfigurationRedisCache.getShortNumberConfigurations() (sətir 43)
↓
5. ValueMapper.getShortNumberConfigurations() → SQL query (sətir 45)
↓
6. Oracle DB → SUBDEALER.PAYCELL_VALUES table
↓
7. Entity → Model mapping (sətir 47-48)
↓
8. Redis cache-ə yazır (sətir 49)
↓
9. Controller-də Response mapping (sətir 31-34)
↓
10. JSON response qaytarır
```
---
## Nümunə response
```json
[
{
"serviceType": 9204,
"price": "20",
"price_full": "22"
},
{
"serviceType": 2206,
"price": "200",
"price_full": "220"
}
]
```
---
## Nə üçün lazımdır?
- Short number konfiqurasiyalarını görmək
- Test və debug üçün
- Frontend/başqa servislər üçün məlumat
Swaggerden sorgu atdiqda bu response qayidir:
[
{
"serviceType": 2206,
"price": "200",
"price_full": "220"
},
{
"serviceType": 2208,
"price": "500",
"price_full": "550"
},
{
"serviceType": 9200,
"price": "50",
"price_full": "50"
},
{
"serviceType": 9202,
"price": "100",
"price_full": "100"
},
{
"serviceType": 9204,
"price": "20",
"price_full": "20"
},
{
"serviceType": 220669,
"price": "200",
"price_full": "220"
},
{
"serviceType": 220869,
"price": "500",
"price_full": "550"
},
{
"serviceType": 920069,
"price": "50",
"price_full": "50"
},
{
"serviceType": 920269,
"price": "100",
"price_full": "100"
},
{
"serviceType": 920469,
"price": "20",
"price_full": "20"
}
]2. Ikinci endpoint ShortNumberController.
package com.azercell.paycell.paycellconsumer.controller;
import com.azercell.paycell.paycellconsumer.service.ShortNumberConfigurationCache;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequiredArgsConstructor
@Tag(name = "ShortNumberController")
@RequestMapping("/api/v1/short-number")
public class ShortNumberController {
private final ShortNumberConfigurationCache cacheService;
@PutMapping("/flush")
public void flush() {
cacheService.flush();
}
}
PUT /api/v1/short-number/flush endpoint-ini addım-addım izah edirəm:
[3 tools called]
PUT /api/v1/short-number/flush endpoint-ini addım-addım izah edirəm:
## PUT /api/v1/short-number/flush — detallı izah
### Nə edir?
Redis cache-i təmizləyir (bütün short number konfiqurasiyalarını silir).
---
## Setir-setir izah
### Sətir 10-13: Class declaration
```java
@RestController // REST controller
@RequiredArgsConstructor // Lombok: constructor injection
@Tag(name = "ShortNumberController") // Swagger UI-da görünəcək ad
@RequestMapping("/api/v1/short-number") // Base path
public class ShortNumberController {
```
Nə edir:
- `@RestController` — REST endpoint-lər üçün
- `@RequestMapping("/api/v1/short-number")` — Base path
Endpoint URL: `PUT http://localhost:8080/api/v1/short-number/flush`
---
### Sətir 15: Dependency injection
```java
private final ShortNumberConfigurationCache cacheService;
```
Nə edir:
- `ShortNumberConfigurationCache` service-ini inject edir
- Cache əməliyyatları üçün istifadə olunur
---
### Sətir 18-21: Endpoint metodu
```java
@PutMapping("/flush") // HTTP PUT metodu
public void flush() { // Parametr yoxdur, response yoxdur
cacheService.flush(); // Cache-i təmizləyir
}
```
Nə edir:
- `@PutMapping("/flush")` — PUT request qəbul edir
- `cacheService.flush()` — Cache-i təmizləyir
---
## Service layer (ShortNumberConfigurationRedisCache.java)
### Sətir 37-40: Flush metodu
```java
@Override
@CacheEvict(value = REDIS_SHORT_NUMBER_CACHE_NAME, allEntries = true)
public void flush() {
log.info("Flush {} redis cache", REDIS_SHORT_NUMBER_CACHE_NAME);
}
```
Nə edir:
- `@CacheEvict` — Spring Cache annotation
- `allEntries = true` — Bütün key-ləri silir
- `REDIS_SHORT_NUMBER_CACHE_NAME` — `"shortNumberConfigCache"`
---
## Tam axın
```
1. Client → PUT /api/v1/short-number/flush
↓
2. Controller.flush() (sətir 20)
↓
3. cacheService.flush() (sətir 20)
↓
4. @CacheEvict annotation işləyir (sətir 37)
↓
5. Redis-dən BÜTÜN key-lər silinir:
- shortNumberConfigCache::2206
- shortNumberConfigCache::2208
- shortNumberConfigCache::9200
- ... (hamısı)
↓
6. Response: 200 OK (boş body)
```
---
## Nə üçün lazımdır?
### Ssenari 1: DB-də dəyişiklik
```
1. DB admin → DB-də konfiqurasiya dəyişir
2. Sən → PUT /api/v1/short-number/flush çağırırsan
3. Cache təmizlənir
4. Növbəti çağırışda → DB-dən yeni məlumat oxuyur
```
### Ssenari 2: Test
```
1. Test zamanı cache-i təmizləmək lazımdır
2. flush() çağırırsan
3. Cache təmizlənir
```
---
## Nümunə istifadə
### Swagger UI-da:
1. PUT /api/v1/short-number/flush → "Try it out"
2. "Execute"
3. Response: 200 OK (boş body)
### Terminaldan:
```bash
curl -X PUT http://localhost:8080/api/v1/short-number/flush \
-u apiuser:apipass
```
---
## Nə baş verir?
### Əvvəl (Redis-də):
```
✅ shortNumberConfigCache::2206
✅ shortNumberConfigCache::2208
✅ shortNumberConfigCache::9200
... (10 key)
```
### flush() çağırılandan sonra:
```
❌ (hamısı silindi)
```
### Növbəti GET /configurations çağırışında:
```
1. Cache-də yoxdur → DB-dən oxuyur
2. Yenidən Redis-ə yazır
3. Response qaytarır
```
---
## Xülasə
Bu endpoint Redis cache-i təmizləyir. DB-də dəyişiklik olduqda və ya test zamanı istifadə olunur.
Комментарии
Отправить комментарий