Merge PR #122: feat: 用户自定义属性系统 + Wechat 字段迁移

This commit is contained in:
shaw 2026-01-01 20:25:50 +08:00
commit 106e59b753
71 changed files with 12793 additions and 596 deletions

View File

@ -114,15 +114,19 @@ func initializeApplication(buildInfo handler.BuildInfo) (*Application, error) {
gitHubReleaseClient := repository.NewGitHubReleaseClient() gitHubReleaseClient := repository.NewGitHubReleaseClient()
serviceBuildInfo := provideServiceBuildInfo(buildInfo) serviceBuildInfo := provideServiceBuildInfo(buildInfo)
updateService := service.ProvideUpdateService(updateCache, gitHubReleaseClient, serviceBuildInfo) updateService := service.ProvideUpdateService(updateCache, gitHubReleaseClient, serviceBuildInfo)
systemHandler := handler.ProvideSystemHandler(updateService)
adminSubscriptionHandler := admin.NewSubscriptionHandler(subscriptionService)
adminUsageHandler := admin.NewUsageHandler(usageService, apiKeyService, adminService)
userAttributeDefinitionRepository := repository.NewUserAttributeDefinitionRepository(client)
userAttributeValueRepository := repository.NewUserAttributeValueRepository(client)
userAttributeService := service.NewUserAttributeService(userAttributeDefinitionRepository, userAttributeValueRepository)
userAttributeHandler := admin.NewUserAttributeHandler(userAttributeService)
adminHandlers := handler.ProvideAdminHandlers(dashboardHandler, adminUserHandler, groupHandler, accountHandler, oAuthHandler, openAIOAuthHandler, geminiOAuthHandler, antigravityOAuthHandler, proxyHandler, adminRedeemHandler, settingHandler, systemHandler, adminSubscriptionHandler, adminUsageHandler, userAttributeHandler)
pricingRemoteClient := repository.NewPricingRemoteClient() pricingRemoteClient := repository.NewPricingRemoteClient()
pricingService, err := service.ProvidePricingService(configConfig, pricingRemoteClient) pricingService, err := service.ProvidePricingService(configConfig, pricingRemoteClient)
if err != nil { if err != nil {
return nil, err return nil, err
} }
systemHandler := handler.ProvideSystemHandler(updateService)
adminSubscriptionHandler := admin.NewSubscriptionHandler(subscriptionService)
adminUsageHandler := admin.NewUsageHandler(usageService, apiKeyService, adminService)
adminHandlers := handler.ProvideAdminHandlers(dashboardHandler, adminUserHandler, groupHandler, accountHandler, oAuthHandler, openAIOAuthHandler, geminiOAuthHandler, antigravityOAuthHandler, proxyHandler, adminRedeemHandler, settingHandler, systemHandler, adminSubscriptionHandler, adminUsageHandler)
billingService := service.NewBillingService(configConfig, pricingService) billingService := service.NewBillingService(configConfig, pricingService)
identityCache := repository.NewIdentityCache(redisClient) identityCache := repository.NewIdentityCache(redisClient)
identityService := service.NewIdentityService(identityCache) identityService := service.NewIdentityService(identityCache)

View File

@ -25,6 +25,8 @@ import (
"github.com/Wei-Shaw/sub2api/ent/usagelog" "github.com/Wei-Shaw/sub2api/ent/usagelog"
"github.com/Wei-Shaw/sub2api/ent/user" "github.com/Wei-Shaw/sub2api/ent/user"
"github.com/Wei-Shaw/sub2api/ent/userallowedgroup" "github.com/Wei-Shaw/sub2api/ent/userallowedgroup"
"github.com/Wei-Shaw/sub2api/ent/userattributedefinition"
"github.com/Wei-Shaw/sub2api/ent/userattributevalue"
"github.com/Wei-Shaw/sub2api/ent/usersubscription" "github.com/Wei-Shaw/sub2api/ent/usersubscription"
stdsql "database/sql" stdsql "database/sql"
@ -55,6 +57,10 @@ type Client struct {
User *UserClient User *UserClient
// UserAllowedGroup is the client for interacting with the UserAllowedGroup builders. // UserAllowedGroup is the client for interacting with the UserAllowedGroup builders.
UserAllowedGroup *UserAllowedGroupClient UserAllowedGroup *UserAllowedGroupClient
// UserAttributeDefinition is the client for interacting with the UserAttributeDefinition builders.
UserAttributeDefinition *UserAttributeDefinitionClient
// UserAttributeValue is the client for interacting with the UserAttributeValue builders.
UserAttributeValue *UserAttributeValueClient
// UserSubscription is the client for interacting with the UserSubscription builders. // UserSubscription is the client for interacting with the UserSubscription builders.
UserSubscription *UserSubscriptionClient UserSubscription *UserSubscriptionClient
} }
@ -78,6 +84,8 @@ func (c *Client) init() {
c.UsageLog = NewUsageLogClient(c.config) c.UsageLog = NewUsageLogClient(c.config)
c.User = NewUserClient(c.config) c.User = NewUserClient(c.config)
c.UserAllowedGroup = NewUserAllowedGroupClient(c.config) c.UserAllowedGroup = NewUserAllowedGroupClient(c.config)
c.UserAttributeDefinition = NewUserAttributeDefinitionClient(c.config)
c.UserAttributeValue = NewUserAttributeValueClient(c.config)
c.UserSubscription = NewUserSubscriptionClient(c.config) c.UserSubscription = NewUserSubscriptionClient(c.config)
} }
@ -169,19 +177,21 @@ func (c *Client) Tx(ctx context.Context) (*Tx, error) {
cfg := c.config cfg := c.config
cfg.driver = tx cfg.driver = tx
return &Tx{ return &Tx{
ctx: ctx, ctx: ctx,
config: cfg, config: cfg,
Account: NewAccountClient(cfg), Account: NewAccountClient(cfg),
AccountGroup: NewAccountGroupClient(cfg), AccountGroup: NewAccountGroupClient(cfg),
ApiKey: NewApiKeyClient(cfg), ApiKey: NewApiKeyClient(cfg),
Group: NewGroupClient(cfg), Group: NewGroupClient(cfg),
Proxy: NewProxyClient(cfg), Proxy: NewProxyClient(cfg),
RedeemCode: NewRedeemCodeClient(cfg), RedeemCode: NewRedeemCodeClient(cfg),
Setting: NewSettingClient(cfg), Setting: NewSettingClient(cfg),
UsageLog: NewUsageLogClient(cfg), UsageLog: NewUsageLogClient(cfg),
User: NewUserClient(cfg), User: NewUserClient(cfg),
UserAllowedGroup: NewUserAllowedGroupClient(cfg), UserAllowedGroup: NewUserAllowedGroupClient(cfg),
UserSubscription: NewUserSubscriptionClient(cfg), UserAttributeDefinition: NewUserAttributeDefinitionClient(cfg),
UserAttributeValue: NewUserAttributeValueClient(cfg),
UserSubscription: NewUserSubscriptionClient(cfg),
}, nil }, nil
} }
@ -199,19 +209,21 @@ func (c *Client) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error)
cfg := c.config cfg := c.config
cfg.driver = &txDriver{tx: tx, drv: c.driver} cfg.driver = &txDriver{tx: tx, drv: c.driver}
return &Tx{ return &Tx{
ctx: ctx, ctx: ctx,
config: cfg, config: cfg,
Account: NewAccountClient(cfg), Account: NewAccountClient(cfg),
AccountGroup: NewAccountGroupClient(cfg), AccountGroup: NewAccountGroupClient(cfg),
ApiKey: NewApiKeyClient(cfg), ApiKey: NewApiKeyClient(cfg),
Group: NewGroupClient(cfg), Group: NewGroupClient(cfg),
Proxy: NewProxyClient(cfg), Proxy: NewProxyClient(cfg),
RedeemCode: NewRedeemCodeClient(cfg), RedeemCode: NewRedeemCodeClient(cfg),
Setting: NewSettingClient(cfg), Setting: NewSettingClient(cfg),
UsageLog: NewUsageLogClient(cfg), UsageLog: NewUsageLogClient(cfg),
User: NewUserClient(cfg), User: NewUserClient(cfg),
UserAllowedGroup: NewUserAllowedGroupClient(cfg), UserAllowedGroup: NewUserAllowedGroupClient(cfg),
UserSubscription: NewUserSubscriptionClient(cfg), UserAttributeDefinition: NewUserAttributeDefinitionClient(cfg),
UserAttributeValue: NewUserAttributeValueClient(cfg),
UserSubscription: NewUserSubscriptionClient(cfg),
}, nil }, nil
} }
@ -242,7 +254,8 @@ func (c *Client) Close() error {
func (c *Client) Use(hooks ...Hook) { func (c *Client) Use(hooks ...Hook) {
for _, n := range []interface{ Use(...Hook) }{ for _, n := range []interface{ Use(...Hook) }{
c.Account, c.AccountGroup, c.ApiKey, c.Group, c.Proxy, c.RedeemCode, c.Setting, c.Account, c.AccountGroup, c.ApiKey, c.Group, c.Proxy, c.RedeemCode, c.Setting,
c.UsageLog, c.User, c.UserAllowedGroup, c.UserSubscription, c.UsageLog, c.User, c.UserAllowedGroup, c.UserAttributeDefinition,
c.UserAttributeValue, c.UserSubscription,
} { } {
n.Use(hooks...) n.Use(hooks...)
} }
@ -253,7 +266,8 @@ func (c *Client) Use(hooks ...Hook) {
func (c *Client) Intercept(interceptors ...Interceptor) { func (c *Client) Intercept(interceptors ...Interceptor) {
for _, n := range []interface{ Intercept(...Interceptor) }{ for _, n := range []interface{ Intercept(...Interceptor) }{
c.Account, c.AccountGroup, c.ApiKey, c.Group, c.Proxy, c.RedeemCode, c.Setting, c.Account, c.AccountGroup, c.ApiKey, c.Group, c.Proxy, c.RedeemCode, c.Setting,
c.UsageLog, c.User, c.UserAllowedGroup, c.UserSubscription, c.UsageLog, c.User, c.UserAllowedGroup, c.UserAttributeDefinition,
c.UserAttributeValue, c.UserSubscription,
} { } {
n.Intercept(interceptors...) n.Intercept(interceptors...)
} }
@ -282,6 +296,10 @@ func (c *Client) Mutate(ctx context.Context, m Mutation) (Value, error) {
return c.User.mutate(ctx, m) return c.User.mutate(ctx, m)
case *UserAllowedGroupMutation: case *UserAllowedGroupMutation:
return c.UserAllowedGroup.mutate(ctx, m) return c.UserAllowedGroup.mutate(ctx, m)
case *UserAttributeDefinitionMutation:
return c.UserAttributeDefinition.mutate(ctx, m)
case *UserAttributeValueMutation:
return c.UserAttributeValue.mutate(ctx, m)
case *UserSubscriptionMutation: case *UserSubscriptionMutation:
return c.UserSubscription.mutate(ctx, m) return c.UserSubscription.mutate(ctx, m)
default: default:
@ -1916,6 +1934,22 @@ func (c *UserClient) QueryUsageLogs(_m *User) *UsageLogQuery {
return query return query
} }
// QueryAttributeValues queries the attribute_values edge of a User.
func (c *UserClient) QueryAttributeValues(_m *User) *UserAttributeValueQuery {
query := (&UserAttributeValueClient{config: c.config}).Query()
query.path = func(context.Context) (fromV *sql.Selector, _ error) {
id := _m.ID
step := sqlgraph.NewStep(
sqlgraph.From(user.Table, user.FieldID, id),
sqlgraph.To(userattributevalue.Table, userattributevalue.FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, user.AttributeValuesTable, user.AttributeValuesColumn),
)
fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step)
return fromV, nil
}
return query
}
// QueryUserAllowedGroups queries the user_allowed_groups edge of a User. // QueryUserAllowedGroups queries the user_allowed_groups edge of a User.
func (c *UserClient) QueryUserAllowedGroups(_m *User) *UserAllowedGroupQuery { func (c *UserClient) QueryUserAllowedGroups(_m *User) *UserAllowedGroupQuery {
query := (&UserAllowedGroupClient{config: c.config}).Query() query := (&UserAllowedGroupClient{config: c.config}).Query()
@ -2075,6 +2109,322 @@ func (c *UserAllowedGroupClient) mutate(ctx context.Context, m *UserAllowedGroup
} }
} }
// UserAttributeDefinitionClient is a client for the UserAttributeDefinition schema.
type UserAttributeDefinitionClient struct {
config
}
// NewUserAttributeDefinitionClient returns a client for the UserAttributeDefinition from the given config.
func NewUserAttributeDefinitionClient(c config) *UserAttributeDefinitionClient {
return &UserAttributeDefinitionClient{config: c}
}
// Use adds a list of mutation hooks to the hooks stack.
// A call to `Use(f, g, h)` equals to `userattributedefinition.Hooks(f(g(h())))`.
func (c *UserAttributeDefinitionClient) Use(hooks ...Hook) {
c.hooks.UserAttributeDefinition = append(c.hooks.UserAttributeDefinition, hooks...)
}
// Intercept adds a list of query interceptors to the interceptors stack.
// A call to `Intercept(f, g, h)` equals to `userattributedefinition.Intercept(f(g(h())))`.
func (c *UserAttributeDefinitionClient) Intercept(interceptors ...Interceptor) {
c.inters.UserAttributeDefinition = append(c.inters.UserAttributeDefinition, interceptors...)
}
// Create returns a builder for creating a UserAttributeDefinition entity.
func (c *UserAttributeDefinitionClient) Create() *UserAttributeDefinitionCreate {
mutation := newUserAttributeDefinitionMutation(c.config, OpCreate)
return &UserAttributeDefinitionCreate{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// CreateBulk returns a builder for creating a bulk of UserAttributeDefinition entities.
func (c *UserAttributeDefinitionClient) CreateBulk(builders ...*UserAttributeDefinitionCreate) *UserAttributeDefinitionCreateBulk {
return &UserAttributeDefinitionCreateBulk{config: c.config, builders: builders}
}
// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates
// a builder and applies setFunc on it.
func (c *UserAttributeDefinitionClient) MapCreateBulk(slice any, setFunc func(*UserAttributeDefinitionCreate, int)) *UserAttributeDefinitionCreateBulk {
rv := reflect.ValueOf(slice)
if rv.Kind() != reflect.Slice {
return &UserAttributeDefinitionCreateBulk{err: fmt.Errorf("calling to UserAttributeDefinitionClient.MapCreateBulk with wrong type %T, need slice", slice)}
}
builders := make([]*UserAttributeDefinitionCreate, rv.Len())
for i := 0; i < rv.Len(); i++ {
builders[i] = c.Create()
setFunc(builders[i], i)
}
return &UserAttributeDefinitionCreateBulk{config: c.config, builders: builders}
}
// Update returns an update builder for UserAttributeDefinition.
func (c *UserAttributeDefinitionClient) Update() *UserAttributeDefinitionUpdate {
mutation := newUserAttributeDefinitionMutation(c.config, OpUpdate)
return &UserAttributeDefinitionUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// UpdateOne returns an update builder for the given entity.
func (c *UserAttributeDefinitionClient) UpdateOne(_m *UserAttributeDefinition) *UserAttributeDefinitionUpdateOne {
mutation := newUserAttributeDefinitionMutation(c.config, OpUpdateOne, withUserAttributeDefinition(_m))
return &UserAttributeDefinitionUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// UpdateOneID returns an update builder for the given id.
func (c *UserAttributeDefinitionClient) UpdateOneID(id int64) *UserAttributeDefinitionUpdateOne {
mutation := newUserAttributeDefinitionMutation(c.config, OpUpdateOne, withUserAttributeDefinitionID(id))
return &UserAttributeDefinitionUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// Delete returns a delete builder for UserAttributeDefinition.
func (c *UserAttributeDefinitionClient) Delete() *UserAttributeDefinitionDelete {
mutation := newUserAttributeDefinitionMutation(c.config, OpDelete)
return &UserAttributeDefinitionDelete{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// DeleteOne returns a builder for deleting the given entity.
func (c *UserAttributeDefinitionClient) DeleteOne(_m *UserAttributeDefinition) *UserAttributeDefinitionDeleteOne {
return c.DeleteOneID(_m.ID)
}
// DeleteOneID returns a builder for deleting the given entity by its id.
func (c *UserAttributeDefinitionClient) DeleteOneID(id int64) *UserAttributeDefinitionDeleteOne {
builder := c.Delete().Where(userattributedefinition.ID(id))
builder.mutation.id = &id
builder.mutation.op = OpDeleteOne
return &UserAttributeDefinitionDeleteOne{builder}
}
// Query returns a query builder for UserAttributeDefinition.
func (c *UserAttributeDefinitionClient) Query() *UserAttributeDefinitionQuery {
return &UserAttributeDefinitionQuery{
config: c.config,
ctx: &QueryContext{Type: TypeUserAttributeDefinition},
inters: c.Interceptors(),
}
}
// Get returns a UserAttributeDefinition entity by its id.
func (c *UserAttributeDefinitionClient) Get(ctx context.Context, id int64) (*UserAttributeDefinition, error) {
return c.Query().Where(userattributedefinition.ID(id)).Only(ctx)
}
// GetX is like Get, but panics if an error occurs.
func (c *UserAttributeDefinitionClient) GetX(ctx context.Context, id int64) *UserAttributeDefinition {
obj, err := c.Get(ctx, id)
if err != nil {
panic(err)
}
return obj
}
// QueryValues queries the values edge of a UserAttributeDefinition.
func (c *UserAttributeDefinitionClient) QueryValues(_m *UserAttributeDefinition) *UserAttributeValueQuery {
query := (&UserAttributeValueClient{config: c.config}).Query()
query.path = func(context.Context) (fromV *sql.Selector, _ error) {
id := _m.ID
step := sqlgraph.NewStep(
sqlgraph.From(userattributedefinition.Table, userattributedefinition.FieldID, id),
sqlgraph.To(userattributevalue.Table, userattributevalue.FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, userattributedefinition.ValuesTable, userattributedefinition.ValuesColumn),
)
fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step)
return fromV, nil
}
return query
}
// Hooks returns the client hooks.
func (c *UserAttributeDefinitionClient) Hooks() []Hook {
hooks := c.hooks.UserAttributeDefinition
return append(hooks[:len(hooks):len(hooks)], userattributedefinition.Hooks[:]...)
}
// Interceptors returns the client interceptors.
func (c *UserAttributeDefinitionClient) Interceptors() []Interceptor {
inters := c.inters.UserAttributeDefinition
return append(inters[:len(inters):len(inters)], userattributedefinition.Interceptors[:]...)
}
func (c *UserAttributeDefinitionClient) mutate(ctx context.Context, m *UserAttributeDefinitionMutation) (Value, error) {
switch m.Op() {
case OpCreate:
return (&UserAttributeDefinitionCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
case OpUpdate:
return (&UserAttributeDefinitionUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
case OpUpdateOne:
return (&UserAttributeDefinitionUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
case OpDelete, OpDeleteOne:
return (&UserAttributeDefinitionDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx)
default:
return nil, fmt.Errorf("ent: unknown UserAttributeDefinition mutation op: %q", m.Op())
}
}
// UserAttributeValueClient is a client for the UserAttributeValue schema.
type UserAttributeValueClient struct {
config
}
// NewUserAttributeValueClient returns a client for the UserAttributeValue from the given config.
func NewUserAttributeValueClient(c config) *UserAttributeValueClient {
return &UserAttributeValueClient{config: c}
}
// Use adds a list of mutation hooks to the hooks stack.
// A call to `Use(f, g, h)` equals to `userattributevalue.Hooks(f(g(h())))`.
func (c *UserAttributeValueClient) Use(hooks ...Hook) {
c.hooks.UserAttributeValue = append(c.hooks.UserAttributeValue, hooks...)
}
// Intercept adds a list of query interceptors to the interceptors stack.
// A call to `Intercept(f, g, h)` equals to `userattributevalue.Intercept(f(g(h())))`.
func (c *UserAttributeValueClient) Intercept(interceptors ...Interceptor) {
c.inters.UserAttributeValue = append(c.inters.UserAttributeValue, interceptors...)
}
// Create returns a builder for creating a UserAttributeValue entity.
func (c *UserAttributeValueClient) Create() *UserAttributeValueCreate {
mutation := newUserAttributeValueMutation(c.config, OpCreate)
return &UserAttributeValueCreate{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// CreateBulk returns a builder for creating a bulk of UserAttributeValue entities.
func (c *UserAttributeValueClient) CreateBulk(builders ...*UserAttributeValueCreate) *UserAttributeValueCreateBulk {
return &UserAttributeValueCreateBulk{config: c.config, builders: builders}
}
// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates
// a builder and applies setFunc on it.
func (c *UserAttributeValueClient) MapCreateBulk(slice any, setFunc func(*UserAttributeValueCreate, int)) *UserAttributeValueCreateBulk {
rv := reflect.ValueOf(slice)
if rv.Kind() != reflect.Slice {
return &UserAttributeValueCreateBulk{err: fmt.Errorf("calling to UserAttributeValueClient.MapCreateBulk with wrong type %T, need slice", slice)}
}
builders := make([]*UserAttributeValueCreate, rv.Len())
for i := 0; i < rv.Len(); i++ {
builders[i] = c.Create()
setFunc(builders[i], i)
}
return &UserAttributeValueCreateBulk{config: c.config, builders: builders}
}
// Update returns an update builder for UserAttributeValue.
func (c *UserAttributeValueClient) Update() *UserAttributeValueUpdate {
mutation := newUserAttributeValueMutation(c.config, OpUpdate)
return &UserAttributeValueUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// UpdateOne returns an update builder for the given entity.
func (c *UserAttributeValueClient) UpdateOne(_m *UserAttributeValue) *UserAttributeValueUpdateOne {
mutation := newUserAttributeValueMutation(c.config, OpUpdateOne, withUserAttributeValue(_m))
return &UserAttributeValueUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// UpdateOneID returns an update builder for the given id.
func (c *UserAttributeValueClient) UpdateOneID(id int64) *UserAttributeValueUpdateOne {
mutation := newUserAttributeValueMutation(c.config, OpUpdateOne, withUserAttributeValueID(id))
return &UserAttributeValueUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// Delete returns a delete builder for UserAttributeValue.
func (c *UserAttributeValueClient) Delete() *UserAttributeValueDelete {
mutation := newUserAttributeValueMutation(c.config, OpDelete)
return &UserAttributeValueDelete{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// DeleteOne returns a builder for deleting the given entity.
func (c *UserAttributeValueClient) DeleteOne(_m *UserAttributeValue) *UserAttributeValueDeleteOne {
return c.DeleteOneID(_m.ID)
}
// DeleteOneID returns a builder for deleting the given entity by its id.
func (c *UserAttributeValueClient) DeleteOneID(id int64) *UserAttributeValueDeleteOne {
builder := c.Delete().Where(userattributevalue.ID(id))
builder.mutation.id = &id
builder.mutation.op = OpDeleteOne
return &UserAttributeValueDeleteOne{builder}
}
// Query returns a query builder for UserAttributeValue.
func (c *UserAttributeValueClient) Query() *UserAttributeValueQuery {
return &UserAttributeValueQuery{
config: c.config,
ctx: &QueryContext{Type: TypeUserAttributeValue},
inters: c.Interceptors(),
}
}
// Get returns a UserAttributeValue entity by its id.
func (c *UserAttributeValueClient) Get(ctx context.Context, id int64) (*UserAttributeValue, error) {
return c.Query().Where(userattributevalue.ID(id)).Only(ctx)
}
// GetX is like Get, but panics if an error occurs.
func (c *UserAttributeValueClient) GetX(ctx context.Context, id int64) *UserAttributeValue {
obj, err := c.Get(ctx, id)
if err != nil {
panic(err)
}
return obj
}
// QueryUser queries the user edge of a UserAttributeValue.
func (c *UserAttributeValueClient) QueryUser(_m *UserAttributeValue) *UserQuery {
query := (&UserClient{config: c.config}).Query()
query.path = func(context.Context) (fromV *sql.Selector, _ error) {
id := _m.ID
step := sqlgraph.NewStep(
sqlgraph.From(userattributevalue.Table, userattributevalue.FieldID, id),
sqlgraph.To(user.Table, user.FieldID),
sqlgraph.Edge(sqlgraph.M2O, true, userattributevalue.UserTable, userattributevalue.UserColumn),
)
fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step)
return fromV, nil
}
return query
}
// QueryDefinition queries the definition edge of a UserAttributeValue.
func (c *UserAttributeValueClient) QueryDefinition(_m *UserAttributeValue) *UserAttributeDefinitionQuery {
query := (&UserAttributeDefinitionClient{config: c.config}).Query()
query.path = func(context.Context) (fromV *sql.Selector, _ error) {
id := _m.ID
step := sqlgraph.NewStep(
sqlgraph.From(userattributevalue.Table, userattributevalue.FieldID, id),
sqlgraph.To(userattributedefinition.Table, userattributedefinition.FieldID),
sqlgraph.Edge(sqlgraph.M2O, true, userattributevalue.DefinitionTable, userattributevalue.DefinitionColumn),
)
fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step)
return fromV, nil
}
return query
}
// Hooks returns the client hooks.
func (c *UserAttributeValueClient) Hooks() []Hook {
return c.hooks.UserAttributeValue
}
// Interceptors returns the client interceptors.
func (c *UserAttributeValueClient) Interceptors() []Interceptor {
return c.inters.UserAttributeValue
}
func (c *UserAttributeValueClient) mutate(ctx context.Context, m *UserAttributeValueMutation) (Value, error) {
switch m.Op() {
case OpCreate:
return (&UserAttributeValueCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
case OpUpdate:
return (&UserAttributeValueUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
case OpUpdateOne:
return (&UserAttributeValueUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
case OpDelete, OpDeleteOne:
return (&UserAttributeValueDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx)
default:
return nil, fmt.Errorf("ent: unknown UserAttributeValue mutation op: %q", m.Op())
}
}
// UserSubscriptionClient is a client for the UserSubscription schema. // UserSubscriptionClient is a client for the UserSubscription schema.
type UserSubscriptionClient struct { type UserSubscriptionClient struct {
config config
@ -2278,11 +2628,13 @@ func (c *UserSubscriptionClient) mutate(ctx context.Context, m *UserSubscription
type ( type (
hooks struct { hooks struct {
Account, AccountGroup, ApiKey, Group, Proxy, RedeemCode, Setting, UsageLog, Account, AccountGroup, ApiKey, Group, Proxy, RedeemCode, Setting, UsageLog,
User, UserAllowedGroup, UserSubscription []ent.Hook User, UserAllowedGroup, UserAttributeDefinition, UserAttributeValue,
UserSubscription []ent.Hook
} }
inters struct { inters struct {
Account, AccountGroup, ApiKey, Group, Proxy, RedeemCode, Setting, UsageLog, Account, AccountGroup, ApiKey, Group, Proxy, RedeemCode, Setting, UsageLog,
User, UserAllowedGroup, UserSubscription []ent.Interceptor User, UserAllowedGroup, UserAttributeDefinition, UserAttributeValue,
UserSubscription []ent.Interceptor
} }
) )

View File

@ -22,6 +22,8 @@ import (
"github.com/Wei-Shaw/sub2api/ent/usagelog" "github.com/Wei-Shaw/sub2api/ent/usagelog"
"github.com/Wei-Shaw/sub2api/ent/user" "github.com/Wei-Shaw/sub2api/ent/user"
"github.com/Wei-Shaw/sub2api/ent/userallowedgroup" "github.com/Wei-Shaw/sub2api/ent/userallowedgroup"
"github.com/Wei-Shaw/sub2api/ent/userattributedefinition"
"github.com/Wei-Shaw/sub2api/ent/userattributevalue"
"github.com/Wei-Shaw/sub2api/ent/usersubscription" "github.com/Wei-Shaw/sub2api/ent/usersubscription"
) )
@ -83,17 +85,19 @@ var (
func checkColumn(t, c string) error { func checkColumn(t, c string) error {
initCheck.Do(func() { initCheck.Do(func() {
columnCheck = sql.NewColumnCheck(map[string]func(string) bool{ columnCheck = sql.NewColumnCheck(map[string]func(string) bool{
account.Table: account.ValidColumn, account.Table: account.ValidColumn,
accountgroup.Table: accountgroup.ValidColumn, accountgroup.Table: accountgroup.ValidColumn,
apikey.Table: apikey.ValidColumn, apikey.Table: apikey.ValidColumn,
group.Table: group.ValidColumn, group.Table: group.ValidColumn,
proxy.Table: proxy.ValidColumn, proxy.Table: proxy.ValidColumn,
redeemcode.Table: redeemcode.ValidColumn, redeemcode.Table: redeemcode.ValidColumn,
setting.Table: setting.ValidColumn, setting.Table: setting.ValidColumn,
usagelog.Table: usagelog.ValidColumn, usagelog.Table: usagelog.ValidColumn,
user.Table: user.ValidColumn, user.Table: user.ValidColumn,
userallowedgroup.Table: userallowedgroup.ValidColumn, userallowedgroup.Table: userallowedgroup.ValidColumn,
usersubscription.Table: usersubscription.ValidColumn, userattributedefinition.Table: userattributedefinition.ValidColumn,
userattributevalue.Table: userattributevalue.ValidColumn,
usersubscription.Table: usersubscription.ValidColumn,
}) })
}) })
return columnCheck(t, c) return columnCheck(t, c)

View File

@ -129,6 +129,30 @@ func (f UserAllowedGroupFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.V
return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.UserAllowedGroupMutation", m) return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.UserAllowedGroupMutation", m)
} }
// The UserAttributeDefinitionFunc type is an adapter to allow the use of ordinary
// function as UserAttributeDefinition mutator.
type UserAttributeDefinitionFunc func(context.Context, *ent.UserAttributeDefinitionMutation) (ent.Value, error)
// Mutate calls f(ctx, m).
func (f UserAttributeDefinitionFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) {
if mv, ok := m.(*ent.UserAttributeDefinitionMutation); ok {
return f(ctx, mv)
}
return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.UserAttributeDefinitionMutation", m)
}
// The UserAttributeValueFunc type is an adapter to allow the use of ordinary
// function as UserAttributeValue mutator.
type UserAttributeValueFunc func(context.Context, *ent.UserAttributeValueMutation) (ent.Value, error)
// Mutate calls f(ctx, m).
func (f UserAttributeValueFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) {
if mv, ok := m.(*ent.UserAttributeValueMutation); ok {
return f(ctx, mv)
}
return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.UserAttributeValueMutation", m)
}
// The UserSubscriptionFunc type is an adapter to allow the use of ordinary // The UserSubscriptionFunc type is an adapter to allow the use of ordinary
// function as UserSubscription mutator. // function as UserSubscription mutator.
type UserSubscriptionFunc func(context.Context, *ent.UserSubscriptionMutation) (ent.Value, error) type UserSubscriptionFunc func(context.Context, *ent.UserSubscriptionMutation) (ent.Value, error)

View File

@ -19,6 +19,8 @@ import (
"github.com/Wei-Shaw/sub2api/ent/usagelog" "github.com/Wei-Shaw/sub2api/ent/usagelog"
"github.com/Wei-Shaw/sub2api/ent/user" "github.com/Wei-Shaw/sub2api/ent/user"
"github.com/Wei-Shaw/sub2api/ent/userallowedgroup" "github.com/Wei-Shaw/sub2api/ent/userallowedgroup"
"github.com/Wei-Shaw/sub2api/ent/userattributedefinition"
"github.com/Wei-Shaw/sub2api/ent/userattributevalue"
"github.com/Wei-Shaw/sub2api/ent/usersubscription" "github.com/Wei-Shaw/sub2api/ent/usersubscription"
) )
@ -348,6 +350,60 @@ func (f TraverseUserAllowedGroup) Traverse(ctx context.Context, q ent.Query) err
return fmt.Errorf("unexpected query type %T. expect *ent.UserAllowedGroupQuery", q) return fmt.Errorf("unexpected query type %T. expect *ent.UserAllowedGroupQuery", q)
} }
// The UserAttributeDefinitionFunc type is an adapter to allow the use of ordinary function as a Querier.
type UserAttributeDefinitionFunc func(context.Context, *ent.UserAttributeDefinitionQuery) (ent.Value, error)
// Query calls f(ctx, q).
func (f UserAttributeDefinitionFunc) Query(ctx context.Context, q ent.Query) (ent.Value, error) {
if q, ok := q.(*ent.UserAttributeDefinitionQuery); ok {
return f(ctx, q)
}
return nil, fmt.Errorf("unexpected query type %T. expect *ent.UserAttributeDefinitionQuery", q)
}
// The TraverseUserAttributeDefinition type is an adapter to allow the use of ordinary function as Traverser.
type TraverseUserAttributeDefinition func(context.Context, *ent.UserAttributeDefinitionQuery) error
// Intercept is a dummy implementation of Intercept that returns the next Querier in the pipeline.
func (f TraverseUserAttributeDefinition) Intercept(next ent.Querier) ent.Querier {
return next
}
// Traverse calls f(ctx, q).
func (f TraverseUserAttributeDefinition) Traverse(ctx context.Context, q ent.Query) error {
if q, ok := q.(*ent.UserAttributeDefinitionQuery); ok {
return f(ctx, q)
}
return fmt.Errorf("unexpected query type %T. expect *ent.UserAttributeDefinitionQuery", q)
}
// The UserAttributeValueFunc type is an adapter to allow the use of ordinary function as a Querier.
type UserAttributeValueFunc func(context.Context, *ent.UserAttributeValueQuery) (ent.Value, error)
// Query calls f(ctx, q).
func (f UserAttributeValueFunc) Query(ctx context.Context, q ent.Query) (ent.Value, error) {
if q, ok := q.(*ent.UserAttributeValueQuery); ok {
return f(ctx, q)
}
return nil, fmt.Errorf("unexpected query type %T. expect *ent.UserAttributeValueQuery", q)
}
// The TraverseUserAttributeValue type is an adapter to allow the use of ordinary function as Traverser.
type TraverseUserAttributeValue func(context.Context, *ent.UserAttributeValueQuery) error
// Intercept is a dummy implementation of Intercept that returns the next Querier in the pipeline.
func (f TraverseUserAttributeValue) Intercept(next ent.Querier) ent.Querier {
return next
}
// Traverse calls f(ctx, q).
func (f TraverseUserAttributeValue) Traverse(ctx context.Context, q ent.Query) error {
if q, ok := q.(*ent.UserAttributeValueQuery); ok {
return f(ctx, q)
}
return fmt.Errorf("unexpected query type %T. expect *ent.UserAttributeValueQuery", q)
}
// The UserSubscriptionFunc type is an adapter to allow the use of ordinary function as a Querier. // The UserSubscriptionFunc type is an adapter to allow the use of ordinary function as a Querier.
type UserSubscriptionFunc func(context.Context, *ent.UserSubscriptionQuery) (ent.Value, error) type UserSubscriptionFunc func(context.Context, *ent.UserSubscriptionQuery) (ent.Value, error)
@ -398,6 +454,10 @@ func NewQuery(q ent.Query) (Query, error) {
return &query[*ent.UserQuery, predicate.User, user.OrderOption]{typ: ent.TypeUser, tq: q}, nil return &query[*ent.UserQuery, predicate.User, user.OrderOption]{typ: ent.TypeUser, tq: q}, nil
case *ent.UserAllowedGroupQuery: case *ent.UserAllowedGroupQuery:
return &query[*ent.UserAllowedGroupQuery, predicate.UserAllowedGroup, userallowedgroup.OrderOption]{typ: ent.TypeUserAllowedGroup, tq: q}, nil return &query[*ent.UserAllowedGroupQuery, predicate.UserAllowedGroup, userallowedgroup.OrderOption]{typ: ent.TypeUserAllowedGroup, tq: q}, nil
case *ent.UserAttributeDefinitionQuery:
return &query[*ent.UserAttributeDefinitionQuery, predicate.UserAttributeDefinition, userattributedefinition.OrderOption]{typ: ent.TypeUserAttributeDefinition, tq: q}, nil
case *ent.UserAttributeValueQuery:
return &query[*ent.UserAttributeValueQuery, predicate.UserAttributeValue, userattributevalue.OrderOption]{typ: ent.TypeUserAttributeValue, tq: q}, nil
case *ent.UserSubscriptionQuery: case *ent.UserSubscriptionQuery:
return &query[*ent.UserSubscriptionQuery, predicate.UserSubscription, usersubscription.OrderOption]{typ: ent.TypeUserSubscription, tq: q}, nil return &query[*ent.UserSubscriptionQuery, predicate.UserSubscription, usersubscription.OrderOption]{typ: ent.TypeUserSubscription, tq: q}, nil
default: default:

View File

@ -477,7 +477,6 @@ var (
{Name: "concurrency", Type: field.TypeInt, Default: 5}, {Name: "concurrency", Type: field.TypeInt, Default: 5},
{Name: "status", Type: field.TypeString, Size: 20, Default: "active"}, {Name: "status", Type: field.TypeString, Size: 20, Default: "active"},
{Name: "username", Type: field.TypeString, Size: 100, Default: ""}, {Name: "username", Type: field.TypeString, Size: 100, Default: ""},
{Name: "wechat", Type: field.TypeString, Size: 100, Default: ""},
{Name: "notes", Type: field.TypeString, Default: "", SchemaType: map[string]string{"postgres": "text"}}, {Name: "notes", Type: field.TypeString, Default: "", SchemaType: map[string]string{"postgres": "text"}},
} }
// UsersTable holds the schema information for the "users" table. // UsersTable holds the schema information for the "users" table.
@ -531,6 +530,92 @@ var (
}, },
}, },
} }
// UserAttributeDefinitionsColumns holds the columns for the "user_attribute_definitions" table.
UserAttributeDefinitionsColumns = []*schema.Column{
{Name: "id", Type: field.TypeInt64, Increment: true},
{Name: "created_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
{Name: "updated_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
{Name: "deleted_at", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{"postgres": "timestamptz"}},
{Name: "key", Type: field.TypeString, Size: 100},
{Name: "name", Type: field.TypeString, Size: 255},
{Name: "description", Type: field.TypeString, Default: "", SchemaType: map[string]string{"postgres": "text"}},
{Name: "type", Type: field.TypeString, Size: 20},
{Name: "options", Type: field.TypeJSON, SchemaType: map[string]string{"postgres": "jsonb"}},
{Name: "required", Type: field.TypeBool, Default: false},
{Name: "validation", Type: field.TypeJSON, SchemaType: map[string]string{"postgres": "jsonb"}},
{Name: "placeholder", Type: field.TypeString, Size: 255, Default: ""},
{Name: "display_order", Type: field.TypeInt, Default: 0},
{Name: "enabled", Type: field.TypeBool, Default: true},
}
// UserAttributeDefinitionsTable holds the schema information for the "user_attribute_definitions" table.
UserAttributeDefinitionsTable = &schema.Table{
Name: "user_attribute_definitions",
Columns: UserAttributeDefinitionsColumns,
PrimaryKey: []*schema.Column{UserAttributeDefinitionsColumns[0]},
Indexes: []*schema.Index{
{
Name: "userattributedefinition_key",
Unique: false,
Columns: []*schema.Column{UserAttributeDefinitionsColumns[4]},
},
{
Name: "userattributedefinition_enabled",
Unique: false,
Columns: []*schema.Column{UserAttributeDefinitionsColumns[13]},
},
{
Name: "userattributedefinition_display_order",
Unique: false,
Columns: []*schema.Column{UserAttributeDefinitionsColumns[12]},
},
{
Name: "userattributedefinition_deleted_at",
Unique: false,
Columns: []*schema.Column{UserAttributeDefinitionsColumns[3]},
},
},
}
// UserAttributeValuesColumns holds the columns for the "user_attribute_values" table.
UserAttributeValuesColumns = []*schema.Column{
{Name: "id", Type: field.TypeInt64, Increment: true},
{Name: "created_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
{Name: "updated_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
{Name: "value", Type: field.TypeString, Size: 2147483647, Default: ""},
{Name: "user_id", Type: field.TypeInt64},
{Name: "attribute_id", Type: field.TypeInt64},
}
// UserAttributeValuesTable holds the schema information for the "user_attribute_values" table.
UserAttributeValuesTable = &schema.Table{
Name: "user_attribute_values",
Columns: UserAttributeValuesColumns,
PrimaryKey: []*schema.Column{UserAttributeValuesColumns[0]},
ForeignKeys: []*schema.ForeignKey{
{
Symbol: "user_attribute_values_users_attribute_values",
Columns: []*schema.Column{UserAttributeValuesColumns[4]},
RefColumns: []*schema.Column{UsersColumns[0]},
OnDelete: schema.NoAction,
},
{
Symbol: "user_attribute_values_user_attribute_definitions_values",
Columns: []*schema.Column{UserAttributeValuesColumns[5]},
RefColumns: []*schema.Column{UserAttributeDefinitionsColumns[0]},
OnDelete: schema.NoAction,
},
},
Indexes: []*schema.Index{
{
Name: "userattributevalue_user_id_attribute_id",
Unique: true,
Columns: []*schema.Column{UserAttributeValuesColumns[4], UserAttributeValuesColumns[5]},
},
{
Name: "userattributevalue_attribute_id",
Unique: false,
Columns: []*schema.Column{UserAttributeValuesColumns[5]},
},
},
}
// UserSubscriptionsColumns holds the columns for the "user_subscriptions" table. // UserSubscriptionsColumns holds the columns for the "user_subscriptions" table.
UserSubscriptionsColumns = []*schema.Column{ UserSubscriptionsColumns = []*schema.Column{
{Name: "id", Type: field.TypeInt64, Increment: true}, {Name: "id", Type: field.TypeInt64, Increment: true},
@ -627,6 +712,8 @@ var (
UsageLogsTable, UsageLogsTable,
UsersTable, UsersTable,
UserAllowedGroupsTable, UserAllowedGroupsTable,
UserAttributeDefinitionsTable,
UserAttributeValuesTable,
UserSubscriptionsTable, UserSubscriptionsTable,
} }
) )
@ -676,6 +763,14 @@ func init() {
UserAllowedGroupsTable.Annotation = &entsql.Annotation{ UserAllowedGroupsTable.Annotation = &entsql.Annotation{
Table: "user_allowed_groups", Table: "user_allowed_groups",
} }
UserAttributeDefinitionsTable.Annotation = &entsql.Annotation{
Table: "user_attribute_definitions",
}
UserAttributeValuesTable.ForeignKeys[0].RefTable = UsersTable
UserAttributeValuesTable.ForeignKeys[1].RefTable = UserAttributeDefinitionsTable
UserAttributeValuesTable.Annotation = &entsql.Annotation{
Table: "user_attribute_values",
}
UserSubscriptionsTable.ForeignKeys[0].RefTable = GroupsTable UserSubscriptionsTable.ForeignKeys[0].RefTable = GroupsTable
UserSubscriptionsTable.ForeignKeys[1].RefTable = UsersTable UserSubscriptionsTable.ForeignKeys[1].RefTable = UsersTable
UserSubscriptionsTable.ForeignKeys[2].RefTable = UsersTable UserSubscriptionsTable.ForeignKeys[2].RefTable = UsersTable

File diff suppressed because it is too large Load Diff

View File

@ -36,5 +36,11 @@ type User func(*sql.Selector)
// UserAllowedGroup is the predicate function for userallowedgroup builders. // UserAllowedGroup is the predicate function for userallowedgroup builders.
type UserAllowedGroup func(*sql.Selector) type UserAllowedGroup func(*sql.Selector)
// UserAttributeDefinition is the predicate function for userattributedefinition builders.
type UserAttributeDefinition func(*sql.Selector)
// UserAttributeValue is the predicate function for userattributevalue builders.
type UserAttributeValue func(*sql.Selector)
// UserSubscription is the predicate function for usersubscription builders. // UserSubscription is the predicate function for usersubscription builders.
type UserSubscription func(*sql.Selector) type UserSubscription func(*sql.Selector)

View File

@ -16,6 +16,8 @@ import (
"github.com/Wei-Shaw/sub2api/ent/usagelog" "github.com/Wei-Shaw/sub2api/ent/usagelog"
"github.com/Wei-Shaw/sub2api/ent/user" "github.com/Wei-Shaw/sub2api/ent/user"
"github.com/Wei-Shaw/sub2api/ent/userallowedgroup" "github.com/Wei-Shaw/sub2api/ent/userallowedgroup"
"github.com/Wei-Shaw/sub2api/ent/userattributedefinition"
"github.com/Wei-Shaw/sub2api/ent/userattributevalue"
"github.com/Wei-Shaw/sub2api/ent/usersubscription" "github.com/Wei-Shaw/sub2api/ent/usersubscription"
) )
@ -604,14 +606,8 @@ func init() {
user.DefaultUsername = userDescUsername.Default.(string) user.DefaultUsername = userDescUsername.Default.(string)
// user.UsernameValidator is a validator for the "username" field. It is called by the builders before save. // user.UsernameValidator is a validator for the "username" field. It is called by the builders before save.
user.UsernameValidator = userDescUsername.Validators[0].(func(string) error) user.UsernameValidator = userDescUsername.Validators[0].(func(string) error)
// userDescWechat is the schema descriptor for wechat field.
userDescWechat := userFields[7].Descriptor()
// user.DefaultWechat holds the default value on creation for the wechat field.
user.DefaultWechat = userDescWechat.Default.(string)
// user.WechatValidator is a validator for the "wechat" field. It is called by the builders before save.
user.WechatValidator = userDescWechat.Validators[0].(func(string) error)
// userDescNotes is the schema descriptor for notes field. // userDescNotes is the schema descriptor for notes field.
userDescNotes := userFields[8].Descriptor() userDescNotes := userFields[7].Descriptor()
// user.DefaultNotes holds the default value on creation for the notes field. // user.DefaultNotes holds the default value on creation for the notes field.
user.DefaultNotes = userDescNotes.Default.(string) user.DefaultNotes = userDescNotes.Default.(string)
userallowedgroupFields := schema.UserAllowedGroup{}.Fields() userallowedgroupFields := schema.UserAllowedGroup{}.Fields()
@ -620,6 +616,128 @@ func init() {
userallowedgroupDescCreatedAt := userallowedgroupFields[2].Descriptor() userallowedgroupDescCreatedAt := userallowedgroupFields[2].Descriptor()
// userallowedgroup.DefaultCreatedAt holds the default value on creation for the created_at field. // userallowedgroup.DefaultCreatedAt holds the default value on creation for the created_at field.
userallowedgroup.DefaultCreatedAt = userallowedgroupDescCreatedAt.Default.(func() time.Time) userallowedgroup.DefaultCreatedAt = userallowedgroupDescCreatedAt.Default.(func() time.Time)
userattributedefinitionMixin := schema.UserAttributeDefinition{}.Mixin()
userattributedefinitionMixinHooks1 := userattributedefinitionMixin[1].Hooks()
userattributedefinition.Hooks[0] = userattributedefinitionMixinHooks1[0]
userattributedefinitionMixinInters1 := userattributedefinitionMixin[1].Interceptors()
userattributedefinition.Interceptors[0] = userattributedefinitionMixinInters1[0]
userattributedefinitionMixinFields0 := userattributedefinitionMixin[0].Fields()
_ = userattributedefinitionMixinFields0
userattributedefinitionFields := schema.UserAttributeDefinition{}.Fields()
_ = userattributedefinitionFields
// userattributedefinitionDescCreatedAt is the schema descriptor for created_at field.
userattributedefinitionDescCreatedAt := userattributedefinitionMixinFields0[0].Descriptor()
// userattributedefinition.DefaultCreatedAt holds the default value on creation for the created_at field.
userattributedefinition.DefaultCreatedAt = userattributedefinitionDescCreatedAt.Default.(func() time.Time)
// userattributedefinitionDescUpdatedAt is the schema descriptor for updated_at field.
userattributedefinitionDescUpdatedAt := userattributedefinitionMixinFields0[1].Descriptor()
// userattributedefinition.DefaultUpdatedAt holds the default value on creation for the updated_at field.
userattributedefinition.DefaultUpdatedAt = userattributedefinitionDescUpdatedAt.Default.(func() time.Time)
// userattributedefinition.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field.
userattributedefinition.UpdateDefaultUpdatedAt = userattributedefinitionDescUpdatedAt.UpdateDefault.(func() time.Time)
// userattributedefinitionDescKey is the schema descriptor for key field.
userattributedefinitionDescKey := userattributedefinitionFields[0].Descriptor()
// userattributedefinition.KeyValidator is a validator for the "key" field. It is called by the builders before save.
userattributedefinition.KeyValidator = func() func(string) error {
validators := userattributedefinitionDescKey.Validators
fns := [...]func(string) error{
validators[0].(func(string) error),
validators[1].(func(string) error),
}
return func(key string) error {
for _, fn := range fns {
if err := fn(key); err != nil {
return err
}
}
return nil
}
}()
// userattributedefinitionDescName is the schema descriptor for name field.
userattributedefinitionDescName := userattributedefinitionFields[1].Descriptor()
// userattributedefinition.NameValidator is a validator for the "name" field. It is called by the builders before save.
userattributedefinition.NameValidator = func() func(string) error {
validators := userattributedefinitionDescName.Validators
fns := [...]func(string) error{
validators[0].(func(string) error),
validators[1].(func(string) error),
}
return func(name string) error {
for _, fn := range fns {
if err := fn(name); err != nil {
return err
}
}
return nil
}
}()
// userattributedefinitionDescDescription is the schema descriptor for description field.
userattributedefinitionDescDescription := userattributedefinitionFields[2].Descriptor()
// userattributedefinition.DefaultDescription holds the default value on creation for the description field.
userattributedefinition.DefaultDescription = userattributedefinitionDescDescription.Default.(string)
// userattributedefinitionDescType is the schema descriptor for type field.
userattributedefinitionDescType := userattributedefinitionFields[3].Descriptor()
// userattributedefinition.TypeValidator is a validator for the "type" field. It is called by the builders before save.
userattributedefinition.TypeValidator = func() func(string) error {
validators := userattributedefinitionDescType.Validators
fns := [...]func(string) error{
validators[0].(func(string) error),
validators[1].(func(string) error),
}
return func(_type string) error {
for _, fn := range fns {
if err := fn(_type); err != nil {
return err
}
}
return nil
}
}()
// userattributedefinitionDescOptions is the schema descriptor for options field.
userattributedefinitionDescOptions := userattributedefinitionFields[4].Descriptor()
// userattributedefinition.DefaultOptions holds the default value on creation for the options field.
userattributedefinition.DefaultOptions = userattributedefinitionDescOptions.Default.([]map[string]interface{})
// userattributedefinitionDescRequired is the schema descriptor for required field.
userattributedefinitionDescRequired := userattributedefinitionFields[5].Descriptor()
// userattributedefinition.DefaultRequired holds the default value on creation for the required field.
userattributedefinition.DefaultRequired = userattributedefinitionDescRequired.Default.(bool)
// userattributedefinitionDescValidation is the schema descriptor for validation field.
userattributedefinitionDescValidation := userattributedefinitionFields[6].Descriptor()
// userattributedefinition.DefaultValidation holds the default value on creation for the validation field.
userattributedefinition.DefaultValidation = userattributedefinitionDescValidation.Default.(map[string]interface{})
// userattributedefinitionDescPlaceholder is the schema descriptor for placeholder field.
userattributedefinitionDescPlaceholder := userattributedefinitionFields[7].Descriptor()
// userattributedefinition.DefaultPlaceholder holds the default value on creation for the placeholder field.
userattributedefinition.DefaultPlaceholder = userattributedefinitionDescPlaceholder.Default.(string)
// userattributedefinition.PlaceholderValidator is a validator for the "placeholder" field. It is called by the builders before save.
userattributedefinition.PlaceholderValidator = userattributedefinitionDescPlaceholder.Validators[0].(func(string) error)
// userattributedefinitionDescDisplayOrder is the schema descriptor for display_order field.
userattributedefinitionDescDisplayOrder := userattributedefinitionFields[8].Descriptor()
// userattributedefinition.DefaultDisplayOrder holds the default value on creation for the display_order field.
userattributedefinition.DefaultDisplayOrder = userattributedefinitionDescDisplayOrder.Default.(int)
// userattributedefinitionDescEnabled is the schema descriptor for enabled field.
userattributedefinitionDescEnabled := userattributedefinitionFields[9].Descriptor()
// userattributedefinition.DefaultEnabled holds the default value on creation for the enabled field.
userattributedefinition.DefaultEnabled = userattributedefinitionDescEnabled.Default.(bool)
userattributevalueMixin := schema.UserAttributeValue{}.Mixin()
userattributevalueMixinFields0 := userattributevalueMixin[0].Fields()
_ = userattributevalueMixinFields0
userattributevalueFields := schema.UserAttributeValue{}.Fields()
_ = userattributevalueFields
// userattributevalueDescCreatedAt is the schema descriptor for created_at field.
userattributevalueDescCreatedAt := userattributevalueMixinFields0[0].Descriptor()
// userattributevalue.DefaultCreatedAt holds the default value on creation for the created_at field.
userattributevalue.DefaultCreatedAt = userattributevalueDescCreatedAt.Default.(func() time.Time)
// userattributevalueDescUpdatedAt is the schema descriptor for updated_at field.
userattributevalueDescUpdatedAt := userattributevalueMixinFields0[1].Descriptor()
// userattributevalue.DefaultUpdatedAt holds the default value on creation for the updated_at field.
userattributevalue.DefaultUpdatedAt = userattributevalueDescUpdatedAt.Default.(func() time.Time)
// userattributevalue.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field.
userattributevalue.UpdateDefaultUpdatedAt = userattributevalueDescUpdatedAt.UpdateDefault.(func() time.Time)
// userattributevalueDescValue is the schema descriptor for value field.
userattributevalueDescValue := userattributevalueFields[2].Descriptor()
// userattributevalue.DefaultValue holds the default value on creation for the value field.
userattributevalue.DefaultValue = userattributevalueDescValue.Default.(string)
usersubscriptionMixin := schema.UserSubscription{}.Mixin() usersubscriptionMixin := schema.UserSubscription{}.Mixin()
usersubscriptionMixinHooks1 := usersubscriptionMixin[1].Hooks() usersubscriptionMixinHooks1 := usersubscriptionMixin[1].Hooks()
usersubscription.Hooks[0] = usersubscriptionMixinHooks1[0] usersubscription.Hooks[0] = usersubscriptionMixinHooks1[0]

View File

@ -57,9 +57,7 @@ func (User) Fields() []ent.Field {
field.String("username"). field.String("username").
MaxLen(100). MaxLen(100).
Default(""), Default(""),
field.String("wechat"). // wechat field migrated to user_attribute_values (see migration 019)
MaxLen(100).
Default(""),
field.String("notes"). field.String("notes").
SchemaType(map[string]string{dialect.Postgres: "text"}). SchemaType(map[string]string{dialect.Postgres: "text"}).
Default(""), Default(""),
@ -75,6 +73,7 @@ func (User) Edges() []ent.Edge {
edge.To("allowed_groups", Group.Type). edge.To("allowed_groups", Group.Type).
Through("user_allowed_groups", UserAllowedGroup.Type), Through("user_allowed_groups", UserAllowedGroup.Type),
edge.To("usage_logs", UsageLog.Type), edge.To("usage_logs", UsageLog.Type),
edge.To("attribute_values", UserAttributeValue.Type),
} }
} }

View File

@ -0,0 +1,109 @@
package schema
import (
"github.com/Wei-Shaw/sub2api/ent/schema/mixins"
"entgo.io/ent"
"entgo.io/ent/dialect"
"entgo.io/ent/dialect/entsql"
"entgo.io/ent/schema"
"entgo.io/ent/schema/edge"
"entgo.io/ent/schema/field"
"entgo.io/ent/schema/index"
)
// UserAttributeDefinition holds the schema definition for custom user attributes.
//
// This entity defines the metadata for user attributes, such as:
// - Attribute key (unique identifier like "company_name")
// - Display name shown in forms
// - Field type (text, number, select, etc.)
// - Validation rules
// - Whether the field is required or enabled
type UserAttributeDefinition struct {
ent.Schema
}
func (UserAttributeDefinition) Annotations() []schema.Annotation {
return []schema.Annotation{
entsql.Annotation{Table: "user_attribute_definitions"},
}
}
func (UserAttributeDefinition) Mixin() []ent.Mixin {
return []ent.Mixin{
mixins.TimeMixin{},
mixins.SoftDeleteMixin{},
}
}
func (UserAttributeDefinition) Fields() []ent.Field {
return []ent.Field{
// key: Unique identifier for the attribute (e.g., "company_name")
// Used for programmatic reference
field.String("key").
MaxLen(100).
NotEmpty(),
// name: Display name shown in forms (e.g., "Company Name")
field.String("name").
MaxLen(255).
NotEmpty(),
// description: Optional description/help text for the attribute
field.String("description").
SchemaType(map[string]string{dialect.Postgres: "text"}).
Default(""),
// type: Attribute type - text, textarea, number, email, url, date, select, multi_select
field.String("type").
MaxLen(20).
NotEmpty(),
// options: Select options for select/multi_select types (stored as JSONB)
// Format: [{"value": "xxx", "label": "XXX"}, ...]
field.JSON("options", []map[string]any{}).
Default([]map[string]any{}).
SchemaType(map[string]string{dialect.Postgres: "jsonb"}),
// required: Whether this attribute is required when editing a user
field.Bool("required").
Default(false),
// validation: Validation rules for the attribute value (stored as JSONB)
// Format: {"min_length": 1, "max_length": 100, "min": 0, "max": 100, "pattern": "^[a-z]+$", "message": "..."}
field.JSON("validation", map[string]any{}).
Default(map[string]any{}).
SchemaType(map[string]string{dialect.Postgres: "jsonb"}),
// placeholder: Placeholder text shown in input fields
field.String("placeholder").
MaxLen(255).
Default(""),
// display_order: Order in which attributes are displayed (lower = first)
field.Int("display_order").
Default(0),
// enabled: Whether this attribute is active and shown in forms
field.Bool("enabled").
Default(true),
}
}
func (UserAttributeDefinition) Edges() []ent.Edge {
return []ent.Edge{
// values: All user values for this attribute definition
edge.To("values", UserAttributeValue.Type),
}
}
func (UserAttributeDefinition) Indexes() []ent.Index {
return []ent.Index{
// Partial unique index on key (WHERE deleted_at IS NULL) via migration
index.Fields("key"),
index.Fields("enabled"),
index.Fields("display_order"),
index.Fields("deleted_at"),
}
}

View File

@ -0,0 +1,74 @@
package schema
import (
"github.com/Wei-Shaw/sub2api/ent/schema/mixins"
"entgo.io/ent"
"entgo.io/ent/dialect/entsql"
"entgo.io/ent/schema"
"entgo.io/ent/schema/edge"
"entgo.io/ent/schema/field"
"entgo.io/ent/schema/index"
)
// UserAttributeValue holds a user's value for a specific attribute.
//
// This entity stores the actual values that users have for each attribute definition.
// Values are stored as strings and converted to the appropriate type by the application.
type UserAttributeValue struct {
ent.Schema
}
func (UserAttributeValue) Annotations() []schema.Annotation {
return []schema.Annotation{
entsql.Annotation{Table: "user_attribute_values"},
}
}
func (UserAttributeValue) Mixin() []ent.Mixin {
return []ent.Mixin{
// Only use TimeMixin, no soft delete - values are hard deleted
mixins.TimeMixin{},
}
}
func (UserAttributeValue) Fields() []ent.Field {
return []ent.Field{
// user_id: References the user this value belongs to
field.Int64("user_id"),
// attribute_id: References the attribute definition
field.Int64("attribute_id"),
// value: The actual value stored as a string
// For multi_select, this is a JSON array string
field.Text("value").
Default(""),
}
}
func (UserAttributeValue) Edges() []ent.Edge {
return []ent.Edge{
// user: The user who owns this attribute value
edge.From("user", User.Type).
Ref("attribute_values").
Field("user_id").
Required().
Unique(),
// definition: The attribute definition this value is for
edge.From("definition", UserAttributeDefinition.Type).
Ref("values").
Field("attribute_id").
Required().
Unique(),
}
}
func (UserAttributeValue) Indexes() []ent.Index {
return []ent.Index{
// Unique index on (user_id, attribute_id)
index.Fields("user_id", "attribute_id").Unique(),
index.Fields("attribute_id"),
}
}

View File

@ -34,6 +34,10 @@ type Tx struct {
User *UserClient User *UserClient
// UserAllowedGroup is the client for interacting with the UserAllowedGroup builders. // UserAllowedGroup is the client for interacting with the UserAllowedGroup builders.
UserAllowedGroup *UserAllowedGroupClient UserAllowedGroup *UserAllowedGroupClient
// UserAttributeDefinition is the client for interacting with the UserAttributeDefinition builders.
UserAttributeDefinition *UserAttributeDefinitionClient
// UserAttributeValue is the client for interacting with the UserAttributeValue builders.
UserAttributeValue *UserAttributeValueClient
// UserSubscription is the client for interacting with the UserSubscription builders. // UserSubscription is the client for interacting with the UserSubscription builders.
UserSubscription *UserSubscriptionClient UserSubscription *UserSubscriptionClient
@ -177,6 +181,8 @@ func (tx *Tx) init() {
tx.UsageLog = NewUsageLogClient(tx.config) tx.UsageLog = NewUsageLogClient(tx.config)
tx.User = NewUserClient(tx.config) tx.User = NewUserClient(tx.config)
tx.UserAllowedGroup = NewUserAllowedGroupClient(tx.config) tx.UserAllowedGroup = NewUserAllowedGroupClient(tx.config)
tx.UserAttributeDefinition = NewUserAttributeDefinitionClient(tx.config)
tx.UserAttributeValue = NewUserAttributeValueClient(tx.config)
tx.UserSubscription = NewUserSubscriptionClient(tx.config) tx.UserSubscription = NewUserSubscriptionClient(tx.config)
} }

View File

@ -37,8 +37,6 @@ type User struct {
Status string `json:"status,omitempty"` Status string `json:"status,omitempty"`
// Username holds the value of the "username" field. // Username holds the value of the "username" field.
Username string `json:"username,omitempty"` Username string `json:"username,omitempty"`
// Wechat holds the value of the "wechat" field.
Wechat string `json:"wechat,omitempty"`
// Notes holds the value of the "notes" field. // Notes holds the value of the "notes" field.
Notes string `json:"notes,omitempty"` Notes string `json:"notes,omitempty"`
// Edges holds the relations/edges for other nodes in the graph. // Edges holds the relations/edges for other nodes in the graph.
@ -61,11 +59,13 @@ type UserEdges struct {
AllowedGroups []*Group `json:"allowed_groups,omitempty"` AllowedGroups []*Group `json:"allowed_groups,omitempty"`
// UsageLogs holds the value of the usage_logs edge. // UsageLogs holds the value of the usage_logs edge.
UsageLogs []*UsageLog `json:"usage_logs,omitempty"` UsageLogs []*UsageLog `json:"usage_logs,omitempty"`
// AttributeValues holds the value of the attribute_values edge.
AttributeValues []*UserAttributeValue `json:"attribute_values,omitempty"`
// UserAllowedGroups holds the value of the user_allowed_groups edge. // UserAllowedGroups holds the value of the user_allowed_groups edge.
UserAllowedGroups []*UserAllowedGroup `json:"user_allowed_groups,omitempty"` UserAllowedGroups []*UserAllowedGroup `json:"user_allowed_groups,omitempty"`
// loadedTypes holds the information for reporting if a // loadedTypes holds the information for reporting if a
// type was loaded (or requested) in eager-loading or not. // type was loaded (or requested) in eager-loading or not.
loadedTypes [7]bool loadedTypes [8]bool
} }
// APIKeysOrErr returns the APIKeys value or an error if the edge // APIKeysOrErr returns the APIKeys value or an error if the edge
@ -122,10 +122,19 @@ func (e UserEdges) UsageLogsOrErr() ([]*UsageLog, error) {
return nil, &NotLoadedError{edge: "usage_logs"} return nil, &NotLoadedError{edge: "usage_logs"}
} }
// AttributeValuesOrErr returns the AttributeValues value or an error if the edge
// was not loaded in eager-loading.
func (e UserEdges) AttributeValuesOrErr() ([]*UserAttributeValue, error) {
if e.loadedTypes[6] {
return e.AttributeValues, nil
}
return nil, &NotLoadedError{edge: "attribute_values"}
}
// UserAllowedGroupsOrErr returns the UserAllowedGroups value or an error if the edge // UserAllowedGroupsOrErr returns the UserAllowedGroups value or an error if the edge
// was not loaded in eager-loading. // was not loaded in eager-loading.
func (e UserEdges) UserAllowedGroupsOrErr() ([]*UserAllowedGroup, error) { func (e UserEdges) UserAllowedGroupsOrErr() ([]*UserAllowedGroup, error) {
if e.loadedTypes[6] { if e.loadedTypes[7] {
return e.UserAllowedGroups, nil return e.UserAllowedGroups, nil
} }
return nil, &NotLoadedError{edge: "user_allowed_groups"} return nil, &NotLoadedError{edge: "user_allowed_groups"}
@ -140,7 +149,7 @@ func (*User) scanValues(columns []string) ([]any, error) {
values[i] = new(sql.NullFloat64) values[i] = new(sql.NullFloat64)
case user.FieldID, user.FieldConcurrency: case user.FieldID, user.FieldConcurrency:
values[i] = new(sql.NullInt64) values[i] = new(sql.NullInt64)
case user.FieldEmail, user.FieldPasswordHash, user.FieldRole, user.FieldStatus, user.FieldUsername, user.FieldWechat, user.FieldNotes: case user.FieldEmail, user.FieldPasswordHash, user.FieldRole, user.FieldStatus, user.FieldUsername, user.FieldNotes:
values[i] = new(sql.NullString) values[i] = new(sql.NullString)
case user.FieldCreatedAt, user.FieldUpdatedAt, user.FieldDeletedAt: case user.FieldCreatedAt, user.FieldUpdatedAt, user.FieldDeletedAt:
values[i] = new(sql.NullTime) values[i] = new(sql.NullTime)
@ -226,12 +235,6 @@ func (_m *User) assignValues(columns []string, values []any) error {
} else if value.Valid { } else if value.Valid {
_m.Username = value.String _m.Username = value.String
} }
case user.FieldWechat:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field wechat", values[i])
} else if value.Valid {
_m.Wechat = value.String
}
case user.FieldNotes: case user.FieldNotes:
if value, ok := values[i].(*sql.NullString); !ok { if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field notes", values[i]) return fmt.Errorf("unexpected type %T for field notes", values[i])
@ -281,6 +284,11 @@ func (_m *User) QueryUsageLogs() *UsageLogQuery {
return NewUserClient(_m.config).QueryUsageLogs(_m) return NewUserClient(_m.config).QueryUsageLogs(_m)
} }
// QueryAttributeValues queries the "attribute_values" edge of the User entity.
func (_m *User) QueryAttributeValues() *UserAttributeValueQuery {
return NewUserClient(_m.config).QueryAttributeValues(_m)
}
// QueryUserAllowedGroups queries the "user_allowed_groups" edge of the User entity. // QueryUserAllowedGroups queries the "user_allowed_groups" edge of the User entity.
func (_m *User) QueryUserAllowedGroups() *UserAllowedGroupQuery { func (_m *User) QueryUserAllowedGroups() *UserAllowedGroupQuery {
return NewUserClient(_m.config).QueryUserAllowedGroups(_m) return NewUserClient(_m.config).QueryUserAllowedGroups(_m)
@ -341,9 +349,6 @@ func (_m *User) String() string {
builder.WriteString("username=") builder.WriteString("username=")
builder.WriteString(_m.Username) builder.WriteString(_m.Username)
builder.WriteString(", ") builder.WriteString(", ")
builder.WriteString("wechat=")
builder.WriteString(_m.Wechat)
builder.WriteString(", ")
builder.WriteString("notes=") builder.WriteString("notes=")
builder.WriteString(_m.Notes) builder.WriteString(_m.Notes)
builder.WriteByte(')') builder.WriteByte(')')

View File

@ -35,8 +35,6 @@ const (
FieldStatus = "status" FieldStatus = "status"
// FieldUsername holds the string denoting the username field in the database. // FieldUsername holds the string denoting the username field in the database.
FieldUsername = "username" FieldUsername = "username"
// FieldWechat holds the string denoting the wechat field in the database.
FieldWechat = "wechat"
// FieldNotes holds the string denoting the notes field in the database. // FieldNotes holds the string denoting the notes field in the database.
FieldNotes = "notes" FieldNotes = "notes"
// EdgeAPIKeys holds the string denoting the api_keys edge name in mutations. // EdgeAPIKeys holds the string denoting the api_keys edge name in mutations.
@ -51,6 +49,8 @@ const (
EdgeAllowedGroups = "allowed_groups" EdgeAllowedGroups = "allowed_groups"
// EdgeUsageLogs holds the string denoting the usage_logs edge name in mutations. // EdgeUsageLogs holds the string denoting the usage_logs edge name in mutations.
EdgeUsageLogs = "usage_logs" EdgeUsageLogs = "usage_logs"
// EdgeAttributeValues holds the string denoting the attribute_values edge name in mutations.
EdgeAttributeValues = "attribute_values"
// EdgeUserAllowedGroups holds the string denoting the user_allowed_groups edge name in mutations. // EdgeUserAllowedGroups holds the string denoting the user_allowed_groups edge name in mutations.
EdgeUserAllowedGroups = "user_allowed_groups" EdgeUserAllowedGroups = "user_allowed_groups"
// Table holds the table name of the user in the database. // Table holds the table name of the user in the database.
@ -95,6 +95,13 @@ const (
UsageLogsInverseTable = "usage_logs" UsageLogsInverseTable = "usage_logs"
// UsageLogsColumn is the table column denoting the usage_logs relation/edge. // UsageLogsColumn is the table column denoting the usage_logs relation/edge.
UsageLogsColumn = "user_id" UsageLogsColumn = "user_id"
// AttributeValuesTable is the table that holds the attribute_values relation/edge.
AttributeValuesTable = "user_attribute_values"
// AttributeValuesInverseTable is the table name for the UserAttributeValue entity.
// It exists in this package in order to avoid circular dependency with the "userattributevalue" package.
AttributeValuesInverseTable = "user_attribute_values"
// AttributeValuesColumn is the table column denoting the attribute_values relation/edge.
AttributeValuesColumn = "user_id"
// UserAllowedGroupsTable is the table that holds the user_allowed_groups relation/edge. // UserAllowedGroupsTable is the table that holds the user_allowed_groups relation/edge.
UserAllowedGroupsTable = "user_allowed_groups" UserAllowedGroupsTable = "user_allowed_groups"
// UserAllowedGroupsInverseTable is the table name for the UserAllowedGroup entity. // UserAllowedGroupsInverseTable is the table name for the UserAllowedGroup entity.
@ -117,7 +124,6 @@ var Columns = []string{
FieldConcurrency, FieldConcurrency,
FieldStatus, FieldStatus,
FieldUsername, FieldUsername,
FieldWechat,
FieldNotes, FieldNotes,
} }
@ -171,10 +177,6 @@ var (
DefaultUsername string DefaultUsername string
// UsernameValidator is a validator for the "username" field. It is called by the builders before save. // UsernameValidator is a validator for the "username" field. It is called by the builders before save.
UsernameValidator func(string) error UsernameValidator func(string) error
// DefaultWechat holds the default value on creation for the "wechat" field.
DefaultWechat string
// WechatValidator is a validator for the "wechat" field. It is called by the builders before save.
WechatValidator func(string) error
// DefaultNotes holds the default value on creation for the "notes" field. // DefaultNotes holds the default value on creation for the "notes" field.
DefaultNotes string DefaultNotes string
) )
@ -237,11 +239,6 @@ func ByUsername(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldUsername, opts...).ToFunc() return sql.OrderByField(FieldUsername, opts...).ToFunc()
} }
// ByWechat orders the results by the wechat field.
func ByWechat(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldWechat, opts...).ToFunc()
}
// ByNotes orders the results by the notes field. // ByNotes orders the results by the notes field.
func ByNotes(opts ...sql.OrderTermOption) OrderOption { func ByNotes(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldNotes, opts...).ToFunc() return sql.OrderByField(FieldNotes, opts...).ToFunc()
@ -331,6 +328,20 @@ func ByUsageLogs(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption {
} }
} }
// ByAttributeValuesCount orders the results by attribute_values count.
func ByAttributeValuesCount(opts ...sql.OrderTermOption) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborsCount(s, newAttributeValuesStep(), opts...)
}
}
// ByAttributeValues orders the results by attribute_values terms.
func ByAttributeValues(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborTerms(s, newAttributeValuesStep(), append([]sql.OrderTerm{term}, terms...)...)
}
}
// ByUserAllowedGroupsCount orders the results by user_allowed_groups count. // ByUserAllowedGroupsCount orders the results by user_allowed_groups count.
func ByUserAllowedGroupsCount(opts ...sql.OrderTermOption) OrderOption { func ByUserAllowedGroupsCount(opts ...sql.OrderTermOption) OrderOption {
return func(s *sql.Selector) { return func(s *sql.Selector) {
@ -386,6 +397,13 @@ func newUsageLogsStep() *sqlgraph.Step {
sqlgraph.Edge(sqlgraph.O2M, false, UsageLogsTable, UsageLogsColumn), sqlgraph.Edge(sqlgraph.O2M, false, UsageLogsTable, UsageLogsColumn),
) )
} }
func newAttributeValuesStep() *sqlgraph.Step {
return sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(AttributeValuesInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, AttributeValuesTable, AttributeValuesColumn),
)
}
func newUserAllowedGroupsStep() *sqlgraph.Step { func newUserAllowedGroupsStep() *sqlgraph.Step {
return sqlgraph.NewStep( return sqlgraph.NewStep(
sqlgraph.From(Table, FieldID), sqlgraph.From(Table, FieldID),

View File

@ -105,11 +105,6 @@ func Username(v string) predicate.User {
return predicate.User(sql.FieldEQ(FieldUsername, v)) return predicate.User(sql.FieldEQ(FieldUsername, v))
} }
// Wechat applies equality check predicate on the "wechat" field. It's identical to WechatEQ.
func Wechat(v string) predicate.User {
return predicate.User(sql.FieldEQ(FieldWechat, v))
}
// Notes applies equality check predicate on the "notes" field. It's identical to NotesEQ. // Notes applies equality check predicate on the "notes" field. It's identical to NotesEQ.
func Notes(v string) predicate.User { func Notes(v string) predicate.User {
return predicate.User(sql.FieldEQ(FieldNotes, v)) return predicate.User(sql.FieldEQ(FieldNotes, v))
@ -650,71 +645,6 @@ func UsernameContainsFold(v string) predicate.User {
return predicate.User(sql.FieldContainsFold(FieldUsername, v)) return predicate.User(sql.FieldContainsFold(FieldUsername, v))
} }
// WechatEQ applies the EQ predicate on the "wechat" field.
func WechatEQ(v string) predicate.User {
return predicate.User(sql.FieldEQ(FieldWechat, v))
}
// WechatNEQ applies the NEQ predicate on the "wechat" field.
func WechatNEQ(v string) predicate.User {
return predicate.User(sql.FieldNEQ(FieldWechat, v))
}
// WechatIn applies the In predicate on the "wechat" field.
func WechatIn(vs ...string) predicate.User {
return predicate.User(sql.FieldIn(FieldWechat, vs...))
}
// WechatNotIn applies the NotIn predicate on the "wechat" field.
func WechatNotIn(vs ...string) predicate.User {
return predicate.User(sql.FieldNotIn(FieldWechat, vs...))
}
// WechatGT applies the GT predicate on the "wechat" field.
func WechatGT(v string) predicate.User {
return predicate.User(sql.FieldGT(FieldWechat, v))
}
// WechatGTE applies the GTE predicate on the "wechat" field.
func WechatGTE(v string) predicate.User {
return predicate.User(sql.FieldGTE(FieldWechat, v))
}
// WechatLT applies the LT predicate on the "wechat" field.
func WechatLT(v string) predicate.User {
return predicate.User(sql.FieldLT(FieldWechat, v))
}
// WechatLTE applies the LTE predicate on the "wechat" field.
func WechatLTE(v string) predicate.User {
return predicate.User(sql.FieldLTE(FieldWechat, v))
}
// WechatContains applies the Contains predicate on the "wechat" field.
func WechatContains(v string) predicate.User {
return predicate.User(sql.FieldContains(FieldWechat, v))
}
// WechatHasPrefix applies the HasPrefix predicate on the "wechat" field.
func WechatHasPrefix(v string) predicate.User {
return predicate.User(sql.FieldHasPrefix(FieldWechat, v))
}
// WechatHasSuffix applies the HasSuffix predicate on the "wechat" field.
func WechatHasSuffix(v string) predicate.User {
return predicate.User(sql.FieldHasSuffix(FieldWechat, v))
}
// WechatEqualFold applies the EqualFold predicate on the "wechat" field.
func WechatEqualFold(v string) predicate.User {
return predicate.User(sql.FieldEqualFold(FieldWechat, v))
}
// WechatContainsFold applies the ContainsFold predicate on the "wechat" field.
func WechatContainsFold(v string) predicate.User {
return predicate.User(sql.FieldContainsFold(FieldWechat, v))
}
// NotesEQ applies the EQ predicate on the "notes" field. // NotesEQ applies the EQ predicate on the "notes" field.
func NotesEQ(v string) predicate.User { func NotesEQ(v string) predicate.User {
return predicate.User(sql.FieldEQ(FieldNotes, v)) return predicate.User(sql.FieldEQ(FieldNotes, v))
@ -918,6 +848,29 @@ func HasUsageLogsWith(preds ...predicate.UsageLog) predicate.User {
}) })
} }
// HasAttributeValues applies the HasEdge predicate on the "attribute_values" edge.
func HasAttributeValues() predicate.User {
return predicate.User(func(s *sql.Selector) {
step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, AttributeValuesTable, AttributeValuesColumn),
)
sqlgraph.HasNeighbors(s, step)
})
}
// HasAttributeValuesWith applies the HasEdge predicate on the "attribute_values" edge with a given conditions (other predicates).
func HasAttributeValuesWith(preds ...predicate.UserAttributeValue) predicate.User {
return predicate.User(func(s *sql.Selector) {
step := newAttributeValuesStep()
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
for _, p := range preds {
p(s)
}
})
})
}
// HasUserAllowedGroups applies the HasEdge predicate on the "user_allowed_groups" edge. // HasUserAllowedGroups applies the HasEdge predicate on the "user_allowed_groups" edge.
func HasUserAllowedGroups() predicate.User { func HasUserAllowedGroups() predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(func(s *sql.Selector) {

View File

@ -16,6 +16,7 @@ import (
"github.com/Wei-Shaw/sub2api/ent/redeemcode" "github.com/Wei-Shaw/sub2api/ent/redeemcode"
"github.com/Wei-Shaw/sub2api/ent/usagelog" "github.com/Wei-Shaw/sub2api/ent/usagelog"
"github.com/Wei-Shaw/sub2api/ent/user" "github.com/Wei-Shaw/sub2api/ent/user"
"github.com/Wei-Shaw/sub2api/ent/userattributevalue"
"github.com/Wei-Shaw/sub2api/ent/usersubscription" "github.com/Wei-Shaw/sub2api/ent/usersubscription"
) )
@ -151,20 +152,6 @@ func (_c *UserCreate) SetNillableUsername(v *string) *UserCreate {
return _c return _c
} }
// SetWechat sets the "wechat" field.
func (_c *UserCreate) SetWechat(v string) *UserCreate {
_c.mutation.SetWechat(v)
return _c
}
// SetNillableWechat sets the "wechat" field if the given value is not nil.
func (_c *UserCreate) SetNillableWechat(v *string) *UserCreate {
if v != nil {
_c.SetWechat(*v)
}
return _c
}
// SetNotes sets the "notes" field. // SetNotes sets the "notes" field.
func (_c *UserCreate) SetNotes(v string) *UserCreate { func (_c *UserCreate) SetNotes(v string) *UserCreate {
_c.mutation.SetNotes(v) _c.mutation.SetNotes(v)
@ -269,6 +256,21 @@ func (_c *UserCreate) AddUsageLogs(v ...*UsageLog) *UserCreate {
return _c.AddUsageLogIDs(ids...) return _c.AddUsageLogIDs(ids...)
} }
// AddAttributeValueIDs adds the "attribute_values" edge to the UserAttributeValue entity by IDs.
func (_c *UserCreate) AddAttributeValueIDs(ids ...int64) *UserCreate {
_c.mutation.AddAttributeValueIDs(ids...)
return _c
}
// AddAttributeValues adds the "attribute_values" edges to the UserAttributeValue entity.
func (_c *UserCreate) AddAttributeValues(v ...*UserAttributeValue) *UserCreate {
ids := make([]int64, len(v))
for i := range v {
ids[i] = v[i].ID
}
return _c.AddAttributeValueIDs(ids...)
}
// Mutation returns the UserMutation object of the builder. // Mutation returns the UserMutation object of the builder.
func (_c *UserCreate) Mutation() *UserMutation { func (_c *UserCreate) Mutation() *UserMutation {
return _c.mutation return _c.mutation
@ -340,10 +342,6 @@ func (_c *UserCreate) defaults() error {
v := user.DefaultUsername v := user.DefaultUsername
_c.mutation.SetUsername(v) _c.mutation.SetUsername(v)
} }
if _, ok := _c.mutation.Wechat(); !ok {
v := user.DefaultWechat
_c.mutation.SetWechat(v)
}
if _, ok := _c.mutation.Notes(); !ok { if _, ok := _c.mutation.Notes(); !ok {
v := user.DefaultNotes v := user.DefaultNotes
_c.mutation.SetNotes(v) _c.mutation.SetNotes(v)
@ -405,14 +403,6 @@ func (_c *UserCreate) check() error {
return &ValidationError{Name: "username", err: fmt.Errorf(`ent: validator failed for field "User.username": %w`, err)} return &ValidationError{Name: "username", err: fmt.Errorf(`ent: validator failed for field "User.username": %w`, err)}
} }
} }
if _, ok := _c.mutation.Wechat(); !ok {
return &ValidationError{Name: "wechat", err: errors.New(`ent: missing required field "User.wechat"`)}
}
if v, ok := _c.mutation.Wechat(); ok {
if err := user.WechatValidator(v); err != nil {
return &ValidationError{Name: "wechat", err: fmt.Errorf(`ent: validator failed for field "User.wechat": %w`, err)}
}
}
if _, ok := _c.mutation.Notes(); !ok { if _, ok := _c.mutation.Notes(); !ok {
return &ValidationError{Name: "notes", err: errors.New(`ent: missing required field "User.notes"`)} return &ValidationError{Name: "notes", err: errors.New(`ent: missing required field "User.notes"`)}
} }
@ -483,10 +473,6 @@ func (_c *UserCreate) createSpec() (*User, *sqlgraph.CreateSpec) {
_spec.SetField(user.FieldUsername, field.TypeString, value) _spec.SetField(user.FieldUsername, field.TypeString, value)
_node.Username = value _node.Username = value
} }
if value, ok := _c.mutation.Wechat(); ok {
_spec.SetField(user.FieldWechat, field.TypeString, value)
_node.Wechat = value
}
if value, ok := _c.mutation.Notes(); ok { if value, ok := _c.mutation.Notes(); ok {
_spec.SetField(user.FieldNotes, field.TypeString, value) _spec.SetField(user.FieldNotes, field.TypeString, value)
_node.Notes = value _node.Notes = value
@ -591,6 +577,22 @@ func (_c *UserCreate) createSpec() (*User, *sqlgraph.CreateSpec) {
} }
_spec.Edges = append(_spec.Edges, edge) _spec.Edges = append(_spec.Edges, edge)
} }
if nodes := _c.mutation.AttributeValuesIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M,
Inverse: false,
Table: user.AttributeValuesTable,
Columns: []string{user.AttributeValuesColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(userattributevalue.FieldID, field.TypeInt64),
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges = append(_spec.Edges, edge)
}
return _node, _spec return _node, _spec
} }
@ -769,18 +771,6 @@ func (u *UserUpsert) UpdateUsername() *UserUpsert {
return u return u
} }
// SetWechat sets the "wechat" field.
func (u *UserUpsert) SetWechat(v string) *UserUpsert {
u.Set(user.FieldWechat, v)
return u
}
// UpdateWechat sets the "wechat" field to the value that was provided on create.
func (u *UserUpsert) UpdateWechat() *UserUpsert {
u.SetExcluded(user.FieldWechat)
return u
}
// SetNotes sets the "notes" field. // SetNotes sets the "notes" field.
func (u *UserUpsert) SetNotes(v string) *UserUpsert { func (u *UserUpsert) SetNotes(v string) *UserUpsert {
u.Set(user.FieldNotes, v) u.Set(user.FieldNotes, v)
@ -985,20 +975,6 @@ func (u *UserUpsertOne) UpdateUsername() *UserUpsertOne {
}) })
} }
// SetWechat sets the "wechat" field.
func (u *UserUpsertOne) SetWechat(v string) *UserUpsertOne {
return u.Update(func(s *UserUpsert) {
s.SetWechat(v)
})
}
// UpdateWechat sets the "wechat" field to the value that was provided on create.
func (u *UserUpsertOne) UpdateWechat() *UserUpsertOne {
return u.Update(func(s *UserUpsert) {
s.UpdateWechat()
})
}
// SetNotes sets the "notes" field. // SetNotes sets the "notes" field.
func (u *UserUpsertOne) SetNotes(v string) *UserUpsertOne { func (u *UserUpsertOne) SetNotes(v string) *UserUpsertOne {
return u.Update(func(s *UserUpsert) { return u.Update(func(s *UserUpsert) {
@ -1371,20 +1347,6 @@ func (u *UserUpsertBulk) UpdateUsername() *UserUpsertBulk {
}) })
} }
// SetWechat sets the "wechat" field.
func (u *UserUpsertBulk) SetWechat(v string) *UserUpsertBulk {
return u.Update(func(s *UserUpsert) {
s.SetWechat(v)
})
}
// UpdateWechat sets the "wechat" field to the value that was provided on create.
func (u *UserUpsertBulk) UpdateWechat() *UserUpsertBulk {
return u.Update(func(s *UserUpsert) {
s.UpdateWechat()
})
}
// SetNotes sets the "notes" field. // SetNotes sets the "notes" field.
func (u *UserUpsertBulk) SetNotes(v string) *UserUpsertBulk { func (u *UserUpsertBulk) SetNotes(v string) *UserUpsertBulk {
return u.Update(func(s *UserUpsert) { return u.Update(func(s *UserUpsert) {

View File

@ -19,6 +19,7 @@ import (
"github.com/Wei-Shaw/sub2api/ent/usagelog" "github.com/Wei-Shaw/sub2api/ent/usagelog"
"github.com/Wei-Shaw/sub2api/ent/user" "github.com/Wei-Shaw/sub2api/ent/user"
"github.com/Wei-Shaw/sub2api/ent/userallowedgroup" "github.com/Wei-Shaw/sub2api/ent/userallowedgroup"
"github.com/Wei-Shaw/sub2api/ent/userattributevalue"
"github.com/Wei-Shaw/sub2api/ent/usersubscription" "github.com/Wei-Shaw/sub2api/ent/usersubscription"
) )
@ -35,6 +36,7 @@ type UserQuery struct {
withAssignedSubscriptions *UserSubscriptionQuery withAssignedSubscriptions *UserSubscriptionQuery
withAllowedGroups *GroupQuery withAllowedGroups *GroupQuery
withUsageLogs *UsageLogQuery withUsageLogs *UsageLogQuery
withAttributeValues *UserAttributeValueQuery
withUserAllowedGroups *UserAllowedGroupQuery withUserAllowedGroups *UserAllowedGroupQuery
// intermediate query (i.e. traversal path). // intermediate query (i.e. traversal path).
sql *sql.Selector sql *sql.Selector
@ -204,6 +206,28 @@ func (_q *UserQuery) QueryUsageLogs() *UsageLogQuery {
return query return query
} }
// QueryAttributeValues chains the current query on the "attribute_values" edge.
func (_q *UserQuery) QueryAttributeValues() *UserAttributeValueQuery {
query := (&UserAttributeValueClient{config: _q.config}).Query()
query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
if err := _q.prepareQuery(ctx); err != nil {
return nil, err
}
selector := _q.sqlQuery(ctx)
if err := selector.Err(); err != nil {
return nil, err
}
step := sqlgraph.NewStep(
sqlgraph.From(user.Table, user.FieldID, selector),
sqlgraph.To(userattributevalue.Table, userattributevalue.FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, user.AttributeValuesTable, user.AttributeValuesColumn),
)
fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step)
return fromU, nil
}
return query
}
// QueryUserAllowedGroups chains the current query on the "user_allowed_groups" edge. // QueryUserAllowedGroups chains the current query on the "user_allowed_groups" edge.
func (_q *UserQuery) QueryUserAllowedGroups() *UserAllowedGroupQuery { func (_q *UserQuery) QueryUserAllowedGroups() *UserAllowedGroupQuery {
query := (&UserAllowedGroupClient{config: _q.config}).Query() query := (&UserAllowedGroupClient{config: _q.config}).Query()
@ -424,6 +448,7 @@ func (_q *UserQuery) Clone() *UserQuery {
withAssignedSubscriptions: _q.withAssignedSubscriptions.Clone(), withAssignedSubscriptions: _q.withAssignedSubscriptions.Clone(),
withAllowedGroups: _q.withAllowedGroups.Clone(), withAllowedGroups: _q.withAllowedGroups.Clone(),
withUsageLogs: _q.withUsageLogs.Clone(), withUsageLogs: _q.withUsageLogs.Clone(),
withAttributeValues: _q.withAttributeValues.Clone(),
withUserAllowedGroups: _q.withUserAllowedGroups.Clone(), withUserAllowedGroups: _q.withUserAllowedGroups.Clone(),
// clone intermediate query. // clone intermediate query.
sql: _q.sql.Clone(), sql: _q.sql.Clone(),
@ -497,6 +522,17 @@ func (_q *UserQuery) WithUsageLogs(opts ...func(*UsageLogQuery)) *UserQuery {
return _q return _q
} }
// WithAttributeValues tells the query-builder to eager-load the nodes that are connected to
// the "attribute_values" edge. The optional arguments are used to configure the query builder of the edge.
func (_q *UserQuery) WithAttributeValues(opts ...func(*UserAttributeValueQuery)) *UserQuery {
query := (&UserAttributeValueClient{config: _q.config}).Query()
for _, opt := range opts {
opt(query)
}
_q.withAttributeValues = query
return _q
}
// WithUserAllowedGroups tells the query-builder to eager-load the nodes that are connected to // WithUserAllowedGroups tells the query-builder to eager-load the nodes that are connected to
// the "user_allowed_groups" edge. The optional arguments are used to configure the query builder of the edge. // the "user_allowed_groups" edge. The optional arguments are used to configure the query builder of the edge.
func (_q *UserQuery) WithUserAllowedGroups(opts ...func(*UserAllowedGroupQuery)) *UserQuery { func (_q *UserQuery) WithUserAllowedGroups(opts ...func(*UserAllowedGroupQuery)) *UserQuery {
@ -586,13 +622,14 @@ func (_q *UserQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*User, e
var ( var (
nodes = []*User{} nodes = []*User{}
_spec = _q.querySpec() _spec = _q.querySpec()
loadedTypes = [7]bool{ loadedTypes = [8]bool{
_q.withAPIKeys != nil, _q.withAPIKeys != nil,
_q.withRedeemCodes != nil, _q.withRedeemCodes != nil,
_q.withSubscriptions != nil, _q.withSubscriptions != nil,
_q.withAssignedSubscriptions != nil, _q.withAssignedSubscriptions != nil,
_q.withAllowedGroups != nil, _q.withAllowedGroups != nil,
_q.withUsageLogs != nil, _q.withUsageLogs != nil,
_q.withAttributeValues != nil,
_q.withUserAllowedGroups != nil, _q.withUserAllowedGroups != nil,
} }
) )
@ -658,6 +695,13 @@ func (_q *UserQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*User, e
return nil, err return nil, err
} }
} }
if query := _q.withAttributeValues; query != nil {
if err := _q.loadAttributeValues(ctx, query, nodes,
func(n *User) { n.Edges.AttributeValues = []*UserAttributeValue{} },
func(n *User, e *UserAttributeValue) { n.Edges.AttributeValues = append(n.Edges.AttributeValues, e) }); err != nil {
return nil, err
}
}
if query := _q.withUserAllowedGroups; query != nil { if query := _q.withUserAllowedGroups; query != nil {
if err := _q.loadUserAllowedGroups(ctx, query, nodes, if err := _q.loadUserAllowedGroups(ctx, query, nodes,
func(n *User) { n.Edges.UserAllowedGroups = []*UserAllowedGroup{} }, func(n *User) { n.Edges.UserAllowedGroups = []*UserAllowedGroup{} },
@ -885,6 +929,36 @@ func (_q *UserQuery) loadUsageLogs(ctx context.Context, query *UsageLogQuery, no
} }
return nil return nil
} }
func (_q *UserQuery) loadAttributeValues(ctx context.Context, query *UserAttributeValueQuery, nodes []*User, init func(*User), assign func(*User, *UserAttributeValue)) error {
fks := make([]driver.Value, 0, len(nodes))
nodeids := make(map[int64]*User)
for i := range nodes {
fks = append(fks, nodes[i].ID)
nodeids[nodes[i].ID] = nodes[i]
if init != nil {
init(nodes[i])
}
}
if len(query.ctx.Fields) > 0 {
query.ctx.AppendFieldOnce(userattributevalue.FieldUserID)
}
query.Where(predicate.UserAttributeValue(func(s *sql.Selector) {
s.Where(sql.InValues(s.C(user.AttributeValuesColumn), fks...))
}))
neighbors, err := query.All(ctx)
if err != nil {
return err
}
for _, n := range neighbors {
fk := n.UserID
node, ok := nodeids[fk]
if !ok {
return fmt.Errorf(`unexpected referenced foreign-key "user_id" returned %v for node %v`, fk, n.ID)
}
assign(node, n)
}
return nil
}
func (_q *UserQuery) loadUserAllowedGroups(ctx context.Context, query *UserAllowedGroupQuery, nodes []*User, init func(*User), assign func(*User, *UserAllowedGroup)) error { func (_q *UserQuery) loadUserAllowedGroups(ctx context.Context, query *UserAllowedGroupQuery, nodes []*User, init func(*User), assign func(*User, *UserAllowedGroup)) error {
fks := make([]driver.Value, 0, len(nodes)) fks := make([]driver.Value, 0, len(nodes))
nodeids := make(map[int64]*User) nodeids := make(map[int64]*User)

View File

@ -17,6 +17,7 @@ import (
"github.com/Wei-Shaw/sub2api/ent/redeemcode" "github.com/Wei-Shaw/sub2api/ent/redeemcode"
"github.com/Wei-Shaw/sub2api/ent/usagelog" "github.com/Wei-Shaw/sub2api/ent/usagelog"
"github.com/Wei-Shaw/sub2api/ent/user" "github.com/Wei-Shaw/sub2api/ent/user"
"github.com/Wei-Shaw/sub2api/ent/userattributevalue"
"github.com/Wei-Shaw/sub2api/ent/usersubscription" "github.com/Wei-Shaw/sub2api/ent/usersubscription"
) )
@ -171,20 +172,6 @@ func (_u *UserUpdate) SetNillableUsername(v *string) *UserUpdate {
return _u return _u
} }
// SetWechat sets the "wechat" field.
func (_u *UserUpdate) SetWechat(v string) *UserUpdate {
_u.mutation.SetWechat(v)
return _u
}
// SetNillableWechat sets the "wechat" field if the given value is not nil.
func (_u *UserUpdate) SetNillableWechat(v *string) *UserUpdate {
if v != nil {
_u.SetWechat(*v)
}
return _u
}
// SetNotes sets the "notes" field. // SetNotes sets the "notes" field.
func (_u *UserUpdate) SetNotes(v string) *UserUpdate { func (_u *UserUpdate) SetNotes(v string) *UserUpdate {
_u.mutation.SetNotes(v) _u.mutation.SetNotes(v)
@ -289,6 +276,21 @@ func (_u *UserUpdate) AddUsageLogs(v ...*UsageLog) *UserUpdate {
return _u.AddUsageLogIDs(ids...) return _u.AddUsageLogIDs(ids...)
} }
// AddAttributeValueIDs adds the "attribute_values" edge to the UserAttributeValue entity by IDs.
func (_u *UserUpdate) AddAttributeValueIDs(ids ...int64) *UserUpdate {
_u.mutation.AddAttributeValueIDs(ids...)
return _u
}
// AddAttributeValues adds the "attribute_values" edges to the UserAttributeValue entity.
func (_u *UserUpdate) AddAttributeValues(v ...*UserAttributeValue) *UserUpdate {
ids := make([]int64, len(v))
for i := range v {
ids[i] = v[i].ID
}
return _u.AddAttributeValueIDs(ids...)
}
// Mutation returns the UserMutation object of the builder. // Mutation returns the UserMutation object of the builder.
func (_u *UserUpdate) Mutation() *UserMutation { func (_u *UserUpdate) Mutation() *UserMutation {
return _u.mutation return _u.mutation
@ -420,6 +422,27 @@ func (_u *UserUpdate) RemoveUsageLogs(v ...*UsageLog) *UserUpdate {
return _u.RemoveUsageLogIDs(ids...) return _u.RemoveUsageLogIDs(ids...)
} }
// ClearAttributeValues clears all "attribute_values" edges to the UserAttributeValue entity.
func (_u *UserUpdate) ClearAttributeValues() *UserUpdate {
_u.mutation.ClearAttributeValues()
return _u
}
// RemoveAttributeValueIDs removes the "attribute_values" edge to UserAttributeValue entities by IDs.
func (_u *UserUpdate) RemoveAttributeValueIDs(ids ...int64) *UserUpdate {
_u.mutation.RemoveAttributeValueIDs(ids...)
return _u
}
// RemoveAttributeValues removes "attribute_values" edges to UserAttributeValue entities.
func (_u *UserUpdate) RemoveAttributeValues(v ...*UserAttributeValue) *UserUpdate {
ids := make([]int64, len(v))
for i := range v {
ids[i] = v[i].ID
}
return _u.RemoveAttributeValueIDs(ids...)
}
// Save executes the query and returns the number of nodes affected by the update operation. // Save executes the query and returns the number of nodes affected by the update operation.
func (_u *UserUpdate) Save(ctx context.Context) (int, error) { func (_u *UserUpdate) Save(ctx context.Context) (int, error) {
if err := _u.defaults(); err != nil { if err := _u.defaults(); err != nil {
@ -489,11 +512,6 @@ func (_u *UserUpdate) check() error {
return &ValidationError{Name: "username", err: fmt.Errorf(`ent: validator failed for field "User.username": %w`, err)} return &ValidationError{Name: "username", err: fmt.Errorf(`ent: validator failed for field "User.username": %w`, err)}
} }
} }
if v, ok := _u.mutation.Wechat(); ok {
if err := user.WechatValidator(v); err != nil {
return &ValidationError{Name: "wechat", err: fmt.Errorf(`ent: validator failed for field "User.wechat": %w`, err)}
}
}
return nil return nil
} }
@ -545,9 +563,6 @@ func (_u *UserUpdate) sqlSave(ctx context.Context) (_node int, err error) {
if value, ok := _u.mutation.Username(); ok { if value, ok := _u.mutation.Username(); ok {
_spec.SetField(user.FieldUsername, field.TypeString, value) _spec.SetField(user.FieldUsername, field.TypeString, value)
} }
if value, ok := _u.mutation.Wechat(); ok {
_spec.SetField(user.FieldWechat, field.TypeString, value)
}
if value, ok := _u.mutation.Notes(); ok { if value, ok := _u.mutation.Notes(); ok {
_spec.SetField(user.FieldNotes, field.TypeString, value) _spec.SetField(user.FieldNotes, field.TypeString, value)
} }
@ -833,6 +848,51 @@ func (_u *UserUpdate) sqlSave(ctx context.Context) (_node int, err error) {
} }
_spec.Edges.Add = append(_spec.Edges.Add, edge) _spec.Edges.Add = append(_spec.Edges.Add, edge)
} }
if _u.mutation.AttributeValuesCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M,
Inverse: false,
Table: user.AttributeValuesTable,
Columns: []string{user.AttributeValuesColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(userattributevalue.FieldID, field.TypeInt64),
},
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
}
if nodes := _u.mutation.RemovedAttributeValuesIDs(); len(nodes) > 0 && !_u.mutation.AttributeValuesCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M,
Inverse: false,
Table: user.AttributeValuesTable,
Columns: []string{user.AttributeValuesColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(userattributevalue.FieldID, field.TypeInt64),
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
}
if nodes := _u.mutation.AttributeValuesIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M,
Inverse: false,
Table: user.AttributeValuesTable,
Columns: []string{user.AttributeValuesColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(userattributevalue.FieldID, field.TypeInt64),
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges.Add = append(_spec.Edges.Add, edge)
}
if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil {
if _, ok := err.(*sqlgraph.NotFoundError); ok { if _, ok := err.(*sqlgraph.NotFoundError); ok {
err = &NotFoundError{user.Label} err = &NotFoundError{user.Label}
@ -991,20 +1051,6 @@ func (_u *UserUpdateOne) SetNillableUsername(v *string) *UserUpdateOne {
return _u return _u
} }
// SetWechat sets the "wechat" field.
func (_u *UserUpdateOne) SetWechat(v string) *UserUpdateOne {
_u.mutation.SetWechat(v)
return _u
}
// SetNillableWechat sets the "wechat" field if the given value is not nil.
func (_u *UserUpdateOne) SetNillableWechat(v *string) *UserUpdateOne {
if v != nil {
_u.SetWechat(*v)
}
return _u
}
// SetNotes sets the "notes" field. // SetNotes sets the "notes" field.
func (_u *UserUpdateOne) SetNotes(v string) *UserUpdateOne { func (_u *UserUpdateOne) SetNotes(v string) *UserUpdateOne {
_u.mutation.SetNotes(v) _u.mutation.SetNotes(v)
@ -1109,6 +1155,21 @@ func (_u *UserUpdateOne) AddUsageLogs(v ...*UsageLog) *UserUpdateOne {
return _u.AddUsageLogIDs(ids...) return _u.AddUsageLogIDs(ids...)
} }
// AddAttributeValueIDs adds the "attribute_values" edge to the UserAttributeValue entity by IDs.
func (_u *UserUpdateOne) AddAttributeValueIDs(ids ...int64) *UserUpdateOne {
_u.mutation.AddAttributeValueIDs(ids...)
return _u
}
// AddAttributeValues adds the "attribute_values" edges to the UserAttributeValue entity.
func (_u *UserUpdateOne) AddAttributeValues(v ...*UserAttributeValue) *UserUpdateOne {
ids := make([]int64, len(v))
for i := range v {
ids[i] = v[i].ID
}
return _u.AddAttributeValueIDs(ids...)
}
// Mutation returns the UserMutation object of the builder. // Mutation returns the UserMutation object of the builder.
func (_u *UserUpdateOne) Mutation() *UserMutation { func (_u *UserUpdateOne) Mutation() *UserMutation {
return _u.mutation return _u.mutation
@ -1240,6 +1301,27 @@ func (_u *UserUpdateOne) RemoveUsageLogs(v ...*UsageLog) *UserUpdateOne {
return _u.RemoveUsageLogIDs(ids...) return _u.RemoveUsageLogIDs(ids...)
} }
// ClearAttributeValues clears all "attribute_values" edges to the UserAttributeValue entity.
func (_u *UserUpdateOne) ClearAttributeValues() *UserUpdateOne {
_u.mutation.ClearAttributeValues()
return _u
}
// RemoveAttributeValueIDs removes the "attribute_values" edge to UserAttributeValue entities by IDs.
func (_u *UserUpdateOne) RemoveAttributeValueIDs(ids ...int64) *UserUpdateOne {
_u.mutation.RemoveAttributeValueIDs(ids...)
return _u
}
// RemoveAttributeValues removes "attribute_values" edges to UserAttributeValue entities.
func (_u *UserUpdateOne) RemoveAttributeValues(v ...*UserAttributeValue) *UserUpdateOne {
ids := make([]int64, len(v))
for i := range v {
ids[i] = v[i].ID
}
return _u.RemoveAttributeValueIDs(ids...)
}
// Where appends a list predicates to the UserUpdate builder. // Where appends a list predicates to the UserUpdate builder.
func (_u *UserUpdateOne) Where(ps ...predicate.User) *UserUpdateOne { func (_u *UserUpdateOne) Where(ps ...predicate.User) *UserUpdateOne {
_u.mutation.Where(ps...) _u.mutation.Where(ps...)
@ -1322,11 +1404,6 @@ func (_u *UserUpdateOne) check() error {
return &ValidationError{Name: "username", err: fmt.Errorf(`ent: validator failed for field "User.username": %w`, err)} return &ValidationError{Name: "username", err: fmt.Errorf(`ent: validator failed for field "User.username": %w`, err)}
} }
} }
if v, ok := _u.mutation.Wechat(); ok {
if err := user.WechatValidator(v); err != nil {
return &ValidationError{Name: "wechat", err: fmt.Errorf(`ent: validator failed for field "User.wechat": %w`, err)}
}
}
return nil return nil
} }
@ -1395,9 +1472,6 @@ func (_u *UserUpdateOne) sqlSave(ctx context.Context) (_node *User, err error) {
if value, ok := _u.mutation.Username(); ok { if value, ok := _u.mutation.Username(); ok {
_spec.SetField(user.FieldUsername, field.TypeString, value) _spec.SetField(user.FieldUsername, field.TypeString, value)
} }
if value, ok := _u.mutation.Wechat(); ok {
_spec.SetField(user.FieldWechat, field.TypeString, value)
}
if value, ok := _u.mutation.Notes(); ok { if value, ok := _u.mutation.Notes(); ok {
_spec.SetField(user.FieldNotes, field.TypeString, value) _spec.SetField(user.FieldNotes, field.TypeString, value)
} }
@ -1683,6 +1757,51 @@ func (_u *UserUpdateOne) sqlSave(ctx context.Context) (_node *User, err error) {
} }
_spec.Edges.Add = append(_spec.Edges.Add, edge) _spec.Edges.Add = append(_spec.Edges.Add, edge)
} }
if _u.mutation.AttributeValuesCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M,
Inverse: false,
Table: user.AttributeValuesTable,
Columns: []string{user.AttributeValuesColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(userattributevalue.FieldID, field.TypeInt64),
},
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
}
if nodes := _u.mutation.RemovedAttributeValuesIDs(); len(nodes) > 0 && !_u.mutation.AttributeValuesCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M,
Inverse: false,
Table: user.AttributeValuesTable,
Columns: []string{user.AttributeValuesColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(userattributevalue.FieldID, field.TypeInt64),
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
}
if nodes := _u.mutation.AttributeValuesIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M,
Inverse: false,
Table: user.AttributeValuesTable,
Columns: []string{user.AttributeValuesColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(userattributevalue.FieldID, field.TypeInt64),
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges.Add = append(_spec.Edges.Add, edge)
}
_node = &User{config: _u.config} _node = &User{config: _u.config}
_spec.Assign = _node.assignValues _spec.Assign = _node.assignValues
_spec.ScanValues = _node.scanValues _spec.ScanValues = _node.scanValues

View File

@ -0,0 +1,276 @@
// Code generated by ent, DO NOT EDIT.
package ent
import (
"encoding/json"
"fmt"
"strings"
"time"
"entgo.io/ent"
"entgo.io/ent/dialect/sql"
"github.com/Wei-Shaw/sub2api/ent/userattributedefinition"
)
// UserAttributeDefinition is the model entity for the UserAttributeDefinition schema.
type UserAttributeDefinition struct {
config `json:"-"`
// ID of the ent.
ID int64 `json:"id,omitempty"`
// CreatedAt holds the value of the "created_at" field.
CreatedAt time.Time `json:"created_at,omitempty"`
// UpdatedAt holds the value of the "updated_at" field.
UpdatedAt time.Time `json:"updated_at,omitempty"`
// DeletedAt holds the value of the "deleted_at" field.
DeletedAt *time.Time `json:"deleted_at,omitempty"`
// Key holds the value of the "key" field.
Key string `json:"key,omitempty"`
// Name holds the value of the "name" field.
Name string `json:"name,omitempty"`
// Description holds the value of the "description" field.
Description string `json:"description,omitempty"`
// Type holds the value of the "type" field.
Type string `json:"type,omitempty"`
// Options holds the value of the "options" field.
Options []map[string]interface{} `json:"options,omitempty"`
// Required holds the value of the "required" field.
Required bool `json:"required,omitempty"`
// Validation holds the value of the "validation" field.
Validation map[string]interface{} `json:"validation,omitempty"`
// Placeholder holds the value of the "placeholder" field.
Placeholder string `json:"placeholder,omitempty"`
// DisplayOrder holds the value of the "display_order" field.
DisplayOrder int `json:"display_order,omitempty"`
// Enabled holds the value of the "enabled" field.
Enabled bool `json:"enabled,omitempty"`
// Edges holds the relations/edges for other nodes in the graph.
// The values are being populated by the UserAttributeDefinitionQuery when eager-loading is set.
Edges UserAttributeDefinitionEdges `json:"edges"`
selectValues sql.SelectValues
}
// UserAttributeDefinitionEdges holds the relations/edges for other nodes in the graph.
type UserAttributeDefinitionEdges struct {
// Values holds the value of the values edge.
Values []*UserAttributeValue `json:"values,omitempty"`
// loadedTypes holds the information for reporting if a
// type was loaded (or requested) in eager-loading or not.
loadedTypes [1]bool
}
// ValuesOrErr returns the Values value or an error if the edge
// was not loaded in eager-loading.
func (e UserAttributeDefinitionEdges) ValuesOrErr() ([]*UserAttributeValue, error) {
if e.loadedTypes[0] {
return e.Values, nil
}
return nil, &NotLoadedError{edge: "values"}
}
// scanValues returns the types for scanning values from sql.Rows.
func (*UserAttributeDefinition) scanValues(columns []string) ([]any, error) {
values := make([]any, len(columns))
for i := range columns {
switch columns[i] {
case userattributedefinition.FieldOptions, userattributedefinition.FieldValidation:
values[i] = new([]byte)
case userattributedefinition.FieldRequired, userattributedefinition.FieldEnabled:
values[i] = new(sql.NullBool)
case userattributedefinition.FieldID, userattributedefinition.FieldDisplayOrder:
values[i] = new(sql.NullInt64)
case userattributedefinition.FieldKey, userattributedefinition.FieldName, userattributedefinition.FieldDescription, userattributedefinition.FieldType, userattributedefinition.FieldPlaceholder:
values[i] = new(sql.NullString)
case userattributedefinition.FieldCreatedAt, userattributedefinition.FieldUpdatedAt, userattributedefinition.FieldDeletedAt:
values[i] = new(sql.NullTime)
default:
values[i] = new(sql.UnknownType)
}
}
return values, nil
}
// assignValues assigns the values that were returned from sql.Rows (after scanning)
// to the UserAttributeDefinition fields.
func (_m *UserAttributeDefinition) assignValues(columns []string, values []any) error {
if m, n := len(values), len(columns); m < n {
return fmt.Errorf("mismatch number of scan values: %d != %d", m, n)
}
for i := range columns {
switch columns[i] {
case userattributedefinition.FieldID:
value, ok := values[i].(*sql.NullInt64)
if !ok {
return fmt.Errorf("unexpected type %T for field id", value)
}
_m.ID = int64(value.Int64)
case userattributedefinition.FieldCreatedAt:
if value, ok := values[i].(*sql.NullTime); !ok {
return fmt.Errorf("unexpected type %T for field created_at", values[i])
} else if value.Valid {
_m.CreatedAt = value.Time
}
case userattributedefinition.FieldUpdatedAt:
if value, ok := values[i].(*sql.NullTime); !ok {
return fmt.Errorf("unexpected type %T for field updated_at", values[i])
} else if value.Valid {
_m.UpdatedAt = value.Time
}
case userattributedefinition.FieldDeletedAt:
if value, ok := values[i].(*sql.NullTime); !ok {
return fmt.Errorf("unexpected type %T for field deleted_at", values[i])
} else if value.Valid {
_m.DeletedAt = new(time.Time)
*_m.DeletedAt = value.Time
}
case userattributedefinition.FieldKey:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field key", values[i])
} else if value.Valid {
_m.Key = value.String
}
case userattributedefinition.FieldName:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field name", values[i])
} else if value.Valid {
_m.Name = value.String
}
case userattributedefinition.FieldDescription:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field description", values[i])
} else if value.Valid {
_m.Description = value.String
}
case userattributedefinition.FieldType:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field type", values[i])
} else if value.Valid {
_m.Type = value.String
}
case userattributedefinition.FieldOptions:
if value, ok := values[i].(*[]byte); !ok {
return fmt.Errorf("unexpected type %T for field options", values[i])
} else if value != nil && len(*value) > 0 {
if err := json.Unmarshal(*value, &_m.Options); err != nil {
return fmt.Errorf("unmarshal field options: %w", err)
}
}
case userattributedefinition.FieldRequired:
if value, ok := values[i].(*sql.NullBool); !ok {
return fmt.Errorf("unexpected type %T for field required", values[i])
} else if value.Valid {
_m.Required = value.Bool
}
case userattributedefinition.FieldValidation:
if value, ok := values[i].(*[]byte); !ok {
return fmt.Errorf("unexpected type %T for field validation", values[i])
} else if value != nil && len(*value) > 0 {
if err := json.Unmarshal(*value, &_m.Validation); err != nil {
return fmt.Errorf("unmarshal field validation: %w", err)
}
}
case userattributedefinition.FieldPlaceholder:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field placeholder", values[i])
} else if value.Valid {
_m.Placeholder = value.String
}
case userattributedefinition.FieldDisplayOrder:
if value, ok := values[i].(*sql.NullInt64); !ok {
return fmt.Errorf("unexpected type %T for field display_order", values[i])
} else if value.Valid {
_m.DisplayOrder = int(value.Int64)
}
case userattributedefinition.FieldEnabled:
if value, ok := values[i].(*sql.NullBool); !ok {
return fmt.Errorf("unexpected type %T for field enabled", values[i])
} else if value.Valid {
_m.Enabled = value.Bool
}
default:
_m.selectValues.Set(columns[i], values[i])
}
}
return nil
}
// Value returns the ent.Value that was dynamically selected and assigned to the UserAttributeDefinition.
// This includes values selected through modifiers, order, etc.
func (_m *UserAttributeDefinition) Value(name string) (ent.Value, error) {
return _m.selectValues.Get(name)
}
// QueryValues queries the "values" edge of the UserAttributeDefinition entity.
func (_m *UserAttributeDefinition) QueryValues() *UserAttributeValueQuery {
return NewUserAttributeDefinitionClient(_m.config).QueryValues(_m)
}
// Update returns a builder for updating this UserAttributeDefinition.
// Note that you need to call UserAttributeDefinition.Unwrap() before calling this method if this UserAttributeDefinition
// was returned from a transaction, and the transaction was committed or rolled back.
func (_m *UserAttributeDefinition) Update() *UserAttributeDefinitionUpdateOne {
return NewUserAttributeDefinitionClient(_m.config).UpdateOne(_m)
}
// Unwrap unwraps the UserAttributeDefinition entity that was returned from a transaction after it was closed,
// so that all future queries will be executed through the driver which created the transaction.
func (_m *UserAttributeDefinition) Unwrap() *UserAttributeDefinition {
_tx, ok := _m.config.driver.(*txDriver)
if !ok {
panic("ent: UserAttributeDefinition is not a transactional entity")
}
_m.config.driver = _tx.drv
return _m
}
// String implements the fmt.Stringer.
func (_m *UserAttributeDefinition) String() string {
var builder strings.Builder
builder.WriteString("UserAttributeDefinition(")
builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID))
builder.WriteString("created_at=")
builder.WriteString(_m.CreatedAt.Format(time.ANSIC))
builder.WriteString(", ")
builder.WriteString("updated_at=")
builder.WriteString(_m.UpdatedAt.Format(time.ANSIC))
builder.WriteString(", ")
if v := _m.DeletedAt; v != nil {
builder.WriteString("deleted_at=")
builder.WriteString(v.Format(time.ANSIC))
}
builder.WriteString(", ")
builder.WriteString("key=")
builder.WriteString(_m.Key)
builder.WriteString(", ")
builder.WriteString("name=")
builder.WriteString(_m.Name)
builder.WriteString(", ")
builder.WriteString("description=")
builder.WriteString(_m.Description)
builder.WriteString(", ")
builder.WriteString("type=")
builder.WriteString(_m.Type)
builder.WriteString(", ")
builder.WriteString("options=")
builder.WriteString(fmt.Sprintf("%v", _m.Options))
builder.WriteString(", ")
builder.WriteString("required=")
builder.WriteString(fmt.Sprintf("%v", _m.Required))
builder.WriteString(", ")
builder.WriteString("validation=")
builder.WriteString(fmt.Sprintf("%v", _m.Validation))
builder.WriteString(", ")
builder.WriteString("placeholder=")
builder.WriteString(_m.Placeholder)
builder.WriteString(", ")
builder.WriteString("display_order=")
builder.WriteString(fmt.Sprintf("%v", _m.DisplayOrder))
builder.WriteString(", ")
builder.WriteString("enabled=")
builder.WriteString(fmt.Sprintf("%v", _m.Enabled))
builder.WriteByte(')')
return builder.String()
}
// UserAttributeDefinitions is a parsable slice of UserAttributeDefinition.
type UserAttributeDefinitions []*UserAttributeDefinition

View File

@ -0,0 +1,205 @@
// Code generated by ent, DO NOT EDIT.
package userattributedefinition
import (
"time"
"entgo.io/ent"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
)
const (
// Label holds the string label denoting the userattributedefinition type in the database.
Label = "user_attribute_definition"
// FieldID holds the string denoting the id field in the database.
FieldID = "id"
// FieldCreatedAt holds the string denoting the created_at field in the database.
FieldCreatedAt = "created_at"
// FieldUpdatedAt holds the string denoting the updated_at field in the database.
FieldUpdatedAt = "updated_at"
// FieldDeletedAt holds the string denoting the deleted_at field in the database.
FieldDeletedAt = "deleted_at"
// FieldKey holds the string denoting the key field in the database.
FieldKey = "key"
// FieldName holds the string denoting the name field in the database.
FieldName = "name"
// FieldDescription holds the string denoting the description field in the database.
FieldDescription = "description"
// FieldType holds the string denoting the type field in the database.
FieldType = "type"
// FieldOptions holds the string denoting the options field in the database.
FieldOptions = "options"
// FieldRequired holds the string denoting the required field in the database.
FieldRequired = "required"
// FieldValidation holds the string denoting the validation field in the database.
FieldValidation = "validation"
// FieldPlaceholder holds the string denoting the placeholder field in the database.
FieldPlaceholder = "placeholder"
// FieldDisplayOrder holds the string denoting the display_order field in the database.
FieldDisplayOrder = "display_order"
// FieldEnabled holds the string denoting the enabled field in the database.
FieldEnabled = "enabled"
// EdgeValues holds the string denoting the values edge name in mutations.
EdgeValues = "values"
// Table holds the table name of the userattributedefinition in the database.
Table = "user_attribute_definitions"
// ValuesTable is the table that holds the values relation/edge.
ValuesTable = "user_attribute_values"
// ValuesInverseTable is the table name for the UserAttributeValue entity.
// It exists in this package in order to avoid circular dependency with the "userattributevalue" package.
ValuesInverseTable = "user_attribute_values"
// ValuesColumn is the table column denoting the values relation/edge.
ValuesColumn = "attribute_id"
)
// Columns holds all SQL columns for userattributedefinition fields.
var Columns = []string{
FieldID,
FieldCreatedAt,
FieldUpdatedAt,
FieldDeletedAt,
FieldKey,
FieldName,
FieldDescription,
FieldType,
FieldOptions,
FieldRequired,
FieldValidation,
FieldPlaceholder,
FieldDisplayOrder,
FieldEnabled,
}
// ValidColumn reports if the column name is valid (part of the table columns).
func ValidColumn(column string) bool {
for i := range Columns {
if column == Columns[i] {
return true
}
}
return false
}
// Note that the variables below are initialized by the runtime
// package on the initialization of the application. Therefore,
// it should be imported in the main as follows:
//
// import _ "github.com/Wei-Shaw/sub2api/ent/runtime"
var (
Hooks [1]ent.Hook
Interceptors [1]ent.Interceptor
// DefaultCreatedAt holds the default value on creation for the "created_at" field.
DefaultCreatedAt func() time.Time
// DefaultUpdatedAt holds the default value on creation for the "updated_at" field.
DefaultUpdatedAt func() time.Time
// UpdateDefaultUpdatedAt holds the default value on update for the "updated_at" field.
UpdateDefaultUpdatedAt func() time.Time
// KeyValidator is a validator for the "key" field. It is called by the builders before save.
KeyValidator func(string) error
// NameValidator is a validator for the "name" field. It is called by the builders before save.
NameValidator func(string) error
// DefaultDescription holds the default value on creation for the "description" field.
DefaultDescription string
// TypeValidator is a validator for the "type" field. It is called by the builders before save.
TypeValidator func(string) error
// DefaultOptions holds the default value on creation for the "options" field.
DefaultOptions []map[string]interface{}
// DefaultRequired holds the default value on creation for the "required" field.
DefaultRequired bool
// DefaultValidation holds the default value on creation for the "validation" field.
DefaultValidation map[string]interface{}
// DefaultPlaceholder holds the default value on creation for the "placeholder" field.
DefaultPlaceholder string
// PlaceholderValidator is a validator for the "placeholder" field. It is called by the builders before save.
PlaceholderValidator func(string) error
// DefaultDisplayOrder holds the default value on creation for the "display_order" field.
DefaultDisplayOrder int
// DefaultEnabled holds the default value on creation for the "enabled" field.
DefaultEnabled bool
)
// OrderOption defines the ordering options for the UserAttributeDefinition queries.
type OrderOption func(*sql.Selector)
// ByID orders the results by the id field.
func ByID(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldID, opts...).ToFunc()
}
// ByCreatedAt orders the results by the created_at field.
func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldCreatedAt, opts...).ToFunc()
}
// ByUpdatedAt orders the results by the updated_at field.
func ByUpdatedAt(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldUpdatedAt, opts...).ToFunc()
}
// ByDeletedAt orders the results by the deleted_at field.
func ByDeletedAt(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldDeletedAt, opts...).ToFunc()
}
// ByKey orders the results by the key field.
func ByKey(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldKey, opts...).ToFunc()
}
// ByName orders the results by the name field.
func ByName(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldName, opts...).ToFunc()
}
// ByDescription orders the results by the description field.
func ByDescription(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldDescription, opts...).ToFunc()
}
// ByType orders the results by the type field.
func ByType(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldType, opts...).ToFunc()
}
// ByRequired orders the results by the required field.
func ByRequired(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldRequired, opts...).ToFunc()
}
// ByPlaceholder orders the results by the placeholder field.
func ByPlaceholder(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldPlaceholder, opts...).ToFunc()
}
// ByDisplayOrder orders the results by the display_order field.
func ByDisplayOrder(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldDisplayOrder, opts...).ToFunc()
}
// ByEnabled orders the results by the enabled field.
func ByEnabled(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldEnabled, opts...).ToFunc()
}
// ByValuesCount orders the results by values count.
func ByValuesCount(opts ...sql.OrderTermOption) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborsCount(s, newValuesStep(), opts...)
}
}
// ByValues orders the results by values terms.
func ByValues(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborTerms(s, newValuesStep(), append([]sql.OrderTerm{term}, terms...)...)
}
}
func newValuesStep() *sqlgraph.Step {
return sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(ValuesInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, ValuesTable, ValuesColumn),
)
}

View File

@ -0,0 +1,664 @@
// Code generated by ent, DO NOT EDIT.
package userattributedefinition
import (
"time"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"github.com/Wei-Shaw/sub2api/ent/predicate"
)
// ID filters vertices based on their ID field.
func ID(id int64) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldEQ(FieldID, id))
}
// IDEQ applies the EQ predicate on the ID field.
func IDEQ(id int64) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldEQ(FieldID, id))
}
// IDNEQ applies the NEQ predicate on the ID field.
func IDNEQ(id int64) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldNEQ(FieldID, id))
}
// IDIn applies the In predicate on the ID field.
func IDIn(ids ...int64) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldIn(FieldID, ids...))
}
// IDNotIn applies the NotIn predicate on the ID field.
func IDNotIn(ids ...int64) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldNotIn(FieldID, ids...))
}
// IDGT applies the GT predicate on the ID field.
func IDGT(id int64) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldGT(FieldID, id))
}
// IDGTE applies the GTE predicate on the ID field.
func IDGTE(id int64) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldGTE(FieldID, id))
}
// IDLT applies the LT predicate on the ID field.
func IDLT(id int64) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldLT(FieldID, id))
}
// IDLTE applies the LTE predicate on the ID field.
func IDLTE(id int64) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldLTE(FieldID, id))
}
// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ.
func CreatedAt(v time.Time) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldEQ(FieldCreatedAt, v))
}
// UpdatedAt applies equality check predicate on the "updated_at" field. It's identical to UpdatedAtEQ.
func UpdatedAt(v time.Time) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldEQ(FieldUpdatedAt, v))
}
// DeletedAt applies equality check predicate on the "deleted_at" field. It's identical to DeletedAtEQ.
func DeletedAt(v time.Time) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldEQ(FieldDeletedAt, v))
}
// Key applies equality check predicate on the "key" field. It's identical to KeyEQ.
func Key(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldEQ(FieldKey, v))
}
// Name applies equality check predicate on the "name" field. It's identical to NameEQ.
func Name(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldEQ(FieldName, v))
}
// Description applies equality check predicate on the "description" field. It's identical to DescriptionEQ.
func Description(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldEQ(FieldDescription, v))
}
// Type applies equality check predicate on the "type" field. It's identical to TypeEQ.
func Type(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldEQ(FieldType, v))
}
// Required applies equality check predicate on the "required" field. It's identical to RequiredEQ.
func Required(v bool) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldEQ(FieldRequired, v))
}
// Placeholder applies equality check predicate on the "placeholder" field. It's identical to PlaceholderEQ.
func Placeholder(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldEQ(FieldPlaceholder, v))
}
// DisplayOrder applies equality check predicate on the "display_order" field. It's identical to DisplayOrderEQ.
func DisplayOrder(v int) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldEQ(FieldDisplayOrder, v))
}
// Enabled applies equality check predicate on the "enabled" field. It's identical to EnabledEQ.
func Enabled(v bool) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldEQ(FieldEnabled, v))
}
// CreatedAtEQ applies the EQ predicate on the "created_at" field.
func CreatedAtEQ(v time.Time) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldEQ(FieldCreatedAt, v))
}
// CreatedAtNEQ applies the NEQ predicate on the "created_at" field.
func CreatedAtNEQ(v time.Time) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldNEQ(FieldCreatedAt, v))
}
// CreatedAtIn applies the In predicate on the "created_at" field.
func CreatedAtIn(vs ...time.Time) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldIn(FieldCreatedAt, vs...))
}
// CreatedAtNotIn applies the NotIn predicate on the "created_at" field.
func CreatedAtNotIn(vs ...time.Time) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldNotIn(FieldCreatedAt, vs...))
}
// CreatedAtGT applies the GT predicate on the "created_at" field.
func CreatedAtGT(v time.Time) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldGT(FieldCreatedAt, v))
}
// CreatedAtGTE applies the GTE predicate on the "created_at" field.
func CreatedAtGTE(v time.Time) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldGTE(FieldCreatedAt, v))
}
// CreatedAtLT applies the LT predicate on the "created_at" field.
func CreatedAtLT(v time.Time) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldLT(FieldCreatedAt, v))
}
// CreatedAtLTE applies the LTE predicate on the "created_at" field.
func CreatedAtLTE(v time.Time) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldLTE(FieldCreatedAt, v))
}
// UpdatedAtEQ applies the EQ predicate on the "updated_at" field.
func UpdatedAtEQ(v time.Time) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldEQ(FieldUpdatedAt, v))
}
// UpdatedAtNEQ applies the NEQ predicate on the "updated_at" field.
func UpdatedAtNEQ(v time.Time) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldNEQ(FieldUpdatedAt, v))
}
// UpdatedAtIn applies the In predicate on the "updated_at" field.
func UpdatedAtIn(vs ...time.Time) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldIn(FieldUpdatedAt, vs...))
}
// UpdatedAtNotIn applies the NotIn predicate on the "updated_at" field.
func UpdatedAtNotIn(vs ...time.Time) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldNotIn(FieldUpdatedAt, vs...))
}
// UpdatedAtGT applies the GT predicate on the "updated_at" field.
func UpdatedAtGT(v time.Time) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldGT(FieldUpdatedAt, v))
}
// UpdatedAtGTE applies the GTE predicate on the "updated_at" field.
func UpdatedAtGTE(v time.Time) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldGTE(FieldUpdatedAt, v))
}
// UpdatedAtLT applies the LT predicate on the "updated_at" field.
func UpdatedAtLT(v time.Time) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldLT(FieldUpdatedAt, v))
}
// UpdatedAtLTE applies the LTE predicate on the "updated_at" field.
func UpdatedAtLTE(v time.Time) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldLTE(FieldUpdatedAt, v))
}
// DeletedAtEQ applies the EQ predicate on the "deleted_at" field.
func DeletedAtEQ(v time.Time) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldEQ(FieldDeletedAt, v))
}
// DeletedAtNEQ applies the NEQ predicate on the "deleted_at" field.
func DeletedAtNEQ(v time.Time) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldNEQ(FieldDeletedAt, v))
}
// DeletedAtIn applies the In predicate on the "deleted_at" field.
func DeletedAtIn(vs ...time.Time) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldIn(FieldDeletedAt, vs...))
}
// DeletedAtNotIn applies the NotIn predicate on the "deleted_at" field.
func DeletedAtNotIn(vs ...time.Time) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldNotIn(FieldDeletedAt, vs...))
}
// DeletedAtGT applies the GT predicate on the "deleted_at" field.
func DeletedAtGT(v time.Time) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldGT(FieldDeletedAt, v))
}
// DeletedAtGTE applies the GTE predicate on the "deleted_at" field.
func DeletedAtGTE(v time.Time) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldGTE(FieldDeletedAt, v))
}
// DeletedAtLT applies the LT predicate on the "deleted_at" field.
func DeletedAtLT(v time.Time) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldLT(FieldDeletedAt, v))
}
// DeletedAtLTE applies the LTE predicate on the "deleted_at" field.
func DeletedAtLTE(v time.Time) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldLTE(FieldDeletedAt, v))
}
// DeletedAtIsNil applies the IsNil predicate on the "deleted_at" field.
func DeletedAtIsNil() predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldIsNull(FieldDeletedAt))
}
// DeletedAtNotNil applies the NotNil predicate on the "deleted_at" field.
func DeletedAtNotNil() predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldNotNull(FieldDeletedAt))
}
// KeyEQ applies the EQ predicate on the "key" field.
func KeyEQ(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldEQ(FieldKey, v))
}
// KeyNEQ applies the NEQ predicate on the "key" field.
func KeyNEQ(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldNEQ(FieldKey, v))
}
// KeyIn applies the In predicate on the "key" field.
func KeyIn(vs ...string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldIn(FieldKey, vs...))
}
// KeyNotIn applies the NotIn predicate on the "key" field.
func KeyNotIn(vs ...string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldNotIn(FieldKey, vs...))
}
// KeyGT applies the GT predicate on the "key" field.
func KeyGT(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldGT(FieldKey, v))
}
// KeyGTE applies the GTE predicate on the "key" field.
func KeyGTE(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldGTE(FieldKey, v))
}
// KeyLT applies the LT predicate on the "key" field.
func KeyLT(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldLT(FieldKey, v))
}
// KeyLTE applies the LTE predicate on the "key" field.
func KeyLTE(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldLTE(FieldKey, v))
}
// KeyContains applies the Contains predicate on the "key" field.
func KeyContains(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldContains(FieldKey, v))
}
// KeyHasPrefix applies the HasPrefix predicate on the "key" field.
func KeyHasPrefix(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldHasPrefix(FieldKey, v))
}
// KeyHasSuffix applies the HasSuffix predicate on the "key" field.
func KeyHasSuffix(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldHasSuffix(FieldKey, v))
}
// KeyEqualFold applies the EqualFold predicate on the "key" field.
func KeyEqualFold(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldEqualFold(FieldKey, v))
}
// KeyContainsFold applies the ContainsFold predicate on the "key" field.
func KeyContainsFold(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldContainsFold(FieldKey, v))
}
// NameEQ applies the EQ predicate on the "name" field.
func NameEQ(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldEQ(FieldName, v))
}
// NameNEQ applies the NEQ predicate on the "name" field.
func NameNEQ(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldNEQ(FieldName, v))
}
// NameIn applies the In predicate on the "name" field.
func NameIn(vs ...string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldIn(FieldName, vs...))
}
// NameNotIn applies the NotIn predicate on the "name" field.
func NameNotIn(vs ...string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldNotIn(FieldName, vs...))
}
// NameGT applies the GT predicate on the "name" field.
func NameGT(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldGT(FieldName, v))
}
// NameGTE applies the GTE predicate on the "name" field.
func NameGTE(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldGTE(FieldName, v))
}
// NameLT applies the LT predicate on the "name" field.
func NameLT(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldLT(FieldName, v))
}
// NameLTE applies the LTE predicate on the "name" field.
func NameLTE(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldLTE(FieldName, v))
}
// NameContains applies the Contains predicate on the "name" field.
func NameContains(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldContains(FieldName, v))
}
// NameHasPrefix applies the HasPrefix predicate on the "name" field.
func NameHasPrefix(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldHasPrefix(FieldName, v))
}
// NameHasSuffix applies the HasSuffix predicate on the "name" field.
func NameHasSuffix(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldHasSuffix(FieldName, v))
}
// NameEqualFold applies the EqualFold predicate on the "name" field.
func NameEqualFold(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldEqualFold(FieldName, v))
}
// NameContainsFold applies the ContainsFold predicate on the "name" field.
func NameContainsFold(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldContainsFold(FieldName, v))
}
// DescriptionEQ applies the EQ predicate on the "description" field.
func DescriptionEQ(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldEQ(FieldDescription, v))
}
// DescriptionNEQ applies the NEQ predicate on the "description" field.
func DescriptionNEQ(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldNEQ(FieldDescription, v))
}
// DescriptionIn applies the In predicate on the "description" field.
func DescriptionIn(vs ...string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldIn(FieldDescription, vs...))
}
// DescriptionNotIn applies the NotIn predicate on the "description" field.
func DescriptionNotIn(vs ...string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldNotIn(FieldDescription, vs...))
}
// DescriptionGT applies the GT predicate on the "description" field.
func DescriptionGT(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldGT(FieldDescription, v))
}
// DescriptionGTE applies the GTE predicate on the "description" field.
func DescriptionGTE(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldGTE(FieldDescription, v))
}
// DescriptionLT applies the LT predicate on the "description" field.
func DescriptionLT(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldLT(FieldDescription, v))
}
// DescriptionLTE applies the LTE predicate on the "description" field.
func DescriptionLTE(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldLTE(FieldDescription, v))
}
// DescriptionContains applies the Contains predicate on the "description" field.
func DescriptionContains(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldContains(FieldDescription, v))
}
// DescriptionHasPrefix applies the HasPrefix predicate on the "description" field.
func DescriptionHasPrefix(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldHasPrefix(FieldDescription, v))
}
// DescriptionHasSuffix applies the HasSuffix predicate on the "description" field.
func DescriptionHasSuffix(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldHasSuffix(FieldDescription, v))
}
// DescriptionEqualFold applies the EqualFold predicate on the "description" field.
func DescriptionEqualFold(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldEqualFold(FieldDescription, v))
}
// DescriptionContainsFold applies the ContainsFold predicate on the "description" field.
func DescriptionContainsFold(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldContainsFold(FieldDescription, v))
}
// TypeEQ applies the EQ predicate on the "type" field.
func TypeEQ(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldEQ(FieldType, v))
}
// TypeNEQ applies the NEQ predicate on the "type" field.
func TypeNEQ(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldNEQ(FieldType, v))
}
// TypeIn applies the In predicate on the "type" field.
func TypeIn(vs ...string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldIn(FieldType, vs...))
}
// TypeNotIn applies the NotIn predicate on the "type" field.
func TypeNotIn(vs ...string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldNotIn(FieldType, vs...))
}
// TypeGT applies the GT predicate on the "type" field.
func TypeGT(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldGT(FieldType, v))
}
// TypeGTE applies the GTE predicate on the "type" field.
func TypeGTE(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldGTE(FieldType, v))
}
// TypeLT applies the LT predicate on the "type" field.
func TypeLT(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldLT(FieldType, v))
}
// TypeLTE applies the LTE predicate on the "type" field.
func TypeLTE(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldLTE(FieldType, v))
}
// TypeContains applies the Contains predicate on the "type" field.
func TypeContains(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldContains(FieldType, v))
}
// TypeHasPrefix applies the HasPrefix predicate on the "type" field.
func TypeHasPrefix(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldHasPrefix(FieldType, v))
}
// TypeHasSuffix applies the HasSuffix predicate on the "type" field.
func TypeHasSuffix(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldHasSuffix(FieldType, v))
}
// TypeEqualFold applies the EqualFold predicate on the "type" field.
func TypeEqualFold(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldEqualFold(FieldType, v))
}
// TypeContainsFold applies the ContainsFold predicate on the "type" field.
func TypeContainsFold(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldContainsFold(FieldType, v))
}
// RequiredEQ applies the EQ predicate on the "required" field.
func RequiredEQ(v bool) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldEQ(FieldRequired, v))
}
// RequiredNEQ applies the NEQ predicate on the "required" field.
func RequiredNEQ(v bool) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldNEQ(FieldRequired, v))
}
// PlaceholderEQ applies the EQ predicate on the "placeholder" field.
func PlaceholderEQ(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldEQ(FieldPlaceholder, v))
}
// PlaceholderNEQ applies the NEQ predicate on the "placeholder" field.
func PlaceholderNEQ(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldNEQ(FieldPlaceholder, v))
}
// PlaceholderIn applies the In predicate on the "placeholder" field.
func PlaceholderIn(vs ...string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldIn(FieldPlaceholder, vs...))
}
// PlaceholderNotIn applies the NotIn predicate on the "placeholder" field.
func PlaceholderNotIn(vs ...string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldNotIn(FieldPlaceholder, vs...))
}
// PlaceholderGT applies the GT predicate on the "placeholder" field.
func PlaceholderGT(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldGT(FieldPlaceholder, v))
}
// PlaceholderGTE applies the GTE predicate on the "placeholder" field.
func PlaceholderGTE(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldGTE(FieldPlaceholder, v))
}
// PlaceholderLT applies the LT predicate on the "placeholder" field.
func PlaceholderLT(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldLT(FieldPlaceholder, v))
}
// PlaceholderLTE applies the LTE predicate on the "placeholder" field.
func PlaceholderLTE(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldLTE(FieldPlaceholder, v))
}
// PlaceholderContains applies the Contains predicate on the "placeholder" field.
func PlaceholderContains(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldContains(FieldPlaceholder, v))
}
// PlaceholderHasPrefix applies the HasPrefix predicate on the "placeholder" field.
func PlaceholderHasPrefix(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldHasPrefix(FieldPlaceholder, v))
}
// PlaceholderHasSuffix applies the HasSuffix predicate on the "placeholder" field.
func PlaceholderHasSuffix(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldHasSuffix(FieldPlaceholder, v))
}
// PlaceholderEqualFold applies the EqualFold predicate on the "placeholder" field.
func PlaceholderEqualFold(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldEqualFold(FieldPlaceholder, v))
}
// PlaceholderContainsFold applies the ContainsFold predicate on the "placeholder" field.
func PlaceholderContainsFold(v string) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldContainsFold(FieldPlaceholder, v))
}
// DisplayOrderEQ applies the EQ predicate on the "display_order" field.
func DisplayOrderEQ(v int) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldEQ(FieldDisplayOrder, v))
}
// DisplayOrderNEQ applies the NEQ predicate on the "display_order" field.
func DisplayOrderNEQ(v int) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldNEQ(FieldDisplayOrder, v))
}
// DisplayOrderIn applies the In predicate on the "display_order" field.
func DisplayOrderIn(vs ...int) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldIn(FieldDisplayOrder, vs...))
}
// DisplayOrderNotIn applies the NotIn predicate on the "display_order" field.
func DisplayOrderNotIn(vs ...int) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldNotIn(FieldDisplayOrder, vs...))
}
// DisplayOrderGT applies the GT predicate on the "display_order" field.
func DisplayOrderGT(v int) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldGT(FieldDisplayOrder, v))
}
// DisplayOrderGTE applies the GTE predicate on the "display_order" field.
func DisplayOrderGTE(v int) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldGTE(FieldDisplayOrder, v))
}
// DisplayOrderLT applies the LT predicate on the "display_order" field.
func DisplayOrderLT(v int) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldLT(FieldDisplayOrder, v))
}
// DisplayOrderLTE applies the LTE predicate on the "display_order" field.
func DisplayOrderLTE(v int) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldLTE(FieldDisplayOrder, v))
}
// EnabledEQ applies the EQ predicate on the "enabled" field.
func EnabledEQ(v bool) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldEQ(FieldEnabled, v))
}
// EnabledNEQ applies the NEQ predicate on the "enabled" field.
func EnabledNEQ(v bool) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.FieldNEQ(FieldEnabled, v))
}
// HasValues applies the HasEdge predicate on the "values" edge.
func HasValues() predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(func(s *sql.Selector) {
step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, ValuesTable, ValuesColumn),
)
sqlgraph.HasNeighbors(s, step)
})
}
// HasValuesWith applies the HasEdge predicate on the "values" edge with a given conditions (other predicates).
func HasValuesWith(preds ...predicate.UserAttributeValue) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(func(s *sql.Selector) {
step := newValuesStep()
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
for _, p := range preds {
p(s)
}
})
})
}
// And groups predicates with the AND operator between them.
func And(predicates ...predicate.UserAttributeDefinition) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.AndPredicates(predicates...))
}
// Or groups predicates with the OR operator between them.
func Or(predicates ...predicate.UserAttributeDefinition) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.OrPredicates(predicates...))
}
// Not applies the not operator on the given predicate.
func Not(p predicate.UserAttributeDefinition) predicate.UserAttributeDefinition {
return predicate.UserAttributeDefinition(sql.NotPredicates(p))
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,88 @@
// Code generated by ent, DO NOT EDIT.
package ent
import (
"context"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field"
"github.com/Wei-Shaw/sub2api/ent/predicate"
"github.com/Wei-Shaw/sub2api/ent/userattributedefinition"
)
// UserAttributeDefinitionDelete is the builder for deleting a UserAttributeDefinition entity.
type UserAttributeDefinitionDelete struct {
config
hooks []Hook
mutation *UserAttributeDefinitionMutation
}
// Where appends a list predicates to the UserAttributeDefinitionDelete builder.
func (_d *UserAttributeDefinitionDelete) Where(ps ...predicate.UserAttributeDefinition) *UserAttributeDefinitionDelete {
_d.mutation.Where(ps...)
return _d
}
// Exec executes the deletion query and returns how many vertices were deleted.
func (_d *UserAttributeDefinitionDelete) Exec(ctx context.Context) (int, error) {
return withHooks(ctx, _d.sqlExec, _d.mutation, _d.hooks)
}
// ExecX is like Exec, but panics if an error occurs.
func (_d *UserAttributeDefinitionDelete) ExecX(ctx context.Context) int {
n, err := _d.Exec(ctx)
if err != nil {
panic(err)
}
return n
}
func (_d *UserAttributeDefinitionDelete) sqlExec(ctx context.Context) (int, error) {
_spec := sqlgraph.NewDeleteSpec(userattributedefinition.Table, sqlgraph.NewFieldSpec(userattributedefinition.FieldID, field.TypeInt64))
if ps := _d.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
affected, err := sqlgraph.DeleteNodes(ctx, _d.driver, _spec)
if err != nil && sqlgraph.IsConstraintError(err) {
err = &ConstraintError{msg: err.Error(), wrap: err}
}
_d.mutation.done = true
return affected, err
}
// UserAttributeDefinitionDeleteOne is the builder for deleting a single UserAttributeDefinition entity.
type UserAttributeDefinitionDeleteOne struct {
_d *UserAttributeDefinitionDelete
}
// Where appends a list predicates to the UserAttributeDefinitionDelete builder.
func (_d *UserAttributeDefinitionDeleteOne) Where(ps ...predicate.UserAttributeDefinition) *UserAttributeDefinitionDeleteOne {
_d._d.mutation.Where(ps...)
return _d
}
// Exec executes the deletion query.
func (_d *UserAttributeDefinitionDeleteOne) Exec(ctx context.Context) error {
n, err := _d._d.Exec(ctx)
switch {
case err != nil:
return err
case n == 0:
return &NotFoundError{userattributedefinition.Label}
default:
return nil
}
}
// ExecX is like Exec, but panics if an error occurs.
func (_d *UserAttributeDefinitionDeleteOne) ExecX(ctx context.Context) {
if err := _d.Exec(ctx); err != nil {
panic(err)
}
}

View File

@ -0,0 +1,606 @@
// Code generated by ent, DO NOT EDIT.
package ent
import (
"context"
"database/sql/driver"
"fmt"
"math"
"entgo.io/ent"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field"
"github.com/Wei-Shaw/sub2api/ent/predicate"
"github.com/Wei-Shaw/sub2api/ent/userattributedefinition"
"github.com/Wei-Shaw/sub2api/ent/userattributevalue"
)
// UserAttributeDefinitionQuery is the builder for querying UserAttributeDefinition entities.
type UserAttributeDefinitionQuery struct {
config
ctx *QueryContext
order []userattributedefinition.OrderOption
inters []Interceptor
predicates []predicate.UserAttributeDefinition
withValues *UserAttributeValueQuery
// intermediate query (i.e. traversal path).
sql *sql.Selector
path func(context.Context) (*sql.Selector, error)
}
// Where adds a new predicate for the UserAttributeDefinitionQuery builder.
func (_q *UserAttributeDefinitionQuery) Where(ps ...predicate.UserAttributeDefinition) *UserAttributeDefinitionQuery {
_q.predicates = append(_q.predicates, ps...)
return _q
}
// Limit the number of records to be returned by this query.
func (_q *UserAttributeDefinitionQuery) Limit(limit int) *UserAttributeDefinitionQuery {
_q.ctx.Limit = &limit
return _q
}
// Offset to start from.
func (_q *UserAttributeDefinitionQuery) Offset(offset int) *UserAttributeDefinitionQuery {
_q.ctx.Offset = &offset
return _q
}
// Unique configures the query builder to filter duplicate records on query.
// By default, unique is set to true, and can be disabled using this method.
func (_q *UserAttributeDefinitionQuery) Unique(unique bool) *UserAttributeDefinitionQuery {
_q.ctx.Unique = &unique
return _q
}
// Order specifies how the records should be ordered.
func (_q *UserAttributeDefinitionQuery) Order(o ...userattributedefinition.OrderOption) *UserAttributeDefinitionQuery {
_q.order = append(_q.order, o...)
return _q
}
// QueryValues chains the current query on the "values" edge.
func (_q *UserAttributeDefinitionQuery) QueryValues() *UserAttributeValueQuery {
query := (&UserAttributeValueClient{config: _q.config}).Query()
query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
if err := _q.prepareQuery(ctx); err != nil {
return nil, err
}
selector := _q.sqlQuery(ctx)
if err := selector.Err(); err != nil {
return nil, err
}
step := sqlgraph.NewStep(
sqlgraph.From(userattributedefinition.Table, userattributedefinition.FieldID, selector),
sqlgraph.To(userattributevalue.Table, userattributevalue.FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, userattributedefinition.ValuesTable, userattributedefinition.ValuesColumn),
)
fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step)
return fromU, nil
}
return query
}
// First returns the first UserAttributeDefinition entity from the query.
// Returns a *NotFoundError when no UserAttributeDefinition was found.
func (_q *UserAttributeDefinitionQuery) First(ctx context.Context) (*UserAttributeDefinition, error) {
nodes, err := _q.Limit(1).All(setContextOp(ctx, _q.ctx, ent.OpQueryFirst))
if err != nil {
return nil, err
}
if len(nodes) == 0 {
return nil, &NotFoundError{userattributedefinition.Label}
}
return nodes[0], nil
}
// FirstX is like First, but panics if an error occurs.
func (_q *UserAttributeDefinitionQuery) FirstX(ctx context.Context) *UserAttributeDefinition {
node, err := _q.First(ctx)
if err != nil && !IsNotFound(err) {
panic(err)
}
return node
}
// FirstID returns the first UserAttributeDefinition ID from the query.
// Returns a *NotFoundError when no UserAttributeDefinition ID was found.
func (_q *UserAttributeDefinitionQuery) FirstID(ctx context.Context) (id int64, err error) {
var ids []int64
if ids, err = _q.Limit(1).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryFirstID)); err != nil {
return
}
if len(ids) == 0 {
err = &NotFoundError{userattributedefinition.Label}
return
}
return ids[0], nil
}
// FirstIDX is like FirstID, but panics if an error occurs.
func (_q *UserAttributeDefinitionQuery) FirstIDX(ctx context.Context) int64 {
id, err := _q.FirstID(ctx)
if err != nil && !IsNotFound(err) {
panic(err)
}
return id
}
// Only returns a single UserAttributeDefinition entity found by the query, ensuring it only returns one.
// Returns a *NotSingularError when more than one UserAttributeDefinition entity is found.
// Returns a *NotFoundError when no UserAttributeDefinition entities are found.
func (_q *UserAttributeDefinitionQuery) Only(ctx context.Context) (*UserAttributeDefinition, error) {
nodes, err := _q.Limit(2).All(setContextOp(ctx, _q.ctx, ent.OpQueryOnly))
if err != nil {
return nil, err
}
switch len(nodes) {
case 1:
return nodes[0], nil
case 0:
return nil, &NotFoundError{userattributedefinition.Label}
default:
return nil, &NotSingularError{userattributedefinition.Label}
}
}
// OnlyX is like Only, but panics if an error occurs.
func (_q *UserAttributeDefinitionQuery) OnlyX(ctx context.Context) *UserAttributeDefinition {
node, err := _q.Only(ctx)
if err != nil {
panic(err)
}
return node
}
// OnlyID is like Only, but returns the only UserAttributeDefinition ID in the query.
// Returns a *NotSingularError when more than one UserAttributeDefinition ID is found.
// Returns a *NotFoundError when no entities are found.
func (_q *UserAttributeDefinitionQuery) OnlyID(ctx context.Context) (id int64, err error) {
var ids []int64
if ids, err = _q.Limit(2).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryOnlyID)); err != nil {
return
}
switch len(ids) {
case 1:
id = ids[0]
case 0:
err = &NotFoundError{userattributedefinition.Label}
default:
err = &NotSingularError{userattributedefinition.Label}
}
return
}
// OnlyIDX is like OnlyID, but panics if an error occurs.
func (_q *UserAttributeDefinitionQuery) OnlyIDX(ctx context.Context) int64 {
id, err := _q.OnlyID(ctx)
if err != nil {
panic(err)
}
return id
}
// All executes the query and returns a list of UserAttributeDefinitions.
func (_q *UserAttributeDefinitionQuery) All(ctx context.Context) ([]*UserAttributeDefinition, error) {
ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll)
if err := _q.prepareQuery(ctx); err != nil {
return nil, err
}
qr := querierAll[[]*UserAttributeDefinition, *UserAttributeDefinitionQuery]()
return withInterceptors[[]*UserAttributeDefinition](ctx, _q, qr, _q.inters)
}
// AllX is like All, but panics if an error occurs.
func (_q *UserAttributeDefinitionQuery) AllX(ctx context.Context) []*UserAttributeDefinition {
nodes, err := _q.All(ctx)
if err != nil {
panic(err)
}
return nodes
}
// IDs executes the query and returns a list of UserAttributeDefinition IDs.
func (_q *UserAttributeDefinitionQuery) IDs(ctx context.Context) (ids []int64, err error) {
if _q.ctx.Unique == nil && _q.path != nil {
_q.Unique(true)
}
ctx = setContextOp(ctx, _q.ctx, ent.OpQueryIDs)
if err = _q.Select(userattributedefinition.FieldID).Scan(ctx, &ids); err != nil {
return nil, err
}
return ids, nil
}
// IDsX is like IDs, but panics if an error occurs.
func (_q *UserAttributeDefinitionQuery) IDsX(ctx context.Context) []int64 {
ids, err := _q.IDs(ctx)
if err != nil {
panic(err)
}
return ids
}
// Count returns the count of the given query.
func (_q *UserAttributeDefinitionQuery) Count(ctx context.Context) (int, error) {
ctx = setContextOp(ctx, _q.ctx, ent.OpQueryCount)
if err := _q.prepareQuery(ctx); err != nil {
return 0, err
}
return withInterceptors[int](ctx, _q, querierCount[*UserAttributeDefinitionQuery](), _q.inters)
}
// CountX is like Count, but panics if an error occurs.
func (_q *UserAttributeDefinitionQuery) CountX(ctx context.Context) int {
count, err := _q.Count(ctx)
if err != nil {
panic(err)
}
return count
}
// Exist returns true if the query has elements in the graph.
func (_q *UserAttributeDefinitionQuery) Exist(ctx context.Context) (bool, error) {
ctx = setContextOp(ctx, _q.ctx, ent.OpQueryExist)
switch _, err := _q.FirstID(ctx); {
case IsNotFound(err):
return false, nil
case err != nil:
return false, fmt.Errorf("ent: check existence: %w", err)
default:
return true, nil
}
}
// ExistX is like Exist, but panics if an error occurs.
func (_q *UserAttributeDefinitionQuery) ExistX(ctx context.Context) bool {
exist, err := _q.Exist(ctx)
if err != nil {
panic(err)
}
return exist
}
// Clone returns a duplicate of the UserAttributeDefinitionQuery builder, including all associated steps. It can be
// used to prepare common query builders and use them differently after the clone is made.
func (_q *UserAttributeDefinitionQuery) Clone() *UserAttributeDefinitionQuery {
if _q == nil {
return nil
}
return &UserAttributeDefinitionQuery{
config: _q.config,
ctx: _q.ctx.Clone(),
order: append([]userattributedefinition.OrderOption{}, _q.order...),
inters: append([]Interceptor{}, _q.inters...),
predicates: append([]predicate.UserAttributeDefinition{}, _q.predicates...),
withValues: _q.withValues.Clone(),
// clone intermediate query.
sql: _q.sql.Clone(),
path: _q.path,
}
}
// WithValues tells the query-builder to eager-load the nodes that are connected to
// the "values" edge. The optional arguments are used to configure the query builder of the edge.
func (_q *UserAttributeDefinitionQuery) WithValues(opts ...func(*UserAttributeValueQuery)) *UserAttributeDefinitionQuery {
query := (&UserAttributeValueClient{config: _q.config}).Query()
for _, opt := range opts {
opt(query)
}
_q.withValues = query
return _q
}
// GroupBy is used to group vertices by one or more fields/columns.
// It is often used with aggregate functions, like: count, max, mean, min, sum.
//
// Example:
//
// var v []struct {
// CreatedAt time.Time `json:"created_at,omitempty"`
// Count int `json:"count,omitempty"`
// }
//
// client.UserAttributeDefinition.Query().
// GroupBy(userattributedefinition.FieldCreatedAt).
// Aggregate(ent.Count()).
// Scan(ctx, &v)
func (_q *UserAttributeDefinitionQuery) GroupBy(field string, fields ...string) *UserAttributeDefinitionGroupBy {
_q.ctx.Fields = append([]string{field}, fields...)
grbuild := &UserAttributeDefinitionGroupBy{build: _q}
grbuild.flds = &_q.ctx.Fields
grbuild.label = userattributedefinition.Label
grbuild.scan = grbuild.Scan
return grbuild
}
// Select allows the selection one or more fields/columns for the given query,
// instead of selecting all fields in the entity.
//
// Example:
//
// var v []struct {
// CreatedAt time.Time `json:"created_at,omitempty"`
// }
//
// client.UserAttributeDefinition.Query().
// Select(userattributedefinition.FieldCreatedAt).
// Scan(ctx, &v)
func (_q *UserAttributeDefinitionQuery) Select(fields ...string) *UserAttributeDefinitionSelect {
_q.ctx.Fields = append(_q.ctx.Fields, fields...)
sbuild := &UserAttributeDefinitionSelect{UserAttributeDefinitionQuery: _q}
sbuild.label = userattributedefinition.Label
sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan
return sbuild
}
// Aggregate returns a UserAttributeDefinitionSelect configured with the given aggregations.
func (_q *UserAttributeDefinitionQuery) Aggregate(fns ...AggregateFunc) *UserAttributeDefinitionSelect {
return _q.Select().Aggregate(fns...)
}
func (_q *UserAttributeDefinitionQuery) prepareQuery(ctx context.Context) error {
for _, inter := range _q.inters {
if inter == nil {
return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)")
}
if trv, ok := inter.(Traverser); ok {
if err := trv.Traverse(ctx, _q); err != nil {
return err
}
}
}
for _, f := range _q.ctx.Fields {
if !userattributedefinition.ValidColumn(f) {
return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
}
}
if _q.path != nil {
prev, err := _q.path(ctx)
if err != nil {
return err
}
_q.sql = prev
}
return nil
}
func (_q *UserAttributeDefinitionQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*UserAttributeDefinition, error) {
var (
nodes = []*UserAttributeDefinition{}
_spec = _q.querySpec()
loadedTypes = [1]bool{
_q.withValues != nil,
}
)
_spec.ScanValues = func(columns []string) ([]any, error) {
return (*UserAttributeDefinition).scanValues(nil, columns)
}
_spec.Assign = func(columns []string, values []any) error {
node := &UserAttributeDefinition{config: _q.config}
nodes = append(nodes, node)
node.Edges.loadedTypes = loadedTypes
return node.assignValues(columns, values)
}
for i := range hooks {
hooks[i](ctx, _spec)
}
if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil {
return nil, err
}
if len(nodes) == 0 {
return nodes, nil
}
if query := _q.withValues; query != nil {
if err := _q.loadValues(ctx, query, nodes,
func(n *UserAttributeDefinition) { n.Edges.Values = []*UserAttributeValue{} },
func(n *UserAttributeDefinition, e *UserAttributeValue) { n.Edges.Values = append(n.Edges.Values, e) }); err != nil {
return nil, err
}
}
return nodes, nil
}
func (_q *UserAttributeDefinitionQuery) loadValues(ctx context.Context, query *UserAttributeValueQuery, nodes []*UserAttributeDefinition, init func(*UserAttributeDefinition), assign func(*UserAttributeDefinition, *UserAttributeValue)) error {
fks := make([]driver.Value, 0, len(nodes))
nodeids := make(map[int64]*UserAttributeDefinition)
for i := range nodes {
fks = append(fks, nodes[i].ID)
nodeids[nodes[i].ID] = nodes[i]
if init != nil {
init(nodes[i])
}
}
if len(query.ctx.Fields) > 0 {
query.ctx.AppendFieldOnce(userattributevalue.FieldAttributeID)
}
query.Where(predicate.UserAttributeValue(func(s *sql.Selector) {
s.Where(sql.InValues(s.C(userattributedefinition.ValuesColumn), fks...))
}))
neighbors, err := query.All(ctx)
if err != nil {
return err
}
for _, n := range neighbors {
fk := n.AttributeID
node, ok := nodeids[fk]
if !ok {
return fmt.Errorf(`unexpected referenced foreign-key "attribute_id" returned %v for node %v`, fk, n.ID)
}
assign(node, n)
}
return nil
}
func (_q *UserAttributeDefinitionQuery) sqlCount(ctx context.Context) (int, error) {
_spec := _q.querySpec()
_spec.Node.Columns = _q.ctx.Fields
if len(_q.ctx.Fields) > 0 {
_spec.Unique = _q.ctx.Unique != nil && *_q.ctx.Unique
}
return sqlgraph.CountNodes(ctx, _q.driver, _spec)
}
func (_q *UserAttributeDefinitionQuery) querySpec() *sqlgraph.QuerySpec {
_spec := sqlgraph.NewQuerySpec(userattributedefinition.Table, userattributedefinition.Columns, sqlgraph.NewFieldSpec(userattributedefinition.FieldID, field.TypeInt64))
_spec.From = _q.sql
if unique := _q.ctx.Unique; unique != nil {
_spec.Unique = *unique
} else if _q.path != nil {
_spec.Unique = true
}
if fields := _q.ctx.Fields; len(fields) > 0 {
_spec.Node.Columns = make([]string, 0, len(fields))
_spec.Node.Columns = append(_spec.Node.Columns, userattributedefinition.FieldID)
for i := range fields {
if fields[i] != userattributedefinition.FieldID {
_spec.Node.Columns = append(_spec.Node.Columns, fields[i])
}
}
}
if ps := _q.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
if limit := _q.ctx.Limit; limit != nil {
_spec.Limit = *limit
}
if offset := _q.ctx.Offset; offset != nil {
_spec.Offset = *offset
}
if ps := _q.order; len(ps) > 0 {
_spec.Order = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
return _spec
}
func (_q *UserAttributeDefinitionQuery) sqlQuery(ctx context.Context) *sql.Selector {
builder := sql.Dialect(_q.driver.Dialect())
t1 := builder.Table(userattributedefinition.Table)
columns := _q.ctx.Fields
if len(columns) == 0 {
columns = userattributedefinition.Columns
}
selector := builder.Select(t1.Columns(columns...)...).From(t1)
if _q.sql != nil {
selector = _q.sql
selector.Select(selector.Columns(columns...)...)
}
if _q.ctx.Unique != nil && *_q.ctx.Unique {
selector.Distinct()
}
for _, p := range _q.predicates {
p(selector)
}
for _, p := range _q.order {
p(selector)
}
if offset := _q.ctx.Offset; offset != nil {
// limit is mandatory for offset clause. We start
// with default value, and override it below if needed.
selector.Offset(*offset).Limit(math.MaxInt32)
}
if limit := _q.ctx.Limit; limit != nil {
selector.Limit(*limit)
}
return selector
}
// UserAttributeDefinitionGroupBy is the group-by builder for UserAttributeDefinition entities.
type UserAttributeDefinitionGroupBy struct {
selector
build *UserAttributeDefinitionQuery
}
// Aggregate adds the given aggregation functions to the group-by query.
func (_g *UserAttributeDefinitionGroupBy) Aggregate(fns ...AggregateFunc) *UserAttributeDefinitionGroupBy {
_g.fns = append(_g.fns, fns...)
return _g
}
// Scan applies the selector query and scans the result into the given value.
func (_g *UserAttributeDefinitionGroupBy) Scan(ctx context.Context, v any) error {
ctx = setContextOp(ctx, _g.build.ctx, ent.OpQueryGroupBy)
if err := _g.build.prepareQuery(ctx); err != nil {
return err
}
return scanWithInterceptors[*UserAttributeDefinitionQuery, *UserAttributeDefinitionGroupBy](ctx, _g.build, _g, _g.build.inters, v)
}
func (_g *UserAttributeDefinitionGroupBy) sqlScan(ctx context.Context, root *UserAttributeDefinitionQuery, v any) error {
selector := root.sqlQuery(ctx).Select()
aggregation := make([]string, 0, len(_g.fns))
for _, fn := range _g.fns {
aggregation = append(aggregation, fn(selector))
}
if len(selector.SelectedColumns()) == 0 {
columns := make([]string, 0, len(*_g.flds)+len(_g.fns))
for _, f := range *_g.flds {
columns = append(columns, selector.C(f))
}
columns = append(columns, aggregation...)
selector.Select(columns...)
}
selector.GroupBy(selector.Columns(*_g.flds...)...)
if err := selector.Err(); err != nil {
return err
}
rows := &sql.Rows{}
query, args := selector.Query()
if err := _g.build.driver.Query(ctx, query, args, rows); err != nil {
return err
}
defer rows.Close()
return sql.ScanSlice(rows, v)
}
// UserAttributeDefinitionSelect is the builder for selecting fields of UserAttributeDefinition entities.
type UserAttributeDefinitionSelect struct {
*UserAttributeDefinitionQuery
selector
}
// Aggregate adds the given aggregation functions to the selector query.
func (_s *UserAttributeDefinitionSelect) Aggregate(fns ...AggregateFunc) *UserAttributeDefinitionSelect {
_s.fns = append(_s.fns, fns...)
return _s
}
// Scan applies the selector query and scans the result into the given value.
func (_s *UserAttributeDefinitionSelect) Scan(ctx context.Context, v any) error {
ctx = setContextOp(ctx, _s.ctx, ent.OpQuerySelect)
if err := _s.prepareQuery(ctx); err != nil {
return err
}
return scanWithInterceptors[*UserAttributeDefinitionQuery, *UserAttributeDefinitionSelect](ctx, _s.UserAttributeDefinitionQuery, _s, _s.inters, v)
}
func (_s *UserAttributeDefinitionSelect) sqlScan(ctx context.Context, root *UserAttributeDefinitionQuery, v any) error {
selector := root.sqlQuery(ctx)
aggregation := make([]string, 0, len(_s.fns))
for _, fn := range _s.fns {
aggregation = append(aggregation, fn(selector))
}
switch n := len(*_s.selector.flds); {
case n == 0 && len(aggregation) > 0:
selector.Select(aggregation...)
case n != 0 && len(aggregation) > 0:
selector.AppendSelect(aggregation...)
}
rows := &sql.Rows{}
query, args := selector.Query()
if err := _s.driver.Query(ctx, query, args, rows); err != nil {
return err
}
defer rows.Close()
return sql.ScanSlice(rows, v)
}

View File

@ -0,0 +1,846 @@
// Code generated by ent, DO NOT EDIT.
package ent
import (
"context"
"errors"
"fmt"
"time"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/dialect/sql/sqljson"
"entgo.io/ent/schema/field"
"github.com/Wei-Shaw/sub2api/ent/predicate"
"github.com/Wei-Shaw/sub2api/ent/userattributedefinition"
"github.com/Wei-Shaw/sub2api/ent/userattributevalue"
)
// UserAttributeDefinitionUpdate is the builder for updating UserAttributeDefinition entities.
type UserAttributeDefinitionUpdate struct {
config
hooks []Hook
mutation *UserAttributeDefinitionMutation
}
// Where appends a list predicates to the UserAttributeDefinitionUpdate builder.
func (_u *UserAttributeDefinitionUpdate) Where(ps ...predicate.UserAttributeDefinition) *UserAttributeDefinitionUpdate {
_u.mutation.Where(ps...)
return _u
}
// SetUpdatedAt sets the "updated_at" field.
func (_u *UserAttributeDefinitionUpdate) SetUpdatedAt(v time.Time) *UserAttributeDefinitionUpdate {
_u.mutation.SetUpdatedAt(v)
return _u
}
// SetDeletedAt sets the "deleted_at" field.
func (_u *UserAttributeDefinitionUpdate) SetDeletedAt(v time.Time) *UserAttributeDefinitionUpdate {
_u.mutation.SetDeletedAt(v)
return _u
}
// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil.
func (_u *UserAttributeDefinitionUpdate) SetNillableDeletedAt(v *time.Time) *UserAttributeDefinitionUpdate {
if v != nil {
_u.SetDeletedAt(*v)
}
return _u
}
// ClearDeletedAt clears the value of the "deleted_at" field.
func (_u *UserAttributeDefinitionUpdate) ClearDeletedAt() *UserAttributeDefinitionUpdate {
_u.mutation.ClearDeletedAt()
return _u
}
// SetKey sets the "key" field.
func (_u *UserAttributeDefinitionUpdate) SetKey(v string) *UserAttributeDefinitionUpdate {
_u.mutation.SetKey(v)
return _u
}
// SetNillableKey sets the "key" field if the given value is not nil.
func (_u *UserAttributeDefinitionUpdate) SetNillableKey(v *string) *UserAttributeDefinitionUpdate {
if v != nil {
_u.SetKey(*v)
}
return _u
}
// SetName sets the "name" field.
func (_u *UserAttributeDefinitionUpdate) SetName(v string) *UserAttributeDefinitionUpdate {
_u.mutation.SetName(v)
return _u
}
// SetNillableName sets the "name" field if the given value is not nil.
func (_u *UserAttributeDefinitionUpdate) SetNillableName(v *string) *UserAttributeDefinitionUpdate {
if v != nil {
_u.SetName(*v)
}
return _u
}
// SetDescription sets the "description" field.
func (_u *UserAttributeDefinitionUpdate) SetDescription(v string) *UserAttributeDefinitionUpdate {
_u.mutation.SetDescription(v)
return _u
}
// SetNillableDescription sets the "description" field if the given value is not nil.
func (_u *UserAttributeDefinitionUpdate) SetNillableDescription(v *string) *UserAttributeDefinitionUpdate {
if v != nil {
_u.SetDescription(*v)
}
return _u
}
// SetType sets the "type" field.
func (_u *UserAttributeDefinitionUpdate) SetType(v string) *UserAttributeDefinitionUpdate {
_u.mutation.SetType(v)
return _u
}
// SetNillableType sets the "type" field if the given value is not nil.
func (_u *UserAttributeDefinitionUpdate) SetNillableType(v *string) *UserAttributeDefinitionUpdate {
if v != nil {
_u.SetType(*v)
}
return _u
}
// SetOptions sets the "options" field.
func (_u *UserAttributeDefinitionUpdate) SetOptions(v []map[string]interface{}) *UserAttributeDefinitionUpdate {
_u.mutation.SetOptions(v)
return _u
}
// AppendOptions appends value to the "options" field.
func (_u *UserAttributeDefinitionUpdate) AppendOptions(v []map[string]interface{}) *UserAttributeDefinitionUpdate {
_u.mutation.AppendOptions(v)
return _u
}
// SetRequired sets the "required" field.
func (_u *UserAttributeDefinitionUpdate) SetRequired(v bool) *UserAttributeDefinitionUpdate {
_u.mutation.SetRequired(v)
return _u
}
// SetNillableRequired sets the "required" field if the given value is not nil.
func (_u *UserAttributeDefinitionUpdate) SetNillableRequired(v *bool) *UserAttributeDefinitionUpdate {
if v != nil {
_u.SetRequired(*v)
}
return _u
}
// SetValidation sets the "validation" field.
func (_u *UserAttributeDefinitionUpdate) SetValidation(v map[string]interface{}) *UserAttributeDefinitionUpdate {
_u.mutation.SetValidation(v)
return _u
}
// SetPlaceholder sets the "placeholder" field.
func (_u *UserAttributeDefinitionUpdate) SetPlaceholder(v string) *UserAttributeDefinitionUpdate {
_u.mutation.SetPlaceholder(v)
return _u
}
// SetNillablePlaceholder sets the "placeholder" field if the given value is not nil.
func (_u *UserAttributeDefinitionUpdate) SetNillablePlaceholder(v *string) *UserAttributeDefinitionUpdate {
if v != nil {
_u.SetPlaceholder(*v)
}
return _u
}
// SetDisplayOrder sets the "display_order" field.
func (_u *UserAttributeDefinitionUpdate) SetDisplayOrder(v int) *UserAttributeDefinitionUpdate {
_u.mutation.ResetDisplayOrder()
_u.mutation.SetDisplayOrder(v)
return _u
}
// SetNillableDisplayOrder sets the "display_order" field if the given value is not nil.
func (_u *UserAttributeDefinitionUpdate) SetNillableDisplayOrder(v *int) *UserAttributeDefinitionUpdate {
if v != nil {
_u.SetDisplayOrder(*v)
}
return _u
}
// AddDisplayOrder adds value to the "display_order" field.
func (_u *UserAttributeDefinitionUpdate) AddDisplayOrder(v int) *UserAttributeDefinitionUpdate {
_u.mutation.AddDisplayOrder(v)
return _u
}
// SetEnabled sets the "enabled" field.
func (_u *UserAttributeDefinitionUpdate) SetEnabled(v bool) *UserAttributeDefinitionUpdate {
_u.mutation.SetEnabled(v)
return _u
}
// SetNillableEnabled sets the "enabled" field if the given value is not nil.
func (_u *UserAttributeDefinitionUpdate) SetNillableEnabled(v *bool) *UserAttributeDefinitionUpdate {
if v != nil {
_u.SetEnabled(*v)
}
return _u
}
// AddValueIDs adds the "values" edge to the UserAttributeValue entity by IDs.
func (_u *UserAttributeDefinitionUpdate) AddValueIDs(ids ...int64) *UserAttributeDefinitionUpdate {
_u.mutation.AddValueIDs(ids...)
return _u
}
// AddValues adds the "values" edges to the UserAttributeValue entity.
func (_u *UserAttributeDefinitionUpdate) AddValues(v ...*UserAttributeValue) *UserAttributeDefinitionUpdate {
ids := make([]int64, len(v))
for i := range v {
ids[i] = v[i].ID
}
return _u.AddValueIDs(ids...)
}
// Mutation returns the UserAttributeDefinitionMutation object of the builder.
func (_u *UserAttributeDefinitionUpdate) Mutation() *UserAttributeDefinitionMutation {
return _u.mutation
}
// ClearValues clears all "values" edges to the UserAttributeValue entity.
func (_u *UserAttributeDefinitionUpdate) ClearValues() *UserAttributeDefinitionUpdate {
_u.mutation.ClearValues()
return _u
}
// RemoveValueIDs removes the "values" edge to UserAttributeValue entities by IDs.
func (_u *UserAttributeDefinitionUpdate) RemoveValueIDs(ids ...int64) *UserAttributeDefinitionUpdate {
_u.mutation.RemoveValueIDs(ids...)
return _u
}
// RemoveValues removes "values" edges to UserAttributeValue entities.
func (_u *UserAttributeDefinitionUpdate) RemoveValues(v ...*UserAttributeValue) *UserAttributeDefinitionUpdate {
ids := make([]int64, len(v))
for i := range v {
ids[i] = v[i].ID
}
return _u.RemoveValueIDs(ids...)
}
// Save executes the query and returns the number of nodes affected by the update operation.
func (_u *UserAttributeDefinitionUpdate) Save(ctx context.Context) (int, error) {
if err := _u.defaults(); err != nil {
return 0, err
}
return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks)
}
// SaveX is like Save, but panics if an error occurs.
func (_u *UserAttributeDefinitionUpdate) SaveX(ctx context.Context) int {
affected, err := _u.Save(ctx)
if err != nil {
panic(err)
}
return affected
}
// Exec executes the query.
func (_u *UserAttributeDefinitionUpdate) Exec(ctx context.Context) error {
_, err := _u.Save(ctx)
return err
}
// ExecX is like Exec, but panics if an error occurs.
func (_u *UserAttributeDefinitionUpdate) ExecX(ctx context.Context) {
if err := _u.Exec(ctx); err != nil {
panic(err)
}
}
// defaults sets the default values of the builder before save.
func (_u *UserAttributeDefinitionUpdate) defaults() error {
if _, ok := _u.mutation.UpdatedAt(); !ok {
if userattributedefinition.UpdateDefaultUpdatedAt == nil {
return fmt.Errorf("ent: uninitialized userattributedefinition.UpdateDefaultUpdatedAt (forgotten import ent/runtime?)")
}
v := userattributedefinition.UpdateDefaultUpdatedAt()
_u.mutation.SetUpdatedAt(v)
}
return nil
}
// check runs all checks and user-defined validators on the builder.
func (_u *UserAttributeDefinitionUpdate) check() error {
if v, ok := _u.mutation.Key(); ok {
if err := userattributedefinition.KeyValidator(v); err != nil {
return &ValidationError{Name: "key", err: fmt.Errorf(`ent: validator failed for field "UserAttributeDefinition.key": %w`, err)}
}
}
if v, ok := _u.mutation.Name(); ok {
if err := userattributedefinition.NameValidator(v); err != nil {
return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "UserAttributeDefinition.name": %w`, err)}
}
}
if v, ok := _u.mutation.GetType(); ok {
if err := userattributedefinition.TypeValidator(v); err != nil {
return &ValidationError{Name: "type", err: fmt.Errorf(`ent: validator failed for field "UserAttributeDefinition.type": %w`, err)}
}
}
if v, ok := _u.mutation.Placeholder(); ok {
if err := userattributedefinition.PlaceholderValidator(v); err != nil {
return &ValidationError{Name: "placeholder", err: fmt.Errorf(`ent: validator failed for field "UserAttributeDefinition.placeholder": %w`, err)}
}
}
return nil
}
func (_u *UserAttributeDefinitionUpdate) sqlSave(ctx context.Context) (_node int, err error) {
if err := _u.check(); err != nil {
return _node, err
}
_spec := sqlgraph.NewUpdateSpec(userattributedefinition.Table, userattributedefinition.Columns, sqlgraph.NewFieldSpec(userattributedefinition.FieldID, field.TypeInt64))
if ps := _u.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
if value, ok := _u.mutation.UpdatedAt(); ok {
_spec.SetField(userattributedefinition.FieldUpdatedAt, field.TypeTime, value)
}
if value, ok := _u.mutation.DeletedAt(); ok {
_spec.SetField(userattributedefinition.FieldDeletedAt, field.TypeTime, value)
}
if _u.mutation.DeletedAtCleared() {
_spec.ClearField(userattributedefinition.FieldDeletedAt, field.TypeTime)
}
if value, ok := _u.mutation.Key(); ok {
_spec.SetField(userattributedefinition.FieldKey, field.TypeString, value)
}
if value, ok := _u.mutation.Name(); ok {
_spec.SetField(userattributedefinition.FieldName, field.TypeString, value)
}
if value, ok := _u.mutation.Description(); ok {
_spec.SetField(userattributedefinition.FieldDescription, field.TypeString, value)
}
if value, ok := _u.mutation.GetType(); ok {
_spec.SetField(userattributedefinition.FieldType, field.TypeString, value)
}
if value, ok := _u.mutation.Options(); ok {
_spec.SetField(userattributedefinition.FieldOptions, field.TypeJSON, value)
}
if value, ok := _u.mutation.AppendedOptions(); ok {
_spec.AddModifier(func(u *sql.UpdateBuilder) {
sqljson.Append(u, userattributedefinition.FieldOptions, value)
})
}
if value, ok := _u.mutation.Required(); ok {
_spec.SetField(userattributedefinition.FieldRequired, field.TypeBool, value)
}
if value, ok := _u.mutation.Validation(); ok {
_spec.SetField(userattributedefinition.FieldValidation, field.TypeJSON, value)
}
if value, ok := _u.mutation.Placeholder(); ok {
_spec.SetField(userattributedefinition.FieldPlaceholder, field.TypeString, value)
}
if value, ok := _u.mutation.DisplayOrder(); ok {
_spec.SetField(userattributedefinition.FieldDisplayOrder, field.TypeInt, value)
}
if value, ok := _u.mutation.AddedDisplayOrder(); ok {
_spec.AddField(userattributedefinition.FieldDisplayOrder, field.TypeInt, value)
}
if value, ok := _u.mutation.Enabled(); ok {
_spec.SetField(userattributedefinition.FieldEnabled, field.TypeBool, value)
}
if _u.mutation.ValuesCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M,
Inverse: false,
Table: userattributedefinition.ValuesTable,
Columns: []string{userattributedefinition.ValuesColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(userattributevalue.FieldID, field.TypeInt64),
},
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
}
if nodes := _u.mutation.RemovedValuesIDs(); len(nodes) > 0 && !_u.mutation.ValuesCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M,
Inverse: false,
Table: userattributedefinition.ValuesTable,
Columns: []string{userattributedefinition.ValuesColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(userattributevalue.FieldID, field.TypeInt64),
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
}
if nodes := _u.mutation.ValuesIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M,
Inverse: false,
Table: userattributedefinition.ValuesTable,
Columns: []string{userattributedefinition.ValuesColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(userattributevalue.FieldID, field.TypeInt64),
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges.Add = append(_spec.Edges.Add, edge)
}
if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil {
if _, ok := err.(*sqlgraph.NotFoundError); ok {
err = &NotFoundError{userattributedefinition.Label}
} else if sqlgraph.IsConstraintError(err) {
err = &ConstraintError{msg: err.Error(), wrap: err}
}
return 0, err
}
_u.mutation.done = true
return _node, nil
}
// UserAttributeDefinitionUpdateOne is the builder for updating a single UserAttributeDefinition entity.
type UserAttributeDefinitionUpdateOne struct {
config
fields []string
hooks []Hook
mutation *UserAttributeDefinitionMutation
}
// SetUpdatedAt sets the "updated_at" field.
func (_u *UserAttributeDefinitionUpdateOne) SetUpdatedAt(v time.Time) *UserAttributeDefinitionUpdateOne {
_u.mutation.SetUpdatedAt(v)
return _u
}
// SetDeletedAt sets the "deleted_at" field.
func (_u *UserAttributeDefinitionUpdateOne) SetDeletedAt(v time.Time) *UserAttributeDefinitionUpdateOne {
_u.mutation.SetDeletedAt(v)
return _u
}
// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil.
func (_u *UserAttributeDefinitionUpdateOne) SetNillableDeletedAt(v *time.Time) *UserAttributeDefinitionUpdateOne {
if v != nil {
_u.SetDeletedAt(*v)
}
return _u
}
// ClearDeletedAt clears the value of the "deleted_at" field.
func (_u *UserAttributeDefinitionUpdateOne) ClearDeletedAt() *UserAttributeDefinitionUpdateOne {
_u.mutation.ClearDeletedAt()
return _u
}
// SetKey sets the "key" field.
func (_u *UserAttributeDefinitionUpdateOne) SetKey(v string) *UserAttributeDefinitionUpdateOne {
_u.mutation.SetKey(v)
return _u
}
// SetNillableKey sets the "key" field if the given value is not nil.
func (_u *UserAttributeDefinitionUpdateOne) SetNillableKey(v *string) *UserAttributeDefinitionUpdateOne {
if v != nil {
_u.SetKey(*v)
}
return _u
}
// SetName sets the "name" field.
func (_u *UserAttributeDefinitionUpdateOne) SetName(v string) *UserAttributeDefinitionUpdateOne {
_u.mutation.SetName(v)
return _u
}
// SetNillableName sets the "name" field if the given value is not nil.
func (_u *UserAttributeDefinitionUpdateOne) SetNillableName(v *string) *UserAttributeDefinitionUpdateOne {
if v != nil {
_u.SetName(*v)
}
return _u
}
// SetDescription sets the "description" field.
func (_u *UserAttributeDefinitionUpdateOne) SetDescription(v string) *UserAttributeDefinitionUpdateOne {
_u.mutation.SetDescription(v)
return _u
}
// SetNillableDescription sets the "description" field if the given value is not nil.
func (_u *UserAttributeDefinitionUpdateOne) SetNillableDescription(v *string) *UserAttributeDefinitionUpdateOne {
if v != nil {
_u.SetDescription(*v)
}
return _u
}
// SetType sets the "type" field.
func (_u *UserAttributeDefinitionUpdateOne) SetType(v string) *UserAttributeDefinitionUpdateOne {
_u.mutation.SetType(v)
return _u
}
// SetNillableType sets the "type" field if the given value is not nil.
func (_u *UserAttributeDefinitionUpdateOne) SetNillableType(v *string) *UserAttributeDefinitionUpdateOne {
if v != nil {
_u.SetType(*v)
}
return _u
}
// SetOptions sets the "options" field.
func (_u *UserAttributeDefinitionUpdateOne) SetOptions(v []map[string]interface{}) *UserAttributeDefinitionUpdateOne {
_u.mutation.SetOptions(v)
return _u
}
// AppendOptions appends value to the "options" field.
func (_u *UserAttributeDefinitionUpdateOne) AppendOptions(v []map[string]interface{}) *UserAttributeDefinitionUpdateOne {
_u.mutation.AppendOptions(v)
return _u
}
// SetRequired sets the "required" field.
func (_u *UserAttributeDefinitionUpdateOne) SetRequired(v bool) *UserAttributeDefinitionUpdateOne {
_u.mutation.SetRequired(v)
return _u
}
// SetNillableRequired sets the "required" field if the given value is not nil.
func (_u *UserAttributeDefinitionUpdateOne) SetNillableRequired(v *bool) *UserAttributeDefinitionUpdateOne {
if v != nil {
_u.SetRequired(*v)
}
return _u
}
// SetValidation sets the "validation" field.
func (_u *UserAttributeDefinitionUpdateOne) SetValidation(v map[string]interface{}) *UserAttributeDefinitionUpdateOne {
_u.mutation.SetValidation(v)
return _u
}
// SetPlaceholder sets the "placeholder" field.
func (_u *UserAttributeDefinitionUpdateOne) SetPlaceholder(v string) *UserAttributeDefinitionUpdateOne {
_u.mutation.SetPlaceholder(v)
return _u
}
// SetNillablePlaceholder sets the "placeholder" field if the given value is not nil.
func (_u *UserAttributeDefinitionUpdateOne) SetNillablePlaceholder(v *string) *UserAttributeDefinitionUpdateOne {
if v != nil {
_u.SetPlaceholder(*v)
}
return _u
}
// SetDisplayOrder sets the "display_order" field.
func (_u *UserAttributeDefinitionUpdateOne) SetDisplayOrder(v int) *UserAttributeDefinitionUpdateOne {
_u.mutation.ResetDisplayOrder()
_u.mutation.SetDisplayOrder(v)
return _u
}
// SetNillableDisplayOrder sets the "display_order" field if the given value is not nil.
func (_u *UserAttributeDefinitionUpdateOne) SetNillableDisplayOrder(v *int) *UserAttributeDefinitionUpdateOne {
if v != nil {
_u.SetDisplayOrder(*v)
}
return _u
}
// AddDisplayOrder adds value to the "display_order" field.
func (_u *UserAttributeDefinitionUpdateOne) AddDisplayOrder(v int) *UserAttributeDefinitionUpdateOne {
_u.mutation.AddDisplayOrder(v)
return _u
}
// SetEnabled sets the "enabled" field.
func (_u *UserAttributeDefinitionUpdateOne) SetEnabled(v bool) *UserAttributeDefinitionUpdateOne {
_u.mutation.SetEnabled(v)
return _u
}
// SetNillableEnabled sets the "enabled" field if the given value is not nil.
func (_u *UserAttributeDefinitionUpdateOne) SetNillableEnabled(v *bool) *UserAttributeDefinitionUpdateOne {
if v != nil {
_u.SetEnabled(*v)
}
return _u
}
// AddValueIDs adds the "values" edge to the UserAttributeValue entity by IDs.
func (_u *UserAttributeDefinitionUpdateOne) AddValueIDs(ids ...int64) *UserAttributeDefinitionUpdateOne {
_u.mutation.AddValueIDs(ids...)
return _u
}
// AddValues adds the "values" edges to the UserAttributeValue entity.
func (_u *UserAttributeDefinitionUpdateOne) AddValues(v ...*UserAttributeValue) *UserAttributeDefinitionUpdateOne {
ids := make([]int64, len(v))
for i := range v {
ids[i] = v[i].ID
}
return _u.AddValueIDs(ids...)
}
// Mutation returns the UserAttributeDefinitionMutation object of the builder.
func (_u *UserAttributeDefinitionUpdateOne) Mutation() *UserAttributeDefinitionMutation {
return _u.mutation
}
// ClearValues clears all "values" edges to the UserAttributeValue entity.
func (_u *UserAttributeDefinitionUpdateOne) ClearValues() *UserAttributeDefinitionUpdateOne {
_u.mutation.ClearValues()
return _u
}
// RemoveValueIDs removes the "values" edge to UserAttributeValue entities by IDs.
func (_u *UserAttributeDefinitionUpdateOne) RemoveValueIDs(ids ...int64) *UserAttributeDefinitionUpdateOne {
_u.mutation.RemoveValueIDs(ids...)
return _u
}
// RemoveValues removes "values" edges to UserAttributeValue entities.
func (_u *UserAttributeDefinitionUpdateOne) RemoveValues(v ...*UserAttributeValue) *UserAttributeDefinitionUpdateOne {
ids := make([]int64, len(v))
for i := range v {
ids[i] = v[i].ID
}
return _u.RemoveValueIDs(ids...)
}
// Where appends a list predicates to the UserAttributeDefinitionUpdate builder.
func (_u *UserAttributeDefinitionUpdateOne) Where(ps ...predicate.UserAttributeDefinition) *UserAttributeDefinitionUpdateOne {
_u.mutation.Where(ps...)
return _u
}
// Select allows selecting one or more fields (columns) of the returned entity.
// The default is selecting all fields defined in the entity schema.
func (_u *UserAttributeDefinitionUpdateOne) Select(field string, fields ...string) *UserAttributeDefinitionUpdateOne {
_u.fields = append([]string{field}, fields...)
return _u
}
// Save executes the query and returns the updated UserAttributeDefinition entity.
func (_u *UserAttributeDefinitionUpdateOne) Save(ctx context.Context) (*UserAttributeDefinition, error) {
if err := _u.defaults(); err != nil {
return nil, err
}
return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks)
}
// SaveX is like Save, but panics if an error occurs.
func (_u *UserAttributeDefinitionUpdateOne) SaveX(ctx context.Context) *UserAttributeDefinition {
node, err := _u.Save(ctx)
if err != nil {
panic(err)
}
return node
}
// Exec executes the query on the entity.
func (_u *UserAttributeDefinitionUpdateOne) Exec(ctx context.Context) error {
_, err := _u.Save(ctx)
return err
}
// ExecX is like Exec, but panics if an error occurs.
func (_u *UserAttributeDefinitionUpdateOne) ExecX(ctx context.Context) {
if err := _u.Exec(ctx); err != nil {
panic(err)
}
}
// defaults sets the default values of the builder before save.
func (_u *UserAttributeDefinitionUpdateOne) defaults() error {
if _, ok := _u.mutation.UpdatedAt(); !ok {
if userattributedefinition.UpdateDefaultUpdatedAt == nil {
return fmt.Errorf("ent: uninitialized userattributedefinition.UpdateDefaultUpdatedAt (forgotten import ent/runtime?)")
}
v := userattributedefinition.UpdateDefaultUpdatedAt()
_u.mutation.SetUpdatedAt(v)
}
return nil
}
// check runs all checks and user-defined validators on the builder.
func (_u *UserAttributeDefinitionUpdateOne) check() error {
if v, ok := _u.mutation.Key(); ok {
if err := userattributedefinition.KeyValidator(v); err != nil {
return &ValidationError{Name: "key", err: fmt.Errorf(`ent: validator failed for field "UserAttributeDefinition.key": %w`, err)}
}
}
if v, ok := _u.mutation.Name(); ok {
if err := userattributedefinition.NameValidator(v); err != nil {
return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "UserAttributeDefinition.name": %w`, err)}
}
}
if v, ok := _u.mutation.GetType(); ok {
if err := userattributedefinition.TypeValidator(v); err != nil {
return &ValidationError{Name: "type", err: fmt.Errorf(`ent: validator failed for field "UserAttributeDefinition.type": %w`, err)}
}
}
if v, ok := _u.mutation.Placeholder(); ok {
if err := userattributedefinition.PlaceholderValidator(v); err != nil {
return &ValidationError{Name: "placeholder", err: fmt.Errorf(`ent: validator failed for field "UserAttributeDefinition.placeholder": %w`, err)}
}
}
return nil
}
func (_u *UserAttributeDefinitionUpdateOne) sqlSave(ctx context.Context) (_node *UserAttributeDefinition, err error) {
if err := _u.check(); err != nil {
return _node, err
}
_spec := sqlgraph.NewUpdateSpec(userattributedefinition.Table, userattributedefinition.Columns, sqlgraph.NewFieldSpec(userattributedefinition.FieldID, field.TypeInt64))
id, ok := _u.mutation.ID()
if !ok {
return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "UserAttributeDefinition.id" for update`)}
}
_spec.Node.ID.Value = id
if fields := _u.fields; len(fields) > 0 {
_spec.Node.Columns = make([]string, 0, len(fields))
_spec.Node.Columns = append(_spec.Node.Columns, userattributedefinition.FieldID)
for _, f := range fields {
if !userattributedefinition.ValidColumn(f) {
return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
}
if f != userattributedefinition.FieldID {
_spec.Node.Columns = append(_spec.Node.Columns, f)
}
}
}
if ps := _u.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
if value, ok := _u.mutation.UpdatedAt(); ok {
_spec.SetField(userattributedefinition.FieldUpdatedAt, field.TypeTime, value)
}
if value, ok := _u.mutation.DeletedAt(); ok {
_spec.SetField(userattributedefinition.FieldDeletedAt, field.TypeTime, value)
}
if _u.mutation.DeletedAtCleared() {
_spec.ClearField(userattributedefinition.FieldDeletedAt, field.TypeTime)
}
if value, ok := _u.mutation.Key(); ok {
_spec.SetField(userattributedefinition.FieldKey, field.TypeString, value)
}
if value, ok := _u.mutation.Name(); ok {
_spec.SetField(userattributedefinition.FieldName, field.TypeString, value)
}
if value, ok := _u.mutation.Description(); ok {
_spec.SetField(userattributedefinition.FieldDescription, field.TypeString, value)
}
if value, ok := _u.mutation.GetType(); ok {
_spec.SetField(userattributedefinition.FieldType, field.TypeString, value)
}
if value, ok := _u.mutation.Options(); ok {
_spec.SetField(userattributedefinition.FieldOptions, field.TypeJSON, value)
}
if value, ok := _u.mutation.AppendedOptions(); ok {
_spec.AddModifier(func(u *sql.UpdateBuilder) {
sqljson.Append(u, userattributedefinition.FieldOptions, value)
})
}
if value, ok := _u.mutation.Required(); ok {
_spec.SetField(userattributedefinition.FieldRequired, field.TypeBool, value)
}
if value, ok := _u.mutation.Validation(); ok {
_spec.SetField(userattributedefinition.FieldValidation, field.TypeJSON, value)
}
if value, ok := _u.mutation.Placeholder(); ok {
_spec.SetField(userattributedefinition.FieldPlaceholder, field.TypeString, value)
}
if value, ok := _u.mutation.DisplayOrder(); ok {
_spec.SetField(userattributedefinition.FieldDisplayOrder, field.TypeInt, value)
}
if value, ok := _u.mutation.AddedDisplayOrder(); ok {
_spec.AddField(userattributedefinition.FieldDisplayOrder, field.TypeInt, value)
}
if value, ok := _u.mutation.Enabled(); ok {
_spec.SetField(userattributedefinition.FieldEnabled, field.TypeBool, value)
}
if _u.mutation.ValuesCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M,
Inverse: false,
Table: userattributedefinition.ValuesTable,
Columns: []string{userattributedefinition.ValuesColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(userattributevalue.FieldID, field.TypeInt64),
},
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
}
if nodes := _u.mutation.RemovedValuesIDs(); len(nodes) > 0 && !_u.mutation.ValuesCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M,
Inverse: false,
Table: userattributedefinition.ValuesTable,
Columns: []string{userattributedefinition.ValuesColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(userattributevalue.FieldID, field.TypeInt64),
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
}
if nodes := _u.mutation.ValuesIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M,
Inverse: false,
Table: userattributedefinition.ValuesTable,
Columns: []string{userattributedefinition.ValuesColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(userattributevalue.FieldID, field.TypeInt64),
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges.Add = append(_spec.Edges.Add, edge)
}
_node = &UserAttributeDefinition{config: _u.config}
_spec.Assign = _node.assignValues
_spec.ScanValues = _node.scanValues
if err = sqlgraph.UpdateNode(ctx, _u.driver, _spec); err != nil {
if _, ok := err.(*sqlgraph.NotFoundError); ok {
err = &NotFoundError{userattributedefinition.Label}
} else if sqlgraph.IsConstraintError(err) {
err = &ConstraintError{msg: err.Error(), wrap: err}
}
return nil, err
}
_u.mutation.done = true
return _node, nil
}

View File

@ -0,0 +1,198 @@
// Code generated by ent, DO NOT EDIT.
package ent
import (
"fmt"
"strings"
"time"
"entgo.io/ent"
"entgo.io/ent/dialect/sql"
"github.com/Wei-Shaw/sub2api/ent/user"
"github.com/Wei-Shaw/sub2api/ent/userattributedefinition"
"github.com/Wei-Shaw/sub2api/ent/userattributevalue"
)
// UserAttributeValue is the model entity for the UserAttributeValue schema.
type UserAttributeValue struct {
config `json:"-"`
// ID of the ent.
ID int64 `json:"id,omitempty"`
// CreatedAt holds the value of the "created_at" field.
CreatedAt time.Time `json:"created_at,omitempty"`
// UpdatedAt holds the value of the "updated_at" field.
UpdatedAt time.Time `json:"updated_at,omitempty"`
// UserID holds the value of the "user_id" field.
UserID int64 `json:"user_id,omitempty"`
// AttributeID holds the value of the "attribute_id" field.
AttributeID int64 `json:"attribute_id,omitempty"`
// Value holds the value of the "value" field.
Value string `json:"value,omitempty"`
// Edges holds the relations/edges for other nodes in the graph.
// The values are being populated by the UserAttributeValueQuery when eager-loading is set.
Edges UserAttributeValueEdges `json:"edges"`
selectValues sql.SelectValues
}
// UserAttributeValueEdges holds the relations/edges for other nodes in the graph.
type UserAttributeValueEdges struct {
// User holds the value of the user edge.
User *User `json:"user,omitempty"`
// Definition holds the value of the definition edge.
Definition *UserAttributeDefinition `json:"definition,omitempty"`
// loadedTypes holds the information for reporting if a
// type was loaded (or requested) in eager-loading or not.
loadedTypes [2]bool
}
// UserOrErr returns the User value or an error if the edge
// was not loaded in eager-loading, or loaded but was not found.
func (e UserAttributeValueEdges) UserOrErr() (*User, error) {
if e.User != nil {
return e.User, nil
} else if e.loadedTypes[0] {
return nil, &NotFoundError{label: user.Label}
}
return nil, &NotLoadedError{edge: "user"}
}
// DefinitionOrErr returns the Definition value or an error if the edge
// was not loaded in eager-loading, or loaded but was not found.
func (e UserAttributeValueEdges) DefinitionOrErr() (*UserAttributeDefinition, error) {
if e.Definition != nil {
return e.Definition, nil
} else if e.loadedTypes[1] {
return nil, &NotFoundError{label: userattributedefinition.Label}
}
return nil, &NotLoadedError{edge: "definition"}
}
// scanValues returns the types for scanning values from sql.Rows.
func (*UserAttributeValue) scanValues(columns []string) ([]any, error) {
values := make([]any, len(columns))
for i := range columns {
switch columns[i] {
case userattributevalue.FieldID, userattributevalue.FieldUserID, userattributevalue.FieldAttributeID:
values[i] = new(sql.NullInt64)
case userattributevalue.FieldValue:
values[i] = new(sql.NullString)
case userattributevalue.FieldCreatedAt, userattributevalue.FieldUpdatedAt:
values[i] = new(sql.NullTime)
default:
values[i] = new(sql.UnknownType)
}
}
return values, nil
}
// assignValues assigns the values that were returned from sql.Rows (after scanning)
// to the UserAttributeValue fields.
func (_m *UserAttributeValue) assignValues(columns []string, values []any) error {
if m, n := len(values), len(columns); m < n {
return fmt.Errorf("mismatch number of scan values: %d != %d", m, n)
}
for i := range columns {
switch columns[i] {
case userattributevalue.FieldID:
value, ok := values[i].(*sql.NullInt64)
if !ok {
return fmt.Errorf("unexpected type %T for field id", value)
}
_m.ID = int64(value.Int64)
case userattributevalue.FieldCreatedAt:
if value, ok := values[i].(*sql.NullTime); !ok {
return fmt.Errorf("unexpected type %T for field created_at", values[i])
} else if value.Valid {
_m.CreatedAt = value.Time
}
case userattributevalue.FieldUpdatedAt:
if value, ok := values[i].(*sql.NullTime); !ok {
return fmt.Errorf("unexpected type %T for field updated_at", values[i])
} else if value.Valid {
_m.UpdatedAt = value.Time
}
case userattributevalue.FieldUserID:
if value, ok := values[i].(*sql.NullInt64); !ok {
return fmt.Errorf("unexpected type %T for field user_id", values[i])
} else if value.Valid {
_m.UserID = value.Int64
}
case userattributevalue.FieldAttributeID:
if value, ok := values[i].(*sql.NullInt64); !ok {
return fmt.Errorf("unexpected type %T for field attribute_id", values[i])
} else if value.Valid {
_m.AttributeID = value.Int64
}
case userattributevalue.FieldValue:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field value", values[i])
} else if value.Valid {
_m.Value = value.String
}
default:
_m.selectValues.Set(columns[i], values[i])
}
}
return nil
}
// GetValue returns the ent.Value that was dynamically selected and assigned to the UserAttributeValue.
// This includes values selected through modifiers, order, etc.
func (_m *UserAttributeValue) GetValue(name string) (ent.Value, error) {
return _m.selectValues.Get(name)
}
// QueryUser queries the "user" edge of the UserAttributeValue entity.
func (_m *UserAttributeValue) QueryUser() *UserQuery {
return NewUserAttributeValueClient(_m.config).QueryUser(_m)
}
// QueryDefinition queries the "definition" edge of the UserAttributeValue entity.
func (_m *UserAttributeValue) QueryDefinition() *UserAttributeDefinitionQuery {
return NewUserAttributeValueClient(_m.config).QueryDefinition(_m)
}
// Update returns a builder for updating this UserAttributeValue.
// Note that you need to call UserAttributeValue.Unwrap() before calling this method if this UserAttributeValue
// was returned from a transaction, and the transaction was committed or rolled back.
func (_m *UserAttributeValue) Update() *UserAttributeValueUpdateOne {
return NewUserAttributeValueClient(_m.config).UpdateOne(_m)
}
// Unwrap unwraps the UserAttributeValue entity that was returned from a transaction after it was closed,
// so that all future queries will be executed through the driver which created the transaction.
func (_m *UserAttributeValue) Unwrap() *UserAttributeValue {
_tx, ok := _m.config.driver.(*txDriver)
if !ok {
panic("ent: UserAttributeValue is not a transactional entity")
}
_m.config.driver = _tx.drv
return _m
}
// String implements the fmt.Stringer.
func (_m *UserAttributeValue) String() string {
var builder strings.Builder
builder.WriteString("UserAttributeValue(")
builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID))
builder.WriteString("created_at=")
builder.WriteString(_m.CreatedAt.Format(time.ANSIC))
builder.WriteString(", ")
builder.WriteString("updated_at=")
builder.WriteString(_m.UpdatedAt.Format(time.ANSIC))
builder.WriteString(", ")
builder.WriteString("user_id=")
builder.WriteString(fmt.Sprintf("%v", _m.UserID))
builder.WriteString(", ")
builder.WriteString("attribute_id=")
builder.WriteString(fmt.Sprintf("%v", _m.AttributeID))
builder.WriteString(", ")
builder.WriteString("value=")
builder.WriteString(_m.Value)
builder.WriteByte(')')
return builder.String()
}
// UserAttributeValues is a parsable slice of UserAttributeValue.
type UserAttributeValues []*UserAttributeValue

View File

@ -0,0 +1,139 @@
// Code generated by ent, DO NOT EDIT.
package userattributevalue
import (
"time"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
)
const (
// Label holds the string label denoting the userattributevalue type in the database.
Label = "user_attribute_value"
// FieldID holds the string denoting the id field in the database.
FieldID = "id"
// FieldCreatedAt holds the string denoting the created_at field in the database.
FieldCreatedAt = "created_at"
// FieldUpdatedAt holds the string denoting the updated_at field in the database.
FieldUpdatedAt = "updated_at"
// FieldUserID holds the string denoting the user_id field in the database.
FieldUserID = "user_id"
// FieldAttributeID holds the string denoting the attribute_id field in the database.
FieldAttributeID = "attribute_id"
// FieldValue holds the string denoting the value field in the database.
FieldValue = "value"
// EdgeUser holds the string denoting the user edge name in mutations.
EdgeUser = "user"
// EdgeDefinition holds the string denoting the definition edge name in mutations.
EdgeDefinition = "definition"
// Table holds the table name of the userattributevalue in the database.
Table = "user_attribute_values"
// UserTable is the table that holds the user relation/edge.
UserTable = "user_attribute_values"
// UserInverseTable is the table name for the User entity.
// It exists in this package in order to avoid circular dependency with the "user" package.
UserInverseTable = "users"
// UserColumn is the table column denoting the user relation/edge.
UserColumn = "user_id"
// DefinitionTable is the table that holds the definition relation/edge.
DefinitionTable = "user_attribute_values"
// DefinitionInverseTable is the table name for the UserAttributeDefinition entity.
// It exists in this package in order to avoid circular dependency with the "userattributedefinition" package.
DefinitionInverseTable = "user_attribute_definitions"
// DefinitionColumn is the table column denoting the definition relation/edge.
DefinitionColumn = "attribute_id"
)
// Columns holds all SQL columns for userattributevalue fields.
var Columns = []string{
FieldID,
FieldCreatedAt,
FieldUpdatedAt,
FieldUserID,
FieldAttributeID,
FieldValue,
}
// ValidColumn reports if the column name is valid (part of the table columns).
func ValidColumn(column string) bool {
for i := range Columns {
if column == Columns[i] {
return true
}
}
return false
}
var (
// DefaultCreatedAt holds the default value on creation for the "created_at" field.
DefaultCreatedAt func() time.Time
// DefaultUpdatedAt holds the default value on creation for the "updated_at" field.
DefaultUpdatedAt func() time.Time
// UpdateDefaultUpdatedAt holds the default value on update for the "updated_at" field.
UpdateDefaultUpdatedAt func() time.Time
// DefaultValue holds the default value on creation for the "value" field.
DefaultValue string
)
// OrderOption defines the ordering options for the UserAttributeValue queries.
type OrderOption func(*sql.Selector)
// ByID orders the results by the id field.
func ByID(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldID, opts...).ToFunc()
}
// ByCreatedAt orders the results by the created_at field.
func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldCreatedAt, opts...).ToFunc()
}
// ByUpdatedAt orders the results by the updated_at field.
func ByUpdatedAt(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldUpdatedAt, opts...).ToFunc()
}
// ByUserID orders the results by the user_id field.
func ByUserID(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldUserID, opts...).ToFunc()
}
// ByAttributeID orders the results by the attribute_id field.
func ByAttributeID(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldAttributeID, opts...).ToFunc()
}
// ByValue orders the results by the value field.
func ByValue(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldValue, opts...).ToFunc()
}
// ByUserField orders the results by user field.
func ByUserField(field string, opts ...sql.OrderTermOption) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborTerms(s, newUserStep(), sql.OrderByField(field, opts...))
}
}
// ByDefinitionField orders the results by definition field.
func ByDefinitionField(field string, opts ...sql.OrderTermOption) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborTerms(s, newDefinitionStep(), sql.OrderByField(field, opts...))
}
}
func newUserStep() *sqlgraph.Step {
return sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(UserInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.M2O, true, UserTable, UserColumn),
)
}
func newDefinitionStep() *sqlgraph.Step {
return sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(DefinitionInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.M2O, true, DefinitionTable, DefinitionColumn),
)
}

View File

@ -0,0 +1,327 @@
// Code generated by ent, DO NOT EDIT.
package userattributevalue
import (
"time"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"github.com/Wei-Shaw/sub2api/ent/predicate"
)
// ID filters vertices based on their ID field.
func ID(id int64) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldEQ(FieldID, id))
}
// IDEQ applies the EQ predicate on the ID field.
func IDEQ(id int64) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldEQ(FieldID, id))
}
// IDNEQ applies the NEQ predicate on the ID field.
func IDNEQ(id int64) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldNEQ(FieldID, id))
}
// IDIn applies the In predicate on the ID field.
func IDIn(ids ...int64) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldIn(FieldID, ids...))
}
// IDNotIn applies the NotIn predicate on the ID field.
func IDNotIn(ids ...int64) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldNotIn(FieldID, ids...))
}
// IDGT applies the GT predicate on the ID field.
func IDGT(id int64) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldGT(FieldID, id))
}
// IDGTE applies the GTE predicate on the ID field.
func IDGTE(id int64) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldGTE(FieldID, id))
}
// IDLT applies the LT predicate on the ID field.
func IDLT(id int64) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldLT(FieldID, id))
}
// IDLTE applies the LTE predicate on the ID field.
func IDLTE(id int64) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldLTE(FieldID, id))
}
// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ.
func CreatedAt(v time.Time) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldEQ(FieldCreatedAt, v))
}
// UpdatedAt applies equality check predicate on the "updated_at" field. It's identical to UpdatedAtEQ.
func UpdatedAt(v time.Time) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldEQ(FieldUpdatedAt, v))
}
// UserID applies equality check predicate on the "user_id" field. It's identical to UserIDEQ.
func UserID(v int64) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldEQ(FieldUserID, v))
}
// AttributeID applies equality check predicate on the "attribute_id" field. It's identical to AttributeIDEQ.
func AttributeID(v int64) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldEQ(FieldAttributeID, v))
}
// Value applies equality check predicate on the "value" field. It's identical to ValueEQ.
func Value(v string) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldEQ(FieldValue, v))
}
// CreatedAtEQ applies the EQ predicate on the "created_at" field.
func CreatedAtEQ(v time.Time) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldEQ(FieldCreatedAt, v))
}
// CreatedAtNEQ applies the NEQ predicate on the "created_at" field.
func CreatedAtNEQ(v time.Time) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldNEQ(FieldCreatedAt, v))
}
// CreatedAtIn applies the In predicate on the "created_at" field.
func CreatedAtIn(vs ...time.Time) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldIn(FieldCreatedAt, vs...))
}
// CreatedAtNotIn applies the NotIn predicate on the "created_at" field.
func CreatedAtNotIn(vs ...time.Time) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldNotIn(FieldCreatedAt, vs...))
}
// CreatedAtGT applies the GT predicate on the "created_at" field.
func CreatedAtGT(v time.Time) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldGT(FieldCreatedAt, v))
}
// CreatedAtGTE applies the GTE predicate on the "created_at" field.
func CreatedAtGTE(v time.Time) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldGTE(FieldCreatedAt, v))
}
// CreatedAtLT applies the LT predicate on the "created_at" field.
func CreatedAtLT(v time.Time) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldLT(FieldCreatedAt, v))
}
// CreatedAtLTE applies the LTE predicate on the "created_at" field.
func CreatedAtLTE(v time.Time) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldLTE(FieldCreatedAt, v))
}
// UpdatedAtEQ applies the EQ predicate on the "updated_at" field.
func UpdatedAtEQ(v time.Time) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldEQ(FieldUpdatedAt, v))
}
// UpdatedAtNEQ applies the NEQ predicate on the "updated_at" field.
func UpdatedAtNEQ(v time.Time) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldNEQ(FieldUpdatedAt, v))
}
// UpdatedAtIn applies the In predicate on the "updated_at" field.
func UpdatedAtIn(vs ...time.Time) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldIn(FieldUpdatedAt, vs...))
}
// UpdatedAtNotIn applies the NotIn predicate on the "updated_at" field.
func UpdatedAtNotIn(vs ...time.Time) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldNotIn(FieldUpdatedAt, vs...))
}
// UpdatedAtGT applies the GT predicate on the "updated_at" field.
func UpdatedAtGT(v time.Time) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldGT(FieldUpdatedAt, v))
}
// UpdatedAtGTE applies the GTE predicate on the "updated_at" field.
func UpdatedAtGTE(v time.Time) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldGTE(FieldUpdatedAt, v))
}
// UpdatedAtLT applies the LT predicate on the "updated_at" field.
func UpdatedAtLT(v time.Time) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldLT(FieldUpdatedAt, v))
}
// UpdatedAtLTE applies the LTE predicate on the "updated_at" field.
func UpdatedAtLTE(v time.Time) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldLTE(FieldUpdatedAt, v))
}
// UserIDEQ applies the EQ predicate on the "user_id" field.
func UserIDEQ(v int64) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldEQ(FieldUserID, v))
}
// UserIDNEQ applies the NEQ predicate on the "user_id" field.
func UserIDNEQ(v int64) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldNEQ(FieldUserID, v))
}
// UserIDIn applies the In predicate on the "user_id" field.
func UserIDIn(vs ...int64) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldIn(FieldUserID, vs...))
}
// UserIDNotIn applies the NotIn predicate on the "user_id" field.
func UserIDNotIn(vs ...int64) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldNotIn(FieldUserID, vs...))
}
// AttributeIDEQ applies the EQ predicate on the "attribute_id" field.
func AttributeIDEQ(v int64) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldEQ(FieldAttributeID, v))
}
// AttributeIDNEQ applies the NEQ predicate on the "attribute_id" field.
func AttributeIDNEQ(v int64) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldNEQ(FieldAttributeID, v))
}
// AttributeIDIn applies the In predicate on the "attribute_id" field.
func AttributeIDIn(vs ...int64) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldIn(FieldAttributeID, vs...))
}
// AttributeIDNotIn applies the NotIn predicate on the "attribute_id" field.
func AttributeIDNotIn(vs ...int64) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldNotIn(FieldAttributeID, vs...))
}
// ValueEQ applies the EQ predicate on the "value" field.
func ValueEQ(v string) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldEQ(FieldValue, v))
}
// ValueNEQ applies the NEQ predicate on the "value" field.
func ValueNEQ(v string) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldNEQ(FieldValue, v))
}
// ValueIn applies the In predicate on the "value" field.
func ValueIn(vs ...string) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldIn(FieldValue, vs...))
}
// ValueNotIn applies the NotIn predicate on the "value" field.
func ValueNotIn(vs ...string) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldNotIn(FieldValue, vs...))
}
// ValueGT applies the GT predicate on the "value" field.
func ValueGT(v string) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldGT(FieldValue, v))
}
// ValueGTE applies the GTE predicate on the "value" field.
func ValueGTE(v string) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldGTE(FieldValue, v))
}
// ValueLT applies the LT predicate on the "value" field.
func ValueLT(v string) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldLT(FieldValue, v))
}
// ValueLTE applies the LTE predicate on the "value" field.
func ValueLTE(v string) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldLTE(FieldValue, v))
}
// ValueContains applies the Contains predicate on the "value" field.
func ValueContains(v string) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldContains(FieldValue, v))
}
// ValueHasPrefix applies the HasPrefix predicate on the "value" field.
func ValueHasPrefix(v string) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldHasPrefix(FieldValue, v))
}
// ValueHasSuffix applies the HasSuffix predicate on the "value" field.
func ValueHasSuffix(v string) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldHasSuffix(FieldValue, v))
}
// ValueEqualFold applies the EqualFold predicate on the "value" field.
func ValueEqualFold(v string) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldEqualFold(FieldValue, v))
}
// ValueContainsFold applies the ContainsFold predicate on the "value" field.
func ValueContainsFold(v string) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.FieldContainsFold(FieldValue, v))
}
// HasUser applies the HasEdge predicate on the "user" edge.
func HasUser() predicate.UserAttributeValue {
return predicate.UserAttributeValue(func(s *sql.Selector) {
step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.Edge(sqlgraph.M2O, true, UserTable, UserColumn),
)
sqlgraph.HasNeighbors(s, step)
})
}
// HasUserWith applies the HasEdge predicate on the "user" edge with a given conditions (other predicates).
func HasUserWith(preds ...predicate.User) predicate.UserAttributeValue {
return predicate.UserAttributeValue(func(s *sql.Selector) {
step := newUserStep()
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
for _, p := range preds {
p(s)
}
})
})
}
// HasDefinition applies the HasEdge predicate on the "definition" edge.
func HasDefinition() predicate.UserAttributeValue {
return predicate.UserAttributeValue(func(s *sql.Selector) {
step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.Edge(sqlgraph.M2O, true, DefinitionTable, DefinitionColumn),
)
sqlgraph.HasNeighbors(s, step)
})
}
// HasDefinitionWith applies the HasEdge predicate on the "definition" edge with a given conditions (other predicates).
func HasDefinitionWith(preds ...predicate.UserAttributeDefinition) predicate.UserAttributeValue {
return predicate.UserAttributeValue(func(s *sql.Selector) {
step := newDefinitionStep()
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
for _, p := range preds {
p(s)
}
})
})
}
// And groups predicates with the AND operator between them.
func And(predicates ...predicate.UserAttributeValue) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.AndPredicates(predicates...))
}
// Or groups predicates with the OR operator between them.
func Or(predicates ...predicate.UserAttributeValue) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.OrPredicates(predicates...))
}
// Not applies the not operator on the given predicate.
func Not(p predicate.UserAttributeValue) predicate.UserAttributeValue {
return predicate.UserAttributeValue(sql.NotPredicates(p))
}

View File

@ -0,0 +1,731 @@
// Code generated by ent, DO NOT EDIT.
package ent
import (
"context"
"errors"
"fmt"
"time"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field"
"github.com/Wei-Shaw/sub2api/ent/user"
"github.com/Wei-Shaw/sub2api/ent/userattributedefinition"
"github.com/Wei-Shaw/sub2api/ent/userattributevalue"
)
// UserAttributeValueCreate is the builder for creating a UserAttributeValue entity.
type UserAttributeValueCreate struct {
config
mutation *UserAttributeValueMutation
hooks []Hook
conflict []sql.ConflictOption
}
// SetCreatedAt sets the "created_at" field.
func (_c *UserAttributeValueCreate) SetCreatedAt(v time.Time) *UserAttributeValueCreate {
_c.mutation.SetCreatedAt(v)
return _c
}
// SetNillableCreatedAt sets the "created_at" field if the given value is not nil.
func (_c *UserAttributeValueCreate) SetNillableCreatedAt(v *time.Time) *UserAttributeValueCreate {
if v != nil {
_c.SetCreatedAt(*v)
}
return _c
}
// SetUpdatedAt sets the "updated_at" field.
func (_c *UserAttributeValueCreate) SetUpdatedAt(v time.Time) *UserAttributeValueCreate {
_c.mutation.SetUpdatedAt(v)
return _c
}
// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil.
func (_c *UserAttributeValueCreate) SetNillableUpdatedAt(v *time.Time) *UserAttributeValueCreate {
if v != nil {
_c.SetUpdatedAt(*v)
}
return _c
}
// SetUserID sets the "user_id" field.
func (_c *UserAttributeValueCreate) SetUserID(v int64) *UserAttributeValueCreate {
_c.mutation.SetUserID(v)
return _c
}
// SetAttributeID sets the "attribute_id" field.
func (_c *UserAttributeValueCreate) SetAttributeID(v int64) *UserAttributeValueCreate {
_c.mutation.SetAttributeID(v)
return _c
}
// SetValue sets the "value" field.
func (_c *UserAttributeValueCreate) SetValue(v string) *UserAttributeValueCreate {
_c.mutation.SetValue(v)
return _c
}
// SetNillableValue sets the "value" field if the given value is not nil.
func (_c *UserAttributeValueCreate) SetNillableValue(v *string) *UserAttributeValueCreate {
if v != nil {
_c.SetValue(*v)
}
return _c
}
// SetUser sets the "user" edge to the User entity.
func (_c *UserAttributeValueCreate) SetUser(v *User) *UserAttributeValueCreate {
return _c.SetUserID(v.ID)
}
// SetDefinitionID sets the "definition" edge to the UserAttributeDefinition entity by ID.
func (_c *UserAttributeValueCreate) SetDefinitionID(id int64) *UserAttributeValueCreate {
_c.mutation.SetDefinitionID(id)
return _c
}
// SetDefinition sets the "definition" edge to the UserAttributeDefinition entity.
func (_c *UserAttributeValueCreate) SetDefinition(v *UserAttributeDefinition) *UserAttributeValueCreate {
return _c.SetDefinitionID(v.ID)
}
// Mutation returns the UserAttributeValueMutation object of the builder.
func (_c *UserAttributeValueCreate) Mutation() *UserAttributeValueMutation {
return _c.mutation
}
// Save creates the UserAttributeValue in the database.
func (_c *UserAttributeValueCreate) Save(ctx context.Context) (*UserAttributeValue, error) {
_c.defaults()
return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks)
}
// SaveX calls Save and panics if Save returns an error.
func (_c *UserAttributeValueCreate) SaveX(ctx context.Context) *UserAttributeValue {
v, err := _c.Save(ctx)
if err != nil {
panic(err)
}
return v
}
// Exec executes the query.
func (_c *UserAttributeValueCreate) Exec(ctx context.Context) error {
_, err := _c.Save(ctx)
return err
}
// ExecX is like Exec, but panics if an error occurs.
func (_c *UserAttributeValueCreate) ExecX(ctx context.Context) {
if err := _c.Exec(ctx); err != nil {
panic(err)
}
}
// defaults sets the default values of the builder before save.
func (_c *UserAttributeValueCreate) defaults() {
if _, ok := _c.mutation.CreatedAt(); !ok {
v := userattributevalue.DefaultCreatedAt()
_c.mutation.SetCreatedAt(v)
}
if _, ok := _c.mutation.UpdatedAt(); !ok {
v := userattributevalue.DefaultUpdatedAt()
_c.mutation.SetUpdatedAt(v)
}
if _, ok := _c.mutation.Value(); !ok {
v := userattributevalue.DefaultValue
_c.mutation.SetValue(v)
}
}
// check runs all checks and user-defined validators on the builder.
func (_c *UserAttributeValueCreate) check() error {
if _, ok := _c.mutation.CreatedAt(); !ok {
return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "UserAttributeValue.created_at"`)}
}
if _, ok := _c.mutation.UpdatedAt(); !ok {
return &ValidationError{Name: "updated_at", err: errors.New(`ent: missing required field "UserAttributeValue.updated_at"`)}
}
if _, ok := _c.mutation.UserID(); !ok {
return &ValidationError{Name: "user_id", err: errors.New(`ent: missing required field "UserAttributeValue.user_id"`)}
}
if _, ok := _c.mutation.AttributeID(); !ok {
return &ValidationError{Name: "attribute_id", err: errors.New(`ent: missing required field "UserAttributeValue.attribute_id"`)}
}
if _, ok := _c.mutation.Value(); !ok {
return &ValidationError{Name: "value", err: errors.New(`ent: missing required field "UserAttributeValue.value"`)}
}
if len(_c.mutation.UserIDs()) == 0 {
return &ValidationError{Name: "user", err: errors.New(`ent: missing required edge "UserAttributeValue.user"`)}
}
if len(_c.mutation.DefinitionIDs()) == 0 {
return &ValidationError{Name: "definition", err: errors.New(`ent: missing required edge "UserAttributeValue.definition"`)}
}
return nil
}
func (_c *UserAttributeValueCreate) sqlSave(ctx context.Context) (*UserAttributeValue, error) {
if err := _c.check(); err != nil {
return nil, err
}
_node, _spec := _c.createSpec()
if err := sqlgraph.CreateNode(ctx, _c.driver, _spec); err != nil {
if sqlgraph.IsConstraintError(err) {
err = &ConstraintError{msg: err.Error(), wrap: err}
}
return nil, err
}
id := _spec.ID.Value.(int64)
_node.ID = int64(id)
_c.mutation.id = &_node.ID
_c.mutation.done = true
return _node, nil
}
func (_c *UserAttributeValueCreate) createSpec() (*UserAttributeValue, *sqlgraph.CreateSpec) {
var (
_node = &UserAttributeValue{config: _c.config}
_spec = sqlgraph.NewCreateSpec(userattributevalue.Table, sqlgraph.NewFieldSpec(userattributevalue.FieldID, field.TypeInt64))
)
_spec.OnConflict = _c.conflict
if value, ok := _c.mutation.CreatedAt(); ok {
_spec.SetField(userattributevalue.FieldCreatedAt, field.TypeTime, value)
_node.CreatedAt = value
}
if value, ok := _c.mutation.UpdatedAt(); ok {
_spec.SetField(userattributevalue.FieldUpdatedAt, field.TypeTime, value)
_node.UpdatedAt = value
}
if value, ok := _c.mutation.Value(); ok {
_spec.SetField(userattributevalue.FieldValue, field.TypeString, value)
_node.Value = value
}
if nodes := _c.mutation.UserIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
Inverse: true,
Table: userattributevalue.UserTable,
Columns: []string{userattributevalue.UserColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt64),
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_node.UserID = nodes[0]
_spec.Edges = append(_spec.Edges, edge)
}
if nodes := _c.mutation.DefinitionIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
Inverse: true,
Table: userattributevalue.DefinitionTable,
Columns: []string{userattributevalue.DefinitionColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(userattributedefinition.FieldID, field.TypeInt64),
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_node.AttributeID = nodes[0]
_spec.Edges = append(_spec.Edges, edge)
}
return _node, _spec
}
// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause
// of the `INSERT` statement. For example:
//
// client.UserAttributeValue.Create().
// SetCreatedAt(v).
// OnConflict(
// // Update the row with the new values
// // the was proposed for insertion.
// sql.ResolveWithNewValues(),
// ).
// // Override some of the fields with custom
// // update values.
// Update(func(u *ent.UserAttributeValueUpsert) {
// SetCreatedAt(v+v).
// }).
// Exec(ctx)
func (_c *UserAttributeValueCreate) OnConflict(opts ...sql.ConflictOption) *UserAttributeValueUpsertOne {
_c.conflict = opts
return &UserAttributeValueUpsertOne{
create: _c,
}
}
// OnConflictColumns calls `OnConflict` and configures the columns
// as conflict target. Using this option is equivalent to using:
//
// client.UserAttributeValue.Create().
// OnConflict(sql.ConflictColumns(columns...)).
// Exec(ctx)
func (_c *UserAttributeValueCreate) OnConflictColumns(columns ...string) *UserAttributeValueUpsertOne {
_c.conflict = append(_c.conflict, sql.ConflictColumns(columns...))
return &UserAttributeValueUpsertOne{
create: _c,
}
}
type (
// UserAttributeValueUpsertOne is the builder for "upsert"-ing
// one UserAttributeValue node.
UserAttributeValueUpsertOne struct {
create *UserAttributeValueCreate
}
// UserAttributeValueUpsert is the "OnConflict" setter.
UserAttributeValueUpsert struct {
*sql.UpdateSet
}
)
// SetUpdatedAt sets the "updated_at" field.
func (u *UserAttributeValueUpsert) SetUpdatedAt(v time.Time) *UserAttributeValueUpsert {
u.Set(userattributevalue.FieldUpdatedAt, v)
return u
}
// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create.
func (u *UserAttributeValueUpsert) UpdateUpdatedAt() *UserAttributeValueUpsert {
u.SetExcluded(userattributevalue.FieldUpdatedAt)
return u
}
// SetUserID sets the "user_id" field.
func (u *UserAttributeValueUpsert) SetUserID(v int64) *UserAttributeValueUpsert {
u.Set(userattributevalue.FieldUserID, v)
return u
}
// UpdateUserID sets the "user_id" field to the value that was provided on create.
func (u *UserAttributeValueUpsert) UpdateUserID() *UserAttributeValueUpsert {
u.SetExcluded(userattributevalue.FieldUserID)
return u
}
// SetAttributeID sets the "attribute_id" field.
func (u *UserAttributeValueUpsert) SetAttributeID(v int64) *UserAttributeValueUpsert {
u.Set(userattributevalue.FieldAttributeID, v)
return u
}
// UpdateAttributeID sets the "attribute_id" field to the value that was provided on create.
func (u *UserAttributeValueUpsert) UpdateAttributeID() *UserAttributeValueUpsert {
u.SetExcluded(userattributevalue.FieldAttributeID)
return u
}
// SetValue sets the "value" field.
func (u *UserAttributeValueUpsert) SetValue(v string) *UserAttributeValueUpsert {
u.Set(userattributevalue.FieldValue, v)
return u
}
// UpdateValue sets the "value" field to the value that was provided on create.
func (u *UserAttributeValueUpsert) UpdateValue() *UserAttributeValueUpsert {
u.SetExcluded(userattributevalue.FieldValue)
return u
}
// UpdateNewValues updates the mutable fields using the new values that were set on create.
// Using this option is equivalent to using:
//
// client.UserAttributeValue.Create().
// OnConflict(
// sql.ResolveWithNewValues(),
// ).
// Exec(ctx)
func (u *UserAttributeValueUpsertOne) UpdateNewValues() *UserAttributeValueUpsertOne {
u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues())
u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) {
if _, exists := u.create.mutation.CreatedAt(); exists {
s.SetIgnore(userattributevalue.FieldCreatedAt)
}
}))
return u
}
// Ignore sets each column to itself in case of conflict.
// Using this option is equivalent to using:
//
// client.UserAttributeValue.Create().
// OnConflict(sql.ResolveWithIgnore()).
// Exec(ctx)
func (u *UserAttributeValueUpsertOne) Ignore() *UserAttributeValueUpsertOne {
u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore())
return u
}
// DoNothing configures the conflict_action to `DO NOTHING`.
// Supported only by SQLite and PostgreSQL.
func (u *UserAttributeValueUpsertOne) DoNothing() *UserAttributeValueUpsertOne {
u.create.conflict = append(u.create.conflict, sql.DoNothing())
return u
}
// Update allows overriding fields `UPDATE` values. See the UserAttributeValueCreate.OnConflict
// documentation for more info.
func (u *UserAttributeValueUpsertOne) Update(set func(*UserAttributeValueUpsert)) *UserAttributeValueUpsertOne {
u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) {
set(&UserAttributeValueUpsert{UpdateSet: update})
}))
return u
}
// SetUpdatedAt sets the "updated_at" field.
func (u *UserAttributeValueUpsertOne) SetUpdatedAt(v time.Time) *UserAttributeValueUpsertOne {
return u.Update(func(s *UserAttributeValueUpsert) {
s.SetUpdatedAt(v)
})
}
// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create.
func (u *UserAttributeValueUpsertOne) UpdateUpdatedAt() *UserAttributeValueUpsertOne {
return u.Update(func(s *UserAttributeValueUpsert) {
s.UpdateUpdatedAt()
})
}
// SetUserID sets the "user_id" field.
func (u *UserAttributeValueUpsertOne) SetUserID(v int64) *UserAttributeValueUpsertOne {
return u.Update(func(s *UserAttributeValueUpsert) {
s.SetUserID(v)
})
}
// UpdateUserID sets the "user_id" field to the value that was provided on create.
func (u *UserAttributeValueUpsertOne) UpdateUserID() *UserAttributeValueUpsertOne {
return u.Update(func(s *UserAttributeValueUpsert) {
s.UpdateUserID()
})
}
// SetAttributeID sets the "attribute_id" field.
func (u *UserAttributeValueUpsertOne) SetAttributeID(v int64) *UserAttributeValueUpsertOne {
return u.Update(func(s *UserAttributeValueUpsert) {
s.SetAttributeID(v)
})
}
// UpdateAttributeID sets the "attribute_id" field to the value that was provided on create.
func (u *UserAttributeValueUpsertOne) UpdateAttributeID() *UserAttributeValueUpsertOne {
return u.Update(func(s *UserAttributeValueUpsert) {
s.UpdateAttributeID()
})
}
// SetValue sets the "value" field.
func (u *UserAttributeValueUpsertOne) SetValue(v string) *UserAttributeValueUpsertOne {
return u.Update(func(s *UserAttributeValueUpsert) {
s.SetValue(v)
})
}
// UpdateValue sets the "value" field to the value that was provided on create.
func (u *UserAttributeValueUpsertOne) UpdateValue() *UserAttributeValueUpsertOne {
return u.Update(func(s *UserAttributeValueUpsert) {
s.UpdateValue()
})
}
// Exec executes the query.
func (u *UserAttributeValueUpsertOne) Exec(ctx context.Context) error {
if len(u.create.conflict) == 0 {
return errors.New("ent: missing options for UserAttributeValueCreate.OnConflict")
}
return u.create.Exec(ctx)
}
// ExecX is like Exec, but panics if an error occurs.
func (u *UserAttributeValueUpsertOne) ExecX(ctx context.Context) {
if err := u.create.Exec(ctx); err != nil {
panic(err)
}
}
// Exec executes the UPSERT query and returns the inserted/updated ID.
func (u *UserAttributeValueUpsertOne) ID(ctx context.Context) (id int64, err error) {
node, err := u.create.Save(ctx)
if err != nil {
return id, err
}
return node.ID, nil
}
// IDX is like ID, but panics if an error occurs.
func (u *UserAttributeValueUpsertOne) IDX(ctx context.Context) int64 {
id, err := u.ID(ctx)
if err != nil {
panic(err)
}
return id
}
// UserAttributeValueCreateBulk is the builder for creating many UserAttributeValue entities in bulk.
type UserAttributeValueCreateBulk struct {
config
err error
builders []*UserAttributeValueCreate
conflict []sql.ConflictOption
}
// Save creates the UserAttributeValue entities in the database.
func (_c *UserAttributeValueCreateBulk) Save(ctx context.Context) ([]*UserAttributeValue, error) {
if _c.err != nil {
return nil, _c.err
}
specs := make([]*sqlgraph.CreateSpec, len(_c.builders))
nodes := make([]*UserAttributeValue, len(_c.builders))
mutators := make([]Mutator, len(_c.builders))
for i := range _c.builders {
func(i int, root context.Context) {
builder := _c.builders[i]
builder.defaults()
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutation, ok := m.(*UserAttributeValueMutation)
if !ok {
return nil, fmt.Errorf("unexpected mutation type %T", m)
}
if err := builder.check(); err != nil {
return nil, err
}
builder.mutation = mutation
var err error
nodes[i], specs[i] = builder.createSpec()
if i < len(mutators)-1 {
_, err = mutators[i+1].Mutate(root, _c.builders[i+1].mutation)
} else {
spec := &sqlgraph.BatchCreateSpec{Nodes: specs}
spec.OnConflict = _c.conflict
// Invoke the actual operation on the latest mutation in the chain.
if err = sqlgraph.BatchCreate(ctx, _c.driver, spec); err != nil {
if sqlgraph.IsConstraintError(err) {
err = &ConstraintError{msg: err.Error(), wrap: err}
}
}
}
if err != nil {
return nil, err
}
mutation.id = &nodes[i].ID
if specs[i].ID.Value != nil {
id := specs[i].ID.Value.(int64)
nodes[i].ID = int64(id)
}
mutation.done = true
return nodes[i], nil
})
for i := len(builder.hooks) - 1; i >= 0; i-- {
mut = builder.hooks[i](mut)
}
mutators[i] = mut
}(i, ctx)
}
if len(mutators) > 0 {
if _, err := mutators[0].Mutate(ctx, _c.builders[0].mutation); err != nil {
return nil, err
}
}
return nodes, nil
}
// SaveX is like Save, but panics if an error occurs.
func (_c *UserAttributeValueCreateBulk) SaveX(ctx context.Context) []*UserAttributeValue {
v, err := _c.Save(ctx)
if err != nil {
panic(err)
}
return v
}
// Exec executes the query.
func (_c *UserAttributeValueCreateBulk) Exec(ctx context.Context) error {
_, err := _c.Save(ctx)
return err
}
// ExecX is like Exec, but panics if an error occurs.
func (_c *UserAttributeValueCreateBulk) ExecX(ctx context.Context) {
if err := _c.Exec(ctx); err != nil {
panic(err)
}
}
// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause
// of the `INSERT` statement. For example:
//
// client.UserAttributeValue.CreateBulk(builders...).
// OnConflict(
// // Update the row with the new values
// // the was proposed for insertion.
// sql.ResolveWithNewValues(),
// ).
// // Override some of the fields with custom
// // update values.
// Update(func(u *ent.UserAttributeValueUpsert) {
// SetCreatedAt(v+v).
// }).
// Exec(ctx)
func (_c *UserAttributeValueCreateBulk) OnConflict(opts ...sql.ConflictOption) *UserAttributeValueUpsertBulk {
_c.conflict = opts
return &UserAttributeValueUpsertBulk{
create: _c,
}
}
// OnConflictColumns calls `OnConflict` and configures the columns
// as conflict target. Using this option is equivalent to using:
//
// client.UserAttributeValue.Create().
// OnConflict(sql.ConflictColumns(columns...)).
// Exec(ctx)
func (_c *UserAttributeValueCreateBulk) OnConflictColumns(columns ...string) *UserAttributeValueUpsertBulk {
_c.conflict = append(_c.conflict, sql.ConflictColumns(columns...))
return &UserAttributeValueUpsertBulk{
create: _c,
}
}
// UserAttributeValueUpsertBulk is the builder for "upsert"-ing
// a bulk of UserAttributeValue nodes.
type UserAttributeValueUpsertBulk struct {
create *UserAttributeValueCreateBulk
}
// UpdateNewValues updates the mutable fields using the new values that
// were set on create. Using this option is equivalent to using:
//
// client.UserAttributeValue.Create().
// OnConflict(
// sql.ResolveWithNewValues(),
// ).
// Exec(ctx)
func (u *UserAttributeValueUpsertBulk) UpdateNewValues() *UserAttributeValueUpsertBulk {
u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues())
u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) {
for _, b := range u.create.builders {
if _, exists := b.mutation.CreatedAt(); exists {
s.SetIgnore(userattributevalue.FieldCreatedAt)
}
}
}))
return u
}
// Ignore sets each column to itself in case of conflict.
// Using this option is equivalent to using:
//
// client.UserAttributeValue.Create().
// OnConflict(sql.ResolveWithIgnore()).
// Exec(ctx)
func (u *UserAttributeValueUpsertBulk) Ignore() *UserAttributeValueUpsertBulk {
u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore())
return u
}
// DoNothing configures the conflict_action to `DO NOTHING`.
// Supported only by SQLite and PostgreSQL.
func (u *UserAttributeValueUpsertBulk) DoNothing() *UserAttributeValueUpsertBulk {
u.create.conflict = append(u.create.conflict, sql.DoNothing())
return u
}
// Update allows overriding fields `UPDATE` values. See the UserAttributeValueCreateBulk.OnConflict
// documentation for more info.
func (u *UserAttributeValueUpsertBulk) Update(set func(*UserAttributeValueUpsert)) *UserAttributeValueUpsertBulk {
u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) {
set(&UserAttributeValueUpsert{UpdateSet: update})
}))
return u
}
// SetUpdatedAt sets the "updated_at" field.
func (u *UserAttributeValueUpsertBulk) SetUpdatedAt(v time.Time) *UserAttributeValueUpsertBulk {
return u.Update(func(s *UserAttributeValueUpsert) {
s.SetUpdatedAt(v)
})
}
// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create.
func (u *UserAttributeValueUpsertBulk) UpdateUpdatedAt() *UserAttributeValueUpsertBulk {
return u.Update(func(s *UserAttributeValueUpsert) {
s.UpdateUpdatedAt()
})
}
// SetUserID sets the "user_id" field.
func (u *UserAttributeValueUpsertBulk) SetUserID(v int64) *UserAttributeValueUpsertBulk {
return u.Update(func(s *UserAttributeValueUpsert) {
s.SetUserID(v)
})
}
// UpdateUserID sets the "user_id" field to the value that was provided on create.
func (u *UserAttributeValueUpsertBulk) UpdateUserID() *UserAttributeValueUpsertBulk {
return u.Update(func(s *UserAttributeValueUpsert) {
s.UpdateUserID()
})
}
// SetAttributeID sets the "attribute_id" field.
func (u *UserAttributeValueUpsertBulk) SetAttributeID(v int64) *UserAttributeValueUpsertBulk {
return u.Update(func(s *UserAttributeValueUpsert) {
s.SetAttributeID(v)
})
}
// UpdateAttributeID sets the "attribute_id" field to the value that was provided on create.
func (u *UserAttributeValueUpsertBulk) UpdateAttributeID() *UserAttributeValueUpsertBulk {
return u.Update(func(s *UserAttributeValueUpsert) {
s.UpdateAttributeID()
})
}
// SetValue sets the "value" field.
func (u *UserAttributeValueUpsertBulk) SetValue(v string) *UserAttributeValueUpsertBulk {
return u.Update(func(s *UserAttributeValueUpsert) {
s.SetValue(v)
})
}
// UpdateValue sets the "value" field to the value that was provided on create.
func (u *UserAttributeValueUpsertBulk) UpdateValue() *UserAttributeValueUpsertBulk {
return u.Update(func(s *UserAttributeValueUpsert) {
s.UpdateValue()
})
}
// Exec executes the query.
func (u *UserAttributeValueUpsertBulk) Exec(ctx context.Context) error {
if u.create.err != nil {
return u.create.err
}
for i, b := range u.create.builders {
if len(b.conflict) != 0 {
return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the UserAttributeValueCreateBulk instead", i)
}
}
if len(u.create.conflict) == 0 {
return errors.New("ent: missing options for UserAttributeValueCreateBulk.OnConflict")
}
return u.create.Exec(ctx)
}
// ExecX is like Exec, but panics if an error occurs.
func (u *UserAttributeValueUpsertBulk) ExecX(ctx context.Context) {
if err := u.create.Exec(ctx); err != nil {
panic(err)
}
}

View File

@ -0,0 +1,88 @@
// Code generated by ent, DO NOT EDIT.
package ent
import (
"context"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field"
"github.com/Wei-Shaw/sub2api/ent/predicate"
"github.com/Wei-Shaw/sub2api/ent/userattributevalue"
)
// UserAttributeValueDelete is the builder for deleting a UserAttributeValue entity.
type UserAttributeValueDelete struct {
config
hooks []Hook
mutation *UserAttributeValueMutation
}
// Where appends a list predicates to the UserAttributeValueDelete builder.
func (_d *UserAttributeValueDelete) Where(ps ...predicate.UserAttributeValue) *UserAttributeValueDelete {
_d.mutation.Where(ps...)
return _d
}
// Exec executes the deletion query and returns how many vertices were deleted.
func (_d *UserAttributeValueDelete) Exec(ctx context.Context) (int, error) {
return withHooks(ctx, _d.sqlExec, _d.mutation, _d.hooks)
}
// ExecX is like Exec, but panics if an error occurs.
func (_d *UserAttributeValueDelete) ExecX(ctx context.Context) int {
n, err := _d.Exec(ctx)
if err != nil {
panic(err)
}
return n
}
func (_d *UserAttributeValueDelete) sqlExec(ctx context.Context) (int, error) {
_spec := sqlgraph.NewDeleteSpec(userattributevalue.Table, sqlgraph.NewFieldSpec(userattributevalue.FieldID, field.TypeInt64))
if ps := _d.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
affected, err := sqlgraph.DeleteNodes(ctx, _d.driver, _spec)
if err != nil && sqlgraph.IsConstraintError(err) {
err = &ConstraintError{msg: err.Error(), wrap: err}
}
_d.mutation.done = true
return affected, err
}
// UserAttributeValueDeleteOne is the builder for deleting a single UserAttributeValue entity.
type UserAttributeValueDeleteOne struct {
_d *UserAttributeValueDelete
}
// Where appends a list predicates to the UserAttributeValueDelete builder.
func (_d *UserAttributeValueDeleteOne) Where(ps ...predicate.UserAttributeValue) *UserAttributeValueDeleteOne {
_d._d.mutation.Where(ps...)
return _d
}
// Exec executes the deletion query.
func (_d *UserAttributeValueDeleteOne) Exec(ctx context.Context) error {
n, err := _d._d.Exec(ctx)
switch {
case err != nil:
return err
case n == 0:
return &NotFoundError{userattributevalue.Label}
default:
return nil
}
}
// ExecX is like Exec, but panics if an error occurs.
func (_d *UserAttributeValueDeleteOne) ExecX(ctx context.Context) {
if err := _d.Exec(ctx); err != nil {
panic(err)
}
}

View File

@ -0,0 +1,681 @@
// Code generated by ent, DO NOT EDIT.
package ent
import (
"context"
"fmt"
"math"
"entgo.io/ent"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field"
"github.com/Wei-Shaw/sub2api/ent/predicate"
"github.com/Wei-Shaw/sub2api/ent/user"
"github.com/Wei-Shaw/sub2api/ent/userattributedefinition"
"github.com/Wei-Shaw/sub2api/ent/userattributevalue"
)
// UserAttributeValueQuery is the builder for querying UserAttributeValue entities.
type UserAttributeValueQuery struct {
config
ctx *QueryContext
order []userattributevalue.OrderOption
inters []Interceptor
predicates []predicate.UserAttributeValue
withUser *UserQuery
withDefinition *UserAttributeDefinitionQuery
// intermediate query (i.e. traversal path).
sql *sql.Selector
path func(context.Context) (*sql.Selector, error)
}
// Where adds a new predicate for the UserAttributeValueQuery builder.
func (_q *UserAttributeValueQuery) Where(ps ...predicate.UserAttributeValue) *UserAttributeValueQuery {
_q.predicates = append(_q.predicates, ps...)
return _q
}
// Limit the number of records to be returned by this query.
func (_q *UserAttributeValueQuery) Limit(limit int) *UserAttributeValueQuery {
_q.ctx.Limit = &limit
return _q
}
// Offset to start from.
func (_q *UserAttributeValueQuery) Offset(offset int) *UserAttributeValueQuery {
_q.ctx.Offset = &offset
return _q
}
// Unique configures the query builder to filter duplicate records on query.
// By default, unique is set to true, and can be disabled using this method.
func (_q *UserAttributeValueQuery) Unique(unique bool) *UserAttributeValueQuery {
_q.ctx.Unique = &unique
return _q
}
// Order specifies how the records should be ordered.
func (_q *UserAttributeValueQuery) Order(o ...userattributevalue.OrderOption) *UserAttributeValueQuery {
_q.order = append(_q.order, o...)
return _q
}
// QueryUser chains the current query on the "user" edge.
func (_q *UserAttributeValueQuery) QueryUser() *UserQuery {
query := (&UserClient{config: _q.config}).Query()
query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
if err := _q.prepareQuery(ctx); err != nil {
return nil, err
}
selector := _q.sqlQuery(ctx)
if err := selector.Err(); err != nil {
return nil, err
}
step := sqlgraph.NewStep(
sqlgraph.From(userattributevalue.Table, userattributevalue.FieldID, selector),
sqlgraph.To(user.Table, user.FieldID),
sqlgraph.Edge(sqlgraph.M2O, true, userattributevalue.UserTable, userattributevalue.UserColumn),
)
fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step)
return fromU, nil
}
return query
}
// QueryDefinition chains the current query on the "definition" edge.
func (_q *UserAttributeValueQuery) QueryDefinition() *UserAttributeDefinitionQuery {
query := (&UserAttributeDefinitionClient{config: _q.config}).Query()
query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
if err := _q.prepareQuery(ctx); err != nil {
return nil, err
}
selector := _q.sqlQuery(ctx)
if err := selector.Err(); err != nil {
return nil, err
}
step := sqlgraph.NewStep(
sqlgraph.From(userattributevalue.Table, userattributevalue.FieldID, selector),
sqlgraph.To(userattributedefinition.Table, userattributedefinition.FieldID),
sqlgraph.Edge(sqlgraph.M2O, true, userattributevalue.DefinitionTable, userattributevalue.DefinitionColumn),
)
fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step)
return fromU, nil
}
return query
}
// First returns the first UserAttributeValue entity from the query.
// Returns a *NotFoundError when no UserAttributeValue was found.
func (_q *UserAttributeValueQuery) First(ctx context.Context) (*UserAttributeValue, error) {
nodes, err := _q.Limit(1).All(setContextOp(ctx, _q.ctx, ent.OpQueryFirst))
if err != nil {
return nil, err
}
if len(nodes) == 0 {
return nil, &NotFoundError{userattributevalue.Label}
}
return nodes[0], nil
}
// FirstX is like First, but panics if an error occurs.
func (_q *UserAttributeValueQuery) FirstX(ctx context.Context) *UserAttributeValue {
node, err := _q.First(ctx)
if err != nil && !IsNotFound(err) {
panic(err)
}
return node
}
// FirstID returns the first UserAttributeValue ID from the query.
// Returns a *NotFoundError when no UserAttributeValue ID was found.
func (_q *UserAttributeValueQuery) FirstID(ctx context.Context) (id int64, err error) {
var ids []int64
if ids, err = _q.Limit(1).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryFirstID)); err != nil {
return
}
if len(ids) == 0 {
err = &NotFoundError{userattributevalue.Label}
return
}
return ids[0], nil
}
// FirstIDX is like FirstID, but panics if an error occurs.
func (_q *UserAttributeValueQuery) FirstIDX(ctx context.Context) int64 {
id, err := _q.FirstID(ctx)
if err != nil && !IsNotFound(err) {
panic(err)
}
return id
}
// Only returns a single UserAttributeValue entity found by the query, ensuring it only returns one.
// Returns a *NotSingularError when more than one UserAttributeValue entity is found.
// Returns a *NotFoundError when no UserAttributeValue entities are found.
func (_q *UserAttributeValueQuery) Only(ctx context.Context) (*UserAttributeValue, error) {
nodes, err := _q.Limit(2).All(setContextOp(ctx, _q.ctx, ent.OpQueryOnly))
if err != nil {
return nil, err
}
switch len(nodes) {
case 1:
return nodes[0], nil
case 0:
return nil, &NotFoundError{userattributevalue.Label}
default:
return nil, &NotSingularError{userattributevalue.Label}
}
}
// OnlyX is like Only, but panics if an error occurs.
func (_q *UserAttributeValueQuery) OnlyX(ctx context.Context) *UserAttributeValue {
node, err := _q.Only(ctx)
if err != nil {
panic(err)
}
return node
}
// OnlyID is like Only, but returns the only UserAttributeValue ID in the query.
// Returns a *NotSingularError when more than one UserAttributeValue ID is found.
// Returns a *NotFoundError when no entities are found.
func (_q *UserAttributeValueQuery) OnlyID(ctx context.Context) (id int64, err error) {
var ids []int64
if ids, err = _q.Limit(2).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryOnlyID)); err != nil {
return
}
switch len(ids) {
case 1:
id = ids[0]
case 0:
err = &NotFoundError{userattributevalue.Label}
default:
err = &NotSingularError{userattributevalue.Label}
}
return
}
// OnlyIDX is like OnlyID, but panics if an error occurs.
func (_q *UserAttributeValueQuery) OnlyIDX(ctx context.Context) int64 {
id, err := _q.OnlyID(ctx)
if err != nil {
panic(err)
}
return id
}
// All executes the query and returns a list of UserAttributeValues.
func (_q *UserAttributeValueQuery) All(ctx context.Context) ([]*UserAttributeValue, error) {
ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll)
if err := _q.prepareQuery(ctx); err != nil {
return nil, err
}
qr := querierAll[[]*UserAttributeValue, *UserAttributeValueQuery]()
return withInterceptors[[]*UserAttributeValue](ctx, _q, qr, _q.inters)
}
// AllX is like All, but panics if an error occurs.
func (_q *UserAttributeValueQuery) AllX(ctx context.Context) []*UserAttributeValue {
nodes, err := _q.All(ctx)
if err != nil {
panic(err)
}
return nodes
}
// IDs executes the query and returns a list of UserAttributeValue IDs.
func (_q *UserAttributeValueQuery) IDs(ctx context.Context) (ids []int64, err error) {
if _q.ctx.Unique == nil && _q.path != nil {
_q.Unique(true)
}
ctx = setContextOp(ctx, _q.ctx, ent.OpQueryIDs)
if err = _q.Select(userattributevalue.FieldID).Scan(ctx, &ids); err != nil {
return nil, err
}
return ids, nil
}
// IDsX is like IDs, but panics if an error occurs.
func (_q *UserAttributeValueQuery) IDsX(ctx context.Context) []int64 {
ids, err := _q.IDs(ctx)
if err != nil {
panic(err)
}
return ids
}
// Count returns the count of the given query.
func (_q *UserAttributeValueQuery) Count(ctx context.Context) (int, error) {
ctx = setContextOp(ctx, _q.ctx, ent.OpQueryCount)
if err := _q.prepareQuery(ctx); err != nil {
return 0, err
}
return withInterceptors[int](ctx, _q, querierCount[*UserAttributeValueQuery](), _q.inters)
}
// CountX is like Count, but panics if an error occurs.
func (_q *UserAttributeValueQuery) CountX(ctx context.Context) int {
count, err := _q.Count(ctx)
if err != nil {
panic(err)
}
return count
}
// Exist returns true if the query has elements in the graph.
func (_q *UserAttributeValueQuery) Exist(ctx context.Context) (bool, error) {
ctx = setContextOp(ctx, _q.ctx, ent.OpQueryExist)
switch _, err := _q.FirstID(ctx); {
case IsNotFound(err):
return false, nil
case err != nil:
return false, fmt.Errorf("ent: check existence: %w", err)
default:
return true, nil
}
}
// ExistX is like Exist, but panics if an error occurs.
func (_q *UserAttributeValueQuery) ExistX(ctx context.Context) bool {
exist, err := _q.Exist(ctx)
if err != nil {
panic(err)
}
return exist
}
// Clone returns a duplicate of the UserAttributeValueQuery builder, including all associated steps. It can be
// used to prepare common query builders and use them differently after the clone is made.
func (_q *UserAttributeValueQuery) Clone() *UserAttributeValueQuery {
if _q == nil {
return nil
}
return &UserAttributeValueQuery{
config: _q.config,
ctx: _q.ctx.Clone(),
order: append([]userattributevalue.OrderOption{}, _q.order...),
inters: append([]Interceptor{}, _q.inters...),
predicates: append([]predicate.UserAttributeValue{}, _q.predicates...),
withUser: _q.withUser.Clone(),
withDefinition: _q.withDefinition.Clone(),
// clone intermediate query.
sql: _q.sql.Clone(),
path: _q.path,
}
}
// WithUser tells the query-builder to eager-load the nodes that are connected to
// the "user" edge. The optional arguments are used to configure the query builder of the edge.
func (_q *UserAttributeValueQuery) WithUser(opts ...func(*UserQuery)) *UserAttributeValueQuery {
query := (&UserClient{config: _q.config}).Query()
for _, opt := range opts {
opt(query)
}
_q.withUser = query
return _q
}
// WithDefinition tells the query-builder to eager-load the nodes that are connected to
// the "definition" edge. The optional arguments are used to configure the query builder of the edge.
func (_q *UserAttributeValueQuery) WithDefinition(opts ...func(*UserAttributeDefinitionQuery)) *UserAttributeValueQuery {
query := (&UserAttributeDefinitionClient{config: _q.config}).Query()
for _, opt := range opts {
opt(query)
}
_q.withDefinition = query
return _q
}
// GroupBy is used to group vertices by one or more fields/columns.
// It is often used with aggregate functions, like: count, max, mean, min, sum.
//
// Example:
//
// var v []struct {
// CreatedAt time.Time `json:"created_at,omitempty"`
// Count int `json:"count,omitempty"`
// }
//
// client.UserAttributeValue.Query().
// GroupBy(userattributevalue.FieldCreatedAt).
// Aggregate(ent.Count()).
// Scan(ctx, &v)
func (_q *UserAttributeValueQuery) GroupBy(field string, fields ...string) *UserAttributeValueGroupBy {
_q.ctx.Fields = append([]string{field}, fields...)
grbuild := &UserAttributeValueGroupBy{build: _q}
grbuild.flds = &_q.ctx.Fields
grbuild.label = userattributevalue.Label
grbuild.scan = grbuild.Scan
return grbuild
}
// Select allows the selection one or more fields/columns for the given query,
// instead of selecting all fields in the entity.
//
// Example:
//
// var v []struct {
// CreatedAt time.Time `json:"created_at,omitempty"`
// }
//
// client.UserAttributeValue.Query().
// Select(userattributevalue.FieldCreatedAt).
// Scan(ctx, &v)
func (_q *UserAttributeValueQuery) Select(fields ...string) *UserAttributeValueSelect {
_q.ctx.Fields = append(_q.ctx.Fields, fields...)
sbuild := &UserAttributeValueSelect{UserAttributeValueQuery: _q}
sbuild.label = userattributevalue.Label
sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan
return sbuild
}
// Aggregate returns a UserAttributeValueSelect configured with the given aggregations.
func (_q *UserAttributeValueQuery) Aggregate(fns ...AggregateFunc) *UserAttributeValueSelect {
return _q.Select().Aggregate(fns...)
}
func (_q *UserAttributeValueQuery) prepareQuery(ctx context.Context) error {
for _, inter := range _q.inters {
if inter == nil {
return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)")
}
if trv, ok := inter.(Traverser); ok {
if err := trv.Traverse(ctx, _q); err != nil {
return err
}
}
}
for _, f := range _q.ctx.Fields {
if !userattributevalue.ValidColumn(f) {
return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
}
}
if _q.path != nil {
prev, err := _q.path(ctx)
if err != nil {
return err
}
_q.sql = prev
}
return nil
}
func (_q *UserAttributeValueQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*UserAttributeValue, error) {
var (
nodes = []*UserAttributeValue{}
_spec = _q.querySpec()
loadedTypes = [2]bool{
_q.withUser != nil,
_q.withDefinition != nil,
}
)
_spec.ScanValues = func(columns []string) ([]any, error) {
return (*UserAttributeValue).scanValues(nil, columns)
}
_spec.Assign = func(columns []string, values []any) error {
node := &UserAttributeValue{config: _q.config}
nodes = append(nodes, node)
node.Edges.loadedTypes = loadedTypes
return node.assignValues(columns, values)
}
for i := range hooks {
hooks[i](ctx, _spec)
}
if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil {
return nil, err
}
if len(nodes) == 0 {
return nodes, nil
}
if query := _q.withUser; query != nil {
if err := _q.loadUser(ctx, query, nodes, nil,
func(n *UserAttributeValue, e *User) { n.Edges.User = e }); err != nil {
return nil, err
}
}
if query := _q.withDefinition; query != nil {
if err := _q.loadDefinition(ctx, query, nodes, nil,
func(n *UserAttributeValue, e *UserAttributeDefinition) { n.Edges.Definition = e }); err != nil {
return nil, err
}
}
return nodes, nil
}
func (_q *UserAttributeValueQuery) loadUser(ctx context.Context, query *UserQuery, nodes []*UserAttributeValue, init func(*UserAttributeValue), assign func(*UserAttributeValue, *User)) error {
ids := make([]int64, 0, len(nodes))
nodeids := make(map[int64][]*UserAttributeValue)
for i := range nodes {
fk := nodes[i].UserID
if _, ok := nodeids[fk]; !ok {
ids = append(ids, fk)
}
nodeids[fk] = append(nodeids[fk], nodes[i])
}
if len(ids) == 0 {
return nil
}
query.Where(user.IDIn(ids...))
neighbors, err := query.All(ctx)
if err != nil {
return err
}
for _, n := range neighbors {
nodes, ok := nodeids[n.ID]
if !ok {
return fmt.Errorf(`unexpected foreign-key "user_id" returned %v`, n.ID)
}
for i := range nodes {
assign(nodes[i], n)
}
}
return nil
}
func (_q *UserAttributeValueQuery) loadDefinition(ctx context.Context, query *UserAttributeDefinitionQuery, nodes []*UserAttributeValue, init func(*UserAttributeValue), assign func(*UserAttributeValue, *UserAttributeDefinition)) error {
ids := make([]int64, 0, len(nodes))
nodeids := make(map[int64][]*UserAttributeValue)
for i := range nodes {
fk := nodes[i].AttributeID
if _, ok := nodeids[fk]; !ok {
ids = append(ids, fk)
}
nodeids[fk] = append(nodeids[fk], nodes[i])
}
if len(ids) == 0 {
return nil
}
query.Where(userattributedefinition.IDIn(ids...))
neighbors, err := query.All(ctx)
if err != nil {
return err
}
for _, n := range neighbors {
nodes, ok := nodeids[n.ID]
if !ok {
return fmt.Errorf(`unexpected foreign-key "attribute_id" returned %v`, n.ID)
}
for i := range nodes {
assign(nodes[i], n)
}
}
return nil
}
func (_q *UserAttributeValueQuery) sqlCount(ctx context.Context) (int, error) {
_spec := _q.querySpec()
_spec.Node.Columns = _q.ctx.Fields
if len(_q.ctx.Fields) > 0 {
_spec.Unique = _q.ctx.Unique != nil && *_q.ctx.Unique
}
return sqlgraph.CountNodes(ctx, _q.driver, _spec)
}
func (_q *UserAttributeValueQuery) querySpec() *sqlgraph.QuerySpec {
_spec := sqlgraph.NewQuerySpec(userattributevalue.Table, userattributevalue.Columns, sqlgraph.NewFieldSpec(userattributevalue.FieldID, field.TypeInt64))
_spec.From = _q.sql
if unique := _q.ctx.Unique; unique != nil {
_spec.Unique = *unique
} else if _q.path != nil {
_spec.Unique = true
}
if fields := _q.ctx.Fields; len(fields) > 0 {
_spec.Node.Columns = make([]string, 0, len(fields))
_spec.Node.Columns = append(_spec.Node.Columns, userattributevalue.FieldID)
for i := range fields {
if fields[i] != userattributevalue.FieldID {
_spec.Node.Columns = append(_spec.Node.Columns, fields[i])
}
}
if _q.withUser != nil {
_spec.Node.AddColumnOnce(userattributevalue.FieldUserID)
}
if _q.withDefinition != nil {
_spec.Node.AddColumnOnce(userattributevalue.FieldAttributeID)
}
}
if ps := _q.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
if limit := _q.ctx.Limit; limit != nil {
_spec.Limit = *limit
}
if offset := _q.ctx.Offset; offset != nil {
_spec.Offset = *offset
}
if ps := _q.order; len(ps) > 0 {
_spec.Order = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
return _spec
}
func (_q *UserAttributeValueQuery) sqlQuery(ctx context.Context) *sql.Selector {
builder := sql.Dialect(_q.driver.Dialect())
t1 := builder.Table(userattributevalue.Table)
columns := _q.ctx.Fields
if len(columns) == 0 {
columns = userattributevalue.Columns
}
selector := builder.Select(t1.Columns(columns...)...).From(t1)
if _q.sql != nil {
selector = _q.sql
selector.Select(selector.Columns(columns...)...)
}
if _q.ctx.Unique != nil && *_q.ctx.Unique {
selector.Distinct()
}
for _, p := range _q.predicates {
p(selector)
}
for _, p := range _q.order {
p(selector)
}
if offset := _q.ctx.Offset; offset != nil {
// limit is mandatory for offset clause. We start
// with default value, and override it below if needed.
selector.Offset(*offset).Limit(math.MaxInt32)
}
if limit := _q.ctx.Limit; limit != nil {
selector.Limit(*limit)
}
return selector
}
// UserAttributeValueGroupBy is the group-by builder for UserAttributeValue entities.
type UserAttributeValueGroupBy struct {
selector
build *UserAttributeValueQuery
}
// Aggregate adds the given aggregation functions to the group-by query.
func (_g *UserAttributeValueGroupBy) Aggregate(fns ...AggregateFunc) *UserAttributeValueGroupBy {
_g.fns = append(_g.fns, fns...)
return _g
}
// Scan applies the selector query and scans the result into the given value.
func (_g *UserAttributeValueGroupBy) Scan(ctx context.Context, v any) error {
ctx = setContextOp(ctx, _g.build.ctx, ent.OpQueryGroupBy)
if err := _g.build.prepareQuery(ctx); err != nil {
return err
}
return scanWithInterceptors[*UserAttributeValueQuery, *UserAttributeValueGroupBy](ctx, _g.build, _g, _g.build.inters, v)
}
func (_g *UserAttributeValueGroupBy) sqlScan(ctx context.Context, root *UserAttributeValueQuery, v any) error {
selector := root.sqlQuery(ctx).Select()
aggregation := make([]string, 0, len(_g.fns))
for _, fn := range _g.fns {
aggregation = append(aggregation, fn(selector))
}
if len(selector.SelectedColumns()) == 0 {
columns := make([]string, 0, len(*_g.flds)+len(_g.fns))
for _, f := range *_g.flds {
columns = append(columns, selector.C(f))
}
columns = append(columns, aggregation...)
selector.Select(columns...)
}
selector.GroupBy(selector.Columns(*_g.flds...)...)
if err := selector.Err(); err != nil {
return err
}
rows := &sql.Rows{}
query, args := selector.Query()
if err := _g.build.driver.Query(ctx, query, args, rows); err != nil {
return err
}
defer rows.Close()
return sql.ScanSlice(rows, v)
}
// UserAttributeValueSelect is the builder for selecting fields of UserAttributeValue entities.
type UserAttributeValueSelect struct {
*UserAttributeValueQuery
selector
}
// Aggregate adds the given aggregation functions to the selector query.
func (_s *UserAttributeValueSelect) Aggregate(fns ...AggregateFunc) *UserAttributeValueSelect {
_s.fns = append(_s.fns, fns...)
return _s
}
// Scan applies the selector query and scans the result into the given value.
func (_s *UserAttributeValueSelect) Scan(ctx context.Context, v any) error {
ctx = setContextOp(ctx, _s.ctx, ent.OpQuerySelect)
if err := _s.prepareQuery(ctx); err != nil {
return err
}
return scanWithInterceptors[*UserAttributeValueQuery, *UserAttributeValueSelect](ctx, _s.UserAttributeValueQuery, _s, _s.inters, v)
}
func (_s *UserAttributeValueSelect) sqlScan(ctx context.Context, root *UserAttributeValueQuery, v any) error {
selector := root.sqlQuery(ctx)
aggregation := make([]string, 0, len(_s.fns))
for _, fn := range _s.fns {
aggregation = append(aggregation, fn(selector))
}
switch n := len(*_s.selector.flds); {
case n == 0 && len(aggregation) > 0:
selector.Select(aggregation...)
case n != 0 && len(aggregation) > 0:
selector.AppendSelect(aggregation...)
}
rows := &sql.Rows{}
query, args := selector.Query()
if err := _s.driver.Query(ctx, query, args, rows); err != nil {
return err
}
defer rows.Close()
return sql.ScanSlice(rows, v)
}

View File

@ -0,0 +1,504 @@
// Code generated by ent, DO NOT EDIT.
package ent
import (
"context"
"errors"
"fmt"
"time"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field"
"github.com/Wei-Shaw/sub2api/ent/predicate"
"github.com/Wei-Shaw/sub2api/ent/user"
"github.com/Wei-Shaw/sub2api/ent/userattributedefinition"
"github.com/Wei-Shaw/sub2api/ent/userattributevalue"
)
// UserAttributeValueUpdate is the builder for updating UserAttributeValue entities.
type UserAttributeValueUpdate struct {
config
hooks []Hook
mutation *UserAttributeValueMutation
}
// Where appends a list predicates to the UserAttributeValueUpdate builder.
func (_u *UserAttributeValueUpdate) Where(ps ...predicate.UserAttributeValue) *UserAttributeValueUpdate {
_u.mutation.Where(ps...)
return _u
}
// SetUpdatedAt sets the "updated_at" field.
func (_u *UserAttributeValueUpdate) SetUpdatedAt(v time.Time) *UserAttributeValueUpdate {
_u.mutation.SetUpdatedAt(v)
return _u
}
// SetUserID sets the "user_id" field.
func (_u *UserAttributeValueUpdate) SetUserID(v int64) *UserAttributeValueUpdate {
_u.mutation.SetUserID(v)
return _u
}
// SetNillableUserID sets the "user_id" field if the given value is not nil.
func (_u *UserAttributeValueUpdate) SetNillableUserID(v *int64) *UserAttributeValueUpdate {
if v != nil {
_u.SetUserID(*v)
}
return _u
}
// SetAttributeID sets the "attribute_id" field.
func (_u *UserAttributeValueUpdate) SetAttributeID(v int64) *UserAttributeValueUpdate {
_u.mutation.SetAttributeID(v)
return _u
}
// SetNillableAttributeID sets the "attribute_id" field if the given value is not nil.
func (_u *UserAttributeValueUpdate) SetNillableAttributeID(v *int64) *UserAttributeValueUpdate {
if v != nil {
_u.SetAttributeID(*v)
}
return _u
}
// SetValue sets the "value" field.
func (_u *UserAttributeValueUpdate) SetValue(v string) *UserAttributeValueUpdate {
_u.mutation.SetValue(v)
return _u
}
// SetNillableValue sets the "value" field if the given value is not nil.
func (_u *UserAttributeValueUpdate) SetNillableValue(v *string) *UserAttributeValueUpdate {
if v != nil {
_u.SetValue(*v)
}
return _u
}
// SetUser sets the "user" edge to the User entity.
func (_u *UserAttributeValueUpdate) SetUser(v *User) *UserAttributeValueUpdate {
return _u.SetUserID(v.ID)
}
// SetDefinitionID sets the "definition" edge to the UserAttributeDefinition entity by ID.
func (_u *UserAttributeValueUpdate) SetDefinitionID(id int64) *UserAttributeValueUpdate {
_u.mutation.SetDefinitionID(id)
return _u
}
// SetDefinition sets the "definition" edge to the UserAttributeDefinition entity.
func (_u *UserAttributeValueUpdate) SetDefinition(v *UserAttributeDefinition) *UserAttributeValueUpdate {
return _u.SetDefinitionID(v.ID)
}
// Mutation returns the UserAttributeValueMutation object of the builder.
func (_u *UserAttributeValueUpdate) Mutation() *UserAttributeValueMutation {
return _u.mutation
}
// ClearUser clears the "user" edge to the User entity.
func (_u *UserAttributeValueUpdate) ClearUser() *UserAttributeValueUpdate {
_u.mutation.ClearUser()
return _u
}
// ClearDefinition clears the "definition" edge to the UserAttributeDefinition entity.
func (_u *UserAttributeValueUpdate) ClearDefinition() *UserAttributeValueUpdate {
_u.mutation.ClearDefinition()
return _u
}
// Save executes the query and returns the number of nodes affected by the update operation.
func (_u *UserAttributeValueUpdate) Save(ctx context.Context) (int, error) {
_u.defaults()
return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks)
}
// SaveX is like Save, but panics if an error occurs.
func (_u *UserAttributeValueUpdate) SaveX(ctx context.Context) int {
affected, err := _u.Save(ctx)
if err != nil {
panic(err)
}
return affected
}
// Exec executes the query.
func (_u *UserAttributeValueUpdate) Exec(ctx context.Context) error {
_, err := _u.Save(ctx)
return err
}
// ExecX is like Exec, but panics if an error occurs.
func (_u *UserAttributeValueUpdate) ExecX(ctx context.Context) {
if err := _u.Exec(ctx); err != nil {
panic(err)
}
}
// defaults sets the default values of the builder before save.
func (_u *UserAttributeValueUpdate) defaults() {
if _, ok := _u.mutation.UpdatedAt(); !ok {
v := userattributevalue.UpdateDefaultUpdatedAt()
_u.mutation.SetUpdatedAt(v)
}
}
// check runs all checks and user-defined validators on the builder.
func (_u *UserAttributeValueUpdate) check() error {
if _u.mutation.UserCleared() && len(_u.mutation.UserIDs()) > 0 {
return errors.New(`ent: clearing a required unique edge "UserAttributeValue.user"`)
}
if _u.mutation.DefinitionCleared() && len(_u.mutation.DefinitionIDs()) > 0 {
return errors.New(`ent: clearing a required unique edge "UserAttributeValue.definition"`)
}
return nil
}
func (_u *UserAttributeValueUpdate) sqlSave(ctx context.Context) (_node int, err error) {
if err := _u.check(); err != nil {
return _node, err
}
_spec := sqlgraph.NewUpdateSpec(userattributevalue.Table, userattributevalue.Columns, sqlgraph.NewFieldSpec(userattributevalue.FieldID, field.TypeInt64))
if ps := _u.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
if value, ok := _u.mutation.UpdatedAt(); ok {
_spec.SetField(userattributevalue.FieldUpdatedAt, field.TypeTime, value)
}
if value, ok := _u.mutation.Value(); ok {
_spec.SetField(userattributevalue.FieldValue, field.TypeString, value)
}
if _u.mutation.UserCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
Inverse: true,
Table: userattributevalue.UserTable,
Columns: []string{userattributevalue.UserColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt64),
},
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
}
if nodes := _u.mutation.UserIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
Inverse: true,
Table: userattributevalue.UserTable,
Columns: []string{userattributevalue.UserColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt64),
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges.Add = append(_spec.Edges.Add, edge)
}
if _u.mutation.DefinitionCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
Inverse: true,
Table: userattributevalue.DefinitionTable,
Columns: []string{userattributevalue.DefinitionColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(userattributedefinition.FieldID, field.TypeInt64),
},
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
}
if nodes := _u.mutation.DefinitionIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
Inverse: true,
Table: userattributevalue.DefinitionTable,
Columns: []string{userattributevalue.DefinitionColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(userattributedefinition.FieldID, field.TypeInt64),
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges.Add = append(_spec.Edges.Add, edge)
}
if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil {
if _, ok := err.(*sqlgraph.NotFoundError); ok {
err = &NotFoundError{userattributevalue.Label}
} else if sqlgraph.IsConstraintError(err) {
err = &ConstraintError{msg: err.Error(), wrap: err}
}
return 0, err
}
_u.mutation.done = true
return _node, nil
}
// UserAttributeValueUpdateOne is the builder for updating a single UserAttributeValue entity.
type UserAttributeValueUpdateOne struct {
config
fields []string
hooks []Hook
mutation *UserAttributeValueMutation
}
// SetUpdatedAt sets the "updated_at" field.
func (_u *UserAttributeValueUpdateOne) SetUpdatedAt(v time.Time) *UserAttributeValueUpdateOne {
_u.mutation.SetUpdatedAt(v)
return _u
}
// SetUserID sets the "user_id" field.
func (_u *UserAttributeValueUpdateOne) SetUserID(v int64) *UserAttributeValueUpdateOne {
_u.mutation.SetUserID(v)
return _u
}
// SetNillableUserID sets the "user_id" field if the given value is not nil.
func (_u *UserAttributeValueUpdateOne) SetNillableUserID(v *int64) *UserAttributeValueUpdateOne {
if v != nil {
_u.SetUserID(*v)
}
return _u
}
// SetAttributeID sets the "attribute_id" field.
func (_u *UserAttributeValueUpdateOne) SetAttributeID(v int64) *UserAttributeValueUpdateOne {
_u.mutation.SetAttributeID(v)
return _u
}
// SetNillableAttributeID sets the "attribute_id" field if the given value is not nil.
func (_u *UserAttributeValueUpdateOne) SetNillableAttributeID(v *int64) *UserAttributeValueUpdateOne {
if v != nil {
_u.SetAttributeID(*v)
}
return _u
}
// SetValue sets the "value" field.
func (_u *UserAttributeValueUpdateOne) SetValue(v string) *UserAttributeValueUpdateOne {
_u.mutation.SetValue(v)
return _u
}
// SetNillableValue sets the "value" field if the given value is not nil.
func (_u *UserAttributeValueUpdateOne) SetNillableValue(v *string) *UserAttributeValueUpdateOne {
if v != nil {
_u.SetValue(*v)
}
return _u
}
// SetUser sets the "user" edge to the User entity.
func (_u *UserAttributeValueUpdateOne) SetUser(v *User) *UserAttributeValueUpdateOne {
return _u.SetUserID(v.ID)
}
// SetDefinitionID sets the "definition" edge to the UserAttributeDefinition entity by ID.
func (_u *UserAttributeValueUpdateOne) SetDefinitionID(id int64) *UserAttributeValueUpdateOne {
_u.mutation.SetDefinitionID(id)
return _u
}
// SetDefinition sets the "definition" edge to the UserAttributeDefinition entity.
func (_u *UserAttributeValueUpdateOne) SetDefinition(v *UserAttributeDefinition) *UserAttributeValueUpdateOne {
return _u.SetDefinitionID(v.ID)
}
// Mutation returns the UserAttributeValueMutation object of the builder.
func (_u *UserAttributeValueUpdateOne) Mutation() *UserAttributeValueMutation {
return _u.mutation
}
// ClearUser clears the "user" edge to the User entity.
func (_u *UserAttributeValueUpdateOne) ClearUser() *UserAttributeValueUpdateOne {
_u.mutation.ClearUser()
return _u
}
// ClearDefinition clears the "definition" edge to the UserAttributeDefinition entity.
func (_u *UserAttributeValueUpdateOne) ClearDefinition() *UserAttributeValueUpdateOne {
_u.mutation.ClearDefinition()
return _u
}
// Where appends a list predicates to the UserAttributeValueUpdate builder.
func (_u *UserAttributeValueUpdateOne) Where(ps ...predicate.UserAttributeValue) *UserAttributeValueUpdateOne {
_u.mutation.Where(ps...)
return _u
}
// Select allows selecting one or more fields (columns) of the returned entity.
// The default is selecting all fields defined in the entity schema.
func (_u *UserAttributeValueUpdateOne) Select(field string, fields ...string) *UserAttributeValueUpdateOne {
_u.fields = append([]string{field}, fields...)
return _u
}
// Save executes the query and returns the updated UserAttributeValue entity.
func (_u *UserAttributeValueUpdateOne) Save(ctx context.Context) (*UserAttributeValue, error) {
_u.defaults()
return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks)
}
// SaveX is like Save, but panics if an error occurs.
func (_u *UserAttributeValueUpdateOne) SaveX(ctx context.Context) *UserAttributeValue {
node, err := _u.Save(ctx)
if err != nil {
panic(err)
}
return node
}
// Exec executes the query on the entity.
func (_u *UserAttributeValueUpdateOne) Exec(ctx context.Context) error {
_, err := _u.Save(ctx)
return err
}
// ExecX is like Exec, but panics if an error occurs.
func (_u *UserAttributeValueUpdateOne) ExecX(ctx context.Context) {
if err := _u.Exec(ctx); err != nil {
panic(err)
}
}
// defaults sets the default values of the builder before save.
func (_u *UserAttributeValueUpdateOne) defaults() {
if _, ok := _u.mutation.UpdatedAt(); !ok {
v := userattributevalue.UpdateDefaultUpdatedAt()
_u.mutation.SetUpdatedAt(v)
}
}
// check runs all checks and user-defined validators on the builder.
func (_u *UserAttributeValueUpdateOne) check() error {
if _u.mutation.UserCleared() && len(_u.mutation.UserIDs()) > 0 {
return errors.New(`ent: clearing a required unique edge "UserAttributeValue.user"`)
}
if _u.mutation.DefinitionCleared() && len(_u.mutation.DefinitionIDs()) > 0 {
return errors.New(`ent: clearing a required unique edge "UserAttributeValue.definition"`)
}
return nil
}
func (_u *UserAttributeValueUpdateOne) sqlSave(ctx context.Context) (_node *UserAttributeValue, err error) {
if err := _u.check(); err != nil {
return _node, err
}
_spec := sqlgraph.NewUpdateSpec(userattributevalue.Table, userattributevalue.Columns, sqlgraph.NewFieldSpec(userattributevalue.FieldID, field.TypeInt64))
id, ok := _u.mutation.ID()
if !ok {
return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "UserAttributeValue.id" for update`)}
}
_spec.Node.ID.Value = id
if fields := _u.fields; len(fields) > 0 {
_spec.Node.Columns = make([]string, 0, len(fields))
_spec.Node.Columns = append(_spec.Node.Columns, userattributevalue.FieldID)
for _, f := range fields {
if !userattributevalue.ValidColumn(f) {
return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
}
if f != userattributevalue.FieldID {
_spec.Node.Columns = append(_spec.Node.Columns, f)
}
}
}
if ps := _u.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
if value, ok := _u.mutation.UpdatedAt(); ok {
_spec.SetField(userattributevalue.FieldUpdatedAt, field.TypeTime, value)
}
if value, ok := _u.mutation.Value(); ok {
_spec.SetField(userattributevalue.FieldValue, field.TypeString, value)
}
if _u.mutation.UserCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
Inverse: true,
Table: userattributevalue.UserTable,
Columns: []string{userattributevalue.UserColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt64),
},
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
}
if nodes := _u.mutation.UserIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
Inverse: true,
Table: userattributevalue.UserTable,
Columns: []string{userattributevalue.UserColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt64),
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges.Add = append(_spec.Edges.Add, edge)
}
if _u.mutation.DefinitionCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
Inverse: true,
Table: userattributevalue.DefinitionTable,
Columns: []string{userattributevalue.DefinitionColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(userattributedefinition.FieldID, field.TypeInt64),
},
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
}
if nodes := _u.mutation.DefinitionIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
Inverse: true,
Table: userattributevalue.DefinitionTable,
Columns: []string{userattributevalue.DefinitionColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(userattributedefinition.FieldID, field.TypeInt64),
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges.Add = append(_spec.Edges.Add, edge)
}
_node = &UserAttributeValue{config: _u.config}
_spec.Assign = _node.assignValues
_spec.ScanValues = _node.scanValues
if err = sqlgraph.UpdateNode(ctx, _u.driver, _spec); err != nil {
if _, ok := err.(*sqlgraph.NotFoundError); ok {
err = &NotFoundError{userattributevalue.Label}
} else if sqlgraph.IsConstraintError(err) {
err = &ConstraintError{msg: err.Error(), wrap: err}
}
return nil, err
}
_u.mutation.done = true
return _node, nil
}

View File

@ -246,7 +246,7 @@ func (h *UsageHandler) SearchUsers(c *gin.Context) {
} }
// Limit to 30 results // Limit to 30 results
users, _, err := h.adminService.ListUsers(c.Request.Context(), 1, 30, "", "", keyword) users, _, err := h.adminService.ListUsers(c.Request.Context(), 1, 30, service.UserListFilters{Search: keyword})
if err != nil { if err != nil {
response.ErrorFrom(c, err) response.ErrorFrom(c, err)
return return

View File

@ -0,0 +1,342 @@
package admin
import (
"strconv"
"github.com/Wei-Shaw/sub2api/internal/pkg/response"
"github.com/Wei-Shaw/sub2api/internal/service"
"github.com/gin-gonic/gin"
)
// UserAttributeHandler handles user attribute management
type UserAttributeHandler struct {
attrService *service.UserAttributeService
}
// NewUserAttributeHandler creates a new handler
func NewUserAttributeHandler(attrService *service.UserAttributeService) *UserAttributeHandler {
return &UserAttributeHandler{attrService: attrService}
}
// --- Request/Response DTOs ---
// CreateAttributeDefinitionRequest represents create attribute definition request
type CreateAttributeDefinitionRequest struct {
Key string `json:"key" binding:"required,min=1,max=100"`
Name string `json:"name" binding:"required,min=1,max=255"`
Description string `json:"description"`
Type string `json:"type" binding:"required"`
Options []service.UserAttributeOption `json:"options"`
Required bool `json:"required"`
Validation service.UserAttributeValidation `json:"validation"`
Placeholder string `json:"placeholder"`
Enabled bool `json:"enabled"`
}
// UpdateAttributeDefinitionRequest represents update attribute definition request
type UpdateAttributeDefinitionRequest struct {
Name *string `json:"name"`
Description *string `json:"description"`
Type *string `json:"type"`
Options *[]service.UserAttributeOption `json:"options"`
Required *bool `json:"required"`
Validation *service.UserAttributeValidation `json:"validation"`
Placeholder *string `json:"placeholder"`
Enabled *bool `json:"enabled"`
}
// ReorderRequest represents reorder attribute definitions request
type ReorderRequest struct {
IDs []int64 `json:"ids" binding:"required"`
}
// UpdateUserAttributesRequest represents update user attributes request
type UpdateUserAttributesRequest struct {
Values map[int64]string `json:"values" binding:"required"`
}
// BatchGetUserAttributesRequest represents batch get user attributes request
type BatchGetUserAttributesRequest struct {
UserIDs []int64 `json:"user_ids" binding:"required"`
}
// BatchUserAttributesResponse represents batch user attributes response
type BatchUserAttributesResponse struct {
// Map of userID -> map of attributeID -> value
Attributes map[int64]map[int64]string `json:"attributes"`
}
// AttributeDefinitionResponse represents attribute definition response
type AttributeDefinitionResponse struct {
ID int64 `json:"id"`
Key string `json:"key"`
Name string `json:"name"`
Description string `json:"description"`
Type string `json:"type"`
Options []service.UserAttributeOption `json:"options"`
Required bool `json:"required"`
Validation service.UserAttributeValidation `json:"validation"`
Placeholder string `json:"placeholder"`
DisplayOrder int `json:"display_order"`
Enabled bool `json:"enabled"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
}
// AttributeValueResponse represents attribute value response
type AttributeValueResponse struct {
ID int64 `json:"id"`
UserID int64 `json:"user_id"`
AttributeID int64 `json:"attribute_id"`
Value string `json:"value"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
}
// --- Helpers ---
func defToResponse(def *service.UserAttributeDefinition) *AttributeDefinitionResponse {
return &AttributeDefinitionResponse{
ID: def.ID,
Key: def.Key,
Name: def.Name,
Description: def.Description,
Type: string(def.Type),
Options: def.Options,
Required: def.Required,
Validation: def.Validation,
Placeholder: def.Placeholder,
DisplayOrder: def.DisplayOrder,
Enabled: def.Enabled,
CreatedAt: def.CreatedAt.Format("2006-01-02T15:04:05Z07:00"),
UpdatedAt: def.UpdatedAt.Format("2006-01-02T15:04:05Z07:00"),
}
}
func valueToResponse(val *service.UserAttributeValue) *AttributeValueResponse {
return &AttributeValueResponse{
ID: val.ID,
UserID: val.UserID,
AttributeID: val.AttributeID,
Value: val.Value,
CreatedAt: val.CreatedAt.Format("2006-01-02T15:04:05Z07:00"),
UpdatedAt: val.UpdatedAt.Format("2006-01-02T15:04:05Z07:00"),
}
}
// --- Handlers ---
// ListDefinitions lists all attribute definitions
// GET /admin/user-attributes
func (h *UserAttributeHandler) ListDefinitions(c *gin.Context) {
enabledOnly := c.Query("enabled") == "true"
defs, err := h.attrService.ListDefinitions(c.Request.Context(), enabledOnly)
if err != nil {
response.ErrorFrom(c, err)
return
}
out := make([]*AttributeDefinitionResponse, 0, len(defs))
for i := range defs {
out = append(out, defToResponse(&defs[i]))
}
response.Success(c, out)
}
// CreateDefinition creates a new attribute definition
// POST /admin/user-attributes
func (h *UserAttributeHandler) CreateDefinition(c *gin.Context) {
var req CreateAttributeDefinitionRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "Invalid request: "+err.Error())
return
}
def, err := h.attrService.CreateDefinition(c.Request.Context(), service.CreateAttributeDefinitionInput{
Key: req.Key,
Name: req.Name,
Description: req.Description,
Type: service.UserAttributeType(req.Type),
Options: req.Options,
Required: req.Required,
Validation: req.Validation,
Placeholder: req.Placeholder,
Enabled: req.Enabled,
})
if err != nil {
response.ErrorFrom(c, err)
return
}
response.Success(c, defToResponse(def))
}
// UpdateDefinition updates an attribute definition
// PUT /admin/user-attributes/:id
func (h *UserAttributeHandler) UpdateDefinition(c *gin.Context) {
id, err := strconv.ParseInt(c.Param("id"), 10, 64)
if err != nil {
response.BadRequest(c, "Invalid attribute ID")
return
}
var req UpdateAttributeDefinitionRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "Invalid request: "+err.Error())
return
}
input := service.UpdateAttributeDefinitionInput{
Name: req.Name,
Description: req.Description,
Options: req.Options,
Required: req.Required,
Validation: req.Validation,
Placeholder: req.Placeholder,
Enabled: req.Enabled,
}
if req.Type != nil {
t := service.UserAttributeType(*req.Type)
input.Type = &t
}
def, err := h.attrService.UpdateDefinition(c.Request.Context(), id, input)
if err != nil {
response.ErrorFrom(c, err)
return
}
response.Success(c, defToResponse(def))
}
// DeleteDefinition deletes an attribute definition
// DELETE /admin/user-attributes/:id
func (h *UserAttributeHandler) DeleteDefinition(c *gin.Context) {
id, err := strconv.ParseInt(c.Param("id"), 10, 64)
if err != nil {
response.BadRequest(c, "Invalid attribute ID")
return
}
if err := h.attrService.DeleteDefinition(c.Request.Context(), id); err != nil {
response.ErrorFrom(c, err)
return
}
response.Success(c, gin.H{"message": "Attribute definition deleted successfully"})
}
// ReorderDefinitions reorders attribute definitions
// PUT /admin/user-attributes/reorder
func (h *UserAttributeHandler) ReorderDefinitions(c *gin.Context) {
var req ReorderRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "Invalid request: "+err.Error())
return
}
// Convert IDs array to orders map (position in array = display_order)
orders := make(map[int64]int, len(req.IDs))
for i, id := range req.IDs {
orders[id] = i
}
if err := h.attrService.ReorderDefinitions(c.Request.Context(), orders); err != nil {
response.ErrorFrom(c, err)
return
}
response.Success(c, gin.H{"message": "Reorder successful"})
}
// GetUserAttributes gets a user's attribute values
// GET /admin/users/:id/attributes
func (h *UserAttributeHandler) GetUserAttributes(c *gin.Context) {
userID, err := strconv.ParseInt(c.Param("id"), 10, 64)
if err != nil {
response.BadRequest(c, "Invalid user ID")
return
}
values, err := h.attrService.GetUserAttributes(c.Request.Context(), userID)
if err != nil {
response.ErrorFrom(c, err)
return
}
out := make([]*AttributeValueResponse, 0, len(values))
for i := range values {
out = append(out, valueToResponse(&values[i]))
}
response.Success(c, out)
}
// UpdateUserAttributes updates a user's attribute values
// PUT /admin/users/:id/attributes
func (h *UserAttributeHandler) UpdateUserAttributes(c *gin.Context) {
userID, err := strconv.ParseInt(c.Param("id"), 10, 64)
if err != nil {
response.BadRequest(c, "Invalid user ID")
return
}
var req UpdateUserAttributesRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "Invalid request: "+err.Error())
return
}
inputs := make([]service.UpdateUserAttributeInput, 0, len(req.Values))
for attrID, value := range req.Values {
inputs = append(inputs, service.UpdateUserAttributeInput{
AttributeID: attrID,
Value: value,
})
}
if err := h.attrService.UpdateUserAttributes(c.Request.Context(), userID, inputs); err != nil {
response.ErrorFrom(c, err)
return
}
// Return updated values
values, err := h.attrService.GetUserAttributes(c.Request.Context(), userID)
if err != nil {
response.ErrorFrom(c, err)
return
}
out := make([]*AttributeValueResponse, 0, len(values))
for i := range values {
out = append(out, valueToResponse(&values[i]))
}
response.Success(c, out)
}
// GetBatchUserAttributes gets attribute values for multiple users
// POST /admin/user-attributes/batch
func (h *UserAttributeHandler) GetBatchUserAttributes(c *gin.Context) {
var req BatchGetUserAttributesRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "Invalid request: "+err.Error())
return
}
if len(req.UserIDs) == 0 {
response.Success(c, BatchUserAttributesResponse{Attributes: map[int64]map[int64]string{}})
return
}
attrs, err := h.attrService.GetBatchUserAttributes(c.Request.Context(), req.UserIDs)
if err != nil {
response.ErrorFrom(c, err)
return
}
response.Success(c, BatchUserAttributesResponse{Attributes: attrs})
}

View File

@ -27,7 +27,6 @@ type CreateUserRequest struct {
Email string `json:"email" binding:"required,email"` Email string `json:"email" binding:"required,email"`
Password string `json:"password" binding:"required,min=6"` Password string `json:"password" binding:"required,min=6"`
Username string `json:"username"` Username string `json:"username"`
Wechat string `json:"wechat"`
Notes string `json:"notes"` Notes string `json:"notes"`
Balance float64 `json:"balance"` Balance float64 `json:"balance"`
Concurrency int `json:"concurrency"` Concurrency int `json:"concurrency"`
@ -40,7 +39,6 @@ type UpdateUserRequest struct {
Email string `json:"email" binding:"omitempty,email"` Email string `json:"email" binding:"omitempty,email"`
Password string `json:"password" binding:"omitempty,min=6"` Password string `json:"password" binding:"omitempty,min=6"`
Username *string `json:"username"` Username *string `json:"username"`
Wechat *string `json:"wechat"`
Notes *string `json:"notes"` Notes *string `json:"notes"`
Balance *float64 `json:"balance"` Balance *float64 `json:"balance"`
Concurrency *int `json:"concurrency"` Concurrency *int `json:"concurrency"`
@ -57,13 +55,22 @@ type UpdateBalanceRequest struct {
// List handles listing all users with pagination // List handles listing all users with pagination
// GET /api/v1/admin/users // GET /api/v1/admin/users
// Query params:
// - status: filter by user status
// - role: filter by user role
// - search: search in email, username
// - attr[{id}]: filter by custom attribute value, e.g. attr[1]=company
func (h *UserHandler) List(c *gin.Context) { func (h *UserHandler) List(c *gin.Context) {
page, pageSize := response.ParsePagination(c) page, pageSize := response.ParsePagination(c)
status := c.Query("status")
role := c.Query("role")
search := c.Query("search")
users, total, err := h.adminService.ListUsers(c.Request.Context(), page, pageSize, status, role, search) filters := service.UserListFilters{
Status: c.Query("status"),
Role: c.Query("role"),
Search: c.Query("search"),
Attributes: parseAttributeFilters(c),
}
users, total, err := h.adminService.ListUsers(c.Request.Context(), page, pageSize, filters)
if err != nil { if err != nil {
response.ErrorFrom(c, err) response.ErrorFrom(c, err)
return return
@ -76,6 +83,29 @@ func (h *UserHandler) List(c *gin.Context) {
response.Paginated(c, out, total, page, pageSize) response.Paginated(c, out, total, page, pageSize)
} }
// parseAttributeFilters extracts attribute filters from query params
// Format: attr[{attributeID}]=value, e.g. attr[1]=company&attr[2]=developer
func parseAttributeFilters(c *gin.Context) map[int64]string {
result := make(map[int64]string)
// Get all query params and look for attr[*] pattern
for key, values := range c.Request.URL.Query() {
if len(values) == 0 || values[0] == "" {
continue
}
// Check if key matches pattern attr[{id}]
if len(key) > 5 && key[:5] == "attr[" && key[len(key)-1] == ']' {
idStr := key[5 : len(key)-1]
id, err := strconv.ParseInt(idStr, 10, 64)
if err == nil && id > 0 {
result[id] = values[0]
}
}
}
return result
}
// GetByID handles getting a user by ID // GetByID handles getting a user by ID
// GET /api/v1/admin/users/:id // GET /api/v1/admin/users/:id
func (h *UserHandler) GetByID(c *gin.Context) { func (h *UserHandler) GetByID(c *gin.Context) {
@ -107,7 +137,6 @@ func (h *UserHandler) Create(c *gin.Context) {
Email: req.Email, Email: req.Email,
Password: req.Password, Password: req.Password,
Username: req.Username, Username: req.Username,
Wechat: req.Wechat,
Notes: req.Notes, Notes: req.Notes,
Balance: req.Balance, Balance: req.Balance,
Concurrency: req.Concurrency, Concurrency: req.Concurrency,
@ -141,7 +170,6 @@ func (h *UserHandler) Update(c *gin.Context) {
Email: req.Email, Email: req.Email,
Password: req.Password, Password: req.Password,
Username: req.Username, Username: req.Username,
Wechat: req.Wechat,
Notes: req.Notes, Notes: req.Notes,
Balance: req.Balance, Balance: req.Balance,
Concurrency: req.Concurrency, Concurrency: req.Concurrency,

View File

@ -10,7 +10,6 @@ func UserFromServiceShallow(u *service.User) *User {
ID: u.ID, ID: u.ID,
Email: u.Email, Email: u.Email,
Username: u.Username, Username: u.Username,
Wechat: u.Wechat,
Notes: u.Notes, Notes: u.Notes,
Role: u.Role, Role: u.Role,
Balance: u.Balance, Balance: u.Balance,

View File

@ -6,7 +6,6 @@ type User struct {
ID int64 `json:"id"` ID int64 `json:"id"`
Email string `json:"email"` Email string `json:"email"`
Username string `json:"username"` Username string `json:"username"`
Wechat string `json:"wechat"`
Notes string `json:"notes"` Notes string `json:"notes"`
Role string `json:"role"` Role string `json:"role"`
Balance float64 `json:"balance"` Balance float64 `json:"balance"`

View File

@ -20,6 +20,7 @@ type AdminHandlers struct {
System *admin.SystemHandler System *admin.SystemHandler
Subscription *admin.SubscriptionHandler Subscription *admin.SubscriptionHandler
Usage *admin.UsageHandler Usage *admin.UsageHandler
UserAttribute *admin.UserAttributeHandler
} }
// Handlers contains all HTTP handlers // Handlers contains all HTTP handlers

View File

@ -30,7 +30,6 @@ type ChangePasswordRequest struct {
// UpdateProfileRequest represents the update profile request payload // UpdateProfileRequest represents the update profile request payload
type UpdateProfileRequest struct { type UpdateProfileRequest struct {
Username *string `json:"username"` Username *string `json:"username"`
Wechat *string `json:"wechat"`
} }
// GetProfile handles getting user profile // GetProfile handles getting user profile
@ -99,7 +98,6 @@ func (h *UserHandler) UpdateProfile(c *gin.Context) {
svcReq := service.UpdateProfileRequest{ svcReq := service.UpdateProfileRequest{
Username: req.Username, Username: req.Username,
Wechat: req.Wechat,
} }
updatedUser, err := h.userService.UpdateProfile(c.Request.Context(), subject.UserID, svcReq) updatedUser, err := h.userService.UpdateProfile(c.Request.Context(), subject.UserID, svcReq)
if err != nil { if err != nil {

View File

@ -23,6 +23,7 @@ func ProvideAdminHandlers(
systemHandler *admin.SystemHandler, systemHandler *admin.SystemHandler,
subscriptionHandler *admin.SubscriptionHandler, subscriptionHandler *admin.SubscriptionHandler,
usageHandler *admin.UsageHandler, usageHandler *admin.UsageHandler,
userAttributeHandler *admin.UserAttributeHandler,
) *AdminHandlers { ) *AdminHandlers {
return &AdminHandlers{ return &AdminHandlers{
Dashboard: dashboardHandler, Dashboard: dashboardHandler,
@ -39,6 +40,7 @@ func ProvideAdminHandlers(
System: systemHandler, System: systemHandler,
Subscription: subscriptionHandler, Subscription: subscriptionHandler,
Usage: usageHandler, Usage: usageHandler,
UserAttribute: userAttributeHandler,
} }
} }
@ -107,6 +109,7 @@ var ProviderSet = wire.NewSet(
ProvideSystemHandler, ProvideSystemHandler,
admin.NewSubscriptionHandler, admin.NewSubscriptionHandler,
admin.NewUsageHandler, admin.NewUsageHandler,
admin.NewUserAttributeHandler,
// AdminHandlers and Handlers constructors // AdminHandlers and Handlers constructors
ProvideAdminHandlers, ProvideAdminHandlers,

View File

@ -294,7 +294,6 @@ func userEntityToService(u *dbent.User) *service.User {
ID: u.ID, ID: u.ID,
Email: u.Email, Email: u.Email,
Username: u.Username, Username: u.Username,
Wechat: u.Wechat,
Notes: u.Notes, Notes: u.Notes,
PasswordHash: u.PasswordHash, PasswordHash: u.PasswordHash,
Role: u.Role, Role: u.Role,

View File

@ -40,7 +40,6 @@ func mustCreateUser(t *testing.T, client *dbent.Client, u *service.User) *servic
SetBalance(u.Balance). SetBalance(u.Balance).
SetConcurrency(u.Concurrency). SetConcurrency(u.Concurrency).
SetUsername(u.Username). SetUsername(u.Username).
SetWechat(u.Wechat).
SetNotes(u.Notes) SetNotes(u.Notes)
if !u.CreatedAt.IsZero() { if !u.CreatedAt.IsZero() {
create.SetCreatedAt(u.CreatedAt) create.SetCreatedAt(u.CreatedAt)

View File

@ -23,7 +23,6 @@ func TestMigrationsRunner_IsIdempotent_AndSchemaIsUpToDate(t *testing.T) {
// users: columns required by repository queries // users: columns required by repository queries
requireColumn(t, tx, "users", "username", "character varying", 100, false) requireColumn(t, tx, "users", "username", "character varying", 100, false)
requireColumn(t, tx, "users", "wechat", "character varying", 100, false)
requireColumn(t, tx, "users", "notes", "text", 0, false) requireColumn(t, tx, "users", "notes", "text", 0, false)
// accounts: schedulable and rate-limit fields // accounts: schedulable and rate-limit fields

View File

@ -0,0 +1,385 @@
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
}

View File

@ -9,6 +9,7 @@ import (
dbent "github.com/Wei-Shaw/sub2api/ent" dbent "github.com/Wei-Shaw/sub2api/ent"
dbuser "github.com/Wei-Shaw/sub2api/ent/user" dbuser "github.com/Wei-Shaw/sub2api/ent/user"
"github.com/Wei-Shaw/sub2api/ent/userallowedgroup" "github.com/Wei-Shaw/sub2api/ent/userallowedgroup"
"github.com/Wei-Shaw/sub2api/ent/userattributevalue"
"github.com/Wei-Shaw/sub2api/ent/usersubscription" "github.com/Wei-Shaw/sub2api/ent/usersubscription"
"github.com/Wei-Shaw/sub2api/internal/pkg/pagination" "github.com/Wei-Shaw/sub2api/internal/pkg/pagination"
"github.com/Wei-Shaw/sub2api/internal/service" "github.com/Wei-Shaw/sub2api/internal/service"
@ -50,7 +51,6 @@ func (r *userRepository) Create(ctx context.Context, userIn *service.User) error
created, err := txClient.User.Create(). created, err := txClient.User.Create().
SetEmail(userIn.Email). SetEmail(userIn.Email).
SetUsername(userIn.Username). SetUsername(userIn.Username).
SetWechat(userIn.Wechat).
SetNotes(userIn.Notes). SetNotes(userIn.Notes).
SetPasswordHash(userIn.PasswordHash). SetPasswordHash(userIn.PasswordHash).
SetRole(userIn.Role). SetRole(userIn.Role).
@ -133,7 +133,6 @@ func (r *userRepository) Update(ctx context.Context, userIn *service.User) error
updated, err := txClient.User.UpdateOneID(userIn.ID). updated, err := txClient.User.UpdateOneID(userIn.ID).
SetEmail(userIn.Email). SetEmail(userIn.Email).
SetUsername(userIn.Username). SetUsername(userIn.Username).
SetWechat(userIn.Wechat).
SetNotes(userIn.Notes). SetNotes(userIn.Notes).
SetPasswordHash(userIn.PasswordHash). SetPasswordHash(userIn.PasswordHash).
SetRole(userIn.Role). SetRole(userIn.Role).
@ -171,28 +170,38 @@ func (r *userRepository) Delete(ctx context.Context, id int64) error {
} }
func (r *userRepository) List(ctx context.Context, params pagination.PaginationParams) ([]service.User, *pagination.PaginationResult, error) { func (r *userRepository) List(ctx context.Context, params pagination.PaginationParams) ([]service.User, *pagination.PaginationResult, error) {
return r.ListWithFilters(ctx, params, "", "", "") return r.ListWithFilters(ctx, params, service.UserListFilters{})
} }
func (r *userRepository) ListWithFilters(ctx context.Context, params pagination.PaginationParams, status, role, search string) ([]service.User, *pagination.PaginationResult, error) { func (r *userRepository) ListWithFilters(ctx context.Context, params pagination.PaginationParams, filters service.UserListFilters) ([]service.User, *pagination.PaginationResult, error) {
q := r.client.User.Query() q := r.client.User.Query()
if status != "" { if filters.Status != "" {
q = q.Where(dbuser.StatusEQ(status)) q = q.Where(dbuser.StatusEQ(filters.Status))
} }
if role != "" { if filters.Role != "" {
q = q.Where(dbuser.RoleEQ(role)) q = q.Where(dbuser.RoleEQ(filters.Role))
} }
if search != "" { if filters.Search != "" {
q = q.Where( q = q.Where(
dbuser.Or( dbuser.Or(
dbuser.EmailContainsFold(search), dbuser.EmailContainsFold(filters.Search),
dbuser.UsernameContainsFold(search), dbuser.UsernameContainsFold(filters.Search),
dbuser.WechatContainsFold(search),
), ),
) )
} }
// If attribute filters are specified, we need to filter by user IDs first
var allowedUserIDs []int64
if len(filters.Attributes) > 0 {
allowedUserIDs = r.filterUsersByAttributes(ctx, filters.Attributes)
if len(allowedUserIDs) == 0 {
// No users match the attribute filters
return []service.User{}, paginationResultFromTotal(0, params), nil
}
q = q.Where(dbuser.IDIn(allowedUserIDs...))
}
total, err := q.Clone().Count(ctx) total, err := q.Clone().Count(ctx)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
@ -252,6 +261,59 @@ func (r *userRepository) ListWithFilters(ctx context.Context, params pagination.
return outUsers, paginationResultFromTotal(int64(total), params), nil return outUsers, paginationResultFromTotal(int64(total), params), nil
} }
// filterUsersByAttributes returns user IDs that match ALL the given attribute filters
func (r *userRepository) filterUsersByAttributes(ctx context.Context, attrs map[int64]string) []int64 {
if len(attrs) == 0 {
return nil
}
// For each attribute filter, get the set of matching user IDs
// Then intersect all sets to get users matching ALL filters
var resultSet map[int64]struct{}
first := true
for attrID, value := range attrs {
// Query user_attribute_values for this attribute
values, err := r.client.UserAttributeValue.Query().
Where(
userattributevalue.AttributeIDEQ(attrID),
userattributevalue.ValueContainsFold(value),
).
All(ctx)
if err != nil {
continue
}
currentSet := make(map[int64]struct{}, len(values))
for _, v := range values {
currentSet[v.UserID] = struct{}{}
}
if first {
resultSet = currentSet
first = false
} else {
// Intersect with previous results
for userID := range resultSet {
if _, ok := currentSet[userID]; !ok {
delete(resultSet, userID)
}
}
}
// Early exit if no users match
if len(resultSet) == 0 {
return nil
}
}
result := make([]int64, 0, len(resultSet))
for userID := range resultSet {
result = append(result, userID)
}
return result
}
func (r *userRepository) UpdateBalance(ctx context.Context, id int64, amount float64) error { func (r *userRepository) UpdateBalance(ctx context.Context, id int64, amount float64) error {
client := clientFromContext(ctx, r.client) client := clientFromContext(ctx, r.client)
n, err := client.User.Update().Where(dbuser.IDEQ(id)).AddBalance(amount).Save(ctx) n, err := client.User.Update().Where(dbuser.IDEQ(id)).AddBalance(amount).Save(ctx)

View File

@ -166,7 +166,7 @@ func (s *UserRepoSuite) TestListWithFilters_Status() {
s.mustCreateUser(&service.User{Email: "active@test.com", Status: service.StatusActive}) s.mustCreateUser(&service.User{Email: "active@test.com", Status: service.StatusActive})
s.mustCreateUser(&service.User{Email: "disabled@test.com", Status: service.StatusDisabled}) s.mustCreateUser(&service.User{Email: "disabled@test.com", Status: service.StatusDisabled})
users, _, err := s.repo.ListWithFilters(s.ctx, pagination.PaginationParams{Page: 1, PageSize: 10}, service.StatusActive, "", "") users, _, err := s.repo.ListWithFilters(s.ctx, pagination.PaginationParams{Page: 1, PageSize: 10}, service.UserListFilters{Status: service.StatusActive})
s.Require().NoError(err) s.Require().NoError(err)
s.Require().Len(users, 1) s.Require().Len(users, 1)
s.Require().Equal(service.StatusActive, users[0].Status) s.Require().Equal(service.StatusActive, users[0].Status)
@ -176,7 +176,7 @@ func (s *UserRepoSuite) TestListWithFilters_Role() {
s.mustCreateUser(&service.User{Email: "user@test.com", Role: service.RoleUser}) s.mustCreateUser(&service.User{Email: "user@test.com", Role: service.RoleUser})
s.mustCreateUser(&service.User{Email: "admin@test.com", Role: service.RoleAdmin}) s.mustCreateUser(&service.User{Email: "admin@test.com", Role: service.RoleAdmin})
users, _, err := s.repo.ListWithFilters(s.ctx, pagination.PaginationParams{Page: 1, PageSize: 10}, "", service.RoleAdmin, "") users, _, err := s.repo.ListWithFilters(s.ctx, pagination.PaginationParams{Page: 1, PageSize: 10}, service.UserListFilters{Role: service.RoleAdmin})
s.Require().NoError(err) s.Require().NoError(err)
s.Require().Len(users, 1) s.Require().Len(users, 1)
s.Require().Equal(service.RoleAdmin, users[0].Role) s.Require().Equal(service.RoleAdmin, users[0].Role)
@ -186,7 +186,7 @@ func (s *UserRepoSuite) TestListWithFilters_Search() {
s.mustCreateUser(&service.User{Email: "alice@test.com", Username: "Alice"}) s.mustCreateUser(&service.User{Email: "alice@test.com", Username: "Alice"})
s.mustCreateUser(&service.User{Email: "bob@test.com", Username: "Bob"}) s.mustCreateUser(&service.User{Email: "bob@test.com", Username: "Bob"})
users, _, err := s.repo.ListWithFilters(s.ctx, pagination.PaginationParams{Page: 1, PageSize: 10}, "", "", "alice") users, _, err := s.repo.ListWithFilters(s.ctx, pagination.PaginationParams{Page: 1, PageSize: 10}, service.UserListFilters{Search: "alice"})
s.Require().NoError(err) s.Require().NoError(err)
s.Require().Len(users, 1) s.Require().Len(users, 1)
s.Require().Contains(users[0].Email, "alice") s.Require().Contains(users[0].Email, "alice")
@ -196,22 +196,12 @@ func (s *UserRepoSuite) TestListWithFilters_SearchByUsername() {
s.mustCreateUser(&service.User{Email: "u1@test.com", Username: "JohnDoe"}) s.mustCreateUser(&service.User{Email: "u1@test.com", Username: "JohnDoe"})
s.mustCreateUser(&service.User{Email: "u2@test.com", Username: "JaneSmith"}) s.mustCreateUser(&service.User{Email: "u2@test.com", Username: "JaneSmith"})
users, _, err := s.repo.ListWithFilters(s.ctx, pagination.PaginationParams{Page: 1, PageSize: 10}, "", "", "john") users, _, err := s.repo.ListWithFilters(s.ctx, pagination.PaginationParams{Page: 1, PageSize: 10}, service.UserListFilters{Search: "john"})
s.Require().NoError(err) s.Require().NoError(err)
s.Require().Len(users, 1) s.Require().Len(users, 1)
s.Require().Equal("JohnDoe", users[0].Username) s.Require().Equal("JohnDoe", users[0].Username)
} }
func (s *UserRepoSuite) TestListWithFilters_SearchByWechat() {
s.mustCreateUser(&service.User{Email: "w1@test.com", Wechat: "wx_hello"})
s.mustCreateUser(&service.User{Email: "w2@test.com", Wechat: "wx_world"})
users, _, err := s.repo.ListWithFilters(s.ctx, pagination.PaginationParams{Page: 1, PageSize: 10}, "", "", "wx_hello")
s.Require().NoError(err)
s.Require().Len(users, 1)
s.Require().Equal("wx_hello", users[0].Wechat)
}
func (s *UserRepoSuite) TestListWithFilters_LoadsActiveSubscriptions() { func (s *UserRepoSuite) TestListWithFilters_LoadsActiveSubscriptions() {
user := s.mustCreateUser(&service.User{Email: "sub@test.com", Status: service.StatusActive}) user := s.mustCreateUser(&service.User{Email: "sub@test.com", Status: service.StatusActive})
groupActive := s.mustCreateGroup("g-sub-active") groupActive := s.mustCreateGroup("g-sub-active")
@ -226,7 +216,7 @@ func (s *UserRepoSuite) TestListWithFilters_LoadsActiveSubscriptions() {
c.SetExpiresAt(time.Now().Add(-1 * time.Hour)) c.SetExpiresAt(time.Now().Add(-1 * time.Hour))
}) })
users, _, err := s.repo.ListWithFilters(s.ctx, pagination.PaginationParams{Page: 1, PageSize: 10}, "", "", "sub@") users, _, err := s.repo.ListWithFilters(s.ctx, pagination.PaginationParams{Page: 1, PageSize: 10}, service.UserListFilters{Search: "sub@"})
s.Require().NoError(err, "ListWithFilters") s.Require().NoError(err, "ListWithFilters")
s.Require().Len(users, 1, "expected 1 user") s.Require().Len(users, 1, "expected 1 user")
s.Require().Len(users[0].Subscriptions, 1, "expected 1 active subscription") s.Require().Len(users[0].Subscriptions, 1, "expected 1 active subscription")
@ -238,7 +228,6 @@ func (s *UserRepoSuite) TestListWithFilters_CombinedFilters() {
s.mustCreateUser(&service.User{ s.mustCreateUser(&service.User{
Email: "a@example.com", Email: "a@example.com",
Username: "Alice", Username: "Alice",
Wechat: "wx_a",
Role: service.RoleUser, Role: service.RoleUser,
Status: service.StatusActive, Status: service.StatusActive,
Balance: 10, Balance: 10,
@ -246,7 +235,6 @@ func (s *UserRepoSuite) TestListWithFilters_CombinedFilters() {
target := s.mustCreateUser(&service.User{ target := s.mustCreateUser(&service.User{
Email: "b@example.com", Email: "b@example.com",
Username: "Bob", Username: "Bob",
Wechat: "wx_b",
Role: service.RoleAdmin, Role: service.RoleAdmin,
Status: service.StatusActive, Status: service.StatusActive,
Balance: 1, Balance: 1,
@ -257,7 +245,7 @@ func (s *UserRepoSuite) TestListWithFilters_CombinedFilters() {
Status: service.StatusDisabled, Status: service.StatusDisabled,
}) })
users, page, err := s.repo.ListWithFilters(s.ctx, pagination.PaginationParams{Page: 1, PageSize: 10}, service.StatusActive, service.RoleAdmin, "b@") users, page, err := s.repo.ListWithFilters(s.ctx, pagination.PaginationParams{Page: 1, PageSize: 10}, service.UserListFilters{Status: service.StatusActive, Role: service.RoleAdmin, Search: "b@"})
s.Require().NoError(err, "ListWithFilters") s.Require().NoError(err, "ListWithFilters")
s.Require().Equal(int64(1), page.Total, "ListWithFilters total mismatch") s.Require().Equal(int64(1), page.Total, "ListWithFilters total mismatch")
s.Require().Len(users, 1, "ListWithFilters len mismatch") s.Require().Len(users, 1, "ListWithFilters len mismatch")
@ -448,7 +436,6 @@ func (s *UserRepoSuite) TestCRUD_And_Filters_And_AtomicUpdates() {
user1 := s.mustCreateUser(&service.User{ user1 := s.mustCreateUser(&service.User{
Email: "a@example.com", Email: "a@example.com",
Username: "Alice", Username: "Alice",
Wechat: "wx_a",
Role: service.RoleUser, Role: service.RoleUser,
Status: service.StatusActive, Status: service.StatusActive,
Balance: 10, Balance: 10,
@ -456,7 +443,6 @@ func (s *UserRepoSuite) TestCRUD_And_Filters_And_AtomicUpdates() {
user2 := s.mustCreateUser(&service.User{ user2 := s.mustCreateUser(&service.User{
Email: "b@example.com", Email: "b@example.com",
Username: "Bob", Username: "Bob",
Wechat: "wx_b",
Role: service.RoleAdmin, Role: service.RoleAdmin,
Status: service.StatusActive, Status: service.StatusActive,
Balance: 1, Balance: 1,
@ -501,7 +487,7 @@ func (s *UserRepoSuite) TestCRUD_And_Filters_And_AtomicUpdates() {
s.Require().Equal(user1.Concurrency+3, got5.Concurrency) s.Require().Equal(user1.Concurrency+3, got5.Concurrency)
params := pagination.PaginationParams{Page: 1, PageSize: 10} params := pagination.PaginationParams{Page: 1, PageSize: 10}
users, page, err := s.repo.ListWithFilters(s.ctx, params, service.StatusActive, service.RoleAdmin, "b@") users, page, err := s.repo.ListWithFilters(s.ctx, params, service.UserListFilters{Status: service.StatusActive, Role: service.RoleAdmin, Search: "b@"})
s.Require().NoError(err, "ListWithFilters") s.Require().NoError(err, "ListWithFilters")
s.Require().Equal(int64(1), page.Total, "ListWithFilters total mismatch") s.Require().Equal(int64(1), page.Total, "ListWithFilters total mismatch")
s.Require().Len(users, 1, "ListWithFilters len mismatch") s.Require().Len(users, 1, "ListWithFilters len mismatch")

View File

@ -36,6 +36,8 @@ var ProviderSet = wire.NewSet(
NewUsageLogRepository, NewUsageLogRepository,
NewSettingRepository, NewSettingRepository,
NewUserSubscriptionRepository, NewUserSubscriptionRepository,
NewUserAttributeDefinitionRepository,
NewUserAttributeValueRepository,
// Cache implementations // Cache implementations
NewGatewayCache, NewGatewayCache,

View File

@ -51,7 +51,6 @@ func TestAPIContracts(t *testing.T) {
"id": 1, "id": 1,
"email": "alice@example.com", "email": "alice@example.com",
"username": "alice", "username": "alice",
"wechat": "wx_alice",
"notes": "hello", "notes": "hello",
"role": "user", "role": "user",
"balance": 12.5, "balance": 12.5,
@ -348,7 +347,6 @@ func newContractDeps(t *testing.T) *contractDeps {
ID: 1, ID: 1,
Email: "alice@example.com", Email: "alice@example.com",
Username: "alice", Username: "alice",
Wechat: "wx_alice",
Notes: "hello", Notes: "hello",
Role: service.RoleUser, Role: service.RoleUser,
Balance: 12.5, Balance: 12.5,
@ -503,7 +501,7 @@ func (r *stubUserRepo) List(ctx context.Context, params pagination.PaginationPar
return nil, nil, errors.New("not implemented") return nil, nil, errors.New("not implemented")
} }
func (r *stubUserRepo) ListWithFilters(ctx context.Context, params pagination.PaginationParams, status, role, search string) ([]service.User, *pagination.PaginationResult, error) { func (r *stubUserRepo) ListWithFilters(ctx context.Context, params pagination.PaginationParams, filters service.UserListFilters) ([]service.User, *pagination.PaginationResult, error) {
return nil, nil, errors.New("not implemented") return nil, nil, errors.New("not implemented")
} }

View File

@ -54,6 +54,9 @@ func RegisterAdminRoutes(
// 使用记录管理 // 使用记录管理
registerUsageRoutes(admin, h) registerUsageRoutes(admin, h)
// 用户属性管理
registerUserAttributeRoutes(admin, h)
} }
} }
@ -82,6 +85,10 @@ func registerUserManagementRoutes(admin *gin.RouterGroup, h *handler.Handlers) {
users.POST("/:id/balance", h.Admin.User.UpdateBalance) users.POST("/:id/balance", h.Admin.User.UpdateBalance)
users.GET("/:id/api-keys", h.Admin.User.GetUserAPIKeys) users.GET("/:id/api-keys", h.Admin.User.GetUserAPIKeys)
users.GET("/:id/usage", h.Admin.User.GetUserUsage) users.GET("/:id/usage", h.Admin.User.GetUserUsage)
// User attribute values
users.GET("/:id/attributes", h.Admin.UserAttribute.GetUserAttributes)
users.PUT("/:id/attributes", h.Admin.UserAttribute.UpdateUserAttributes)
} }
} }
@ -244,3 +251,15 @@ func registerUsageRoutes(admin *gin.RouterGroup, h *handler.Handlers) {
usage.GET("/search-api-keys", h.Admin.Usage.SearchApiKeys) usage.GET("/search-api-keys", h.Admin.Usage.SearchApiKeys)
} }
} }
func registerUserAttributeRoutes(admin *gin.RouterGroup, h *handler.Handlers) {
attrs := admin.Group("/user-attributes")
{
attrs.GET("", h.Admin.UserAttribute.ListDefinitions)
attrs.POST("", h.Admin.UserAttribute.CreateDefinition)
attrs.POST("/batch", h.Admin.UserAttribute.GetBatchUserAttributes)
attrs.PUT("/reorder", h.Admin.UserAttribute.ReorderDefinitions)
attrs.PUT("/:id", h.Admin.UserAttribute.UpdateDefinition)
attrs.DELETE("/:id", h.Admin.UserAttribute.DeleteDefinition)
}
}

View File

@ -13,7 +13,7 @@ import (
// AdminService interface defines admin management operations // AdminService interface defines admin management operations
type AdminService interface { type AdminService interface {
// User management // User management
ListUsers(ctx context.Context, page, pageSize int, status, role, search string) ([]User, int64, error) ListUsers(ctx context.Context, page, pageSize int, filters UserListFilters) ([]User, int64, error)
GetUser(ctx context.Context, id int64) (*User, error) GetUser(ctx context.Context, id int64) (*User, error)
CreateUser(ctx context.Context, input *CreateUserInput) (*User, error) CreateUser(ctx context.Context, input *CreateUserInput) (*User, error)
UpdateUser(ctx context.Context, id int64, input *UpdateUserInput) (*User, error) UpdateUser(ctx context.Context, id int64, input *UpdateUserInput) (*User, error)
@ -70,7 +70,6 @@ type CreateUserInput struct {
Email string Email string
Password string Password string
Username string Username string
Wechat string
Notes string Notes string
Balance float64 Balance float64
Concurrency int Concurrency int
@ -81,7 +80,6 @@ type UpdateUserInput struct {
Email string Email string
Password string Password string
Username *string Username *string
Wechat *string
Notes *string Notes *string
Balance *float64 // 使用指针区分"未提供"和"设置为0" Balance *float64 // 使用指针区分"未提供"和"设置为0"
Concurrency *int // 使用指针区分"未提供"和"设置为0" Concurrency *int // 使用指针区分"未提供"和"设置为0"
@ -252,9 +250,9 @@ func NewAdminService(
} }
// User management implementations // User management implementations
func (s *adminServiceImpl) ListUsers(ctx context.Context, page, pageSize int, status, role, search string) ([]User, int64, error) { func (s *adminServiceImpl) ListUsers(ctx context.Context, page, pageSize int, filters UserListFilters) ([]User, int64, error) {
params := pagination.PaginationParams{Page: page, PageSize: pageSize} params := pagination.PaginationParams{Page: page, PageSize: pageSize}
users, result, err := s.userRepo.ListWithFilters(ctx, params, status, role, search) users, result, err := s.userRepo.ListWithFilters(ctx, params, filters)
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
} }
@ -269,7 +267,6 @@ func (s *adminServiceImpl) CreateUser(ctx context.Context, input *CreateUserInpu
user := &User{ user := &User{
Email: input.Email, Email: input.Email,
Username: input.Username, Username: input.Username,
Wechat: input.Wechat,
Notes: input.Notes, Notes: input.Notes,
Role: RoleUser, // Always create as regular user, never admin Role: RoleUser, // Always create as regular user, never admin
Balance: input.Balance, Balance: input.Balance,
@ -311,9 +308,6 @@ func (s *adminServiceImpl) UpdateUser(ctx context.Context, id int64, input *Upda
if input.Username != nil { if input.Username != nil {
user.Username = *input.Username user.Username = *input.Username
} }
if input.Wechat != nil {
user.Wechat = *input.Wechat
}
if input.Notes != nil { if input.Notes != nil {
user.Notes = *input.Notes user.Notes = *input.Notes
} }

View File

@ -18,7 +18,6 @@ func TestAdminService_CreateUser_Success(t *testing.T) {
Email: "user@test.com", Email: "user@test.com",
Password: "strong-pass", Password: "strong-pass",
Username: "tester", Username: "tester",
Wechat: "wx",
Notes: "note", Notes: "note",
Balance: 12.5, Balance: 12.5,
Concurrency: 7, Concurrency: 7,
@ -31,7 +30,6 @@ func TestAdminService_CreateUser_Success(t *testing.T) {
require.Equal(t, int64(10), user.ID) require.Equal(t, int64(10), user.ID)
require.Equal(t, input.Email, user.Email) require.Equal(t, input.Email, user.Email)
require.Equal(t, input.Username, user.Username) require.Equal(t, input.Username, user.Username)
require.Equal(t, input.Wechat, user.Wechat)
require.Equal(t, input.Notes, user.Notes) require.Equal(t, input.Notes, user.Notes)
require.Equal(t, input.Balance, user.Balance) require.Equal(t, input.Balance, user.Balance)
require.Equal(t, input.Concurrency, user.Concurrency) require.Equal(t, input.Concurrency, user.Concurrency)

View File

@ -66,7 +66,7 @@ func (s *userRepoStub) List(ctx context.Context, params pagination.PaginationPar
panic("unexpected List call") panic("unexpected List call")
} }
func (s *userRepoStub) ListWithFilters(ctx context.Context, params pagination.PaginationParams, status, role, search string) ([]User, *pagination.PaginationResult, error) { func (s *userRepoStub) ListWithFilters(ctx context.Context, params pagination.PaginationParams, filters UserListFilters) ([]User, *pagination.PaginationResult, error) {
panic("unexpected ListWithFilters call") panic("unexpected ListWithFilters call")
} }

View File

@ -10,7 +10,6 @@ type User struct {
ID int64 ID int64
Email string Email string
Username string Username string
Wechat string
Notes string Notes string
PasswordHash string PasswordHash string
Role string Role string

View File

@ -0,0 +1,125 @@
package service
import (
"context"
"time"
infraerrors "github.com/Wei-Shaw/sub2api/internal/pkg/errors"
)
// Error definitions for user attribute operations
var (
ErrAttributeDefinitionNotFound = infraerrors.NotFound("ATTRIBUTE_DEFINITION_NOT_FOUND", "attribute definition not found")
ErrAttributeKeyExists = infraerrors.Conflict("ATTRIBUTE_KEY_EXISTS", "attribute key already exists")
ErrInvalidAttributeType = infraerrors.BadRequest("INVALID_ATTRIBUTE_TYPE", "invalid attribute type")
ErrAttributeValidationFailed = infraerrors.BadRequest("ATTRIBUTE_VALIDATION_FAILED", "attribute value validation failed")
)
// UserAttributeType represents supported attribute types
type UserAttributeType string
const (
AttributeTypeText UserAttributeType = "text"
AttributeTypeTextarea UserAttributeType = "textarea"
AttributeTypeNumber UserAttributeType = "number"
AttributeTypeEmail UserAttributeType = "email"
AttributeTypeURL UserAttributeType = "url"
AttributeTypeDate UserAttributeType = "date"
AttributeTypeSelect UserAttributeType = "select"
AttributeTypeMultiSelect UserAttributeType = "multi_select"
)
// UserAttributeOption represents a select option for select/multi_select types
type UserAttributeOption struct {
Value string `json:"value"`
Label string `json:"label"`
}
// UserAttributeValidation represents validation rules for an attribute
type UserAttributeValidation struct {
MinLength *int `json:"min_length,omitempty"`
MaxLength *int `json:"max_length,omitempty"`
Min *int `json:"min,omitempty"`
Max *int `json:"max,omitempty"`
Pattern *string `json:"pattern,omitempty"`
Message *string `json:"message,omitempty"`
}
// UserAttributeDefinition represents a custom attribute definition
type UserAttributeDefinition struct {
ID int64
Key string
Name string
Description string
Type UserAttributeType
Options []UserAttributeOption
Required bool
Validation UserAttributeValidation
Placeholder string
DisplayOrder int
Enabled bool
CreatedAt time.Time
UpdatedAt time.Time
}
// UserAttributeValue represents a user's attribute value
type UserAttributeValue struct {
ID int64
UserID int64
AttributeID int64
Value string
CreatedAt time.Time
UpdatedAt time.Time
}
// CreateAttributeDefinitionInput for creating new definition
type CreateAttributeDefinitionInput struct {
Key string
Name string
Description string
Type UserAttributeType
Options []UserAttributeOption
Required bool
Validation UserAttributeValidation
Placeholder string
Enabled bool
}
// UpdateAttributeDefinitionInput for updating definition
type UpdateAttributeDefinitionInput struct {
Name *string
Description *string
Type *UserAttributeType
Options *[]UserAttributeOption
Required *bool
Validation *UserAttributeValidation
Placeholder *string
Enabled *bool
}
// UpdateUserAttributeInput for updating a single attribute value
type UpdateUserAttributeInput struct {
AttributeID int64
Value string
}
// UserAttributeDefinitionRepository interface for attribute definition persistence
type UserAttributeDefinitionRepository interface {
Create(ctx context.Context, def *UserAttributeDefinition) error
GetByID(ctx context.Context, id int64) (*UserAttributeDefinition, error)
GetByKey(ctx context.Context, key string) (*UserAttributeDefinition, error)
Update(ctx context.Context, def *UserAttributeDefinition) error
Delete(ctx context.Context, id int64) error
List(ctx context.Context, enabledOnly bool) ([]UserAttributeDefinition, error)
UpdateDisplayOrders(ctx context.Context, orders map[int64]int) error
ExistsByKey(ctx context.Context, key string) (bool, error)
}
// UserAttributeValueRepository interface for user attribute value persistence
type UserAttributeValueRepository interface {
GetByUserID(ctx context.Context, userID int64) ([]UserAttributeValue, error)
GetByUserIDs(ctx context.Context, userIDs []int64) ([]UserAttributeValue, error)
UpsertBatch(ctx context.Context, userID int64, values []UpdateUserAttributeInput) error
DeleteByAttributeID(ctx context.Context, attributeID int64) error
DeleteByUserID(ctx context.Context, userID int64) error
}

View File

@ -0,0 +1,295 @@
package service
import (
"context"
"encoding/json"
"fmt"
"regexp"
"strconv"
"strings"
infraerrors "github.com/Wei-Shaw/sub2api/internal/pkg/errors"
)
// UserAttributeService handles attribute management
type UserAttributeService struct {
defRepo UserAttributeDefinitionRepository
valueRepo UserAttributeValueRepository
}
// NewUserAttributeService creates a new service instance
func NewUserAttributeService(
defRepo UserAttributeDefinitionRepository,
valueRepo UserAttributeValueRepository,
) *UserAttributeService {
return &UserAttributeService{
defRepo: defRepo,
valueRepo: valueRepo,
}
}
// CreateDefinition creates a new attribute definition
func (s *UserAttributeService) CreateDefinition(ctx context.Context, input CreateAttributeDefinitionInput) (*UserAttributeDefinition, error) {
// Validate type
if !isValidAttributeType(input.Type) {
return nil, ErrInvalidAttributeType
}
// Check if key exists
exists, err := s.defRepo.ExistsByKey(ctx, input.Key)
if err != nil {
return nil, fmt.Errorf("check key exists: %w", err)
}
if exists {
return nil, ErrAttributeKeyExists
}
def := &UserAttributeDefinition{
Key: input.Key,
Name: input.Name,
Description: input.Description,
Type: input.Type,
Options: input.Options,
Required: input.Required,
Validation: input.Validation,
Placeholder: input.Placeholder,
Enabled: input.Enabled,
}
if err := s.defRepo.Create(ctx, def); err != nil {
return nil, fmt.Errorf("create definition: %w", err)
}
return def, nil
}
// GetDefinition retrieves a definition by ID
func (s *UserAttributeService) GetDefinition(ctx context.Context, id int64) (*UserAttributeDefinition, error) {
return s.defRepo.GetByID(ctx, id)
}
// ListDefinitions lists all definitions
func (s *UserAttributeService) ListDefinitions(ctx context.Context, enabledOnly bool) ([]UserAttributeDefinition, error) {
return s.defRepo.List(ctx, enabledOnly)
}
// UpdateDefinition updates an existing definition
func (s *UserAttributeService) UpdateDefinition(ctx context.Context, id int64, input UpdateAttributeDefinitionInput) (*UserAttributeDefinition, error) {
def, err := s.defRepo.GetByID(ctx, id)
if err != nil {
return nil, err
}
if input.Name != nil {
def.Name = *input.Name
}
if input.Description != nil {
def.Description = *input.Description
}
if input.Type != nil {
if !isValidAttributeType(*input.Type) {
return nil, ErrInvalidAttributeType
}
def.Type = *input.Type
}
if input.Options != nil {
def.Options = *input.Options
}
if input.Required != nil {
def.Required = *input.Required
}
if input.Validation != nil {
def.Validation = *input.Validation
}
if input.Placeholder != nil {
def.Placeholder = *input.Placeholder
}
if input.Enabled != nil {
def.Enabled = *input.Enabled
}
if err := s.defRepo.Update(ctx, def); err != nil {
return nil, fmt.Errorf("update definition: %w", err)
}
return def, nil
}
// DeleteDefinition soft-deletes a definition and hard-deletes associated values
func (s *UserAttributeService) DeleteDefinition(ctx context.Context, id int64) error {
// Check if definition exists
_, err := s.defRepo.GetByID(ctx, id)
if err != nil {
return err
}
// First delete all values (hard delete)
if err := s.valueRepo.DeleteByAttributeID(ctx, id); err != nil {
return fmt.Errorf("delete values: %w", err)
}
// Then soft-delete the definition
if err := s.defRepo.Delete(ctx, id); err != nil {
return fmt.Errorf("delete definition: %w", err)
}
return nil
}
// ReorderDefinitions updates display order for multiple definitions
func (s *UserAttributeService) ReorderDefinitions(ctx context.Context, orders map[int64]int) error {
return s.defRepo.UpdateDisplayOrders(ctx, orders)
}
// GetUserAttributes retrieves all attribute values for a user
func (s *UserAttributeService) GetUserAttributes(ctx context.Context, userID int64) ([]UserAttributeValue, error) {
return s.valueRepo.GetByUserID(ctx, userID)
}
// GetBatchUserAttributes retrieves attribute values for multiple users
// Returns a map of userID -> map of attributeID -> value
func (s *UserAttributeService) GetBatchUserAttributes(ctx context.Context, userIDs []int64) (map[int64]map[int64]string, error) {
values, err := s.valueRepo.GetByUserIDs(ctx, userIDs)
if err != nil {
return nil, err
}
result := make(map[int64]map[int64]string)
for _, v := range values {
if result[v.UserID] == nil {
result[v.UserID] = make(map[int64]string)
}
result[v.UserID][v.AttributeID] = v.Value
}
return result, nil
}
// UpdateUserAttributes batch updates attribute values for a user
func (s *UserAttributeService) UpdateUserAttributes(ctx context.Context, userID int64, inputs []UpdateUserAttributeInput) error {
// Validate all values before updating
defs, err := s.defRepo.List(ctx, true)
if err != nil {
return fmt.Errorf("list definitions: %w", err)
}
defMap := make(map[int64]*UserAttributeDefinition, len(defs))
for i := range defs {
defMap[defs[i].ID] = &defs[i]
}
for _, input := range inputs {
def, ok := defMap[input.AttributeID]
if !ok {
return ErrAttributeDefinitionNotFound
}
if err := s.validateValue(def, input.Value); err != nil {
return err
}
}
return s.valueRepo.UpsertBatch(ctx, userID, inputs)
}
// validateValue validates a value against its definition
func (s *UserAttributeService) validateValue(def *UserAttributeDefinition, value string) error {
// Skip validation for empty non-required fields
if value == "" && !def.Required {
return nil
}
// Required check
if def.Required && value == "" {
return validationError(fmt.Sprintf("%s is required", def.Name))
}
v := def.Validation
// String length validation
if v.MinLength != nil && len(value) < *v.MinLength {
return validationError(fmt.Sprintf("%s must be at least %d characters", def.Name, *v.MinLength))
}
if v.MaxLength != nil && len(value) > *v.MaxLength {
return validationError(fmt.Sprintf("%s must be at most %d characters", def.Name, *v.MaxLength))
}
// Number validation
if def.Type == AttributeTypeNumber && value != "" {
num, err := strconv.Atoi(value)
if err != nil {
return validationError(fmt.Sprintf("%s must be a number", def.Name))
}
if v.Min != nil && num < *v.Min {
return validationError(fmt.Sprintf("%s must be at least %d", def.Name, *v.Min))
}
if v.Max != nil && num > *v.Max {
return validationError(fmt.Sprintf("%s must be at most %d", def.Name, *v.Max))
}
}
// Pattern validation
if v.Pattern != nil && *v.Pattern != "" && value != "" {
re, err := regexp.Compile(*v.Pattern)
if err == nil && !re.MatchString(value) {
msg := def.Name + " format is invalid"
if v.Message != nil && *v.Message != "" {
msg = *v.Message
}
return validationError(msg)
}
}
// Select validation
if def.Type == AttributeTypeSelect && value != "" {
found := false
for _, opt := range def.Options {
if opt.Value == value {
found = true
break
}
}
if !found {
return validationError(fmt.Sprintf("%s: invalid option", def.Name))
}
}
// Multi-select validation (stored as JSON array)
if def.Type == AttributeTypeMultiSelect && value != "" {
var values []string
if err := json.Unmarshal([]byte(value), &values); err != nil {
// Try comma-separated fallback
values = strings.Split(value, ",")
}
for _, val := range values {
val = strings.TrimSpace(val)
found := false
for _, opt := range def.Options {
if opt.Value == val {
found = true
break
}
}
if !found {
return validationError(fmt.Sprintf("%s: invalid option %s", def.Name, val))
}
}
}
return nil
}
// validationError creates a validation error with a custom message
func validationError(msg string) error {
return infraerrors.BadRequest("ATTRIBUTE_VALIDATION_FAILED", msg)
}
func isValidAttributeType(t UserAttributeType) bool {
switch t {
case AttributeTypeText, AttributeTypeTextarea, AttributeTypeNumber,
AttributeTypeEmail, AttributeTypeURL, AttributeTypeDate,
AttributeTypeSelect, AttributeTypeMultiSelect:
return true
}
return false
}

View File

@ -14,6 +14,14 @@ var (
ErrInsufficientPerms = infraerrors.Forbidden("INSUFFICIENT_PERMISSIONS", "insufficient permissions") ErrInsufficientPerms = infraerrors.Forbidden("INSUFFICIENT_PERMISSIONS", "insufficient permissions")
) )
// UserListFilters contains all filter options for listing users
type UserListFilters struct {
Status string // User status filter
Role string // User role filter
Search string // Search in email, username
Attributes map[int64]string // Custom attribute filters: attributeID -> value
}
type UserRepository interface { type UserRepository interface {
Create(ctx context.Context, user *User) error Create(ctx context.Context, user *User) error
GetByID(ctx context.Context, id int64) (*User, error) GetByID(ctx context.Context, id int64) (*User, error)
@ -23,7 +31,7 @@ type UserRepository interface {
Delete(ctx context.Context, id int64) error Delete(ctx context.Context, id int64) error
List(ctx context.Context, params pagination.PaginationParams) ([]User, *pagination.PaginationResult, error) List(ctx context.Context, params pagination.PaginationParams) ([]User, *pagination.PaginationResult, error)
ListWithFilters(ctx context.Context, params pagination.PaginationParams, status, role, search string) ([]User, *pagination.PaginationResult, error) ListWithFilters(ctx context.Context, params pagination.PaginationParams, filters UserListFilters) ([]User, *pagination.PaginationResult, error)
UpdateBalance(ctx context.Context, id int64, amount float64) error UpdateBalance(ctx context.Context, id int64, amount float64) error
DeductBalance(ctx context.Context, id int64, amount float64) error DeductBalance(ctx context.Context, id int64, amount float64) error
@ -36,7 +44,6 @@ type UserRepository interface {
type UpdateProfileRequest struct { type UpdateProfileRequest struct {
Email *string `json:"email"` Email *string `json:"email"`
Username *string `json:"username"` Username *string `json:"username"`
Wechat *string `json:"wechat"`
Concurrency *int `json:"concurrency"` Concurrency *int `json:"concurrency"`
} }
@ -100,10 +107,6 @@ func (s *UserService) UpdateProfile(ctx context.Context, userID int64, req Updat
user.Username = *req.Username user.Username = *req.Username
} }
if req.Wechat != nil {
user.Wechat = *req.Wechat
}
if req.Concurrency != nil { if req.Concurrency != nil {
user.Concurrency = *req.Concurrency user.Concurrency = *req.Concurrency
} }

View File

@ -125,4 +125,5 @@ var ProviderSet = wire.NewSet(
ProvideTimingWheelService, ProvideTimingWheelService,
ProvideDeferredService, ProvideDeferredService,
ProvideAntigravityQuotaRefresher, ProvideAntigravityQuotaRefresher,
NewUserAttributeService,
) )

View File

@ -0,0 +1,48 @@
-- Add user attribute definitions and values tables for custom user attributes.
-- User Attribute Definitions table (with soft delete support)
CREATE TABLE IF NOT EXISTS user_attribute_definitions (
id BIGSERIAL PRIMARY KEY,
key VARCHAR(100) NOT NULL,
name VARCHAR(255) NOT NULL,
description TEXT DEFAULT '',
type VARCHAR(20) NOT NULL,
options JSONB DEFAULT '[]'::jsonb,
required BOOLEAN NOT NULL DEFAULT FALSE,
validation JSONB DEFAULT '{}'::jsonb,
placeholder VARCHAR(255) DEFAULT '',
display_order INT NOT NULL DEFAULT 0,
enabled BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
deleted_at TIMESTAMPTZ
);
-- Partial unique index for key (only for non-deleted records)
-- Allows reusing keys after soft delete
CREATE UNIQUE INDEX IF NOT EXISTS idx_user_attribute_definitions_key_unique
ON user_attribute_definitions(key) WHERE deleted_at IS NULL;
CREATE INDEX IF NOT EXISTS idx_user_attribute_definitions_enabled
ON user_attribute_definitions(enabled);
CREATE INDEX IF NOT EXISTS idx_user_attribute_definitions_display_order
ON user_attribute_definitions(display_order);
CREATE INDEX IF NOT EXISTS idx_user_attribute_definitions_deleted_at
ON user_attribute_definitions(deleted_at);
-- User Attribute Values table (hard delete only, no deleted_at)
CREATE TABLE IF NOT EXISTS user_attribute_values (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
attribute_id BIGINT NOT NULL REFERENCES user_attribute_definitions(id) ON DELETE CASCADE,
value TEXT DEFAULT '',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE(user_id, attribute_id)
);
CREATE INDEX IF NOT EXISTS idx_user_attribute_values_user_id
ON user_attribute_values(user_id);
CREATE INDEX IF NOT EXISTS idx_user_attribute_values_attribute_id
ON user_attribute_values(attribute_id);

View File

@ -0,0 +1,83 @@
-- Migration: Move wechat field from users table to user_attribute_values
-- This migration:
-- 1. Creates a "wechat" attribute definition
-- 2. Migrates existing wechat data to user_attribute_values
-- 3. Does NOT drop the wechat column (for rollback safety, can be done in a later migration)
-- +goose Up
-- +goose StatementBegin
-- Step 1: Insert wechat attribute definition if not exists
INSERT INTO user_attribute_definitions (key, name, description, type, options, required, validation, placeholder, display_order, enabled, created_at, updated_at)
SELECT 'wechat', '微信', '用户微信号', 'text', '[]'::jsonb, false, '{}'::jsonb, '请输入微信号', 0, true, NOW(), NOW()
WHERE NOT EXISTS (
SELECT 1 FROM user_attribute_definitions WHERE key = 'wechat' AND deleted_at IS NULL
);
-- Step 2: Migrate existing wechat values to user_attribute_values
-- Only migrate non-empty values
INSERT INTO user_attribute_values (user_id, attribute_id, value, created_at, updated_at)
SELECT
u.id,
(SELECT id FROM user_attribute_definitions WHERE key = 'wechat' AND deleted_at IS NULL LIMIT 1),
u.wechat,
NOW(),
NOW()
FROM users u
WHERE u.wechat IS NOT NULL
AND u.wechat != ''
AND u.deleted_at IS NULL
AND NOT EXISTS (
SELECT 1 FROM user_attribute_values uav
WHERE uav.user_id = u.id
AND uav.attribute_id = (SELECT id FROM user_attribute_definitions WHERE key = 'wechat' AND deleted_at IS NULL LIMIT 1)
);
-- Step 3: Update display_order to ensure wechat appears first
UPDATE user_attribute_definitions
SET display_order = -1
WHERE key = 'wechat' AND deleted_at IS NULL;
-- Reorder all attributes starting from 0
WITH ordered AS (
SELECT id, ROW_NUMBER() OVER (ORDER BY display_order, id) - 1 as new_order
FROM user_attribute_definitions
WHERE deleted_at IS NULL
)
UPDATE user_attribute_definitions
SET display_order = ordered.new_order
FROM ordered
WHERE user_attribute_definitions.id = ordered.id;
-- Step 4: Drop the redundant wechat column from users table
ALTER TABLE users DROP COLUMN IF EXISTS wechat;
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
-- Restore wechat column
ALTER TABLE users ADD COLUMN IF NOT EXISTS wechat VARCHAR(100) DEFAULT '';
-- Copy attribute values back to users.wechat column
UPDATE users u
SET wechat = uav.value
FROM user_attribute_values uav
JOIN user_attribute_definitions uad ON uav.attribute_id = uad.id
WHERE uav.user_id = u.id
AND uad.key = 'wechat'
AND uad.deleted_at IS NULL;
-- Delete migrated attribute values
DELETE FROM user_attribute_values
WHERE attribute_id IN (
SELECT id FROM user_attribute_definitions WHERE key = 'wechat' AND deleted_at IS NULL
);
-- Soft-delete the wechat attribute definition
UPDATE user_attribute_definitions
SET deleted_at = NOW()
WHERE key = 'wechat' AND deleted_at IS NULL;
-- +goose StatementEnd

View File

@ -15,6 +15,7 @@ import subscriptionsAPI from './subscriptions'
import usageAPI from './usage' import usageAPI from './usage'
import geminiAPI from './gemini' import geminiAPI from './gemini'
import antigravityAPI from './antigravity' import antigravityAPI from './antigravity'
import userAttributesAPI from './userAttributes'
/** /**
* Unified admin API object for convenient access * Unified admin API object for convenient access
@ -31,7 +32,8 @@ export const adminAPI = {
subscriptions: subscriptionsAPI, subscriptions: subscriptionsAPI,
usage: usageAPI, usage: usageAPI,
gemini: geminiAPI, gemini: geminiAPI,
antigravity: antigravityAPI antigravity: antigravityAPI,
userAttributes: userAttributesAPI
} }
export { export {
@ -46,7 +48,8 @@ export {
subscriptionsAPI, subscriptionsAPI,
usageAPI, usageAPI,
geminiAPI, geminiAPI,
antigravityAPI antigravityAPI,
userAttributesAPI
} }
export default adminAPI export default adminAPI

View File

@ -0,0 +1,131 @@
/**
* Admin User Attributes API endpoints
* Handles user custom attribute definitions and values
*/
import { apiClient } from '../client'
import type {
UserAttributeDefinition,
UserAttributeValue,
CreateUserAttributeRequest,
UpdateUserAttributeRequest,
UserAttributeValuesMap
} from '@/types'
/**
* Get all attribute definitions
*/
export async function listDefinitions(): Promise<UserAttributeDefinition[]> {
const { data } = await apiClient.get<UserAttributeDefinition[]>('/admin/user-attributes')
return data
}
/**
* Get enabled attribute definitions only
*/
export async function listEnabledDefinitions(): Promise<UserAttributeDefinition[]> {
const { data } = await apiClient.get<UserAttributeDefinition[]>('/admin/user-attributes', {
params: { enabled: true }
})
return data
}
/**
* Create a new attribute definition
*/
export async function createDefinition(
request: CreateUserAttributeRequest
): Promise<UserAttributeDefinition> {
const { data } = await apiClient.post<UserAttributeDefinition>('/admin/user-attributes', request)
return data
}
/**
* Update an attribute definition
*/
export async function updateDefinition(
id: number,
request: UpdateUserAttributeRequest
): Promise<UserAttributeDefinition> {
const { data } = await apiClient.put<UserAttributeDefinition>(
`/admin/user-attributes/${id}`,
request
)
return data
}
/**
* Delete an attribute definition
*/
export async function deleteDefinition(id: number): Promise<{ message: string }> {
const { data } = await apiClient.delete<{ message: string }>(`/admin/user-attributes/${id}`)
return data
}
/**
* Reorder attribute definitions
*/
export async function reorderDefinitions(ids: number[]): Promise<{ message: string }> {
const { data } = await apiClient.put<{ message: string }>('/admin/user-attributes/reorder', {
ids
})
return data
}
/**
* Get user's attribute values
*/
export async function getUserAttributeValues(userId: number): Promise<UserAttributeValue[]> {
const { data } = await apiClient.get<UserAttributeValue[]>(
`/admin/users/${userId}/attributes`
)
return data
}
/**
* Update user's attribute values (batch)
*/
export async function updateUserAttributeValues(
userId: number,
values: UserAttributeValuesMap
): Promise<{ message: string }> {
const { data } = await apiClient.put<{ message: string }>(
`/admin/users/${userId}/attributes`,
{ values }
)
return data
}
/**
* Batch response type
*/
export interface BatchUserAttributesResponse {
attributes: Record<number, Record<number, string>>
}
/**
* Get attribute values for multiple users
*/
export async function getBatchUserAttributes(
userIds: number[]
): Promise<BatchUserAttributesResponse> {
const { data } = await apiClient.post<BatchUserAttributesResponse>(
'/admin/user-attributes/batch',
{ user_ids: userIds }
)
return data
}
export const userAttributesAPI = {
listDefinitions,
listEnabledDefinitions,
createDefinition,
updateDefinition,
deleteDefinition,
reorderDefinitions,
getUserAttributeValues,
updateUserAttributeValues,
getBatchUserAttributes
}
export default userAttributesAPI

View File

@ -10,7 +10,7 @@ import type { User, UpdateUserRequest, PaginatedResponse } from '@/types'
* List all users with pagination * List all users with pagination
* @param page - Page number (default: 1) * @param page - Page number (default: 1)
* @param pageSize - Items per page (default: 20) * @param pageSize - Items per page (default: 20)
* @param filters - Optional filters (status, role, search) * @param filters - Optional filters (status, role, search, attributes)
* @param options - Optional request options (signal) * @param options - Optional request options (signal)
* @returns Paginated list of users * @returns Paginated list of users
*/ */
@ -21,17 +21,32 @@ export async function list(
status?: 'active' | 'disabled' status?: 'active' | 'disabled'
role?: 'admin' | 'user' role?: 'admin' | 'user'
search?: string search?: string
attributes?: Record<number, string> // attributeId -> value
}, },
options?: { options?: {
signal?: AbortSignal signal?: AbortSignal
} }
): Promise<PaginatedResponse<User>> { ): Promise<PaginatedResponse<User>> {
// Build params with attribute filters in attr[id]=value format
const params: Record<string, any> = {
page,
page_size: pageSize,
status: filters?.status,
role: filters?.role,
search: filters?.search
}
// Add attribute filters as attr[id]=value
if (filters?.attributes) {
for (const [attrId, value] of Object.entries(filters.attributes)) {
if (value) {
params[`attr[${attrId}]`] = value
}
}
}
const { data } = await apiClient.get<PaginatedResponse<User>>('/admin/users', { const { data } = await apiClient.get<PaginatedResponse<User>>('/admin/users', {
params: { params,
page,
page_size: pageSize,
...filters
},
signal: options?.signal signal: options?.signal
}) })
return data return data

View File

@ -22,7 +22,6 @@ export async function getProfile(): Promise<User> {
*/ */
export async function updateProfile(profile: { export async function updateProfile(profile: {
username?: string username?: string
wechat?: string
}): Promise<User> { }): Promise<User> {
const { data } = await apiClient.put<User>('/user', profile) const { data } = await apiClient.put<User>('/user', profile)
return data return data

View File

@ -0,0 +1,207 @@
<template>
<div v-if="attributes.length > 0" class="space-y-4">
<div v-for="attr in attributes" :key="attr.id">
<label class="input-label">
{{ attr.name }}
<span v-if="attr.required" class="text-red-500">*</span>
</label>
<!-- Text Input -->
<input
v-if="attr.type === 'text' || attr.type === 'email' || attr.type === 'url'"
v-model="localValues[attr.id]"
:type="attr.type === 'text' ? 'text' : attr.type"
:required="attr.required"
:placeholder="attr.placeholder"
class="input"
@input="emitChange"
/>
<!-- Number Input -->
<input
v-else-if="attr.type === 'number'"
v-model.number="localValues[attr.id]"
type="number"
:required="attr.required"
:placeholder="attr.placeholder"
:min="attr.validation?.min"
:max="attr.validation?.max"
class="input"
@input="emitChange"
/>
<!-- Date Input -->
<input
v-else-if="attr.type === 'date'"
v-model="localValues[attr.id]"
type="date"
:required="attr.required"
class="input"
@input="emitChange"
/>
<!-- Textarea -->
<textarea
v-else-if="attr.type === 'textarea'"
v-model="localValues[attr.id]"
:required="attr.required"
:placeholder="attr.placeholder"
rows="3"
class="input"
@input="emitChange"
/>
<!-- Select -->
<select
v-else-if="attr.type === 'select'"
v-model="localValues[attr.id]"
:required="attr.required"
class="input"
@change="emitChange"
>
<option value="">{{ t('common.selectOption') }}</option>
<option v-for="opt in attr.options" :key="opt.value" :value="opt.value">
{{ opt.label }}
</option>
</select>
<!-- Multi-Select (Checkboxes) -->
<div v-else-if="attr.type === 'multi_select'" class="space-y-2">
<label
v-for="opt in attr.options"
:key="opt.value"
class="flex items-center gap-2"
>
<input
type="checkbox"
:value="opt.value"
:checked="isOptionSelected(attr.id, opt.value)"
@change="toggleMultiSelectOption(attr.id, opt.value)"
class="h-4 w-4 rounded border-gray-300 text-primary-600"
/>
<span class="text-sm text-gray-700 dark:text-gray-300">{{ opt.label }}</span>
</label>
</div>
<!-- Description -->
<p v-if="attr.description" class="input-hint">{{ attr.description }}</p>
</div>
</div>
<!-- Loading State -->
<div v-else-if="loading" class="flex justify-center py-4">
<svg class="h-5 w-5 animate-spin text-gray-400" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" />
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" />
</svg>
</div>
</template>
<script setup lang="ts">
import { ref, watch, onMounted } from 'vue'
import { useI18n } from 'vue-i18n'
import { adminAPI } from '@/api/admin'
import type { UserAttributeDefinition, UserAttributeValuesMap } from '@/types'
const { t } = useI18n()
interface Props {
userId?: number
modelValue: UserAttributeValuesMap
}
interface Emits {
(e: 'update:modelValue', value: UserAttributeValuesMap): void
}
const props = defineProps<Props>()
const emit = defineEmits<Emits>()
const loading = ref(false)
const attributes = ref<UserAttributeDefinition[]>([])
const localValues = ref<UserAttributeValuesMap>({})
const loadAttributes = async () => {
loading.value = true
try {
attributes.value = await adminAPI.userAttributes.listEnabledDefinitions()
} catch (error) {
console.error('Failed to load attributes:', error)
} finally {
loading.value = false
}
}
const loadUserValues = async () => {
if (!props.userId) return
try {
const values = await adminAPI.userAttributes.getUserAttributeValues(props.userId)
const valuesMap: UserAttributeValuesMap = {}
values.forEach(v => {
valuesMap[v.attribute_id] = v.value
})
localValues.value = { ...valuesMap }
emit('update:modelValue', localValues.value)
} catch (error) {
console.error('Failed to load user attribute values:', error)
}
}
const emitChange = () => {
emit('update:modelValue', { ...localValues.value })
}
const isOptionSelected = (attrId: number, optionValue: string): boolean => {
const value = localValues.value[attrId]
if (!value) return false
try {
const arr = JSON.parse(value)
return Array.isArray(arr) && arr.includes(optionValue)
} catch {
return false
}
}
const toggleMultiSelectOption = (attrId: number, optionValue: string) => {
let arr: string[] = []
const value = localValues.value[attrId]
if (value) {
try {
arr = JSON.parse(value)
if (!Array.isArray(arr)) arr = []
} catch {
arr = []
}
}
const index = arr.indexOf(optionValue)
if (index > -1) {
arr.splice(index, 1)
} else {
arr.push(optionValue)
}
localValues.value[attrId] = JSON.stringify(arr)
emitChange()
}
watch(() => props.modelValue, (newVal) => {
if (newVal && Object.keys(newVal).length > 0) {
localValues.value = { ...newVal }
}
}, { immediate: true })
watch(() => props.userId, (newUserId) => {
if (newUserId) {
loadUserValues()
} else {
// Reset for new user
localValues.value = {}
}
}, { immediate: true })
onMounted(() => {
loadAttributes()
})
</script>

View File

@ -0,0 +1,404 @@
<template>
<BaseDialog :show="show" :title="t('admin.users.attributes.title')" width="wide" @close="emit('close')">
<div class="space-y-4">
<!-- Header with Add Button -->
<div class="flex items-center justify-between">
<p class="text-sm text-gray-500 dark:text-dark-400">
{{ t('admin.users.attributes.description') }}
</p>
<button @click="openCreateModal" class="btn btn-primary btn-sm">
<svg class="mr-1.5 h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
</svg>
{{ t('admin.users.attributes.addAttribute') }}
</button>
</div>
<!-- Loading State -->
<div v-if="loading" class="flex justify-center py-12">
<svg class="h-8 w-8 animate-spin text-primary-500" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" />
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" />
</svg>
</div>
<!-- Empty State -->
<div v-else-if="attributes.length === 0" class="py-12 text-center">
<svg class="mx-auto h-12 w-12 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1">
<path stroke-linecap="round" stroke-linejoin="round" d="M9.568 3H5.25A2.25 2.25 0 003 5.25v4.318c0 .597.237 1.17.659 1.591l9.581 9.581c.699.699 1.78.872 2.607.33a18.095 18.095 0 005.223-5.223c.542-.827.369-1.908-.33-2.607L11.16 3.66A2.25 2.25 0 009.568 3z" />
<path stroke-linecap="round" stroke-linejoin="round" d="M6 6h.008v.008H6V6z" />
</svg>
<p class="mt-2 text-sm text-gray-500 dark:text-dark-400">
{{ t('admin.users.attributes.noAttributes') }}
</p>
<p class="text-xs text-gray-400 dark:text-dark-500">
{{ t('admin.users.attributes.noAttributesHint') }}
</p>
</div>
<!-- Attributes List -->
<div v-else class="max-h-96 space-y-2 overflow-y-auto">
<div
v-for="attr in attributes"
:key="attr.id"
class="flex items-center gap-3 rounded-lg border border-gray-200 bg-white p-3 dark:border-dark-600 dark:bg-dark-800"
>
<!-- Drag Handle -->
<div class="cursor-move text-gray-400 hover:text-gray-600 dark:hover:text-gray-300" :title="t('admin.users.attributes.dragToReorder')">
<svg class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" />
</svg>
</div>
<!-- Attribute Info -->
<div class="min-w-0 flex-1">
<div class="flex items-center gap-2">
<span class="font-medium text-gray-900 dark:text-white">{{ attr.name }}</span>
<span class="rounded bg-gray-100 px-1.5 py-0.5 font-mono text-xs text-gray-500 dark:bg-dark-700 dark:text-dark-400">
{{ attr.key }}
</span>
<span v-if="attr.required" class="badge badge-danger text-xs">
{{ t('admin.users.attributes.required') }}
</span>
<span v-if="!attr.enabled" class="badge badge-gray text-xs">
{{ t('common.disabled') }}
</span>
</div>
<div class="mt-0.5 flex items-center gap-2 text-xs text-gray-500 dark:text-dark-400">
<span class="badge badge-gray">{{ t(`admin.users.attributes.types.${attr.type}`) }}</span>
<span v-if="attr.description" class="truncate">{{ attr.description }}</span>
</div>
</div>
<!-- Actions -->
<div class="flex items-center gap-1">
<button
@click="openEditModal(attr)"
class="rounded-lg p-1.5 text-gray-500 hover:bg-gray-100 hover:text-primary-600 dark:hover:bg-dark-700 dark:hover:text-primary-400"
:title="t('common.edit')"
>
<svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L10.582 16.07a4.5 4.5 0 01-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 011.13-1.897l8.932-8.931zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0115.75 21H5.25A2.25 2.25 0 013 18.75V8.25A2.25 2.25 0 015.25 6H10" />
</svg>
</button>
<button
@click="confirmDelete(attr)"
class="rounded-lg p-1.5 text-gray-500 hover:bg-red-50 hover:text-red-600 dark:hover:bg-red-900/20 dark:hover:text-red-400"
:title="t('common.delete')"
>
<svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0" />
</svg>
</button>
</div>
</div>
</div>
</div>
<template #footer>
<div class="flex justify-end">
<button @click="emit('close')" class="btn btn-secondary">
{{ t('common.close') }}
</button>
</div>
</template>
</BaseDialog>
<!-- Create/Edit Attribute Modal -->
<BaseDialog
:show="showEditModal"
:title="editingAttribute ? t('admin.users.attributes.editAttribute') : t('admin.users.attributes.addAttribute')"
width="normal"
@close="closeEditModal"
>
<form id="attribute-form" @submit.prevent="handleSave" class="space-y-4">
<!-- Key -->
<div>
<label class="input-label">{{ t('admin.users.attributes.key') }}</label>
<input
v-model="form.key"
type="text"
required
pattern="^[a-zA-Z][a-zA-Z0-9_]*$"
class="input font-mono"
:placeholder="t('admin.users.attributes.keyHint')"
:disabled="!!editingAttribute"
/>
<p class="input-hint">{{ t('admin.users.attributes.keyHint') }}</p>
</div>
<!-- Name -->
<div>
<label class="input-label">{{ t('admin.users.attributes.name') }}</label>
<input
v-model="form.name"
type="text"
required
class="input"
:placeholder="t('admin.users.attributes.nameHint')"
/>
</div>
<!-- Type -->
<div>
<label class="input-label">{{ t('admin.users.attributes.type') }}</label>
<select v-model="form.type" class="input" required>
<option v-for="type in attributeTypes" :key="type" :value="type">
{{ t(`admin.users.attributes.types.${type}`) }}
</option>
</select>
</div>
<!-- Options (for select/multi_select) -->
<div v-if="form.type === 'select' || form.type === 'multi_select'" class="space-y-2">
<label class="input-label">{{ t('admin.users.attributes.options') }}</label>
<div v-for="(option, index) in form.options" :key="index" class="flex items-center gap-2">
<input
v-model="option.value"
type="text"
class="input flex-1 font-mono text-sm"
:placeholder="t('admin.users.attributes.optionValue')"
required
/>
<input
v-model="option.label"
type="text"
class="input flex-1 text-sm"
:placeholder="t('admin.users.attributes.optionLabel')"
required
/>
<button
type="button"
@click="removeOption(index)"
class="rounded-lg p-1.5 text-gray-500 hover:bg-red-50 hover:text-red-600"
>
<svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<button type="button" @click="addOption" class="btn btn-secondary btn-sm">
<svg class="mr-1 h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
</svg>
{{ t('admin.users.attributes.addOption') }}
</button>
</div>
<!-- Description -->
<div>
<label class="input-label">{{ t('admin.users.attributes.fieldDescription') }}</label>
<input
v-model="form.description"
type="text"
class="input"
:placeholder="t('admin.users.attributes.fieldDescriptionHint')"
/>
</div>
<!-- Placeholder -->
<div>
<label class="input-label">{{ t('admin.users.attributes.placeholder') }}</label>
<input
v-model="form.placeholder"
type="text"
class="input"
:placeholder="t('admin.users.attributes.placeholderHint')"
/>
</div>
<!-- Required & Enabled -->
<div class="flex items-center gap-6">
<label class="flex items-center gap-2">
<input v-model="form.required" type="checkbox" class="h-4 w-4 rounded border-gray-300 text-primary-600" />
<span class="text-sm text-gray-700 dark:text-gray-300">{{ t('admin.users.attributes.required') }}</span>
</label>
<label class="flex items-center gap-2">
<input v-model="form.enabled" type="checkbox" class="h-4 w-4 rounded border-gray-300 text-primary-600" />
<span class="text-sm text-gray-700 dark:text-gray-300">{{ t('admin.users.attributes.enabled') }}</span>
</label>
</div>
</form>
<template #footer>
<div class="flex justify-end gap-3">
<button @click="closeEditModal" type="button" class="btn btn-secondary">
{{ t('common.cancel') }}
</button>
<button type="submit" form="attribute-form" :disabled="saving" class="btn btn-primary">
<svg v-if="saving" class="-ml-1 mr-2 h-4 w-4 animate-spin" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" />
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" />
</svg>
{{ saving ? t('common.saving') : (editingAttribute ? t('common.update') : t('common.create')) }}
</button>
</div>
</template>
</BaseDialog>
<!-- Delete Confirmation -->
<ConfirmDialog
:show="showDeleteDialog"
:title="t('admin.users.attributes.deleteAttribute')"
:message="t('admin.users.attributes.deleteConfirm', { name: deletingAttribute?.name })"
:confirm-text="t('common.delete')"
:cancel-text="t('common.cancel')"
:danger="true"
@confirm="handleDelete"
@cancel="showDeleteDialog = false"
/>
</template>
<script setup lang="ts">
import { ref, reactive, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { useAppStore } from '@/stores/app'
import { adminAPI } from '@/api/admin'
import type { UserAttributeDefinition, UserAttributeType, UserAttributeOption } from '@/types'
import BaseDialog from '@/components/common/BaseDialog.vue'
import ConfirmDialog from '@/components/common/ConfirmDialog.vue'
const { t } = useI18n()
const appStore = useAppStore()
interface Props {
show: boolean
}
interface Emits {
(e: 'close'): void
}
const props = defineProps<Props>()
const emit = defineEmits<Emits>()
const attributeTypes: UserAttributeType[] = ['text', 'textarea', 'number', 'email', 'url', 'date', 'select', 'multi_select']
const loading = ref(false)
const saving = ref(false)
const attributes = ref<UserAttributeDefinition[]>([])
const showEditModal = ref(false)
const showDeleteDialog = ref(false)
const editingAttribute = ref<UserAttributeDefinition | null>(null)
const deletingAttribute = ref<UserAttributeDefinition | null>(null)
const form = reactive({
key: '',
name: '',
type: 'text' as UserAttributeType,
description: '',
placeholder: '',
required: false,
enabled: true,
options: [] as UserAttributeOption[]
})
const loadAttributes = async () => {
loading.value = true
try {
attributes.value = await adminAPI.userAttributes.listDefinitions()
} catch (error: any) {
appStore.showError(error.response?.data?.detail || t('admin.users.attributes.failedToLoad'))
} finally {
loading.value = false
}
}
const openCreateModal = () => {
editingAttribute.value = null
form.key = ''
form.name = ''
form.type = 'text'
form.description = ''
form.placeholder = ''
form.required = false
form.enabled = true
form.options = []
showEditModal.value = true
}
const openEditModal = (attr: UserAttributeDefinition) => {
editingAttribute.value = attr
form.key = attr.key
form.name = attr.name
form.type = attr.type
form.description = attr.description || ''
form.placeholder = attr.placeholder || ''
form.required = attr.required
form.enabled = attr.enabled
form.options = attr.options ? [...attr.options] : []
showEditModal.value = true
}
const closeEditModal = () => {
showEditModal.value = false
editingAttribute.value = null
}
const addOption = () => {
form.options.push({ value: '', label: '' })
}
const removeOption = (index: number) => {
form.options.splice(index, 1)
}
const handleSave = async () => {
saving.value = true
try {
const data = {
key: form.key,
name: form.name,
type: form.type,
description: form.description || undefined,
placeholder: form.placeholder || undefined,
required: form.required,
enabled: form.enabled,
options: (form.type === 'select' || form.type === 'multi_select') ? form.options : undefined
}
if (editingAttribute.value) {
await adminAPI.userAttributes.updateDefinition(editingAttribute.value.id, data)
appStore.showSuccess(t('admin.users.attributes.updated'))
} else {
await adminAPI.userAttributes.createDefinition(data)
appStore.showSuccess(t('admin.users.attributes.created'))
}
closeEditModal()
loadAttributes()
} catch (error: any) {
const msg = editingAttribute.value
? t('admin.users.attributes.failedToUpdate')
: t('admin.users.attributes.failedToCreate')
appStore.showError(error.response?.data?.detail || msg)
} finally {
saving.value = false
}
}
const confirmDelete = (attr: UserAttributeDefinition) => {
deletingAttribute.value = attr
showDeleteDialog.value = true
}
const handleDelete = async () => {
if (!deletingAttribute.value) return
try {
await adminAPI.userAttributes.deleteDefinition(deletingAttribute.value.id)
appStore.showSuccess(t('admin.users.attributes.deleted'))
showDeleteDialog.value = false
deletingAttribute.value = null
loadAttributes()
} catch (error: any) {
appStore.showError(error.response?.data?.detail || t('admin.users.attributes.failedToDelete'))
}
}
watch(() => props.show, (isShow) => {
if (isShow) {
loadAttributes()
}
})
</script>

View File

@ -434,9 +434,7 @@ export default {
administrator: 'Administrator', administrator: 'Administrator',
user: 'User', user: 'User',
username: 'Username', username: 'Username',
wechat: 'WeChat ID',
enterUsername: 'Enter username', enterUsername: 'Enter username',
enterWechat: 'Enter WeChat ID',
editProfile: 'Edit Profile', editProfile: 'Edit Profile',
updateProfile: 'Update Profile', updateProfile: 'Update Profile',
updating: 'Updating...', updating: 'Updating...',
@ -565,12 +563,10 @@ export default {
email: 'Email', email: 'Email',
password: 'Password', password: 'Password',
username: 'Username', username: 'Username',
wechat: 'WeChat ID',
notes: 'Notes', notes: 'Notes',
enterEmail: 'Enter email', enterEmail: 'Enter email',
enterPassword: 'Enter password', enterPassword: 'Enter password',
enterUsername: 'Enter username (optional)', enterUsername: 'Enter username (optional)',
enterWechat: 'Enter WeChat ID (optional)',
enterNotes: 'Enter notes (admin only)', enterNotes: 'Enter notes (admin only)',
notesHint: 'This note is only visible to administrators', notesHint: 'This note is only visible to administrators',
enterNewPassword: 'Enter new password (optional)', enterNewPassword: 'Enter new password (optional)',
@ -582,7 +578,6 @@ export default {
columns: { columns: {
user: 'User', user: 'User',
username: 'Username', username: 'Username',
wechat: 'WeChat ID',
notes: 'Notes', notes: 'Notes',
role: 'Role', role: 'Role',
subscriptions: 'Subscriptions', subscriptions: 'Subscriptions',
@ -653,7 +648,67 @@ export default {
failedToDeposit: 'Failed to deposit', failedToDeposit: 'Failed to deposit',
failedToWithdraw: 'Failed to withdraw', failedToWithdraw: 'Failed to withdraw',
useDepositWithdrawButtons: 'Please use deposit/withdraw buttons to adjust balance', useDepositWithdrawButtons: 'Please use deposit/withdraw buttons to adjust balance',
insufficientBalance: 'Insufficient balance, balance cannot be negative after withdrawal' insufficientBalance: 'Insufficient balance, balance cannot be negative after withdrawal',
// Settings Dropdowns
filterSettings: 'Filter Settings',
columnSettings: 'Column Settings',
filterValue: 'Enter value',
// User Attributes
attributes: {
title: 'User Attributes',
description: 'Configure custom user attribute fields',
configButton: 'Attributes',
addAttribute: 'Add Attribute',
editAttribute: 'Edit Attribute',
deleteAttribute: 'Delete Attribute',
deleteConfirm: "Are you sure you want to delete attribute '{name}'? All user values for this attribute will be deleted.",
noAttributes: 'No custom attributes',
noAttributesHint: 'Click the button above to add custom attributes',
key: 'Attribute Key',
keyHint: 'For programmatic reference, only letters, numbers and underscores',
name: 'Display Name',
nameHint: 'Name shown in forms',
type: 'Attribute Type',
fieldDescription: 'Description',
fieldDescriptionHint: 'Description text for the attribute',
placeholder: 'Placeholder',
placeholderHint: 'Placeholder text for input field',
required: 'Required',
enabled: 'Enabled',
options: 'Options',
optionsHint: 'For select/multi-select types',
addOption: 'Add Option',
optionValue: 'Option Value',
optionLabel: 'Display Text',
validation: 'Validation Rules',
minLength: 'Min Length',
maxLength: 'Max Length',
min: 'Min Value',
max: 'Max Value',
pattern: 'Regex Pattern',
patternMessage: 'Validation Error Message',
types: {
text: 'Text',
textarea: 'Textarea',
number: 'Number',
email: 'Email',
url: 'URL',
date: 'Date',
select: 'Select',
multi_select: 'Multi-Select'
},
created: 'Attribute created successfully',
updated: 'Attribute updated successfully',
deleted: 'Attribute deleted successfully',
reordered: 'Attribute order updated successfully',
failedToLoad: 'Failed to load attributes',
failedToCreate: 'Failed to create attribute',
failedToUpdate: 'Failed to update attribute',
failedToDelete: 'Failed to delete attribute',
failedToReorder: 'Failed to update order',
keyExists: 'Attribute key already exists',
dragToReorder: 'Drag to reorder'
}
}, },
// Groups // Groups

View File

@ -430,9 +430,7 @@ export default {
administrator: '管理员', administrator: '管理员',
user: '用户', user: '用户',
username: '用户名', username: '用户名',
wechat: '微信号',
enterUsername: '输入用户名', enterUsername: '输入用户名',
enterWechat: '输入微信号',
editProfile: '编辑个人资料', editProfile: '编辑个人资料',
updateProfile: '更新资料', updateProfile: '更新资料',
updating: '更新中...', updating: '更新中...',
@ -583,12 +581,10 @@ export default {
email: '邮箱', email: '邮箱',
password: '密码', password: '密码',
username: '用户名', username: '用户名',
wechat: '微信号',
notes: '备注', notes: '备注',
enterEmail: '请输入邮箱', enterEmail: '请输入邮箱',
enterPassword: '请输入密码', enterPassword: '请输入密码',
enterUsername: '请输入用户名(选填)', enterUsername: '请输入用户名(选填)',
enterWechat: '请输入微信号(选填)',
enterNotes: '请输入备注(仅管理员可见)', enterNotes: '请输入备注(仅管理员可见)',
notesHint: '此备注仅对管理员可见', notesHint: '此备注仅对管理员可见',
enterNewPassword: '请输入新密码(选填)', enterNewPassword: '请输入新密码(选填)',
@ -601,7 +597,6 @@ export default {
user: '用户', user: '用户',
email: '邮箱', email: '邮箱',
username: '用户名', username: '用户名',
wechat: '微信号',
notes: '备注', notes: '备注',
role: '角色', role: '角色',
subscriptions: '订阅分组', subscriptions: '订阅分组',
@ -655,8 +650,6 @@ export default {
emailPlaceholder: '请输入邮箱', emailPlaceholder: '请输入邮箱',
usernameLabel: '用户名', usernameLabel: '用户名',
usernamePlaceholder: '请输入用户名(选填)', usernamePlaceholder: '请输入用户名(选填)',
wechatLabel: '微信号',
wechatPlaceholder: '请输入微信号(选填)',
notesLabel: '备注', notesLabel: '备注',
notesPlaceholder: '请输入备注(仅管理员可见)', notesPlaceholder: '请输入备注(仅管理员可见)',
notesHint: '此备注仅对管理员可见', notesHint: '此备注仅对管理员可见',
@ -711,7 +704,67 @@ export default {
failedToDeposit: '充值失败', failedToDeposit: '充值失败',
failedToWithdraw: '退款失败', failedToWithdraw: '退款失败',
useDepositWithdrawButtons: '请使用充值/退款按钮调整余额', useDepositWithdrawButtons: '请使用充值/退款按钮调整余额',
insufficientBalance: '余额不足,退款后余额不能为负数' insufficientBalance: '余额不足,退款后余额不能为负数',
// Settings Dropdowns
filterSettings: '筛选设置',
columnSettings: '列设置',
filterValue: '输入值',
// User Attributes
attributes: {
title: '用户属性配置',
description: '配置用户的自定义属性字段',
configButton: '属性配置',
addAttribute: '添加属性',
editAttribute: '编辑属性',
deleteAttribute: '删除属性',
deleteConfirm: "确定要删除属性 '{name}' 吗?所有用户的该属性值将被删除。",
noAttributes: '暂无自定义属性',
noAttributesHint: '点击上方按钮添加自定义属性',
key: '属性键',
keyHint: '用于程序引用,只能包含字母、数字和下划线',
name: '显示名称',
nameHint: '在表单中显示的名称',
type: '属性类型',
fieldDescription: '描述',
fieldDescriptionHint: '属性的说明文字',
placeholder: '占位符',
placeholderHint: '输入框的提示文字',
required: '必填',
enabled: '启用',
options: '选项配置',
optionsHint: '用于单选/多选类型',
addOption: '添加选项',
optionValue: '选项值',
optionLabel: '显示文本',
validation: '验证规则',
minLength: '最小长度',
maxLength: '最大长度',
min: '最小值',
max: '最大值',
pattern: '正则表达式',
patternMessage: '验证失败提示',
types: {
text: '单行文本',
textarea: '多行文本',
number: '数字',
email: '邮箱',
url: '链接',
date: '日期',
select: '单选',
multi_select: '多选'
},
created: '属性创建成功',
updated: '属性更新成功',
deleted: '属性删除成功',
reordered: '属性排序更新成功',
failedToLoad: '加载属性列表失败',
failedToCreate: '创建属性失败',
failedToUpdate: '更新属性失败',
failedToDelete: '删除属性失败',
failedToReorder: '更新排序失败',
keyExists: '属性键已存在',
dragToReorder: '拖拽排序'
}
}, },
// Groups Management // Groups Management

View File

@ -7,7 +7,6 @@
export interface User { export interface User {
id: number id: number
username: string username: string
wechat: string
notes: string notes: string
email: string email: string
role: 'admin' | 'user' // User role for authorization role: 'admin' | 'user' // User role for authorization
@ -634,7 +633,6 @@ export interface UpdateUserRequest {
email?: string email?: string
password?: string password?: string
username?: string username?: string
wechat?: string
notes?: string notes?: string
role?: 'admin' | 'user' role?: 'admin' | 'user'
balance?: number balance?: number
@ -771,3 +769,76 @@ export interface AccountUsageStatsResponse {
summary: AccountUsageSummary summary: AccountUsageSummary
models: ModelStat[] models: ModelStat[]
} }
// ==================== User Attribute Types ====================
export type UserAttributeType = 'text' | 'textarea' | 'number' | 'email' | 'url' | 'date' | 'select' | 'multi_select'
export interface UserAttributeOption {
value: string
label: string
}
export interface UserAttributeValidation {
min_length?: number
max_length?: number
min?: number
max?: number
pattern?: string
message?: string
}
export interface UserAttributeDefinition {
id: number
key: string
name: string
description: string
type: UserAttributeType
options: UserAttributeOption[]
required: boolean
validation: UserAttributeValidation
placeholder: string
display_order: number
enabled: boolean
created_at: string
updated_at: string
}
export interface UserAttributeValue {
id: number
user_id: number
attribute_id: number
value: string
created_at: string
updated_at: string
}
export interface CreateUserAttributeRequest {
key: string
name: string
description?: string
type: UserAttributeType
options?: UserAttributeOption[]
required?: boolean
validation?: UserAttributeValidation
placeholder?: string
display_order?: number
enabled?: boolean
}
export interface UpdateUserAttributeRequest {
key?: string
name?: string
description?: string
type?: UserAttributeType
options?: UserAttributeOption[]
required?: boolean
validation?: UserAttributeValidation
placeholder?: string
display_order?: number
enabled?: boolean
}
export interface UserAttributeValuesMap {
[attributeId: number]: string
}

View File

@ -1,86 +1,289 @@
<template> <template>
<AppLayout> <AppLayout>
<TablePageLayout> <TablePageLayout>
<!-- Page Header Actions --> <!-- Single Row: Search, Filters, and Actions -->
<template #actions>
<div class="flex justify-end gap-3">
<button
@click="loadUsers"
:disabled="loading"
class="btn btn-secondary"
:title="t('common.refresh')"
>
<svg
:class="['h-5 w-5', loading ? 'animate-spin' : '']"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
stroke-width="1.5"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99"
/>
</svg>
</button>
<button @click="showCreateModal = true" class="btn btn-primary">
<svg
class="mr-2 h-5 w-5"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
stroke-width="1.5"
>
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
</svg>
{{ t('admin.users.createUser') }}
</button>
</div>
</template>
<!-- Search and Filters -->
<template #filters> <template #filters>
<div class="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between"> <div class="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
<div class="relative max-w-md flex-1"> <!-- Left: Search + Active Filters -->
<svg <div class="flex flex-1 flex-wrap items-center gap-3">
class="absolute left-3 top-1/2 h-5 w-5 -translate-y-1/2 text-gray-400" <!-- Search Box -->
fill="none" <div class="relative w-64">
stroke="currentColor" <svg
viewBox="0 0 24 24" class="absolute left-3 top-1/2 h-5 w-5 -translate-y-1/2 text-gray-400"
stroke-width="1.5" fill="none"
> stroke="currentColor"
<path viewBox="0 0 24 24"
stroke-linecap="round" stroke-width="1.5"
stroke-linejoin="round" >
d="M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z" <path
/> stroke-linecap="round"
</svg> stroke-linejoin="round"
<input d="M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z"
v-model="searchQuery" />
type="text" </svg>
:placeholder="t('admin.users.searchUsers')" <input
class="input pl-10" v-model="searchQuery"
@input="handleSearch" type="text"
/> :placeholder="t('admin.users.searchUsers')"
class="input pl-10"
@input="handleSearch"
/>
</div>
<!-- Role Filter (visible when enabled) -->
<div v-if="visibleFilters.has('role')" class="relative">
<select
v-model="filters.role"
@change="applyFilter"
class="input w-32 cursor-pointer appearance-none pr-8"
>
<option value="">{{ t('admin.users.allRoles') }}</option>
<option value="admin">{{ t('admin.users.admin') }}</option>
<option value="user">{{ t('admin.users.user') }}</option>
</select>
<svg
class="pointer-events-none absolute right-2.5 top-1/2 h-4 w-4 -translate-y-1/2 text-gray-400"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
stroke-width="1.5"
>
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5" />
</svg>
</div>
<!-- Status Filter (visible when enabled) -->
<div v-if="visibleFilters.has('status')" class="relative">
<select
v-model="filters.status"
@change="applyFilter"
class="input w-32 cursor-pointer appearance-none pr-8"
>
<option value="">{{ t('admin.users.allStatus') }}</option>
<option value="active">{{ t('common.active') }}</option>
<option value="disabled">{{ t('admin.users.disabled') }}</option>
</select>
<svg
class="pointer-events-none absolute right-2.5 top-1/2 h-4 w-4 -translate-y-1/2 text-gray-400"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
stroke-width="1.5"
>
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5" />
</svg>
</div>
<!-- Dynamic Attribute Filters -->
<template v-for="(value, attrId) in activeAttributeFilters" :key="attrId">
<div v-if="visibleFilters.has(`attr_${attrId}`)" class="relative">
<!-- Text/Email/URL/Textarea/Date type: styled input -->
<input
v-if="['text', 'textarea', 'email', 'url', 'date'].includes(getAttributeDefinition(Number(attrId))?.type || 'text')"
:value="value"
@input="(e) => updateAttributeFilter(Number(attrId), (e.target as HTMLInputElement).value)"
@keyup.enter="applyFilter"
:placeholder="getAttributeDefinitionName(Number(attrId))"
class="input w-36"
/>
<!-- Number type: number input -->
<input
v-else-if="getAttributeDefinition(Number(attrId))?.type === 'number'"
:value="value"
type="number"
@input="(e) => updateAttributeFilter(Number(attrId), (e.target as HTMLInputElement).value)"
@keyup.enter="applyFilter"
:placeholder="getAttributeDefinitionName(Number(attrId))"
class="input w-32"
/>
<!-- Select/Multi-select type -->
<template v-else-if="['select', 'multi_select'].includes(getAttributeDefinition(Number(attrId))?.type || '')">
<select
:value="value"
@change="(e) => { updateAttributeFilter(Number(attrId), (e.target as HTMLSelectElement).value); applyFilter() }"
class="input w-36 cursor-pointer appearance-none pr-8"
>
<option value="">{{ getAttributeDefinitionName(Number(attrId)) }}</option>
<option
v-for="opt in getAttributeDefinition(Number(attrId))?.options || []"
:key="opt.value"
:value="opt.value"
>
{{ opt.label }}
</option>
</select>
<svg
class="pointer-events-none absolute right-2.5 top-1/2 h-4 w-4 -translate-y-1/2 text-gray-400"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
stroke-width="1.5"
>
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5" />
</svg>
</template>
<!-- Fallback -->
<input
v-else
:value="value"
@input="(e) => updateAttributeFilter(Number(attrId), (e.target as HTMLInputElement).value)"
@keyup.enter="applyFilter"
:placeholder="getAttributeDefinitionName(Number(attrId))"
class="input w-36"
/>
</div>
</template>
</div>
<!-- Right: Actions and Settings -->
<div class="flex items-center gap-3">
<!-- Refresh Button -->
<button
@click="loadUsers"
:disabled="loading"
class="btn btn-secondary"
:title="t('common.refresh')"
>
<svg
:class="['h-5 w-5', loading ? 'animate-spin' : '']"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
stroke-width="1.5"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99"
/>
</svg>
</button>
<!-- Filter Settings Dropdown -->
<div class="relative" ref="filterDropdownRef">
<button
@click="showFilterDropdown = !showFilterDropdown"
class="btn btn-secondary"
>
<svg class="mr-1.5 h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 3c2.755 0 5.455.232 8.083.678.533.09.917.556.917 1.096v1.044a2.25 2.25 0 01-.659 1.591l-5.432 5.432a2.25 2.25 0 00-.659 1.591v2.927a2.25 2.25 0 01-1.244 2.013L9.75 21v-6.568a2.25 2.25 0 00-.659-1.591L3.659 7.409A2.25 2.25 0 013 5.818V4.774c0-.54.384-1.006.917-1.096A48.32 48.32 0 0112 3z" />
</svg>
{{ t('admin.users.filterSettings') }}
</button>
<!-- Dropdown menu -->
<div
v-if="showFilterDropdown"
class="absolute right-0 top-full z-50 mt-1 w-48 rounded-lg border border-gray-200 bg-white py-1 shadow-lg dark:border-dark-600 dark:bg-dark-800"
>
<!-- Built-in filters -->
<button
v-for="filter in builtInFilters"
:key="filter.key"
@click="toggleBuiltInFilter(filter.key)"
class="flex w-full items-center justify-between px-4 py-2 text-left text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-dark-700"
>
<span>{{ filter.name }}</span>
<svg
v-if="visibleFilters.has(filter.key)"
class="h-4 w-4 text-primary-500"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
stroke-width="2"
>
<path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" />
</svg>
</button>
<!-- Divider if custom attributes exist -->
<div
v-if="filterableAttributes.length > 0"
class="my-1 border-t border-gray-100 dark:border-dark-700"
></div>
<!-- Custom attribute filters -->
<button
v-for="attr in filterableAttributes"
:key="attr.id"
@click="toggleAttributeFilter(attr)"
class="flex w-full items-center justify-between px-4 py-2 text-left text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-dark-700"
>
<span>{{ attr.name }}</span>
<svg
v-if="visibleFilters.has(`attr_${attr.id}`)"
class="h-4 w-4 text-primary-500"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
stroke-width="2"
>
<path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" />
</svg>
</button>
</div>
</div>
<!-- Column Settings Dropdown -->
<div class="relative" ref="columnDropdownRef">
<button
@click="showColumnDropdown = !showColumnDropdown"
class="btn btn-secondary"
>
<svg class="mr-1.5 h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 4.5v15m6-15v15m-10.875 0h15.75c.621 0 1.125-.504 1.125-1.125V5.625c0-.621-.504-1.125-1.125-1.125H4.125C3.504 4.5 3 5.004 3 5.625v12.75c0 .621.504 1.125 1.125 1.125z" />
</svg>
{{ t('admin.users.columnSettings') }}
</button>
<!-- Dropdown menu -->
<div
v-if="showColumnDropdown"
class="absolute right-0 top-full z-50 mt-1 max-h-80 w-48 overflow-y-auto rounded-lg border border-gray-200 bg-white py-1 shadow-lg dark:border-dark-600 dark:bg-dark-800"
>
<button
v-for="col in toggleableColumns"
:key="col.key"
@click="toggleColumn(col.key)"
class="flex w-full items-center justify-between px-4 py-2 text-left text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-dark-700"
>
<span>{{ col.label }}</span>
<svg
v-if="isColumnVisible(col.key)"
class="h-4 w-4 text-primary-500"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
stroke-width="2"
>
<path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" />
</svg>
</button>
</div>
</div>
<!-- Attributes Config Button -->
<button @click="showAttributesModal = true" class="btn btn-secondary">
<svg
class="mr-1.5 h-4 w-4"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
stroke-width="1.5"
>
<path stroke-linecap="round" stroke-linejoin="round" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 011.37.49l1.296 2.247a1.125 1.125 0 01-.26 1.431l-1.003.827c-.293.24-.438.613-.431.992a6.759 6.759 0 010 .255c-.007.378.138.75.43.99l1.005.828c.424.35.534.954.26 1.43l-1.298 2.247a1.125 1.125 0 01-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.57 6.57 0 01-.22.128c-.331.183-.581.495-.644.869l-.213 1.28c-.09.543-.56.941-1.11.941h-2.594c-.55 0-1.02-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 01-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 01-1.369-.49l-1.297-2.247a1.125 1.125 0 01.26-1.431l1.004-.827c.292-.24.437-.613.43-.992a6.932 6.932 0 010-.255c.007-.378-.138-.75-.43-.99l-1.004-.828a1.125 1.125 0 01-.26-1.43l1.297-2.247a1.125 1.125 0 011.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.087.22-.128.332-.183.582-.495.644-.869l.214-1.281z" />
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
{{ t('admin.users.attributes.configButton') }}
</button>
<!-- Create User Button -->
<button @click="showCreateModal = true" class="btn btn-primary">
<svg
class="mr-2 h-5 w-5"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
stroke-width="1.5"
>
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
</svg>
{{ t('admin.users.createUser') }}
</button>
</div>
</div> </div>
<div class="flex flex-wrap gap-3">
<Select
v-model="filters.role"
:options="roleOptions"
:placeholder="t('admin.users.allRoles')"
class="w-36"
@change="loadUsers"
/>
<Select
v-model="filters.status"
:options="statusOptions"
:placeholder="t('admin.users.allStatus')"
class="w-36"
@change="loadUsers"
/>
</div>
</div>
</template> </template>
<!-- Users Table --> <!-- Users Table -->
@ -103,10 +306,6 @@
<span class="text-sm text-gray-700 dark:text-gray-300">{{ value || '-' }}</span> <span class="text-sm text-gray-700 dark:text-gray-300">{{ value || '-' }}</span>
</template> </template>
<template #cell-wechat="{ value }">
<span class="text-sm text-gray-700 dark:text-gray-300">{{ value || '-' }}</span>
</template>
<template #cell-notes="{ value }"> <template #cell-notes="{ value }">
<div class="max-w-xs"> <div class="max-w-xs">
<span <span
@ -120,6 +319,22 @@
</div> </div>
</template> </template>
<!-- Dynamic attribute columns -->
<template
v-for="def in attributeDefinitions.filter(d => d.enabled)"
:key="def.id"
#[`cell-attr_${def.id}`]="{ row }"
>
<div class="max-w-xs">
<span
class="block truncate text-sm text-gray-700 dark:text-gray-300"
:title="getAttributeValue(row.id, def.id)"
>
{{ getAttributeValue(row.id, def.id) }}
</span>
</div>
</template>
<template #cell-role="{ value }"> <template #cell-role="{ value }">
<span :class="['badge', value === 'admin' ? 'badge-purple' : 'badge-gray']"> <span :class="['badge', value === 'admin' ? 'badge-purple' : 'badge-gray']">
{{ value }} {{ value }}
@ -189,9 +404,17 @@
</template> </template>
<template #cell-status="{ value }"> <template #cell-status="{ value }">
<span :class="['badge', value === 'active' ? 'badge-success' : 'badge-danger']"> <div class="flex items-center gap-1.5">
{{ value }} <span
</span> :class="[
'inline-block h-2 w-2 rounded-full',
value === 'active' ? 'bg-green-500' : 'bg-red-500'
]"
></span>
<span class="text-sm text-gray-700 dark:text-gray-300">
{{ value === 'active' ? t('common.active') : t('admin.users.disabled') }}
</span>
</div>
</template> </template>
<template #cell-created_at="{ value }"> <template #cell-created_at="{ value }">
@ -471,15 +694,6 @@
:placeholder="t('admin.users.enterUsername')" :placeholder="t('admin.users.enterUsername')"
/> />
</div> </div>
<div>
<label class="input-label">{{ t('admin.users.wechat') }}</label>
<input
v-model="createForm.wechat"
type="text"
class="input"
:placeholder="t('admin.users.enterWechat')"
/>
</div>
<div> <div>
<label class="input-label">{{ t('admin.users.notes') }}</label> <label class="input-label">{{ t('admin.users.notes') }}</label>
<textarea <textarea
@ -640,15 +854,6 @@
:placeholder="t('admin.users.enterUsername')" :placeholder="t('admin.users.enterUsername')"
/> />
</div> </div>
<div>
<label class="input-label">{{ t('admin.users.wechat') }}</label>
<input
v-model="editForm.wechat"
type="text"
class="input"
:placeholder="t('admin.users.enterWechat')"
/>
</div>
<div> <div>
<label class="input-label">{{ t('admin.users.notes') }}</label> <label class="input-label">{{ t('admin.users.notes') }}</label>
<textarea <textarea
@ -664,6 +869,12 @@
<input v-model.number="editForm.concurrency" type="number" class="input" /> <input v-model.number="editForm.concurrency" type="number" class="input" />
</div> </div>
<!-- Custom Attributes -->
<UserAttributeForm
v-model="editForm.customAttributes"
:user-id="editingUser?.id"
/>
</form> </form>
<template #footer> <template #footer>
@ -1179,6 +1390,12 @@
@confirm="confirmDelete" @confirm="confirmDelete"
@cancel="showDeleteDialog = false" @cancel="showDeleteDialog = false"
/> />
<!-- User Attributes Config Modal -->
<UserAttributesConfigModal
:show="showAttributesModal"
@close="handleAttributesModalClose"
/>
</AppLayout> </AppLayout>
</template> </template>
@ -1191,7 +1408,7 @@ import { formatDateTime } from '@/utils/format'
const { t } = useI18n() const { t } = useI18n()
import { adminAPI } from '@/api/admin' import { adminAPI } from '@/api/admin'
import type { User, ApiKey, Group } from '@/types' import type { User, ApiKey, Group, UserAttributeValuesMap, UserAttributeDefinition } from '@/types'
import type { BatchUserUsageStats } from '@/api/admin/dashboard' import type { BatchUserUsageStats } from '@/api/admin/dashboard'
import type { Column } from '@/components/common/types' import type { Column } from '@/components/common/types'
import AppLayout from '@/components/layout/AppLayout.vue' import AppLayout from '@/components/layout/AppLayout.vue'
@ -1201,17 +1418,66 @@ import Pagination from '@/components/common/Pagination.vue'
import BaseDialog from '@/components/common/BaseDialog.vue' import BaseDialog from '@/components/common/BaseDialog.vue'
import ConfirmDialog from '@/components/common/ConfirmDialog.vue' import ConfirmDialog from '@/components/common/ConfirmDialog.vue'
import EmptyState from '@/components/common/EmptyState.vue' import EmptyState from '@/components/common/EmptyState.vue'
import Select from '@/components/common/Select.vue'
import GroupBadge from '@/components/common/GroupBadge.vue' import GroupBadge from '@/components/common/GroupBadge.vue'
import UserAttributesConfigModal from '@/components/user/UserAttributesConfigModal.vue'
import UserAttributeForm from '@/components/user/UserAttributeForm.vue'
const appStore = useAppStore() const appStore = useAppStore()
const { copyToClipboard: clipboardCopy } = useClipboard() const { copyToClipboard: clipboardCopy } = useClipboard()
const columns = computed<Column[]>(() => [ // Generate dynamic attribute columns from enabled definitions
const attributeColumns = computed<Column[]>(() =>
attributeDefinitions.value
.filter(def => def.enabled)
.map(def => ({
key: `attr_${def.id}`,
label: def.name,
sortable: false
}))
)
// Get formatted attribute value for display in table
const getAttributeValue = (userId: number, attrId: number): string => {
const userAttrs = userAttributeValues.value[userId]
if (!userAttrs) return '-'
const value = userAttrs[attrId]
if (!value) return '-'
// Find definition for this attribute
const def = attributeDefinitions.value.find(d => d.id === attrId)
if (!def) return value
// Format based on type
if (def.type === 'multi_select' && value) {
try {
const arr = JSON.parse(value)
if (Array.isArray(arr)) {
// Map values to labels
return arr.map(v => {
const opt = def.options?.find(o => o.value === v)
return opt?.label || v
}).join(', ')
}
} catch {
return value
}
}
if (def.type === 'select' && value && def.options) {
const opt = def.options.find(o => o.value === value)
return opt?.label || value
}
return value
}
// All possible columns (for column settings)
const allColumns = computed<Column[]>(() => [
{ key: 'email', label: t('admin.users.columns.user'), sortable: true }, { key: 'email', label: t('admin.users.columns.user'), sortable: true },
{ key: 'username', label: t('admin.users.columns.username'), sortable: true }, { key: 'username', label: t('admin.users.columns.username'), sortable: true },
{ key: 'wechat', label: t('admin.users.columns.wechat'), sortable: false },
{ key: 'notes', label: t('admin.users.columns.notes'), sortable: false }, { key: 'notes', label: t('admin.users.columns.notes'), sortable: false },
// Dynamic attribute columns
...attributeColumns.value,
{ key: 'role', label: t('admin.users.columns.role'), sortable: true }, { key: 'role', label: t('admin.users.columns.role'), sortable: true },
{ key: 'subscriptions', label: t('admin.users.columns.subscriptions'), sortable: false }, { key: 'subscriptions', label: t('admin.users.columns.subscriptions'), sortable: false },
{ key: 'balance', label: t('admin.users.columns.balance'), sortable: true }, { key: 'balance', label: t('admin.users.columns.balance'), sortable: true },
@ -1222,27 +1488,154 @@ const columns = computed<Column[]>(() => [
{ key: 'actions', label: t('admin.users.columns.actions'), sortable: false } { key: 'actions', label: t('admin.users.columns.actions'), sortable: false }
]) ])
// Filter options // Columns that can be toggled (exclude email and actions which are always visible)
const roleOptions = computed(() => [ const toggleableColumns = computed(() =>
{ value: '', label: t('admin.users.allRoles') }, allColumns.value.filter(col => col.key !== 'email' && col.key !== 'actions')
{ value: 'admin', label: t('admin.users.admin') }, )
{ value: 'user', label: t('admin.users.user') }
])
const statusOptions = computed(() => [ // Hidden columns (stored in Set - columns NOT in this set are visible)
{ value: '', label: t('admin.users.allStatus') }, // This way, new columns are visible by default
{ value: 'active', label: t('common.active') }, const hiddenColumns = reactive<Set<string>>(new Set())
{ value: 'disabled', label: t('admin.users.disabled') }
]) // Default hidden columns (columns hidden by default on first load)
const DEFAULT_HIDDEN_COLUMNS = ['notes', 'subscriptions', 'usage', 'concurrency']
// localStorage key for column settings
const HIDDEN_COLUMNS_KEY = 'user-hidden-columns'
// Load saved column settings
const loadSavedColumns = () => {
try {
const saved = localStorage.getItem(HIDDEN_COLUMNS_KEY)
if (saved) {
const parsed = JSON.parse(saved) as string[]
parsed.forEach(key => hiddenColumns.add(key))
} else {
// Use default hidden columns on first load
DEFAULT_HIDDEN_COLUMNS.forEach(key => hiddenColumns.add(key))
}
} catch (e) {
console.error('Failed to load saved columns:', e)
DEFAULT_HIDDEN_COLUMNS.forEach(key => hiddenColumns.add(key))
}
}
// Save column settings to localStorage
const saveColumnsToStorage = () => {
try {
localStorage.setItem(HIDDEN_COLUMNS_KEY, JSON.stringify([...hiddenColumns]))
} catch (e) {
console.error('Failed to save columns:', e)
}
}
// Toggle column visibility
const toggleColumn = (key: string) => {
if (hiddenColumns.has(key)) {
hiddenColumns.delete(key)
} else {
hiddenColumns.add(key)
}
saveColumnsToStorage()
}
// Check if column is visible (not in hidden set)
const isColumnVisible = (key: string) => !hiddenColumns.has(key)
// Filtered columns based on visibility
const columns = computed<Column[]>(() =>
allColumns.value.filter(col =>
col.key === 'email' || col.key === 'actions' || !hiddenColumns.has(col.key)
)
)
const users = ref<User[]>([]) const users = ref<User[]>([])
const loading = ref(false) const loading = ref(false)
const searchQuery = ref('') const searchQuery = ref('')
// Filter values (role, status, and custom attributes)
const filters = reactive({ const filters = reactive({
role: '', role: '',
status: '' status: ''
}) })
const activeAttributeFilters = reactive<Record<number, string>>({})
// Visible filters tracking (which filters are shown in the UI)
// Keys: 'role', 'status', 'attr_${id}'
const visibleFilters = reactive<Set<string>>(new Set())
// Dropdown states
const showFilterDropdown = ref(false)
const showColumnDropdown = ref(false)
// Dropdown refs for click outside detection
const filterDropdownRef = ref<HTMLElement | null>(null)
const columnDropdownRef = ref<HTMLElement | null>(null)
// localStorage keys
const FILTER_VALUES_KEY = 'user-filter-values'
const VISIBLE_FILTERS_KEY = 'user-visible-filters'
// All filterable attribute definitions (enabled attributes)
const filterableAttributes = computed(() =>
attributeDefinitions.value.filter(def => def.enabled)
)
// Built-in filter definitions
const builtInFilters = computed(() => [
{ key: 'role', name: t('admin.users.columns.role'), type: 'select' as const },
{ key: 'status', name: t('admin.users.columns.status'), type: 'select' as const }
])
// Load saved filters from localStorage
const loadSavedFilters = () => {
try {
// Load visible filters
const savedVisible = localStorage.getItem(VISIBLE_FILTERS_KEY)
if (savedVisible) {
const parsed = JSON.parse(savedVisible) as string[]
parsed.forEach(key => visibleFilters.add(key))
}
// Load filter values
const savedValues = localStorage.getItem(FILTER_VALUES_KEY)
if (savedValues) {
const parsed = JSON.parse(savedValues)
if (parsed.role) filters.role = parsed.role
if (parsed.status) filters.status = parsed.status
if (parsed.attributes) {
Object.assign(activeAttributeFilters, parsed.attributes)
}
}
} catch (e) {
console.error('Failed to load saved filters:', e)
}
}
// Save filters to localStorage
const saveFiltersToStorage = () => {
try {
// Save visible filters
localStorage.setItem(VISIBLE_FILTERS_KEY, JSON.stringify([...visibleFilters]))
// Save filter values
const values = {
role: filters.role,
status: filters.status,
attributes: activeAttributeFilters
}
localStorage.setItem(FILTER_VALUES_KEY, JSON.stringify(values))
} catch (e) {
console.error('Failed to save filters:', e)
}
}
// Get attribute definition by ID
const getAttributeDefinition = (attrId: number): UserAttributeDefinition | undefined => {
return attributeDefinitions.value.find(d => d.id === attrId)
}
const usageStats = ref<Record<string, BatchUserUsageStats>>({}) const usageStats = ref<Record<string, BatchUserUsageStats>>({})
// User attribute definitions and values
const attributeDefinitions = ref<UserAttributeDefinition[]>([])
const userAttributeValues = ref<Record<number, Record<number, string>>>({})
const pagination = reactive({ const pagination = reactive({
page: 1, page: 1,
page_size: 20, page_size: 20,
@ -1254,6 +1647,7 @@ const showCreateModal = ref(false)
const showEditModal = ref(false) const showEditModal = ref(false)
const showDeleteDialog = ref(false) const showDeleteDialog = ref(false)
const showApiKeysModal = ref(false) const showApiKeysModal = ref(false)
const showAttributesModal = ref(false)
const submitting = ref(false) const submitting = ref(false)
const editingUser = ref<User | null>(null) const editingUser = ref<User | null>(null)
const deletingUser = ref<User | null>(null) const deletingUser = ref<User | null>(null)
@ -1317,6 +1711,14 @@ const handleClickOutside = (event: MouseEvent) => {
if (!target.closest('.action-menu-trigger') && !target.closest('.action-menu-content')) { if (!target.closest('.action-menu-trigger') && !target.closest('.action-menu-content')) {
closeActionMenu() closeActionMenu()
} }
// Close filter dropdown when clicking outside
if (filterDropdownRef.value && !filterDropdownRef.value.contains(target)) {
showFilterDropdown.value = false
}
// Close column dropdown when clicking outside
if (columnDropdownRef.value && !columnDropdownRef.value.contains(target)) {
showColumnDropdown.value = false
}
} }
// Allowed groups modal state // Allowed groups modal state
@ -1341,7 +1743,6 @@ const createForm = reactive({
email: '', email: '',
password: '', password: '',
username: '', username: '',
wechat: '',
notes: '', notes: '',
balance: 0, balance: 0,
concurrency: 1 concurrency: 1
@ -1351,9 +1752,9 @@ const editForm = reactive({
email: '', email: '',
password: '', password: '',
username: '', username: '',
wechat: '',
notes: '', notes: '',
concurrency: 1 concurrency: 1,
customAttributes: {} as UserAttributeValuesMap
}) })
const editPasswordCopied = ref(false) const editPasswordCopied = ref(false)
@ -1404,6 +1805,21 @@ const copyEditPassword = async () => {
} }
} }
const loadAttributeDefinitions = async () => {
try {
attributeDefinitions.value = await adminAPI.userAttributes.listEnabledDefinitions()
} catch (e) {
console.error('Failed to load attribute definitions:', e)
}
}
// Handle attributes modal close - reload definitions and users
const handleAttributesModalClose = async () => {
showAttributesModal.value = false
await loadAttributeDefinitions()
loadUsers()
}
const loadUsers = async () => { const loadUsers = async () => {
abortController?.abort() abortController?.abort()
const currentAbortController = new AbortController() const currentAbortController = new AbortController()
@ -1411,13 +1827,22 @@ const loadUsers = async () => {
const { signal } = currentAbortController const { signal } = currentAbortController
loading.value = true loading.value = true
try { try {
// Build attribute filters from active filters
const attrFilters: Record<number, string> = {}
for (const [attrId, value] of Object.entries(activeAttributeFilters)) {
if (value) {
attrFilters[Number(attrId)] = value
}
}
const response = await adminAPI.users.list( const response = await adminAPI.users.list(
pagination.page, pagination.page,
pagination.page_size, pagination.page_size,
{ {
role: filters.role as any, role: filters.role as any,
status: filters.status as any, status: filters.status as any,
search: searchQuery.value || undefined search: searchQuery.value || undefined,
attributes: Object.keys(attrFilters).length > 0 ? attrFilters : undefined
}, },
{ signal } { signal }
) )
@ -1428,9 +1853,10 @@ const loadUsers = async () => {
pagination.total = response.total pagination.total = response.total
pagination.pages = response.pages pagination.pages = response.pages
// Load usage stats for all users in the list // Load usage stats and attribute values for all users in the list
if (response.items.length > 0) { if (response.items.length > 0) {
const userIds = response.items.map((u) => u.id) const userIds = response.items.map((u) => u.id)
// Load usage stats
try { try {
const usageResponse = await adminAPI.dashboard.getBatchUsersUsage(userIds) const usageResponse = await adminAPI.dashboard.getBatchUsersUsage(userIds)
if (signal.aborted) { if (signal.aborted) {
@ -1443,6 +1869,21 @@ const loadUsers = async () => {
} }
console.error('Failed to load usage stats:', e) console.error('Failed to load usage stats:', e)
} }
// Load attribute values
if (attributeDefinitions.value.length > 0) {
try {
const attrResponse = await adminAPI.userAttributes.getBatchUserAttributes(userIds)
if (signal.aborted) {
return
}
userAttributeValues.value = attrResponse.attributes
} catch (e) {
if (signal.aborted) {
return
}
console.error('Failed to load user attribute values:', e)
}
}
} }
} catch (error) { } catch (error) {
const errorInfo = error as { name?: string; code?: string } const errorInfo = error as { name?: string; code?: string }
@ -1478,12 +1919,54 @@ const handlePageSizeChange = (pageSize: number) => {
loadUsers() loadUsers()
} }
// Filter helpers
const getAttributeDefinitionName = (attrId: number): string => {
const def = attributeDefinitions.value.find(d => d.id === attrId)
return def?.name || String(attrId)
}
// Toggle a built-in filter (role/status)
const toggleBuiltInFilter = (key: string) => {
if (visibleFilters.has(key)) {
visibleFilters.delete(key)
if (key === 'role') filters.role = ''
if (key === 'status') filters.status = ''
} else {
visibleFilters.add(key)
}
saveFiltersToStorage()
loadUsers()
}
// Toggle a custom attribute filter
const toggleAttributeFilter = (attr: UserAttributeDefinition) => {
const key = `attr_${attr.id}`
if (visibleFilters.has(key)) {
visibleFilters.delete(key)
delete activeAttributeFilters[attr.id]
} else {
visibleFilters.add(key)
activeAttributeFilters[attr.id] = ''
}
saveFiltersToStorage()
loadUsers()
}
const updateAttributeFilter = (attrId: number, value: string) => {
activeAttributeFilters[attrId] = value
}
// Apply filter and save to localStorage
const applyFilter = () => {
saveFiltersToStorage()
loadUsers()
}
const closeCreateModal = () => { const closeCreateModal = () => {
showCreateModal.value = false showCreateModal.value = false
createForm.email = '' createForm.email = ''
createForm.password = '' createForm.password = ''
createForm.username = '' createForm.username = ''
createForm.wechat = ''
createForm.notes = '' createForm.notes = ''
createForm.balance = 0 createForm.balance = 0
createForm.concurrency = 1 createForm.concurrency = 1
@ -1514,9 +1997,9 @@ const handleEdit = (user: User) => {
editForm.email = user.email editForm.email = user.email
editForm.password = '' editForm.password = ''
editForm.username = user.username || '' editForm.username = user.username || ''
editForm.wechat = user.wechat || ''
editForm.notes = user.notes || '' editForm.notes = user.notes || ''
editForm.concurrency = user.concurrency editForm.concurrency = user.concurrency
editForm.customAttributes = {}
editPasswordCopied.value = false editPasswordCopied.value = false
showEditModal.value = true showEditModal.value = true
} }
@ -1525,6 +2008,7 @@ const closeEditModal = () => {
showEditModal.value = false showEditModal.value = false
editingUser.value = null editingUser.value = null
editForm.password = '' editForm.password = ''
editForm.customAttributes = {}
editPasswordCopied.value = false editPasswordCopied.value = false
} }
@ -1536,7 +2020,6 @@ const handleUpdateUser = async () => {
const updateData: Record<string, any> = { const updateData: Record<string, any> = {
email: editForm.email, email: editForm.email,
username: editForm.username, username: editForm.username,
wechat: editForm.wechat,
notes: editForm.notes, notes: editForm.notes,
concurrency: editForm.concurrency concurrency: editForm.concurrency
} }
@ -1545,6 +2028,15 @@ const handleUpdateUser = async () => {
} }
await adminAPI.users.update(editingUser.value.id, updateData) await adminAPI.users.update(editingUser.value.id, updateData)
// Save custom attributes if any
if (Object.keys(editForm.customAttributes).length > 0) {
await adminAPI.userAttributes.updateUserAttributeValues(
editingUser.value.id,
editForm.customAttributes
)
}
appStore.showSuccess(t('admin.users.userUpdated')) appStore.showSuccess(t('admin.users.userUpdated'))
closeEditModal() closeEditModal()
loadUsers() loadUsers()
@ -1730,7 +2222,10 @@ const handleBalanceSubmit = async () => {
} }
} }
onMounted(() => { onMounted(async () => {
await loadAttributeDefinitions()
loadSavedFilters()
loadSavedColumns()
loadUsers() loadUsers()
document.addEventListener('click', handleClickOutside) document.addEventListener('click', handleClickOutside)
}) })

View File

@ -89,25 +89,6 @@
</svg> </svg>
<span class="truncate">{{ user.username }}</span> <span class="truncate">{{ user.username }}</span>
</div> </div>
<div
v-if="user?.wechat"
class="flex items-center gap-3 text-sm text-gray-600 dark:text-gray-400"
>
<svg
class="h-4 w-4 text-gray-400 dark:text-gray-500"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
stroke-width="1.5"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M8.625 12a.375.375 0 11-.75 0 .375.375 0 01.75 0zm0 0H8.25m4.125 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zm0 0H12m4.125 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zm0 0h-.375M21 12c0 4.556-4.03 8.25-9 8.25a9.764 9.764 0 01-2.555-.337A5.972 5.972 0 015.41 20.97a5.969 5.969 0 01-.474-.065 4.48 4.48 0 00.978-2.025c.09-.457-.133-.901-.467-1.226C3.93 16.178 3 14.189 3 12c0-4.556 4.03-8.25 9-8.25s9 3.694 9 8.25z"
/>
</svg>
<span class="truncate">{{ user.wechat }}</span>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -170,19 +151,6 @@
/> />
</div> </div>
<div>
<label for="wechat" class="input-label">
{{ t('profile.wechat') }}
</label>
<input
id="wechat"
v-model="profileForm.wechat"
type="text"
class="input"
:placeholder="t('profile.enterWechat')"
/>
</div>
<div class="flex justify-end pt-4"> <div class="flex justify-end pt-4">
<button type="submit" :disabled="updatingProfile" class="btn btn-primary"> <button type="submit" :disabled="updatingProfile" class="btn btn-primary">
{{ updatingProfile ? t('profile.updating') : t('profile.updateProfile') }} {{ updatingProfile ? t('profile.updating') : t('profile.updateProfile') }}
@ -338,8 +306,7 @@ const passwordForm = ref({
}) })
const profileForm = ref({ const profileForm = ref({
username: '', username: ''
wechat: ''
}) })
const changingPassword = ref(false) const changingPassword = ref(false)
@ -354,7 +321,6 @@ onMounted(async () => {
// Initialize profile form with current user data // Initialize profile form with current user data
if (user.value) { if (user.value) {
profileForm.value.username = user.value.username || '' profileForm.value.username = user.value.username || ''
profileForm.value.wechat = user.value.wechat || ''
} }
} catch (error) { } catch (error) {
console.error('Failed to load contact info:', error) console.error('Failed to load contact info:', error)
@ -407,8 +373,7 @@ const handleUpdateProfile = async () => {
updatingProfile.value = true updatingProfile.value = true
try { try {
const updatedUser = await userAPI.updateProfile({ const updatedUser = await userAPI.updateProfile({
username: profileForm.value.username, username: profileForm.value.username
wechat: profileForm.value.wechat
}) })
// Update auth store with new user data // Update auth store with new user data