All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m2s
- 新增成对迁移 000223(tb_phone_asset_association,含有效关系部分唯一索引与 down 守卫)与 000224(解绑导入任务表),不回填历史 - H5:need_bind_phone 三支判定(开关关闭完全短路);已有主号幂等建联;十项上限按手机号 advisory 串行化(含换绑到全新号的并发场景);换绑原子迁移与冲突整单回滚;不写遗留列 - 后台:关联列表、单项/批量解绑、CSV 导入解绑(B1–B16),超管/平台 gate + 资产数据范围复核,三态统一文案 - 读侧:卡/设备列表与详情按页一次 IN 聚合;两类导出补「关联手机号」列并保留历史表头反解兼容 - 脱敏:关联审计走独立动作/资源只写脱敏手机号;访问日志手机号类字段脱敏 - 同步主 Spec openspec/specs/phone-asset-association 并归档 AUG26-009,补齐 requirement-evidence 与入口矩阵,context-health 通过
192 lines
5.7 KiB
Go
192 lines
5.7 KiB
Go
package app
|
||
|
||
import (
|
||
"github.com/break/junhong_cmp_fiber/internal/middleware"
|
||
"github.com/break/junhong_cmp_fiber/internal/model/dto"
|
||
clientAuthSvc "github.com/break/junhong_cmp_fiber/internal/service/client_auth"
|
||
"github.com/break/junhong_cmp_fiber/pkg/errors"
|
||
"github.com/break/junhong_cmp_fiber/pkg/logger"
|
||
"github.com/break/junhong_cmp_fiber/pkg/response"
|
||
"github.com/go-playground/validator/v10"
|
||
"github.com/gofiber/fiber/v2"
|
||
"go.uber.org/zap"
|
||
)
|
||
|
||
var clientAuthValidator = validator.New()
|
||
|
||
// ClientAuthHandler C 端认证处理器
|
||
type ClientAuthHandler struct {
|
||
service *clientAuthSvc.Service
|
||
logger *zap.Logger
|
||
}
|
||
|
||
// NewClientAuthHandler 创建 C 端认证处理器
|
||
func NewClientAuthHandler(service *clientAuthSvc.Service, logger *zap.Logger) *ClientAuthHandler {
|
||
return &ClientAuthHandler{service: service, logger: logger}
|
||
}
|
||
|
||
// VerifyAsset A1 资产验证
|
||
// POST /api/c/v1/auth/verify-asset
|
||
func (h *ClientAuthHandler) VerifyAsset(c *fiber.Ctx) error {
|
||
var req dto.VerifyAssetRequest
|
||
if err := c.BodyParser(&req); err != nil {
|
||
return errors.New(errors.CodeInvalidParam)
|
||
}
|
||
if err := clientAuthValidator.Struct(&req); err != nil {
|
||
logger.GetAppLogger().Warn("资产验证参数校验失败", zap.Error(err))
|
||
return errors.New(errors.CodeInvalidParam)
|
||
}
|
||
|
||
resp, err := h.service.VerifyAsset(c.UserContext(), &req, c.IP())
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return response.Success(c, resp)
|
||
}
|
||
|
||
// WechatLogin A2 公众号登录
|
||
// POST /api/c/v1/auth/wechat-login
|
||
func (h *ClientAuthHandler) WechatLogin(c *fiber.Ctx) error {
|
||
var req dto.WechatLoginRequest
|
||
if err := c.BodyParser(&req); err != nil {
|
||
return errors.New(errors.CodeInvalidParam)
|
||
}
|
||
if err := clientAuthValidator.Struct(&req); err != nil {
|
||
logger.GetAppLogger().Warn("公众号登录参数校验失败", zap.Error(err))
|
||
return errors.New(errors.CodeInvalidParam)
|
||
}
|
||
|
||
resp, err := h.service.WechatLogin(c.UserContext(), &req, c.IP())
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return response.Success(c, resp)
|
||
}
|
||
|
||
// MiniappLogin A3 小程序登录
|
||
// POST /api/c/v1/auth/miniapp-login
|
||
func (h *ClientAuthHandler) MiniappLogin(c *fiber.Ctx) error {
|
||
var req dto.MiniappLoginRequest
|
||
if err := c.BodyParser(&req); err != nil {
|
||
return errors.New(errors.CodeInvalidParam)
|
||
}
|
||
if err := clientAuthValidator.Struct(&req); err != nil {
|
||
logger.GetAppLogger().Warn("小程序登录参数校验失败", zap.Error(err))
|
||
return errors.New(errors.CodeInvalidParam)
|
||
}
|
||
|
||
resp, err := h.service.MiniappLogin(c.UserContext(), &req, c.IP())
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return response.Success(c, resp)
|
||
}
|
||
|
||
// SendCode A4 发送验证码
|
||
// POST /api/c/v1/auth/send-code
|
||
func (h *ClientAuthHandler) SendCode(c *fiber.Ctx) error {
|
||
var req dto.ClientSendCodeRequest
|
||
if err := c.BodyParser(&req); err != nil {
|
||
return errors.New(errors.CodeInvalidParam)
|
||
}
|
||
if err := clientAuthValidator.Struct(&req); err != nil {
|
||
logger.GetAppLogger().Warn("发送验证码参数校验失败", zap.Error(err))
|
||
return errors.New(errors.CodeInvalidParam)
|
||
}
|
||
|
||
resp, err := h.service.SendCode(c.UserContext(), &req, c.IP())
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return response.Success(c, resp)
|
||
}
|
||
|
||
// BindPhone A5 绑定手机号
|
||
// POST /api/c/v1/auth/bind-phone
|
||
func (h *ClientAuthHandler) BindPhone(c *fiber.Ctx) error {
|
||
customerID, ok := middleware.GetCustomerID(c)
|
||
if !ok || customerID == 0 {
|
||
return errors.New(errors.CodeUnauthorized)
|
||
}
|
||
|
||
var req dto.BindPhoneRequest
|
||
if err := c.BodyParser(&req); err != nil {
|
||
return errors.New(errors.CodeInvalidParam)
|
||
}
|
||
if err := clientAuthValidator.Struct(&req); err != nil {
|
||
logger.GetAppLogger().Warn("绑定手机号参数校验失败", zap.Error(err))
|
||
return errors.New(errors.CodeInvalidParam)
|
||
}
|
||
|
||
// 建联只依据当前访问资产身份:JWT 缺失资产身份时只完成账号手机号绑定。
|
||
assetType, assetID, _ := middleware.GetCurrentAsset(c)
|
||
|
||
resp, err := h.service.BindPhone(c.UserContext(), customerID, assetType, assetID, &req)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return response.Success(c, resp)
|
||
}
|
||
|
||
// ChangePhone A6 更换手机号
|
||
// POST /api/c/v1/auth/change-phone
|
||
func (h *ClientAuthHandler) ChangePhone(c *fiber.Ctx) error {
|
||
customerID, ok := middleware.GetCustomerID(c)
|
||
if !ok || customerID == 0 {
|
||
return errors.New(errors.CodeUnauthorized)
|
||
}
|
||
|
||
var req dto.ChangePhoneRequest
|
||
if err := c.BodyParser(&req); err != nil {
|
||
return errors.New(errors.CodeInvalidParam)
|
||
}
|
||
if err := clientAuthValidator.Struct(&req); err != nil {
|
||
logger.GetAppLogger().Warn("更换手机号参数校验失败", zap.Error(err))
|
||
return errors.New(errors.CodeInvalidParam)
|
||
}
|
||
|
||
resp, err := h.service.ChangePhone(c.UserContext(), customerID, &req)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return response.Success(c, resp)
|
||
}
|
||
|
||
// DevLogin 开发环境测试登录(仅开发模式可用)
|
||
// POST /api/c/v1/auth/dev-login
|
||
func (h *ClientAuthHandler) DevLogin(c *fiber.Ctx) error {
|
||
var req dto.DevLoginRequest
|
||
if err := c.BodyParser(&req); err != nil {
|
||
return errors.New(errors.CodeInvalidParam)
|
||
}
|
||
if err := clientAuthValidator.Struct(&req); err != nil {
|
||
h.logger.Warn("测试登录参数校验失败", zap.Error(err))
|
||
return errors.New(errors.CodeInvalidParam)
|
||
}
|
||
|
||
token, customerID, isNewUser, err := h.service.DevLogin(c.UserContext(), req.Identifier)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return response.Success(c, dto.DevLoginResponse{
|
||
Token: token,
|
||
CustomerID: customerID,
|
||
IsNewUser: isNewUser,
|
||
})
|
||
}
|
||
|
||
// Logout A7 退出登录
|
||
// POST /api/c/v1/auth/logout
|
||
func (h *ClientAuthHandler) Logout(c *fiber.Ctx) error {
|
||
customerID, ok := middleware.GetCustomerID(c)
|
||
if !ok || customerID == 0 {
|
||
return errors.New(errors.CodeUnauthorized)
|
||
}
|
||
|
||
resp, err := h.service.Logout(c.UserContext(), customerID)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return response.Success(c, resp)
|
||
}
|