feat: 系统全面优化 - 设备管理/登录日志导出/性能监控/设置页面
后端: - 新增全局设备管理 API(DeviceHandler.GetAllDevices) - 新增登录日志导出功能(LogHandler.ExportLoginLogs, CSV/XLSX) - 新增设置服务(SettingsService)和设置页面 API - 设备管理支持多条件筛选(状态/信任状态/关键词) - 登录日志支持流式导出防 OOM - 操作日志支持按方法/时间范围搜索 - 主题配置服务(ThemeService) - 增强监控健康检查(Prometheus metrics + SLO) - 移除旧 ratelimit.go(已迁移至 robustness) - 修复 SocialAccount NULL 扫描问题 - 新增 API 契约测试、Handler 测试、Settings 测试 前端: - 新增管理员设备管理页面(DevicesPage) - 新增管理员登录日志导出功能 - 新增系统设置页面(SettingsPage) - 设备管理支持筛选和分页 - 增强 HTTP 响应类型 测试: - 业务逻辑测试 68 个(含并发 CONC_001~003) - 规模测试 16 个(P99 百分位统计) - E2E 测试、集成测试、契约测试 - 性能基准测试、鲁棒性测试 全面测试通过(38 个测试包)
This commit is contained in:
@@ -4,20 +4,95 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/user-management-system/internal/service"
|
||||
)
|
||||
|
||||
// SMSHandler handles SMS requests
|
||||
type SMSHandler struct{}
|
||||
type SMSHandler struct {
|
||||
authService *service.AuthService
|
||||
smsCodeService *service.SMSCodeService
|
||||
}
|
||||
|
||||
// NewSMSHandler creates a new SMSHandler
|
||||
// NewSMSHandler creates a new SMSHandler (stub, no SMS configured)
|
||||
func NewSMSHandler() *SMSHandler {
|
||||
return &SMSHandler{}
|
||||
}
|
||||
|
||||
func (h *SMSHandler) SendCode(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"message": "SMS not configured"})
|
||||
// NewSMSHandlerWithService creates a SMSHandler backed by real AuthService + SMSCodeService
|
||||
func NewSMSHandlerWithService(authService *service.AuthService, smsCodeService *service.SMSCodeService) *SMSHandler {
|
||||
return &SMSHandler{
|
||||
authService: authService,
|
||||
smsCodeService: smsCodeService,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *SMSHandler) LoginByCode(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"error": "SMS login not configured"})
|
||||
// SendCode 发送短信验证码(用于注册/登录)
|
||||
func (h *SMSHandler) SendCode(c *gin.Context) {
|
||||
if h.smsCodeService == nil {
|
||||
c.JSON(http.StatusServiceUnavailable, gin.H{"error": "SMS service not configured"})
|
||||
return
|
||||
}
|
||||
|
||||
var req service.SendCodeRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.smsCodeService.SendCode(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
handleError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, resp)
|
||||
}
|
||||
|
||||
// LoginByCode 短信验证码登录(带设备信息以支持设备信任链路)
|
||||
func (h *SMSHandler) LoginByCode(c *gin.Context) {
|
||||
if h.authService == nil {
|
||||
c.JSON(http.StatusServiceUnavailable, gin.H{"error": "SMS login not configured"})
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
Phone string `json:"phone" binding:"required"`
|
||||
Code string `json:"code" binding:"required"`
|
||||
DeviceID string `json:"device_id"`
|
||||
DeviceName string `json:"device_name"`
|
||||
DeviceBrowser string `json:"device_browser"`
|
||||
DeviceOS string `json:"device_os"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
clientIP := c.ClientIP()
|
||||
resp, err := h.authService.LoginByCode(c.Request.Context(), req.Phone, req.Code, clientIP)
|
||||
if err != nil {
|
||||
handleError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 自动注册/更新设备记录(不阻塞主流程)
|
||||
// 注意:必须用独立的 background context,不能用 c.Request.Context()(gin 回收后会取消)
|
||||
if req.DeviceID != "" && resp != nil && resp.User != nil {
|
||||
loginReq := &service.LoginRequest{
|
||||
DeviceID: req.DeviceID,
|
||||
DeviceName: req.DeviceName,
|
||||
DeviceBrowser: req.DeviceBrowser,
|
||||
DeviceOS: req.DeviceOS,
|
||||
}
|
||||
userID := resp.User.ID
|
||||
go func() {
|
||||
devCtx, cancel := newBackgroundCtx(5)
|
||||
defer cancel()
|
||||
h.authService.BestEffortRegisterDevicePublic(devCtx, userID, loginReq)
|
||||
}()
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, resp)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user