Files
junhong_cmp_fiber/internal/service/customer/service.go
huang 9c6d4a3bd4 实现个人客户微信认证和短信验证功能
- 添加个人客户微信登录和手机验证码登录接口
- 实现个人客户设备、ICCID、手机号关联管理
- 添加短信发送服务(HTTP 客户端)
- 添加微信认证服务(含 mock 实现)
- 添加 JWT Token 生成和验证工具
- 创建数据库迁移脚本(personal_customer 关联表)
- 修复测试文件中的路由注册参数错误
- 重构 scripts 目录结构(分离独立脚本到子目录)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-10 11:42:38 +08:00

137 lines
4.2 KiB
Go

package customer
import (
"context"
"github.com/break/junhong_cmp_fiber/internal/model"
"github.com/break/junhong_cmp_fiber/internal/store"
"github.com/break/junhong_cmp_fiber/internal/store/postgres"
"github.com/break/junhong_cmp_fiber/pkg/constants"
"github.com/break/junhong_cmp_fiber/pkg/errors"
)
// Service 个人客户业务服务
type Service struct {
customerStore *postgres.PersonalCustomerStore
}
// New 创建个人客户服务
func New(customerStore *postgres.PersonalCustomerStore) *Service {
return &Service{
customerStore: customerStore,
}
}
// Create 创建个人客户
func (s *Service) Create(ctx context.Context, req *model.CreatePersonalCustomerRequest) (*model.PersonalCustomer, error) {
// 检查手机号唯一性
if req.Phone != "" {
existing, err := s.customerStore.GetByPhone(ctx, req.Phone)
if err == nil && existing != nil {
return nil, errors.New(errors.CodeCustomerPhoneExists, "手机号已存在")
}
}
// 创建个人客户
// 注意:根据新的数据模型,手机号应该存储在 PersonalCustomerPhone 表中
// 这里暂时先创建客户记录,手机号的存储后续通过 PersonalCustomerPhoneStore 实现
customer := &model.PersonalCustomer{
Nickname: req.Nickname,
AvatarURL: req.AvatarURL,
WxOpenID: req.WxOpenID,
WxUnionID: req.WxUnionID,
Status: constants.StatusEnabled,
}
if err := s.customerStore.Create(ctx, customer); err != nil {
return nil, err
}
// TODO: 创建 PersonalCustomerPhone 记录
// if req.Phone != "" {
// phoneRecord := &model.PersonalCustomerPhone{
// CustomerID: customer.ID,
// Phone: req.Phone,
// IsPrimary: true,
// Status: constants.StatusEnabled,
// }
// // 需要通过 PersonalCustomerPhoneStore 创建
// }
return customer, nil
}
// Update 更新个人客户信息
func (s *Service) Update(ctx context.Context, id uint, req *model.UpdatePersonalCustomerRequest) (*model.PersonalCustomer, error) {
// 查询客户
customer, err := s.customerStore.GetByID(ctx, id)
if err != nil {
return nil, errors.New(errors.CodeCustomerNotFound, "个人客户不存在")
}
// 注意:手机号的更新逻辑需要通过 PersonalCustomerPhone 表处理
// TODO: 实现手机号的更新逻辑
// if req.Phone != nil {
// // 通过 PersonalCustomerPhoneStore 更新或创建手机号记录
// }
// 更新字段
if req.Nickname != nil {
customer.Nickname = *req.Nickname
}
if req.AvatarURL != nil {
customer.AvatarURL = *req.AvatarURL
}
if err := s.customerStore.Update(ctx, customer); err != nil {
return nil, err
}
return customer, nil
}
// BindWeChat 绑定微信信息
func (s *Service) BindWeChat(ctx context.Context, id uint, wxOpenID, wxUnionID string) error {
customer, err := s.customerStore.GetByID(ctx, id)
if err != nil {
return errors.New(errors.CodeCustomerNotFound, "个人客户不存在")
}
customer.WxOpenID = wxOpenID
customer.WxUnionID = wxUnionID
return s.customerStore.Update(ctx, customer)
}
// GetByID 获取个人客户详情
func (s *Service) GetByID(ctx context.Context, id uint) (*model.PersonalCustomer, error) {
customer, err := s.customerStore.GetByID(ctx, id)
if err != nil {
return nil, errors.New(errors.CodeCustomerNotFound, "个人客户不存在")
}
return customer, nil
}
// GetByPhone 根据手机号获取个人客户
func (s *Service) GetByPhone(ctx context.Context, phone string) (*model.PersonalCustomer, error) {
customer, err := s.customerStore.GetByPhone(ctx, phone)
if err != nil {
return nil, errors.New(errors.CodeCustomerNotFound, "个人客户不存在")
}
return customer, nil
}
// GetByWxOpenID 根据微信 OpenID 获取个人客户
func (s *Service) GetByWxOpenID(ctx context.Context, wxOpenID string) (*model.PersonalCustomer, error) {
customer, err := s.customerStore.GetByWxOpenID(ctx, wxOpenID)
if err != nil {
return nil, errors.New(errors.CodeCustomerNotFound, "个人客户不存在")
}
return customer, nil
}
// List 查询个人客户列表
func (s *Service) List(ctx context.Context, opts *store.QueryOptions, filters map[string]interface{}) ([]*model.PersonalCustomer, int64, error) {
return s.customerStore.List(ctx, opts, filters)
}