Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
陈曦
sub2api
Commits
d3062b2e
Unverified
Commit
d3062b2e
authored
Feb 02, 2026
by
Wesley Liddick
Committed by
GitHub
Feb 02, 2026
Browse files
Merge pull request #434 from DuckyProject/feat/announcement-system-pr-upstream
feat(announcements): admin/user announcement system
parents
b7777fb4
9bee0a20
Changes
70
Show whitespace changes
Inline
Side-by-side
backend/ent/mutation.go
View file @
d3062b2e
...
...
@@ -14,6 +14,8 @@ import (
"entgo.io/ent/dialect/sql"
"github.com/Wei-Shaw/sub2api/ent/account"
"github.com/Wei-Shaw/sub2api/ent/accountgroup"
"github.com/Wei-Shaw/sub2api/ent/announcement"
"github.com/Wei-Shaw/sub2api/ent/announcementread"
"github.com/Wei-Shaw/sub2api/ent/apikey"
"github.com/Wei-Shaw/sub2api/ent/group"
"github.com/Wei-Shaw/sub2api/ent/predicate"
...
...
@@ -29,6 +31,7 @@ import (
"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/internal/domain"
)
const (
...
...
@@ -43,6 +46,8 @@ const (
TypeAPIKey = "APIKey"
TypeAccount = "Account"
TypeAccountGroup = "AccountGroup"
TypeAnnouncement = "Announcement"
TypeAnnouncementRead = "AnnouncementRead"
TypeGroup = "Group"
TypePromoCode = "PromoCode"
TypePromoCodeUsage = "PromoCodeUsage"
...
...
@@ -3833,6 +3838,1671 @@ func (m *AccountGroupMutation) ResetEdge(name string) error {
return fmt.Errorf("unknown AccountGroup edge %s", name)
}
// AnnouncementMutation represents an operation that mutates the Announcement nodes in the graph.
type AnnouncementMutation struct {
config
op Op
typ string
id *int64
title *string
content *string
status *string
targeting *domain.AnnouncementTargeting
starts_at *time.Time
ends_at *time.Time
created_by *int64
addcreated_by *int64
updated_by *int64
addupdated_by *int64
created_at *time.Time
updated_at *time.Time
clearedFields map[string]struct{}
reads map[int64]struct{}
removedreads map[int64]struct{}
clearedreads bool
done bool
oldValue func(context.Context) (*Announcement, error)
predicates []predicate.Announcement
}
var _ ent.Mutation = (*AnnouncementMutation)(nil)
// announcementOption allows management of the mutation configuration using functional options.
type announcementOption func(*AnnouncementMutation)
// newAnnouncementMutation creates new mutation for the Announcement entity.
func newAnnouncementMutation(c config, op Op, opts ...announcementOption) *AnnouncementMutation {
m := &AnnouncementMutation{
config: c,
op: op,
typ: TypeAnnouncement,
clearedFields: make(map[string]struct{}),
}
for _, opt := range opts {
opt(m)
}
return m
}
// withAnnouncementID sets the ID field of the mutation.
func withAnnouncementID(id int64) announcementOption {
return func(m *AnnouncementMutation) {
var (
err error
once sync.Once
value *Announcement
)
m.oldValue = func(ctx context.Context) (*Announcement, error) {
once.Do(func() {
if m.done {
err = errors.New("querying old values post mutation is not allowed")
} else {
value, err = m.Client().Announcement.Get(ctx, id)
}
})
return value, err
}
m.id = &id
}
}
// withAnnouncement sets the old Announcement of the mutation.
func withAnnouncement(node *Announcement) announcementOption {
return func(m *AnnouncementMutation) {
m.oldValue = func(context.Context) (*Announcement, error) {
return node, nil
}
m.id = &node.ID
}
}
// Client returns a new `ent.Client` from the mutation. If the mutation was
// executed in a transaction (ent.Tx), a transactional client is returned.
func (m AnnouncementMutation) Client() *Client {
client := &Client{config: m.config}
client.init()
return client
}
// Tx returns an `ent.Tx` for mutations that were executed in transactions;
// it returns an error otherwise.
func (m AnnouncementMutation) Tx() (*Tx, error) {
if _, ok := m.driver.(*txDriver); !ok {
return nil, errors.New("ent: mutation is not running in a transaction")
}
tx := &Tx{config: m.config}
tx.init()
return tx, nil
}
// ID returns the ID value in the mutation. Note that the ID is only available
// if it was provided to the builder or after it was returned from the database.
func (m *AnnouncementMutation) ID() (id int64, exists bool) {
if m.id == nil {
return
}
return *m.id, true
}
// IDs queries the database and returns the entity ids that match the mutation's predicate.
// That means, if the mutation is applied within a transaction with an isolation level such
// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated
// or updated by the mutation.
func (m *AnnouncementMutation) IDs(ctx context.Context) ([]int64, error) {
switch {
case m.op.Is(OpUpdateOne | OpDeleteOne):
id, exists := m.ID()
if exists {
return []int64{id}, nil
}
fallthrough
case m.op.Is(OpUpdate | OpDelete):
return m.Client().Announcement.Query().Where(m.predicates...).IDs(ctx)
default:
return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op)
}
}
// SetTitle sets the "title" field.
func (m *AnnouncementMutation) SetTitle(s string) {
m.title = &s
}
// Title returns the value of the "title" field in the mutation.
func (m *AnnouncementMutation) Title() (r string, exists bool) {
v := m.title
if v == nil {
return
}
return *v, true
}
// OldTitle returns the old "title" field's value of the Announcement entity.
// If the Announcement object wasn't provided to the builder, the object is fetched from the database.
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
func (m *AnnouncementMutation) OldTitle(ctx context.Context) (v string, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldTitle is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldTitle requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldTitle: %w", err)
}
return oldValue.Title, nil
}
// ResetTitle resets all changes to the "title" field.
func (m *AnnouncementMutation) ResetTitle() {
m.title = nil
}
// SetContent sets the "content" field.
func (m *AnnouncementMutation) SetContent(s string) {
m.content = &s
}
// Content returns the value of the "content" field in the mutation.
func (m *AnnouncementMutation) Content() (r string, exists bool) {
v := m.content
if v == nil {
return
}
return *v, true
}
// OldContent returns the old "content" field's value of the Announcement entity.
// If the Announcement object wasn't provided to the builder, the object is fetched from the database.
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
func (m *AnnouncementMutation) OldContent(ctx context.Context) (v string, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldContent is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldContent requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldContent: %w", err)
}
return oldValue.Content, nil
}
// ResetContent resets all changes to the "content" field.
func (m *AnnouncementMutation) ResetContent() {
m.content = nil
}
// SetStatus sets the "status" field.
func (m *AnnouncementMutation) SetStatus(s string) {
m.status = &s
}
// Status returns the value of the "status" field in the mutation.
func (m *AnnouncementMutation) Status() (r string, exists bool) {
v := m.status
if v == nil {
return
}
return *v, true
}
// OldStatus returns the old "status" field's value of the Announcement entity.
// If the Announcement object wasn't provided to the builder, the object is fetched from the database.
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
func (m *AnnouncementMutation) OldStatus(ctx context.Context) (v string, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldStatus is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldStatus requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldStatus: %w", err)
}
return oldValue.Status, nil
}
// ResetStatus resets all changes to the "status" field.
func (m *AnnouncementMutation) ResetStatus() {
m.status = nil
}
// SetTargeting sets the "targeting" field.
func (m *AnnouncementMutation) SetTargeting(dt domain.AnnouncementTargeting) {
m.targeting = &dt
}
// Targeting returns the value of the "targeting" field in the mutation.
func (m *AnnouncementMutation) Targeting() (r domain.AnnouncementTargeting, exists bool) {
v := m.targeting
if v == nil {
return
}
return *v, true
}
// OldTargeting returns the old "targeting" field's value of the Announcement entity.
// If the Announcement object wasn't provided to the builder, the object is fetched from the database.
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
func (m *AnnouncementMutation) OldTargeting(ctx context.Context) (v domain.AnnouncementTargeting, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldTargeting is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldTargeting requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldTargeting: %w", err)
}
return oldValue.Targeting, nil
}
// ClearTargeting clears the value of the "targeting" field.
func (m *AnnouncementMutation) ClearTargeting() {
m.targeting = nil
m.clearedFields[announcement.FieldTargeting] = struct{}{}
}
// TargetingCleared returns if the "targeting" field was cleared in this mutation.
func (m *AnnouncementMutation) TargetingCleared() bool {
_, ok := m.clearedFields[announcement.FieldTargeting]
return ok
}
// ResetTargeting resets all changes to the "targeting" field.
func (m *AnnouncementMutation) ResetTargeting() {
m.targeting = nil
delete(m.clearedFields, announcement.FieldTargeting)
}
// SetStartsAt sets the "starts_at" field.
func (m *AnnouncementMutation) SetStartsAt(t time.Time) {
m.starts_at = &t
}
// StartsAt returns the value of the "starts_at" field in the mutation.
func (m *AnnouncementMutation) StartsAt() (r time.Time, exists bool) {
v := m.starts_at
if v == nil {
return
}
return *v, true
}
// OldStartsAt returns the old "starts_at" field's value of the Announcement entity.
// If the Announcement object wasn't provided to the builder, the object is fetched from the database.
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
func (m *AnnouncementMutation) OldStartsAt(ctx context.Context) (v *time.Time, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldStartsAt is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldStartsAt requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldStartsAt: %w", err)
}
return oldValue.StartsAt, nil
}
// ClearStartsAt clears the value of the "starts_at" field.
func (m *AnnouncementMutation) ClearStartsAt() {
m.starts_at = nil
m.clearedFields[announcement.FieldStartsAt] = struct{}{}
}
// StartsAtCleared returns if the "starts_at" field was cleared in this mutation.
func (m *AnnouncementMutation) StartsAtCleared() bool {
_, ok := m.clearedFields[announcement.FieldStartsAt]
return ok
}
// ResetStartsAt resets all changes to the "starts_at" field.
func (m *AnnouncementMutation) ResetStartsAt() {
m.starts_at = nil
delete(m.clearedFields, announcement.FieldStartsAt)
}
// SetEndsAt sets the "ends_at" field.
func (m *AnnouncementMutation) SetEndsAt(t time.Time) {
m.ends_at = &t
}
// EndsAt returns the value of the "ends_at" field in the mutation.
func (m *AnnouncementMutation) EndsAt() (r time.Time, exists bool) {
v := m.ends_at
if v == nil {
return
}
return *v, true
}
// OldEndsAt returns the old "ends_at" field's value of the Announcement entity.
// If the Announcement object wasn't provided to the builder, the object is fetched from the database.
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
func (m *AnnouncementMutation) OldEndsAt(ctx context.Context) (v *time.Time, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldEndsAt is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldEndsAt requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldEndsAt: %w", err)
}
return oldValue.EndsAt, nil
}
// ClearEndsAt clears the value of the "ends_at" field.
func (m *AnnouncementMutation) ClearEndsAt() {
m.ends_at = nil
m.clearedFields[announcement.FieldEndsAt] = struct{}{}
}
// EndsAtCleared returns if the "ends_at" field was cleared in this mutation.
func (m *AnnouncementMutation) EndsAtCleared() bool {
_, ok := m.clearedFields[announcement.FieldEndsAt]
return ok
}
// ResetEndsAt resets all changes to the "ends_at" field.
func (m *AnnouncementMutation) ResetEndsAt() {
m.ends_at = nil
delete(m.clearedFields, announcement.FieldEndsAt)
}
// SetCreatedBy sets the "created_by" field.
func (m *AnnouncementMutation) SetCreatedBy(i int64) {
m.created_by = &i
m.addcreated_by = nil
}
// CreatedBy returns the value of the "created_by" field in the mutation.
func (m *AnnouncementMutation) CreatedBy() (r int64, exists bool) {
v := m.created_by
if v == nil {
return
}
return *v, true
}
// OldCreatedBy returns the old "created_by" field's value of the Announcement entity.
// If the Announcement object wasn't provided to the builder, the object is fetched from the database.
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
func (m *AnnouncementMutation) OldCreatedBy(ctx context.Context) (v *int64, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldCreatedBy is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldCreatedBy requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldCreatedBy: %w", err)
}
return oldValue.CreatedBy, nil
}
// AddCreatedBy adds i to the "created_by" field.
func (m *AnnouncementMutation) AddCreatedBy(i int64) {
if m.addcreated_by != nil {
*m.addcreated_by += i
} else {
m.addcreated_by = &i
}
}
// AddedCreatedBy returns the value that was added to the "created_by" field in this mutation.
func (m *AnnouncementMutation) AddedCreatedBy() (r int64, exists bool) {
v := m.addcreated_by
if v == nil {
return
}
return *v, true
}
// ClearCreatedBy clears the value of the "created_by" field.
func (m *AnnouncementMutation) ClearCreatedBy() {
m.created_by = nil
m.addcreated_by = nil
m.clearedFields[announcement.FieldCreatedBy] = struct{}{}
}
// CreatedByCleared returns if the "created_by" field was cleared in this mutation.
func (m *AnnouncementMutation) CreatedByCleared() bool {
_, ok := m.clearedFields[announcement.FieldCreatedBy]
return ok
}
// ResetCreatedBy resets all changes to the "created_by" field.
func (m *AnnouncementMutation) ResetCreatedBy() {
m.created_by = nil
m.addcreated_by = nil
delete(m.clearedFields, announcement.FieldCreatedBy)
}
// SetUpdatedBy sets the "updated_by" field.
func (m *AnnouncementMutation) SetUpdatedBy(i int64) {
m.updated_by = &i
m.addupdated_by = nil
}
// UpdatedBy returns the value of the "updated_by" field in the mutation.
func (m *AnnouncementMutation) UpdatedBy() (r int64, exists bool) {
v := m.updated_by
if v == nil {
return
}
return *v, true
}
// OldUpdatedBy returns the old "updated_by" field's value of the Announcement entity.
// If the Announcement object wasn't provided to the builder, the object is fetched from the database.
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
func (m *AnnouncementMutation) OldUpdatedBy(ctx context.Context) (v *int64, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldUpdatedBy is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldUpdatedBy requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldUpdatedBy: %w", err)
}
return oldValue.UpdatedBy, nil
}
// AddUpdatedBy adds i to the "updated_by" field.
func (m *AnnouncementMutation) AddUpdatedBy(i int64) {
if m.addupdated_by != nil {
*m.addupdated_by += i
} else {
m.addupdated_by = &i
}
}
// AddedUpdatedBy returns the value that was added to the "updated_by" field in this mutation.
func (m *AnnouncementMutation) AddedUpdatedBy() (r int64, exists bool) {
v := m.addupdated_by
if v == nil {
return
}
return *v, true
}
// ClearUpdatedBy clears the value of the "updated_by" field.
func (m *AnnouncementMutation) ClearUpdatedBy() {
m.updated_by = nil
m.addupdated_by = nil
m.clearedFields[announcement.FieldUpdatedBy] = struct{}{}
}
// UpdatedByCleared returns if the "updated_by" field was cleared in this mutation.
func (m *AnnouncementMutation) UpdatedByCleared() bool {
_, ok := m.clearedFields[announcement.FieldUpdatedBy]
return ok
}
// ResetUpdatedBy resets all changes to the "updated_by" field.
func (m *AnnouncementMutation) ResetUpdatedBy() {
m.updated_by = nil
m.addupdated_by = nil
delete(m.clearedFields, announcement.FieldUpdatedBy)
}
// SetCreatedAt sets the "created_at" field.
func (m *AnnouncementMutation) SetCreatedAt(t time.Time) {
m.created_at = &t
}
// CreatedAt returns the value of the "created_at" field in the mutation.
func (m *AnnouncementMutation) CreatedAt() (r time.Time, exists bool) {
v := m.created_at
if v == nil {
return
}
return *v, true
}
// OldCreatedAt returns the old "created_at" field's value of the Announcement entity.
// If the Announcement object wasn't provided to the builder, the object is fetched from the database.
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
func (m *AnnouncementMutation) OldCreatedAt(ctx context.Context) (v time.Time, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldCreatedAt is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldCreatedAt requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldCreatedAt: %w", err)
}
return oldValue.CreatedAt, nil
}
// ResetCreatedAt resets all changes to the "created_at" field.
func (m *AnnouncementMutation) ResetCreatedAt() {
m.created_at = nil
}
// SetUpdatedAt sets the "updated_at" field.
func (m *AnnouncementMutation) SetUpdatedAt(t time.Time) {
m.updated_at = &t
}
// UpdatedAt returns the value of the "updated_at" field in the mutation.
func (m *AnnouncementMutation) UpdatedAt() (r time.Time, exists bool) {
v := m.updated_at
if v == nil {
return
}
return *v, true
}
// OldUpdatedAt returns the old "updated_at" field's value of the Announcement entity.
// If the Announcement object wasn't provided to the builder, the object is fetched from the database.
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
func (m *AnnouncementMutation) OldUpdatedAt(ctx context.Context) (v time.Time, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldUpdatedAt is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldUpdatedAt requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldUpdatedAt: %w", err)
}
return oldValue.UpdatedAt, nil
}
// ResetUpdatedAt resets all changes to the "updated_at" field.
func (m *AnnouncementMutation) ResetUpdatedAt() {
m.updated_at = nil
}
// AddReadIDs adds the "reads" edge to the AnnouncementRead entity by ids.
func (m *AnnouncementMutation) AddReadIDs(ids ...int64) {
if m.reads == nil {
m.reads = make(map[int64]struct{})
}
for i := range ids {
m.reads[ids[i]] = struct{}{}
}
}
// ClearReads clears the "reads" edge to the AnnouncementRead entity.
func (m *AnnouncementMutation) ClearReads() {
m.clearedreads = true
}
// ReadsCleared reports if the "reads" edge to the AnnouncementRead entity was cleared.
func (m *AnnouncementMutation) ReadsCleared() bool {
return m.clearedreads
}
// RemoveReadIDs removes the "reads" edge to the AnnouncementRead entity by IDs.
func (m *AnnouncementMutation) RemoveReadIDs(ids ...int64) {
if m.removedreads == nil {
m.removedreads = make(map[int64]struct{})
}
for i := range ids {
delete(m.reads, ids[i])
m.removedreads[ids[i]] = struct{}{}
}
}
// RemovedReads returns the removed IDs of the "reads" edge to the AnnouncementRead entity.
func (m *AnnouncementMutation) RemovedReadsIDs() (ids []int64) {
for id := range m.removedreads {
ids = append(ids, id)
}
return
}
// ReadsIDs returns the "reads" edge IDs in the mutation.
func (m *AnnouncementMutation) ReadsIDs() (ids []int64) {
for id := range m.reads {
ids = append(ids, id)
}
return
}
// ResetReads resets all changes to the "reads" edge.
func (m *AnnouncementMutation) ResetReads() {
m.reads = nil
m.clearedreads = false
m.removedreads = nil
}
// Where appends a list predicates to the AnnouncementMutation builder.
func (m *AnnouncementMutation) Where(ps ...predicate.Announcement) {
m.predicates = append(m.predicates, ps...)
}
// WhereP appends storage-level predicates to the AnnouncementMutation builder. Using this method,
// users can use type-assertion to append predicates that do not depend on any generated package.
func (m *AnnouncementMutation) WhereP(ps ...func(*sql.Selector)) {
p := make([]predicate.Announcement, len(ps))
for i := range ps {
p[i] = ps[i]
}
m.Where(p...)
}
// Op returns the operation name.
func (m *AnnouncementMutation) Op() Op {
return m.op
}
// SetOp allows setting the mutation operation.
func (m *AnnouncementMutation) SetOp(op Op) {
m.op = op
}
// Type returns the node type of this mutation (Announcement).
func (m *AnnouncementMutation) Type() string {
return m.typ
}
// Fields returns all fields that were changed during this mutation. Note that in
// order to get all numeric fields that were incremented/decremented, call
// AddedFields().
func (m *AnnouncementMutation) Fields() []string {
fields := make([]string, 0, 10)
if m.title != nil {
fields = append(fields, announcement.FieldTitle)
}
if m.content != nil {
fields = append(fields, announcement.FieldContent)
}
if m.status != nil {
fields = append(fields, announcement.FieldStatus)
}
if m.targeting != nil {
fields = append(fields, announcement.FieldTargeting)
}
if m.starts_at != nil {
fields = append(fields, announcement.FieldStartsAt)
}
if m.ends_at != nil {
fields = append(fields, announcement.FieldEndsAt)
}
if m.created_by != nil {
fields = append(fields, announcement.FieldCreatedBy)
}
if m.updated_by != nil {
fields = append(fields, announcement.FieldUpdatedBy)
}
if m.created_at != nil {
fields = append(fields, announcement.FieldCreatedAt)
}
if m.updated_at != nil {
fields = append(fields, announcement.FieldUpdatedAt)
}
return fields
}
// Field returns the value of a field with the given name. The second boolean
// return value indicates that this field was not set, or was not defined in the
// schema.
func (m *AnnouncementMutation) Field(name string) (ent.Value, bool) {
switch name {
case announcement.FieldTitle:
return m.Title()
case announcement.FieldContent:
return m.Content()
case announcement.FieldStatus:
return m.Status()
case announcement.FieldTargeting:
return m.Targeting()
case announcement.FieldStartsAt:
return m.StartsAt()
case announcement.FieldEndsAt:
return m.EndsAt()
case announcement.FieldCreatedBy:
return m.CreatedBy()
case announcement.FieldUpdatedBy:
return m.UpdatedBy()
case announcement.FieldCreatedAt:
return m.CreatedAt()
case announcement.FieldUpdatedAt:
return m.UpdatedAt()
}
return nil, false
}
// OldField returns the old value of the field from the database. An error is
// returned if the mutation operation is not UpdateOne, or the query to the
// database failed.
func (m *AnnouncementMutation) OldField(ctx context.Context, name string) (ent.Value, error) {
switch name {
case announcement.FieldTitle:
return m.OldTitle(ctx)
case announcement.FieldContent:
return m.OldContent(ctx)
case announcement.FieldStatus:
return m.OldStatus(ctx)
case announcement.FieldTargeting:
return m.OldTargeting(ctx)
case announcement.FieldStartsAt:
return m.OldStartsAt(ctx)
case announcement.FieldEndsAt:
return m.OldEndsAt(ctx)
case announcement.FieldCreatedBy:
return m.OldCreatedBy(ctx)
case announcement.FieldUpdatedBy:
return m.OldUpdatedBy(ctx)
case announcement.FieldCreatedAt:
return m.OldCreatedAt(ctx)
case announcement.FieldUpdatedAt:
return m.OldUpdatedAt(ctx)
}
return nil, fmt.Errorf("unknown Announcement field %s", name)
}
// SetField sets the value of a field with the given name. It returns an error if
// the field is not defined in the schema, or if the type mismatched the field
// type.
func (m *AnnouncementMutation) SetField(name string, value ent.Value) error {
switch name {
case announcement.FieldTitle:
v, ok := value.(string)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetTitle(v)
return nil
case announcement.FieldContent:
v, ok := value.(string)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetContent(v)
return nil
case announcement.FieldStatus:
v, ok := value.(string)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetStatus(v)
return nil
case announcement.FieldTargeting:
v, ok := value.(domain.AnnouncementTargeting)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetTargeting(v)
return nil
case announcement.FieldStartsAt:
v, ok := value.(time.Time)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetStartsAt(v)
return nil
case announcement.FieldEndsAt:
v, ok := value.(time.Time)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetEndsAt(v)
return nil
case announcement.FieldCreatedBy:
v, ok := value.(int64)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetCreatedBy(v)
return nil
case announcement.FieldUpdatedBy:
v, ok := value.(int64)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetUpdatedBy(v)
return nil
case announcement.FieldCreatedAt:
v, ok := value.(time.Time)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetCreatedAt(v)
return nil
case announcement.FieldUpdatedAt:
v, ok := value.(time.Time)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetUpdatedAt(v)
return nil
}
return fmt.Errorf("unknown Announcement field %s", name)
}
// AddedFields returns all numeric fields that were incremented/decremented during
// this mutation.
func (m *AnnouncementMutation) AddedFields() []string {
var fields []string
if m.addcreated_by != nil {
fields = append(fields, announcement.FieldCreatedBy)
}
if m.addupdated_by != nil {
fields = append(fields, announcement.FieldUpdatedBy)
}
return fields
}
// AddedField returns the numeric value that was incremented/decremented on a field
// with the given name. The second boolean return value indicates that this field
// was not set, or was not defined in the schema.
func (m *AnnouncementMutation) AddedField(name string) (ent.Value, bool) {
switch name {
case announcement.FieldCreatedBy:
return m.AddedCreatedBy()
case announcement.FieldUpdatedBy:
return m.AddedUpdatedBy()
}
return nil, false
}
// AddField adds the value to the field with the given name. It returns an error if
// the field is not defined in the schema, or if the type mismatched the field
// type.
func (m *AnnouncementMutation) AddField(name string, value ent.Value) error {
switch name {
case announcement.FieldCreatedBy:
v, ok := value.(int64)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.AddCreatedBy(v)
return nil
case announcement.FieldUpdatedBy:
v, ok := value.(int64)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.AddUpdatedBy(v)
return nil
}
return fmt.Errorf("unknown Announcement numeric field %s", name)
}
// ClearedFields returns all nullable fields that were cleared during this
// mutation.
func (m *AnnouncementMutation) ClearedFields() []string {
var fields []string
if m.FieldCleared(announcement.FieldTargeting) {
fields = append(fields, announcement.FieldTargeting)
}
if m.FieldCleared(announcement.FieldStartsAt) {
fields = append(fields, announcement.FieldStartsAt)
}
if m.FieldCleared(announcement.FieldEndsAt) {
fields = append(fields, announcement.FieldEndsAt)
}
if m.FieldCleared(announcement.FieldCreatedBy) {
fields = append(fields, announcement.FieldCreatedBy)
}
if m.FieldCleared(announcement.FieldUpdatedBy) {
fields = append(fields, announcement.FieldUpdatedBy)
}
return fields
}
// FieldCleared returns a boolean indicating if a field with the given name was
// cleared in this mutation.
func (m *AnnouncementMutation) FieldCleared(name string) bool {
_, ok := m.clearedFields[name]
return ok
}
// ClearField clears the value of the field with the given name. It returns an
// error if the field is not defined in the schema.
func (m *AnnouncementMutation) ClearField(name string) error {
switch name {
case announcement.FieldTargeting:
m.ClearTargeting()
return nil
case announcement.FieldStartsAt:
m.ClearStartsAt()
return nil
case announcement.FieldEndsAt:
m.ClearEndsAt()
return nil
case announcement.FieldCreatedBy:
m.ClearCreatedBy()
return nil
case announcement.FieldUpdatedBy:
m.ClearUpdatedBy()
return nil
}
return fmt.Errorf("unknown Announcement nullable field %s", name)
}
// ResetField resets all changes in the mutation for the field with the given name.
// It returns an error if the field is not defined in the schema.
func (m *AnnouncementMutation) ResetField(name string) error {
switch name {
case announcement.FieldTitle:
m.ResetTitle()
return nil
case announcement.FieldContent:
m.ResetContent()
return nil
case announcement.FieldStatus:
m.ResetStatus()
return nil
case announcement.FieldTargeting:
m.ResetTargeting()
return nil
case announcement.FieldStartsAt:
m.ResetStartsAt()
return nil
case announcement.FieldEndsAt:
m.ResetEndsAt()
return nil
case announcement.FieldCreatedBy:
m.ResetCreatedBy()
return nil
case announcement.FieldUpdatedBy:
m.ResetUpdatedBy()
return nil
case announcement.FieldCreatedAt:
m.ResetCreatedAt()
return nil
case announcement.FieldUpdatedAt:
m.ResetUpdatedAt()
return nil
}
return fmt.Errorf("unknown Announcement field %s", name)
}
// AddedEdges returns all edge names that were set/added in this mutation.
func (m *AnnouncementMutation) AddedEdges() []string {
edges := make([]string, 0, 1)
if m.reads != nil {
edges = append(edges, announcement.EdgeReads)
}
return edges
}
// AddedIDs returns all IDs (to other nodes) that were added for the given edge
// name in this mutation.
func (m *AnnouncementMutation) AddedIDs(name string) []ent.Value {
switch name {
case announcement.EdgeReads:
ids := make([]ent.Value, 0, len(m.reads))
for id := range m.reads {
ids = append(ids, id)
}
return ids
}
return nil
}
// RemovedEdges returns all edge names that were removed in this mutation.
func (m *AnnouncementMutation) RemovedEdges() []string {
edges := make([]string, 0, 1)
if m.removedreads != nil {
edges = append(edges, announcement.EdgeReads)
}
return edges
}
// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with
// the given name in this mutation.
func (m *AnnouncementMutation) RemovedIDs(name string) []ent.Value {
switch name {
case announcement.EdgeReads:
ids := make([]ent.Value, 0, len(m.removedreads))
for id := range m.removedreads {
ids = append(ids, id)
}
return ids
}
return nil
}
// ClearedEdges returns all edge names that were cleared in this mutation.
func (m *AnnouncementMutation) ClearedEdges() []string {
edges := make([]string, 0, 1)
if m.clearedreads {
edges = append(edges, announcement.EdgeReads)
}
return edges
}
// EdgeCleared returns a boolean which indicates if the edge with the given name
// was cleared in this mutation.
func (m *AnnouncementMutation) EdgeCleared(name string) bool {
switch name {
case announcement.EdgeReads:
return m.clearedreads
}
return false
}
// ClearEdge clears the value of the edge with the given name. It returns an error
// if that edge is not defined in the schema.
func (m *AnnouncementMutation) ClearEdge(name string) error {
switch name {
}
return fmt.Errorf("unknown Announcement unique edge %s", name)
}
// ResetEdge resets all changes to the edge with the given name in this mutation.
// It returns an error if the edge is not defined in the schema.
func (m *AnnouncementMutation) ResetEdge(name string) error {
switch name {
case announcement.EdgeReads:
m.ResetReads()
return nil
}
return fmt.Errorf("unknown Announcement edge %s", name)
}
// AnnouncementReadMutation represents an operation that mutates the AnnouncementRead nodes in the graph.
type AnnouncementReadMutation struct {
config
op Op
typ string
id *int64
read_at *time.Time
created_at *time.Time
clearedFields map[string]struct{}
announcement *int64
clearedannouncement bool
user *int64
cleareduser bool
done bool
oldValue func(context.Context) (*AnnouncementRead, error)
predicates []predicate.AnnouncementRead
}
var _ ent.Mutation = (*AnnouncementReadMutation)(nil)
// announcementreadOption allows management of the mutation configuration using functional options.
type announcementreadOption func(*AnnouncementReadMutation)
// newAnnouncementReadMutation creates new mutation for the AnnouncementRead entity.
func newAnnouncementReadMutation(c config, op Op, opts ...announcementreadOption) *AnnouncementReadMutation {
m := &AnnouncementReadMutation{
config: c,
op: op,
typ: TypeAnnouncementRead,
clearedFields: make(map[string]struct{}),
}
for _, opt := range opts {
opt(m)
}
return m
}
// withAnnouncementReadID sets the ID field of the mutation.
func withAnnouncementReadID(id int64) announcementreadOption {
return func(m *AnnouncementReadMutation) {
var (
err error
once sync.Once
value *AnnouncementRead
)
m.oldValue = func(ctx context.Context) (*AnnouncementRead, error) {
once.Do(func() {
if m.done {
err = errors.New("querying old values post mutation is not allowed")
} else {
value, err = m.Client().AnnouncementRead.Get(ctx, id)
}
})
return value, err
}
m.id = &id
}
}
// withAnnouncementRead sets the old AnnouncementRead of the mutation.
func withAnnouncementRead(node *AnnouncementRead) announcementreadOption {
return func(m *AnnouncementReadMutation) {
m.oldValue = func(context.Context) (*AnnouncementRead, error) {
return node, nil
}
m.id = &node.ID
}
}
// Client returns a new `ent.Client` from the mutation. If the mutation was
// executed in a transaction (ent.Tx), a transactional client is returned.
func (m AnnouncementReadMutation) Client() *Client {
client := &Client{config: m.config}
client.init()
return client
}
// Tx returns an `ent.Tx` for mutations that were executed in transactions;
// it returns an error otherwise.
func (m AnnouncementReadMutation) Tx() (*Tx, error) {
if _, ok := m.driver.(*txDriver); !ok {
return nil, errors.New("ent: mutation is not running in a transaction")
}
tx := &Tx{config: m.config}
tx.init()
return tx, nil
}
// ID returns the ID value in the mutation. Note that the ID is only available
// if it was provided to the builder or after it was returned from the database.
func (m *AnnouncementReadMutation) ID() (id int64, exists bool) {
if m.id == nil {
return
}
return *m.id, true
}
// IDs queries the database and returns the entity ids that match the mutation's predicate.
// That means, if the mutation is applied within a transaction with an isolation level such
// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated
// or updated by the mutation.
func (m *AnnouncementReadMutation) IDs(ctx context.Context) ([]int64, error) {
switch {
case m.op.Is(OpUpdateOne | OpDeleteOne):
id, exists := m.ID()
if exists {
return []int64{id}, nil
}
fallthrough
case m.op.Is(OpUpdate | OpDelete):
return m.Client().AnnouncementRead.Query().Where(m.predicates...).IDs(ctx)
default:
return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op)
}
}
// SetAnnouncementID sets the "announcement_id" field.
func (m *AnnouncementReadMutation) SetAnnouncementID(i int64) {
m.announcement = &i
}
// AnnouncementID returns the value of the "announcement_id" field in the mutation.
func (m *AnnouncementReadMutation) AnnouncementID() (r int64, exists bool) {
v := m.announcement
if v == nil {
return
}
return *v, true
}
// OldAnnouncementID returns the old "announcement_id" field's value of the AnnouncementRead entity.
// If the AnnouncementRead object wasn't provided to the builder, the object is fetched from the database.
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
func (m *AnnouncementReadMutation) OldAnnouncementID(ctx context.Context) (v int64, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldAnnouncementID is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldAnnouncementID requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldAnnouncementID: %w", err)
}
return oldValue.AnnouncementID, nil
}
// ResetAnnouncementID resets all changes to the "announcement_id" field.
func (m *AnnouncementReadMutation) ResetAnnouncementID() {
m.announcement = nil
}
// SetUserID sets the "user_id" field.
func (m *AnnouncementReadMutation) SetUserID(i int64) {
m.user = &i
}
// UserID returns the value of the "user_id" field in the mutation.
func (m *AnnouncementReadMutation) UserID() (r int64, exists bool) {
v := m.user
if v == nil {
return
}
return *v, true
}
// OldUserID returns the old "user_id" field's value of the AnnouncementRead entity.
// If the AnnouncementRead object wasn't provided to the builder, the object is fetched from the database.
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
func (m *AnnouncementReadMutation) OldUserID(ctx context.Context) (v int64, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldUserID is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldUserID requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldUserID: %w", err)
}
return oldValue.UserID, nil
}
// ResetUserID resets all changes to the "user_id" field.
func (m *AnnouncementReadMutation) ResetUserID() {
m.user = nil
}
// SetReadAt sets the "read_at" field.
func (m *AnnouncementReadMutation) SetReadAt(t time.Time) {
m.read_at = &t
}
// ReadAt returns the value of the "read_at" field in the mutation.
func (m *AnnouncementReadMutation) ReadAt() (r time.Time, exists bool) {
v := m.read_at
if v == nil {
return
}
return *v, true
}
// OldReadAt returns the old "read_at" field's value of the AnnouncementRead entity.
// If the AnnouncementRead object wasn't provided to the builder, the object is fetched from the database.
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
func (m *AnnouncementReadMutation) OldReadAt(ctx context.Context) (v time.Time, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldReadAt is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldReadAt requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldReadAt: %w", err)
}
return oldValue.ReadAt, nil
}
// ResetReadAt resets all changes to the "read_at" field.
func (m *AnnouncementReadMutation) ResetReadAt() {
m.read_at = nil
}
// SetCreatedAt sets the "created_at" field.
func (m *AnnouncementReadMutation) SetCreatedAt(t time.Time) {
m.created_at = &t
}
// CreatedAt returns the value of the "created_at" field in the mutation.
func (m *AnnouncementReadMutation) CreatedAt() (r time.Time, exists bool) {
v := m.created_at
if v == nil {
return
}
return *v, true
}
// OldCreatedAt returns the old "created_at" field's value of the AnnouncementRead entity.
// If the AnnouncementRead object wasn't provided to the builder, the object is fetched from the database.
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
func (m *AnnouncementReadMutation) OldCreatedAt(ctx context.Context) (v time.Time, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldCreatedAt is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldCreatedAt requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldCreatedAt: %w", err)
}
return oldValue.CreatedAt, nil
}
// ResetCreatedAt resets all changes to the "created_at" field.
func (m *AnnouncementReadMutation) ResetCreatedAt() {
m.created_at = nil
}
// ClearAnnouncement clears the "announcement" edge to the Announcement entity.
func (m *AnnouncementReadMutation) ClearAnnouncement() {
m.clearedannouncement = true
m.clearedFields[announcementread.FieldAnnouncementID] = struct{}{}
}
// AnnouncementCleared reports if the "announcement" edge to the Announcement entity was cleared.
func (m *AnnouncementReadMutation) AnnouncementCleared() bool {
return m.clearedannouncement
}
// AnnouncementIDs returns the "announcement" edge IDs in the mutation.
// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use
// AnnouncementID instead. It exists only for internal usage by the builders.
func (m *AnnouncementReadMutation) AnnouncementIDs() (ids []int64) {
if id := m.announcement; id != nil {
ids = append(ids, *id)
}
return
}
// ResetAnnouncement resets all changes to the "announcement" edge.
func (m *AnnouncementReadMutation) ResetAnnouncement() {
m.announcement = nil
m.clearedannouncement = false
}
// ClearUser clears the "user" edge to the User entity.
func (m *AnnouncementReadMutation) ClearUser() {
m.cleareduser = true
m.clearedFields[announcementread.FieldUserID] = struct{}{}
}
// UserCleared reports if the "user" edge to the User entity was cleared.
func (m *AnnouncementReadMutation) UserCleared() bool {
return m.cleareduser
}
// UserIDs returns the "user" edge IDs in the mutation.
// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use
// UserID instead. It exists only for internal usage by the builders.
func (m *AnnouncementReadMutation) UserIDs() (ids []int64) {
if id := m.user; id != nil {
ids = append(ids, *id)
}
return
}
// ResetUser resets all changes to the "user" edge.
func (m *AnnouncementReadMutation) ResetUser() {
m.user = nil
m.cleareduser = false
}
// Where appends a list predicates to the AnnouncementReadMutation builder.
func (m *AnnouncementReadMutation) Where(ps ...predicate.AnnouncementRead) {
m.predicates = append(m.predicates, ps...)
}
// WhereP appends storage-level predicates to the AnnouncementReadMutation builder. Using this method,
// users can use type-assertion to append predicates that do not depend on any generated package.
func (m *AnnouncementReadMutation) WhereP(ps ...func(*sql.Selector)) {
p := make([]predicate.AnnouncementRead, len(ps))
for i := range ps {
p[i] = ps[i]
}
m.Where(p...)
}
// Op returns the operation name.
func (m *AnnouncementReadMutation) Op() Op {
return m.op
}
// SetOp allows setting the mutation operation.
func (m *AnnouncementReadMutation) SetOp(op Op) {
m.op = op
}
// Type returns the node type of this mutation (AnnouncementRead).
func (m *AnnouncementReadMutation) Type() string {
return m.typ
}
// Fields returns all fields that were changed during this mutation. Note that in
// order to get all numeric fields that were incremented/decremented, call
// AddedFields().
func (m *AnnouncementReadMutation) Fields() []string {
fields := make([]string, 0, 4)
if m.announcement != nil {
fields = append(fields, announcementread.FieldAnnouncementID)
}
if m.user != nil {
fields = append(fields, announcementread.FieldUserID)
}
if m.read_at != nil {
fields = append(fields, announcementread.FieldReadAt)
}
if m.created_at != nil {
fields = append(fields, announcementread.FieldCreatedAt)
}
return fields
}
// Field returns the value of a field with the given name. The second boolean
// return value indicates that this field was not set, or was not defined in the
// schema.
func (m *AnnouncementReadMutation) Field(name string) (ent.Value, bool) {
switch name {
case announcementread.FieldAnnouncementID:
return m.AnnouncementID()
case announcementread.FieldUserID:
return m.UserID()
case announcementread.FieldReadAt:
return m.ReadAt()
case announcementread.FieldCreatedAt:
return m.CreatedAt()
}
return nil, false
}
// OldField returns the old value of the field from the database. An error is
// returned if the mutation operation is not UpdateOne, or the query to the
// database failed.
func (m *AnnouncementReadMutation) OldField(ctx context.Context, name string) (ent.Value, error) {
switch name {
case announcementread.FieldAnnouncementID:
return m.OldAnnouncementID(ctx)
case announcementread.FieldUserID:
return m.OldUserID(ctx)
case announcementread.FieldReadAt:
return m.OldReadAt(ctx)
case announcementread.FieldCreatedAt:
return m.OldCreatedAt(ctx)
}
return nil, fmt.Errorf("unknown AnnouncementRead field %s", name)
}
// SetField sets the value of a field with the given name. It returns an error if
// the field is not defined in the schema, or if the type mismatched the field
// type.
func (m *AnnouncementReadMutation) SetField(name string, value ent.Value) error {
switch name {
case announcementread.FieldAnnouncementID:
v, ok := value.(int64)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetAnnouncementID(v)
return nil
case announcementread.FieldUserID:
v, ok := value.(int64)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetUserID(v)
return nil
case announcementread.FieldReadAt:
v, ok := value.(time.Time)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetReadAt(v)
return nil
case announcementread.FieldCreatedAt:
v, ok := value.(time.Time)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetCreatedAt(v)
return nil
}
return fmt.Errorf("unknown AnnouncementRead field %s", name)
}
// AddedFields returns all numeric fields that were incremented/decremented during
// this mutation.
func (m *AnnouncementReadMutation) AddedFields() []string {
var fields []string
return fields
}
// AddedField returns the numeric value that was incremented/decremented on a field
// with the given name. The second boolean return value indicates that this field
// was not set, or was not defined in the schema.
func (m *AnnouncementReadMutation) AddedField(name string) (ent.Value, bool) {
switch name {
}
return nil, false
}
// AddField adds the value to the field with the given name. It returns an error if
// the field is not defined in the schema, or if the type mismatched the field
// type.
func (m *AnnouncementReadMutation) AddField(name string, value ent.Value) error {
switch name {
}
return fmt.Errorf("unknown AnnouncementRead numeric field %s", name)
}
// ClearedFields returns all nullable fields that were cleared during this
// mutation.
func (m *AnnouncementReadMutation) ClearedFields() []string {
return nil
}
// FieldCleared returns a boolean indicating if a field with the given name was
// cleared in this mutation.
func (m *AnnouncementReadMutation) FieldCleared(name string) bool {
_, ok := m.clearedFields[name]
return ok
}
// ClearField clears the value of the field with the given name. It returns an
// error if the field is not defined in the schema.
func (m *AnnouncementReadMutation) ClearField(name string) error {
return fmt.Errorf("unknown AnnouncementRead nullable field %s", name)
}
// ResetField resets all changes in the mutation for the field with the given name.
// It returns an error if the field is not defined in the schema.
func (m *AnnouncementReadMutation) ResetField(name string) error {
switch name {
case announcementread.FieldAnnouncementID:
m.ResetAnnouncementID()
return nil
case announcementread.FieldUserID:
m.ResetUserID()
return nil
case announcementread.FieldReadAt:
m.ResetReadAt()
return nil
case announcementread.FieldCreatedAt:
m.ResetCreatedAt()
return nil
}
return fmt.Errorf("unknown AnnouncementRead field %s", name)
}
// AddedEdges returns all edge names that were set/added in this mutation.
func (m *AnnouncementReadMutation) AddedEdges() []string {
edges := make([]string, 0, 2)
if m.announcement != nil {
edges = append(edges, announcementread.EdgeAnnouncement)
}
if m.user != nil {
edges = append(edges, announcementread.EdgeUser)
}
return edges
}
// AddedIDs returns all IDs (to other nodes) that were added for the given edge
// name in this mutation.
func (m *AnnouncementReadMutation) AddedIDs(name string) []ent.Value {
switch name {
case announcementread.EdgeAnnouncement:
if id := m.announcement; id != nil {
return []ent.Value{*id}
}
case announcementread.EdgeUser:
if id := m.user; id != nil {
return []ent.Value{*id}
}
}
return nil
}
// RemovedEdges returns all edge names that were removed in this mutation.
func (m *AnnouncementReadMutation) RemovedEdges() []string {
edges := make([]string, 0, 2)
return edges
}
// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with
// the given name in this mutation.
func (m *AnnouncementReadMutation) RemovedIDs(name string) []ent.Value {
return nil
}
// ClearedEdges returns all edge names that were cleared in this mutation.
func (m *AnnouncementReadMutation) ClearedEdges() []string {
edges := make([]string, 0, 2)
if m.clearedannouncement {
edges = append(edges, announcementread.EdgeAnnouncement)
}
if m.cleareduser {
edges = append(edges, announcementread.EdgeUser)
}
return edges
}
// EdgeCleared returns a boolean which indicates if the edge with the given name
// was cleared in this mutation.
func (m *AnnouncementReadMutation) EdgeCleared(name string) bool {
switch name {
case announcementread.EdgeAnnouncement:
return m.clearedannouncement
case announcementread.EdgeUser:
return m.cleareduser
}
return false
}
// ClearEdge clears the value of the edge with the given name. It returns an error
// if that edge is not defined in the schema.
func (m *AnnouncementReadMutation) ClearEdge(name string) error {
switch name {
case announcementread.EdgeAnnouncement:
m.ClearAnnouncement()
return nil
case announcementread.EdgeUser:
m.ClearUser()
return nil
}
return fmt.Errorf("unknown AnnouncementRead unique edge %s", name)
}
// ResetEdge resets all changes to the edge with the given name in this mutation.
// It returns an error if the edge is not defined in the schema.
func (m *AnnouncementReadMutation) ResetEdge(name string) error {
switch name {
case announcementread.EdgeAnnouncement:
m.ResetAnnouncement()
return nil
case announcementread.EdgeUser:
m.ResetUser()
return nil
}
return fmt.Errorf("unknown AnnouncementRead edge %s", name)
}
// GroupMutation represents an operation that mutates the Group nodes in the graph.
type GroupMutation struct {
config
...
...
@@ -14376,6 +16046,9 @@ type UserMutation struct {
assigned_subscriptions map[int64]struct{}
removedassigned_subscriptions map[int64]struct{}
clearedassigned_subscriptions bool
announcement_reads map[int64]struct{}
removedannouncement_reads map[int64]struct{}
clearedannouncement_reads bool
allowed_groups map[int64]struct{}
removedallowed_groups map[int64]struct{}
clearedallowed_groups bool
...
...
@@ -15290,6 +16963,60 @@ func (m *UserMutation) ResetAssignedSubscriptions() {
m.removedassigned_subscriptions = nil
}
// AddAnnouncementReadIDs adds the "announcement_reads" edge to the AnnouncementRead entity by ids.
func (m *UserMutation) AddAnnouncementReadIDs(ids ...int64) {
if m.announcement_reads == nil {
m.announcement_reads = make(map[int64]struct{})
}
for i := range ids {
m.announcement_reads[ids[i]] = struct{}{}
}
}
// ClearAnnouncementReads clears the "announcement_reads" edge to the AnnouncementRead entity.
func (m *UserMutation) ClearAnnouncementReads() {
m.clearedannouncement_reads = true
}
// AnnouncementReadsCleared reports if the "announcement_reads" edge to the AnnouncementRead entity was cleared.
func (m *UserMutation) AnnouncementReadsCleared() bool {
return m.clearedannouncement_reads
}
// RemoveAnnouncementReadIDs removes the "announcement_reads" edge to the AnnouncementRead entity by IDs.
func (m *UserMutation) RemoveAnnouncementReadIDs(ids ...int64) {
if m.removedannouncement_reads == nil {
m.removedannouncement_reads = make(map[int64]struct{})
}
for i := range ids {
delete(m.announcement_reads, ids[i])
m.removedannouncement_reads[ids[i]] = struct{}{}
}
}
// RemovedAnnouncementReads returns the removed IDs of the "announcement_reads" edge to the AnnouncementRead entity.
func (m *UserMutation) RemovedAnnouncementReadsIDs() (ids []int64) {
for id := range m.removedannouncement_reads {
ids = append(ids, id)
}
return
}
// AnnouncementReadsIDs returns the "announcement_reads" edge IDs in the mutation.
func (m *UserMutation) AnnouncementReadsIDs() (ids []int64) {
for id := range m.announcement_reads {
ids = append(ids, id)
}
return
}
// ResetAnnouncementReads resets all changes to the "announcement_reads" edge.
func (m *UserMutation) ResetAnnouncementReads() {
m.announcement_reads = nil
m.clearedannouncement_reads = false
m.removedannouncement_reads = nil
}
// AddAllowedGroupIDs adds the "allowed_groups" edge to the Group entity by ids.
func (m *UserMutation) AddAllowedGroupIDs(ids ...int64) {
if m.allowed_groups == nil {
...
...
@@ -15908,7 +17635,7 @@ func (m *UserMutation) ResetField(name string) error {
// AddedEdges returns all edge names that were set/added in this mutation.
func (m *UserMutation) AddedEdges() []string {
edges := make([]string, 0,
8
)
edges := make([]string, 0,
9
)
if m.api_keys != nil {
edges = append(edges, user.EdgeAPIKeys)
}
...
...
@@ -15921,6 +17648,9 @@ func (m *UserMutation) AddedEdges() []string {
if m.assigned_subscriptions != nil {
edges = append(edges, user.EdgeAssignedSubscriptions)
}
if m.announcement_reads != nil {
edges = append(edges, user.EdgeAnnouncementReads)
}
if m.allowed_groups != nil {
edges = append(edges, user.EdgeAllowedGroups)
}
...
...
@@ -15964,6 +17694,12 @@ func (m *UserMutation) AddedIDs(name string) []ent.Value {
ids = append(ids, id)
}
return ids
case user.EdgeAnnouncementReads:
ids := make([]ent.Value, 0, len(m.announcement_reads))
for id := range m.announcement_reads {
ids = append(ids, id)
}
return ids
case user.EdgeAllowedGroups:
ids := make([]ent.Value, 0, len(m.allowed_groups))
for id := range m.allowed_groups {
...
...
@@ -15994,7 +17730,7 @@ func (m *UserMutation) AddedIDs(name string) []ent.Value {
// RemovedEdges returns all edge names that were removed in this mutation.
func (m *UserMutation) RemovedEdges() []string {
edges := make([]string, 0,
8
)
edges := make([]string, 0,
9
)
if m.removedapi_keys != nil {
edges = append(edges, user.EdgeAPIKeys)
}
...
...
@@ -16007,6 +17743,9 @@ func (m *UserMutation) RemovedEdges() []string {
if m.removedassigned_subscriptions != nil {
edges = append(edges, user.EdgeAssignedSubscriptions)
}
if m.removedannouncement_reads != nil {
edges = append(edges, user.EdgeAnnouncementReads)
}
if m.removedallowed_groups != nil {
edges = append(edges, user.EdgeAllowedGroups)
}
...
...
@@ -16050,6 +17789,12 @@ func (m *UserMutation) RemovedIDs(name string) []ent.Value {
ids = append(ids, id)
}
return ids
case user.EdgeAnnouncementReads:
ids := make([]ent.Value, 0, len(m.removedannouncement_reads))
for id := range m.removedannouncement_reads {
ids = append(ids, id)
}
return ids
case user.EdgeAllowedGroups:
ids := make([]ent.Value, 0, len(m.removedallowed_groups))
for id := range m.removedallowed_groups {
...
...
@@ -16080,7 +17825,7 @@ func (m *UserMutation) RemovedIDs(name string) []ent.Value {
// ClearedEdges returns all edge names that were cleared in this mutation.
func (m *UserMutation) ClearedEdges() []string {
edges := make([]string, 0,
8
)
edges := make([]string, 0,
9
)
if m.clearedapi_keys {
edges = append(edges, user.EdgeAPIKeys)
}
...
...
@@ -16093,6 +17838,9 @@ func (m *UserMutation) ClearedEdges() []string {
if m.clearedassigned_subscriptions {
edges = append(edges, user.EdgeAssignedSubscriptions)
}
if m.clearedannouncement_reads {
edges = append(edges, user.EdgeAnnouncementReads)
}
if m.clearedallowed_groups {
edges = append(edges, user.EdgeAllowedGroups)
}
...
...
@@ -16120,6 +17868,8 @@ func (m *UserMutation) EdgeCleared(name string) bool {
return m.clearedsubscriptions
case user.EdgeAssignedSubscriptions:
return m.clearedassigned_subscriptions
case user.EdgeAnnouncementReads:
return m.clearedannouncement_reads
case user.EdgeAllowedGroups:
return m.clearedallowed_groups
case user.EdgeUsageLogs:
...
...
@@ -16156,6 +17906,9 @@ func (m *UserMutation) ResetEdge(name string) error {
case user.EdgeAssignedSubscriptions:
m.ResetAssignedSubscriptions()
return nil
case user.EdgeAnnouncementReads:
m.ResetAnnouncementReads()
return nil
case user.EdgeAllowedGroups:
m.ResetAllowedGroups()
return nil
...
...
backend/ent/predicate/predicate.go
View file @
d3062b2e
...
...
@@ -15,6 +15,12 @@ type Account func(*sql.Selector)
// AccountGroup is the predicate function for accountgroup builders.
type
AccountGroup
func
(
*
sql
.
Selector
)
// Announcement is the predicate function for announcement builders.
type
Announcement
func
(
*
sql
.
Selector
)
// AnnouncementRead is the predicate function for announcementread builders.
type
AnnouncementRead
func
(
*
sql
.
Selector
)
// Group is the predicate function for group builders.
type
Group
func
(
*
sql
.
Selector
)
...
...
backend/ent/runtime/runtime.go
View file @
d3062b2e
...
...
@@ -7,6 +7,8 @@ import (
"github.com/Wei-Shaw/sub2api/ent/account"
"github.com/Wei-Shaw/sub2api/ent/accountgroup"
"github.com/Wei-Shaw/sub2api/ent/announcement"
"github.com/Wei-Shaw/sub2api/ent/announcementread"
"github.com/Wei-Shaw/sub2api/ent/apikey"
"github.com/Wei-Shaw/sub2api/ent/group"
"github.com/Wei-Shaw/sub2api/ent/promocode"
...
...
@@ -210,6 +212,56 @@ func init() {
accountgroupDescCreatedAt
:=
accountgroupFields
[
3
]
.
Descriptor
()
// accountgroup.DefaultCreatedAt holds the default value on creation for the created_at field.
accountgroup
.
DefaultCreatedAt
=
accountgroupDescCreatedAt
.
Default
.
(
func
()
time
.
Time
)
announcementFields
:=
schema
.
Announcement
{}
.
Fields
()
_
=
announcementFields
// announcementDescTitle is the schema descriptor for title field.
announcementDescTitle
:=
announcementFields
[
0
]
.
Descriptor
()
// announcement.TitleValidator is a validator for the "title" field. It is called by the builders before save.
announcement
.
TitleValidator
=
func
()
func
(
string
)
error
{
validators
:=
announcementDescTitle
.
Validators
fns
:=
[
...
]
func
(
string
)
error
{
validators
[
0
]
.
(
func
(
string
)
error
),
validators
[
1
]
.
(
func
(
string
)
error
),
}
return
func
(
title
string
)
error
{
for
_
,
fn
:=
range
fns
{
if
err
:=
fn
(
title
);
err
!=
nil
{
return
err
}
}
return
nil
}
}()
// announcementDescContent is the schema descriptor for content field.
announcementDescContent
:=
announcementFields
[
1
]
.
Descriptor
()
// announcement.ContentValidator is a validator for the "content" field. It is called by the builders before save.
announcement
.
ContentValidator
=
announcementDescContent
.
Validators
[
0
]
.
(
func
(
string
)
error
)
// announcementDescStatus is the schema descriptor for status field.
announcementDescStatus
:=
announcementFields
[
2
]
.
Descriptor
()
// announcement.DefaultStatus holds the default value on creation for the status field.
announcement
.
DefaultStatus
=
announcementDescStatus
.
Default
.
(
string
)
// announcement.StatusValidator is a validator for the "status" field. It is called by the builders before save.
announcement
.
StatusValidator
=
announcementDescStatus
.
Validators
[
0
]
.
(
func
(
string
)
error
)
// announcementDescCreatedAt is the schema descriptor for created_at field.
announcementDescCreatedAt
:=
announcementFields
[
8
]
.
Descriptor
()
// announcement.DefaultCreatedAt holds the default value on creation for the created_at field.
announcement
.
DefaultCreatedAt
=
announcementDescCreatedAt
.
Default
.
(
func
()
time
.
Time
)
// announcementDescUpdatedAt is the schema descriptor for updated_at field.
announcementDescUpdatedAt
:=
announcementFields
[
9
]
.
Descriptor
()
// announcement.DefaultUpdatedAt holds the default value on creation for the updated_at field.
announcement
.
DefaultUpdatedAt
=
announcementDescUpdatedAt
.
Default
.
(
func
()
time
.
Time
)
// announcement.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field.
announcement
.
UpdateDefaultUpdatedAt
=
announcementDescUpdatedAt
.
UpdateDefault
.
(
func
()
time
.
Time
)
announcementreadFields
:=
schema
.
AnnouncementRead
{}
.
Fields
()
_
=
announcementreadFields
// announcementreadDescReadAt is the schema descriptor for read_at field.
announcementreadDescReadAt
:=
announcementreadFields
[
2
]
.
Descriptor
()
// announcementread.DefaultReadAt holds the default value on creation for the read_at field.
announcementread
.
DefaultReadAt
=
announcementreadDescReadAt
.
Default
.
(
func
()
time
.
Time
)
// announcementreadDescCreatedAt is the schema descriptor for created_at field.
announcementreadDescCreatedAt
:=
announcementreadFields
[
3
]
.
Descriptor
()
// announcementread.DefaultCreatedAt holds the default value on creation for the created_at field.
announcementread
.
DefaultCreatedAt
=
announcementreadDescCreatedAt
.
Default
.
(
func
()
time
.
Time
)
groupMixin
:=
schema
.
Group
{}
.
Mixin
()
groupMixinHooks1
:=
groupMixin
[
1
]
.
Hooks
()
group
.
Hooks
[
0
]
=
groupMixinHooks1
[
0
]
...
...
backend/ent/schema/account.go
View file @
d3062b2e
...
...
@@ -4,7 +4,7 @@ package schema
import
(
"github.com/Wei-Shaw/sub2api/ent/schema/mixins"
"github.com/Wei-Shaw/sub2api/internal/
service
"
"github.com/Wei-Shaw/sub2api/internal/
domain
"
"entgo.io/ent"
"entgo.io/ent/dialect"
...
...
@@ -111,7 +111,7 @@ func (Account) Fields() []ent.Field {
// status: 账户状态,如 "active", "error", "disabled"
field
.
String
(
"status"
)
.
MaxLen
(
20
)
.
Default
(
service
.
StatusActive
),
Default
(
domain
.
StatusActive
),
// error_message: 错误信息,记录账户异常时的详细信息
field
.
String
(
"error_message"
)
.
...
...
backend/ent/schema/announcement.go
0 → 100644
View file @
d3062b2e
package
schema
import
(
"time"
"github.com/Wei-Shaw/sub2api/internal/domain"
"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"
)
// Announcement holds the schema definition for the Announcement entity.
//
// 删除策略:硬删除(已读记录通过外键级联删除)
type
Announcement
struct
{
ent
.
Schema
}
func
(
Announcement
)
Annotations
()
[]
schema
.
Annotation
{
return
[]
schema
.
Annotation
{
entsql
.
Annotation
{
Table
:
"announcements"
},
}
}
func
(
Announcement
)
Fields
()
[]
ent
.
Field
{
return
[]
ent
.
Field
{
field
.
String
(
"title"
)
.
MaxLen
(
200
)
.
NotEmpty
()
.
Comment
(
"公告标题"
),
field
.
String
(
"content"
)
.
SchemaType
(
map
[
string
]
string
{
dialect
.
Postgres
:
"text"
})
.
NotEmpty
()
.
Comment
(
"公告内容(支持 Markdown)"
),
field
.
String
(
"status"
)
.
MaxLen
(
20
)
.
Default
(
domain
.
AnnouncementStatusDraft
)
.
Comment
(
"状态: draft, active, archived"
),
field
.
JSON
(
"targeting"
,
domain
.
AnnouncementTargeting
{})
.
Optional
()
.
SchemaType
(
map
[
string
]
string
{
dialect
.
Postgres
:
"jsonb"
})
.
Comment
(
"展示条件(JSON 规则)"
),
field
.
Time
(
"starts_at"
)
.
Optional
()
.
Nillable
()
.
SchemaType
(
map
[
string
]
string
{
dialect
.
Postgres
:
"timestamptz"
})
.
Comment
(
"开始展示时间(为空表示立即生效)"
),
field
.
Time
(
"ends_at"
)
.
Optional
()
.
Nillable
()
.
SchemaType
(
map
[
string
]
string
{
dialect
.
Postgres
:
"timestamptz"
})
.
Comment
(
"结束展示时间(为空表示永久生效)"
),
field
.
Int64
(
"created_by"
)
.
Optional
()
.
Nillable
()
.
Comment
(
"创建人用户ID(管理员)"
),
field
.
Int64
(
"updated_by"
)
.
Optional
()
.
Nillable
()
.
Comment
(
"更新人用户ID(管理员)"
),
field
.
Time
(
"created_at"
)
.
Immutable
()
.
Default
(
time
.
Now
)
.
SchemaType
(
map
[
string
]
string
{
dialect
.
Postgres
:
"timestamptz"
}),
field
.
Time
(
"updated_at"
)
.
Default
(
time
.
Now
)
.
UpdateDefault
(
time
.
Now
)
.
SchemaType
(
map
[
string
]
string
{
dialect
.
Postgres
:
"timestamptz"
}),
}
}
func
(
Announcement
)
Edges
()
[]
ent
.
Edge
{
return
[]
ent
.
Edge
{
edge
.
To
(
"reads"
,
AnnouncementRead
.
Type
),
}
}
func
(
Announcement
)
Indexes
()
[]
ent
.
Index
{
return
[]
ent
.
Index
{
index
.
Fields
(
"status"
),
index
.
Fields
(
"created_at"
),
index
.
Fields
(
"starts_at"
),
index
.
Fields
(
"ends_at"
),
}
}
backend/ent/schema/announcement_read.go
0 → 100644
View file @
d3062b2e
package
schema
import
(
"time"
"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"
)
// AnnouncementRead holds the schema definition for the AnnouncementRead entity.
//
// 记录用户对公告的已读状态(首次已读时间)。
type
AnnouncementRead
struct
{
ent
.
Schema
}
func
(
AnnouncementRead
)
Annotations
()
[]
schema
.
Annotation
{
return
[]
schema
.
Annotation
{
entsql
.
Annotation
{
Table
:
"announcement_reads"
},
}
}
func
(
AnnouncementRead
)
Fields
()
[]
ent
.
Field
{
return
[]
ent
.
Field
{
field
.
Int64
(
"announcement_id"
),
field
.
Int64
(
"user_id"
),
field
.
Time
(
"read_at"
)
.
Default
(
time
.
Now
)
.
SchemaType
(
map
[
string
]
string
{
dialect
.
Postgres
:
"timestamptz"
})
.
Comment
(
"用户首次已读时间"
),
field
.
Time
(
"created_at"
)
.
Immutable
()
.
Default
(
time
.
Now
)
.
SchemaType
(
map
[
string
]
string
{
dialect
.
Postgres
:
"timestamptz"
}),
}
}
func
(
AnnouncementRead
)
Edges
()
[]
ent
.
Edge
{
return
[]
ent
.
Edge
{
edge
.
From
(
"announcement"
,
Announcement
.
Type
)
.
Ref
(
"reads"
)
.
Field
(
"announcement_id"
)
.
Unique
()
.
Required
(),
edge
.
From
(
"user"
,
User
.
Type
)
.
Ref
(
"announcement_reads"
)
.
Field
(
"user_id"
)
.
Unique
()
.
Required
(),
}
}
func
(
AnnouncementRead
)
Indexes
()
[]
ent
.
Index
{
return
[]
ent
.
Index
{
index
.
Fields
(
"announcement_id"
),
index
.
Fields
(
"user_id"
),
index
.
Fields
(
"read_at"
),
index
.
Fields
(
"announcement_id"
,
"user_id"
)
.
Unique
(),
}
}
backend/ent/schema/api_key.go
View file @
d3062b2e
...
...
@@ -2,7 +2,7 @@ package schema
import
(
"github.com/Wei-Shaw/sub2api/ent/schema/mixins"
"github.com/Wei-Shaw/sub2api/internal/
service
"
"github.com/Wei-Shaw/sub2api/internal/
domain
"
"entgo.io/ent"
"entgo.io/ent/dialect/entsql"
...
...
@@ -45,7 +45,7 @@ func (APIKey) Fields() []ent.Field {
Nillable
(),
field
.
String
(
"status"
)
.
MaxLen
(
20
)
.
Default
(
service
.
StatusActive
),
Default
(
domain
.
StatusActive
),
field
.
JSON
(
"ip_whitelist"
,
[]
string
{})
.
Optional
()
.
Comment
(
"Allowed IPs/CIDRs, e.g. [
\"
192.168.1.100
\"
,
\"
10.0.0.0/8
\"
]"
),
...
...
backend/ent/schema/group.go
View file @
d3062b2e
...
...
@@ -2,7 +2,7 @@ package schema
import
(
"github.com/Wei-Shaw/sub2api/ent/schema/mixins"
"github.com/Wei-Shaw/sub2api/internal/
service
"
"github.com/Wei-Shaw/sub2api/internal/
domain
"
"entgo.io/ent"
"entgo.io/ent/dialect"
...
...
@@ -49,15 +49,15 @@ func (Group) Fields() []ent.Field {
Default
(
false
),
field
.
String
(
"status"
)
.
MaxLen
(
20
)
.
Default
(
service
.
StatusActive
),
Default
(
domain
.
StatusActive
),
// Subscription-related fields (added by migration 003)
field
.
String
(
"platform"
)
.
MaxLen
(
50
)
.
Default
(
service
.
PlatformAnthropic
),
Default
(
domain
.
PlatformAnthropic
),
field
.
String
(
"subscription_type"
)
.
MaxLen
(
20
)
.
Default
(
service
.
SubscriptionTypeStandard
),
Default
(
domain
.
SubscriptionTypeStandard
),
field
.
Float
(
"daily_limit_usd"
)
.
Optional
()
.
Nillable
()
.
...
...
backend/ent/schema/promo_code.go
View file @
d3062b2e
...
...
@@ -3,7 +3,7 @@ package schema
import
(
"time"
"github.com/Wei-Shaw/sub2api/internal/
service
"
"github.com/Wei-Shaw/sub2api/internal/
domain
"
"entgo.io/ent"
"entgo.io/ent/dialect"
...
...
@@ -49,7 +49,7 @@ func (PromoCode) Fields() []ent.Field {
Comment
(
"已使用次数"
),
field
.
String
(
"status"
)
.
MaxLen
(
20
)
.
Default
(
service
.
PromoCodeStatusActive
)
.
Default
(
domain
.
PromoCodeStatusActive
)
.
Comment
(
"状态: active, disabled"
),
field
.
Time
(
"expires_at"
)
.
Optional
()
.
...
...
backend/ent/schema/redeem_code.go
View file @
d3062b2e
...
...
@@ -3,7 +3,7 @@ package schema
import
(
"time"
"github.com/Wei-Shaw/sub2api/internal/
service
"
"github.com/Wei-Shaw/sub2api/internal/
domain
"
"entgo.io/ent"
"entgo.io/ent/dialect"
...
...
@@ -41,13 +41,13 @@ func (RedeemCode) Fields() []ent.Field {
Unique
(),
field
.
String
(
"type"
)
.
MaxLen
(
20
)
.
Default
(
service
.
RedeemTypeBalance
),
Default
(
domain
.
RedeemTypeBalance
),
field
.
Float
(
"value"
)
.
SchemaType
(
map
[
string
]
string
{
dialect
.
Postgres
:
"decimal(20,8)"
})
.
Default
(
0
),
field
.
String
(
"status"
)
.
MaxLen
(
20
)
.
Default
(
service
.
StatusUnused
),
Default
(
domain
.
StatusUnused
),
field
.
Int64
(
"used_by"
)
.
Optional
()
.
Nillable
(),
...
...
backend/ent/schema/user.go
View file @
d3062b2e
...
...
@@ -2,7 +2,7 @@ package schema
import
(
"github.com/Wei-Shaw/sub2api/ent/schema/mixins"
"github.com/Wei-Shaw/sub2api/internal/
service
"
"github.com/Wei-Shaw/sub2api/internal/
domain
"
"entgo.io/ent"
"entgo.io/ent/dialect"
...
...
@@ -43,7 +43,7 @@ func (User) Fields() []ent.Field {
NotEmpty
(),
field
.
String
(
"role"
)
.
MaxLen
(
20
)
.
Default
(
service
.
RoleUser
),
Default
(
domain
.
RoleUser
),
field
.
Float
(
"balance"
)
.
SchemaType
(
map
[
string
]
string
{
dialect
.
Postgres
:
"decimal(20,8)"
})
.
Default
(
0
),
...
...
@@ -51,7 +51,7 @@ func (User) Fields() []ent.Field {
Default
(
5
),
field
.
String
(
"status"
)
.
MaxLen
(
20
)
.
Default
(
service
.
StatusActive
),
Default
(
domain
.
StatusActive
),
// Optional profile fields (added later; default '' in DB migration)
field
.
String
(
"username"
)
.
...
...
@@ -81,6 +81,7 @@ func (User) Edges() []ent.Edge {
edge
.
To
(
"redeem_codes"
,
RedeemCode
.
Type
),
edge
.
To
(
"subscriptions"
,
UserSubscription
.
Type
),
edge
.
To
(
"assigned_subscriptions"
,
UserSubscription
.
Type
),
edge
.
To
(
"announcement_reads"
,
AnnouncementRead
.
Type
),
edge
.
To
(
"allowed_groups"
,
Group
.
Type
)
.
Through
(
"user_allowed_groups"
,
UserAllowedGroup
.
Type
),
edge
.
To
(
"usage_logs"
,
UsageLog
.
Type
),
...
...
backend/ent/schema/user_subscription.go
View file @
d3062b2e
...
...
@@ -4,7 +4,7 @@ import (
"time"
"github.com/Wei-Shaw/sub2api/ent/schema/mixins"
"github.com/Wei-Shaw/sub2api/internal/
service
"
"github.com/Wei-Shaw/sub2api/internal/
domain
"
"entgo.io/ent"
"entgo.io/ent/dialect"
...
...
@@ -44,7 +44,7 @@ func (UserSubscription) Fields() []ent.Field {
SchemaType
(
map
[
string
]
string
{
dialect
.
Postgres
:
"timestamptz"
}),
field
.
String
(
"status"
)
.
MaxLen
(
20
)
.
Default
(
service
.
SubscriptionStatusActive
),
Default
(
domain
.
SubscriptionStatusActive
),
field
.
Time
(
"daily_window_start"
)
.
Optional
()
.
...
...
backend/ent/tx.go
View file @
d3062b2e
...
...
@@ -20,6 +20,10 @@ type Tx struct {
Account
*
AccountClient
// AccountGroup is the client for interacting with the AccountGroup builders.
AccountGroup
*
AccountGroupClient
// Announcement is the client for interacting with the Announcement builders.
Announcement
*
AnnouncementClient
// AnnouncementRead is the client for interacting with the AnnouncementRead builders.
AnnouncementRead
*
AnnouncementReadClient
// Group is the client for interacting with the Group builders.
Group
*
GroupClient
// PromoCode is the client for interacting with the PromoCode builders.
...
...
@@ -180,6 +184,8 @@ func (tx *Tx) init() {
tx
.
APIKey
=
NewAPIKeyClient
(
tx
.
config
)
tx
.
Account
=
NewAccountClient
(
tx
.
config
)
tx
.
AccountGroup
=
NewAccountGroupClient
(
tx
.
config
)
tx
.
Announcement
=
NewAnnouncementClient
(
tx
.
config
)
tx
.
AnnouncementRead
=
NewAnnouncementReadClient
(
tx
.
config
)
tx
.
Group
=
NewGroupClient
(
tx
.
config
)
tx
.
PromoCode
=
NewPromoCodeClient
(
tx
.
config
)
tx
.
PromoCodeUsage
=
NewPromoCodeUsageClient
(
tx
.
config
)
...
...
backend/ent/user.go
View file @
d3062b2e
...
...
@@ -61,6 +61,8 @@ type UserEdges struct {
Subscriptions
[]
*
UserSubscription
`json:"subscriptions,omitempty"`
// AssignedSubscriptions holds the value of the assigned_subscriptions edge.
AssignedSubscriptions
[]
*
UserSubscription
`json:"assigned_subscriptions,omitempty"`
// AnnouncementReads holds the value of the announcement_reads edge.
AnnouncementReads
[]
*
AnnouncementRead
`json:"announcement_reads,omitempty"`
// AllowedGroups holds the value of the allowed_groups edge.
AllowedGroups
[]
*
Group
`json:"allowed_groups,omitempty"`
// UsageLogs holds the value of the usage_logs edge.
...
...
@@ -73,7 +75,7 @@ type UserEdges struct {
UserAllowedGroups
[]
*
UserAllowedGroup
`json:"user_allowed_groups,omitempty"`
// loadedTypes holds the information for reporting if a
// type was loaded (or requested) in eager-loading or not.
loadedTypes
[
9
]
bool
loadedTypes
[
10
]
bool
}
// APIKeysOrErr returns the APIKeys value or an error if the edge
...
...
@@ -112,10 +114,19 @@ func (e UserEdges) AssignedSubscriptionsOrErr() ([]*UserSubscription, error) {
return
nil
,
&
NotLoadedError
{
edge
:
"assigned_subscriptions"
}
}
// AnnouncementReadsOrErr returns the AnnouncementReads value or an error if the edge
// was not loaded in eager-loading.
func
(
e
UserEdges
)
AnnouncementReadsOrErr
()
([]
*
AnnouncementRead
,
error
)
{
if
e
.
loadedTypes
[
4
]
{
return
e
.
AnnouncementReads
,
nil
}
return
nil
,
&
NotLoadedError
{
edge
:
"announcement_reads"
}
}
// AllowedGroupsOrErr returns the AllowedGroups value or an error if the edge
// was not loaded in eager-loading.
func
(
e
UserEdges
)
AllowedGroupsOrErr
()
([]
*
Group
,
error
)
{
if
e
.
loadedTypes
[
4
]
{
if
e
.
loadedTypes
[
5
]
{
return
e
.
AllowedGroups
,
nil
}
return
nil
,
&
NotLoadedError
{
edge
:
"allowed_groups"
}
...
...
@@ -124,7 +135,7 @@ func (e UserEdges) AllowedGroupsOrErr() ([]*Group, error) {
// UsageLogsOrErr returns the UsageLogs value or an error if the edge
// was not loaded in eager-loading.
func
(
e
UserEdges
)
UsageLogsOrErr
()
([]
*
UsageLog
,
error
)
{
if
e
.
loadedTypes
[
5
]
{
if
e
.
loadedTypes
[
6
]
{
return
e
.
UsageLogs
,
nil
}
return
nil
,
&
NotLoadedError
{
edge
:
"usage_logs"
}
...
...
@@ -133,7 +144,7 @@ func (e UserEdges) UsageLogsOrErr() ([]*UsageLog, error) {
// 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
]
{
if
e
.
loadedTypes
[
7
]
{
return
e
.
AttributeValues
,
nil
}
return
nil
,
&
NotLoadedError
{
edge
:
"attribute_values"
}
...
...
@@ -142,7 +153,7 @@ func (e UserEdges) AttributeValuesOrErr() ([]*UserAttributeValue, error) {
// PromoCodeUsagesOrErr returns the PromoCodeUsages value or an error if the edge
// was not loaded in eager-loading.
func
(
e
UserEdges
)
PromoCodeUsagesOrErr
()
([]
*
PromoCodeUsage
,
error
)
{
if
e
.
loadedTypes
[
7
]
{
if
e
.
loadedTypes
[
8
]
{
return
e
.
PromoCodeUsages
,
nil
}
return
nil
,
&
NotLoadedError
{
edge
:
"promo_code_usages"
}
...
...
@@ -151,7 +162,7 @@ func (e UserEdges) PromoCodeUsagesOrErr() ([]*PromoCodeUsage, error) {
// UserAllowedGroupsOrErr returns the UserAllowedGroups value or an error if the edge
// was not loaded in eager-loading.
func
(
e
UserEdges
)
UserAllowedGroupsOrErr
()
([]
*
UserAllowedGroup
,
error
)
{
if
e
.
loadedTypes
[
8
]
{
if
e
.
loadedTypes
[
9
]
{
return
e
.
UserAllowedGroups
,
nil
}
return
nil
,
&
NotLoadedError
{
edge
:
"user_allowed_groups"
}
...
...
@@ -313,6 +324,11 @@ func (_m *User) QueryAssignedSubscriptions() *UserSubscriptionQuery {
return
NewUserClient
(
_m
.
config
)
.
QueryAssignedSubscriptions
(
_m
)
}
// QueryAnnouncementReads queries the "announcement_reads" edge of the User entity.
func
(
_m
*
User
)
QueryAnnouncementReads
()
*
AnnouncementReadQuery
{
return
NewUserClient
(
_m
.
config
)
.
QueryAnnouncementReads
(
_m
)
}
// QueryAllowedGroups queries the "allowed_groups" edge of the User entity.
func
(
_m
*
User
)
QueryAllowedGroups
()
*
GroupQuery
{
return
NewUserClient
(
_m
.
config
)
.
QueryAllowedGroups
(
_m
)
...
...
backend/ent/user/user.go
View file @
d3062b2e
...
...
@@ -51,6 +51,8 @@ const (
EdgeSubscriptions
=
"subscriptions"
// EdgeAssignedSubscriptions holds the string denoting the assigned_subscriptions edge name in mutations.
EdgeAssignedSubscriptions
=
"assigned_subscriptions"
// EdgeAnnouncementReads holds the string denoting the announcement_reads edge name in mutations.
EdgeAnnouncementReads
=
"announcement_reads"
// EdgeAllowedGroups holds the string denoting the allowed_groups edge name in mutations.
EdgeAllowedGroups
=
"allowed_groups"
// EdgeUsageLogs holds the string denoting the usage_logs edge name in mutations.
...
...
@@ -91,6 +93,13 @@ const (
AssignedSubscriptionsInverseTable
=
"user_subscriptions"
// AssignedSubscriptionsColumn is the table column denoting the assigned_subscriptions relation/edge.
AssignedSubscriptionsColumn
=
"assigned_by"
// AnnouncementReadsTable is the table that holds the announcement_reads relation/edge.
AnnouncementReadsTable
=
"announcement_reads"
// AnnouncementReadsInverseTable is the table name for the AnnouncementRead entity.
// It exists in this package in order to avoid circular dependency with the "announcementread" package.
AnnouncementReadsInverseTable
=
"announcement_reads"
// AnnouncementReadsColumn is the table column denoting the announcement_reads relation/edge.
AnnouncementReadsColumn
=
"user_id"
// AllowedGroupsTable is the table that holds the allowed_groups relation/edge. The primary key declared below.
AllowedGroupsTable
=
"user_allowed_groups"
// AllowedGroupsInverseTable is the table name for the Group entity.
...
...
@@ -335,6 +344,20 @@ func ByAssignedSubscriptions(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOp
}
}
// ByAnnouncementReadsCount orders the results by announcement_reads count.
func
ByAnnouncementReadsCount
(
opts
...
sql
.
OrderTermOption
)
OrderOption
{
return
func
(
s
*
sql
.
Selector
)
{
sqlgraph
.
OrderByNeighborsCount
(
s
,
newAnnouncementReadsStep
(),
opts
...
)
}
}
// ByAnnouncementReads orders the results by announcement_reads terms.
func
ByAnnouncementReads
(
term
sql
.
OrderTerm
,
terms
...
sql
.
OrderTerm
)
OrderOption
{
return
func
(
s
*
sql
.
Selector
)
{
sqlgraph
.
OrderByNeighborTerms
(
s
,
newAnnouncementReadsStep
(),
append
([]
sql
.
OrderTerm
{
term
},
terms
...
)
...
)
}
}
// ByAllowedGroupsCount orders the results by allowed_groups count.
func
ByAllowedGroupsCount
(
opts
...
sql
.
OrderTermOption
)
OrderOption
{
return
func
(
s
*
sql
.
Selector
)
{
...
...
@@ -432,6 +455,13 @@ func newAssignedSubscriptionsStep() *sqlgraph.Step {
sqlgraph
.
Edge
(
sqlgraph
.
O2M
,
false
,
AssignedSubscriptionsTable
,
AssignedSubscriptionsColumn
),
)
}
func
newAnnouncementReadsStep
()
*
sqlgraph
.
Step
{
return
sqlgraph
.
NewStep
(
sqlgraph
.
From
(
Table
,
FieldID
),
sqlgraph
.
To
(
AnnouncementReadsInverseTable
,
FieldID
),
sqlgraph
.
Edge
(
sqlgraph
.
O2M
,
false
,
AnnouncementReadsTable
,
AnnouncementReadsColumn
),
)
}
func
newAllowedGroupsStep
()
*
sqlgraph
.
Step
{
return
sqlgraph
.
NewStep
(
sqlgraph
.
From
(
Table
,
FieldID
),
...
...
backend/ent/user/where.go
View file @
d3062b2e
...
...
@@ -952,6 +952,29 @@ func HasAssignedSubscriptionsWith(preds ...predicate.UserSubscription) predicate
})
}
// HasAnnouncementReads applies the HasEdge predicate on the "announcement_reads" edge.
func
HasAnnouncementReads
()
predicate
.
User
{
return
predicate
.
User
(
func
(
s
*
sql
.
Selector
)
{
step
:=
sqlgraph
.
NewStep
(
sqlgraph
.
From
(
Table
,
FieldID
),
sqlgraph
.
Edge
(
sqlgraph
.
O2M
,
false
,
AnnouncementReadsTable
,
AnnouncementReadsColumn
),
)
sqlgraph
.
HasNeighbors
(
s
,
step
)
})
}
// HasAnnouncementReadsWith applies the HasEdge predicate on the "announcement_reads" edge with a given conditions (other predicates).
func
HasAnnouncementReadsWith
(
preds
...
predicate
.
AnnouncementRead
)
predicate
.
User
{
return
predicate
.
User
(
func
(
s
*
sql
.
Selector
)
{
step
:=
newAnnouncementReadsStep
()
sqlgraph
.
HasNeighborsWith
(
s
,
step
,
func
(
s
*
sql
.
Selector
)
{
for
_
,
p
:=
range
preds
{
p
(
s
)
}
})
})
}
// HasAllowedGroups applies the HasEdge predicate on the "allowed_groups" edge.
func
HasAllowedGroups
()
predicate
.
User
{
return
predicate
.
User
(
func
(
s
*
sql
.
Selector
)
{
...
...
backend/ent/user_create.go
View file @
d3062b2e
...
...
@@ -11,6 +11,7 @@ import (
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field"
"github.com/Wei-Shaw/sub2api/ent/announcementread"
"github.com/Wei-Shaw/sub2api/ent/apikey"
"github.com/Wei-Shaw/sub2api/ent/group"
"github.com/Wei-Shaw/sub2api/ent/promocodeusage"
...
...
@@ -269,6 +270,21 @@ func (_c *UserCreate) AddAssignedSubscriptions(v ...*UserSubscription) *UserCrea
return
_c
.
AddAssignedSubscriptionIDs
(
ids
...
)
}
// AddAnnouncementReadIDs adds the "announcement_reads" edge to the AnnouncementRead entity by IDs.
func
(
_c
*
UserCreate
)
AddAnnouncementReadIDs
(
ids
...
int64
)
*
UserCreate
{
_c
.
mutation
.
AddAnnouncementReadIDs
(
ids
...
)
return
_c
}
// AddAnnouncementReads adds the "announcement_reads" edges to the AnnouncementRead entity.
func
(
_c
*
UserCreate
)
AddAnnouncementReads
(
v
...*
AnnouncementRead
)
*
UserCreate
{
ids
:=
make
([]
int64
,
len
(
v
))
for
i
:=
range
v
{
ids
[
i
]
=
v
[
i
]
.
ID
}
return
_c
.
AddAnnouncementReadIDs
(
ids
...
)
}
// AddAllowedGroupIDs adds the "allowed_groups" edge to the Group entity by IDs.
func
(
_c
*
UserCreate
)
AddAllowedGroupIDs
(
ids
...
int64
)
*
UserCreate
{
_c
.
mutation
.
AddAllowedGroupIDs
(
ids
...
)
...
...
@@ -618,6 +634,22 @@ func (_c *UserCreate) createSpec() (*User, *sqlgraph.CreateSpec) {
}
_spec
.
Edges
=
append
(
_spec
.
Edges
,
edge
)
}
if
nodes
:=
_c
.
mutation
.
AnnouncementReadsIDs
();
len
(
nodes
)
>
0
{
edge
:=
&
sqlgraph
.
EdgeSpec
{
Rel
:
sqlgraph
.
O2M
,
Inverse
:
false
,
Table
:
user
.
AnnouncementReadsTable
,
Columns
:
[]
string
{
user
.
AnnouncementReadsColumn
},
Bidi
:
false
,
Target
:
&
sqlgraph
.
EdgeTarget
{
IDSpec
:
sqlgraph
.
NewFieldSpec
(
announcementread
.
FieldID
,
field
.
TypeInt64
),
},
}
for
_
,
k
:=
range
nodes
{
edge
.
Target
.
Nodes
=
append
(
edge
.
Target
.
Nodes
,
k
)
}
_spec
.
Edges
=
append
(
_spec
.
Edges
,
edge
)
}
if
nodes
:=
_c
.
mutation
.
AllowedGroupsIDs
();
len
(
nodes
)
>
0
{
edge
:=
&
sqlgraph
.
EdgeSpec
{
Rel
:
sqlgraph
.
M2M
,
...
...
backend/ent/user_query.go
View file @
d3062b2e
...
...
@@ -13,6 +13,7 @@ import (
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field"
"github.com/Wei-Shaw/sub2api/ent/announcementread"
"github.com/Wei-Shaw/sub2api/ent/apikey"
"github.com/Wei-Shaw/sub2api/ent/group"
"github.com/Wei-Shaw/sub2api/ent/predicate"
...
...
@@ -36,6 +37,7 @@ type UserQuery struct {
withRedeemCodes
*
RedeemCodeQuery
withSubscriptions
*
UserSubscriptionQuery
withAssignedSubscriptions
*
UserSubscriptionQuery
withAnnouncementReads
*
AnnouncementReadQuery
withAllowedGroups
*
GroupQuery
withUsageLogs
*
UsageLogQuery
withAttributeValues
*
UserAttributeValueQuery
...
...
@@ -166,6 +168,28 @@ func (_q *UserQuery) QueryAssignedSubscriptions() *UserSubscriptionQuery {
return
query
}
// QueryAnnouncementReads chains the current query on the "announcement_reads" edge.
func
(
_q
*
UserQuery
)
QueryAnnouncementReads
()
*
AnnouncementReadQuery
{
query
:=
(
&
AnnouncementReadClient
{
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
(
announcementread
.
Table
,
announcementread
.
FieldID
),
sqlgraph
.
Edge
(
sqlgraph
.
O2M
,
false
,
user
.
AnnouncementReadsTable
,
user
.
AnnouncementReadsColumn
),
)
fromU
=
sqlgraph
.
SetNeighbors
(
_q
.
driver
.
Dialect
(),
step
)
return
fromU
,
nil
}
return
query
}
// QueryAllowedGroups chains the current query on the "allowed_groups" edge.
func
(
_q
*
UserQuery
)
QueryAllowedGroups
()
*
GroupQuery
{
query
:=
(
&
GroupClient
{
config
:
_q
.
config
})
.
Query
()
...
...
@@ -472,6 +496,7 @@ func (_q *UserQuery) Clone() *UserQuery {
withRedeemCodes
:
_q
.
withRedeemCodes
.
Clone
(),
withSubscriptions
:
_q
.
withSubscriptions
.
Clone
(),
withAssignedSubscriptions
:
_q
.
withAssignedSubscriptions
.
Clone
(),
withAnnouncementReads
:
_q
.
withAnnouncementReads
.
Clone
(),
withAllowedGroups
:
_q
.
withAllowedGroups
.
Clone
(),
withUsageLogs
:
_q
.
withUsageLogs
.
Clone
(),
withAttributeValues
:
_q
.
withAttributeValues
.
Clone
(),
...
...
@@ -527,6 +552,17 @@ func (_q *UserQuery) WithAssignedSubscriptions(opts ...func(*UserSubscriptionQue
return
_q
}
// WithAnnouncementReads tells the query-builder to eager-load the nodes that are connected to
// the "announcement_reads" edge. The optional arguments are used to configure the query builder of the edge.
func
(
_q
*
UserQuery
)
WithAnnouncementReads
(
opts
...
func
(
*
AnnouncementReadQuery
))
*
UserQuery
{
query
:=
(
&
AnnouncementReadClient
{
config
:
_q
.
config
})
.
Query
()
for
_
,
opt
:=
range
opts
{
opt
(
query
)
}
_q
.
withAnnouncementReads
=
query
return
_q
}
// WithAllowedGroups tells the query-builder to eager-load the nodes that are connected to
// the "allowed_groups" edge. The optional arguments are used to configure the query builder of the edge.
func
(
_q
*
UserQuery
)
WithAllowedGroups
(
opts
...
func
(
*
GroupQuery
))
*
UserQuery
{
...
...
@@ -660,11 +696,12 @@ func (_q *UserQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*User, e
var
(
nodes
=
[]
*
User
{}
_spec
=
_q
.
querySpec
()
loadedTypes
=
[
9
]
bool
{
loadedTypes
=
[
10
]
bool
{
_q
.
withAPIKeys
!=
nil
,
_q
.
withRedeemCodes
!=
nil
,
_q
.
withSubscriptions
!=
nil
,
_q
.
withAssignedSubscriptions
!=
nil
,
_q
.
withAnnouncementReads
!=
nil
,
_q
.
withAllowedGroups
!=
nil
,
_q
.
withUsageLogs
!=
nil
,
_q
.
withAttributeValues
!=
nil
,
...
...
@@ -723,6 +760,13 @@ func (_q *UserQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*User, e
return
nil
,
err
}
}
if
query
:=
_q
.
withAnnouncementReads
;
query
!=
nil
{
if
err
:=
_q
.
loadAnnouncementReads
(
ctx
,
query
,
nodes
,
func
(
n
*
User
)
{
n
.
Edges
.
AnnouncementReads
=
[]
*
AnnouncementRead
{}
},
func
(
n
*
User
,
e
*
AnnouncementRead
)
{
n
.
Edges
.
AnnouncementReads
=
append
(
n
.
Edges
.
AnnouncementReads
,
e
)
});
err
!=
nil
{
return
nil
,
err
}
}
if
query
:=
_q
.
withAllowedGroups
;
query
!=
nil
{
if
err
:=
_q
.
loadAllowedGroups
(
ctx
,
query
,
nodes
,
func
(
n
*
User
)
{
n
.
Edges
.
AllowedGroups
=
[]
*
Group
{}
},
...
...
@@ -887,6 +931,36 @@ func (_q *UserQuery) loadAssignedSubscriptions(ctx context.Context, query *UserS
}
return
nil
}
func
(
_q
*
UserQuery
)
loadAnnouncementReads
(
ctx
context
.
Context
,
query
*
AnnouncementReadQuery
,
nodes
[]
*
User
,
init
func
(
*
User
),
assign
func
(
*
User
,
*
AnnouncementRead
))
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
(
announcementread
.
FieldUserID
)
}
query
.
Where
(
predicate
.
AnnouncementRead
(
func
(
s
*
sql
.
Selector
)
{
s
.
Where
(
sql
.
InValues
(
s
.
C
(
user
.
AnnouncementReadsColumn
),
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
)
loadAllowedGroups
(
ctx
context
.
Context
,
query
*
GroupQuery
,
nodes
[]
*
User
,
init
func
(
*
User
),
assign
func
(
*
User
,
*
Group
))
error
{
edgeIDs
:=
make
([]
driver
.
Value
,
len
(
nodes
))
byID
:=
make
(
map
[
int64
]
*
User
)
...
...
backend/ent/user_update.go
View file @
d3062b2e
...
...
@@ -11,6 +11,7 @@ import (
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field"
"github.com/Wei-Shaw/sub2api/ent/announcementread"
"github.com/Wei-Shaw/sub2api/ent/apikey"
"github.com/Wei-Shaw/sub2api/ent/group"
"github.com/Wei-Shaw/sub2api/ent/predicate"
...
...
@@ -301,6 +302,21 @@ func (_u *UserUpdate) AddAssignedSubscriptions(v ...*UserSubscription) *UserUpda
return
_u
.
AddAssignedSubscriptionIDs
(
ids
...
)
}
// AddAnnouncementReadIDs adds the "announcement_reads" edge to the AnnouncementRead entity by IDs.
func
(
_u
*
UserUpdate
)
AddAnnouncementReadIDs
(
ids
...
int64
)
*
UserUpdate
{
_u
.
mutation
.
AddAnnouncementReadIDs
(
ids
...
)
return
_u
}
// AddAnnouncementReads adds the "announcement_reads" edges to the AnnouncementRead entity.
func
(
_u
*
UserUpdate
)
AddAnnouncementReads
(
v
...*
AnnouncementRead
)
*
UserUpdate
{
ids
:=
make
([]
int64
,
len
(
v
))
for
i
:=
range
v
{
ids
[
i
]
=
v
[
i
]
.
ID
}
return
_u
.
AddAnnouncementReadIDs
(
ids
...
)
}
// AddAllowedGroupIDs adds the "allowed_groups" edge to the Group entity by IDs.
func
(
_u
*
UserUpdate
)
AddAllowedGroupIDs
(
ids
...
int64
)
*
UserUpdate
{
_u
.
mutation
.
AddAllowedGroupIDs
(
ids
...
)
...
...
@@ -450,6 +466,27 @@ func (_u *UserUpdate) RemoveAssignedSubscriptions(v ...*UserSubscription) *UserU
return
_u
.
RemoveAssignedSubscriptionIDs
(
ids
...
)
}
// ClearAnnouncementReads clears all "announcement_reads" edges to the AnnouncementRead entity.
func
(
_u
*
UserUpdate
)
ClearAnnouncementReads
()
*
UserUpdate
{
_u
.
mutation
.
ClearAnnouncementReads
()
return
_u
}
// RemoveAnnouncementReadIDs removes the "announcement_reads" edge to AnnouncementRead entities by IDs.
func
(
_u
*
UserUpdate
)
RemoveAnnouncementReadIDs
(
ids
...
int64
)
*
UserUpdate
{
_u
.
mutation
.
RemoveAnnouncementReadIDs
(
ids
...
)
return
_u
}
// RemoveAnnouncementReads removes "announcement_reads" edges to AnnouncementRead entities.
func
(
_u
*
UserUpdate
)
RemoveAnnouncementReads
(
v
...*
AnnouncementRead
)
*
UserUpdate
{
ids
:=
make
([]
int64
,
len
(
v
))
for
i
:=
range
v
{
ids
[
i
]
=
v
[
i
]
.
ID
}
return
_u
.
RemoveAnnouncementReadIDs
(
ids
...
)
}
// ClearAllowedGroups clears all "allowed_groups" edges to the Group entity.
func
(
_u
*
UserUpdate
)
ClearAllowedGroups
()
*
UserUpdate
{
_u
.
mutation
.
ClearAllowedGroups
()
...
...
@@ -852,6 +889,51 @@ func (_u *UserUpdate) sqlSave(ctx context.Context) (_node int, err error) {
}
_spec
.
Edges
.
Add
=
append
(
_spec
.
Edges
.
Add
,
edge
)
}
if
_u
.
mutation
.
AnnouncementReadsCleared
()
{
edge
:=
&
sqlgraph
.
EdgeSpec
{
Rel
:
sqlgraph
.
O2M
,
Inverse
:
false
,
Table
:
user
.
AnnouncementReadsTable
,
Columns
:
[]
string
{
user
.
AnnouncementReadsColumn
},
Bidi
:
false
,
Target
:
&
sqlgraph
.
EdgeTarget
{
IDSpec
:
sqlgraph
.
NewFieldSpec
(
announcementread
.
FieldID
,
field
.
TypeInt64
),
},
}
_spec
.
Edges
.
Clear
=
append
(
_spec
.
Edges
.
Clear
,
edge
)
}
if
nodes
:=
_u
.
mutation
.
RemovedAnnouncementReadsIDs
();
len
(
nodes
)
>
0
&&
!
_u
.
mutation
.
AnnouncementReadsCleared
()
{
edge
:=
&
sqlgraph
.
EdgeSpec
{
Rel
:
sqlgraph
.
O2M
,
Inverse
:
false
,
Table
:
user
.
AnnouncementReadsTable
,
Columns
:
[]
string
{
user
.
AnnouncementReadsColumn
},
Bidi
:
false
,
Target
:
&
sqlgraph
.
EdgeTarget
{
IDSpec
:
sqlgraph
.
NewFieldSpec
(
announcementread
.
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
.
AnnouncementReadsIDs
();
len
(
nodes
)
>
0
{
edge
:=
&
sqlgraph
.
EdgeSpec
{
Rel
:
sqlgraph
.
O2M
,
Inverse
:
false
,
Table
:
user
.
AnnouncementReadsTable
,
Columns
:
[]
string
{
user
.
AnnouncementReadsColumn
},
Bidi
:
false
,
Target
:
&
sqlgraph
.
EdgeTarget
{
IDSpec
:
sqlgraph
.
NewFieldSpec
(
announcementread
.
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
.
AllowedGroupsCleared
()
{
edge
:=
&
sqlgraph
.
EdgeSpec
{
Rel
:
sqlgraph
.
M2M
,
...
...
@@ -1330,6 +1412,21 @@ func (_u *UserUpdateOne) AddAssignedSubscriptions(v ...*UserSubscription) *UserU
return
_u
.
AddAssignedSubscriptionIDs
(
ids
...
)
}
// AddAnnouncementReadIDs adds the "announcement_reads" edge to the AnnouncementRead entity by IDs.
func
(
_u
*
UserUpdateOne
)
AddAnnouncementReadIDs
(
ids
...
int64
)
*
UserUpdateOne
{
_u
.
mutation
.
AddAnnouncementReadIDs
(
ids
...
)
return
_u
}
// AddAnnouncementReads adds the "announcement_reads" edges to the AnnouncementRead entity.
func
(
_u
*
UserUpdateOne
)
AddAnnouncementReads
(
v
...*
AnnouncementRead
)
*
UserUpdateOne
{
ids
:=
make
([]
int64
,
len
(
v
))
for
i
:=
range
v
{
ids
[
i
]
=
v
[
i
]
.
ID
}
return
_u
.
AddAnnouncementReadIDs
(
ids
...
)
}
// AddAllowedGroupIDs adds the "allowed_groups" edge to the Group entity by IDs.
func
(
_u
*
UserUpdateOne
)
AddAllowedGroupIDs
(
ids
...
int64
)
*
UserUpdateOne
{
_u
.
mutation
.
AddAllowedGroupIDs
(
ids
...
)
...
...
@@ -1479,6 +1576,27 @@ func (_u *UserUpdateOne) RemoveAssignedSubscriptions(v ...*UserSubscription) *Us
return
_u
.
RemoveAssignedSubscriptionIDs
(
ids
...
)
}
// ClearAnnouncementReads clears all "announcement_reads" edges to the AnnouncementRead entity.
func
(
_u
*
UserUpdateOne
)
ClearAnnouncementReads
()
*
UserUpdateOne
{
_u
.
mutation
.
ClearAnnouncementReads
()
return
_u
}
// RemoveAnnouncementReadIDs removes the "announcement_reads" edge to AnnouncementRead entities by IDs.
func
(
_u
*
UserUpdateOne
)
RemoveAnnouncementReadIDs
(
ids
...
int64
)
*
UserUpdateOne
{
_u
.
mutation
.
RemoveAnnouncementReadIDs
(
ids
...
)
return
_u
}
// RemoveAnnouncementReads removes "announcement_reads" edges to AnnouncementRead entities.
func
(
_u
*
UserUpdateOne
)
RemoveAnnouncementReads
(
v
...*
AnnouncementRead
)
*
UserUpdateOne
{
ids
:=
make
([]
int64
,
len
(
v
))
for
i
:=
range
v
{
ids
[
i
]
=
v
[
i
]
.
ID
}
return
_u
.
RemoveAnnouncementReadIDs
(
ids
...
)
}
// ClearAllowedGroups clears all "allowed_groups" edges to the Group entity.
func
(
_u
*
UserUpdateOne
)
ClearAllowedGroups
()
*
UserUpdateOne
{
_u
.
mutation
.
ClearAllowedGroups
()
...
...
@@ -1911,6 +2029,51 @@ func (_u *UserUpdateOne) sqlSave(ctx context.Context) (_node *User, err error) {
}
_spec
.
Edges
.
Add
=
append
(
_spec
.
Edges
.
Add
,
edge
)
}
if
_u
.
mutation
.
AnnouncementReadsCleared
()
{
edge
:=
&
sqlgraph
.
EdgeSpec
{
Rel
:
sqlgraph
.
O2M
,
Inverse
:
false
,
Table
:
user
.
AnnouncementReadsTable
,
Columns
:
[]
string
{
user
.
AnnouncementReadsColumn
},
Bidi
:
false
,
Target
:
&
sqlgraph
.
EdgeTarget
{
IDSpec
:
sqlgraph
.
NewFieldSpec
(
announcementread
.
FieldID
,
field
.
TypeInt64
),
},
}
_spec
.
Edges
.
Clear
=
append
(
_spec
.
Edges
.
Clear
,
edge
)
}
if
nodes
:=
_u
.
mutation
.
RemovedAnnouncementReadsIDs
();
len
(
nodes
)
>
0
&&
!
_u
.
mutation
.
AnnouncementReadsCleared
()
{
edge
:=
&
sqlgraph
.
EdgeSpec
{
Rel
:
sqlgraph
.
O2M
,
Inverse
:
false
,
Table
:
user
.
AnnouncementReadsTable
,
Columns
:
[]
string
{
user
.
AnnouncementReadsColumn
},
Bidi
:
false
,
Target
:
&
sqlgraph
.
EdgeTarget
{
IDSpec
:
sqlgraph
.
NewFieldSpec
(
announcementread
.
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
.
AnnouncementReadsIDs
();
len
(
nodes
)
>
0
{
edge
:=
&
sqlgraph
.
EdgeSpec
{
Rel
:
sqlgraph
.
O2M
,
Inverse
:
false
,
Table
:
user
.
AnnouncementReadsTable
,
Columns
:
[]
string
{
user
.
AnnouncementReadsColumn
},
Bidi
:
false
,
Target
:
&
sqlgraph
.
EdgeTarget
{
IDSpec
:
sqlgraph
.
NewFieldSpec
(
announcementread
.
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
.
AllowedGroupsCleared
()
{
edge
:=
&
sqlgraph
.
EdgeSpec
{
Rel
:
sqlgraph
.
M2M
,
...
...
backend/internal/domain/announcement.go
0 → 100644
View file @
d3062b2e
package
domain
import
(
"strings"
"time"
infraerrors
"github.com/Wei-Shaw/sub2api/internal/pkg/errors"
)
const
(
AnnouncementStatusDraft
=
"draft"
AnnouncementStatusActive
=
"active"
AnnouncementStatusArchived
=
"archived"
)
const
(
AnnouncementConditionTypeSubscription
=
"subscription"
AnnouncementConditionTypeBalance
=
"balance"
)
const
(
AnnouncementOperatorIn
=
"in"
AnnouncementOperatorGT
=
"gt"
AnnouncementOperatorGTE
=
"gte"
AnnouncementOperatorLT
=
"lt"
AnnouncementOperatorLTE
=
"lte"
AnnouncementOperatorEQ
=
"eq"
)
var
(
ErrAnnouncementNotFound
=
infraerrors
.
NotFound
(
"ANNOUNCEMENT_NOT_FOUND"
,
"announcement not found"
)
ErrAnnouncementInvalidTarget
=
infraerrors
.
BadRequest
(
"ANNOUNCEMENT_INVALID_TARGET"
,
"invalid announcement targeting rules"
)
)
type
AnnouncementTargeting
struct
{
// AnyOf 表示 OR:任意一个条件组满足即可展示。
AnyOf
[]
AnnouncementConditionGroup
`json:"any_of,omitempty"`
}
type
AnnouncementConditionGroup
struct
{
// AllOf 表示 AND:组内所有条件都满足才算命中该组。
AllOf
[]
AnnouncementCondition
`json:"all_of,omitempty"`
}
type
AnnouncementCondition
struct
{
// Type: subscription | balance
Type
string
`json:"type"`
// Operator:
// - subscription: in
// - balance: gt/gte/lt/lte/eq
Operator
string
`json:"operator"`
// subscription 条件:匹配的订阅套餐(group_id)
GroupIDs
[]
int64
`json:"group_ids,omitempty"`
// balance 条件:比较阈值
Value
float64
`json:"value,omitempty"`
}
func
(
t
AnnouncementTargeting
)
Matches
(
balance
float64
,
activeSubscriptionGroupIDs
map
[
int64
]
struct
{})
bool
{
// 空规则:展示给所有用户
if
len
(
t
.
AnyOf
)
==
0
{
return
true
}
for
_
,
group
:=
range
t
.
AnyOf
{
if
len
(
group
.
AllOf
)
==
0
{
// 空条件组不命中(避免 OR 中出现无条件 “全命中”)
continue
}
allMatched
:=
true
for
_
,
cond
:=
range
group
.
AllOf
{
if
!
cond
.
Matches
(
balance
,
activeSubscriptionGroupIDs
)
{
allMatched
=
false
break
}
}
if
allMatched
{
return
true
}
}
return
false
}
func
(
c
AnnouncementCondition
)
Matches
(
balance
float64
,
activeSubscriptionGroupIDs
map
[
int64
]
struct
{})
bool
{
switch
c
.
Type
{
case
AnnouncementConditionTypeSubscription
:
if
c
.
Operator
!=
AnnouncementOperatorIn
{
return
false
}
if
len
(
c
.
GroupIDs
)
==
0
{
return
false
}
if
len
(
activeSubscriptionGroupIDs
)
==
0
{
return
false
}
for
_
,
gid
:=
range
c
.
GroupIDs
{
if
_
,
ok
:=
activeSubscriptionGroupIDs
[
gid
];
ok
{
return
true
}
}
return
false
case
AnnouncementConditionTypeBalance
:
switch
c
.
Operator
{
case
AnnouncementOperatorGT
:
return
balance
>
c
.
Value
case
AnnouncementOperatorGTE
:
return
balance
>=
c
.
Value
case
AnnouncementOperatorLT
:
return
balance
<
c
.
Value
case
AnnouncementOperatorLTE
:
return
balance
<=
c
.
Value
case
AnnouncementOperatorEQ
:
return
balance
==
c
.
Value
default
:
return
false
}
default
:
return
false
}
}
func
(
t
AnnouncementTargeting
)
NormalizeAndValidate
()
(
AnnouncementTargeting
,
error
)
{
normalized
:=
AnnouncementTargeting
{
AnyOf
:
make
([]
AnnouncementConditionGroup
,
0
,
len
(
t
.
AnyOf
))}
// 允许空 targeting(展示给所有用户)
if
len
(
t
.
AnyOf
)
==
0
{
return
normalized
,
nil
}
if
len
(
t
.
AnyOf
)
>
50
{
return
AnnouncementTargeting
{},
ErrAnnouncementInvalidTarget
}
for
_
,
g
:=
range
t
.
AnyOf
{
if
len
(
g
.
AllOf
)
==
0
{
return
AnnouncementTargeting
{},
ErrAnnouncementInvalidTarget
}
if
len
(
g
.
AllOf
)
>
50
{
return
AnnouncementTargeting
{},
ErrAnnouncementInvalidTarget
}
group
:=
AnnouncementConditionGroup
{
AllOf
:
make
([]
AnnouncementCondition
,
0
,
len
(
g
.
AllOf
))}
for
_
,
c
:=
range
g
.
AllOf
{
cond
:=
AnnouncementCondition
{
Type
:
strings
.
TrimSpace
(
c
.
Type
),
Operator
:
strings
.
TrimSpace
(
c
.
Operator
),
Value
:
c
.
Value
,
}
for
_
,
gid
:=
range
c
.
GroupIDs
{
if
gid
<=
0
{
return
AnnouncementTargeting
{},
ErrAnnouncementInvalidTarget
}
cond
.
GroupIDs
=
append
(
cond
.
GroupIDs
,
gid
)
}
if
err
:=
cond
.
validate
();
err
!=
nil
{
return
AnnouncementTargeting
{},
err
}
group
.
AllOf
=
append
(
group
.
AllOf
,
cond
)
}
normalized
.
AnyOf
=
append
(
normalized
.
AnyOf
,
group
)
}
return
normalized
,
nil
}
func
(
c
AnnouncementCondition
)
validate
()
error
{
switch
c
.
Type
{
case
AnnouncementConditionTypeSubscription
:
if
c
.
Operator
!=
AnnouncementOperatorIn
{
return
ErrAnnouncementInvalidTarget
}
if
len
(
c
.
GroupIDs
)
==
0
{
return
ErrAnnouncementInvalidTarget
}
return
nil
case
AnnouncementConditionTypeBalance
:
switch
c
.
Operator
{
case
AnnouncementOperatorGT
,
AnnouncementOperatorGTE
,
AnnouncementOperatorLT
,
AnnouncementOperatorLTE
,
AnnouncementOperatorEQ
:
return
nil
default
:
return
ErrAnnouncementInvalidTarget
}
default
:
return
ErrAnnouncementInvalidTarget
}
}
type
Announcement
struct
{
ID
int64
Title
string
Content
string
Status
string
Targeting
AnnouncementTargeting
StartsAt
*
time
.
Time
EndsAt
*
time
.
Time
CreatedBy
*
int64
UpdatedBy
*
int64
CreatedAt
time
.
Time
UpdatedAt
time
.
Time
}
func
(
a
*
Announcement
)
IsActiveAt
(
now
time
.
Time
)
bool
{
if
a
==
nil
{
return
false
}
if
a
.
Status
!=
AnnouncementStatusActive
{
return
false
}
if
a
.
StartsAt
!=
nil
&&
now
.
Before
(
*
a
.
StartsAt
)
{
return
false
}
if
a
.
EndsAt
!=
nil
&&
!
now
.
Before
(
*
a
.
EndsAt
)
{
// ends_at 语义:到点即下线
return
false
}
return
true
}
Prev
1
2
3
4
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment