feat(auth): 接入 DCAuth v2 登录并完善成员管理

- 登录链路改为用户名 + 密码 + 短信验证码 + DCAuth v2 设备签名
- 迁移 014:新增 login_sms_codes / user_devices / device_challenges 与 users.phone
- 新增 /api/dcauth/authorize 与 challenge 状态轮询;旧密码登录仅放行未绑定手机号的账号
- 成员管理支持手机号绑定、设备重置、账号改名、昵称与重置密码
- 移除最后一个商户关系时软删除账号,释放用户名、手机号并撤销设备绑定
This commit is contained in:
yml2213
2026-10-21 22:37:49 +08:00
parent aa052d420a
commit bab522bcb3
29 changed files with 5490 additions and 1170 deletions
+16 -1
View File
@@ -20,6 +20,7 @@ import (
type Handlers struct {
Auth *handler.AuthHandler
DCAuth *handler.DCAuthHandler
Dashboard *handler.DashboardHandler
Delivery *handler.DeliveryHandler
User *handler.UserHandler
@@ -153,7 +154,7 @@ func Setup(h *Handlers) *gin.Engine {
r.Use(cors.New(cors.Config{
AllowOrigins: corsOrigins(h),
AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"},
AllowHeaders: []string{"Origin", "Content-Type", "Authorization", "X-Merchant-ID", "X-App-Key", "X-Api-Key", "X-Timestamp", "X-Nonce", "X-Sign", "X-Request-ID"},
AllowHeaders: []string{"Origin", "Content-Type", "Authorization", "X-Merchant-ID", "X-App-Key", "X-Api-Key", "X-Timestamp", "X-Nonce", "X-Sign", "X-Request-ID", "X-Poll-Token"},
ExposeHeaders: []string{"Content-Length", "X-Request-ID"},
AllowCredentials: true,
}))
@@ -170,6 +171,15 @@ func Setup(h *Handlers) *gin.Engine {
api := r.Group("/api")
{
api.POST("/auth/login", h.Auth.Login)
api.POST("/auth/login/sms-code", h.Auth.SendLoginSMSCode)
api.POST("/auth/login/prepare", h.Auth.PrepareLogin)
api.POST("/auth/login/complete", h.Auth.CompleteLogin)
// DCAuth 2.0.0 客户端协议端点(客户端硬编码 /api/dcauth/authorize)。
if h.DCAuth != nil {
api.POST("/dcauth/authorize", h.DCAuth.Authorize)
api.GET("/dcauth/challenge/:challengeId/status", h.DCAuth.Status)
}
// 乐玩开放平台 Webhook 回调
if h.LewanWebhook != nil {
@@ -235,6 +245,9 @@ func Setup(h *Handlers) *gin.Engine {
platform.POST("/platform/admins", h.User.CreatePlatformAdmin)
platform.PATCH("/platform/admins/:id", h.User.UpdatePlatformAdmin)
platform.DELETE("/platform/admins/:id", h.User.DeletePlatformAdmin)
// 登录安全:管理端绑定手机号与重置 DCAuth 设备。
platform.POST("/admin/users/:id/phone", h.User.BindPlatformUserPhone)
platform.POST("/admin/users/:id/device/reset", h.User.ResetPlatformUserDevice)
platform.GET("/platform/merchants", h.Merchant.ListPlatformMerchants)
// 不分页的精简商户列表,供筛选下拉使用(分页接口的 size 上限是 100)
platform.GET("/platform/merchant-options", h.Merchant.ListMerchantOptions)
@@ -330,6 +343,8 @@ func Setup(h *Handlers) *gin.Engine {
merchant.POST("/members", middleware.RequireMerchantPermissions(h.Tenant, model.PermissionMembersManage), h.Merchant.AddCurrentMerchantMember)
merchant.PATCH("/members/:id", middleware.RequireMerchantPermissions(h.Tenant, model.PermissionMembersManage), h.Merchant.UpdateCurrentMerchantMember)
merchant.DELETE("/members/:id", middleware.RequireMerchantPermissions(h.Tenant, model.PermissionMembersManage), h.Merchant.RemoveCurrentMerchantMember)
merchant.POST("/members/:id/phone", middleware.RequireMerchantPermissions(h.Tenant, model.PermissionMembersManage), h.Merchant.BindCurrentMerchantMemberPhone)
merchant.POST("/members/:id/device/reset", middleware.RequireMerchantPermissions(h.Tenant, model.PermissionMembersManage), h.Merchant.ResetCurrentMerchantMemberDevice)
merchant.GET("/roles", middleware.RequireMerchantPermissions(h.Tenant, model.PermissionMembersManage), h.Merchant.ListRoles)
merchant.POST("/roles", middleware.RequireMerchantPermissions(h.Tenant, model.PermissionMembersManage), h.Merchant.CreateRole)
merchant.PATCH("/roles/:id", middleware.RequireMerchantPermissions(h.Tenant, model.PermissionMembersManage), h.Merchant.UpdateRole)