feat: 添加用户订单和设备汇总接口设计文档,支持获取当前登录用户的订单和设备统计信息
This commit is contained in:
1 parent
194accefd8
commit
6bfde74e21
15 files changed
+575
-3
No files matched your search
@@ -0,0 +1,179 @@
|
||||
# 用户端设备汇总接口设计文档
|
||||
|
||||
## 需求概述
|
||||
|
||||
提供用户端设备汇总统计接口,返回当前登录客户的设备总数、在线设备数、离线设备数。
|
||||
|
||||
## 接口路径
|
||||
|
||||
```
|
||||
GET /api/cu/devices/summary
|
||||
```
|
||||
|
||||
## 认证要求
|
||||
|
||||
- 需要 Bearer Token 认证(C端用户JWT)
|
||||
- 基于当前登录用户的客户ID进行统计
|
||||
|
||||
## 请求参数
|
||||
|
||||
无查询参数
|
||||
|
||||
## 响应数据结构
|
||||
|
||||
### 成功响应
|
||||
|
||||
```json
|
||||
{
|
||||
"code": "000000",
|
||||
"message": "成功",
|
||||
"data": {
|
||||
"total_devices": 100,
|
||||
"online_devices": 85,
|
||||
"offline_devices": 15
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 响应字段说明
|
||||
|
||||
| 字段 | 类型 | 必填 | 描述 |
|
||||
|------|------|------|------|
|
||||
| total_devices | int64 | 是 | 设备总数(活跃许可证数) |
|
||||
| online_devices | int64 | 是 | 在线设备数 |
|
||||
| offline_devices | int64 | 是 | 离线设备数 |
|
||||
|
||||
### 在线状态判断逻辑
|
||||
|
||||
参考现有代码实现:
|
||||
- 通过 `last_heartbeat` 时间戳判断设备是否在线
|
||||
- 心跳超时时间从配置获取,默认5分钟(300秒)
|
||||
- 如果 `last_heartbeat + heartbeat_timeout > 当前时间`,则设备在线
|
||||
|
||||
### 业务规则
|
||||
|
||||
- 统计范围:当前登录用户的客户ID下的所有活跃设备(`status = 'active'` 且 `deleted_at IS NULL`)
|
||||
- 权限控制:只能查看自己客户ID下的设备数据
|
||||
- 数据一致性:`total_devices = online_devices + offline_devices`
|
||||
|
||||
## 错误响应
|
||||
|
||||
| 错误码 | HTTP状态码 | 描述 |
|
||||
|--------|------------|------|
|
||||
| 500011 | 401 | 认证令牌无效 |
|
||||
| 500012 | 401 | 认证令牌已过期 |
|
||||
| 500010 | 401 | 认证令牌缺失 |
|
||||
| 900004 | 500 | 服务器内部错误 |
|
||||
|
||||
## Swagger 文档
|
||||
|
||||
```go
|
||||
// GetDeviceSummary 获取设备汇总统计
|
||||
// @Summary 获取设备汇总统计
|
||||
// @Description 获取当前登录用户的设备汇总统计信息(总数、在线数、离线数)
|
||||
// @Tags 客户设备管理
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Success 200 {object} models.APIResponse{data=models.DeviceSummaryResponse} "获取成功"
|
||||
// @Failure 401 {object} models.ErrorResponse "未认证"
|
||||
// @Failure 500 {object} models.ErrorResponse "服务器内部错误"
|
||||
// @Router /api/cu/devices/summary [get]
|
||||
```
|
||||
|
||||
## 技术实现方案
|
||||
|
||||
### 1. 新增响应模型(models/license.go)
|
||||
|
||||
```go
|
||||
// DeviceSummaryResponse 设备汇总响应结构
|
||||
type DeviceSummaryResponse struct {
|
||||
TotalDevices int64 `json:"total_devices"` // 设备总数
|
||||
OnlineDevices int64 `json:"online_devices"` // 在线设备数
|
||||
OfflineDevices int64 `json:"offline_devices"` // 离线设备数
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 服务层接口扩展(service/interfaces.go)
|
||||
|
||||
在 `CuDeviceService` 接口中添加:
|
||||
|
||||
```go
|
||||
// 获取设备汇总统计
|
||||
GetDeviceSummary(ctx context.Context, customerID string) (*models.DeviceSummaryResponse, error)
|
||||
```
|
||||
|
||||
### 3. Repository层接口扩展(repository/interfaces.go)
|
||||
|
||||
在 `LicenseRepository` 接口中添加:
|
||||
|
||||
```go
|
||||
// GetCustomerDeviceSummary 获取客户设备汇总统计
|
||||
GetCustomerDeviceSummary(ctx context.Context, customerID string) (*models.DeviceSummaryResponse, error)
|
||||
```
|
||||
|
||||
### 4. 处理器扩展(handlers/cu_device_handler.go)
|
||||
|
||||
添加新的处理器方法:
|
||||
|
||||
```go
|
||||
func (h *CuDeviceHandler) GetDeviceSummary(c *gin.Context) {
|
||||
// 从JWT Token中获取用户ID和客户ID
|
||||
claims := c.MustGet("cu_user").(*utils.CuClaims)
|
||||
|
||||
// 调用服务层
|
||||
result, err := h.cuDeviceService.GetDeviceSummary(c.Request.Context(), claims.CustomerID)
|
||||
if err != nil {
|
||||
// err已经是i18n.I18nError,直接使用
|
||||
i18nErr, ok := err.(*i18n.I18nError)
|
||||
if ok {
|
||||
c.JSON(i18nErr.HttpCode, models.ErrorResponse{
|
||||
Code: i18nErr.Code,
|
||||
Message: i18nErr.Message,
|
||||
Timestamp: getCurrentTimestamp(),
|
||||
})
|
||||
return
|
||||
}
|
||||
// 如果不是i18n错误,使用通用错误
|
||||
lang := middleware.GetLanguage(c)
|
||||
status, errCode, message := i18n.NewI18nErrorResponse("900004", lang, err.Error())
|
||||
c.JSON(status, models.ErrorResponse{
|
||||
Code: errCode,
|
||||
Message: message + ": " + err.Error(),
|
||||
Timestamp: getCurrentTimestamp(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
lang := middleware.GetLanguage(c)
|
||||
successMessage := i18n.GetI18nErrorMessage("000000", lang)
|
||||
c.JSON(http.StatusOK, models.APIResponse{
|
||||
Code: "000000",
|
||||
Message: successMessage,
|
||||
Data: result,
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### 5. 路由配置(routes/router.go)
|
||||
|
||||
在 cuAuth 路由组中添加:
|
||||
|
||||
```go
|
||||
cuAuth.GET("/devices/summary", cuDeviceHandler.GetDeviceSummary)
|
||||
```
|
||||
|
||||
## 数据库查询逻辑
|
||||
|
||||
基于现有 `licenses` 表结构,统计逻辑:
|
||||
|
||||
1. **设备总数**:查询 `customer_id = ? AND status = 'active' AND deleted_at IS NULL` 的记录数
|
||||
2. **在线设备数**:在总数基础上,筛选 `last_heartbeat > (NOW() - INTERVAL heartbeat_timeout SECOND)` 的记录数
|
||||
3. **离线设备数**:总数减去在线设备数
|
||||
|
||||
## 性能优化建议
|
||||
|
||||
- 建议对该接口使用缓存,避免频繁的数据库查询
|
||||
- 可以使用 Redis 缓存,缓存时间建议为 1-5 分钟
|
||||
- 缓存键设计:`device_summary:{customer_id}`
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
# 用户订单汇总接口设计文档
|
||||
|
||||
## 需求概述
|
||||
|
||||
提供用户订单汇总统计接口,返回当前登录客户的订单总数、待支付订单数、已支付订单数。
|
||||
|
||||
## 接口路径
|
||||
|
||||
```
|
||||
GET /api/cu/orders/summary
|
||||
```
|
||||
|
||||
## 认证要求
|
||||
|
||||
- 需要 Bearer Token 认证(C端用户JWT)
|
||||
- 基于当前登录用户的客户ID进行统计
|
||||
|
||||
## 请求参数
|
||||
|
||||
无查询参数
|
||||
|
||||
## 响应数据结构
|
||||
|
||||
### 成功响应
|
||||
|
||||
```json
|
||||
{
|
||||
"code": "000000",
|
||||
"message": "成功",
|
||||
"data": {
|
||||
"total_orders": 100,
|
||||
"pending_orders": 15,
|
||||
"paid_orders": 85
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 响应字段说明
|
||||
|
||||
| 字段 | 类型 | 必填 | 描述 |
|
||||
|------|------|------|------|
|
||||
| total_orders | int64 | 是 | 订单总数 |
|
||||
| pending_orders | int64 | 是 | 待支付订单数 |
|
||||
| paid_orders | int64 | 是 | 已支付订单数 |
|
||||
|
||||
## 业务规则
|
||||
|
||||
- 统计范围:当前登录用户的客户ID下的所有订单(包含已删除订单)
|
||||
- 订单状态:
|
||||
- `pending`: 待支付订单
|
||||
- `paid`: 已支付订单
|
||||
- 数据一致性:`total_orders = pending_orders + paid_orders`
|
||||
|
||||
## 错误响应
|
||||
|
||||
| 错误码 | HTTP状态码 | 描述 |
|
||||
|--------|------------|------|
|
||||
| 500011 | 401 | 认证令牌无效 |
|
||||
| 500012 | 401 | 认证令牌已过期 |
|
||||
| 500010 | 401 | 认证令牌缺失 |
|
||||
| 900004 | 500 | 服务器内部错误 |
|
||||
|
||||
## Swagger 文档
|
||||
|
||||
```go
|
||||
// GetOrderSummary 获取订单汇总统计
|
||||
// @Summary 获取订单汇总统计
|
||||
// @Description 获取当前登录用户的订单汇总统计信息(总数、待支付数、已支付数)
|
||||
// @Tags 客户订单管理
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Success 200 {object} models.APIResponse{data=models.OrderSummaryResponse} "获取成功"
|
||||
// @Failure 401 {object} models.ErrorResponse "未认证"
|
||||
// @Failure 500 {object} models.ErrorResponse "服务器内部错误"
|
||||
// @Router /api/cu/orders/summary [get]
|
||||
```
|
||||
|
||||
## 技术实现方案
|
||||
|
||||
### 1. 新增响应模型(models/cu_order.go)
|
||||
|
||||
```go
|
||||
// OrderSummaryResponse 订单汇总响应结构
|
||||
type OrderSummaryResponse struct {
|
||||
TotalOrders int64 `json:"total_orders"` // 订单总数
|
||||
PendingOrders int64 `json:"pending_orders"` // 待支付订单数
|
||||
PaidOrders int64 `json:"paid_orders"` // 已支付订单数
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 服务层接口扩展(service/interfaces.go)
|
||||
|
||||
在 `CuOrderService` 接口中添加:
|
||||
|
||||
```go
|
||||
// 获取订单汇总统计
|
||||
GetOrderSummary(ctx context.Context, customerID string) (*models.OrderSummaryResponse, error)
|
||||
```
|
||||
|
||||
### 3. Repository层接口扩展(repository/interfaces.go)
|
||||
|
||||
在 `CuOrderRepository` 接口中添加:
|
||||
|
||||
```go
|
||||
// GetCustomerOrderSummary 获取客户订单汇总统计
|
||||
GetCustomerOrderSummary(ctx context.Context, customerID string) (*models.OrderSummaryResponse, error)
|
||||
```
|
||||
|
||||
### 4. 处理器扩展(handlers/cu_order_handler.go)
|
||||
|
||||
添加新的处理器方法:
|
||||
|
||||
```go
|
||||
func (h *CuOrderHandler) GetOrderSummary(c *gin.Context) {
|
||||
// 从JWT Token中获取用户ID和客户ID
|
||||
claims := c.MustGet("cu_user").(*utils.CuClaims)
|
||||
|
||||
// 调用服务层
|
||||
result, err := h.cuOrderService.GetOrderSummary(c.Request.Context(), claims.CustomerID)
|
||||
if err != nil {
|
||||
// err已经是i18n.I18nError,直接使用
|
||||
i18nErr, ok := err.(*i18n.I18nError)
|
||||
if ok {
|
||||
c.JSON(i18nErr.HttpCode, models.ErrorResponse{
|
||||
Code: i18nErr.Code,
|
||||
Message: i18nErr.Message,
|
||||
Timestamp: getCurrentTimestamp(),
|
||||
})
|
||||
return
|
||||
}
|
||||
// 如果不是i18n错误,使用通用错误
|
||||
lang := middleware.GetLanguage(c)
|
||||
status, errCode, message := i18n.NewI18nErrorResponse("900004", lang, err.Error())
|
||||
c.JSON(status, models.ErrorResponse{
|
||||
Code: errCode,
|
||||
Message: message + ": " + err.Error(),
|
||||
Timestamp: getCurrentTimestamp(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
lang := middleware.GetLanguage(c)
|
||||
successMessage := i18n.GetI18nErrorMessage("000000", lang)
|
||||
c.JSON(http.StatusOK, models.APIResponse{
|
||||
Code: "000000",
|
||||
Message: successMessage,
|
||||
Data: result,
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### 5. 路由配置(routes/router.go)
|
||||
|
||||
在 cuAuth 路由组中添加:
|
||||
|
||||
```go
|
||||
cuAuth.GET("/orders/summary", cuOrderHandler.GetOrderSummary)
|
||||
```
|
||||
|
||||
## 数据库查询逻辑
|
||||
|
||||
基于现有 `cu_orders` 表结构,统计逻辑:
|
||||
|
||||
1. **订单总数**:查询 `customer_id = ?` 的记录数(包含所有状态)
|
||||
2. **待支付订单数**:查询 `customer_id = ? AND status = 'pending'` 的记录数
|
||||
3. **已支付订单数**:查询 `customer_id = ? AND status = 'paid'` 的记录数
|
||||
@@ -0,0 +1,5 @@
|
||||
# 提示词
|
||||
|
||||
## 接口设计提示词
|
||||
|
||||
请参考至少三个当前已经实现的接口,熟悉接口设计规范,然后设计新的接口。
|
||||
@@ -152,3 +152,51 @@ func (h *CuDeviceHandler) UnbindDevice(c *gin.Context) {
|
||||
Data: nil,
|
||||
})
|
||||
}
|
||||
|
||||
// GetDeviceSummary 获取设备汇总统计
|
||||
// @Summary 获取设备汇总统计
|
||||
// @Description 获取当前登录用户的设备汇总统计信息(总数、在线数、离线数)
|
||||
// @Tags 客户设备管理
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Success 200 {object} models.APIResponse{data=models.DeviceSummaryResponse} "获取成功"
|
||||
// @Failure 401 {object} models.ErrorResponse "未认证"
|
||||
// @Failure 500 {object} models.ErrorResponse "服务器内部错误"
|
||||
// @Router /api/cu/devices/summary [get]
|
||||
func (h *CuDeviceHandler) GetDeviceSummary(c *gin.Context) {
|
||||
// 从JWT Token中获取用户ID和客户ID
|
||||
claims := c.MustGet("cu_user").(*utils.CuClaims)
|
||||
|
||||
// 调用服务层
|
||||
result, err := h.cuDeviceService.GetDeviceSummary(c.Request.Context(), claims.CustomerID)
|
||||
if err != nil {
|
||||
// err已经是i18n.I18nError,直接使用
|
||||
i18nErr, ok := err.(*i18n.I18nError)
|
||||
if ok {
|
||||
c.JSON(i18nErr.HttpCode, models.ErrorResponse{
|
||||
Code: i18nErr.Code,
|
||||
Message: i18nErr.Message,
|
||||
Timestamp: getCurrentTimestamp(),
|
||||
})
|
||||
return
|
||||
}
|
||||
// 如果不是i18n错误,使用通用错误
|
||||
lang := middleware.GetLanguage(c)
|
||||
status, errCode, message := i18n.NewI18nErrorResponse("900004", lang, err.Error())
|
||||
c.JSON(status, models.ErrorResponse{
|
||||
Code: errCode,
|
||||
Message: message + ": " + err.Error(),
|
||||
Timestamp: getCurrentTimestamp(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
lang := middleware.GetLanguage(c)
|
||||
successMessage := i18n.GetI18nErrorMessage("000000", lang)
|
||||
c.JSON(http.StatusOK, models.APIResponse{
|
||||
Code: "000000",
|
||||
Message: successMessage,
|
||||
Data: result,
|
||||
})
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"license-manager/internal/models"
|
||||
"license-manager/internal/service"
|
||||
"license-manager/pkg/i18n"
|
||||
"license-manager/pkg/utils"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
@@ -430,3 +431,51 @@ func (h *CuOrderHandler) GetUserOrders(c *gin.Context) {
|
||||
Timestamp: getCurrentTimestamp(),
|
||||
})
|
||||
}
|
||||
|
||||
// GetOrderSummary 获取订单汇总统计
|
||||
// @Summary 获取订单汇总统计
|
||||
// @Description 获取当前登录用户的订单汇总统计信息(总数、待支付数、已支付数)
|
||||
// @Tags 客户订单管理
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Success 200 {object} models.APIResponse{data=models.OrderSummaryResponse} "获取成功"
|
||||
// @Failure 401 {object} models.ErrorResponse "未认证"
|
||||
// @Failure 500 {object} models.ErrorResponse "服务器内部错误"
|
||||
// @Router /api/cu/orders/summary [get]
|
||||
func (h *CuOrderHandler) GetOrderSummary(c *gin.Context) {
|
||||
// 从JWT Token中获取用户ID和客户ID
|
||||
claims := c.MustGet("cu_user").(*utils.CuClaims)
|
||||
|
||||
// 调用服务层
|
||||
result, err := h.cuOrderService.GetOrderSummary(c.Request.Context(), claims.CustomerID)
|
||||
if err != nil {
|
||||
// err已经是i18n.I18nError,直接使用
|
||||
i18nErr, ok := err.(*i18n.I18nError)
|
||||
if ok {
|
||||
c.JSON(i18nErr.HttpCode, models.ErrorResponse{
|
||||
Code: i18nErr.Code,
|
||||
Message: i18nErr.Message,
|
||||
Timestamp: getCurrentTimestamp(),
|
||||
})
|
||||
return
|
||||
}
|
||||
// 如果不是i18n错误,使用通用错误
|
||||
lang := middleware.GetLanguage(c)
|
||||
status, errCode, message := i18n.NewI18nErrorResponse("900004", lang, err.Error())
|
||||
c.JSON(status, models.ErrorResponse{
|
||||
Code: errCode,
|
||||
Message: message + ": " + err.Error(),
|
||||
Timestamp: getCurrentTimestamp(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
lang := middleware.GetLanguage(c)
|
||||
successMessage := i18n.GetI18nErrorMessage("000000", lang)
|
||||
c.JSON(http.StatusOK, models.APIResponse{
|
||||
Code: "000000",
|
||||
Message: successMessage,
|
||||
Data: result,
|
||||
})
|
||||
}
|
||||
@@ -217,6 +217,7 @@ func SetupRouter() *gin.Engine {
|
||||
cuAuth.POST("/orders", cuOrderHandler.CreateOrder)
|
||||
cuAuth.GET("/orders/:order_id", cuOrderHandler.GetOrder)
|
||||
cuAuth.GET("/orders", cuOrderHandler.GetUserOrders)
|
||||
cuAuth.GET("/orders/summary", cuOrderHandler.GetOrderSummary)
|
||||
|
||||
// 支付管理
|
||||
cuAuth.GET("/payment/:payment_no/status", paymentHandler.GetPaymentStatus)
|
||||
@@ -227,6 +228,7 @@ func SetupRouter() *gin.Engine {
|
||||
|
||||
// 设备管理
|
||||
cuAuth.GET("/devices", cuDeviceHandler.GetDevices)
|
||||
cuAuth.GET("/devices/summary", cuDeviceHandler.GetDeviceSummary)
|
||||
cuAuth.DELETE("/devices/:id", cuDeviceHandler.UnbindDevice)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,3 +111,10 @@ type CuOrderListResponse struct {
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"page_size"`
|
||||
}
|
||||
|
||||
// OrderSummaryResponse 订单汇总响应结构
|
||||
type OrderSummaryResponse struct {
|
||||
TotalOrders int64 `json:"total_orders"` // 订单总数
|
||||
PendingOrders int64 `json:"pending_orders"` // 待支付订单数
|
||||
PaidOrders int64 `json:"paid_orders"` // 已支付订单数
|
||||
}
|
||||
@@ -218,3 +218,10 @@ type DeviceListResponse struct {
|
||||
Page int `json:"page"` // 当前页码
|
||||
PageSize int `json:"page_size"` // 每页数量
|
||||
}
|
||||
|
||||
// DeviceSummaryResponse 设备汇总响应结构
|
||||
type DeviceSummaryResponse struct {
|
||||
TotalDevices int64 `json:"total_devices"` // 设备总数
|
||||
OnlineDevices int64 `json:"online_devices"` // 在线设备数
|
||||
OfflineDevices int64 `json:"offline_devices"` // 离线设备数
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"license-manager/internal/models"
|
||||
|
||||
"gorm.io/gorm"
|
||||
@@ -12,6 +13,7 @@ type CuOrderRepository interface {
|
||||
GetByOrderNo(orderNo string) (*models.CuOrder, error)
|
||||
GetByCuUserID(cuUserID string, offset, limit int) ([]*models.CuOrder, int64, error)
|
||||
GetByCustomerID(customerID string, offset, limit int) ([]*models.CuOrder, int64, error)
|
||||
GetCustomerOrderSummary(ctx context.Context, customerID string) (*models.OrderSummaryResponse, error)
|
||||
Update(order *models.CuOrder) error
|
||||
Delete(id string) error
|
||||
CheckTrialOrderExists(cuUserID string, currentMonth string) (bool, error)
|
||||
@@ -111,3 +113,39 @@ func (r *cuOrderRepository) CheckTrialOrderExists(cuUserID string, currentMonth
|
||||
|
||||
return count > 0, err
|
||||
}
|
||||
|
||||
// GetCustomerOrderSummary 获取客户订单汇总统计
|
||||
func (r *cuOrderRepository) GetCustomerOrderSummary(ctx context.Context, customerID string) (*models.OrderSummaryResponse, error) {
|
||||
// 查询订单总数
|
||||
var totalOrders int64
|
||||
err := r.db.Model(&models.CuOrder{}).
|
||||
Where("customer_id = ?", customerID).
|
||||
Count(&totalOrders).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 查询待支付订单数
|
||||
var pendingOrders int64
|
||||
err = r.db.Model(&models.CuOrder{}).
|
||||
Where("customer_id = ? AND status = ?", customerID, "pending").
|
||||
Count(&pendingOrders).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 查询已支付订单数
|
||||
var paidOrders int64
|
||||
err = r.db.Model(&models.CuOrder{}).
|
||||
Where("customer_id = ? AND status = ?", customerID, "paid").
|
||||
Count(&paidOrders).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &models.OrderSummaryResponse{
|
||||
TotalOrders: totalOrders,
|
||||
PendingOrders: pendingOrders,
|
||||
PaidOrders: paidOrders,
|
||||
}, nil
|
||||
}
|
||||
@@ -126,6 +126,9 @@ type LicenseRepository interface {
|
||||
// GetCustomerDeviceList 查询客户设备列表(关联授权码信息)
|
||||
GetCustomerDeviceList(ctx context.Context, customerID string, req *models.DeviceListRequest) (*models.DeviceListResponse, error)
|
||||
|
||||
// GetCustomerDeviceSummary 获取客户设备汇总统计
|
||||
GetCustomerDeviceSummary(ctx context.Context, customerID string) (*models.DeviceSummaryResponse, error)
|
||||
|
||||
// DeleteLicenseByID 根据ID删除许可证(物理删除,用于设备解绑)
|
||||
DeleteLicenseByID(ctx context.Context, id string) error
|
||||
|
||||
@@ -170,4 +173,4 @@ type PaymentRepository interface {
|
||||
|
||||
// GetExpiredPayments 获取过期的支付订单
|
||||
GetExpiredPayments(ctx context.Context) ([]*models.Payment, error)
|
||||
}
|
||||
}
|
||||
@@ -387,6 +387,44 @@ func (r *licenseRepository) DeleteLicenseByID(ctx context.Context, id string) er
|
||||
return r.db.Unscoped().Delete(&models.License{}, "id = ?", id).Error
|
||||
}
|
||||
|
||||
// GetCustomerDeviceSummary 获取客户设备汇总统计
|
||||
func (r *licenseRepository) GetCustomerDeviceSummary(ctx context.Context, customerID string) (*models.DeviceSummaryResponse, error) {
|
||||
// 获取心跳超时配置(秒)
|
||||
cfg := config.GetConfig()
|
||||
heartbeatTimeoutSeconds := cfg.License.HeartbeatTimeout
|
||||
if heartbeatTimeoutSeconds <= 0 {
|
||||
heartbeatTimeoutSeconds = 300 // 默认5分钟
|
||||
}
|
||||
|
||||
// 查询设备总数
|
||||
var totalDevices int64
|
||||
err := r.db.Model(&models.License{}).
|
||||
Where("customer_id = ? AND status = ? AND deleted_at IS NULL", customerID, "active").
|
||||
Count(&totalDevices).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 查询在线设备数
|
||||
var onlineDevices int64
|
||||
err = r.db.Model(&models.License{}).
|
||||
Where("customer_id = ? AND status = ? AND deleted_at IS NULL AND last_heartbeat > ?",
|
||||
customerID, "active", time.Now().Add(-time.Duration(heartbeatTimeoutSeconds)*time.Second)).
|
||||
Count(&onlineDevices).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 计算离线设备数
|
||||
offlineDevices := totalDevices - onlineDevices
|
||||
|
||||
return &models.DeviceSummaryResponse{
|
||||
TotalDevices: totalDevices,
|
||||
OnlineDevices: onlineDevices,
|
||||
OfflineDevices: offlineDevices,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CheckLicenseBelongsToCustomer 检查许可证是否属于指定客户
|
||||
func (r *licenseRepository) CheckLicenseBelongsToCustomer(ctx context.Context, licenseID, customerID string) (bool, error) {
|
||||
var count int64
|
||||
|
||||
@@ -36,6 +36,19 @@ func (s *cuDeviceService) GetDeviceList(ctx context.Context, customerID string,
|
||||
return s.licenseRepo.GetCustomerDeviceList(ctx, customerID, req)
|
||||
}
|
||||
|
||||
// GetDeviceSummary 获取设备汇总统计
|
||||
func (s *cuDeviceService) GetDeviceSummary(ctx context.Context, customerID string) (*models.DeviceSummaryResponse, error) {
|
||||
lang := pkgcontext.GetLanguageFromContext(ctx)
|
||||
|
||||
// 参数验证
|
||||
if customerID == "" {
|
||||
return nil, i18n.NewI18nError("900001", lang) // 业务错误,不覆盖多语言message
|
||||
}
|
||||
|
||||
// 委托给Repository层进行数据访问
|
||||
return s.licenseRepo.GetCustomerDeviceSummary(ctx, customerID)
|
||||
}
|
||||
|
||||
// UnbindDevice 解绑设备
|
||||
func (s *cuDeviceService) UnbindDevice(ctx context.Context, customerID, licenseID string) error {
|
||||
lang := pkgcontext.GetLanguageFromContext(ctx)
|
||||
|
||||
@@ -20,6 +20,7 @@ type CuOrderService interface {
|
||||
CreatePendingOrder(ctx context.Context, cuUserID, customerID string, req *models.CuOrderCreateRequest) (*models.CuOrder, error)
|
||||
GetOrder(ctx context.Context, orderID, cuUserID string) (*models.CuOrder, error)
|
||||
GetUserOrders(ctx context.Context, cuUserID string, offset, limit int) ([]*models.CuOrder, int64, error)
|
||||
GetOrderSummary(ctx context.Context, customerID string) (*models.OrderSummaryResponse, error)
|
||||
CalculatePrice(ctx context.Context, packageID string, licenseCount int) (*PriceCalculationResult, error)
|
||||
UpdateOrderStatus(ctx context.Context, orderID string, status string) error
|
||||
}
|
||||
@@ -356,6 +357,19 @@ func (s *cuOrderService) GetUserOrders(ctx context.Context, cuUserID string, off
|
||||
return orders, total, nil
|
||||
}
|
||||
|
||||
// GetOrderSummary 获取订单汇总统计
|
||||
func (s *cuOrderService) GetOrderSummary(ctx context.Context, customerID string) (*models.OrderSummaryResponse, error) {
|
||||
lang := pkgcontext.GetLanguageFromContext(ctx)
|
||||
|
||||
// 参数验证
|
||||
if customerID == "" {
|
||||
return nil, i18n.NewI18nError("900001", lang) // 业务错误,不覆盖多语言message
|
||||
}
|
||||
|
||||
// 委托给Repository层进行数据访问
|
||||
return s.repo.GetCustomerOrderSummary(ctx, customerID)
|
||||
}
|
||||
|
||||
func (s *cuOrderService) CalculatePrice(ctx context.Context, packageID string, licenseCount int) (*PriceCalculationResult, error) {
|
||||
lang := pkgcontext.GetLanguageFromContext(ctx)
|
||||
|
||||
|
||||
@@ -79,7 +79,9 @@ type CuDeviceService interface {
|
||||
// 获取设备列表
|
||||
GetDeviceList(ctx context.Context, customerID string, req *models.DeviceListRequest) (*models.DeviceListResponse, error)
|
||||
|
||||
// 获取设备汇总统计
|
||||
GetDeviceSummary(ctx context.Context, customerID string) (*models.DeviceSummaryResponse, error)
|
||||
|
||||
// 解绑设备
|
||||
UnbindDevice(ctx context.Context, customerID, licenseID string) error
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ const (
|
||||
SMSLimitKeyPrefix = "sms:limit"
|
||||
SMSCodeExpireTime = 5 * time.Minute
|
||||
SMSLimitTimeWindow = time.Hour
|
||||
SMSLimitMaxCount = 25
|
||||
SMSLimitMaxCount = 5
|
||||
)
|
||||
|
||||
// SMSService 短信服务接口
|
||||
|
||||
Reference in new issue
Block a user