- Fix gofmt formatting issue in user_service.go - Remove unused sql field from userAttributeValueRepository - Update ListWithFilters signature in test stubs to match interface - Remove Wechat field from test user data 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
386 lines
10 KiB
Go
386 lines
10 KiB
Go
package repository
|
|
|
|
import (
|
|
"context"
|
|
|
|
dbent "github.com/Wei-Shaw/sub2api/ent"
|
|
"github.com/Wei-Shaw/sub2api/ent/userattributedefinition"
|
|
"github.com/Wei-Shaw/sub2api/ent/userattributevalue"
|
|
"github.com/Wei-Shaw/sub2api/internal/service"
|
|
)
|
|
|
|
// UserAttributeDefinitionRepository implementation
|
|
type userAttributeDefinitionRepository struct {
|
|
client *dbent.Client
|
|
}
|
|
|
|
// NewUserAttributeDefinitionRepository creates a new repository instance
|
|
func NewUserAttributeDefinitionRepository(client *dbent.Client) service.UserAttributeDefinitionRepository {
|
|
return &userAttributeDefinitionRepository{client: client}
|
|
}
|
|
|
|
func (r *userAttributeDefinitionRepository) Create(ctx context.Context, def *service.UserAttributeDefinition) error {
|
|
client := clientFromContext(ctx, r.client)
|
|
|
|
created, err := client.UserAttributeDefinition.Create().
|
|
SetKey(def.Key).
|
|
SetName(def.Name).
|
|
SetDescription(def.Description).
|
|
SetType(string(def.Type)).
|
|
SetOptions(toEntOptions(def.Options)).
|
|
SetRequired(def.Required).
|
|
SetValidation(toEntValidation(def.Validation)).
|
|
SetPlaceholder(def.Placeholder).
|
|
SetEnabled(def.Enabled).
|
|
Save(ctx)
|
|
|
|
if err != nil {
|
|
return translatePersistenceError(err, nil, service.ErrAttributeKeyExists)
|
|
}
|
|
|
|
def.ID = created.ID
|
|
def.DisplayOrder = created.DisplayOrder
|
|
def.CreatedAt = created.CreatedAt
|
|
def.UpdatedAt = created.UpdatedAt
|
|
return nil
|
|
}
|
|
|
|
func (r *userAttributeDefinitionRepository) GetByID(ctx context.Context, id int64) (*service.UserAttributeDefinition, error) {
|
|
client := clientFromContext(ctx, r.client)
|
|
|
|
e, err := client.UserAttributeDefinition.Query().
|
|
Where(userattributedefinition.IDEQ(id)).
|
|
Only(ctx)
|
|
if err != nil {
|
|
return nil, translatePersistenceError(err, service.ErrAttributeDefinitionNotFound, nil)
|
|
}
|
|
return defEntityToService(e), nil
|
|
}
|
|
|
|
func (r *userAttributeDefinitionRepository) GetByKey(ctx context.Context, key string) (*service.UserAttributeDefinition, error) {
|
|
client := clientFromContext(ctx, r.client)
|
|
|
|
e, err := client.UserAttributeDefinition.Query().
|
|
Where(userattributedefinition.KeyEQ(key)).
|
|
Only(ctx)
|
|
if err != nil {
|
|
return nil, translatePersistenceError(err, service.ErrAttributeDefinitionNotFound, nil)
|
|
}
|
|
return defEntityToService(e), nil
|
|
}
|
|
|
|
func (r *userAttributeDefinitionRepository) Update(ctx context.Context, def *service.UserAttributeDefinition) error {
|
|
client := clientFromContext(ctx, r.client)
|
|
|
|
updated, err := client.UserAttributeDefinition.UpdateOneID(def.ID).
|
|
SetName(def.Name).
|
|
SetDescription(def.Description).
|
|
SetType(string(def.Type)).
|
|
SetOptions(toEntOptions(def.Options)).
|
|
SetRequired(def.Required).
|
|
SetValidation(toEntValidation(def.Validation)).
|
|
SetPlaceholder(def.Placeholder).
|
|
SetDisplayOrder(def.DisplayOrder).
|
|
SetEnabled(def.Enabled).
|
|
Save(ctx)
|
|
|
|
if err != nil {
|
|
return translatePersistenceError(err, service.ErrAttributeDefinitionNotFound, service.ErrAttributeKeyExists)
|
|
}
|
|
|
|
def.UpdatedAt = updated.UpdatedAt
|
|
return nil
|
|
}
|
|
|
|
func (r *userAttributeDefinitionRepository) Delete(ctx context.Context, id int64) error {
|
|
client := clientFromContext(ctx, r.client)
|
|
|
|
_, err := client.UserAttributeDefinition.Delete().
|
|
Where(userattributedefinition.IDEQ(id)).
|
|
Exec(ctx)
|
|
return translatePersistenceError(err, service.ErrAttributeDefinitionNotFound, nil)
|
|
}
|
|
|
|
func (r *userAttributeDefinitionRepository) List(ctx context.Context, enabledOnly bool) ([]service.UserAttributeDefinition, error) {
|
|
client := clientFromContext(ctx, r.client)
|
|
|
|
q := client.UserAttributeDefinition.Query()
|
|
if enabledOnly {
|
|
q = q.Where(userattributedefinition.EnabledEQ(true))
|
|
}
|
|
|
|
entities, err := q.Order(dbent.Asc(userattributedefinition.FieldDisplayOrder)).All(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := make([]service.UserAttributeDefinition, 0, len(entities))
|
|
for _, e := range entities {
|
|
result = append(result, *defEntityToService(e))
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func (r *userAttributeDefinitionRepository) UpdateDisplayOrders(ctx context.Context, orders map[int64]int) error {
|
|
tx, err := r.client.Tx(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer func() { _ = tx.Rollback() }()
|
|
|
|
for id, order := range orders {
|
|
if _, err := tx.UserAttributeDefinition.UpdateOneID(id).
|
|
SetDisplayOrder(order).
|
|
Save(ctx); err != nil {
|
|
return translatePersistenceError(err, service.ErrAttributeDefinitionNotFound, nil)
|
|
}
|
|
}
|
|
|
|
return tx.Commit()
|
|
}
|
|
|
|
func (r *userAttributeDefinitionRepository) ExistsByKey(ctx context.Context, key string) (bool, error) {
|
|
client := clientFromContext(ctx, r.client)
|
|
return client.UserAttributeDefinition.Query().
|
|
Where(userattributedefinition.KeyEQ(key)).
|
|
Exist(ctx)
|
|
}
|
|
|
|
// UserAttributeValueRepository implementation
|
|
type userAttributeValueRepository struct {
|
|
client *dbent.Client
|
|
}
|
|
|
|
// NewUserAttributeValueRepository creates a new repository instance
|
|
func NewUserAttributeValueRepository(client *dbent.Client) service.UserAttributeValueRepository {
|
|
return &userAttributeValueRepository{client: client}
|
|
}
|
|
|
|
func (r *userAttributeValueRepository) GetByUserID(ctx context.Context, userID int64) ([]service.UserAttributeValue, error) {
|
|
client := clientFromContext(ctx, r.client)
|
|
|
|
entities, err := client.UserAttributeValue.Query().
|
|
Where(userattributevalue.UserIDEQ(userID)).
|
|
All(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := make([]service.UserAttributeValue, 0, len(entities))
|
|
for _, e := range entities {
|
|
result = append(result, service.UserAttributeValue{
|
|
ID: e.ID,
|
|
UserID: e.UserID,
|
|
AttributeID: e.AttributeID,
|
|
Value: e.Value,
|
|
CreatedAt: e.CreatedAt,
|
|
UpdatedAt: e.UpdatedAt,
|
|
})
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func (r *userAttributeValueRepository) GetByUserIDs(ctx context.Context, userIDs []int64) ([]service.UserAttributeValue, error) {
|
|
if len(userIDs) == 0 {
|
|
return []service.UserAttributeValue{}, nil
|
|
}
|
|
|
|
client := clientFromContext(ctx, r.client)
|
|
|
|
entities, err := client.UserAttributeValue.Query().
|
|
Where(userattributevalue.UserIDIn(userIDs...)).
|
|
All(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := make([]service.UserAttributeValue, 0, len(entities))
|
|
for _, e := range entities {
|
|
result = append(result, service.UserAttributeValue{
|
|
ID: e.ID,
|
|
UserID: e.UserID,
|
|
AttributeID: e.AttributeID,
|
|
Value: e.Value,
|
|
CreatedAt: e.CreatedAt,
|
|
UpdatedAt: e.UpdatedAt,
|
|
})
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func (r *userAttributeValueRepository) UpsertBatch(ctx context.Context, userID int64, inputs []service.UpdateUserAttributeInput) error {
|
|
if len(inputs) == 0 {
|
|
return nil
|
|
}
|
|
|
|
tx, err := r.client.Tx(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer func() { _ = tx.Rollback() }()
|
|
|
|
for _, input := range inputs {
|
|
// Use upsert (ON CONFLICT DO UPDATE)
|
|
err := tx.UserAttributeValue.Create().
|
|
SetUserID(userID).
|
|
SetAttributeID(input.AttributeID).
|
|
SetValue(input.Value).
|
|
OnConflictColumns(userattributevalue.FieldUserID, userattributevalue.FieldAttributeID).
|
|
UpdateValue().
|
|
UpdateUpdatedAt().
|
|
Exec(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return tx.Commit()
|
|
}
|
|
|
|
func (r *userAttributeValueRepository) DeleteByAttributeID(ctx context.Context, attributeID int64) error {
|
|
client := clientFromContext(ctx, r.client)
|
|
|
|
_, err := client.UserAttributeValue.Delete().
|
|
Where(userattributevalue.AttributeIDEQ(attributeID)).
|
|
Exec(ctx)
|
|
return err
|
|
}
|
|
|
|
func (r *userAttributeValueRepository) DeleteByUserID(ctx context.Context, userID int64) error {
|
|
client := clientFromContext(ctx, r.client)
|
|
|
|
_, err := client.UserAttributeValue.Delete().
|
|
Where(userattributevalue.UserIDEQ(userID)).
|
|
Exec(ctx)
|
|
return err
|
|
}
|
|
|
|
// Helper functions for entity to service conversion
|
|
func defEntityToService(e *dbent.UserAttributeDefinition) *service.UserAttributeDefinition {
|
|
if e == nil {
|
|
return nil
|
|
}
|
|
return &service.UserAttributeDefinition{
|
|
ID: e.ID,
|
|
Key: e.Key,
|
|
Name: e.Name,
|
|
Description: e.Description,
|
|
Type: service.UserAttributeType(e.Type),
|
|
Options: toServiceOptions(e.Options),
|
|
Required: e.Required,
|
|
Validation: toServiceValidation(e.Validation),
|
|
Placeholder: e.Placeholder,
|
|
DisplayOrder: e.DisplayOrder,
|
|
Enabled: e.Enabled,
|
|
CreatedAt: e.CreatedAt,
|
|
UpdatedAt: e.UpdatedAt,
|
|
}
|
|
}
|
|
|
|
// Type conversion helpers (map types <-> service types)
|
|
func toEntOptions(opts []service.UserAttributeOption) []map[string]any {
|
|
if opts == nil {
|
|
return []map[string]any{}
|
|
}
|
|
result := make([]map[string]any, len(opts))
|
|
for i, o := range opts {
|
|
result[i] = map[string]any{"value": o.Value, "label": o.Label}
|
|
}
|
|
return result
|
|
}
|
|
|
|
func toServiceOptions(opts []map[string]any) []service.UserAttributeOption {
|
|
if opts == nil {
|
|
return []service.UserAttributeOption{}
|
|
}
|
|
result := make([]service.UserAttributeOption, len(opts))
|
|
for i, o := range opts {
|
|
result[i] = service.UserAttributeOption{
|
|
Value: getString(o, "value"),
|
|
Label: getString(o, "label"),
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
func toEntValidation(v service.UserAttributeValidation) map[string]any {
|
|
result := map[string]any{}
|
|
if v.MinLength != nil {
|
|
result["min_length"] = *v.MinLength
|
|
}
|
|
if v.MaxLength != nil {
|
|
result["max_length"] = *v.MaxLength
|
|
}
|
|
if v.Min != nil {
|
|
result["min"] = *v.Min
|
|
}
|
|
if v.Max != nil {
|
|
result["max"] = *v.Max
|
|
}
|
|
if v.Pattern != nil {
|
|
result["pattern"] = *v.Pattern
|
|
}
|
|
if v.Message != nil {
|
|
result["message"] = *v.Message
|
|
}
|
|
return result
|
|
}
|
|
|
|
func toServiceValidation(v map[string]any) service.UserAttributeValidation {
|
|
result := service.UserAttributeValidation{}
|
|
if val := getInt(v, "min_length"); val != nil {
|
|
result.MinLength = val
|
|
}
|
|
if val := getInt(v, "max_length"); val != nil {
|
|
result.MaxLength = val
|
|
}
|
|
if val := getInt(v, "min"); val != nil {
|
|
result.Min = val
|
|
}
|
|
if val := getInt(v, "max"); val != nil {
|
|
result.Max = val
|
|
}
|
|
if val := getStringPtr(v, "pattern"); val != nil {
|
|
result.Pattern = val
|
|
}
|
|
if val := getStringPtr(v, "message"); val != nil {
|
|
result.Message = val
|
|
}
|
|
return result
|
|
}
|
|
|
|
// Helper functions for type conversion
|
|
func getString(m map[string]any, key string) string {
|
|
if v, ok := m[key]; ok {
|
|
if s, ok := v.(string); ok {
|
|
return s
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func getStringPtr(m map[string]any, key string) *string {
|
|
if v, ok := m[key]; ok {
|
|
if s, ok := v.(string); ok {
|
|
return &s
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func getInt(m map[string]any, key string) *int {
|
|
if v, ok := m[key]; ok {
|
|
switch n := v.(type) {
|
|
case int:
|
|
return &n
|
|
case int64:
|
|
i := int(n)
|
|
return &i
|
|
case float64:
|
|
i := int(n)
|
|
return &i
|
|
}
|
|
}
|
|
return nil
|
|
}
|