добавил стату по стоимости заказа
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,4 +1,4 @@
|
|||||||
-- Тестовые клиенты
|
|
||||||
INSERT INTO customers (id, name, email, created_at, updated_at)
|
INSERT INTO customers (id, name, email, created_at, updated_at)
|
||||||
VALUES
|
VALUES
|
||||||
(1, 'Иван Иванов', 'ivan@mail.ru', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
|
(1, 'Иван Иванов', 'ivan@mail.ru', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
|
||||||
@@ -7,7 +7,6 @@ VALUES
|
|||||||
(4, 'Алексей Козлов', 'alex@mail.ru', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
|
(4, 'Алексей Козлов', 'alex@mail.ru', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
|
||||||
(5, 'Ольга Новикова', 'olga@mail.ru', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);
|
(5, 'Ольга Новикова', 'olga@mail.ru', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);
|
||||||
|
|
||||||
-- Тестовые доставки (опционально)
|
|
||||||
INSERT INTO deliveries (id, tracking_number, destination, status, customer_name, created_at, updated_at)
|
INSERT INTO deliveries (id, tracking_number, destination, status, customer_name, created_at, updated_at)
|
||||||
VALUES
|
VALUES
|
||||||
(1, 'IVN777777', 'гоголя 34', 'В пути', 'Иван Иванов', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
|
(1, 'IVN777777', 'гоголя 34', 'В пути', 'Иван Иванов', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -4,7 +4,8 @@ function OrderForm({ customers, deliveries, onSubmit, editOrder }) {
|
|||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
customerId: '',
|
customerId: '',
|
||||||
deliveryId: '',
|
deliveryId: '',
|
||||||
status: 'В пути'
|
status: 'В пути',
|
||||||
|
orderAmount: ''
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -12,17 +13,40 @@ function OrderForm({ customers, deliveries, onSubmit, editOrder }) {
|
|||||||
setFormData({
|
setFormData({
|
||||||
customerId: editOrder.customer?.id || '',
|
customerId: editOrder.customer?.id || '',
|
||||||
deliveryId: editOrder.delivery?.id || '',
|
deliveryId: editOrder.delivery?.id || '',
|
||||||
status: editOrder.status || 'В пути'
|
status: editOrder.status || 'В пути',
|
||||||
|
orderAmount: editOrder.orderAmount || ''
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [editOrder]);
|
}, [editOrder]);
|
||||||
|
|
||||||
const handleSubmit = (e) => {
|
const handleSubmit = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
onSubmit(formData);
|
|
||||||
if (!editOrder) {
|
|
||||||
setFormData({ customerId: '', deliveryId: '', status: 'В пути' });
|
const submitData = {
|
||||||
|
...formData,
|
||||||
|
customerId: formData.customerId ? parseInt(formData.customerId) : null,
|
||||||
|
deliveryId: formData.deliveryId ? parseInt(formData.deliveryId) : null,
|
||||||
|
orderAmount: parseFloat(formData.orderAmount) || 0
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
if (!submitData.customerId || !submitData.deliveryId) {
|
||||||
|
alert("Пожалуйста, выберите клиента и доставку!");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log("Отправляемые данные:", submitData);
|
||||||
|
onSubmit(submitData);
|
||||||
|
|
||||||
|
if (!editOrder) {
|
||||||
|
setFormData({ customerId: '', deliveryId: '', status: 'В пути', orderAmount: '' });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleChange = (e) => {
|
||||||
|
const { name, value } = e.target;
|
||||||
|
setFormData(prev => ({ ...prev, [name]: value }));
|
||||||
};
|
};
|
||||||
|
|
||||||
const statusOptions = ['В пути', 'Доставлено', 'Обработка', 'Отменен'];
|
const statusOptions = ['В пути', 'Доставлено', 'Обработка', 'Отменен'];
|
||||||
@@ -34,8 +58,9 @@ function OrderForm({ customers, deliveries, onSubmit, editOrder }) {
|
|||||||
<div style={{ marginBottom: '10px' }}>
|
<div style={{ marginBottom: '10px' }}>
|
||||||
<label>Клиент: </label>
|
<label>Клиент: </label>
|
||||||
<select
|
<select
|
||||||
|
name="customerId"
|
||||||
value={formData.customerId}
|
value={formData.customerId}
|
||||||
onChange={(e) => setFormData({...formData, customerId: e.target.value})}
|
onChange={handleChange}
|
||||||
required
|
required
|
||||||
>
|
>
|
||||||
<option value="">Выберите клиента</option>
|
<option value="">Выберите клиента</option>
|
||||||
@@ -50,8 +75,9 @@ function OrderForm({ customers, deliveries, onSubmit, editOrder }) {
|
|||||||
<div style={{ marginBottom: '10px' }}>
|
<div style={{ marginBottom: '10px' }}>
|
||||||
<label>Доставка: </label>
|
<label>Доставка: </label>
|
||||||
<select
|
<select
|
||||||
|
name="deliveryId"
|
||||||
value={formData.deliveryId}
|
value={formData.deliveryId}
|
||||||
onChange={(e) => setFormData({...formData, deliveryId: e.target.value})}
|
onChange={handleChange}
|
||||||
required
|
required
|
||||||
>
|
>
|
||||||
<option value="">Выберите доставку</option>
|
<option value="">Выберите доставку</option>
|
||||||
@@ -66,8 +92,9 @@ function OrderForm({ customers, deliveries, onSubmit, editOrder }) {
|
|||||||
<div style={{ marginBottom: '10px' }}>
|
<div style={{ marginBottom: '10px' }}>
|
||||||
<label>Статус: </label>
|
<label>Статус: </label>
|
||||||
<select
|
<select
|
||||||
|
name="status"
|
||||||
value={formData.status}
|
value={formData.status}
|
||||||
onChange={(e) => setFormData({...formData, status: e.target.value})}
|
onChange={handleChange}
|
||||||
>
|
>
|
||||||
{statusOptions.map(status => (
|
{statusOptions.map(status => (
|
||||||
<option key={status} value={status}>{status}</option>
|
<option key={status} value={status}>{status}</option>
|
||||||
@@ -75,6 +102,21 @@ function OrderForm({ customers, deliveries, onSubmit, editOrder }) {
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div style={{ marginBottom: '10px' }}>
|
||||||
|
<label>Стоимость заказа (руб): </label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
name="orderAmount"
|
||||||
|
value={formData.orderAmount}
|
||||||
|
onChange={handleChange}
|
||||||
|
placeholder="Введите сумму"
|
||||||
|
min="0"
|
||||||
|
step="0.01"
|
||||||
|
required
|
||||||
|
style={{ marginLeft: '10px', padding: '5px' }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<button type="submit" style={{ padding: '10px 20px' }}>
|
<button type="submit" style={{ padding: '10px 20px' }}>
|
||||||
{editOrder ? 'Обновить заказ' : 'Создать заказ'}
|
{editOrder ? 'Обновить заказ' : 'Создать заказ'}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -1,26 +1,36 @@
|
|||||||
function OrderItem({ order, onEdit, onDelete }) {
|
function OrderItem({ order, onEdit, onDelete }) {
|
||||||
return (
|
return (
|
||||||
<div style={{
|
<div style={{
|
||||||
border: '1px solid #ccc',
|
border: '1px solid #ccc',
|
||||||
padding: '15px',
|
padding: '15px',
|
||||||
margin: '10px 0',
|
margin: '10px 0',
|
||||||
borderRadius: '5px'
|
borderRadius: '5px'
|
||||||
}}>
|
}}>
|
||||||
<h4>Заказ #{order.id}</h4>
|
<h3>Заказ #{order.id}</h3>
|
||||||
|
|
||||||
<p><strong>Клиент:</strong> {order.customer?.name} ({order.customer?.email})</p>
|
<p><strong>Клиент:</strong> {order.customer?.name} ({order.customer?.email})</p>
|
||||||
|
|
||||||
<p><strong>Доставка:</strong> {order.delivery?.trackingNumber} - {order.delivery?.destination}</p>
|
<p><strong>Доставка:</strong> {order.delivery?.trackingNumber} - {order.delivery?.destination}</p>
|
||||||
<p><strong>Статус:</strong>
|
|
||||||
<span style={{
|
|
||||||
color: order.status === 'Доставлено' ? 'green' :
|
|
||||||
order.status === 'В пути' ? 'orange' : 'gray',
|
|
||||||
fontWeight: 'bold',
|
|
||||||
marginLeft: '10px'
|
|
||||||
}}>
|
|
||||||
{order.status}
|
|
||||||
</span>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
|
<p><strong>Статус:</strong> {order.status}</p>
|
||||||
|
|
||||||
|
{}
|
||||||
|
<p><strong>Сумма заказа:</strong> {order.orderAmount ? `${order.orderAmount} руб.` : 'Не указана'}</p>
|
||||||
|
|
||||||
|
<div style={{ marginTop: '10px' }}>
|
||||||
|
<button
|
||||||
|
onClick={() => onEdit(order)}
|
||||||
|
style={{ marginRight: '10px', padding: '5px 10px' }}
|
||||||
|
>
|
||||||
|
Редактировать
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => onDelete(order.id)}
|
||||||
|
style={{ padding: '5px 10px', backgroundColor: '#ff4444', color: 'white' }}
|
||||||
|
>
|
||||||
|
Удалить
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,13 +62,13 @@ function OrderList() {
|
|||||||
|
|
||||||
const handleCreateOrder = async (orderData) => {
|
const handleCreateOrder = async (orderData) => {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
const orderRq = {
|
const orderRq = {
|
||||||
customerId: orderData.customerId,
|
customerId: orderData.customerId,
|
||||||
deliveryId: orderData.deliveryId,
|
deliveryId: orderData.deliveryId,
|
||||||
status: orderData.status
|
status: orderData.status,
|
||||||
|
orderAmount: orderData.orderAmount
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await fetch('http://localhost:8080/api/orders', {
|
const response = await fetch('http://localhost:8080/api/orders', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
@@ -77,12 +77,12 @@ function OrderList() {
|
|||||||
},
|
},
|
||||||
body: JSON.stringify(orderRq)
|
body: JSON.stringify(orderRq)
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const errorText = await response.text();
|
const errorText = await response.text();
|
||||||
throw new Error(errorText || `HTTP error! status: ${response.status}`);
|
throw new Error(errorText || `HTTP error! status: ${response.status}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
await loadData();
|
await loadData();
|
||||||
setEditingOrder(null);
|
setEditingOrder(null);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -96,9 +96,10 @@ function OrderList() {
|
|||||||
const orderRq = {
|
const orderRq = {
|
||||||
customerId: orderData.customerId,
|
customerId: orderData.customerId,
|
||||||
deliveryId: orderData.deliveryId,
|
deliveryId: orderData.deliveryId,
|
||||||
status: orderData.status
|
status: orderData.status,
|
||||||
|
orderAmount: orderData.orderAmount
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await fetch(`http://localhost:8080/api/orders/${editingOrder.id}`, {
|
const response = await fetch(`http://localhost:8080/api/orders/${editingOrder.id}`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
headers: {
|
headers: {
|
||||||
@@ -107,12 +108,12 @@ function OrderList() {
|
|||||||
},
|
},
|
||||||
body: JSON.stringify(orderRq)
|
body: JSON.stringify(orderRq)
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const errorText = await response.text();
|
const errorText = await response.text();
|
||||||
throw new Error(errorText || `HTTP error! status: ${response.status}`);
|
throw new Error(errorText || `HTTP error! status: ${response.status}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
await loadData();
|
await loadData();
|
||||||
setEditingOrder(null);
|
setEditingOrder(null);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -9,8 +9,9 @@ import jakarta.validation.Valid;
|
|||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Map;
|
||||||
|
|
||||||
@CrossOrigin(origins = "http://localhost:5173")
|
@CrossOrigin(origins = "http://localhost:5173")
|
||||||
@RestController
|
@RestController
|
||||||
@@ -32,63 +33,60 @@ public class OrderController {
|
|||||||
|
|
||||||
@GetMapping("/{id}")
|
@GetMapping("/{id}")
|
||||||
@Operation(summary = "Получить заказ по ID")
|
@Operation(summary = "Получить заказ по ID")
|
||||||
public ResponseEntity<OrderRs> getOne(@PathVariable Long id) {
|
public ResponseEntity<OrderRs> getOne(@PathVariable Long id) {
|
||||||
Optional<OrderRs> order = orderService.findById(id);
|
return orderService.findById(id)
|
||||||
return order.map(ResponseEntity::ok)
|
.map(ResponseEntity::ok)
|
||||||
.orElse(ResponseEntity.notFound().build());
|
.orElse(ResponseEntity.notFound().build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/customer/{customerId}")
|
|
||||||
@Operation(summary = "Получить заказы по ID клиента")
|
|
||||||
public List<OrderRs> getByCustomer(@PathVariable Long customerId) {
|
|
||||||
return orderService.findByCustomerId(customerId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/status/{status}")
|
|
||||||
@Operation(summary = "Получить заказы по статусу")
|
|
||||||
public List<OrderRs> getByStatus(@PathVariable String status) {
|
|
||||||
return orderService.findByStatus(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping
|
@PostMapping
|
||||||
@Operation(summary = "Создать новый заказ")
|
@Operation(summary = "Создать новый заказ")
|
||||||
public ResponseEntity<?> create(@Valid @RequestBody OrderRq orderRq) {
|
public OrderRs create(@Valid @RequestBody OrderRq orderRq) {
|
||||||
try {
|
return orderService.create(orderRq);
|
||||||
OrderRs created = orderService.create(orderRq);
|
|
||||||
return ResponseEntity.ok(created);
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
return ResponseEntity.badRequest().body(e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/{id}")
|
@PutMapping("/{id}")
|
||||||
@Operation(summary = "Обновить заказ")
|
@Operation(summary = "Обновить данные заказа")
|
||||||
public ResponseEntity<?> update(@PathVariable Long id, @Valid @RequestBody OrderRq orderRq) {
|
public ResponseEntity<OrderRs> update(@PathVariable Long id, @Valid @RequestBody OrderRq orderRq) {
|
||||||
try {
|
return orderService.update(id, orderRq)
|
||||||
Optional<OrderRs> updated = orderService.update(id, orderRq);
|
.map(ResponseEntity::ok)
|
||||||
return updated.map(ResponseEntity::ok)
|
.orElse(ResponseEntity.notFound().build());
|
||||||
.orElse(ResponseEntity.notFound().build());
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
return ResponseEntity.badRequest().body(e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/{id}")
|
@DeleteMapping("/{id}")
|
||||||
@Operation(summary = "Удалить заказ")
|
@Operation(summary = "Удалить заказ")
|
||||||
public ResponseEntity<Void> delete(@PathVariable Long id) {
|
public ResponseEntity<Void> delete(@PathVariable Long id) {
|
||||||
boolean deleted = orderService.delete(id);
|
boolean deleted = orderService.delete(id);
|
||||||
return deleted ? ResponseEntity.ok().build() : ResponseEntity.notFound().build();
|
return deleted ? ResponseEntity.ok().build() : ResponseEntity.notFound().build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/stats/customer-spending")
|
||||||
|
@Operation(summary = "Получить статистику трат клиентов")
|
||||||
|
public List<OrderService.CustomerSpendingStats> getCustomerSpendingStats() {
|
||||||
|
return orderService.getCustomerSpendingStats();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/stats/total-revenue")
|
||||||
|
@Operation(summary = "Получить общую выручку")
|
||||||
|
public Map<String, BigDecimal> getTotalRevenue() {
|
||||||
|
return Map.of("totalRevenue", orderService.getTotalRevenue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/stats/average-order-value")
|
||||||
|
@Operation(summary = "Получить средний чек")
|
||||||
|
public Map<String, BigDecimal> getAverageOrderValue() {
|
||||||
|
return orderService.getAverageOrderValue();
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping("/stats/monthly")
|
@GetMapping("/stats/monthly")
|
||||||
@Operation(summary = "Получить месячную статистику заказов")
|
@Operation(summary = "Получить статистику заказов по месяцам")
|
||||||
public List<OrderService.OrderMonthlyStats> getOrderMonthlyStats() {
|
public List<OrderService.OrderMonthlyStats> getOrderMonthlyStats() {
|
||||||
return orderService.getOrderMonthlyStats();
|
return orderService.getOrderMonthlyStats();
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/stats/status")
|
@GetMapping("/stats/status")
|
||||||
@Operation(summary = "Получить статистику заказов по статусам")
|
@Operation(summary = "Получить статистику заказов по статусам")
|
||||||
public List<OrderService.OrderStatusStats> getOrderStatusStats() {
|
public List<OrderService.OrderStatusStats> getOrderStatusStats() {
|
||||||
return orderService.getOrderStatusStats();
|
return orderService.getOrderStatusStats();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.example.dto;
|
package com.example.dto;
|
||||||
|
|
||||||
import jakarta.validation.constraints.NotNull;
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
public class OrderRq {
|
public class OrderRq {
|
||||||
@NotNull(message = "ID клиента обязателен")
|
@NotNull(message = "ID клиента обязателен")
|
||||||
@@ -12,16 +13,18 @@ public class OrderRq {
|
|||||||
@NotNull(message = "Статус обязателен")
|
@NotNull(message = "Статус обязателен")
|
||||||
private String status;
|
private String status;
|
||||||
|
|
||||||
|
@NotNull(message = "Стоимость заказа обязательна")
|
||||||
|
private BigDecimal orderAmount;
|
||||||
|
|
||||||
public OrderRq() {}
|
public OrderRq() {}
|
||||||
|
|
||||||
public OrderRq(Long customerId, Long deliveryId, String status) {
|
public OrderRq(Long customerId, Long deliveryId, String status, BigDecimal orderAmount) {
|
||||||
this.customerId = customerId;
|
this.customerId = customerId;
|
||||||
this.deliveryId = deliveryId;
|
this.deliveryId = deliveryId;
|
||||||
this.status = status;
|
this.status = status;
|
||||||
|
this.orderAmount = orderAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Long getCustomerId() { return customerId; }
|
public Long getCustomerId() { return customerId; }
|
||||||
public void setCustomerId(Long customerId) { this.customerId = customerId; }
|
public void setCustomerId(Long customerId) { this.customerId = customerId; }
|
||||||
|
|
||||||
@@ -30,4 +33,7 @@ public class OrderRq {
|
|||||||
|
|
||||||
public String getStatus() { return status; }
|
public String getStatus() { return status; }
|
||||||
public void setStatus(String status) { this.status = status; }
|
public void setStatus(String status) { this.status = status; }
|
||||||
|
|
||||||
|
public BigDecimal getOrderAmount() { return orderAmount; }
|
||||||
|
public void setOrderAmount(BigDecimal orderAmount) { this.orderAmount = orderAmount; }
|
||||||
}
|
}
|
||||||
@@ -1,18 +1,19 @@
|
|||||||
package com.example.dto;
|
package com.example.dto;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
public class OrderRs {
|
public class OrderRs {
|
||||||
private Long id;
|
private Long id;
|
||||||
private CustomerRs customer;
|
private CustomerRs customer;
|
||||||
private DeliveryRs delivery;
|
private DeliveryRs delivery;
|
||||||
private String status;
|
private String status;
|
||||||
|
private BigDecimal orderAmount;
|
||||||
private LocalDateTime createdAt;
|
private LocalDateTime createdAt;
|
||||||
private LocalDateTime updatedAt;
|
private LocalDateTime updatedAt;
|
||||||
|
|
||||||
|
public Long getId() { return id; }
|
||||||
public Long getId() { return id; }
|
public void setId(Long id) { this.id = id; }
|
||||||
public void setId(Long id) { this.id = id; }
|
|
||||||
|
|
||||||
public CustomerRs getCustomer() { return customer; }
|
public CustomerRs getCustomer() { return customer; }
|
||||||
public void setCustomer(CustomerRs customer) { this.customer = customer; }
|
public void setCustomer(CustomerRs customer) { this.customer = customer; }
|
||||||
@@ -23,6 +24,9 @@ public class OrderRs {
|
|||||||
public String getStatus() { return status; }
|
public String getStatus() { return status; }
|
||||||
public void setStatus(String status) { this.status = status; }
|
public void setStatus(String status) { this.status = status; }
|
||||||
|
|
||||||
|
public BigDecimal getOrderAmount() { return orderAmount; }
|
||||||
|
public void setOrderAmount(BigDecimal orderAmount) { this.orderAmount = orderAmount; }
|
||||||
|
|
||||||
public LocalDateTime getCreatedAt() { return createdAt; }
|
public LocalDateTime getCreatedAt() { return createdAt; }
|
||||||
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
|
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,16 @@
|
|||||||
package com.example.entity;
|
package com.example.entity;
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "orders")
|
@Table(name = "orders")
|
||||||
public class Order extends BaseEntity {
|
public class Order extends BaseEntity {
|
||||||
|
|
||||||
|
|
||||||
@ManyToOne(fetch = FetchType.LAZY)
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
@JoinColumn(name = "customer_id", nullable = false)
|
@JoinColumn(name = "customer_id", nullable = false)
|
||||||
private Customer customer;
|
private Customer customer;
|
||||||
|
|
||||||
|
|
||||||
@ManyToOne(fetch = FetchType.LAZY)
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
@JoinColumn(name = "delivery_id", nullable = false)
|
@JoinColumn(name = "delivery_id", nullable = false)
|
||||||
private Delivery delivery;
|
private Delivery delivery;
|
||||||
@@ -19,70 +18,37 @@ public class Order extends BaseEntity {
|
|||||||
@Column(name = "status", nullable = false)
|
@Column(name = "status", nullable = false)
|
||||||
private String status;
|
private String status;
|
||||||
|
|
||||||
|
@Column(name = "order_amount", nullable = false)
|
||||||
|
private BigDecimal orderAmount;
|
||||||
|
|
||||||
public Order() {}
|
public Order() {}
|
||||||
|
|
||||||
public Order(Customer customer, Delivery delivery, String status) {
|
public Order(Customer customer, Delivery delivery, String status, BigDecimal orderAmount) {
|
||||||
this.customer = customer;
|
this.customer = customer;
|
||||||
this.delivery = delivery;
|
this.delivery = delivery;
|
||||||
this.status = status;
|
this.status = status;
|
||||||
|
this.orderAmount = orderAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Customer getCustomer() { return customer; }
|
||||||
public Customer getCustomer() {
|
|
||||||
return customer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCustomer(Customer customer) {
|
public void setCustomer(Customer customer) {
|
||||||
this.customer = customer;
|
this.customer = customer;
|
||||||
|
|
||||||
if (customer != null && !customer.getOrders().contains(this)) {
|
if (customer != null && !customer.getOrders().contains(this)) {
|
||||||
customer.getOrders().add(this);
|
customer.getOrders().add(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Delivery getDelivery() {
|
public Delivery getDelivery() { return delivery; }
|
||||||
return delivery;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDelivery(Delivery delivery) {
|
public void setDelivery(Delivery delivery) {
|
||||||
this.delivery = delivery;
|
this.delivery = delivery;
|
||||||
|
|
||||||
if (delivery != null && !delivery.getOrders().contains(this)) {
|
if (delivery != null && !delivery.getOrders().contains(this)) {
|
||||||
delivery.getOrders().add(this);
|
delivery.getOrders().add(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getStatus() {
|
public String getStatus() { return status; }
|
||||||
return status;
|
public void setStatus(String status) { this.status = status; }
|
||||||
}
|
|
||||||
|
|
||||||
public void setStatus(String status) {
|
public BigDecimal getOrderAmount() { return orderAmount; }
|
||||||
this.status = status;
|
public void setOrderAmount(BigDecimal orderAmount) { this.orderAmount = orderAmount; }
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "Order{" +
|
|
||||||
"id=" + getId() +
|
|
||||||
", customer=" + (customer != null ? customer.getName() : "null") +
|
|
||||||
", delivery=" + (delivery != null ? delivery.getTrackingNumber() : "null") +
|
|
||||||
", status='" + status + '\'' +
|
|
||||||
", createdAt=" + getCreatedAt() +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (this == o) return true;
|
|
||||||
if (!(o instanceof Order)) return false;
|
|
||||||
Order order = (Order) o;
|
|
||||||
return getId() != null && getId().equals(order.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return getClass().hashCode();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -13,6 +13,7 @@ public class OrderMapper {
|
|||||||
public Order toEntity(OrderRq orderRq) {
|
public Order toEntity(OrderRq orderRq) {
|
||||||
Order order = new Order();
|
Order order = new Order();
|
||||||
order.setStatus(orderRq.getStatus());
|
order.setStatus(orderRq.getStatus());
|
||||||
|
order.setOrderAmount(orderRq.getOrderAmount());
|
||||||
return order;
|
return order;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -20,10 +21,10 @@ public class OrderMapper {
|
|||||||
OrderRs orderRs = new OrderRs();
|
OrderRs orderRs = new OrderRs();
|
||||||
orderRs.setId(order.getId());
|
orderRs.setId(order.getId());
|
||||||
orderRs.setStatus(order.getStatus());
|
orderRs.setStatus(order.getStatus());
|
||||||
|
orderRs.setOrderAmount(order.getOrderAmount());
|
||||||
orderRs.setCreatedAt(order.getCreatedAt());
|
orderRs.setCreatedAt(order.getCreatedAt());
|
||||||
orderRs.setUpdatedAt(order.getUpdatedAt());
|
orderRs.setUpdatedAt(order.getUpdatedAt());
|
||||||
|
|
||||||
|
|
||||||
if (order.getCustomer() != null) {
|
if (order.getCustomer() != null) {
|
||||||
CustomerRs customerRs = new CustomerRs();
|
CustomerRs customerRs = new CustomerRs();
|
||||||
customerRs.setId(order.getCustomer().getId());
|
customerRs.setId(order.getCustomer().getId());
|
||||||
|
|||||||
@@ -3,47 +3,36 @@ package com.example.repository;
|
|||||||
import com.example.entity.Order;
|
import com.example.entity.Order;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
import org.springframework.data.jpa.repository.Query;
|
import org.springframework.data.jpa.repository.Query;
|
||||||
import org.springframework.data.repository.query.Param;
|
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
public interface OrderRepository extends JpaRepository<Order, Long> {
|
public interface OrderRepository extends JpaRepository<Order, Long> {
|
||||||
|
|
||||||
List<Order> findByCustomerId(Long customerId);
|
List<Order> findByCustomerId(Long customerId);
|
||||||
|
|
||||||
List<Order> findByDeliveryId(Long deliveryId);
|
List<Order> findByDeliveryId(Long deliveryId);
|
||||||
|
|
||||||
List<Order> findByStatus(String status);
|
List<Order> findByStatus(String status);
|
||||||
|
|
||||||
|
|
||||||
@Query("SELECT MONTH(o.createdAt) as month, " +
|
@Query("SELECT o.status, COUNT(o) FROM Order o GROUP BY o.status")
|
||||||
"COUNT(o.id) as orderCount " +
|
List<Object[]> findOrderStatusStats();
|
||||||
"FROM Order o " +
|
|
||||||
"GROUP BY MONTH(o.createdAt) " +
|
|
||||||
"ORDER BY month")
|
|
||||||
List<OrderMonthlyStats> findOrderMonthlyStats();
|
|
||||||
|
|
||||||
interface OrderMonthlyStats {
|
|
||||||
Integer getMonth();
|
|
||||||
Long getOrderCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Query("SELECT o.status as status, " +
|
@Query("SELECT MONTH(o.createdAt), COUNT(o) FROM Order o GROUP BY MONTH(o.createdAt)")
|
||||||
"COUNT(o.id) as total " +
|
List<Object[]> findOrderMonthlyStats();
|
||||||
"FROM Order o " +
|
|
||||||
"GROUP BY o.status")
|
|
||||||
List<OrderStatusStats> findOrderStatusStats();
|
|
||||||
|
|
||||||
interface OrderStatusStats {
|
|
||||||
String getStatus();
|
@Query("SELECT o.customer.id, o.customer.name, SUM(o.orderAmount), COUNT(o) " +
|
||||||
Long getTotal();
|
"FROM Order o GROUP BY o.customer.id, o.customer.name ORDER BY SUM(o.orderAmount) DESC")
|
||||||
}
|
List<Object[]> findCustomerSpendingStats();
|
||||||
|
|
||||||
|
|
||||||
|
@Query("SELECT SUM(o.orderAmount) FROM Order o")
|
||||||
|
BigDecimal getTotalRevenue();
|
||||||
|
|
||||||
|
|
||||||
@Query("SELECT o FROM Order o WHERE o.customer.id = :customerId AND o.status = :status")
|
@Query("SELECT o FROM Order o WHERE o.customer.id = :customerId AND o.status = :status")
|
||||||
List<Order> findCustomerOrdersByStatus(@Param("customerId") Long customerId,
|
List<Order> findCustomerOrdersByStatus(Long customerId, String status);
|
||||||
@Param("status") String status);
|
|
||||||
}
|
}
|
||||||
@@ -12,7 +12,10 @@ import com.example.repository.OrderRepository;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.math.RoundingMode;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@@ -95,6 +98,7 @@ public class OrderService {
|
|||||||
order.setCustomer(customer);
|
order.setCustomer(customer);
|
||||||
order.setDelivery(delivery);
|
order.setDelivery(delivery);
|
||||||
order.setStatus(orderRq.getStatus());
|
order.setStatus(orderRq.getStatus());
|
||||||
|
order.setOrderAmount(orderRq.getOrderAmount());
|
||||||
|
|
||||||
Order updated = orderRepository.save(order);
|
Order updated = orderRepository.save(order);
|
||||||
return orderMapper.toResponse(updated);
|
return orderMapper.toResponse(updated);
|
||||||
@@ -109,31 +113,116 @@ public class OrderService {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public List<OrderMonthlyStats> getOrderMonthlyStats() {
|
public List<CustomerSpendingStats> getCustomerSpendingStats() {
|
||||||
List<OrderRepository.OrderMonthlyStats> repoStats = orderRepository.findOrderMonthlyStats();
|
List<Order> allOrders = orderRepository.findAll();
|
||||||
|
|
||||||
return repoStats.stream()
|
|
||||||
.map(stat -> new OrderMonthlyStats(stat.getMonth(), stat.getOrderCount()))
|
Map<Customer, List<Order>> ordersByCustomer = allOrders.stream()
|
||||||
|
.collect(java.util.stream.Collectors.groupingBy(Order::getCustomer));
|
||||||
|
|
||||||
|
return ordersByCustomer.entrySet().stream()
|
||||||
|
.map(entry -> {
|
||||||
|
Customer customer = entry.getKey();
|
||||||
|
List<Order> customerOrders = entry.getValue();
|
||||||
|
|
||||||
|
BigDecimal totalSpent = customerOrders.stream()
|
||||||
|
.map(Order::getOrderAmount)
|
||||||
|
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||||
|
|
||||||
|
return new CustomerSpendingStats(
|
||||||
|
customer.getId(),
|
||||||
|
customer.getName(),
|
||||||
|
totalSpent,
|
||||||
|
(long) customerOrders.size()
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.sorted((a, b) -> b.totalSpent().compareTo(a.totalSpent()))
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public record OrderMonthlyStats(Integer month, Long orderCount) {}
|
public record CustomerSpendingStats(Long customerId, String customerName, BigDecimal totalSpent, Long orderCount) {}
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public List<OrderStatusStats> getOrderStatusStats() {
|
public List<OrderStatusStats> getOrderStatusStats() {
|
||||||
List<OrderRepository.OrderStatusStats> repoStats = orderRepository.findOrderStatusStats();
|
List<Order> allOrders = orderRepository.findAll();
|
||||||
|
|
||||||
return repoStats.stream()
|
|
||||||
.map(stat -> new OrderStatusStats(stat.getStatus(), stat.getTotal()))
|
Map<String, List<Order>> ordersByStatus = allOrders.stream()
|
||||||
|
.collect(java.util.stream.Collectors.groupingBy(Order::getStatus));
|
||||||
|
|
||||||
|
return ordersByStatus.entrySet().stream()
|
||||||
|
.map(entry -> new OrderStatusStats(
|
||||||
|
entry.getKey(),
|
||||||
|
(long) entry.getValue().size()
|
||||||
|
))
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public record OrderStatusStats(String status, Long total) {}
|
public record OrderStatusStats(String status, Long total) {}
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public List<Order> getCustomerOrdersByStatus(Long customerId, String status) {
|
public List<OrderMonthlyStats> getOrderMonthlyStats() {
|
||||||
return orderRepository.findCustomerOrdersByStatus(customerId, status);
|
List<Order> allOrders = orderRepository.findAll();
|
||||||
|
|
||||||
|
|
||||||
|
Map<Integer, List<Order>> ordersByMonth = allOrders.stream()
|
||||||
|
.collect(java.util.stream.Collectors.groupingBy(order ->
|
||||||
|
order.getCreatedAt().getMonthValue()
|
||||||
|
));
|
||||||
|
|
||||||
|
return ordersByMonth.entrySet().stream()
|
||||||
|
.map(entry -> new OrderMonthlyStats(
|
||||||
|
entry.getKey(),
|
||||||
|
(long) entry.getValue().size()
|
||||||
|
))
|
||||||
|
.sorted((a, b) -> a.month().compareTo(b.month()))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public record OrderMonthlyStats(Integer month, Long orderCount) {}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public BigDecimal getTotalRevenue() {
|
||||||
|
List<Order> allOrders = orderRepository.findAll();
|
||||||
|
return allOrders.stream()
|
||||||
|
.map(Order::getOrderAmount)
|
||||||
|
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public Map<String, BigDecimal> getAverageOrderValue() {
|
||||||
|
List<Order> allOrders = orderRepository.findAll();
|
||||||
|
|
||||||
|
if (allOrders.isEmpty()) {
|
||||||
|
return Map.of("averageOrderValue", BigDecimal.ZERO);
|
||||||
|
}
|
||||||
|
|
||||||
|
BigDecimal totalRevenue = getTotalRevenue();
|
||||||
|
BigDecimal average = totalRevenue.divide(BigDecimal.valueOf(allOrders.size()), 2, RoundingMode.HALF_UP);
|
||||||
|
|
||||||
|
return Map.of("averageOrderValue", average);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public Map<String, Object> getBusinessSummary() {
|
||||||
|
long totalCustomers = customerRepository.count();
|
||||||
|
long totalDeliveries = deliveryRepository.count();
|
||||||
|
long totalOrders = orderRepository.count();
|
||||||
|
BigDecimal totalRevenue = getTotalRevenue();
|
||||||
|
|
||||||
|
List<Order> activeOrders = orderRepository.findByStatus("В пути");
|
||||||
|
long activeOrdersCount = activeOrders.size();
|
||||||
|
|
||||||
|
return Map.of(
|
||||||
|
"totalCustomers", totalCustomers,
|
||||||
|
"totalDeliveries", totalDeliveries,
|
||||||
|
"totalOrders", totalOrders,
|
||||||
|
"totalRevenue", totalRevenue,
|
||||||
|
"activeOrders", activeOrdersCount
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
-- Тестовые клиенты
|
|
||||||
INSERT INTO customers (id, name, email, created_at, updated_at)
|
INSERT INTO customers (id, name, email, created_at, updated_at)
|
||||||
VALUES
|
VALUES
|
||||||
(1, 'Иван Иванов', 'ivan@mail.ru', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
|
(1, 'Иван Иванов', 'ivan@mail.ru', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
|
||||||
@@ -7,7 +7,6 @@ VALUES
|
|||||||
(4, 'Алексей Козлов', 'alex@mail.ru', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
|
(4, 'Алексей Козлов', 'alex@mail.ru', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
|
||||||
(5, 'Ольга Новикова', 'olga@mail.ru', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);
|
(5, 'Ольга Новикова', 'olga@mail.ru', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);
|
||||||
|
|
||||||
-- Тестовые доставки (опционально)
|
|
||||||
INSERT INTO deliveries (id, tracking_number, destination, status, customer_name, created_at, updated_at)
|
INSERT INTO deliveries (id, tracking_number, destination, status, customer_name, created_at, updated_at)
|
||||||
VALUES
|
VALUES
|
||||||
(1, 'IVN777777', 'гоголя 34', 'В пути', 'Иван Иванов', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
|
(1, 'IVN777777', 'гоголя 34', 'В пути', 'Иван Иванов', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
|
||||||
|
|||||||
Reference in New Issue
Block a user