- 修复 health.go handler 直接操作响应的架构违规问题 - 为 model 字段添加 GORM comment 标签(account_role、base、role_permission) - 为 handler、service、store 包添加包级文档注释 - 清理 customer service 和 personal_customer handler 中注释掉的代码 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
199 lines
5.9 KiB
Go
199 lines
5.9 KiB
Go
// Package app 提供移动端(H5/小程序)的 HTTP 处理器
|
||
// 包含个人客户认证、注册、微信绑定等功能的 Handler 实现
|
||
package app
|
||
|
||
import (
|
||
"github.com/break/junhong_cmp_fiber/internal/service/personal_customer"
|
||
"github.com/break/junhong_cmp_fiber/pkg/errors"
|
||
"github.com/break/junhong_cmp_fiber/pkg/response"
|
||
"github.com/gofiber/fiber/v2"
|
||
"go.uber.org/zap"
|
||
)
|
||
|
||
// PersonalCustomerHandler 个人客户处理器
|
||
type PersonalCustomerHandler struct {
|
||
service *personal_customer.Service
|
||
logger *zap.Logger
|
||
}
|
||
|
||
// NewPersonalCustomerHandler 创建个人客户处理器实例
|
||
func NewPersonalCustomerHandler(service *personal_customer.Service, logger *zap.Logger) *PersonalCustomerHandler {
|
||
return &PersonalCustomerHandler{
|
||
service: service,
|
||
logger: logger,
|
||
}
|
||
}
|
||
|
||
// SendCodeRequest 发送验证码请求
|
||
type SendCodeRequest struct {
|
||
Phone string `json:"phone" validate:"required,len=11"` // 手机号(11位)
|
||
}
|
||
|
||
// SendCode 发送验证码
|
||
// POST /api/c/v1/login/send-code
|
||
func (h *PersonalCustomerHandler) SendCode(c *fiber.Ctx) error {
|
||
var req SendCodeRequest
|
||
if err := c.BodyParser(&req); err != nil {
|
||
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
|
||
}
|
||
|
||
// 发送验证码
|
||
if err := h.service.SendVerificationCode(c.Context(), req.Phone); err != nil {
|
||
h.logger.Error("发送验证码失败",
|
||
zap.String("phone", req.Phone),
|
||
zap.Error(err),
|
||
)
|
||
return errors.Wrap(errors.CodeInternalError, "发送验证码失败", err)
|
||
}
|
||
|
||
return response.Success(c, fiber.Map{
|
||
"message": "验证码已发送",
|
||
})
|
||
}
|
||
|
||
// LoginRequest 登录请求
|
||
type LoginRequest struct {
|
||
Phone string `json:"phone" validate:"required,len=11"` // 手机号(11位)
|
||
Code string `json:"code" validate:"required,len=6"` // 验证码(6位)
|
||
}
|
||
|
||
// LoginResponse 登录响应
|
||
type LoginResponse struct {
|
||
Token string `json:"token"` // 访问令牌
|
||
Customer *PersonalCustomerDTO `json:"customer"` // 客户信息
|
||
}
|
||
|
||
// PersonalCustomerDTO 个人客户 DTO
|
||
type PersonalCustomerDTO struct {
|
||
ID uint `json:"id"`
|
||
Phone string `json:"phone"`
|
||
Nickname string `json:"nickname"`
|
||
AvatarURL string `json:"avatar_url"`
|
||
WxOpenID string `json:"wx_open_id"`
|
||
Status int `json:"status"`
|
||
}
|
||
|
||
// Login 登录(手机号 + 验证码)
|
||
// POST /api/c/v1/login
|
||
func (h *PersonalCustomerHandler) Login(c *fiber.Ctx) error {
|
||
var req LoginRequest
|
||
if err := c.BodyParser(&req); err != nil {
|
||
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
|
||
}
|
||
|
||
// 登录
|
||
token, customer, err := h.service.LoginByPhone(c.Context(), req.Phone, req.Code)
|
||
if err != nil {
|
||
h.logger.Error("登录失败",
|
||
zap.String("phone", req.Phone),
|
||
zap.Error(err),
|
||
)
|
||
return errors.Wrap(errors.CodeInternalError, "登录失败", err)
|
||
}
|
||
|
||
// 构造响应
|
||
// 注意:Phone 字段已从 PersonalCustomer 模型移除,需要从 PersonalCustomerPhone 表查询
|
||
resp := &LoginResponse{
|
||
Token: token,
|
||
Customer: &PersonalCustomerDTO{
|
||
ID: customer.ID,
|
||
Phone: req.Phone, // 使用请求中的手机号(临时方案)
|
||
Nickname: customer.Nickname,
|
||
AvatarURL: customer.AvatarURL,
|
||
WxOpenID: customer.WxOpenID,
|
||
Status: customer.Status,
|
||
},
|
||
}
|
||
|
||
return response.Success(c, resp)
|
||
}
|
||
|
||
// BindWechatRequest 绑定微信请求
|
||
type BindWechatRequest struct {
|
||
Code string `json:"code" validate:"required"` // 微信授权码
|
||
}
|
||
|
||
// BindWechat 绑定微信
|
||
// POST /api/c/v1/bind-wechat
|
||
// TODO: 实现微信 OAuth 授权逻辑
|
||
func (h *PersonalCustomerHandler) BindWechat(c *fiber.Ctx) error {
|
||
var req BindWechatRequest
|
||
if err := c.BodyParser(&req); err != nil {
|
||
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
|
||
}
|
||
|
||
// TODO: 实现完整的微信绑定流程
|
||
// 1. 从 context 中获取当前登录的客户 ID
|
||
// 2. 使用微信授权码换取 OpenID 和 UnionID
|
||
// 3. 调用 service 层的 BindWechat 方法绑定微信
|
||
|
||
return response.Success(c, fiber.Map{
|
||
"message": "微信绑定功能暂未实现,待微信 SDK 对接后启用",
|
||
})
|
||
}
|
||
|
||
// UpdateProfileRequest 更新个人资料请求
|
||
type UpdateProfileRequest struct {
|
||
Nickname string `json:"nickname"` // 昵称
|
||
AvatarURL string `json:"avatar_url"` // 头像 URL
|
||
}
|
||
|
||
// UpdateProfile 更新个人资料
|
||
// PUT /api/c/v1/profile
|
||
func (h *PersonalCustomerHandler) UpdateProfile(c *fiber.Ctx) error {
|
||
var req UpdateProfileRequest
|
||
if err := c.BodyParser(&req); err != nil {
|
||
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
|
||
}
|
||
|
||
// 从 context 中获取当前登录的客户 ID
|
||
customerID, ok := c.Locals("customer_id").(uint)
|
||
if !ok {
|
||
return errors.New(errors.CodeUnauthorized, "未找到客户信息")
|
||
}
|
||
|
||
if err := h.service.UpdateProfile(c.Context(), customerID, req.Nickname, req.AvatarURL); err != nil {
|
||
h.logger.Error("更新个人资料失败",
|
||
zap.Uint("customer_id", customerID),
|
||
zap.Error(err),
|
||
)
|
||
return errors.Wrap(errors.CodeInternalError, "更新个人资料失败", err)
|
||
}
|
||
|
||
return response.Success(c, fiber.Map{
|
||
"message": "更新成功",
|
||
})
|
||
}
|
||
|
||
// GetProfile 获取个人资料
|
||
// GET /api/c/v1/profile
|
||
func (h *PersonalCustomerHandler) GetProfile(c *fiber.Ctx) error {
|
||
// 从 context 中获取当前登录的客户 ID
|
||
customerID, ok := c.Locals("customer_id").(uint)
|
||
if !ok {
|
||
return errors.New(errors.CodeUnauthorized, "未找到客户信息")
|
||
}
|
||
|
||
// 获取客户资料(包含主手机号)
|
||
customer, phone, err := h.service.GetProfileWithPhone(c.Context(), customerID)
|
||
if err != nil {
|
||
h.logger.Error("获取个人资料失败",
|
||
zap.Uint("customer_id", customerID),
|
||
zap.Error(err),
|
||
)
|
||
return errors.Wrap(errors.CodeInternalError, "获取个人资料失败", err)
|
||
}
|
||
|
||
// 构造响应
|
||
resp := &PersonalCustomerDTO{
|
||
ID: customer.ID,
|
||
Phone: phone, // 使用查询到的主手机号
|
||
Nickname: customer.Nickname,
|
||
AvatarURL: customer.AvatarURL,
|
||
WxOpenID: customer.WxOpenID,
|
||
Status: customer.Status,
|
||
}
|
||
|
||
return response.Success(c, resp)
|
||
}
|