feat: 完成B端认证系统和商户管理模块测试补全

主要变更:
- 新增B端认证系统(后台+H5):登录、登出、Token刷新、密码修改
- 完善商户管理和商户账号管理功能
- 补全单元测试(ShopService: 72.5%, ShopAccountService: 79.8%)
- 新增集成测试(商户管理+商户账号管理)
- 归档OpenSpec提案(add-shop-account-management, implement-b-end-auth-system)
- 完善文档(使用指南、API文档、认证架构说明)

测试统计:
- 13个测试套件,37个测试用例,100%通过率
- 平均覆盖率76.2%,达标

OpenSpec验证:通过(strict模式)
This commit is contained in:
2026-01-15 18:15:17 +08:00
parent 7ccd3d146c
commit 18f35f3ef4
64 changed files with 11875 additions and 242 deletions

View File

@@ -56,6 +56,15 @@ func (s *AccountStore) GetByPhone(ctx context.Context, phone string) (*model.Acc
return &account, nil
}
// GetByUsernameOrPhone 根据用户名或手机号获取账号
func (s *AccountStore) GetByUsernameOrPhone(ctx context.Context, identifier string) (*model.Account, error) {
var account model.Account
if err := s.db.WithContext(ctx).Where("username = ? OR phone = ?", identifier, identifier).First(&account).Error; err != nil {
return nil, err
}
return &account, nil
}
// GetByShopID 根据店铺 ID 查询账号列表
func (s *AccountStore) GetByShopID(ctx context.Context, shopID uint) ([]*model.Account, error) {
var accounts []*model.Account
@@ -197,3 +206,52 @@ func (s *AccountStore) UpdateStatus(ctx context.Context, id uint, status int, up
"updater": updater,
}).Error
}
// BulkUpdateStatus 批量更新账号状态
func (s *AccountStore) BulkUpdateStatus(ctx context.Context, ids []uint, status int, updater uint) error {
return s.db.WithContext(ctx).
Model(&model.Account{}).
Where("id IN ?", ids).
Updates(map[string]interface{}{
"status": status,
"updater": updater,
}).Error
}
// ListByShopID 按店铺ID分页查询账号列表
func (s *AccountStore) ListByShopID(ctx context.Context, shopID uint, opts *store.QueryOptions, filters map[string]interface{}) ([]*model.Account, int64, error) {
var accounts []*model.Account
var total int64
query := s.db.WithContext(ctx).Model(&model.Account{}).Where("shop_id = ?", shopID)
if username, ok := filters["username"].(string); ok && username != "" {
query = query.Where("username LIKE ?", "%"+username+"%")
}
if phone, ok := filters["phone"].(string); ok && phone != "" {
query = query.Where("phone LIKE ?", "%"+phone+"%")
}
if status, ok := filters["status"].(int); ok {
query = query.Where("status = ?", status)
}
if err := query.Count(&total).Error; err != nil {
return nil, 0, err
}
if opts == nil {
opts = store.DefaultQueryOptions()
}
offset := (opts.Page - 1) * opts.PageSize
query = query.Offset(offset).Limit(opts.PageSize)
if opts.OrderBy != "" {
query = query.Order(opts.OrderBy)
}
if err := query.Find(&accounts).Error; err != nil {
return nil, 0, err
}
return accounts, total, nil
}