Browse Source

fix:submit token/check API

jimmyyem 7 months ago
parent
commit
d63c131c9d

+ 2 - 1
desc/all.api

@@ -17,4 +17,5 @@ import "./wechat/batch_msg.api"
 import "./wechat/agent.api"
 import "./wechat/employee.api"
 import "./wechat/work_experience.api"
-import "./wechat/tutorial.api"
+import "./wechat/tutorial.api"
+import "./wechat/token.api"

+ 101 - 0
desc/wechat/token.api

@@ -0,0 +1,101 @@
+import "../base.api"
+
+type (
+    // The data of token information | Token信息
+    TokenInfo {
+        BaseIDInfo
+
+        // 过期时间 
+        ExpireAt  *int64 `json:"expireAt,optional"`
+
+        // Token 
+        Token  *string `json:"token,optional"`
+
+        // Mac地址 
+        Mac  *string `json:"mac,optional"`
+    }
+
+    // The response data of token list | Token列表数据
+    TokenListResp {
+        BaseDataInfo
+
+        // Token list data | Token列表数据
+        Data TokenListInfo `json:"data"`
+    }
+
+    // Token list data | Token列表数据
+    TokenListInfo {
+        BaseListInfo
+
+        // The API list data | Token列表数据
+        Data  []TokenInfo  `json:"data"`
+    }
+
+    // Get token list request params | Token列表请求参数
+    TokenListReq {
+        PageInfo
+
+        // Token 
+        Token  *string `json:"token,optional"`
+
+        // Mac地址 
+        Mac  *string `json:"mac,optional"`
+    }
+
+    // Token information response | Token信息返回体
+    TokenInfoResp {
+        BaseDataInfo
+
+        // Token information | Token数据
+        Data TokenInfo `json:"data"`
+    }
+
+	CheckTokenReq {
+		// Token
+		Token  *string `json:"token"`
+
+		// Mac地址
+		Mac  *string `json:"mac"`
+	}
+
+	CheckTokenResp {
+		// 是否合法
+		Valid *bool `json:"valid"`
+
+		// Sign 签名内容
+		Sign  *string `json:"sign"`
+
+		// Timestamp 时间戳
+		Timestamp  *int64 `json:"timestamp"`
+	}
+)
+
+@server(
+    group: token
+)
+
+service Wechat {
+    // Create token information | 创建Token
+    @handler createToken
+    post /token/create (TokenInfo) returns (BaseMsgResp)
+
+    // Update token information | 更新Token
+    @handler updateToken
+    post /token/update (TokenInfo) returns (BaseMsgResp)
+
+    // Delete token information | 删除Token信息
+    @handler deleteToken
+    post /token/delete (IDsReq) returns (BaseMsgResp)
+
+    // Get token list | 获取Token列表
+    @handler getTokenList
+    post /token/list (TokenListReq) returns (TokenListResp)
+
+    // Get token by ID | 通过ID获取Token
+    @handler getTokenById
+    post /token (IDReq) returns (TokenInfoResp)
+
+	// Check if token and mac are valid | 检查token和mac的合法性
+	@handler checkToken
+	post /token/check (CheckTokenReq) returns (CheckTokenResp)
+}

+ 147 - 4
ent/client.go

@@ -24,6 +24,7 @@ import (
 	"wechat-api/ent/sopnode"
 	"wechat-api/ent/sopstage"
 	"wechat-api/ent/soptask"
+	"wechat-api/ent/token"
 	"wechat-api/ent/tutorial"
 	"wechat-api/ent/workexperience"
 	"wechat-api/ent/wx"
@@ -67,6 +68,8 @@ type Client struct {
 	SopStage *SopStageClient
 	// SopTask is the client for interacting with the SopTask builders.
 	SopTask *SopTaskClient
+	// Token is the client for interacting with the Token builders.
+	Token *TokenClient
 	// Tutorial is the client for interacting with the Tutorial builders.
 	Tutorial *TutorialClient
 	// WorkExperience is the client for interacting with the WorkExperience builders.
@@ -97,6 +100,7 @@ func (c *Client) init() {
 	c.SopNode = NewSopNodeClient(c.config)
 	c.SopStage = NewSopStageClient(c.config)
 	c.SopTask = NewSopTaskClient(c.config)
+	c.Token = NewTokenClient(c.config)
 	c.Tutorial = NewTutorialClient(c.config)
 	c.WorkExperience = NewWorkExperienceClient(c.config)
 	c.Wx = NewWxClient(c.config)
@@ -205,6 +209,7 @@ func (c *Client) Tx(ctx context.Context) (*Tx, error) {
 		SopNode:           NewSopNodeClient(cfg),
 		SopStage:          NewSopStageClient(cfg),
 		SopTask:           NewSopTaskClient(cfg),
+		Token:             NewTokenClient(cfg),
 		Tutorial:          NewTutorialClient(cfg),
 		WorkExperience:    NewWorkExperienceClient(cfg),
 		Wx:                NewWxClient(cfg),
@@ -240,6 +245,7 @@ func (c *Client) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error)
 		SopNode:           NewSopNodeClient(cfg),
 		SopStage:          NewSopStageClient(cfg),
 		SopTask:           NewSopTaskClient(cfg),
+		Token:             NewTokenClient(cfg),
 		Tutorial:          NewTutorialClient(cfg),
 		WorkExperience:    NewWorkExperienceClient(cfg),
 		Wx:                NewWxClient(cfg),
@@ -274,7 +280,7 @@ func (c *Client) Use(hooks ...Hook) {
 	for _, n := range []interface{ Use(...Hook) }{
 		c.Agent, c.BatchMsg, c.Contact, c.Employee, c.Label, c.LabelRelationship,
 		c.Message, c.MessageRecords, c.Msg, c.Server, c.SopNode, c.SopStage, c.SopTask,
-		c.Tutorial, c.WorkExperience, c.Wx,
+		c.Token, c.Tutorial, c.WorkExperience, c.Wx,
 	} {
 		n.Use(hooks...)
 	}
@@ -286,7 +292,7 @@ func (c *Client) Intercept(interceptors ...Interceptor) {
 	for _, n := range []interface{ Intercept(...Interceptor) }{
 		c.Agent, c.BatchMsg, c.Contact, c.Employee, c.Label, c.LabelRelationship,
 		c.Message, c.MessageRecords, c.Msg, c.Server, c.SopNode, c.SopStage, c.SopTask,
-		c.Tutorial, c.WorkExperience, c.Wx,
+		c.Token, c.Tutorial, c.WorkExperience, c.Wx,
 	} {
 		n.Intercept(interceptors...)
 	}
@@ -321,6 +327,8 @@ func (c *Client) Mutate(ctx context.Context, m Mutation) (Value, error) {
 		return c.SopStage.mutate(ctx, m)
 	case *SopTaskMutation:
 		return c.SopTask.mutate(ctx, m)
+	case *TokenMutation:
+		return c.Token.mutate(ctx, m)
 	case *TutorialMutation:
 		return c.Tutorial.mutate(ctx, m)
 	case *WorkExperienceMutation:
@@ -2371,6 +2379,141 @@ func (c *SopTaskClient) mutate(ctx context.Context, m *SopTaskMutation) (Value,
 	}
 }
 
+// TokenClient is a client for the Token schema.
+type TokenClient struct {
+	config
+}
+
+// NewTokenClient returns a client for the Token from the given config.
+func NewTokenClient(c config) *TokenClient {
+	return &TokenClient{config: c}
+}
+
+// Use adds a list of mutation hooks to the hooks stack.
+// A call to `Use(f, g, h)` equals to `token.Hooks(f(g(h())))`.
+func (c *TokenClient) Use(hooks ...Hook) {
+	c.hooks.Token = append(c.hooks.Token, hooks...)
+}
+
+// Intercept adds a list of query interceptors to the interceptors stack.
+// A call to `Intercept(f, g, h)` equals to `token.Intercept(f(g(h())))`.
+func (c *TokenClient) Intercept(interceptors ...Interceptor) {
+	c.inters.Token = append(c.inters.Token, interceptors...)
+}
+
+// Create returns a builder for creating a Token entity.
+func (c *TokenClient) Create() *TokenCreate {
+	mutation := newTokenMutation(c.config, OpCreate)
+	return &TokenCreate{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// CreateBulk returns a builder for creating a bulk of Token entities.
+func (c *TokenClient) CreateBulk(builders ...*TokenCreate) *TokenCreateBulk {
+	return &TokenCreateBulk{config: c.config, builders: builders}
+}
+
+// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates
+// a builder and applies setFunc on it.
+func (c *TokenClient) MapCreateBulk(slice any, setFunc func(*TokenCreate, int)) *TokenCreateBulk {
+	rv := reflect.ValueOf(slice)
+	if rv.Kind() != reflect.Slice {
+		return &TokenCreateBulk{err: fmt.Errorf("calling to TokenClient.MapCreateBulk with wrong type %T, need slice", slice)}
+	}
+	builders := make([]*TokenCreate, rv.Len())
+	for i := 0; i < rv.Len(); i++ {
+		builders[i] = c.Create()
+		setFunc(builders[i], i)
+	}
+	return &TokenCreateBulk{config: c.config, builders: builders}
+}
+
+// Update returns an update builder for Token.
+func (c *TokenClient) Update() *TokenUpdate {
+	mutation := newTokenMutation(c.config, OpUpdate)
+	return &TokenUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// UpdateOne returns an update builder for the given entity.
+func (c *TokenClient) UpdateOne(t *Token) *TokenUpdateOne {
+	mutation := newTokenMutation(c.config, OpUpdateOne, withToken(t))
+	return &TokenUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// UpdateOneID returns an update builder for the given id.
+func (c *TokenClient) UpdateOneID(id uint64) *TokenUpdateOne {
+	mutation := newTokenMutation(c.config, OpUpdateOne, withTokenID(id))
+	return &TokenUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// Delete returns a delete builder for Token.
+func (c *TokenClient) Delete() *TokenDelete {
+	mutation := newTokenMutation(c.config, OpDelete)
+	return &TokenDelete{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// DeleteOne returns a builder for deleting the given entity.
+func (c *TokenClient) DeleteOne(t *Token) *TokenDeleteOne {
+	return c.DeleteOneID(t.ID)
+}
+
+// DeleteOneID returns a builder for deleting the given entity by its id.
+func (c *TokenClient) DeleteOneID(id uint64) *TokenDeleteOne {
+	builder := c.Delete().Where(token.ID(id))
+	builder.mutation.id = &id
+	builder.mutation.op = OpDeleteOne
+	return &TokenDeleteOne{builder}
+}
+
+// Query returns a query builder for Token.
+func (c *TokenClient) Query() *TokenQuery {
+	return &TokenQuery{
+		config: c.config,
+		ctx:    &QueryContext{Type: TypeToken},
+		inters: c.Interceptors(),
+	}
+}
+
+// Get returns a Token entity by its id.
+func (c *TokenClient) Get(ctx context.Context, id uint64) (*Token, error) {
+	return c.Query().Where(token.ID(id)).Only(ctx)
+}
+
+// GetX is like Get, but panics if an error occurs.
+func (c *TokenClient) GetX(ctx context.Context, id uint64) *Token {
+	obj, err := c.Get(ctx, id)
+	if err != nil {
+		panic(err)
+	}
+	return obj
+}
+
+// Hooks returns the client hooks.
+func (c *TokenClient) Hooks() []Hook {
+	hooks := c.hooks.Token
+	return append(hooks[:len(hooks):len(hooks)], token.Hooks[:]...)
+}
+
+// Interceptors returns the client interceptors.
+func (c *TokenClient) Interceptors() []Interceptor {
+	inters := c.inters.Token
+	return append(inters[:len(inters):len(inters)], token.Interceptors[:]...)
+}
+
+func (c *TokenClient) mutate(ctx context.Context, m *TokenMutation) (Value, error) {
+	switch m.Op() {
+	case OpCreate:
+		return (&TokenCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
+	case OpUpdate:
+		return (&TokenUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
+	case OpUpdateOne:
+		return (&TokenUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
+	case OpDelete, OpDeleteOne:
+		return (&TokenDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx)
+	default:
+		return nil, fmt.Errorf("ent: unknown Token mutation op: %q", m.Op())
+	}
+}
+
 // TutorialClient is a client for the Tutorial schema.
 type TutorialClient struct {
 	config
@@ -2844,12 +2987,12 @@ func (c *WxClient) mutate(ctx context.Context, m *WxMutation) (Value, error) {
 type (
 	hooks struct {
 		Agent, BatchMsg, Contact, Employee, Label, LabelRelationship, Message,
-		MessageRecords, Msg, Server, SopNode, SopStage, SopTask, Tutorial,
+		MessageRecords, Msg, Server, SopNode, SopStage, SopTask, Token, Tutorial,
 		WorkExperience, Wx []ent.Hook
 	}
 	inters struct {
 		Agent, BatchMsg, Contact, Employee, Label, LabelRelationship, Message,
-		MessageRecords, Msg, Server, SopNode, SopStage, SopTask, Tutorial,
+		MessageRecords, Msg, Server, SopNode, SopStage, SopTask, Token, Tutorial,
 		WorkExperience, Wx []ent.Interceptor
 	}
 )

+ 2 - 0
ent/ent.go

@@ -21,6 +21,7 @@ import (
 	"wechat-api/ent/sopnode"
 	"wechat-api/ent/sopstage"
 	"wechat-api/ent/soptask"
+	"wechat-api/ent/token"
 	"wechat-api/ent/tutorial"
 	"wechat-api/ent/workexperience"
 	"wechat-api/ent/wx"
@@ -101,6 +102,7 @@ func checkColumn(table, column string) error {
 			sopnode.Table:           sopnode.ValidColumn,
 			sopstage.Table:          sopstage.ValidColumn,
 			soptask.Table:           soptask.ValidColumn,
+			token.Table:             token.ValidColumn,
 			tutorial.Table:          tutorial.ValidColumn,
 			workexperience.Table:    workexperience.ValidColumn,
 			wx.Table:                wx.ValidColumn,

+ 12 - 0
ent/hook/hook.go

@@ -164,6 +164,18 @@ func (f SopTaskFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, err
 	return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.SopTaskMutation", m)
 }
 
+// The TokenFunc type is an adapter to allow the use of ordinary
+// function as Token mutator.
+type TokenFunc func(context.Context, *ent.TokenMutation) (ent.Value, error)
+
+// Mutate calls f(ctx, m).
+func (f TokenFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) {
+	if mv, ok := m.(*ent.TokenMutation); ok {
+		return f(ctx, mv)
+	}
+	return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.TokenMutation", m)
+}
+
 // The TutorialFunc type is an adapter to allow the use of ordinary
 // function as Tutorial mutator.
 type TutorialFunc func(context.Context, *ent.TutorialMutation) (ent.Value, error)

+ 30 - 0
ent/intercept/intercept.go

@@ -20,6 +20,7 @@ import (
 	"wechat-api/ent/sopnode"
 	"wechat-api/ent/sopstage"
 	"wechat-api/ent/soptask"
+	"wechat-api/ent/token"
 	"wechat-api/ent/tutorial"
 	"wechat-api/ent/workexperience"
 	"wechat-api/ent/wx"
@@ -434,6 +435,33 @@ func (f TraverseSopTask) Traverse(ctx context.Context, q ent.Query) error {
 	return fmt.Errorf("unexpected query type %T. expect *ent.SopTaskQuery", q)
 }
 
+// The TokenFunc type is an adapter to allow the use of ordinary function as a Querier.
+type TokenFunc func(context.Context, *ent.TokenQuery) (ent.Value, error)
+
+// Query calls f(ctx, q).
+func (f TokenFunc) Query(ctx context.Context, q ent.Query) (ent.Value, error) {
+	if q, ok := q.(*ent.TokenQuery); ok {
+		return f(ctx, q)
+	}
+	return nil, fmt.Errorf("unexpected query type %T. expect *ent.TokenQuery", q)
+}
+
+// The TraverseToken type is an adapter to allow the use of ordinary function as Traverser.
+type TraverseToken func(context.Context, *ent.TokenQuery) error
+
+// Intercept is a dummy implementation of Intercept that returns the next Querier in the pipeline.
+func (f TraverseToken) Intercept(next ent.Querier) ent.Querier {
+	return next
+}
+
+// Traverse calls f(ctx, q).
+func (f TraverseToken) Traverse(ctx context.Context, q ent.Query) error {
+	if q, ok := q.(*ent.TokenQuery); ok {
+		return f(ctx, q)
+	}
+	return fmt.Errorf("unexpected query type %T. expect *ent.TokenQuery", q)
+}
+
 // The TutorialFunc type is an adapter to allow the use of ordinary function as a Querier.
 type TutorialFunc func(context.Context, *ent.TutorialQuery) (ent.Value, error)
 
@@ -544,6 +572,8 @@ func NewQuery(q ent.Query) (Query, error) {
 		return &query[*ent.SopStageQuery, predicate.SopStage, sopstage.OrderOption]{typ: ent.TypeSopStage, tq: q}, nil
 	case *ent.SopTaskQuery:
 		return &query[*ent.SopTaskQuery, predicate.SopTask, soptask.OrderOption]{typ: ent.TypeSopTask, tq: q}, nil
+	case *ent.TokenQuery:
+		return &query[*ent.TokenQuery, predicate.Token, token.OrderOption]{typ: ent.TypeToken, tq: q}, nil
 	case *ent.TutorialQuery:
 		return &query[*ent.TutorialQuery, predicate.Tutorial, tutorial.OrderOption]{typ: ent.TypeTutorial, tq: q}, nil
 	case *ent.WorkExperienceQuery:

+ 27 - 0
ent/migrate/schema.go

@@ -468,6 +468,29 @@ var (
 			},
 		},
 	}
+	// TokenColumns holds the columns for the "token" table.
+	TokenColumns = []*schema.Column{
+		{Name: "id", Type: field.TypeUint64, Increment: true},
+		{Name: "created_at", Type: field.TypeTime, Comment: "Create Time | 创建日期"},
+		{Name: "updated_at", Type: field.TypeTime, Comment: "Update Time | 修改日期"},
+		{Name: "deleted_at", Type: field.TypeTime, Nullable: true, Comment: "Delete Time | 删除日期"},
+		{Name: "expire_at", Type: field.TypeTime, Nullable: true, Comment: "过期时间"},
+		{Name: "token", Type: field.TypeString, Nullable: true, Comment: "Token", Default: ""},
+		{Name: "mac", Type: field.TypeString, Comment: "Mac地址", Default: ""},
+	}
+	// TokenTable holds the schema information for the "token" table.
+	TokenTable = &schema.Table{
+		Name:       "token",
+		Columns:    TokenColumns,
+		PrimaryKey: []*schema.Column{TokenColumns[0]},
+		Indexes: []*schema.Index{
+			{
+				Name:    "token_token",
+				Unique:  false,
+				Columns: []*schema.Column{TokenColumns[5]},
+			},
+		},
+	}
 	// TutorialColumns holds the columns for the "tutorial" table.
 	TutorialColumns = []*schema.Column{
 		{Name: "id", Type: field.TypeUint64, Increment: true},
@@ -616,6 +639,7 @@ var (
 		SopNodeTable,
 		SopStageTable,
 		SopTaskTable,
+		TokenTable,
 		TutorialTable,
 		WorkExperienceTable,
 		WxTable,
@@ -669,6 +693,9 @@ func init() {
 	SopTaskTable.Annotation = &entsql.Annotation{
 		Table: "sop_task",
 	}
+	TokenTable.Annotation = &entsql.Annotation{
+		Table: "token",
+	}
 	TutorialTable.ForeignKeys[0].RefTable = EmployeeTable
 	TutorialTable.Annotation = &entsql.Annotation{
 		Table: "tutorial",

+ 664 - 0
ent/mutation.go

@@ -23,6 +23,7 @@ import (
 	"wechat-api/ent/sopnode"
 	"wechat-api/ent/sopstage"
 	"wechat-api/ent/soptask"
+	"wechat-api/ent/token"
 	"wechat-api/ent/tutorial"
 	"wechat-api/ent/workexperience"
 	"wechat-api/ent/wx"
@@ -53,6 +54,7 @@ const (
 	TypeSopNode           = "SopNode"
 	TypeSopStage          = "SopStage"
 	TypeSopTask           = "SopTask"
+	TypeToken             = "Token"
 	TypeTutorial          = "Tutorial"
 	TypeWorkExperience    = "WorkExperience"
 	TypeWx                = "Wx"
@@ -16058,6 +16060,668 @@ func (m *SopTaskMutation) ResetEdge(name string) error {
 	return fmt.Errorf("unknown SopTask edge %s", name)
 }
 
+// TokenMutation represents an operation that mutates the Token nodes in the graph.
+type TokenMutation struct {
+	config
+	op            Op
+	typ           string
+	id            *uint64
+	created_at    *time.Time
+	updated_at    *time.Time
+	deleted_at    *time.Time
+	expire_at     *time.Time
+	token         *string
+	mac           *string
+	clearedFields map[string]struct{}
+	done          bool
+	oldValue      func(context.Context) (*Token, error)
+	predicates    []predicate.Token
+}
+
+var _ ent.Mutation = (*TokenMutation)(nil)
+
+// tokenOption allows management of the mutation configuration using functional options.
+type tokenOption func(*TokenMutation)
+
+// newTokenMutation creates new mutation for the Token entity.
+func newTokenMutation(c config, op Op, opts ...tokenOption) *TokenMutation {
+	m := &TokenMutation{
+		config:        c,
+		op:            op,
+		typ:           TypeToken,
+		clearedFields: make(map[string]struct{}),
+	}
+	for _, opt := range opts {
+		opt(m)
+	}
+	return m
+}
+
+// withTokenID sets the ID field of the mutation.
+func withTokenID(id uint64) tokenOption {
+	return func(m *TokenMutation) {
+		var (
+			err   error
+			once  sync.Once
+			value *Token
+		)
+		m.oldValue = func(ctx context.Context) (*Token, error) {
+			once.Do(func() {
+				if m.done {
+					err = errors.New("querying old values post mutation is not allowed")
+				} else {
+					value, err = m.Client().Token.Get(ctx, id)
+				}
+			})
+			return value, err
+		}
+		m.id = &id
+	}
+}
+
+// withToken sets the old Token of the mutation.
+func withToken(node *Token) tokenOption {
+	return func(m *TokenMutation) {
+		m.oldValue = func(context.Context) (*Token, 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 TokenMutation) 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 TokenMutation) 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
+}
+
+// SetID sets the value of the id field. Note that this
+// operation is only accepted on creation of Token entities.
+func (m *TokenMutation) SetID(id uint64) {
+	m.id = &id
+}
+
+// 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 *TokenMutation) ID() (id uint64, 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 *TokenMutation) IDs(ctx context.Context) ([]uint64, error) {
+	switch {
+	case m.op.Is(OpUpdateOne | OpDeleteOne):
+		id, exists := m.ID()
+		if exists {
+			return []uint64{id}, nil
+		}
+		fallthrough
+	case m.op.Is(OpUpdate | OpDelete):
+		return m.Client().Token.Query().Where(m.predicates...).IDs(ctx)
+	default:
+		return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op)
+	}
+}
+
+// SetCreatedAt sets the "created_at" field.
+func (m *TokenMutation) SetCreatedAt(t time.Time) {
+	m.created_at = &t
+}
+
+// CreatedAt returns the value of the "created_at" field in the mutation.
+func (m *TokenMutation) 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 Token entity.
+// If the Token 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 *TokenMutation) 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 *TokenMutation) ResetCreatedAt() {
+	m.created_at = nil
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (m *TokenMutation) SetUpdatedAt(t time.Time) {
+	m.updated_at = &t
+}
+
+// UpdatedAt returns the value of the "updated_at" field in the mutation.
+func (m *TokenMutation) 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 Token entity.
+// If the Token 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 *TokenMutation) 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 *TokenMutation) ResetUpdatedAt() {
+	m.updated_at = nil
+}
+
+// SetDeletedAt sets the "deleted_at" field.
+func (m *TokenMutation) SetDeletedAt(t time.Time) {
+	m.deleted_at = &t
+}
+
+// DeletedAt returns the value of the "deleted_at" field in the mutation.
+func (m *TokenMutation) DeletedAt() (r time.Time, exists bool) {
+	v := m.deleted_at
+	if v == nil {
+		return
+	}
+	return *v, true
+}
+
+// OldDeletedAt returns the old "deleted_at" field's value of the Token entity.
+// If the Token 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 *TokenMutation) OldDeletedAt(ctx context.Context) (v time.Time, err error) {
+	if !m.op.Is(OpUpdateOne) {
+		return v, errors.New("OldDeletedAt is only allowed on UpdateOne operations")
+	}
+	if m.id == nil || m.oldValue == nil {
+		return v, errors.New("OldDeletedAt requires an ID field in the mutation")
+	}
+	oldValue, err := m.oldValue(ctx)
+	if err != nil {
+		return v, fmt.Errorf("querying old value for OldDeletedAt: %w", err)
+	}
+	return oldValue.DeletedAt, nil
+}
+
+// ClearDeletedAt clears the value of the "deleted_at" field.
+func (m *TokenMutation) ClearDeletedAt() {
+	m.deleted_at = nil
+	m.clearedFields[token.FieldDeletedAt] = struct{}{}
+}
+
+// DeletedAtCleared returns if the "deleted_at" field was cleared in this mutation.
+func (m *TokenMutation) DeletedAtCleared() bool {
+	_, ok := m.clearedFields[token.FieldDeletedAt]
+	return ok
+}
+
+// ResetDeletedAt resets all changes to the "deleted_at" field.
+func (m *TokenMutation) ResetDeletedAt() {
+	m.deleted_at = nil
+	delete(m.clearedFields, token.FieldDeletedAt)
+}
+
+// SetExpireAt sets the "expire_at" field.
+func (m *TokenMutation) SetExpireAt(t time.Time) {
+	m.expire_at = &t
+}
+
+// ExpireAt returns the value of the "expire_at" field in the mutation.
+func (m *TokenMutation) ExpireAt() (r time.Time, exists bool) {
+	v := m.expire_at
+	if v == nil {
+		return
+	}
+	return *v, true
+}
+
+// OldExpireAt returns the old "expire_at" field's value of the Token entity.
+// If the Token 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 *TokenMutation) OldExpireAt(ctx context.Context) (v time.Time, err error) {
+	if !m.op.Is(OpUpdateOne) {
+		return v, errors.New("OldExpireAt is only allowed on UpdateOne operations")
+	}
+	if m.id == nil || m.oldValue == nil {
+		return v, errors.New("OldExpireAt requires an ID field in the mutation")
+	}
+	oldValue, err := m.oldValue(ctx)
+	if err != nil {
+		return v, fmt.Errorf("querying old value for OldExpireAt: %w", err)
+	}
+	return oldValue.ExpireAt, nil
+}
+
+// ClearExpireAt clears the value of the "expire_at" field.
+func (m *TokenMutation) ClearExpireAt() {
+	m.expire_at = nil
+	m.clearedFields[token.FieldExpireAt] = struct{}{}
+}
+
+// ExpireAtCleared returns if the "expire_at" field was cleared in this mutation.
+func (m *TokenMutation) ExpireAtCleared() bool {
+	_, ok := m.clearedFields[token.FieldExpireAt]
+	return ok
+}
+
+// ResetExpireAt resets all changes to the "expire_at" field.
+func (m *TokenMutation) ResetExpireAt() {
+	m.expire_at = nil
+	delete(m.clearedFields, token.FieldExpireAt)
+}
+
+// SetToken sets the "token" field.
+func (m *TokenMutation) SetToken(s string) {
+	m.token = &s
+}
+
+// Token returns the value of the "token" field in the mutation.
+func (m *TokenMutation) Token() (r string, exists bool) {
+	v := m.token
+	if v == nil {
+		return
+	}
+	return *v, true
+}
+
+// OldToken returns the old "token" field's value of the Token entity.
+// If the Token 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 *TokenMutation) OldToken(ctx context.Context) (v string, err error) {
+	if !m.op.Is(OpUpdateOne) {
+		return v, errors.New("OldToken is only allowed on UpdateOne operations")
+	}
+	if m.id == nil || m.oldValue == nil {
+		return v, errors.New("OldToken requires an ID field in the mutation")
+	}
+	oldValue, err := m.oldValue(ctx)
+	if err != nil {
+		return v, fmt.Errorf("querying old value for OldToken: %w", err)
+	}
+	return oldValue.Token, nil
+}
+
+// ClearToken clears the value of the "token" field.
+func (m *TokenMutation) ClearToken() {
+	m.token = nil
+	m.clearedFields[token.FieldToken] = struct{}{}
+}
+
+// TokenCleared returns if the "token" field was cleared in this mutation.
+func (m *TokenMutation) TokenCleared() bool {
+	_, ok := m.clearedFields[token.FieldToken]
+	return ok
+}
+
+// ResetToken resets all changes to the "token" field.
+func (m *TokenMutation) ResetToken() {
+	m.token = nil
+	delete(m.clearedFields, token.FieldToken)
+}
+
+// SetMAC sets the "mac" field.
+func (m *TokenMutation) SetMAC(s string) {
+	m.mac = &s
+}
+
+// MAC returns the value of the "mac" field in the mutation.
+func (m *TokenMutation) MAC() (r string, exists bool) {
+	v := m.mac
+	if v == nil {
+		return
+	}
+	return *v, true
+}
+
+// OldMAC returns the old "mac" field's value of the Token entity.
+// If the Token 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 *TokenMutation) OldMAC(ctx context.Context) (v string, err error) {
+	if !m.op.Is(OpUpdateOne) {
+		return v, errors.New("OldMAC is only allowed on UpdateOne operations")
+	}
+	if m.id == nil || m.oldValue == nil {
+		return v, errors.New("OldMAC requires an ID field in the mutation")
+	}
+	oldValue, err := m.oldValue(ctx)
+	if err != nil {
+		return v, fmt.Errorf("querying old value for OldMAC: %w", err)
+	}
+	return oldValue.MAC, nil
+}
+
+// ResetMAC resets all changes to the "mac" field.
+func (m *TokenMutation) ResetMAC() {
+	m.mac = nil
+}
+
+// Where appends a list predicates to the TokenMutation builder.
+func (m *TokenMutation) Where(ps ...predicate.Token) {
+	m.predicates = append(m.predicates, ps...)
+}
+
+// WhereP appends storage-level predicates to the TokenMutation builder. Using this method,
+// users can use type-assertion to append predicates that do not depend on any generated package.
+func (m *TokenMutation) WhereP(ps ...func(*sql.Selector)) {
+	p := make([]predicate.Token, len(ps))
+	for i := range ps {
+		p[i] = ps[i]
+	}
+	m.Where(p...)
+}
+
+// Op returns the operation name.
+func (m *TokenMutation) Op() Op {
+	return m.op
+}
+
+// SetOp allows setting the mutation operation.
+func (m *TokenMutation) SetOp(op Op) {
+	m.op = op
+}
+
+// Type returns the node type of this mutation (Token).
+func (m *TokenMutation) 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 *TokenMutation) Fields() []string {
+	fields := make([]string, 0, 6)
+	if m.created_at != nil {
+		fields = append(fields, token.FieldCreatedAt)
+	}
+	if m.updated_at != nil {
+		fields = append(fields, token.FieldUpdatedAt)
+	}
+	if m.deleted_at != nil {
+		fields = append(fields, token.FieldDeletedAt)
+	}
+	if m.expire_at != nil {
+		fields = append(fields, token.FieldExpireAt)
+	}
+	if m.token != nil {
+		fields = append(fields, token.FieldToken)
+	}
+	if m.mac != nil {
+		fields = append(fields, token.FieldMAC)
+	}
+	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 *TokenMutation) Field(name string) (ent.Value, bool) {
+	switch name {
+	case token.FieldCreatedAt:
+		return m.CreatedAt()
+	case token.FieldUpdatedAt:
+		return m.UpdatedAt()
+	case token.FieldDeletedAt:
+		return m.DeletedAt()
+	case token.FieldExpireAt:
+		return m.ExpireAt()
+	case token.FieldToken:
+		return m.Token()
+	case token.FieldMAC:
+		return m.MAC()
+	}
+	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 *TokenMutation) OldField(ctx context.Context, name string) (ent.Value, error) {
+	switch name {
+	case token.FieldCreatedAt:
+		return m.OldCreatedAt(ctx)
+	case token.FieldUpdatedAt:
+		return m.OldUpdatedAt(ctx)
+	case token.FieldDeletedAt:
+		return m.OldDeletedAt(ctx)
+	case token.FieldExpireAt:
+		return m.OldExpireAt(ctx)
+	case token.FieldToken:
+		return m.OldToken(ctx)
+	case token.FieldMAC:
+		return m.OldMAC(ctx)
+	}
+	return nil, fmt.Errorf("unknown Token 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 *TokenMutation) SetField(name string, value ent.Value) error {
+	switch name {
+	case token.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 token.FieldUpdatedAt:
+		v, ok := value.(time.Time)
+		if !ok {
+			return fmt.Errorf("unexpected type %T for field %s", value, name)
+		}
+		m.SetUpdatedAt(v)
+		return nil
+	case token.FieldDeletedAt:
+		v, ok := value.(time.Time)
+		if !ok {
+			return fmt.Errorf("unexpected type %T for field %s", value, name)
+		}
+		m.SetDeletedAt(v)
+		return nil
+	case token.FieldExpireAt:
+		v, ok := value.(time.Time)
+		if !ok {
+			return fmt.Errorf("unexpected type %T for field %s", value, name)
+		}
+		m.SetExpireAt(v)
+		return nil
+	case token.FieldToken:
+		v, ok := value.(string)
+		if !ok {
+			return fmt.Errorf("unexpected type %T for field %s", value, name)
+		}
+		m.SetToken(v)
+		return nil
+	case token.FieldMAC:
+		v, ok := value.(string)
+		if !ok {
+			return fmt.Errorf("unexpected type %T for field %s", value, name)
+		}
+		m.SetMAC(v)
+		return nil
+	}
+	return fmt.Errorf("unknown Token field %s", name)
+}
+
+// AddedFields returns all numeric fields that were incremented/decremented during
+// this mutation.
+func (m *TokenMutation) AddedFields() []string {
+	return nil
+}
+
+// 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 *TokenMutation) AddedField(name string) (ent.Value, bool) {
+	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 *TokenMutation) AddField(name string, value ent.Value) error {
+	switch name {
+	}
+	return fmt.Errorf("unknown Token numeric field %s", name)
+}
+
+// ClearedFields returns all nullable fields that were cleared during this
+// mutation.
+func (m *TokenMutation) ClearedFields() []string {
+	var fields []string
+	if m.FieldCleared(token.FieldDeletedAt) {
+		fields = append(fields, token.FieldDeletedAt)
+	}
+	if m.FieldCleared(token.FieldExpireAt) {
+		fields = append(fields, token.FieldExpireAt)
+	}
+	if m.FieldCleared(token.FieldToken) {
+		fields = append(fields, token.FieldToken)
+	}
+	return fields
+}
+
+// FieldCleared returns a boolean indicating if a field with the given name was
+// cleared in this mutation.
+func (m *TokenMutation) 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 *TokenMutation) ClearField(name string) error {
+	switch name {
+	case token.FieldDeletedAt:
+		m.ClearDeletedAt()
+		return nil
+	case token.FieldExpireAt:
+		m.ClearExpireAt()
+		return nil
+	case token.FieldToken:
+		m.ClearToken()
+		return nil
+	}
+	return fmt.Errorf("unknown Token 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 *TokenMutation) ResetField(name string) error {
+	switch name {
+	case token.FieldCreatedAt:
+		m.ResetCreatedAt()
+		return nil
+	case token.FieldUpdatedAt:
+		m.ResetUpdatedAt()
+		return nil
+	case token.FieldDeletedAt:
+		m.ResetDeletedAt()
+		return nil
+	case token.FieldExpireAt:
+		m.ResetExpireAt()
+		return nil
+	case token.FieldToken:
+		m.ResetToken()
+		return nil
+	case token.FieldMAC:
+		m.ResetMAC()
+		return nil
+	}
+	return fmt.Errorf("unknown Token field %s", name)
+}
+
+// AddedEdges returns all edge names that were set/added in this mutation.
+func (m *TokenMutation) AddedEdges() []string {
+	edges := make([]string, 0, 0)
+	return edges
+}
+
+// AddedIDs returns all IDs (to other nodes) that were added for the given edge
+// name in this mutation.
+func (m *TokenMutation) AddedIDs(name string) []ent.Value {
+	return nil
+}
+
+// RemovedEdges returns all edge names that were removed in this mutation.
+func (m *TokenMutation) RemovedEdges() []string {
+	edges := make([]string, 0, 0)
+	return edges
+}
+
+// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with
+// the given name in this mutation.
+func (m *TokenMutation) RemovedIDs(name string) []ent.Value {
+	return nil
+}
+
+// ClearedEdges returns all edge names that were cleared in this mutation.
+func (m *TokenMutation) ClearedEdges() []string {
+	edges := make([]string, 0, 0)
+	return edges
+}
+
+// EdgeCleared returns a boolean which indicates if the edge with the given name
+// was cleared in this mutation.
+func (m *TokenMutation) EdgeCleared(name string) bool {
+	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 *TokenMutation) ClearEdge(name string) error {
+	return fmt.Errorf("unknown Token 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 *TokenMutation) ResetEdge(name string) error {
+	return fmt.Errorf("unknown Token edge %s", name)
+}
+
 // TutorialMutation represents an operation that mutates the Tutorial nodes in the graph.
 type TutorialMutation struct {
 	config

+ 82 - 0
ent/pagination.go

@@ -18,6 +18,7 @@ import (
 	"wechat-api/ent/sopnode"
 	"wechat-api/ent/sopstage"
 	"wechat-api/ent/soptask"
+	"wechat-api/ent/token"
 	"wechat-api/ent/tutorial"
 	"wechat-api/ent/workexperience"
 	"wechat-api/ent/wx"
@@ -1122,6 +1123,87 @@ func (st *SopTaskQuery) Page(
 	return ret, nil
 }
 
+type TokenPager struct {
+	Order  token.OrderOption
+	Filter func(*TokenQuery) (*TokenQuery, error)
+}
+
+// TokenPaginateOption enables pagination customization.
+type TokenPaginateOption func(*TokenPager)
+
+// DefaultTokenOrder is the default ordering of Token.
+var DefaultTokenOrder = Desc(token.FieldID)
+
+func newTokenPager(opts []TokenPaginateOption) (*TokenPager, error) {
+	pager := &TokenPager{}
+	for _, opt := range opts {
+		opt(pager)
+	}
+	if pager.Order == nil {
+		pager.Order = DefaultTokenOrder
+	}
+	return pager, nil
+}
+
+func (p *TokenPager) ApplyFilter(query *TokenQuery) (*TokenQuery, error) {
+	if p.Filter != nil {
+		return p.Filter(query)
+	}
+	return query, nil
+}
+
+// TokenPageList is Token PageList result.
+type TokenPageList struct {
+	List        []*Token     `json:"list"`
+	PageDetails *PageDetails `json:"pageDetails"`
+}
+
+func (t *TokenQuery) Page(
+	ctx context.Context, pageNum uint64, pageSize uint64, opts ...TokenPaginateOption,
+) (*TokenPageList, error) {
+
+	pager, err := newTokenPager(opts)
+	if err != nil {
+		return nil, err
+	}
+
+	if t, err = pager.ApplyFilter(t); err != nil {
+		return nil, err
+	}
+
+	ret := &TokenPageList{}
+
+	ret.PageDetails = &PageDetails{
+		Page: pageNum,
+		Size: pageSize,
+	}
+
+	query := t.Clone()
+	query.ctx.Fields = nil
+	count, err := query.Count(ctx)
+
+	if err != nil {
+		return nil, err
+	}
+
+	ret.PageDetails.Total = uint64(count)
+
+	if pager.Order != nil {
+		t = t.Order(pager.Order)
+	} else {
+		t = t.Order(DefaultTokenOrder)
+	}
+
+	t = t.Offset(int((pageNum - 1) * pageSize)).Limit(int(pageSize))
+	list, err := t.All(ctx)
+	if err != nil {
+		return nil, err
+	}
+	ret.List = list
+
+	return ret, nil
+}
+
 type TutorialPager struct {
 	Order  tutorial.OrderOption
 	Filter func(*TutorialQuery) (*TutorialQuery, error)

+ 3 - 0
ent/predicate/predicate.go

@@ -45,6 +45,9 @@ type SopStage func(*sql.Selector)
 // SopTask is the predicate function for soptask builders.
 type SopTask func(*sql.Selector)
 
+// Token is the predicate function for token builders.
+type Token func(*sql.Selector)
+
 // Tutorial is the predicate function for tutorial builders.
 type Tutorial func(*sql.Selector)
 

+ 28 - 0
ent/runtime/runtime.go

@@ -18,6 +18,7 @@ import (
 	"wechat-api/ent/sopnode"
 	"wechat-api/ent/sopstage"
 	"wechat-api/ent/soptask"
+	"wechat-api/ent/token"
 	"wechat-api/ent/tutorial"
 	"wechat-api/ent/workexperience"
 	"wechat-api/ent/wx"
@@ -593,6 +594,33 @@ func init() {
 	soptaskDescOrganizationID := soptaskFields[6].Descriptor()
 	// soptask.DefaultOrganizationID holds the default value on creation for the organization_id field.
 	soptask.DefaultOrganizationID = soptaskDescOrganizationID.Default.(uint64)
+	tokenMixin := schema.Token{}.Mixin()
+	tokenMixinHooks1 := tokenMixin[1].Hooks()
+	token.Hooks[0] = tokenMixinHooks1[0]
+	tokenMixinInters1 := tokenMixin[1].Interceptors()
+	token.Interceptors[0] = tokenMixinInters1[0]
+	tokenMixinFields0 := tokenMixin[0].Fields()
+	_ = tokenMixinFields0
+	tokenFields := schema.Token{}.Fields()
+	_ = tokenFields
+	// tokenDescCreatedAt is the schema descriptor for created_at field.
+	tokenDescCreatedAt := tokenMixinFields0[1].Descriptor()
+	// token.DefaultCreatedAt holds the default value on creation for the created_at field.
+	token.DefaultCreatedAt = tokenDescCreatedAt.Default.(func() time.Time)
+	// tokenDescUpdatedAt is the schema descriptor for updated_at field.
+	tokenDescUpdatedAt := tokenMixinFields0[2].Descriptor()
+	// token.DefaultUpdatedAt holds the default value on creation for the updated_at field.
+	token.DefaultUpdatedAt = tokenDescUpdatedAt.Default.(func() time.Time)
+	// token.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field.
+	token.UpdateDefaultUpdatedAt = tokenDescUpdatedAt.UpdateDefault.(func() time.Time)
+	// tokenDescToken is the schema descriptor for token field.
+	tokenDescToken := tokenFields[1].Descriptor()
+	// token.DefaultToken holds the default value on creation for the token field.
+	token.DefaultToken = tokenDescToken.Default.(string)
+	// tokenDescMAC is the schema descriptor for mac field.
+	tokenDescMAC := tokenFields[2].Descriptor()
+	// token.DefaultMAC holds the default value on creation for the mac field.
+	token.DefaultMAC = tokenDescMAC.Default.(string)
 	tutorialMixin := schema.Tutorial{}.Mixin()
 	tutorialMixinHooks1 := tutorialMixin[1].Hooks()
 	tutorial.Hooks[0] = tutorialMixinHooks1[0]

+ 48 - 0
ent/schema/token.go

@@ -0,0 +1,48 @@
+package schema
+
+import (
+	"wechat-api/ent/schema/localmixin"
+
+	"entgo.io/ent"
+	"entgo.io/ent/dialect/entsql"
+	"entgo.io/ent/schema"
+	"entgo.io/ent/schema/field"
+	"entgo.io/ent/schema/index"
+	"github.com/suyuan32/simple-admin-common/orm/ent/mixins"
+)
+
+type Token struct {
+	ent.Schema
+}
+
+func (Token) Fields() []ent.Field {
+	return []ent.Field{
+		field.Time("expire_at").Optional().Default(nil).Comment("过期时间"),
+		field.String("token").Optional().Default("").Comment("Token"),
+		field.String("mac").Default("").Comment("Mac地址"),
+	}
+}
+
+func (Token) Mixin() []ent.Mixin {
+	return []ent.Mixin{
+		mixins.IDMixin{},
+		localmixin.SoftDeleteMixin{},
+	}
+}
+
+func (Token) Indexes() []ent.Index {
+	return []ent.Index{
+		index.Fields("token"),
+	}
+}
+
+func (Token) Edges() []ent.Edge {
+	return nil
+}
+
+func (Token) Annotations() []schema.Annotation {
+	return []schema.Annotation{
+		entsql.WithComments(true),
+		entsql.Annotation{Table: "token"},
+	}
+}

+ 120 - 0
ent/set_not_nil.go

@@ -3320,6 +3320,126 @@ func (st *SopTaskCreate) SetNotNilOrganizationID(value *uint64) *SopTaskCreate {
 }
 
 // set field if value's pointer is not nil.
+func (t *TokenUpdate) SetNotNilUpdatedAt(value *time.Time) *TokenUpdate {
+	if value != nil {
+		return t.SetUpdatedAt(*value)
+	}
+	return t
+}
+
+// set field if value's pointer is not nil.
+func (t *TokenUpdateOne) SetNotNilUpdatedAt(value *time.Time) *TokenUpdateOne {
+	if value != nil {
+		return t.SetUpdatedAt(*value)
+	}
+	return t
+}
+
+// set field if value's pointer is not nil.
+func (t *TokenCreate) SetNotNilUpdatedAt(value *time.Time) *TokenCreate {
+	if value != nil {
+		return t.SetUpdatedAt(*value)
+	}
+	return t
+}
+
+// set field if value's pointer is not nil.
+func (t *TokenUpdate) SetNotNilDeletedAt(value *time.Time) *TokenUpdate {
+	if value != nil {
+		return t.SetDeletedAt(*value)
+	}
+	return t
+}
+
+// set field if value's pointer is not nil.
+func (t *TokenUpdateOne) SetNotNilDeletedAt(value *time.Time) *TokenUpdateOne {
+	if value != nil {
+		return t.SetDeletedAt(*value)
+	}
+	return t
+}
+
+// set field if value's pointer is not nil.
+func (t *TokenCreate) SetNotNilDeletedAt(value *time.Time) *TokenCreate {
+	if value != nil {
+		return t.SetDeletedAt(*value)
+	}
+	return t
+}
+
+// set field if value's pointer is not nil.
+func (t *TokenUpdate) SetNotNilExpireAt(value *time.Time) *TokenUpdate {
+	if value != nil {
+		return t.SetExpireAt(*value)
+	}
+	return t
+}
+
+// set field if value's pointer is not nil.
+func (t *TokenUpdateOne) SetNotNilExpireAt(value *time.Time) *TokenUpdateOne {
+	if value != nil {
+		return t.SetExpireAt(*value)
+	}
+	return t
+}
+
+// set field if value's pointer is not nil.
+func (t *TokenCreate) SetNotNilExpireAt(value *time.Time) *TokenCreate {
+	if value != nil {
+		return t.SetExpireAt(*value)
+	}
+	return t
+}
+
+// set field if value's pointer is not nil.
+func (t *TokenUpdate) SetNotNilToken(value *string) *TokenUpdate {
+	if value != nil {
+		return t.SetToken(*value)
+	}
+	return t
+}
+
+// set field if value's pointer is not nil.
+func (t *TokenUpdateOne) SetNotNilToken(value *string) *TokenUpdateOne {
+	if value != nil {
+		return t.SetToken(*value)
+	}
+	return t
+}
+
+// set field if value's pointer is not nil.
+func (t *TokenCreate) SetNotNilToken(value *string) *TokenCreate {
+	if value != nil {
+		return t.SetToken(*value)
+	}
+	return t
+}
+
+// set field if value's pointer is not nil.
+func (t *TokenUpdate) SetNotNilMAC(value *string) *TokenUpdate {
+	if value != nil {
+		return t.SetMAC(*value)
+	}
+	return t
+}
+
+// set field if value's pointer is not nil.
+func (t *TokenUpdateOne) SetNotNilMAC(value *string) *TokenUpdateOne {
+	if value != nil {
+		return t.SetMAC(*value)
+	}
+	return t
+}
+
+// set field if value's pointer is not nil.
+func (t *TokenCreate) SetNotNilMAC(value *string) *TokenCreate {
+	if value != nil {
+		return t.SetMAC(*value)
+	}
+	return t
+}
+
+// set field if value's pointer is not nil.
 func (t *TutorialUpdate) SetNotNilUpdatedAt(value *time.Time) *TutorialUpdate {
 	if value != nil {
 		return t.SetUpdatedAt(*value)

+ 161 - 0
ent/token.go

@@ -0,0 +1,161 @@
+// Code generated by ent, DO NOT EDIT.
+
+package ent
+
+import (
+	"fmt"
+	"strings"
+	"time"
+	"wechat-api/ent/token"
+
+	"entgo.io/ent"
+	"entgo.io/ent/dialect/sql"
+)
+
+// Token is the model entity for the Token schema.
+type Token struct {
+	config `json:"-"`
+	// ID of the ent.
+	ID uint64 `json:"id,omitempty"`
+	// Create Time | 创建日期
+	CreatedAt time.Time `json:"created_at,omitempty"`
+	// Update Time | 修改日期
+	UpdatedAt time.Time `json:"updated_at,omitempty"`
+	// Delete Time | 删除日期
+	DeletedAt time.Time `json:"deleted_at,omitempty"`
+	// 过期时间
+	ExpireAt time.Time `json:"expire_at,omitempty"`
+	// Token
+	Token string `json:"token,omitempty"`
+	// Mac地址
+	MAC          string `json:"mac,omitempty"`
+	selectValues sql.SelectValues
+}
+
+// scanValues returns the types for scanning values from sql.Rows.
+func (*Token) scanValues(columns []string) ([]any, error) {
+	values := make([]any, len(columns))
+	for i := range columns {
+		switch columns[i] {
+		case token.FieldID:
+			values[i] = new(sql.NullInt64)
+		case token.FieldToken, token.FieldMAC:
+			values[i] = new(sql.NullString)
+		case token.FieldCreatedAt, token.FieldUpdatedAt, token.FieldDeletedAt, token.FieldExpireAt:
+			values[i] = new(sql.NullTime)
+		default:
+			values[i] = new(sql.UnknownType)
+		}
+	}
+	return values, nil
+}
+
+// assignValues assigns the values that were returned from sql.Rows (after scanning)
+// to the Token fields.
+func (t *Token) assignValues(columns []string, values []any) error {
+	if m, n := len(values), len(columns); m < n {
+		return fmt.Errorf("mismatch number of scan values: %d != %d", m, n)
+	}
+	for i := range columns {
+		switch columns[i] {
+		case token.FieldID:
+			value, ok := values[i].(*sql.NullInt64)
+			if !ok {
+				return fmt.Errorf("unexpected type %T for field id", value)
+			}
+			t.ID = uint64(value.Int64)
+		case token.FieldCreatedAt:
+			if value, ok := values[i].(*sql.NullTime); !ok {
+				return fmt.Errorf("unexpected type %T for field created_at", values[i])
+			} else if value.Valid {
+				t.CreatedAt = value.Time
+			}
+		case token.FieldUpdatedAt:
+			if value, ok := values[i].(*sql.NullTime); !ok {
+				return fmt.Errorf("unexpected type %T for field updated_at", values[i])
+			} else if value.Valid {
+				t.UpdatedAt = value.Time
+			}
+		case token.FieldDeletedAt:
+			if value, ok := values[i].(*sql.NullTime); !ok {
+				return fmt.Errorf("unexpected type %T for field deleted_at", values[i])
+			} else if value.Valid {
+				t.DeletedAt = value.Time
+			}
+		case token.FieldExpireAt:
+			if value, ok := values[i].(*sql.NullTime); !ok {
+				return fmt.Errorf("unexpected type %T for field expire_at", values[i])
+			} else if value.Valid {
+				t.ExpireAt = value.Time
+			}
+		case token.FieldToken:
+			if value, ok := values[i].(*sql.NullString); !ok {
+				return fmt.Errorf("unexpected type %T for field token", values[i])
+			} else if value.Valid {
+				t.Token = value.String
+			}
+		case token.FieldMAC:
+			if value, ok := values[i].(*sql.NullString); !ok {
+				return fmt.Errorf("unexpected type %T for field mac", values[i])
+			} else if value.Valid {
+				t.MAC = value.String
+			}
+		default:
+			t.selectValues.Set(columns[i], values[i])
+		}
+	}
+	return nil
+}
+
+// Value returns the ent.Value that was dynamically selected and assigned to the Token.
+// This includes values selected through modifiers, order, etc.
+func (t *Token) Value(name string) (ent.Value, error) {
+	return t.selectValues.Get(name)
+}
+
+// Update returns a builder for updating this Token.
+// Note that you need to call Token.Unwrap() before calling this method if this Token
+// was returned from a transaction, and the transaction was committed or rolled back.
+func (t *Token) Update() *TokenUpdateOne {
+	return NewTokenClient(t.config).UpdateOne(t)
+}
+
+// Unwrap unwraps the Token entity that was returned from a transaction after it was closed,
+// so that all future queries will be executed through the driver which created the transaction.
+func (t *Token) Unwrap() *Token {
+	_tx, ok := t.config.driver.(*txDriver)
+	if !ok {
+		panic("ent: Token is not a transactional entity")
+	}
+	t.config.driver = _tx.drv
+	return t
+}
+
+// String implements the fmt.Stringer.
+func (t *Token) String() string {
+	var builder strings.Builder
+	builder.WriteString("Token(")
+	builder.WriteString(fmt.Sprintf("id=%v, ", t.ID))
+	builder.WriteString("created_at=")
+	builder.WriteString(t.CreatedAt.Format(time.ANSIC))
+	builder.WriteString(", ")
+	builder.WriteString("updated_at=")
+	builder.WriteString(t.UpdatedAt.Format(time.ANSIC))
+	builder.WriteString(", ")
+	builder.WriteString("deleted_at=")
+	builder.WriteString(t.DeletedAt.Format(time.ANSIC))
+	builder.WriteString(", ")
+	builder.WriteString("expire_at=")
+	builder.WriteString(t.ExpireAt.Format(time.ANSIC))
+	builder.WriteString(", ")
+	builder.WriteString("token=")
+	builder.WriteString(t.Token)
+	builder.WriteString(", ")
+	builder.WriteString("mac=")
+	builder.WriteString(t.MAC)
+	builder.WriteByte(')')
+	return builder.String()
+}
+
+// Tokens is a parsable slice of Token.
+type Tokens []*Token

+ 110 - 0
ent/token/token.go

@@ -0,0 +1,110 @@
+// Code generated by ent, DO NOT EDIT.
+
+package token
+
+import (
+	"time"
+
+	"entgo.io/ent"
+	"entgo.io/ent/dialect/sql"
+)
+
+const (
+	// Label holds the string label denoting the token type in the database.
+	Label = "token"
+	// FieldID holds the string denoting the id field in the database.
+	FieldID = "id"
+	// FieldCreatedAt holds the string denoting the created_at field in the database.
+	FieldCreatedAt = "created_at"
+	// FieldUpdatedAt holds the string denoting the updated_at field in the database.
+	FieldUpdatedAt = "updated_at"
+	// FieldDeletedAt holds the string denoting the deleted_at field in the database.
+	FieldDeletedAt = "deleted_at"
+	// FieldExpireAt holds the string denoting the expire_at field in the database.
+	FieldExpireAt = "expire_at"
+	// FieldToken holds the string denoting the token field in the database.
+	FieldToken = "token"
+	// FieldMAC holds the string denoting the mac field in the database.
+	FieldMAC = "mac"
+	// Table holds the table name of the token in the database.
+	Table = "token"
+)
+
+// Columns holds all SQL columns for token fields.
+var Columns = []string{
+	FieldID,
+	FieldCreatedAt,
+	FieldUpdatedAt,
+	FieldDeletedAt,
+	FieldExpireAt,
+	FieldToken,
+	FieldMAC,
+}
+
+// ValidColumn reports if the column name is valid (part of the table columns).
+func ValidColumn(column string) bool {
+	for i := range Columns {
+		if column == Columns[i] {
+			return true
+		}
+	}
+	return false
+}
+
+// Note that the variables below are initialized by the runtime
+// package on the initialization of the application. Therefore,
+// it should be imported in the main as follows:
+//
+//	import _ "wechat-api/ent/runtime"
+var (
+	Hooks        [1]ent.Hook
+	Interceptors [1]ent.Interceptor
+	// DefaultCreatedAt holds the default value on creation for the "created_at" field.
+	DefaultCreatedAt func() time.Time
+	// DefaultUpdatedAt holds the default value on creation for the "updated_at" field.
+	DefaultUpdatedAt func() time.Time
+	// UpdateDefaultUpdatedAt holds the default value on update for the "updated_at" field.
+	UpdateDefaultUpdatedAt func() time.Time
+	// DefaultToken holds the default value on creation for the "token" field.
+	DefaultToken string
+	// DefaultMAC holds the default value on creation for the "mac" field.
+	DefaultMAC string
+)
+
+// OrderOption defines the ordering options for the Token queries.
+type OrderOption func(*sql.Selector)
+
+// ByID orders the results by the id field.
+func ByID(opts ...sql.OrderTermOption) OrderOption {
+	return sql.OrderByField(FieldID, opts...).ToFunc()
+}
+
+// ByCreatedAt orders the results by the created_at field.
+func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption {
+	return sql.OrderByField(FieldCreatedAt, opts...).ToFunc()
+}
+
+// ByUpdatedAt orders the results by the updated_at field.
+func ByUpdatedAt(opts ...sql.OrderTermOption) OrderOption {
+	return sql.OrderByField(FieldUpdatedAt, opts...).ToFunc()
+}
+
+// ByDeletedAt orders the results by the deleted_at field.
+func ByDeletedAt(opts ...sql.OrderTermOption) OrderOption {
+	return sql.OrderByField(FieldDeletedAt, opts...).ToFunc()
+}
+
+// ByExpireAt orders the results by the expire_at field.
+func ByExpireAt(opts ...sql.OrderTermOption) OrderOption {
+	return sql.OrderByField(FieldExpireAt, opts...).ToFunc()
+}
+
+// ByToken orders the results by the token field.
+func ByToken(opts ...sql.OrderTermOption) OrderOption {
+	return sql.OrderByField(FieldToken, opts...).ToFunc()
+}
+
+// ByMAC orders the results by the mac field.
+func ByMAC(opts ...sql.OrderTermOption) OrderOption {
+	return sql.OrderByField(FieldMAC, opts...).ToFunc()
+}

+ 420 - 0
ent/token/where.go

@@ -0,0 +1,420 @@
+// Code generated by ent, DO NOT EDIT.
+
+package token
+
+import (
+	"time"
+	"wechat-api/ent/predicate"
+
+	"entgo.io/ent/dialect/sql"
+)
+
+// ID filters vertices based on their ID field.
+func ID(id uint64) predicate.Token {
+	return predicate.Token(sql.FieldEQ(FieldID, id))
+}
+
+// IDEQ applies the EQ predicate on the ID field.
+func IDEQ(id uint64) predicate.Token {
+	return predicate.Token(sql.FieldEQ(FieldID, id))
+}
+
+// IDNEQ applies the NEQ predicate on the ID field.
+func IDNEQ(id uint64) predicate.Token {
+	return predicate.Token(sql.FieldNEQ(FieldID, id))
+}
+
+// IDIn applies the In predicate on the ID field.
+func IDIn(ids ...uint64) predicate.Token {
+	return predicate.Token(sql.FieldIn(FieldID, ids...))
+}
+
+// IDNotIn applies the NotIn predicate on the ID field.
+func IDNotIn(ids ...uint64) predicate.Token {
+	return predicate.Token(sql.FieldNotIn(FieldID, ids...))
+}
+
+// IDGT applies the GT predicate on the ID field.
+func IDGT(id uint64) predicate.Token {
+	return predicate.Token(sql.FieldGT(FieldID, id))
+}
+
+// IDGTE applies the GTE predicate on the ID field.
+func IDGTE(id uint64) predicate.Token {
+	return predicate.Token(sql.FieldGTE(FieldID, id))
+}
+
+// IDLT applies the LT predicate on the ID field.
+func IDLT(id uint64) predicate.Token {
+	return predicate.Token(sql.FieldLT(FieldID, id))
+}
+
+// IDLTE applies the LTE predicate on the ID field.
+func IDLTE(id uint64) predicate.Token {
+	return predicate.Token(sql.FieldLTE(FieldID, id))
+}
+
+// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ.
+func CreatedAt(v time.Time) predicate.Token {
+	return predicate.Token(sql.FieldEQ(FieldCreatedAt, v))
+}
+
+// UpdatedAt applies equality check predicate on the "updated_at" field. It's identical to UpdatedAtEQ.
+func UpdatedAt(v time.Time) predicate.Token {
+	return predicate.Token(sql.FieldEQ(FieldUpdatedAt, v))
+}
+
+// DeletedAt applies equality check predicate on the "deleted_at" field. It's identical to DeletedAtEQ.
+func DeletedAt(v time.Time) predicate.Token {
+	return predicate.Token(sql.FieldEQ(FieldDeletedAt, v))
+}
+
+// ExpireAt applies equality check predicate on the "expire_at" field. It's identical to ExpireAtEQ.
+func ExpireAt(v time.Time) predicate.Token {
+	return predicate.Token(sql.FieldEQ(FieldExpireAt, v))
+}
+
+// Token applies equality check predicate on the "token" field. It's identical to TokenEQ.
+func Token(v string) predicate.Token {
+	return predicate.Token(sql.FieldEQ(FieldToken, v))
+}
+
+// MAC applies equality check predicate on the "mac" field. It's identical to MACEQ.
+func MAC(v string) predicate.Token {
+	return predicate.Token(sql.FieldEQ(FieldMAC, v))
+}
+
+// CreatedAtEQ applies the EQ predicate on the "created_at" field.
+func CreatedAtEQ(v time.Time) predicate.Token {
+	return predicate.Token(sql.FieldEQ(FieldCreatedAt, v))
+}
+
+// CreatedAtNEQ applies the NEQ predicate on the "created_at" field.
+func CreatedAtNEQ(v time.Time) predicate.Token {
+	return predicate.Token(sql.FieldNEQ(FieldCreatedAt, v))
+}
+
+// CreatedAtIn applies the In predicate on the "created_at" field.
+func CreatedAtIn(vs ...time.Time) predicate.Token {
+	return predicate.Token(sql.FieldIn(FieldCreatedAt, vs...))
+}
+
+// CreatedAtNotIn applies the NotIn predicate on the "created_at" field.
+func CreatedAtNotIn(vs ...time.Time) predicate.Token {
+	return predicate.Token(sql.FieldNotIn(FieldCreatedAt, vs...))
+}
+
+// CreatedAtGT applies the GT predicate on the "created_at" field.
+func CreatedAtGT(v time.Time) predicate.Token {
+	return predicate.Token(sql.FieldGT(FieldCreatedAt, v))
+}
+
+// CreatedAtGTE applies the GTE predicate on the "created_at" field.
+func CreatedAtGTE(v time.Time) predicate.Token {
+	return predicate.Token(sql.FieldGTE(FieldCreatedAt, v))
+}
+
+// CreatedAtLT applies the LT predicate on the "created_at" field.
+func CreatedAtLT(v time.Time) predicate.Token {
+	return predicate.Token(sql.FieldLT(FieldCreatedAt, v))
+}
+
+// CreatedAtLTE applies the LTE predicate on the "created_at" field.
+func CreatedAtLTE(v time.Time) predicate.Token {
+	return predicate.Token(sql.FieldLTE(FieldCreatedAt, v))
+}
+
+// UpdatedAtEQ applies the EQ predicate on the "updated_at" field.
+func UpdatedAtEQ(v time.Time) predicate.Token {
+	return predicate.Token(sql.FieldEQ(FieldUpdatedAt, v))
+}
+
+// UpdatedAtNEQ applies the NEQ predicate on the "updated_at" field.
+func UpdatedAtNEQ(v time.Time) predicate.Token {
+	return predicate.Token(sql.FieldNEQ(FieldUpdatedAt, v))
+}
+
+// UpdatedAtIn applies the In predicate on the "updated_at" field.
+func UpdatedAtIn(vs ...time.Time) predicate.Token {
+	return predicate.Token(sql.FieldIn(FieldUpdatedAt, vs...))
+}
+
+// UpdatedAtNotIn applies the NotIn predicate on the "updated_at" field.
+func UpdatedAtNotIn(vs ...time.Time) predicate.Token {
+	return predicate.Token(sql.FieldNotIn(FieldUpdatedAt, vs...))
+}
+
+// UpdatedAtGT applies the GT predicate on the "updated_at" field.
+func UpdatedAtGT(v time.Time) predicate.Token {
+	return predicate.Token(sql.FieldGT(FieldUpdatedAt, v))
+}
+
+// UpdatedAtGTE applies the GTE predicate on the "updated_at" field.
+func UpdatedAtGTE(v time.Time) predicate.Token {
+	return predicate.Token(sql.FieldGTE(FieldUpdatedAt, v))
+}
+
+// UpdatedAtLT applies the LT predicate on the "updated_at" field.
+func UpdatedAtLT(v time.Time) predicate.Token {
+	return predicate.Token(sql.FieldLT(FieldUpdatedAt, v))
+}
+
+// UpdatedAtLTE applies the LTE predicate on the "updated_at" field.
+func UpdatedAtLTE(v time.Time) predicate.Token {
+	return predicate.Token(sql.FieldLTE(FieldUpdatedAt, v))
+}
+
+// DeletedAtEQ applies the EQ predicate on the "deleted_at" field.
+func DeletedAtEQ(v time.Time) predicate.Token {
+	return predicate.Token(sql.FieldEQ(FieldDeletedAt, v))
+}
+
+// DeletedAtNEQ applies the NEQ predicate on the "deleted_at" field.
+func DeletedAtNEQ(v time.Time) predicate.Token {
+	return predicate.Token(sql.FieldNEQ(FieldDeletedAt, v))
+}
+
+// DeletedAtIn applies the In predicate on the "deleted_at" field.
+func DeletedAtIn(vs ...time.Time) predicate.Token {
+	return predicate.Token(sql.FieldIn(FieldDeletedAt, vs...))
+}
+
+// DeletedAtNotIn applies the NotIn predicate on the "deleted_at" field.
+func DeletedAtNotIn(vs ...time.Time) predicate.Token {
+	return predicate.Token(sql.FieldNotIn(FieldDeletedAt, vs...))
+}
+
+// DeletedAtGT applies the GT predicate on the "deleted_at" field.
+func DeletedAtGT(v time.Time) predicate.Token {
+	return predicate.Token(sql.FieldGT(FieldDeletedAt, v))
+}
+
+// DeletedAtGTE applies the GTE predicate on the "deleted_at" field.
+func DeletedAtGTE(v time.Time) predicate.Token {
+	return predicate.Token(sql.FieldGTE(FieldDeletedAt, v))
+}
+
+// DeletedAtLT applies the LT predicate on the "deleted_at" field.
+func DeletedAtLT(v time.Time) predicate.Token {
+	return predicate.Token(sql.FieldLT(FieldDeletedAt, v))
+}
+
+// DeletedAtLTE applies the LTE predicate on the "deleted_at" field.
+func DeletedAtLTE(v time.Time) predicate.Token {
+	return predicate.Token(sql.FieldLTE(FieldDeletedAt, v))
+}
+
+// DeletedAtIsNil applies the IsNil predicate on the "deleted_at" field.
+func DeletedAtIsNil() predicate.Token {
+	return predicate.Token(sql.FieldIsNull(FieldDeletedAt))
+}
+
+// DeletedAtNotNil applies the NotNil predicate on the "deleted_at" field.
+func DeletedAtNotNil() predicate.Token {
+	return predicate.Token(sql.FieldNotNull(FieldDeletedAt))
+}
+
+// ExpireAtEQ applies the EQ predicate on the "expire_at" field.
+func ExpireAtEQ(v time.Time) predicate.Token {
+	return predicate.Token(sql.FieldEQ(FieldExpireAt, v))
+}
+
+// ExpireAtNEQ applies the NEQ predicate on the "expire_at" field.
+func ExpireAtNEQ(v time.Time) predicate.Token {
+	return predicate.Token(sql.FieldNEQ(FieldExpireAt, v))
+}
+
+// ExpireAtIn applies the In predicate on the "expire_at" field.
+func ExpireAtIn(vs ...time.Time) predicate.Token {
+	return predicate.Token(sql.FieldIn(FieldExpireAt, vs...))
+}
+
+// ExpireAtNotIn applies the NotIn predicate on the "expire_at" field.
+func ExpireAtNotIn(vs ...time.Time) predicate.Token {
+	return predicate.Token(sql.FieldNotIn(FieldExpireAt, vs...))
+}
+
+// ExpireAtGT applies the GT predicate on the "expire_at" field.
+func ExpireAtGT(v time.Time) predicate.Token {
+	return predicate.Token(sql.FieldGT(FieldExpireAt, v))
+}
+
+// ExpireAtGTE applies the GTE predicate on the "expire_at" field.
+func ExpireAtGTE(v time.Time) predicate.Token {
+	return predicate.Token(sql.FieldGTE(FieldExpireAt, v))
+}
+
+// ExpireAtLT applies the LT predicate on the "expire_at" field.
+func ExpireAtLT(v time.Time) predicate.Token {
+	return predicate.Token(sql.FieldLT(FieldExpireAt, v))
+}
+
+// ExpireAtLTE applies the LTE predicate on the "expire_at" field.
+func ExpireAtLTE(v time.Time) predicate.Token {
+	return predicate.Token(sql.FieldLTE(FieldExpireAt, v))
+}
+
+// ExpireAtIsNil applies the IsNil predicate on the "expire_at" field.
+func ExpireAtIsNil() predicate.Token {
+	return predicate.Token(sql.FieldIsNull(FieldExpireAt))
+}
+
+// ExpireAtNotNil applies the NotNil predicate on the "expire_at" field.
+func ExpireAtNotNil() predicate.Token {
+	return predicate.Token(sql.FieldNotNull(FieldExpireAt))
+}
+
+// TokenEQ applies the EQ predicate on the "token" field.
+func TokenEQ(v string) predicate.Token {
+	return predicate.Token(sql.FieldEQ(FieldToken, v))
+}
+
+// TokenNEQ applies the NEQ predicate on the "token" field.
+func TokenNEQ(v string) predicate.Token {
+	return predicate.Token(sql.FieldNEQ(FieldToken, v))
+}
+
+// TokenIn applies the In predicate on the "token" field.
+func TokenIn(vs ...string) predicate.Token {
+	return predicate.Token(sql.FieldIn(FieldToken, vs...))
+}
+
+// TokenNotIn applies the NotIn predicate on the "token" field.
+func TokenNotIn(vs ...string) predicate.Token {
+	return predicate.Token(sql.FieldNotIn(FieldToken, vs...))
+}
+
+// TokenGT applies the GT predicate on the "token" field.
+func TokenGT(v string) predicate.Token {
+	return predicate.Token(sql.FieldGT(FieldToken, v))
+}
+
+// TokenGTE applies the GTE predicate on the "token" field.
+func TokenGTE(v string) predicate.Token {
+	return predicate.Token(sql.FieldGTE(FieldToken, v))
+}
+
+// TokenLT applies the LT predicate on the "token" field.
+func TokenLT(v string) predicate.Token {
+	return predicate.Token(sql.FieldLT(FieldToken, v))
+}
+
+// TokenLTE applies the LTE predicate on the "token" field.
+func TokenLTE(v string) predicate.Token {
+	return predicate.Token(sql.FieldLTE(FieldToken, v))
+}
+
+// TokenContains applies the Contains predicate on the "token" field.
+func TokenContains(v string) predicate.Token {
+	return predicate.Token(sql.FieldContains(FieldToken, v))
+}
+
+// TokenHasPrefix applies the HasPrefix predicate on the "token" field.
+func TokenHasPrefix(v string) predicate.Token {
+	return predicate.Token(sql.FieldHasPrefix(FieldToken, v))
+}
+
+// TokenHasSuffix applies the HasSuffix predicate on the "token" field.
+func TokenHasSuffix(v string) predicate.Token {
+	return predicate.Token(sql.FieldHasSuffix(FieldToken, v))
+}
+
+// TokenIsNil applies the IsNil predicate on the "token" field.
+func TokenIsNil() predicate.Token {
+	return predicate.Token(sql.FieldIsNull(FieldToken))
+}
+
+// TokenNotNil applies the NotNil predicate on the "token" field.
+func TokenNotNil() predicate.Token {
+	return predicate.Token(sql.FieldNotNull(FieldToken))
+}
+
+// TokenEqualFold applies the EqualFold predicate on the "token" field.
+func TokenEqualFold(v string) predicate.Token {
+	return predicate.Token(sql.FieldEqualFold(FieldToken, v))
+}
+
+// TokenContainsFold applies the ContainsFold predicate on the "token" field.
+func TokenContainsFold(v string) predicate.Token {
+	return predicate.Token(sql.FieldContainsFold(FieldToken, v))
+}
+
+// MACEQ applies the EQ predicate on the "mac" field.
+func MACEQ(v string) predicate.Token {
+	return predicate.Token(sql.FieldEQ(FieldMAC, v))
+}
+
+// MACNEQ applies the NEQ predicate on the "mac" field.
+func MACNEQ(v string) predicate.Token {
+	return predicate.Token(sql.FieldNEQ(FieldMAC, v))
+}
+
+// MACIn applies the In predicate on the "mac" field.
+func MACIn(vs ...string) predicate.Token {
+	return predicate.Token(sql.FieldIn(FieldMAC, vs...))
+}
+
+// MACNotIn applies the NotIn predicate on the "mac" field.
+func MACNotIn(vs ...string) predicate.Token {
+	return predicate.Token(sql.FieldNotIn(FieldMAC, vs...))
+}
+
+// MACGT applies the GT predicate on the "mac" field.
+func MACGT(v string) predicate.Token {
+	return predicate.Token(sql.FieldGT(FieldMAC, v))
+}
+
+// MACGTE applies the GTE predicate on the "mac" field.
+func MACGTE(v string) predicate.Token {
+	return predicate.Token(sql.FieldGTE(FieldMAC, v))
+}
+
+// MACLT applies the LT predicate on the "mac" field.
+func MACLT(v string) predicate.Token {
+	return predicate.Token(sql.FieldLT(FieldMAC, v))
+}
+
+// MACLTE applies the LTE predicate on the "mac" field.
+func MACLTE(v string) predicate.Token {
+	return predicate.Token(sql.FieldLTE(FieldMAC, v))
+}
+
+// MACContains applies the Contains predicate on the "mac" field.
+func MACContains(v string) predicate.Token {
+	return predicate.Token(sql.FieldContains(FieldMAC, v))
+}
+
+// MACHasPrefix applies the HasPrefix predicate on the "mac" field.
+func MACHasPrefix(v string) predicate.Token {
+	return predicate.Token(sql.FieldHasPrefix(FieldMAC, v))
+}
+
+// MACHasSuffix applies the HasSuffix predicate on the "mac" field.
+func MACHasSuffix(v string) predicate.Token {
+	return predicate.Token(sql.FieldHasSuffix(FieldMAC, v))
+}
+
+// MACEqualFold applies the EqualFold predicate on the "mac" field.
+func MACEqualFold(v string) predicate.Token {
+	return predicate.Token(sql.FieldEqualFold(FieldMAC, v))
+}
+
+// MACContainsFold applies the ContainsFold predicate on the "mac" field.
+func MACContainsFold(v string) predicate.Token {
+	return predicate.Token(sql.FieldContainsFold(FieldMAC, v))
+}
+
+// And groups predicates with the AND operator between them.
+func And(predicates ...predicate.Token) predicate.Token {
+	return predicate.Token(sql.AndPredicates(predicates...))
+}
+
+// Or groups predicates with the OR operator between them.
+func Or(predicates ...predicate.Token) predicate.Token {
+	return predicate.Token(sql.OrPredicates(predicates...))
+}
+
+// Not applies the not operator on the given predicate.
+func Not(p predicate.Token) predicate.Token {
+	return predicate.Token(sql.NotPredicates(p))
+}

+ 846 - 0
ent/token_create.go

@@ -0,0 +1,846 @@
+// Code generated by ent, DO NOT EDIT.
+
+package ent
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"time"
+	"wechat-api/ent/token"
+
+	"entgo.io/ent/dialect/sql"
+	"entgo.io/ent/dialect/sql/sqlgraph"
+	"entgo.io/ent/schema/field"
+)
+
+// TokenCreate is the builder for creating a Token entity.
+type TokenCreate struct {
+	config
+	mutation *TokenMutation
+	hooks    []Hook
+	conflict []sql.ConflictOption
+}
+
+// SetCreatedAt sets the "created_at" field.
+func (tc *TokenCreate) SetCreatedAt(t time.Time) *TokenCreate {
+	tc.mutation.SetCreatedAt(t)
+	return tc
+}
+
+// SetNillableCreatedAt sets the "created_at" field if the given value is not nil.
+func (tc *TokenCreate) SetNillableCreatedAt(t *time.Time) *TokenCreate {
+	if t != nil {
+		tc.SetCreatedAt(*t)
+	}
+	return tc
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (tc *TokenCreate) SetUpdatedAt(t time.Time) *TokenCreate {
+	tc.mutation.SetUpdatedAt(t)
+	return tc
+}
+
+// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil.
+func (tc *TokenCreate) SetNillableUpdatedAt(t *time.Time) *TokenCreate {
+	if t != nil {
+		tc.SetUpdatedAt(*t)
+	}
+	return tc
+}
+
+// SetDeletedAt sets the "deleted_at" field.
+func (tc *TokenCreate) SetDeletedAt(t time.Time) *TokenCreate {
+	tc.mutation.SetDeletedAt(t)
+	return tc
+}
+
+// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil.
+func (tc *TokenCreate) SetNillableDeletedAt(t *time.Time) *TokenCreate {
+	if t != nil {
+		tc.SetDeletedAt(*t)
+	}
+	return tc
+}
+
+// SetExpireAt sets the "expire_at" field.
+func (tc *TokenCreate) SetExpireAt(t time.Time) *TokenCreate {
+	tc.mutation.SetExpireAt(t)
+	return tc
+}
+
+// SetNillableExpireAt sets the "expire_at" field if the given value is not nil.
+func (tc *TokenCreate) SetNillableExpireAt(t *time.Time) *TokenCreate {
+	if t != nil {
+		tc.SetExpireAt(*t)
+	}
+	return tc
+}
+
+// SetToken sets the "token" field.
+func (tc *TokenCreate) SetToken(s string) *TokenCreate {
+	tc.mutation.SetToken(s)
+	return tc
+}
+
+// SetNillableToken sets the "token" field if the given value is not nil.
+func (tc *TokenCreate) SetNillableToken(s *string) *TokenCreate {
+	if s != nil {
+		tc.SetToken(*s)
+	}
+	return tc
+}
+
+// SetMAC sets the "mac" field.
+func (tc *TokenCreate) SetMAC(s string) *TokenCreate {
+	tc.mutation.SetMAC(s)
+	return tc
+}
+
+// SetNillableMAC sets the "mac" field if the given value is not nil.
+func (tc *TokenCreate) SetNillableMAC(s *string) *TokenCreate {
+	if s != nil {
+		tc.SetMAC(*s)
+	}
+	return tc
+}
+
+// SetID sets the "id" field.
+func (tc *TokenCreate) SetID(u uint64) *TokenCreate {
+	tc.mutation.SetID(u)
+	return tc
+}
+
+// Mutation returns the TokenMutation object of the builder.
+func (tc *TokenCreate) Mutation() *TokenMutation {
+	return tc.mutation
+}
+
+// Save creates the Token in the database.
+func (tc *TokenCreate) Save(ctx context.Context) (*Token, error) {
+	if err := tc.defaults(); err != nil {
+		return nil, err
+	}
+	return withHooks(ctx, tc.sqlSave, tc.mutation, tc.hooks)
+}
+
+// SaveX calls Save and panics if Save returns an error.
+func (tc *TokenCreate) SaveX(ctx context.Context) *Token {
+	v, err := tc.Save(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return v
+}
+
+// Exec executes the query.
+func (tc *TokenCreate) Exec(ctx context.Context) error {
+	_, err := tc.Save(ctx)
+	return err
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (tc *TokenCreate) ExecX(ctx context.Context) {
+	if err := tc.Exec(ctx); err != nil {
+		panic(err)
+	}
+}
+
+// defaults sets the default values of the builder before save.
+func (tc *TokenCreate) defaults() error {
+	if _, ok := tc.mutation.CreatedAt(); !ok {
+		if token.DefaultCreatedAt == nil {
+			return fmt.Errorf("ent: uninitialized token.DefaultCreatedAt (forgotten import ent/runtime?)")
+		}
+		v := token.DefaultCreatedAt()
+		tc.mutation.SetCreatedAt(v)
+	}
+	if _, ok := tc.mutation.UpdatedAt(); !ok {
+		if token.DefaultUpdatedAt == nil {
+			return fmt.Errorf("ent: uninitialized token.DefaultUpdatedAt (forgotten import ent/runtime?)")
+		}
+		v := token.DefaultUpdatedAt()
+		tc.mutation.SetUpdatedAt(v)
+	}
+	if _, ok := tc.mutation.Token(); !ok {
+		v := token.DefaultToken
+		tc.mutation.SetToken(v)
+	}
+	if _, ok := tc.mutation.MAC(); !ok {
+		v := token.DefaultMAC
+		tc.mutation.SetMAC(v)
+	}
+	return nil
+}
+
+// check runs all checks and user-defined validators on the builder.
+func (tc *TokenCreate) check() error {
+	if _, ok := tc.mutation.CreatedAt(); !ok {
+		return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "Token.created_at"`)}
+	}
+	if _, ok := tc.mutation.UpdatedAt(); !ok {
+		return &ValidationError{Name: "updated_at", err: errors.New(`ent: missing required field "Token.updated_at"`)}
+	}
+	if _, ok := tc.mutation.MAC(); !ok {
+		return &ValidationError{Name: "mac", err: errors.New(`ent: missing required field "Token.mac"`)}
+	}
+	return nil
+}
+
+func (tc *TokenCreate) sqlSave(ctx context.Context) (*Token, error) {
+	if err := tc.check(); err != nil {
+		return nil, err
+	}
+	_node, _spec := tc.createSpec()
+	if err := sqlgraph.CreateNode(ctx, tc.driver, _spec); err != nil {
+		if sqlgraph.IsConstraintError(err) {
+			err = &ConstraintError{msg: err.Error(), wrap: err}
+		}
+		return nil, err
+	}
+	if _spec.ID.Value != _node.ID {
+		id := _spec.ID.Value.(int64)
+		_node.ID = uint64(id)
+	}
+	tc.mutation.id = &_node.ID
+	tc.mutation.done = true
+	return _node, nil
+}
+
+func (tc *TokenCreate) createSpec() (*Token, *sqlgraph.CreateSpec) {
+	var (
+		_node = &Token{config: tc.config}
+		_spec = sqlgraph.NewCreateSpec(token.Table, sqlgraph.NewFieldSpec(token.FieldID, field.TypeUint64))
+	)
+	_spec.OnConflict = tc.conflict
+	if id, ok := tc.mutation.ID(); ok {
+		_node.ID = id
+		_spec.ID.Value = id
+	}
+	if value, ok := tc.mutation.CreatedAt(); ok {
+		_spec.SetField(token.FieldCreatedAt, field.TypeTime, value)
+		_node.CreatedAt = value
+	}
+	if value, ok := tc.mutation.UpdatedAt(); ok {
+		_spec.SetField(token.FieldUpdatedAt, field.TypeTime, value)
+		_node.UpdatedAt = value
+	}
+	if value, ok := tc.mutation.DeletedAt(); ok {
+		_spec.SetField(token.FieldDeletedAt, field.TypeTime, value)
+		_node.DeletedAt = value
+	}
+	if value, ok := tc.mutation.ExpireAt(); ok {
+		_spec.SetField(token.FieldExpireAt, field.TypeTime, value)
+		_node.ExpireAt = value
+	}
+	if value, ok := tc.mutation.Token(); ok {
+		_spec.SetField(token.FieldToken, field.TypeString, value)
+		_node.Token = value
+	}
+	if value, ok := tc.mutation.MAC(); ok {
+		_spec.SetField(token.FieldMAC, field.TypeString, value)
+		_node.MAC = value
+	}
+	return _node, _spec
+}
+
+// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause
+// of the `INSERT` statement. For example:
+//
+//	client.Token.Create().
+//		SetCreatedAt(v).
+//		OnConflict(
+//			// Update the row with the new values
+//			// the was proposed for insertion.
+//			sql.ResolveWithNewValues(),
+//		).
+//		// Override some of the fields with custom
+//		// update values.
+//		Update(func(u *ent.TokenUpsert) {
+//			SetCreatedAt(v+v).
+//		}).
+//		Exec(ctx)
+func (tc *TokenCreate) OnConflict(opts ...sql.ConflictOption) *TokenUpsertOne {
+	tc.conflict = opts
+	return &TokenUpsertOne{
+		create: tc,
+	}
+}
+
+// OnConflictColumns calls `OnConflict` and configures the columns
+// as conflict target. Using this option is equivalent to using:
+//
+//	client.Token.Create().
+//		OnConflict(sql.ConflictColumns(columns...)).
+//		Exec(ctx)
+func (tc *TokenCreate) OnConflictColumns(columns ...string) *TokenUpsertOne {
+	tc.conflict = append(tc.conflict, sql.ConflictColumns(columns...))
+	return &TokenUpsertOne{
+		create: tc,
+	}
+}
+
+type (
+	// TokenUpsertOne is the builder for "upsert"-ing
+	//  one Token node.
+	TokenUpsertOne struct {
+		create *TokenCreate
+	}
+
+	// TokenUpsert is the "OnConflict" setter.
+	TokenUpsert struct {
+		*sql.UpdateSet
+	}
+)
+
+// SetUpdatedAt sets the "updated_at" field.
+func (u *TokenUpsert) SetUpdatedAt(v time.Time) *TokenUpsert {
+	u.Set(token.FieldUpdatedAt, v)
+	return u
+}
+
+// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create.
+func (u *TokenUpsert) UpdateUpdatedAt() *TokenUpsert {
+	u.SetExcluded(token.FieldUpdatedAt)
+	return u
+}
+
+// SetDeletedAt sets the "deleted_at" field.
+func (u *TokenUpsert) SetDeletedAt(v time.Time) *TokenUpsert {
+	u.Set(token.FieldDeletedAt, v)
+	return u
+}
+
+// UpdateDeletedAt sets the "deleted_at" field to the value that was provided on create.
+func (u *TokenUpsert) UpdateDeletedAt() *TokenUpsert {
+	u.SetExcluded(token.FieldDeletedAt)
+	return u
+}
+
+// ClearDeletedAt clears the value of the "deleted_at" field.
+func (u *TokenUpsert) ClearDeletedAt() *TokenUpsert {
+	u.SetNull(token.FieldDeletedAt)
+	return u
+}
+
+// SetExpireAt sets the "expire_at" field.
+func (u *TokenUpsert) SetExpireAt(v time.Time) *TokenUpsert {
+	u.Set(token.FieldExpireAt, v)
+	return u
+}
+
+// UpdateExpireAt sets the "expire_at" field to the value that was provided on create.
+func (u *TokenUpsert) UpdateExpireAt() *TokenUpsert {
+	u.SetExcluded(token.FieldExpireAt)
+	return u
+}
+
+// ClearExpireAt clears the value of the "expire_at" field.
+func (u *TokenUpsert) ClearExpireAt() *TokenUpsert {
+	u.SetNull(token.FieldExpireAt)
+	return u
+}
+
+// SetToken sets the "token" field.
+func (u *TokenUpsert) SetToken(v string) *TokenUpsert {
+	u.Set(token.FieldToken, v)
+	return u
+}
+
+// UpdateToken sets the "token" field to the value that was provided on create.
+func (u *TokenUpsert) UpdateToken() *TokenUpsert {
+	u.SetExcluded(token.FieldToken)
+	return u
+}
+
+// ClearToken clears the value of the "token" field.
+func (u *TokenUpsert) ClearToken() *TokenUpsert {
+	u.SetNull(token.FieldToken)
+	return u
+}
+
+// SetMAC sets the "mac" field.
+func (u *TokenUpsert) SetMAC(v string) *TokenUpsert {
+	u.Set(token.FieldMAC, v)
+	return u
+}
+
+// UpdateMAC sets the "mac" field to the value that was provided on create.
+func (u *TokenUpsert) UpdateMAC() *TokenUpsert {
+	u.SetExcluded(token.FieldMAC)
+	return u
+}
+
+// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field.
+// Using this option is equivalent to using:
+//
+//	client.Token.Create().
+//		OnConflict(
+//			sql.ResolveWithNewValues(),
+//			sql.ResolveWith(func(u *sql.UpdateSet) {
+//				u.SetIgnore(token.FieldID)
+//			}),
+//		).
+//		Exec(ctx)
+func (u *TokenUpsertOne) UpdateNewValues() *TokenUpsertOne {
+	u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues())
+	u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) {
+		if _, exists := u.create.mutation.ID(); exists {
+			s.SetIgnore(token.FieldID)
+		}
+		if _, exists := u.create.mutation.CreatedAt(); exists {
+			s.SetIgnore(token.FieldCreatedAt)
+		}
+	}))
+	return u
+}
+
+// Ignore sets each column to itself in case of conflict.
+// Using this option is equivalent to using:
+//
+//	client.Token.Create().
+//	    OnConflict(sql.ResolveWithIgnore()).
+//	    Exec(ctx)
+func (u *TokenUpsertOne) Ignore() *TokenUpsertOne {
+	u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore())
+	return u
+}
+
+// DoNothing configures the conflict_action to `DO NOTHING`.
+// Supported only by SQLite and PostgreSQL.
+func (u *TokenUpsertOne) DoNothing() *TokenUpsertOne {
+	u.create.conflict = append(u.create.conflict, sql.DoNothing())
+	return u
+}
+
+// Update allows overriding fields `UPDATE` values. See the TokenCreate.OnConflict
+// documentation for more info.
+func (u *TokenUpsertOne) Update(set func(*TokenUpsert)) *TokenUpsertOne {
+	u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) {
+		set(&TokenUpsert{UpdateSet: update})
+	}))
+	return u
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (u *TokenUpsertOne) SetUpdatedAt(v time.Time) *TokenUpsertOne {
+	return u.Update(func(s *TokenUpsert) {
+		s.SetUpdatedAt(v)
+	})
+}
+
+// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create.
+func (u *TokenUpsertOne) UpdateUpdatedAt() *TokenUpsertOne {
+	return u.Update(func(s *TokenUpsert) {
+		s.UpdateUpdatedAt()
+	})
+}
+
+// SetDeletedAt sets the "deleted_at" field.
+func (u *TokenUpsertOne) SetDeletedAt(v time.Time) *TokenUpsertOne {
+	return u.Update(func(s *TokenUpsert) {
+		s.SetDeletedAt(v)
+	})
+}
+
+// UpdateDeletedAt sets the "deleted_at" field to the value that was provided on create.
+func (u *TokenUpsertOne) UpdateDeletedAt() *TokenUpsertOne {
+	return u.Update(func(s *TokenUpsert) {
+		s.UpdateDeletedAt()
+	})
+}
+
+// ClearDeletedAt clears the value of the "deleted_at" field.
+func (u *TokenUpsertOne) ClearDeletedAt() *TokenUpsertOne {
+	return u.Update(func(s *TokenUpsert) {
+		s.ClearDeletedAt()
+	})
+}
+
+// SetExpireAt sets the "expire_at" field.
+func (u *TokenUpsertOne) SetExpireAt(v time.Time) *TokenUpsertOne {
+	return u.Update(func(s *TokenUpsert) {
+		s.SetExpireAt(v)
+	})
+}
+
+// UpdateExpireAt sets the "expire_at" field to the value that was provided on create.
+func (u *TokenUpsertOne) UpdateExpireAt() *TokenUpsertOne {
+	return u.Update(func(s *TokenUpsert) {
+		s.UpdateExpireAt()
+	})
+}
+
+// ClearExpireAt clears the value of the "expire_at" field.
+func (u *TokenUpsertOne) ClearExpireAt() *TokenUpsertOne {
+	return u.Update(func(s *TokenUpsert) {
+		s.ClearExpireAt()
+	})
+}
+
+// SetToken sets the "token" field.
+func (u *TokenUpsertOne) SetToken(v string) *TokenUpsertOne {
+	return u.Update(func(s *TokenUpsert) {
+		s.SetToken(v)
+	})
+}
+
+// UpdateToken sets the "token" field to the value that was provided on create.
+func (u *TokenUpsertOne) UpdateToken() *TokenUpsertOne {
+	return u.Update(func(s *TokenUpsert) {
+		s.UpdateToken()
+	})
+}
+
+// ClearToken clears the value of the "token" field.
+func (u *TokenUpsertOne) ClearToken() *TokenUpsertOne {
+	return u.Update(func(s *TokenUpsert) {
+		s.ClearToken()
+	})
+}
+
+// SetMAC sets the "mac" field.
+func (u *TokenUpsertOne) SetMAC(v string) *TokenUpsertOne {
+	return u.Update(func(s *TokenUpsert) {
+		s.SetMAC(v)
+	})
+}
+
+// UpdateMAC sets the "mac" field to the value that was provided on create.
+func (u *TokenUpsertOne) UpdateMAC() *TokenUpsertOne {
+	return u.Update(func(s *TokenUpsert) {
+		s.UpdateMAC()
+	})
+}
+
+// Exec executes the query.
+func (u *TokenUpsertOne) Exec(ctx context.Context) error {
+	if len(u.create.conflict) == 0 {
+		return errors.New("ent: missing options for TokenCreate.OnConflict")
+	}
+	return u.create.Exec(ctx)
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (u *TokenUpsertOne) ExecX(ctx context.Context) {
+	if err := u.create.Exec(ctx); err != nil {
+		panic(err)
+	}
+}
+
+// Exec executes the UPSERT query and returns the inserted/updated ID.
+func (u *TokenUpsertOne) ID(ctx context.Context) (id uint64, err error) {
+	node, err := u.create.Save(ctx)
+	if err != nil {
+		return id, err
+	}
+	return node.ID, nil
+}
+
+// IDX is like ID, but panics if an error occurs.
+func (u *TokenUpsertOne) IDX(ctx context.Context) uint64 {
+	id, err := u.ID(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return id
+}
+
+// TokenCreateBulk is the builder for creating many Token entities in bulk.
+type TokenCreateBulk struct {
+	config
+	err      error
+	builders []*TokenCreate
+	conflict []sql.ConflictOption
+}
+
+// Save creates the Token entities in the database.
+func (tcb *TokenCreateBulk) Save(ctx context.Context) ([]*Token, error) {
+	if tcb.err != nil {
+		return nil, tcb.err
+	}
+	specs := make([]*sqlgraph.CreateSpec, len(tcb.builders))
+	nodes := make([]*Token, len(tcb.builders))
+	mutators := make([]Mutator, len(tcb.builders))
+	for i := range tcb.builders {
+		func(i int, root context.Context) {
+			builder := tcb.builders[i]
+			builder.defaults()
+			var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
+				mutation, ok := m.(*TokenMutation)
+				if !ok {
+					return nil, fmt.Errorf("unexpected mutation type %T", m)
+				}
+				if err := builder.check(); err != nil {
+					return nil, err
+				}
+				builder.mutation = mutation
+				var err error
+				nodes[i], specs[i] = builder.createSpec()
+				if i < len(mutators)-1 {
+					_, err = mutators[i+1].Mutate(root, tcb.builders[i+1].mutation)
+				} else {
+					spec := &sqlgraph.BatchCreateSpec{Nodes: specs}
+					spec.OnConflict = tcb.conflict
+					// Invoke the actual operation on the latest mutation in the chain.
+					if err = sqlgraph.BatchCreate(ctx, tcb.driver, spec); err != nil {
+						if sqlgraph.IsConstraintError(err) {
+							err = &ConstraintError{msg: err.Error(), wrap: err}
+						}
+					}
+				}
+				if err != nil {
+					return nil, err
+				}
+				mutation.id = &nodes[i].ID
+				if specs[i].ID.Value != nil && nodes[i].ID == 0 {
+					id := specs[i].ID.Value.(int64)
+					nodes[i].ID = uint64(id)
+				}
+				mutation.done = true
+				return nodes[i], nil
+			})
+			for i := len(builder.hooks) - 1; i >= 0; i-- {
+				mut = builder.hooks[i](mut)
+			}
+			mutators[i] = mut
+		}(i, ctx)
+	}
+	if len(mutators) > 0 {
+		if _, err := mutators[0].Mutate(ctx, tcb.builders[0].mutation); err != nil {
+			return nil, err
+		}
+	}
+	return nodes, nil
+}
+
+// SaveX is like Save, but panics if an error occurs.
+func (tcb *TokenCreateBulk) SaveX(ctx context.Context) []*Token {
+	v, err := tcb.Save(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return v
+}
+
+// Exec executes the query.
+func (tcb *TokenCreateBulk) Exec(ctx context.Context) error {
+	_, err := tcb.Save(ctx)
+	return err
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (tcb *TokenCreateBulk) ExecX(ctx context.Context) {
+	if err := tcb.Exec(ctx); err != nil {
+		panic(err)
+	}
+}
+
+// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause
+// of the `INSERT` statement. For example:
+//
+//	client.Token.CreateBulk(builders...).
+//		OnConflict(
+//			// Update the row with the new values
+//			// the was proposed for insertion.
+//			sql.ResolveWithNewValues(),
+//		).
+//		// Override some of the fields with custom
+//		// update values.
+//		Update(func(u *ent.TokenUpsert) {
+//			SetCreatedAt(v+v).
+//		}).
+//		Exec(ctx)
+func (tcb *TokenCreateBulk) OnConflict(opts ...sql.ConflictOption) *TokenUpsertBulk {
+	tcb.conflict = opts
+	return &TokenUpsertBulk{
+		create: tcb,
+	}
+}
+
+// OnConflictColumns calls `OnConflict` and configures the columns
+// as conflict target. Using this option is equivalent to using:
+//
+//	client.Token.Create().
+//		OnConflict(sql.ConflictColumns(columns...)).
+//		Exec(ctx)
+func (tcb *TokenCreateBulk) OnConflictColumns(columns ...string) *TokenUpsertBulk {
+	tcb.conflict = append(tcb.conflict, sql.ConflictColumns(columns...))
+	return &TokenUpsertBulk{
+		create: tcb,
+	}
+}
+
+// TokenUpsertBulk is the builder for "upsert"-ing
+// a bulk of Token nodes.
+type TokenUpsertBulk struct {
+	create *TokenCreateBulk
+}
+
+// UpdateNewValues updates the mutable fields using the new values that
+// were set on create. Using this option is equivalent to using:
+//
+//	client.Token.Create().
+//		OnConflict(
+//			sql.ResolveWithNewValues(),
+//			sql.ResolveWith(func(u *sql.UpdateSet) {
+//				u.SetIgnore(token.FieldID)
+//			}),
+//		).
+//		Exec(ctx)
+func (u *TokenUpsertBulk) UpdateNewValues() *TokenUpsertBulk {
+	u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues())
+	u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) {
+		for _, b := range u.create.builders {
+			if _, exists := b.mutation.ID(); exists {
+				s.SetIgnore(token.FieldID)
+			}
+			if _, exists := b.mutation.CreatedAt(); exists {
+				s.SetIgnore(token.FieldCreatedAt)
+			}
+		}
+	}))
+	return u
+}
+
+// Ignore sets each column to itself in case of conflict.
+// Using this option is equivalent to using:
+//
+//	client.Token.Create().
+//		OnConflict(sql.ResolveWithIgnore()).
+//		Exec(ctx)
+func (u *TokenUpsertBulk) Ignore() *TokenUpsertBulk {
+	u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore())
+	return u
+}
+
+// DoNothing configures the conflict_action to `DO NOTHING`.
+// Supported only by SQLite and PostgreSQL.
+func (u *TokenUpsertBulk) DoNothing() *TokenUpsertBulk {
+	u.create.conflict = append(u.create.conflict, sql.DoNothing())
+	return u
+}
+
+// Update allows overriding fields `UPDATE` values. See the TokenCreateBulk.OnConflict
+// documentation for more info.
+func (u *TokenUpsertBulk) Update(set func(*TokenUpsert)) *TokenUpsertBulk {
+	u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) {
+		set(&TokenUpsert{UpdateSet: update})
+	}))
+	return u
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (u *TokenUpsertBulk) SetUpdatedAt(v time.Time) *TokenUpsertBulk {
+	return u.Update(func(s *TokenUpsert) {
+		s.SetUpdatedAt(v)
+	})
+}
+
+// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create.
+func (u *TokenUpsertBulk) UpdateUpdatedAt() *TokenUpsertBulk {
+	return u.Update(func(s *TokenUpsert) {
+		s.UpdateUpdatedAt()
+	})
+}
+
+// SetDeletedAt sets the "deleted_at" field.
+func (u *TokenUpsertBulk) SetDeletedAt(v time.Time) *TokenUpsertBulk {
+	return u.Update(func(s *TokenUpsert) {
+		s.SetDeletedAt(v)
+	})
+}
+
+// UpdateDeletedAt sets the "deleted_at" field to the value that was provided on create.
+func (u *TokenUpsertBulk) UpdateDeletedAt() *TokenUpsertBulk {
+	return u.Update(func(s *TokenUpsert) {
+		s.UpdateDeletedAt()
+	})
+}
+
+// ClearDeletedAt clears the value of the "deleted_at" field.
+func (u *TokenUpsertBulk) ClearDeletedAt() *TokenUpsertBulk {
+	return u.Update(func(s *TokenUpsert) {
+		s.ClearDeletedAt()
+	})
+}
+
+// SetExpireAt sets the "expire_at" field.
+func (u *TokenUpsertBulk) SetExpireAt(v time.Time) *TokenUpsertBulk {
+	return u.Update(func(s *TokenUpsert) {
+		s.SetExpireAt(v)
+	})
+}
+
+// UpdateExpireAt sets the "expire_at" field to the value that was provided on create.
+func (u *TokenUpsertBulk) UpdateExpireAt() *TokenUpsertBulk {
+	return u.Update(func(s *TokenUpsert) {
+		s.UpdateExpireAt()
+	})
+}
+
+// ClearExpireAt clears the value of the "expire_at" field.
+func (u *TokenUpsertBulk) ClearExpireAt() *TokenUpsertBulk {
+	return u.Update(func(s *TokenUpsert) {
+		s.ClearExpireAt()
+	})
+}
+
+// SetToken sets the "token" field.
+func (u *TokenUpsertBulk) SetToken(v string) *TokenUpsertBulk {
+	return u.Update(func(s *TokenUpsert) {
+		s.SetToken(v)
+	})
+}
+
+// UpdateToken sets the "token" field to the value that was provided on create.
+func (u *TokenUpsertBulk) UpdateToken() *TokenUpsertBulk {
+	return u.Update(func(s *TokenUpsert) {
+		s.UpdateToken()
+	})
+}
+
+// ClearToken clears the value of the "token" field.
+func (u *TokenUpsertBulk) ClearToken() *TokenUpsertBulk {
+	return u.Update(func(s *TokenUpsert) {
+		s.ClearToken()
+	})
+}
+
+// SetMAC sets the "mac" field.
+func (u *TokenUpsertBulk) SetMAC(v string) *TokenUpsertBulk {
+	return u.Update(func(s *TokenUpsert) {
+		s.SetMAC(v)
+	})
+}
+
+// UpdateMAC sets the "mac" field to the value that was provided on create.
+func (u *TokenUpsertBulk) UpdateMAC() *TokenUpsertBulk {
+	return u.Update(func(s *TokenUpsert) {
+		s.UpdateMAC()
+	})
+}
+
+// Exec executes the query.
+func (u *TokenUpsertBulk) Exec(ctx context.Context) error {
+	if u.create.err != nil {
+		return u.create.err
+	}
+	for i, b := range u.create.builders {
+		if len(b.conflict) != 0 {
+			return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the TokenCreateBulk instead", i)
+		}
+	}
+	if len(u.create.conflict) == 0 {
+		return errors.New("ent: missing options for TokenCreateBulk.OnConflict")
+	}
+	return u.create.Exec(ctx)
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (u *TokenUpsertBulk) ExecX(ctx context.Context) {
+	if err := u.create.Exec(ctx); err != nil {
+		panic(err)
+	}
+}

+ 88 - 0
ent/token_delete.go

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

+ 526 - 0
ent/token_query.go

@@ -0,0 +1,526 @@
+// Code generated by ent, DO NOT EDIT.
+
+package ent
+
+import (
+	"context"
+	"fmt"
+	"math"
+	"wechat-api/ent/predicate"
+	"wechat-api/ent/token"
+
+	"entgo.io/ent/dialect/sql"
+	"entgo.io/ent/dialect/sql/sqlgraph"
+	"entgo.io/ent/schema/field"
+)
+
+// TokenQuery is the builder for querying Token entities.
+type TokenQuery struct {
+	config
+	ctx        *QueryContext
+	order      []token.OrderOption
+	inters     []Interceptor
+	predicates []predicate.Token
+	// intermediate query (i.e. traversal path).
+	sql  *sql.Selector
+	path func(context.Context) (*sql.Selector, error)
+}
+
+// Where adds a new predicate for the TokenQuery builder.
+func (tq *TokenQuery) Where(ps ...predicate.Token) *TokenQuery {
+	tq.predicates = append(tq.predicates, ps...)
+	return tq
+}
+
+// Limit the number of records to be returned by this query.
+func (tq *TokenQuery) Limit(limit int) *TokenQuery {
+	tq.ctx.Limit = &limit
+	return tq
+}
+
+// Offset to start from.
+func (tq *TokenQuery) Offset(offset int) *TokenQuery {
+	tq.ctx.Offset = &offset
+	return tq
+}
+
+// Unique configures the query builder to filter duplicate records on query.
+// By default, unique is set to true, and can be disabled using this method.
+func (tq *TokenQuery) Unique(unique bool) *TokenQuery {
+	tq.ctx.Unique = &unique
+	return tq
+}
+
+// Order specifies how the records should be ordered.
+func (tq *TokenQuery) Order(o ...token.OrderOption) *TokenQuery {
+	tq.order = append(tq.order, o...)
+	return tq
+}
+
+// First returns the first Token entity from the query.
+// Returns a *NotFoundError when no Token was found.
+func (tq *TokenQuery) First(ctx context.Context) (*Token, error) {
+	nodes, err := tq.Limit(1).All(setContextOp(ctx, tq.ctx, "First"))
+	if err != nil {
+		return nil, err
+	}
+	if len(nodes) == 0 {
+		return nil, &NotFoundError{token.Label}
+	}
+	return nodes[0], nil
+}
+
+// FirstX is like First, but panics if an error occurs.
+func (tq *TokenQuery) FirstX(ctx context.Context) *Token {
+	node, err := tq.First(ctx)
+	if err != nil && !IsNotFound(err) {
+		panic(err)
+	}
+	return node
+}
+
+// FirstID returns the first Token ID from the query.
+// Returns a *NotFoundError when no Token ID was found.
+func (tq *TokenQuery) FirstID(ctx context.Context) (id uint64, err error) {
+	var ids []uint64
+	if ids, err = tq.Limit(1).IDs(setContextOp(ctx, tq.ctx, "FirstID")); err != nil {
+		return
+	}
+	if len(ids) == 0 {
+		err = &NotFoundError{token.Label}
+		return
+	}
+	return ids[0], nil
+}
+
+// FirstIDX is like FirstID, but panics if an error occurs.
+func (tq *TokenQuery) FirstIDX(ctx context.Context) uint64 {
+	id, err := tq.FirstID(ctx)
+	if err != nil && !IsNotFound(err) {
+		panic(err)
+	}
+	return id
+}
+
+// Only returns a single Token entity found by the query, ensuring it only returns one.
+// Returns a *NotSingularError when more than one Token entity is found.
+// Returns a *NotFoundError when no Token entities are found.
+func (tq *TokenQuery) Only(ctx context.Context) (*Token, error) {
+	nodes, err := tq.Limit(2).All(setContextOp(ctx, tq.ctx, "Only"))
+	if err != nil {
+		return nil, err
+	}
+	switch len(nodes) {
+	case 1:
+		return nodes[0], nil
+	case 0:
+		return nil, &NotFoundError{token.Label}
+	default:
+		return nil, &NotSingularError{token.Label}
+	}
+}
+
+// OnlyX is like Only, but panics if an error occurs.
+func (tq *TokenQuery) OnlyX(ctx context.Context) *Token {
+	node, err := tq.Only(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return node
+}
+
+// OnlyID is like Only, but returns the only Token ID in the query.
+// Returns a *NotSingularError when more than one Token ID is found.
+// Returns a *NotFoundError when no entities are found.
+func (tq *TokenQuery) OnlyID(ctx context.Context) (id uint64, err error) {
+	var ids []uint64
+	if ids, err = tq.Limit(2).IDs(setContextOp(ctx, tq.ctx, "OnlyID")); err != nil {
+		return
+	}
+	switch len(ids) {
+	case 1:
+		id = ids[0]
+	case 0:
+		err = &NotFoundError{token.Label}
+	default:
+		err = &NotSingularError{token.Label}
+	}
+	return
+}
+
+// OnlyIDX is like OnlyID, but panics if an error occurs.
+func (tq *TokenQuery) OnlyIDX(ctx context.Context) uint64 {
+	id, err := tq.OnlyID(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return id
+}
+
+// All executes the query and returns a list of Tokens.
+func (tq *TokenQuery) All(ctx context.Context) ([]*Token, error) {
+	ctx = setContextOp(ctx, tq.ctx, "All")
+	if err := tq.prepareQuery(ctx); err != nil {
+		return nil, err
+	}
+	qr := querierAll[[]*Token, *TokenQuery]()
+	return withInterceptors[[]*Token](ctx, tq, qr, tq.inters)
+}
+
+// AllX is like All, but panics if an error occurs.
+func (tq *TokenQuery) AllX(ctx context.Context) []*Token {
+	nodes, err := tq.All(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return nodes
+}
+
+// IDs executes the query and returns a list of Token IDs.
+func (tq *TokenQuery) IDs(ctx context.Context) (ids []uint64, err error) {
+	if tq.ctx.Unique == nil && tq.path != nil {
+		tq.Unique(true)
+	}
+	ctx = setContextOp(ctx, tq.ctx, "IDs")
+	if err = tq.Select(token.FieldID).Scan(ctx, &ids); err != nil {
+		return nil, err
+	}
+	return ids, nil
+}
+
+// IDsX is like IDs, but panics if an error occurs.
+func (tq *TokenQuery) IDsX(ctx context.Context) []uint64 {
+	ids, err := tq.IDs(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return ids
+}
+
+// Count returns the count of the given query.
+func (tq *TokenQuery) Count(ctx context.Context) (int, error) {
+	ctx = setContextOp(ctx, tq.ctx, "Count")
+	if err := tq.prepareQuery(ctx); err != nil {
+		return 0, err
+	}
+	return withInterceptors[int](ctx, tq, querierCount[*TokenQuery](), tq.inters)
+}
+
+// CountX is like Count, but panics if an error occurs.
+func (tq *TokenQuery) CountX(ctx context.Context) int {
+	count, err := tq.Count(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return count
+}
+
+// Exist returns true if the query has elements in the graph.
+func (tq *TokenQuery) Exist(ctx context.Context) (bool, error) {
+	ctx = setContextOp(ctx, tq.ctx, "Exist")
+	switch _, err := tq.FirstID(ctx); {
+	case IsNotFound(err):
+		return false, nil
+	case err != nil:
+		return false, fmt.Errorf("ent: check existence: %w", err)
+	default:
+		return true, nil
+	}
+}
+
+// ExistX is like Exist, but panics if an error occurs.
+func (tq *TokenQuery) ExistX(ctx context.Context) bool {
+	exist, err := tq.Exist(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return exist
+}
+
+// Clone returns a duplicate of the TokenQuery builder, including all associated steps. It can be
+// used to prepare common query builders and use them differently after the clone is made.
+func (tq *TokenQuery) Clone() *TokenQuery {
+	if tq == nil {
+		return nil
+	}
+	return &TokenQuery{
+		config:     tq.config,
+		ctx:        tq.ctx.Clone(),
+		order:      append([]token.OrderOption{}, tq.order...),
+		inters:     append([]Interceptor{}, tq.inters...),
+		predicates: append([]predicate.Token{}, tq.predicates...),
+		// clone intermediate query.
+		sql:  tq.sql.Clone(),
+		path: tq.path,
+	}
+}
+
+// GroupBy is used to group vertices by one or more fields/columns.
+// It is often used with aggregate functions, like: count, max, mean, min, sum.
+//
+// Example:
+//
+//	var v []struct {
+//		CreatedAt time.Time `json:"created_at,omitempty"`
+//		Count int `json:"count,omitempty"`
+//	}
+//
+//	client.Token.Query().
+//		GroupBy(token.FieldCreatedAt).
+//		Aggregate(ent.Count()).
+//		Scan(ctx, &v)
+func (tq *TokenQuery) GroupBy(field string, fields ...string) *TokenGroupBy {
+	tq.ctx.Fields = append([]string{field}, fields...)
+	grbuild := &TokenGroupBy{build: tq}
+	grbuild.flds = &tq.ctx.Fields
+	grbuild.label = token.Label
+	grbuild.scan = grbuild.Scan
+	return grbuild
+}
+
+// Select allows the selection one or more fields/columns for the given query,
+// instead of selecting all fields in the entity.
+//
+// Example:
+//
+//	var v []struct {
+//		CreatedAt time.Time `json:"created_at,omitempty"`
+//	}
+//
+//	client.Token.Query().
+//		Select(token.FieldCreatedAt).
+//		Scan(ctx, &v)
+func (tq *TokenQuery) Select(fields ...string) *TokenSelect {
+	tq.ctx.Fields = append(tq.ctx.Fields, fields...)
+	sbuild := &TokenSelect{TokenQuery: tq}
+	sbuild.label = token.Label
+	sbuild.flds, sbuild.scan = &tq.ctx.Fields, sbuild.Scan
+	return sbuild
+}
+
+// Aggregate returns a TokenSelect configured with the given aggregations.
+func (tq *TokenQuery) Aggregate(fns ...AggregateFunc) *TokenSelect {
+	return tq.Select().Aggregate(fns...)
+}
+
+func (tq *TokenQuery) prepareQuery(ctx context.Context) error {
+	for _, inter := range tq.inters {
+		if inter == nil {
+			return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)")
+		}
+		if trv, ok := inter.(Traverser); ok {
+			if err := trv.Traverse(ctx, tq); err != nil {
+				return err
+			}
+		}
+	}
+	for _, f := range tq.ctx.Fields {
+		if !token.ValidColumn(f) {
+			return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
+		}
+	}
+	if tq.path != nil {
+		prev, err := tq.path(ctx)
+		if err != nil {
+			return err
+		}
+		tq.sql = prev
+	}
+	return nil
+}
+
+func (tq *TokenQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Token, error) {
+	var (
+		nodes = []*Token{}
+		_spec = tq.querySpec()
+	)
+	_spec.ScanValues = func(columns []string) ([]any, error) {
+		return (*Token).scanValues(nil, columns)
+	}
+	_spec.Assign = func(columns []string, values []any) error {
+		node := &Token{config: tq.config}
+		nodes = append(nodes, node)
+		return node.assignValues(columns, values)
+	}
+	for i := range hooks {
+		hooks[i](ctx, _spec)
+	}
+	if err := sqlgraph.QueryNodes(ctx, tq.driver, _spec); err != nil {
+		return nil, err
+	}
+	if len(nodes) == 0 {
+		return nodes, nil
+	}
+	return nodes, nil
+}
+
+func (tq *TokenQuery) sqlCount(ctx context.Context) (int, error) {
+	_spec := tq.querySpec()
+	_spec.Node.Columns = tq.ctx.Fields
+	if len(tq.ctx.Fields) > 0 {
+		_spec.Unique = tq.ctx.Unique != nil && *tq.ctx.Unique
+	}
+	return sqlgraph.CountNodes(ctx, tq.driver, _spec)
+}
+
+func (tq *TokenQuery) querySpec() *sqlgraph.QuerySpec {
+	_spec := sqlgraph.NewQuerySpec(token.Table, token.Columns, sqlgraph.NewFieldSpec(token.FieldID, field.TypeUint64))
+	_spec.From = tq.sql
+	if unique := tq.ctx.Unique; unique != nil {
+		_spec.Unique = *unique
+	} else if tq.path != nil {
+		_spec.Unique = true
+	}
+	if fields := tq.ctx.Fields; len(fields) > 0 {
+		_spec.Node.Columns = make([]string, 0, len(fields))
+		_spec.Node.Columns = append(_spec.Node.Columns, token.FieldID)
+		for i := range fields {
+			if fields[i] != token.FieldID {
+				_spec.Node.Columns = append(_spec.Node.Columns, fields[i])
+			}
+		}
+	}
+	if ps := tq.predicates; len(ps) > 0 {
+		_spec.Predicate = func(selector *sql.Selector) {
+			for i := range ps {
+				ps[i](selector)
+			}
+		}
+	}
+	if limit := tq.ctx.Limit; limit != nil {
+		_spec.Limit = *limit
+	}
+	if offset := tq.ctx.Offset; offset != nil {
+		_spec.Offset = *offset
+	}
+	if ps := tq.order; len(ps) > 0 {
+		_spec.Order = func(selector *sql.Selector) {
+			for i := range ps {
+				ps[i](selector)
+			}
+		}
+	}
+	return _spec
+}
+
+func (tq *TokenQuery) sqlQuery(ctx context.Context) *sql.Selector {
+	builder := sql.Dialect(tq.driver.Dialect())
+	t1 := builder.Table(token.Table)
+	columns := tq.ctx.Fields
+	if len(columns) == 0 {
+		columns = token.Columns
+	}
+	selector := builder.Select(t1.Columns(columns...)...).From(t1)
+	if tq.sql != nil {
+		selector = tq.sql
+		selector.Select(selector.Columns(columns...)...)
+	}
+	if tq.ctx.Unique != nil && *tq.ctx.Unique {
+		selector.Distinct()
+	}
+	for _, p := range tq.predicates {
+		p(selector)
+	}
+	for _, p := range tq.order {
+		p(selector)
+	}
+	if offset := tq.ctx.Offset; offset != nil {
+		// limit is mandatory for offset clause. We start
+		// with default value, and override it below if needed.
+		selector.Offset(*offset).Limit(math.MaxInt32)
+	}
+	if limit := tq.ctx.Limit; limit != nil {
+		selector.Limit(*limit)
+	}
+	return selector
+}
+
+// TokenGroupBy is the group-by builder for Token entities.
+type TokenGroupBy struct {
+	selector
+	build *TokenQuery
+}
+
+// Aggregate adds the given aggregation functions to the group-by query.
+func (tgb *TokenGroupBy) Aggregate(fns ...AggregateFunc) *TokenGroupBy {
+	tgb.fns = append(tgb.fns, fns...)
+	return tgb
+}
+
+// Scan applies the selector query and scans the result into the given value.
+func (tgb *TokenGroupBy) Scan(ctx context.Context, v any) error {
+	ctx = setContextOp(ctx, tgb.build.ctx, "GroupBy")
+	if err := tgb.build.prepareQuery(ctx); err != nil {
+		return err
+	}
+	return scanWithInterceptors[*TokenQuery, *TokenGroupBy](ctx, tgb.build, tgb, tgb.build.inters, v)
+}
+
+func (tgb *TokenGroupBy) sqlScan(ctx context.Context, root *TokenQuery, v any) error {
+	selector := root.sqlQuery(ctx).Select()
+	aggregation := make([]string, 0, len(tgb.fns))
+	for _, fn := range tgb.fns {
+		aggregation = append(aggregation, fn(selector))
+	}
+	if len(selector.SelectedColumns()) == 0 {
+		columns := make([]string, 0, len(*tgb.flds)+len(tgb.fns))
+		for _, f := range *tgb.flds {
+			columns = append(columns, selector.C(f))
+		}
+		columns = append(columns, aggregation...)
+		selector.Select(columns...)
+	}
+	selector.GroupBy(selector.Columns(*tgb.flds...)...)
+	if err := selector.Err(); err != nil {
+		return err
+	}
+	rows := &sql.Rows{}
+	query, args := selector.Query()
+	if err := tgb.build.driver.Query(ctx, query, args, rows); err != nil {
+		return err
+	}
+	defer rows.Close()
+	return sql.ScanSlice(rows, v)
+}
+
+// TokenSelect is the builder for selecting fields of Token entities.
+type TokenSelect struct {
+	*TokenQuery
+	selector
+}
+
+// Aggregate adds the given aggregation functions to the selector query.
+func (ts *TokenSelect) Aggregate(fns ...AggregateFunc) *TokenSelect {
+	ts.fns = append(ts.fns, fns...)
+	return ts
+}
+
+// Scan applies the selector query and scans the result into the given value.
+func (ts *TokenSelect) Scan(ctx context.Context, v any) error {
+	ctx = setContextOp(ctx, ts.ctx, "Select")
+	if err := ts.prepareQuery(ctx); err != nil {
+		return err
+	}
+	return scanWithInterceptors[*TokenQuery, *TokenSelect](ctx, ts.TokenQuery, ts, ts.inters, v)
+}
+
+func (ts *TokenSelect) sqlScan(ctx context.Context, root *TokenQuery, v any) error {
+	selector := root.sqlQuery(ctx)
+	aggregation := make([]string, 0, len(ts.fns))
+	for _, fn := range ts.fns {
+		aggregation = append(aggregation, fn(selector))
+	}
+	switch n := len(*ts.selector.flds); {
+	case n == 0 && len(aggregation) > 0:
+		selector.Select(aggregation...)
+	case n != 0 && len(aggregation) > 0:
+		selector.AppendSelect(aggregation...)
+	}
+	rows := &sql.Rows{}
+	query, args := selector.Query()
+	if err := ts.driver.Query(ctx, query, args, rows); err != nil {
+		return err
+	}
+	defer rows.Close()
+	return sql.ScanSlice(rows, v)
+}

+ 414 - 0
ent/token_update.go

@@ -0,0 +1,414 @@
+// Code generated by ent, DO NOT EDIT.
+
+package ent
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"time"
+	"wechat-api/ent/predicate"
+	"wechat-api/ent/token"
+
+	"entgo.io/ent/dialect/sql"
+	"entgo.io/ent/dialect/sql/sqlgraph"
+	"entgo.io/ent/schema/field"
+)
+
+// TokenUpdate is the builder for updating Token entities.
+type TokenUpdate struct {
+	config
+	hooks    []Hook
+	mutation *TokenMutation
+}
+
+// Where appends a list predicates to the TokenUpdate builder.
+func (tu *TokenUpdate) Where(ps ...predicate.Token) *TokenUpdate {
+	tu.mutation.Where(ps...)
+	return tu
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (tu *TokenUpdate) SetUpdatedAt(t time.Time) *TokenUpdate {
+	tu.mutation.SetUpdatedAt(t)
+	return tu
+}
+
+// SetDeletedAt sets the "deleted_at" field.
+func (tu *TokenUpdate) SetDeletedAt(t time.Time) *TokenUpdate {
+	tu.mutation.SetDeletedAt(t)
+	return tu
+}
+
+// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil.
+func (tu *TokenUpdate) SetNillableDeletedAt(t *time.Time) *TokenUpdate {
+	if t != nil {
+		tu.SetDeletedAt(*t)
+	}
+	return tu
+}
+
+// ClearDeletedAt clears the value of the "deleted_at" field.
+func (tu *TokenUpdate) ClearDeletedAt() *TokenUpdate {
+	tu.mutation.ClearDeletedAt()
+	return tu
+}
+
+// SetExpireAt sets the "expire_at" field.
+func (tu *TokenUpdate) SetExpireAt(t time.Time) *TokenUpdate {
+	tu.mutation.SetExpireAt(t)
+	return tu
+}
+
+// SetNillableExpireAt sets the "expire_at" field if the given value is not nil.
+func (tu *TokenUpdate) SetNillableExpireAt(t *time.Time) *TokenUpdate {
+	if t != nil {
+		tu.SetExpireAt(*t)
+	}
+	return tu
+}
+
+// ClearExpireAt clears the value of the "expire_at" field.
+func (tu *TokenUpdate) ClearExpireAt() *TokenUpdate {
+	tu.mutation.ClearExpireAt()
+	return tu
+}
+
+// SetToken sets the "token" field.
+func (tu *TokenUpdate) SetToken(s string) *TokenUpdate {
+	tu.mutation.SetToken(s)
+	return tu
+}
+
+// SetNillableToken sets the "token" field if the given value is not nil.
+func (tu *TokenUpdate) SetNillableToken(s *string) *TokenUpdate {
+	if s != nil {
+		tu.SetToken(*s)
+	}
+	return tu
+}
+
+// ClearToken clears the value of the "token" field.
+func (tu *TokenUpdate) ClearToken() *TokenUpdate {
+	tu.mutation.ClearToken()
+	return tu
+}
+
+// SetMAC sets the "mac" field.
+func (tu *TokenUpdate) SetMAC(s string) *TokenUpdate {
+	tu.mutation.SetMAC(s)
+	return tu
+}
+
+// SetNillableMAC sets the "mac" field if the given value is not nil.
+func (tu *TokenUpdate) SetNillableMAC(s *string) *TokenUpdate {
+	if s != nil {
+		tu.SetMAC(*s)
+	}
+	return tu
+}
+
+// Mutation returns the TokenMutation object of the builder.
+func (tu *TokenUpdate) Mutation() *TokenMutation {
+	return tu.mutation
+}
+
+// Save executes the query and returns the number of nodes affected by the update operation.
+func (tu *TokenUpdate) Save(ctx context.Context) (int, error) {
+	if err := tu.defaults(); err != nil {
+		return 0, err
+	}
+	return withHooks(ctx, tu.sqlSave, tu.mutation, tu.hooks)
+}
+
+// SaveX is like Save, but panics if an error occurs.
+func (tu *TokenUpdate) SaveX(ctx context.Context) int {
+	affected, err := tu.Save(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return affected
+}
+
+// Exec executes the query.
+func (tu *TokenUpdate) Exec(ctx context.Context) error {
+	_, err := tu.Save(ctx)
+	return err
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (tu *TokenUpdate) ExecX(ctx context.Context) {
+	if err := tu.Exec(ctx); err != nil {
+		panic(err)
+	}
+}
+
+// defaults sets the default values of the builder before save.
+func (tu *TokenUpdate) defaults() error {
+	if _, ok := tu.mutation.UpdatedAt(); !ok {
+		if token.UpdateDefaultUpdatedAt == nil {
+			return fmt.Errorf("ent: uninitialized token.UpdateDefaultUpdatedAt (forgotten import ent/runtime?)")
+		}
+		v := token.UpdateDefaultUpdatedAt()
+		tu.mutation.SetUpdatedAt(v)
+	}
+	return nil
+}
+
+func (tu *TokenUpdate) sqlSave(ctx context.Context) (n int, err error) {
+	_spec := sqlgraph.NewUpdateSpec(token.Table, token.Columns, sqlgraph.NewFieldSpec(token.FieldID, field.TypeUint64))
+	if ps := tu.mutation.predicates; len(ps) > 0 {
+		_spec.Predicate = func(selector *sql.Selector) {
+			for i := range ps {
+				ps[i](selector)
+			}
+		}
+	}
+	if value, ok := tu.mutation.UpdatedAt(); ok {
+		_spec.SetField(token.FieldUpdatedAt, field.TypeTime, value)
+	}
+	if value, ok := tu.mutation.DeletedAt(); ok {
+		_spec.SetField(token.FieldDeletedAt, field.TypeTime, value)
+	}
+	if tu.mutation.DeletedAtCleared() {
+		_spec.ClearField(token.FieldDeletedAt, field.TypeTime)
+	}
+	if value, ok := tu.mutation.ExpireAt(); ok {
+		_spec.SetField(token.FieldExpireAt, field.TypeTime, value)
+	}
+	if tu.mutation.ExpireAtCleared() {
+		_spec.ClearField(token.FieldExpireAt, field.TypeTime)
+	}
+	if value, ok := tu.mutation.Token(); ok {
+		_spec.SetField(token.FieldToken, field.TypeString, value)
+	}
+	if tu.mutation.TokenCleared() {
+		_spec.ClearField(token.FieldToken, field.TypeString)
+	}
+	if value, ok := tu.mutation.MAC(); ok {
+		_spec.SetField(token.FieldMAC, field.TypeString, value)
+	}
+	if n, err = sqlgraph.UpdateNodes(ctx, tu.driver, _spec); err != nil {
+		if _, ok := err.(*sqlgraph.NotFoundError); ok {
+			err = &NotFoundError{token.Label}
+		} else if sqlgraph.IsConstraintError(err) {
+			err = &ConstraintError{msg: err.Error(), wrap: err}
+		}
+		return 0, err
+	}
+	tu.mutation.done = true
+	return n, nil
+}
+
+// TokenUpdateOne is the builder for updating a single Token entity.
+type TokenUpdateOne struct {
+	config
+	fields   []string
+	hooks    []Hook
+	mutation *TokenMutation
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (tuo *TokenUpdateOne) SetUpdatedAt(t time.Time) *TokenUpdateOne {
+	tuo.mutation.SetUpdatedAt(t)
+	return tuo
+}
+
+// SetDeletedAt sets the "deleted_at" field.
+func (tuo *TokenUpdateOne) SetDeletedAt(t time.Time) *TokenUpdateOne {
+	tuo.mutation.SetDeletedAt(t)
+	return tuo
+}
+
+// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil.
+func (tuo *TokenUpdateOne) SetNillableDeletedAt(t *time.Time) *TokenUpdateOne {
+	if t != nil {
+		tuo.SetDeletedAt(*t)
+	}
+	return tuo
+}
+
+// ClearDeletedAt clears the value of the "deleted_at" field.
+func (tuo *TokenUpdateOne) ClearDeletedAt() *TokenUpdateOne {
+	tuo.mutation.ClearDeletedAt()
+	return tuo
+}
+
+// SetExpireAt sets the "expire_at" field.
+func (tuo *TokenUpdateOne) SetExpireAt(t time.Time) *TokenUpdateOne {
+	tuo.mutation.SetExpireAt(t)
+	return tuo
+}
+
+// SetNillableExpireAt sets the "expire_at" field if the given value is not nil.
+func (tuo *TokenUpdateOne) SetNillableExpireAt(t *time.Time) *TokenUpdateOne {
+	if t != nil {
+		tuo.SetExpireAt(*t)
+	}
+	return tuo
+}
+
+// ClearExpireAt clears the value of the "expire_at" field.
+func (tuo *TokenUpdateOne) ClearExpireAt() *TokenUpdateOne {
+	tuo.mutation.ClearExpireAt()
+	return tuo
+}
+
+// SetToken sets the "token" field.
+func (tuo *TokenUpdateOne) SetToken(s string) *TokenUpdateOne {
+	tuo.mutation.SetToken(s)
+	return tuo
+}
+
+// SetNillableToken sets the "token" field if the given value is not nil.
+func (tuo *TokenUpdateOne) SetNillableToken(s *string) *TokenUpdateOne {
+	if s != nil {
+		tuo.SetToken(*s)
+	}
+	return tuo
+}
+
+// ClearToken clears the value of the "token" field.
+func (tuo *TokenUpdateOne) ClearToken() *TokenUpdateOne {
+	tuo.mutation.ClearToken()
+	return tuo
+}
+
+// SetMAC sets the "mac" field.
+func (tuo *TokenUpdateOne) SetMAC(s string) *TokenUpdateOne {
+	tuo.mutation.SetMAC(s)
+	return tuo
+}
+
+// SetNillableMAC sets the "mac" field if the given value is not nil.
+func (tuo *TokenUpdateOne) SetNillableMAC(s *string) *TokenUpdateOne {
+	if s != nil {
+		tuo.SetMAC(*s)
+	}
+	return tuo
+}
+
+// Mutation returns the TokenMutation object of the builder.
+func (tuo *TokenUpdateOne) Mutation() *TokenMutation {
+	return tuo.mutation
+}
+
+// Where appends a list predicates to the TokenUpdate builder.
+func (tuo *TokenUpdateOne) Where(ps ...predicate.Token) *TokenUpdateOne {
+	tuo.mutation.Where(ps...)
+	return tuo
+}
+
+// Select allows selecting one or more fields (columns) of the returned entity.
+// The default is selecting all fields defined in the entity schema.
+func (tuo *TokenUpdateOne) Select(field string, fields ...string) *TokenUpdateOne {
+	tuo.fields = append([]string{field}, fields...)
+	return tuo
+}
+
+// Save executes the query and returns the updated Token entity.
+func (tuo *TokenUpdateOne) Save(ctx context.Context) (*Token, error) {
+	if err := tuo.defaults(); err != nil {
+		return nil, err
+	}
+	return withHooks(ctx, tuo.sqlSave, tuo.mutation, tuo.hooks)
+}
+
+// SaveX is like Save, but panics if an error occurs.
+func (tuo *TokenUpdateOne) SaveX(ctx context.Context) *Token {
+	node, err := tuo.Save(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return node
+}
+
+// Exec executes the query on the entity.
+func (tuo *TokenUpdateOne) Exec(ctx context.Context) error {
+	_, err := tuo.Save(ctx)
+	return err
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (tuo *TokenUpdateOne) ExecX(ctx context.Context) {
+	if err := tuo.Exec(ctx); err != nil {
+		panic(err)
+	}
+}
+
+// defaults sets the default values of the builder before save.
+func (tuo *TokenUpdateOne) defaults() error {
+	if _, ok := tuo.mutation.UpdatedAt(); !ok {
+		if token.UpdateDefaultUpdatedAt == nil {
+			return fmt.Errorf("ent: uninitialized token.UpdateDefaultUpdatedAt (forgotten import ent/runtime?)")
+		}
+		v := token.UpdateDefaultUpdatedAt()
+		tuo.mutation.SetUpdatedAt(v)
+	}
+	return nil
+}
+
+func (tuo *TokenUpdateOne) sqlSave(ctx context.Context) (_node *Token, err error) {
+	_spec := sqlgraph.NewUpdateSpec(token.Table, token.Columns, sqlgraph.NewFieldSpec(token.FieldID, field.TypeUint64))
+	id, ok := tuo.mutation.ID()
+	if !ok {
+		return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "Token.id" for update`)}
+	}
+	_spec.Node.ID.Value = id
+	if fields := tuo.fields; len(fields) > 0 {
+		_spec.Node.Columns = make([]string, 0, len(fields))
+		_spec.Node.Columns = append(_spec.Node.Columns, token.FieldID)
+		for _, f := range fields {
+			if !token.ValidColumn(f) {
+				return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
+			}
+			if f != token.FieldID {
+				_spec.Node.Columns = append(_spec.Node.Columns, f)
+			}
+		}
+	}
+	if ps := tuo.mutation.predicates; len(ps) > 0 {
+		_spec.Predicate = func(selector *sql.Selector) {
+			for i := range ps {
+				ps[i](selector)
+			}
+		}
+	}
+	if value, ok := tuo.mutation.UpdatedAt(); ok {
+		_spec.SetField(token.FieldUpdatedAt, field.TypeTime, value)
+	}
+	if value, ok := tuo.mutation.DeletedAt(); ok {
+		_spec.SetField(token.FieldDeletedAt, field.TypeTime, value)
+	}
+	if tuo.mutation.DeletedAtCleared() {
+		_spec.ClearField(token.FieldDeletedAt, field.TypeTime)
+	}
+	if value, ok := tuo.mutation.ExpireAt(); ok {
+		_spec.SetField(token.FieldExpireAt, field.TypeTime, value)
+	}
+	if tuo.mutation.ExpireAtCleared() {
+		_spec.ClearField(token.FieldExpireAt, field.TypeTime)
+	}
+	if value, ok := tuo.mutation.Token(); ok {
+		_spec.SetField(token.FieldToken, field.TypeString, value)
+	}
+	if tuo.mutation.TokenCleared() {
+		_spec.ClearField(token.FieldToken, field.TypeString)
+	}
+	if value, ok := tuo.mutation.MAC(); ok {
+		_spec.SetField(token.FieldMAC, field.TypeString, value)
+	}
+	_node = &Token{config: tuo.config}
+	_spec.Assign = _node.assignValues
+	_spec.ScanValues = _node.scanValues
+	if err = sqlgraph.UpdateNode(ctx, tuo.driver, _spec); err != nil {
+		if _, ok := err.(*sqlgraph.NotFoundError); ok {
+			err = &NotFoundError{token.Label}
+		} else if sqlgraph.IsConstraintError(err) {
+			err = &ConstraintError{msg: err.Error(), wrap: err}
+		}
+		return nil, err
+	}
+	tuo.mutation.done = true
+	return _node, nil
+}

+ 3 - 0
ent/tx.go

@@ -40,6 +40,8 @@ type Tx struct {
 	SopStage *SopStageClient
 	// SopTask is the client for interacting with the SopTask builders.
 	SopTask *SopTaskClient
+	// Token is the client for interacting with the Token builders.
+	Token *TokenClient
 	// Tutorial is the client for interacting with the Tutorial builders.
 	Tutorial *TutorialClient
 	// WorkExperience is the client for interacting with the WorkExperience builders.
@@ -190,6 +192,7 @@ func (tx *Tx) init() {
 	tx.SopNode = NewSopNodeClient(tx.config)
 	tx.SopStage = NewSopStageClient(tx.config)
 	tx.SopTask = NewSopTaskClient(tx.config)
+	tx.Token = NewTokenClient(tx.config)
 	tx.Tutorial = NewTutorialClient(tx.config)
 	tx.WorkExperience = NewWorkExperienceClient(tx.config)
 	tx.Wx = NewWxClient(tx.config)

+ 36 - 0
internal/handler/routes.go

@@ -22,6 +22,7 @@ import (
 	sop_node "wechat-api/internal/handler/sop_node"
 	sop_stage "wechat-api/internal/handler/sop_stage"
 	sop_task "wechat-api/internal/handler/sop_task"
+	token "wechat-api/internal/handler/token"
 	tutorial "wechat-api/internal/handler/tutorial"
 	work_experience "wechat-api/internal/handler/work_experience"
 	"wechat-api/internal/svc"
@@ -697,4 +698,39 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
 		),
 		rest.WithJwt(serverCtx.Config.Auth.AccessSecret),
 	)
+
+	server.AddRoutes(
+		[]rest.Route{
+			{
+				Method:  http.MethodPost,
+				Path:    "/token/create",
+				Handler: token.CreateTokenHandler(serverCtx),
+			},
+			{
+				Method:  http.MethodPost,
+				Path:    "/token/update",
+				Handler: token.UpdateTokenHandler(serverCtx),
+			},
+			{
+				Method:  http.MethodPost,
+				Path:    "/token/delete",
+				Handler: token.DeleteTokenHandler(serverCtx),
+			},
+			{
+				Method:  http.MethodPost,
+				Path:    "/token/list",
+				Handler: token.GetTokenListHandler(serverCtx),
+			},
+			{
+				Method:  http.MethodPost,
+				Path:    "/token",
+				Handler: token.GetTokenByIdHandler(serverCtx),
+			},
+			{
+				Method:  http.MethodPost,
+				Path:    "/token/check",
+				Handler: token.CheckTokenHandler(serverCtx),
+			},
+		},
+	)
 }

+ 44 - 0
internal/handler/token/check_token_handler.go

@@ -0,0 +1,44 @@
+package token
+
+import (
+	"net/http"
+
+	"github.com/zeromicro/go-zero/rest/httpx"
+
+	"wechat-api/internal/logic/token"
+	"wechat-api/internal/svc"
+	"wechat-api/internal/types"
+)
+
+// swagger:route post /token/check token CheckToken
+//
+// Check if token and mac are valid | 检查token和mac的合法性
+//
+// Check if token and mac are valid | 检查token和mac的合法性
+//
+// Parameters:
+//  + name: body
+//    require: true
+//    in: body
+//    type: CheckTokenReq
+//
+// Responses:
+//  200: CheckTokenResp
+
+func CheckTokenHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+		var req types.CheckTokenReq
+		if err := httpx.Parse(r, &req, true); err != nil {
+			httpx.ErrorCtx(r.Context(), w, err)
+			return
+		}
+
+		l := token.NewCheckTokenLogic(r.Context(), svcCtx)
+		resp, err := l.CheckToken(&req)
+		if err != nil {
+			httpx.ErrorCtx(r.Context(), w, err)
+		} else {
+			httpx.OkJsonCtx(r.Context(), w, resp)
+		}
+	}
+}

+ 44 - 0
internal/handler/token/create_token_handler.go

@@ -0,0 +1,44 @@
+package token
+
+import (
+	"net/http"
+
+	"github.com/zeromicro/go-zero/rest/httpx"
+
+	"wechat-api/internal/logic/token"
+	"wechat-api/internal/svc"
+	"wechat-api/internal/types"
+)
+
+// swagger:route post /token/create token CreateToken
+//
+// Create token information | 创建Token
+//
+// Create token information | 创建Token
+//
+// Parameters:
+//  + name: body
+//    require: true
+//    in: body
+//    type: TokenInfo
+//
+// Responses:
+//  200: BaseMsgResp
+
+func CreateTokenHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+		var req types.TokenInfo
+		if err := httpx.Parse(r, &req, true); err != nil {
+			httpx.ErrorCtx(r.Context(), w, err)
+			return
+		}
+
+		l := token.NewCreateTokenLogic(r.Context(), svcCtx)
+		resp, err := l.CreateToken(&req)
+		if err != nil {
+			httpx.ErrorCtx(r.Context(), w, err)
+		} else {
+			httpx.OkJsonCtx(r.Context(), w, resp)
+		}
+	}
+}

+ 44 - 0
internal/handler/token/delete_token_handler.go

@@ -0,0 +1,44 @@
+package token
+
+import (
+	"net/http"
+
+	"github.com/zeromicro/go-zero/rest/httpx"
+
+	"wechat-api/internal/logic/token"
+	"wechat-api/internal/svc"
+	"wechat-api/internal/types"
+)
+
+// swagger:route post /token/delete token DeleteToken
+//
+// Delete token information | 删除Token信息
+//
+// Delete token information | 删除Token信息
+//
+// Parameters:
+//  + name: body
+//    require: true
+//    in: body
+//    type: IDsReq
+//
+// Responses:
+//  200: BaseMsgResp
+
+func DeleteTokenHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+		var req types.IDsReq
+		if err := httpx.Parse(r, &req, true); err != nil {
+			httpx.ErrorCtx(r.Context(), w, err)
+			return
+		}
+
+		l := token.NewDeleteTokenLogic(r.Context(), svcCtx)
+		resp, err := l.DeleteToken(&req)
+		if err != nil {
+			httpx.ErrorCtx(r.Context(), w, err)
+		} else {
+			httpx.OkJsonCtx(r.Context(), w, resp)
+		}
+	}
+}

+ 44 - 0
internal/handler/token/get_token_by_id_handler.go

@@ -0,0 +1,44 @@
+package token
+
+import (
+	"net/http"
+
+	"github.com/zeromicro/go-zero/rest/httpx"
+
+	"wechat-api/internal/logic/token"
+	"wechat-api/internal/svc"
+	"wechat-api/internal/types"
+)
+
+// swagger:route post /token token GetTokenById
+//
+// Get token by ID | 通过ID获取Token
+//
+// Get token by ID | 通过ID获取Token
+//
+// Parameters:
+//  + name: body
+//    require: true
+//    in: body
+//    type: IDReq
+//
+// Responses:
+//  200: TokenInfoResp
+
+func GetTokenByIdHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+		var req types.IDReq
+		if err := httpx.Parse(r, &req, true); err != nil {
+			httpx.ErrorCtx(r.Context(), w, err)
+			return
+		}
+
+		l := token.NewGetTokenByIdLogic(r.Context(), svcCtx)
+		resp, err := l.GetTokenById(&req)
+		if err != nil {
+			httpx.ErrorCtx(r.Context(), w, err)
+		} else {
+			httpx.OkJsonCtx(r.Context(), w, resp)
+		}
+	}
+}

+ 44 - 0
internal/handler/token/get_token_list_handler.go

@@ -0,0 +1,44 @@
+package token
+
+import (
+	"net/http"
+
+	"github.com/zeromicro/go-zero/rest/httpx"
+
+	"wechat-api/internal/logic/token"
+	"wechat-api/internal/svc"
+	"wechat-api/internal/types"
+)
+
+// swagger:route post /token/list token GetTokenList
+//
+// Get token list | 获取Token列表
+//
+// Get token list | 获取Token列表
+//
+// Parameters:
+//  + name: body
+//    require: true
+//    in: body
+//    type: TokenListReq
+//
+// Responses:
+//  200: TokenListResp
+
+func GetTokenListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+		var req types.TokenListReq
+		if err := httpx.Parse(r, &req, true); err != nil {
+			httpx.ErrorCtx(r.Context(), w, err)
+			return
+		}
+
+		l := token.NewGetTokenListLogic(r.Context(), svcCtx)
+		resp, err := l.GetTokenList(&req)
+		if err != nil {
+			httpx.ErrorCtx(r.Context(), w, err)
+		} else {
+			httpx.OkJsonCtx(r.Context(), w, resp)
+		}
+	}
+}

+ 44 - 0
internal/handler/token/update_token_handler.go

@@ -0,0 +1,44 @@
+package token
+
+import (
+	"net/http"
+
+	"github.com/zeromicro/go-zero/rest/httpx"
+
+	"wechat-api/internal/logic/token"
+	"wechat-api/internal/svc"
+	"wechat-api/internal/types"
+)
+
+// swagger:route post /token/update token UpdateToken
+//
+// Update token information | 更新Token
+//
+// Update token information | 更新Token
+//
+// Parameters:
+//  + name: body
+//    require: true
+//    in: body
+//    type: TokenInfo
+//
+// Responses:
+//  200: BaseMsgResp
+
+func UpdateTokenHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+		var req types.TokenInfo
+		if err := httpx.Parse(r, &req, true); err != nil {
+			httpx.ErrorCtx(r.Context(), w, err)
+			return
+		}
+
+		l := token.NewUpdateTokenLogic(r.Context(), svcCtx)
+		resp, err := l.UpdateToken(&req)
+		if err != nil {
+			httpx.ErrorCtx(r.Context(), w, err)
+		} else {
+			httpx.OkJsonCtx(r.Context(), w, resp)
+		}
+	}
+}

+ 62 - 0
internal/logic/base/init_api_data.go

@@ -6,6 +6,68 @@ import (
 )
 
 func (l *InitDatabaseLogic) insertApiData() (err error) {
+	// Token
+
+	_, err = l.svcCtx.CoreRpc.CreateApi(l.ctx, &core.ApiInfo{
+		ServiceName: pointy.GetPointer("Wechat"),
+		Path:        pointy.GetPointer("/token/create"),
+		Description: pointy.GetPointer("apiDesc.createToken"),
+		ApiGroup:    pointy.GetPointer("token"),
+		Method:      pointy.GetPointer("POST"),
+	})
+
+	if err != nil {
+		return err
+	}
+
+	_, err = l.svcCtx.CoreRpc.CreateApi(l.ctx, &core.ApiInfo{
+		ServiceName: pointy.GetPointer("Wechat"),
+		Path:        pointy.GetPointer("/token/update"),
+		Description: pointy.GetPointer("apiDesc.updateToken"),
+		ApiGroup:    pointy.GetPointer("token"),
+		Method:      pointy.GetPointer("POST"),
+	})
+
+	if err != nil {
+		return err
+	}
+
+	_, err = l.svcCtx.CoreRpc.CreateApi(l.ctx, &core.ApiInfo{
+		ServiceName: pointy.GetPointer("Wechat"),
+		Path:        pointy.GetPointer("/token/delete"),
+		Description: pointy.GetPointer("apiDesc.deleteToken"),
+		ApiGroup:    pointy.GetPointer("token"),
+		Method:      pointy.GetPointer("POST"),
+	})
+
+	if err != nil {
+		return err
+	}
+
+	_, err = l.svcCtx.CoreRpc.CreateApi(l.ctx, &core.ApiInfo{
+		ServiceName: pointy.GetPointer("Wechat"),
+		Path:        pointy.GetPointer("/token/list"),
+		Description: pointy.GetPointer("apiDesc.getTokenList"),
+		ApiGroup:    pointy.GetPointer("token"),
+		Method:      pointy.GetPointer("POST"),
+	})
+
+	if err != nil {
+		return err
+	}
+
+	_, err = l.svcCtx.CoreRpc.CreateApi(l.ctx, &core.ApiInfo{
+		ServiceName: pointy.GetPointer("Wechat"),
+		Path:        pointy.GetPointer("/token"),
+		Description: pointy.GetPointer("apiDesc.getTokenById"),
+		ApiGroup:    pointy.GetPointer("token"),
+		Method:      pointy.GetPointer("POST"),
+	})
+
+	if err != nil {
+		return err
+	}
+
 	// Tutorial
 
 	_, err = l.svcCtx.CoreRpc.CreateApi(l.ctx, &core.ApiInfo{

+ 80 - 0
internal/logic/token/check_token_logic.go

@@ -0,0 +1,80 @@
+package token
+
+import (
+	"context"
+	"crypto/md5"
+	"encoding/hex"
+	"io"
+	"strconv"
+	"time"
+	"wechat-api/ent"
+	"wechat-api/ent/token"
+	"wechat-api/internal/utils/dberrorhandler"
+
+	"wechat-api/internal/svc"
+	"wechat-api/internal/types"
+
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type CheckTokenLogic struct {
+	logx.Logger
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+}
+
+func NewCheckTokenLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CheckTokenLogic {
+	return &CheckTokenLogic{
+		Logger: logx.WithContext(ctx),
+		ctx:    ctx,
+		svcCtx: svcCtx}
+}
+
+func (l *CheckTokenLogic) CheckToken(req *types.CheckTokenReq) (resp *types.CheckTokenResp, err error) {
+	var (
+		valid bool
+		sign  string
+	)
+	timestamp := time.Now().Unix()
+
+	tokenItem, err := l.svcCtx.DB.Token.Query().Where(token.TokenEQ(*req.Token)).Limit(1).Only(l.ctx)
+	if err != nil && !ent.IsNotFound(err) {
+		return nil, dberrorhandler.DefaultEntError(l.Logger, err, req)
+	}
+
+	if tokenItem == nil { // 判断Token是否存在
+		valid = false
+	} else if tokenItem.ExpireAt.Unix() > timestamp { // 判断Token是否过期
+		valid = true
+		if tokenItem.MAC == "" {
+			err = l.svcCtx.DB.Token.UpdateOneID(tokenItem.ID).SetNotNilMAC(req.Mac).Exec(l.ctx)
+			if err != nil {
+				return nil, dberrorhandler.DefaultEntError(l.Logger, err, req)
+			}
+		}
+
+		sign = generateSign(*req.Token, *req.Mac, strconv.Itoa(int(timestamp)))
+	}
+
+	return &types.CheckTokenResp{
+		Valid:     &valid,
+		Sign:      &sign,
+		Timestamp: &timestamp,
+	}, nil
+}
+
+// 内部Md5方法
+func generateSign(token, mac, timestamp string) string {
+	md5Token := easyMd5(token)
+	md5Mac := easyMd5(mac)
+	md5Ts := easyMd5(timestamp)
+
+	return easyMd5(md5Token + md5Mac + md5Ts)
+}
+
+// 简易Md5用法
+func easyMd5(s string) string {
+	hash := md5.New()
+	_, _ = io.WriteString(hash, s)
+	return hex.EncodeToString(hash.Sum(nil))
+}

+ 40 - 0
internal/logic/token/create_token_logic.go

@@ -0,0 +1,40 @@
+package token
+
+import (
+	"context"
+
+	"wechat-api/internal/svc"
+	"wechat-api/internal/types"
+	"wechat-api/internal/utils/dberrorhandler"
+
+	"github.com/suyuan32/simple-admin-common/msg/errormsg"
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type CreateTokenLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewCreateTokenLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateTokenLogic {
+	return &CreateTokenLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+func (l *CreateTokenLogic) CreateToken(req *types.TokenInfo) (*types.BaseMsgResp, error) {
+	_, err := l.svcCtx.DB.Token.Create().
+		//SetNotNilExpireAt(pointy.GetTimeMilliPointer(req.ExpireAt)).
+		SetNotNilToken(req.Token).
+		SetNotNilMAC(req.Mac).
+		Save(l.ctx)
+
+	if err != nil {
+		return nil, dberrorhandler.DefaultEntError(l.Logger, err, req)
+	}
+
+	return &types.BaseMsgResp{Msg: errormsg.CreateSuccess}, nil
+}

+ 37 - 0
internal/logic/token/delete_token_logic.go

@@ -0,0 +1,37 @@
+package token
+
+import (
+	"context"
+
+    "wechat-api/ent/token"
+    "wechat-api/internal/svc"
+    "wechat-api/internal/types"
+    "wechat-api/internal/utils/dberrorhandler"
+
+    "github.com/suyuan32/simple-admin-common/msg/errormsg"
+    "github.com/zeromicro/go-zero/core/logx"
+)
+
+type DeleteTokenLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewDeleteTokenLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteTokenLogic {
+	return &DeleteTokenLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+func (l *DeleteTokenLogic) DeleteToken(req *types.IDsReq) (*types.BaseMsgResp, error) {
+	_, err := l.svcCtx.DB.Token.Delete().Where(token.IDIn(req.Ids...)).Exec(l.ctx)
+
+    if err != nil {
+		return nil, dberrorhandler.DefaultEntError(l.Logger, err, req)
+	}
+
+    return &types.BaseMsgResp{Msg: errormsg.DeleteSuccess}, nil
+}

+ 52 - 0
internal/logic/token/get_token_by_id_logic.go

@@ -0,0 +1,52 @@
+package token
+
+import (
+	"context"
+
+	"wechat-api/internal/svc"
+	"wechat-api/internal/types"
+	"wechat-api/internal/utils/dberrorhandler"
+
+	"github.com/suyuan32/simple-admin-common/msg/errormsg"
+
+	"github.com/suyuan32/simple-admin-common/utils/pointy"
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type GetTokenByIdLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewGetTokenByIdLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetTokenByIdLogic {
+	return &GetTokenByIdLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+func (l *GetTokenByIdLogic) GetTokenById(req *types.IDReq) (*types.TokenInfoResp, error) {
+	data, err := l.svcCtx.DB.Token.Get(l.ctx, req.Id)
+	if err != nil {
+		return nil, dberrorhandler.DefaultEntError(l.Logger, err, req)
+	}
+
+	return &types.TokenInfoResp{
+		BaseDataInfo: types.BaseDataInfo{
+			Code: 0,
+			Msg:  errormsg.Success,
+		},
+		Data: types.TokenInfo{
+			BaseIDInfo: types.BaseIDInfo{
+				Id:        &data.ID,
+				CreatedAt: pointy.GetPointer(data.CreatedAt.UnixMilli()),
+				UpdatedAt: pointy.GetPointer(data.UpdatedAt.UnixMilli()),
+			},
+			ExpireAt: pointy.GetUnixMilliPointer(data.ExpireAt.UnixMilli()),
+			Token:    &data.Token,
+			Mac:      &data.MAC,
+		},
+	}, nil
+}

+ 65 - 0
internal/logic/token/get_token_list_logic.go

@@ -0,0 +1,65 @@
+package token
+
+import (
+	"context"
+
+	"wechat-api/ent/predicate"
+	"wechat-api/ent/token"
+	"wechat-api/internal/svc"
+	"wechat-api/internal/types"
+	"wechat-api/internal/utils/dberrorhandler"
+
+	"github.com/suyuan32/simple-admin-common/msg/errormsg"
+
+	"github.com/suyuan32/simple-admin-common/utils/pointy"
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type GetTokenListLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewGetTokenListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetTokenListLogic {
+	return &GetTokenListLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+func (l *GetTokenListLogic) GetTokenList(req *types.TokenListReq) (*types.TokenListResp, error) {
+	var predicates []predicate.Token
+	if req.Token != nil {
+		predicates = append(predicates, token.TokenContains(*req.Token))
+	}
+	if req.Mac != nil {
+		predicates = append(predicates, token.MACContains(*req.Mac))
+	}
+	data, err := l.svcCtx.DB.Token.Query().Where(predicates...).Page(l.ctx, req.Page, req.PageSize)
+
+	if err != nil {
+		return nil, dberrorhandler.DefaultEntError(l.Logger, err, req)
+	}
+
+	resp := &types.TokenListResp{}
+	resp.Msg = errormsg.Success
+	resp.Data.Total = data.PageDetails.Total
+
+	for _, v := range data.List {
+		resp.Data.Data = append(resp.Data.Data,
+			types.TokenInfo{
+				BaseIDInfo: types.BaseIDInfo{
+					Id:        &v.ID,
+					CreatedAt: pointy.GetPointer(v.CreatedAt.UnixMilli()),
+					UpdatedAt: pointy.GetPointer(v.UpdatedAt.UnixMilli()),
+				},
+				ExpireAt: pointy.GetUnixMilliPointer(v.ExpireAt.UnixMilli()),
+				Token:    &v.Token,
+				Mac:      &v.MAC,
+			})
+	}
+
+	return resp, nil
+}

+ 40 - 0
internal/logic/token/update_token_logic.go

@@ -0,0 +1,40 @@
+package token
+
+import (
+	"context"
+
+	"wechat-api/internal/svc"
+	"wechat-api/internal/types"
+	"wechat-api/internal/utils/dberrorhandler"
+
+	"github.com/suyuan32/simple-admin-common/msg/errormsg"
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type UpdateTokenLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewUpdateTokenLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateTokenLogic {
+	return &UpdateTokenLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+func (l *UpdateTokenLogic) UpdateToken(req *types.TokenInfo) (*types.BaseMsgResp, error) {
+	err := l.svcCtx.DB.Token.UpdateOneID(*req.Id).
+		//SetNotNilExpireAt(pointy.GetTimeMilliPointer(req.ExpireAt)).
+		SetNotNilToken(req.Token).
+		SetNotNilMAC(req.Mac).
+		Exec(l.ctx)
+
+	if err != nil {
+		return nil, dberrorhandler.DefaultEntError(l.Logger, err, req)
+	}
+
+	return &types.BaseMsgResp{Msg: errormsg.UpdateSuccess}, nil
+}

+ 64 - 0
internal/types/types.go

@@ -1450,3 +1450,67 @@ type TutorialInfoResp struct {
 	// Tutorial information | Tutorial数据
 	Data TutorialInfo `json:"data"`
 }
+
+// The data of token information | Token信息
+// swagger:model TokenInfo
+type TokenInfo struct {
+	BaseIDInfo
+	// 过期时间
+	ExpireAt *int64 `json:"expireAt,optional"`
+	// Token
+	Token *string `json:"token,optional"`
+	// Mac地址
+	Mac *string `json:"mac,optional"`
+}
+
+// The response data of token list | Token列表数据
+// swagger:model TokenListResp
+type TokenListResp struct {
+	BaseDataInfo
+	// Token list data | Token列表数据
+	Data TokenListInfo `json:"data"`
+}
+
+// Token list data | Token列表数据
+// swagger:model TokenListInfo
+type TokenListInfo struct {
+	BaseListInfo
+	// The API list data | Token列表数据
+	Data []TokenInfo `json:"data"`
+}
+
+// Get token list request params | Token列表请求参数
+// swagger:model TokenListReq
+type TokenListReq struct {
+	PageInfo
+	// Token
+	Token *string `json:"token,optional"`
+	// Mac地址
+	Mac *string `json:"mac,optional"`
+}
+
+// Token information response | Token信息返回体
+// swagger:model TokenInfoResp
+type TokenInfoResp struct {
+	BaseDataInfo
+	// Token information | Token数据
+	Data TokenInfo `json:"data"`
+}
+
+// swagger:model CheckTokenReq
+type CheckTokenReq struct {
+	// Token
+	Token *string `json:"token"`
+	// Mac地址
+	Mac *string `json:"mac"`
+}
+
+// swagger:model CheckTokenResp
+type CheckTokenResp struct {
+	// 是否合法
+	Valid *bool `json:"valid"`
+	// Sign 签名内容
+	Sign *string `json:"sign"`
+	// Timestamp 时间戳
+	Timestamp *int64 `json:"timestamp"`
+}