Files
junhong_cmp_fiber/internal/handler/app/personal_customer.go
huang 4507de577b 代码质量改进:修复架构违规、完善文档注释和清理冗余代码
- 修复 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>
2026-01-12 16:28:48 +08:00

199 lines
5.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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)
}