Browse Source

Merge branch 'debug' into yhg_200228

jimmyyem 1 month ago
parent
commit
f679ef80a8
86 changed files with 8545 additions and 17 deletions
  1. 3 3
      crontask/send_wx.go
  2. 6 1
      desc/all.api
  3. 81 0
      desc/wechat/api_key.api
  4. 101 0
      desc/wechat/department.api
  5. 24 0
      desc/wechat/fastgpt.api
  6. 26 0
      desc/wp_wecom/send_msg.api
  7. 16 0
      ent/agent.go
  8. 30 0
      ent/agent/agent.go
  9. 22 0
      ent/agent/where.go
  10. 30 0
      ent/agent_create.go
  11. 79 0
      ent/agent_query.go
  12. 151 0
      ent/agent_update.go
  13. 245 0
      ent/apikey.go
  14. 186 0
      ent/apikey/apikey.go
  15. 789 0
      ent/apikey/where.go
  16. 1285 0
      ent/apikey_create.go
  17. 88 0
      ent/apikey_delete.go
  18. 605 0
      ent/apikey_query.go
  19. 773 0
      ent/apikey_update.go
  20. 180 4
      ent/client.go
  21. 2 0
      ent/ent.go
  22. 12 0
      ent/hook/hook.go
  23. 30 0
      ent/intercept/intercept.go
  24. 41 0
      ent/migrate/schema.go
  25. 1188 0
      ent/mutation.go
  26. 82 0
      ent/pagination.go
  27. 3 0
      ent/predicate/predicate.go
  28. 52 0
      ent/runtime/runtime.go
  29. 1 0
      ent/schema/agent.go
  30. 60 0
      ent/schema/api_key.go
  31. 240 0
      ent/set_not_nil.go
  32. 3 0
      ent/tx.go
  33. 8 0
      etc/wechat.yaml
  34. 7 0
      go.mod
  35. 15 0
      go.sum
  36. 1 0
      internal/config/config.go
  37. 44 0
      internal/handler/api_key/create_api_key_handler.go
  38. 44 0
      internal/handler/api_key/delete_api_key_handler.go
  39. 44 0
      internal/handler/api_key/get_api_key_list_handler.go
  40. 44 0
      internal/handler/api_key/update_api_key_handler.go
  41. 44 0
      internal/handler/department/create_department_handler.go
  42. 44 0
      internal/handler/department/delete_department_handler.go
  43. 44 0
      internal/handler/department/get_department_by_id_handler.go
  44. 44 0
      internal/handler/department/get_department_list_handler.go
  45. 44 0
      internal/handler/department/update_department_handler.go
  46. 44 0
      internal/handler/fastgpt/create_fastgpt_handler.go
  47. 32 0
      internal/handler/fastgpt/set_token_handler.go
  48. 86 0
      internal/handler/routes.go
  49. 44 0
      internal/handler/wp_wecom/send_msg_by_chan_handler.go
  50. 44 0
      internal/handler/wp_wecom/send_msg_handler.go
  51. 1 1
      internal/logic/Wxhook/send_text_msg_logic.go
  52. 65 0
      internal/logic/api_key/create_api_key_logic.go
  53. 36 0
      internal/logic/api_key/delete_api_key_logic.go
  54. 89 0
      internal/logic/api_key/get_api_key_list_logic.go
  55. 42 0
      internal/logic/api_key/update_api_key_logic.go
  56. 96 0
      internal/logic/department/create_department_logic.go
  57. 29 0
      internal/logic/department/delete_department_logic.go
  58. 29 0
      internal/logic/department/get_department_by_id_logic.go
  59. 29 0
      internal/logic/department/get_department_list_logic.go
  60. 29 0
      internal/logic/department/update_department_logic.go
  61. 80 0
      internal/logic/fastgpt/create_fastgpt_logic.go
  62. 149 0
      internal/logic/fastgpt/set_token_logic.go
  63. 29 0
      internal/logic/wp_wecom/send_msg_by_chan_logic.go
  64. 44 0
      internal/logic/wp_wecom/send_msg_logic.go
  65. 0 1
      internal/pkg/wechat_ws/wechat_ws_client.go
  66. 12 7
      internal/svc/service_context.go
  67. 6 0
      internal/types/mongo.go
  68. 133 0
      internal/types/types.go
  69. 6 0
      internal/types/ws.go
  70. 21 0
      mongo_model/allmodel.go
  71. 12 0
      mongo_model/team_members/error.go
  72. 25 0
      mongo_model/team_members/teammembersmodel.go
  73. 73 0
      mongo_model/team_members/teammembersmodelgen.go
  74. 19 0
      mongo_model/team_members/teammemberstypes.go
  75. 12 0
      mongo_model/teams/error.go
  76. 25 0
      mongo_model/teams/teamsmodel.go
  77. 73 0
      mongo_model/teams/teamsmodelgen.go
  78. 18 0
      mongo_model/teams/teamstypes.go
  79. 12 0
      mongo_model/users/error.go
  80. 27 0
      mongo_model/users/usersmodel.go
  81. 70 0
      mongo_model/users/usersmodelgen.go
  82. 16 0
      mongo_model/users/userstypes.go
  83. 12 0
      teams/error.go
  84. 25 0
      teams/teamsmodel.go
  85. 76 0
      teams/teamsmodelgen.go
  86. 14 0
      teams/teamstypes.go

+ 3 - 3
crontask/send_wx.go

@@ -75,9 +75,6 @@ func (l *CronTask) sendWx() {
 	getServerInfo := func(wxid string) (string, error) {
 		key := "crontask_wx_server_info"
 		val, _ := l.svcCtx.Rds.HGet(ctx, key, wxid).Result()
-		//if err != nil {
-		//	return "", err
-		//}
 
 		if val == "" {
 			wx, err := l.svcCtx.DB.Wx.Query().Where(wx.WxidEQ(wxid)).First(l.ctx)
@@ -94,6 +91,9 @@ func (l *CronTask) sendWx() {
 				ctype := strconv.Itoa(int(wx.Ctype))
 				val = server.PrivateIP + ":" + server.AdminPort + ":" + wx.Port + ":" + ctype
 				l.svcCtx.Rds.HSet(ctx, key, wxid, val)
+			} else {
+				val = "0:0" + ":" + wx.Port + ":" + strconv.Itoa(int(wx.Ctype))
+				l.svcCtx.Rds.HSet(ctx, key, wxid, val)
 			}
 		}
 

+ 6 - 1
desc/all.api

@@ -43,4 +43,9 @@ import "./wechat/pay_recharge.api"
 import "./wechat/whatsapp.api"
 import "./wechat/whatsapp_channel.api"
 import "./wechat/xunji.api"
-import "./wechat/xunji_service.api"
+import "./wechat/xunji_service.api"
+import "./wp_wecom/send_msg.api"
+import "./wechat/xunji.api"
+import "./wechat/fastgpt.api"
+import "./wechat/department.api"
+import "./wechat/api_key.api"

+ 81 - 0
desc/wechat/api_key.api

@@ -0,0 +1,81 @@
+import "../base.api"
+import "./agent.api"
+
+type (
+    // The data of api_key information | ApiKey信息
+    ApiKeyInfo {
+        BaseIDInfo
+
+        // Title
+        Title  *string `json:"title,optional"`
+
+        // Key
+        Key  *string `json:"key,optional"`
+
+		// 租户ID
+		OrganizationId *uint64 `json:"organization_id,optional"`
+
+		AgentId *uint64 `json:"agent_id,optional"`
+
+		AgentInfo *AgentInfo `json:"agent_info,optional"`
+
+        CustomAgentBase *string `json:"custom_agent_base,optional"`
+
+        CustomAgentKey *string `json:"custom_agent_key,optional"`
+
+        OpenaiBase *string `json:"openai_base,optional"`
+
+        OpenaiKey *string `json:"openai_key,optional"`
+    }
+
+    // The response data of api_key list | ApiKey列表数据
+    ApiKeyListResp {
+        BaseDataInfo
+
+        // ApiKey list data | ApiKey列表数据
+        Data ApiKeyListInfo `json:"data"`
+    }
+
+    // ApiKey list data | ApiKey列表数据
+    ApiKeyListInfo {
+        BaseListInfo
+
+        // The API list data | ApiKey列表数据
+        Data  []ApiKeyInfo  `json:"data"`
+    }
+
+    // Get ApiKey list request params | ApiKey列表请求参数
+    ApiKeyListReq {
+        PageInfo
+
+        // Key
+        Key  *string `json:"key,optional"`
+
+        OrganizationId *uint64 `json:"organization_id,optional"`
+    }
+)
+
+@server(
+	group: api_key
+	jwt: Auth
+	middleware: Authority
+)
+
+service Wechat {
+	// Create api_key information | 创建ApiKey
+	@handler createApiKey
+	post /api_key/create (ApiKeyInfo) returns (BaseMsgResp)
+
+	// Update api_key information | 更新ApiKey
+	@handler updateApiKey
+	post /api_key/update (ApiKeyInfo) returns (BaseMsgResp)
+
+	// Delete api_key information | 删除ApiKey信息
+	@handler deleteApiKey
+	post /api_key/delete (IDsReq) returns (BaseMsgResp)
+
+	// Get api_key list | 获取ApiKey列表
+	@handler getApiKeyList
+	post /api_key/list (ApiKeyListReq) returns (ApiKeyListResp)
+
+}

+ 101 - 0
desc/wechat/department.api

@@ -0,0 +1,101 @@
+import "../base.api"
+
+type (
+    // The response data of department information | 部门信息
+    DepartmentInfo {
+        BaseIDInfo
+
+        // Translated Name | 展示名称
+        Trans string `json:"trans,optional"`
+
+        // Status | 状态
+        Status *uint32 `json:"status,optional" validate:"omitempty,lt=20"`
+
+        // Sort | 排序
+        Sort *uint32 `json:"sort,optional" validate:"omitempty,lt=10000"`
+
+        // Name | 部门名称
+        Name *string `json:"name,optional" validate:"omitempty,min=1,max=50"`
+
+        // Ancestors | 父级部门列表
+        Ancestors *string `json:"ancestors,optional" validate:"omitempty,max=200"`
+
+        // Leader | 部门负责人
+        Leader *string `json:"leader,optional" validate:"omitempty,max=20"`
+
+        // Phone | 电话号码
+        Phone *string `json:"phone,optional" validate:"omitempty,max=18"`
+
+        // Email | 邮箱
+        Email *string `json:"email,optional" validate:"omitempty,max=70"`
+
+        // Remark | 备注
+        Remark *string `json:"remark,optional" validate:"omitempty,max=200"`
+
+        // ParentId | 父级 ID
+        ParentId *uint64 `json:"parentId,optional"`
+    }
+
+    // The response data of department list | 部门列表数据
+    DepartmentListResp {
+        BaseDataInfo
+
+        // Department list data | 部门列表数据
+        Data DepartmentListInfo `json:"data"`
+    }
+
+    // Department list data | 部门列表数据
+    DepartmentListInfo {
+        BaseListInfo
+
+        // The API list data | 部门列表数据
+        Data  []DepartmentInfo  `json:"data"`
+    }
+
+    // Get department list request params | 部门列表请求参数
+    DepartmentListReq {
+        PageInfo
+
+        // Name | 部门名称
+        Name *string `json:"name,optional" validate:"omitempty,max=50"`
+
+        // Leader | 部门负责人
+        Leader *string `json:"leader,optional" validate:"omitempty,max=20"`
+    }
+
+    // Department information response | 部门信息返回体
+    DepartmentInfoResp {
+        BaseDataInfo
+
+        // Department information | 部门数据
+        Data DepartmentInfo `json:"data"`
+    }
+)
+
+@server(
+    jwt: Auth
+    group: department
+    middleware: Authority
+)
+
+service Wechat {
+    // Create department information | 创建部门
+    @handler createDepartment
+    post /department/create (DepartmentInfo) returns (BaseMsgResp)
+
+    // Update department information | 更新部门
+    @handler updateDepartment
+    post /department/update (DepartmentInfo) returns (BaseMsgResp)
+
+    // Delete department information | 删除部门信息
+    @handler deleteDepartment
+    post /department/delete (IDsReq) returns (BaseMsgResp)
+
+    // Get department list | 获取部门列表
+    @handler getDepartmentList
+    post /department/list (DepartmentListReq) returns (DepartmentListResp)
+
+    // Get Department by ID | 通过ID获取部门
+    @handler getDepartmentById
+    post /department (IDReq) returns (DepartmentInfoResp)
+}

+ 24 - 0
desc/wechat/fastgpt.api

@@ -0,0 +1,24 @@
+import "../base.api"
+
+type (
+    CreateInfo {
+        // Translated Name | 展示名称
+        UserName string `json:"username"`
+
+        // Name | 部门名称
+        Title *string `json:"title"`
+    }
+)
+
+@server(
+    group: fastgpt
+)
+
+service Wechat {
+    @handler setToken
+    get /api/fastgpt/set_token () returns (BaseMsgResp)
+
+    // 创建 fastgpt 租户
+    @handler CreateFastgpt
+    post /api/fastgpt/create (CreateInfo) returns (BaseMsgResp)
+}

+ 26 - 0
desc/wp_wecom/send_msg.api

@@ -0,0 +1,26 @@
+import "../base.api"
+
+type (
+    WpWecomMsgReq {
+        // 机器人微信id
+        WxId *string `json:"wx_id"`
+        // 接收方微信 id
+        ConvId *string `json:"conv_id"`
+        // 消息类型
+        ContentType *string `json:"content_type"`
+        // 消息内容
+        Content *string `json:"content"`
+    }
+)
+
+@server(
+	group: wp_wecom
+)
+
+service Wechat {
+    @handler sendMsg
+    post /wp/wecom/send_msg (WpWecomMsgReq) returns (BaseMsgResp)
+
+    @handler sendMsgByChan
+    post /wp/wecom/send_msg_by_chan (WpWecomMsgReq) returns (BaseMsgResp)
+}

+ 16 - 0
ent/agent.go

@@ -55,6 +55,8 @@ type AgentEdges struct {
 	WaAgent []*Whatsapp `json:"wa_agent,omitempty"`
 	// XjsAgent holds the value of the xjs_agent edge.
 	XjsAgent []*XunjiService `json:"xjs_agent,omitempty"`
+	// KeyAgent holds the value of the key_agent edge.
+	KeyAgent []*ApiKey `json:"key_agent,omitempty"`
 	// loadedTypes holds the information for reporting if a
 	// type was loaded (or requested) in eager-loading or not.
 	loadedTypes [4]bool
@@ -87,6 +89,7 @@ func (e AgentEdges) WaAgentOrErr() ([]*Whatsapp, error) {
 	return nil, &NotLoadedError{edge: "wa_agent"}
 }
 
+
 // XjsAgentOrErr returns the XjsAgent value or an error if the edge
 // was not loaded in eager-loading.
 func (e AgentEdges) XjsAgentOrErr() ([]*XunjiService, error) {
@@ -95,6 +98,14 @@ func (e AgentEdges) XjsAgentOrErr() ([]*XunjiService, error) {
 	}
 	return nil, &NotLoadedError{edge: "xjs_agent"}
 }
+// KeyAgentOrErr returns the KeyAgent value or an error if the edge
+// was not loaded in eager-loading.
+func (e AgentEdges) KeyAgentOrErr() ([]*ApiKey, error) {
+	if e.loadedTypes[3] {
+		return e.KeyAgent, nil
+	}
+	return nil, &NotLoadedError{edge: "key_agent"}
+}
 
 // scanValues returns the types for scanning values from sql.Rows.
 func (*Agent) scanValues(columns []string) ([]any, error) {
@@ -222,10 +233,15 @@ func (a *Agent) QueryWaAgent() *WhatsappQuery {
 	return NewAgentClient(a.config).QueryWaAgent(a)
 }
 
+
 // QueryXjsAgent queries the "xjs_agent" edge of the Agent entity.
 func (a *Agent) QueryXjsAgent() *XunjiServiceQuery {
 	return NewAgentClient(a.config).QueryXjsAgent(a)
 }
+// QueryKeyAgent queries the "key_agent" edge of the Agent entity.
+func (a *Agent) QueryKeyAgent() *ApiKeyQuery {
+	return NewAgentClient(a.config).QueryKeyAgent(a)
+}
 
 // Update returns a builder for updating this Agent.
 // Note that you need to call Agent.Unwrap() before calling this method if this Agent

+ 30 - 0
ent/agent/agent.go

@@ -45,6 +45,8 @@ const (
 	EdgeWaAgent = "wa_agent"
 	// EdgeXjsAgent holds the string denoting the xjs_agent edge name in mutations.
 	EdgeXjsAgent = "xjs_agent"
+	// EdgeKeyAgent holds the string denoting the key_agent edge name in mutations.
+	EdgeKeyAgent = "key_agent"
 	// Table holds the table name of the agent in the database.
 	Table = "agent"
 	// WxAgentTable is the table that holds the wx_agent relation/edge.
@@ -75,6 +77,13 @@ const (
 	XjsAgentInverseTable = "xunji_service"
 	// XjsAgentColumn is the table column denoting the xjs_agent relation/edge.
 	XjsAgentColumn = "agent_id"
+	// KeyAgentTable is the table that holds the key_agent relation/edge.
+	KeyAgentTable = "api_key"
+	// KeyAgentInverseTable is the table name for the ApiKey entity.
+	// It exists in this package in order to avoid circular dependency with the "apikey" package.
+	KeyAgentInverseTable = "api_key"
+	// KeyAgentColumn is the table column denoting the key_agent relation/edge.
+	KeyAgentColumn = "agent_id"
 )
 
 // Columns holds all SQL columns for agent fields.
@@ -244,6 +253,7 @@ func ByWaAgent(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption {
 	}
 }
 
+
 // ByXjsAgentCount orders the results by xjs_agent count.
 func ByXjsAgentCount(opts ...sql.OrderTermOption) OrderOption {
 	return func(s *sql.Selector) {
@@ -257,6 +267,19 @@ func ByXjsAgent(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption {
 		sqlgraph.OrderByNeighborTerms(s, newXjsAgentStep(), append([]sql.OrderTerm{term}, terms...)...)
 	}
 }
+// ByKeyAgentCount orders the results by key_agent count.
+func ByKeyAgentCount(opts ...sql.OrderTermOption) OrderOption {
+	return func(s *sql.Selector) {
+		sqlgraph.OrderByNeighborsCount(s, newKeyAgentStep(), opts...)
+	}
+}
+
+// ByKeyAgent orders the results by key_agent terms.
+func ByKeyAgent(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption {
+	return func(s *sql.Selector) {
+		sqlgraph.OrderByNeighborTerms(s, newKeyAgentStep(), append([]sql.OrderTerm{term}, terms...)...)
+	}
+}
 func newWxAgentStep() *sqlgraph.Step {
 	return sqlgraph.NewStep(
 		sqlgraph.From(Table, FieldID),
@@ -285,3 +308,10 @@ func newXjsAgentStep() *sqlgraph.Step {
 		sqlgraph.Edge(sqlgraph.O2M, false, XjsAgentTable, XjsAgentColumn),
 	)
 }
+func newKeyAgentStep() *sqlgraph.Step {
+	return sqlgraph.NewStep(
+		sqlgraph.From(Table, FieldID),
+		sqlgraph.To(KeyAgentInverseTable, FieldID),
+		sqlgraph.Edge(sqlgraph.O2M, false, KeyAgentTable, KeyAgentColumn),
+	)
+}

+ 22 - 0
ent/agent/where.go

@@ -819,6 +819,17 @@ func HasXjsAgent() predicate.Agent {
 		sqlgraph.HasNeighbors(s, step)
 	})
 }
+// HasKeyAgent applies the HasEdge predicate on the "key_agent" edge.
+func HasKeyAgent() predicate.Agent {
+	return predicate.Agent(func(s *sql.Selector) {
+		step := sqlgraph.NewStep(
+			sqlgraph.From(Table, FieldID),
+			sqlgraph.Edge(sqlgraph.O2M, false, KeyAgentTable, KeyAgentColumn),
+		)
+		sqlgraph.HasNeighbors(s, step)
+	})
+}
+
 
 // HasXjsAgentWith applies the HasEdge predicate on the "xjs_agent" edge with a given conditions (other predicates).
 func HasXjsAgentWith(preds ...predicate.XunjiService) predicate.Agent {
@@ -831,6 +842,17 @@ func HasXjsAgentWith(preds ...predicate.XunjiService) predicate.Agent {
 		})
 	})
 }
+// HasKeyAgentWith applies the HasEdge predicate on the "key_agent" edge with a given conditions (other predicates).
+func HasKeyAgentWith(preds ...predicate.ApiKey) predicate.Agent {
+	return predicate.Agent(func(s *sql.Selector) {
+		step := newKeyAgentStep()
+		sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
+			for _, p := range preds {
+				p(s)
+			}
+		})
+	})
+}
 
 // And groups predicates with the AND operator between them.
 func And(predicates ...predicate.Agent) predicate.Agent {

+ 30 - 0
ent/agent_create.go

@@ -8,6 +8,7 @@ import (
 	"fmt"
 	"time"
 	"wechat-api/ent/agent"
+	"wechat-api/ent/apikey"
 	"wechat-api/ent/token"
 	"wechat-api/ent/whatsapp"
 	"wechat-api/ent/wx"
@@ -207,6 +208,7 @@ func (ac *AgentCreate) AddWaAgent(w ...*Whatsapp) *AgentCreate {
 	return ac.AddWaAgentIDs(ids...)
 }
 
+
 // AddXjsAgentIDs adds the "xjs_agent" edge to the XunjiService entity by IDs.
 func (ac *AgentCreate) AddXjsAgentIDs(ids ...uint64) *AgentCreate {
 	ac.mutation.AddXjsAgentIDs(ids...)
@@ -221,6 +223,20 @@ func (ac *AgentCreate) AddXjsAgent(x ...*XunjiService) *AgentCreate {
 	}
 	return ac.AddXjsAgentIDs(ids...)
 }
+// AddKeyAgentIDs adds the "key_agent" edge to the ApiKey entity by IDs.
+func (ac *AgentCreate) AddKeyAgentIDs(ids ...uint64) *AgentCreate {
+	ac.mutation.AddKeyAgentIDs(ids...)
+	return ac
+}
+
+// AddKeyAgent adds the "key_agent" edges to the ApiKey entity.
+func (ac *AgentCreate) AddKeyAgent(a ...*ApiKey) *AgentCreate {
+	ids := make([]uint64, len(a))
+	for i := range a {
+		ids[i] = a[i].ID
+	}
+	return ac.AddKeyAgentIDs(ids...)
+}
 
 // Mutation returns the AgentMutation object of the builder.
 func (ac *AgentCreate) Mutation() *AgentMutation {
@@ -469,6 +485,7 @@ func (ac *AgentCreate) createSpec() (*Agent, *sqlgraph.CreateSpec) {
 		}
 		_spec.Edges = append(_spec.Edges, edge)
 	}
+
 	if nodes := ac.mutation.XjsAgentIDs(); len(nodes) > 0 {
 		edge := &sqlgraph.EdgeSpec{
 			Rel:     sqlgraph.O2M,
@@ -479,6 +496,19 @@ func (ac *AgentCreate) createSpec() (*Agent, *sqlgraph.CreateSpec) {
 			Target: &sqlgraph.EdgeTarget{
 				IDSpec: sqlgraph.NewFieldSpec(xunjiservice.FieldID, field.TypeUint64),
 			},
+		},
+	}
+
+	if nodes := ac.mutation.KeyAgentIDs(); len(nodes) > 0 {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.O2M,
+			Inverse: false,
+			Table:   agent.KeyAgentTable,
+			Columns: []string{agent.KeyAgentColumn},
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: sqlgraph.NewFieldSpec(apikey.FieldID, field.TypeUint64),
+			},
 		}
 		for _, k := range nodes {
 			edge.Target.Nodes = append(edge.Target.Nodes, k)

+ 79 - 0
ent/agent_query.go

@@ -8,6 +8,7 @@ import (
 	"fmt"
 	"math"
 	"wechat-api/ent/agent"
+	"wechat-api/ent/apikey"
 	"wechat-api/ent/predicate"
 	"wechat-api/ent/token"
 	"wechat-api/ent/whatsapp"
@@ -30,6 +31,7 @@ type AgentQuery struct {
 	withTokenAgent *TokenQuery
 	withWaAgent    *WhatsappQuery
 	withXjsAgent   *XunjiServiceQuery
+	withKeyAgent   *ApiKeyQuery
 	// intermediate query (i.e. traversal path).
 	sql  *sql.Selector
 	path func(context.Context) (*sql.Selector, error)
@@ -153,6 +155,28 @@ func (aq *AgentQuery) QueryXjsAgent() *XunjiServiceQuery {
 	}
 	return query
 }
+// QueryKeyAgent chains the current query on the "key_agent" edge.
+func (aq *AgentQuery) QueryKeyAgent() *ApiKeyQuery {
+	query := (&ApiKeyClient{config: aq.config}).Query()
+
+	query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
+		if err := aq.prepareQuery(ctx); err != nil {
+			return nil, err
+		}
+		selector := aq.sqlQuery(ctx)
+		if err := selector.Err(); err != nil {
+			return nil, err
+		}
+		step := sqlgraph.NewStep(
+			sqlgraph.From(agent.Table, agent.FieldID, selector),
+			sqlgraph.To(apikey.Table, apikey.FieldID),
+			sqlgraph.Edge(sqlgraph.O2M, false, agent.KeyAgentTable, agent.KeyAgentColumn),
+		)
+		fromU = sqlgraph.SetNeighbors(aq.driver.Dialect(), step)
+		return fromU, nil
+	}
+	return query
+}
 
 // First returns the first Agent entity from the query.
 // Returns a *NotFoundError when no Agent was found.
@@ -350,6 +374,7 @@ func (aq *AgentQuery) Clone() *AgentQuery {
 		withTokenAgent: aq.withTokenAgent.Clone(),
 		withWaAgent:    aq.withWaAgent.Clone(),
 		withXjsAgent:   aq.withXjsAgent.Clone(),
+		withKeyAgent:   aq.withKeyAgent.Clone(),
 		// clone intermediate query.
 		sql:  aq.sql.Clone(),
 		path: aq.path,
@@ -389,6 +414,7 @@ func (aq *AgentQuery) WithWaAgent(opts ...func(*WhatsappQuery)) *AgentQuery {
 	return aq
 }
 
+
 // WithXjsAgent tells the query-builder to eager-load the nodes that are connected to
 // the "xjs_agent" edge. The optional arguments are used to configure the query builder of the edge.
 func (aq *AgentQuery) WithXjsAgent(opts ...func(*XunjiServiceQuery)) *AgentQuery {
@@ -400,6 +426,17 @@ func (aq *AgentQuery) WithXjsAgent(opts ...func(*XunjiServiceQuery)) *AgentQuery
 	return aq
 }
 
+// WithKeyAgent tells the query-builder to eager-load the nodes that are connected to
+// the "key_agent" edge. The optional arguments are used to configure the query builder of the edge.
+func (aq *AgentQuery) WithKeyAgent(opts ...func(*ApiKeyQuery)) *AgentQuery {
+	query := (&ApiKeyClient{config: aq.config}).Query()
+	for _, opt := range opts {
+		opt(query)
+	}
+	aq.withKeyAgent = query
+	return aq
+}
+
 // 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.
 //
@@ -483,6 +520,7 @@ func (aq *AgentQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Agent,
 			aq.withTokenAgent != nil,
 			aq.withWaAgent != nil,
 			aq.withXjsAgent != nil,
+			aq.withKeyAgent != nil,
 		}
 	)
 	_spec.ScanValues = func(columns []string) ([]any, error) {
@@ -531,6 +569,14 @@ func (aq *AgentQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Agent,
 			return nil, err
 		}
 	}
+
+	if query := aq.withKeyAgent; query != nil {
+		if err := aq.loadKeyAgent(ctx, query, nodes,
+			func(n *Agent) { n.Edges.KeyAgent = []*ApiKey{} },
+			func(n *Agent, e *ApiKey) { n.Edges.KeyAgent = append(n.Edges.KeyAgent, e) }); err != nil {
+			return nil, err
+		}
+	}
 	return nodes, nil
 }
 
@@ -625,6 +671,7 @@ func (aq *AgentQuery) loadWaAgent(ctx context.Context, query *WhatsappQuery, nod
 	}
 	return nil
 }
+
 func (aq *AgentQuery) loadXjsAgent(ctx context.Context, query *XunjiServiceQuery, nodes []*Agent, init func(*Agent), assign func(*Agent, *XunjiService)) error {
 	fks := make([]driver.Value, 0, len(nodes))
 	nodeids := make(map[uint64]*Agent)
@@ -640,6 +687,38 @@ func (aq *AgentQuery) loadXjsAgent(ctx context.Context, query *XunjiServiceQuery
 	}
 	query.Where(predicate.XunjiService(func(s *sql.Selector) {
 		s.Where(sql.InValues(s.C(agent.XjsAgentColumn), fks...))
+		query.ctx.AppendFieldOnce(apikey.FieldAgentID)
+	}
+	neighbors, err := query.All(ctx)
+	if err != nil {
+		return err
+	}
+	for _, n := range neighbors {
+		fk := n.AgentID
+		node, ok := nodeids[fk]
+		if !ok {
+			return fmt.Errorf(`unexpected referenced foreign-key "agent_id" returned %v for node %v`, fk, n.ID)
+		}
+		assign(node, n)
+	}
+	return nil
+}
+
+func (aq *AgentQuery) loadKeyAgent(ctx context.Context, query *ApiKeyQuery, nodes []*Agent, init func(*Agent), assign func(*Agent, *ApiKey)) error {
+	fks := make([]driver.Value, 0, len(nodes))
+	nodeids := make(map[uint64]*Agent)
+	for i := range nodes {
+		fks = append(fks, nodes[i].ID)
+		nodeids[nodes[i].ID] = nodes[i]
+		if init != nil {
+			init(nodes[i])
+		}
+	}
+	if len(query.ctx.Fields) > 0 {
+		query.ctx.AppendFieldOnce(apikey.FieldAgentID)
+	}
+	query.Where(predicate.ApiKey(func(s *sql.Selector) {
+		s.Where(sql.InValues(s.C(agent.KeyAgentColumn), fks...))
 	}))
 	neighbors, err := query.All(ctx)
 	if err != nil {

+ 151 - 0
ent/agent_update.go

@@ -8,6 +8,7 @@ import (
 	"fmt"
 	"time"
 	"wechat-api/ent/agent"
+	"wechat-api/ent/apikey"
 	"wechat-api/ent/predicate"
 	"wechat-api/ent/token"
 	"wechat-api/ent/whatsapp"
@@ -247,6 +248,7 @@ func (au *AgentUpdate) AddWaAgent(w ...*Whatsapp) *AgentUpdate {
 	return au.AddWaAgentIDs(ids...)
 }
 
+
 // AddXjsAgentIDs adds the "xjs_agent" edge to the XunjiService entity by IDs.
 func (au *AgentUpdate) AddXjsAgentIDs(ids ...uint64) *AgentUpdate {
 	au.mutation.AddXjsAgentIDs(ids...)
@@ -261,6 +263,20 @@ func (au *AgentUpdate) AddXjsAgent(x ...*XunjiService) *AgentUpdate {
 	}
 	return au.AddXjsAgentIDs(ids...)
 }
+// AddKeyAgentIDs adds the "key_agent" edge to the ApiKey entity by IDs.
+func (au *AgentUpdate) AddKeyAgentIDs(ids ...uint64) *AgentUpdate {
+	au.mutation.AddKeyAgentIDs(ids...)
+	return au
+}
+
+// AddKeyAgent adds the "key_agent" edges to the ApiKey entity.
+func (au *AgentUpdate) AddKeyAgent(a ...*ApiKey) *AgentUpdate {
+	ids := make([]uint64, len(a))
+	for i := range a {
+		ids[i] = a[i].ID
+	}
+	return au.AddKeyAgentIDs(ids...)
+}
 
 // Mutation returns the AgentMutation object of the builder.
 func (au *AgentUpdate) Mutation() *AgentMutation {
@@ -330,6 +346,7 @@ func (au *AgentUpdate) RemoveWaAgent(w ...*Whatsapp) *AgentUpdate {
 	return au.RemoveWaAgentIDs(ids...)
 }
 
+
 // ClearXjsAgent clears all "xjs_agent" edges to the XunjiService entity.
 func (au *AgentUpdate) ClearXjsAgent() *AgentUpdate {
 	au.mutation.ClearXjsAgent()
@@ -351,6 +368,27 @@ func (au *AgentUpdate) RemoveXjsAgent(x ...*XunjiService) *AgentUpdate {
 	return au.RemoveXjsAgentIDs(ids...)
 }
 
+// ClearKeyAgent clears all "key_agent" edges to the ApiKey entity.
+func (au *AgentUpdate) ClearKeyAgent() *AgentUpdate {
+	au.mutation.ClearKeyAgent()
+	return au
+}
+
+// RemoveKeyAgentIDs removes the "key_agent" edge to ApiKey entities by IDs.
+func (au *AgentUpdate) RemoveKeyAgentIDs(ids ...uint64) *AgentUpdate {
+	au.mutation.RemoveKeyAgentIDs(ids...)
+	return au
+}
+
+// RemoveKeyAgent removes "key_agent" edges to ApiKey entities.
+func (au *AgentUpdate) RemoveKeyAgent(a ...*ApiKey) *AgentUpdate {
+	ids := make([]uint64, len(a))
+	for i := range a {
+		ids[i] = a[i].ID
+	}
+	return au.RemoveKeyAgentIDs(ids...)
+}
+
 // Save executes the query and returns the number of nodes affected by the update operation.
 func (au *AgentUpdate) Save(ctx context.Context) (int, error) {
 	if err := au.defaults(); err != nil {
@@ -618,6 +656,7 @@ func (au *AgentUpdate) sqlSave(ctx context.Context) (n int, err error) {
 		}
 		_spec.Edges.Add = append(_spec.Edges.Add, edge)
 	}
+
 	if au.mutation.XjsAgentCleared() {
 		edge := &sqlgraph.EdgeSpec{
 			Rel:     sqlgraph.O2M,
@@ -628,9 +667,22 @@ func (au *AgentUpdate) sqlSave(ctx context.Context) (n int, err error) {
 			Target: &sqlgraph.EdgeTarget{
 				IDSpec: sqlgraph.NewFieldSpec(xunjiservice.FieldID, field.TypeUint64),
 			},
+		},
+	}
+	if au.mutation.KeyAgentCleared() {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.O2M,
+			Inverse: false,
+			Table:   agent.KeyAgentTable,
+			Columns: []string{agent.KeyAgentColumn},
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: sqlgraph.NewFieldSpec(apikey.FieldID, field.TypeUint64),
+			},
 		}
 		_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
 	}
+
 	if nodes := au.mutation.RemovedXjsAgentIDs(); len(nodes) > 0 && !au.mutation.XjsAgentCleared() {
 		edge := &sqlgraph.EdgeSpec{
 			Rel:     sqlgraph.O2M,
@@ -642,11 +694,24 @@ func (au *AgentUpdate) sqlSave(ctx context.Context) (n int, err error) {
 				IDSpec: sqlgraph.NewFieldSpec(xunjiservice.FieldID, field.TypeUint64),
 			},
 		}
+	}
+	if nodes := au.mutation.RemovedKeyAgentIDs(); len(nodes) > 0 && !au.mutation.KeyAgentCleared() {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.O2M,
+			Inverse: false,
+			Table:   agent.KeyAgentTable,
+			Columns: []string{agent.KeyAgentColumn},
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: sqlgraph.NewFieldSpec(apikey.FieldID, field.TypeUint64),
+			},
+		}
 		for _, k := range nodes {
 			edge.Target.Nodes = append(edge.Target.Nodes, k)
 		}
 		_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
 	}
+
 	if nodes := au.mutation.XjsAgentIDs(); len(nodes) > 0 {
 		edge := &sqlgraph.EdgeSpec{
 			Rel:     sqlgraph.O2M,
@@ -658,6 +723,18 @@ func (au *AgentUpdate) sqlSave(ctx context.Context) (n int, err error) {
 				IDSpec: sqlgraph.NewFieldSpec(xunjiservice.FieldID, field.TypeUint64),
 			},
 		}
+	}
+	if nodes := au.mutation.KeyAgentIDs(); len(nodes) > 0 {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.O2M,
+			Inverse: false,
+			Table:   agent.KeyAgentTable,
+			Columns: []string{agent.KeyAgentColumn},
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: sqlgraph.NewFieldSpec(apikey.FieldID, field.TypeUint64),
+			},
+		}
 		for _, k := range nodes {
 			edge.Target.Nodes = append(edge.Target.Nodes, k)
 		}
@@ -912,6 +989,20 @@ func (auo *AgentUpdateOne) AddXjsAgent(x ...*XunjiService) *AgentUpdateOne {
 	}
 	return auo.AddXjsAgentIDs(ids...)
 }
+// AddKeyAgentIDs adds the "key_agent" edge to the ApiKey entity by IDs.
+func (auo *AgentUpdateOne) AddKeyAgentIDs(ids ...uint64) *AgentUpdateOne {
+	auo.mutation.AddKeyAgentIDs(ids...)
+	return auo
+}
+
+// AddKeyAgent adds the "key_agent" edges to the ApiKey entity.
+func (auo *AgentUpdateOne) AddKeyAgent(a ...*ApiKey) *AgentUpdateOne {
+	ids := make([]uint64, len(a))
+	for i := range a {
+		ids[i] = a[i].ID
+	}
+	return auo.AddKeyAgentIDs(ids...)
+}
 
 // Mutation returns the AgentMutation object of the builder.
 func (auo *AgentUpdateOne) Mutation() *AgentMutation {
@@ -981,6 +1072,7 @@ func (auo *AgentUpdateOne) RemoveWaAgent(w ...*Whatsapp) *AgentUpdateOne {
 	return auo.RemoveWaAgentIDs(ids...)
 }
 
+
 // ClearXjsAgent clears all "xjs_agent" edges to the XunjiService entity.
 func (auo *AgentUpdateOne) ClearXjsAgent() *AgentUpdateOne {
 	auo.mutation.ClearXjsAgent()
@@ -1001,6 +1093,26 @@ func (auo *AgentUpdateOne) RemoveXjsAgent(x ...*XunjiService) *AgentUpdateOne {
 	}
 	return auo.RemoveXjsAgentIDs(ids...)
 }
+// ClearKeyAgent clears all "key_agent" edges to the ApiKey entity.
+func (auo *AgentUpdateOne) ClearKeyAgent() *AgentUpdateOne {
+	auo.mutation.ClearKeyAgent()
+	return auo
+}
+
+// RemoveKeyAgentIDs removes the "key_agent" edge to ApiKey entities by IDs.
+func (auo *AgentUpdateOne) RemoveKeyAgentIDs(ids ...uint64) *AgentUpdateOne {
+	auo.mutation.RemoveKeyAgentIDs(ids...)
+	return auo
+}
+
+// RemoveKeyAgent removes "key_agent" edges to ApiKey entities.
+func (auo *AgentUpdateOne) RemoveKeyAgent(a ...*ApiKey) *AgentUpdateOne {
+	ids := make([]uint64, len(a))
+	for i := range a {
+		ids[i] = a[i].ID
+	}
+	return auo.RemoveKeyAgentIDs(ids...)
+}
 
 // Where appends a list predicates to the AgentUpdate builder.
 func (auo *AgentUpdateOne) Where(ps ...predicate.Agent) *AgentUpdateOne {
@@ -1299,6 +1411,7 @@ func (auo *AgentUpdateOne) sqlSave(ctx context.Context) (_node *Agent, err error
 		}
 		_spec.Edges.Add = append(_spec.Edges.Add, edge)
 	}
+
 	if auo.mutation.XjsAgentCleared() {
 		edge := &sqlgraph.EdgeSpec{
 			Rel:     sqlgraph.O2M,
@@ -1310,8 +1423,21 @@ func (auo *AgentUpdateOne) sqlSave(ctx context.Context) (_node *Agent, err error
 				IDSpec: sqlgraph.NewFieldSpec(xunjiservice.FieldID, field.TypeUint64),
 			},
 		}
+	}
+	if auo.mutation.KeyAgentCleared() {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.O2M,
+			Inverse: false,
+			Table:   agent.KeyAgentTable,
+			Columns: []string{agent.KeyAgentColumn},
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: sqlgraph.NewFieldSpec(apikey.FieldID, field.TypeUint64),
+			},
+		}
 		_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
 	}
+
 	if nodes := auo.mutation.RemovedXjsAgentIDs(); len(nodes) > 0 && !auo.mutation.XjsAgentCleared() {
 		edge := &sqlgraph.EdgeSpec{
 			Rel:     sqlgraph.O2M,
@@ -1323,11 +1449,24 @@ func (auo *AgentUpdateOne) sqlSave(ctx context.Context) (_node *Agent, err error
 				IDSpec: sqlgraph.NewFieldSpec(xunjiservice.FieldID, field.TypeUint64),
 			},
 		}
+	}
+	if nodes := auo.mutation.RemovedKeyAgentIDs(); len(nodes) > 0 && !auo.mutation.KeyAgentCleared() {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.O2M,
+			Inverse: false,
+			Table:   agent.KeyAgentTable,
+			Columns: []string{agent.KeyAgentColumn},
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: sqlgraph.NewFieldSpec(apikey.FieldID, field.TypeUint64),
+			},
+		}
 		for _, k := range nodes {
 			edge.Target.Nodes = append(edge.Target.Nodes, k)
 		}
 		_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
 	}
+
 	if nodes := auo.mutation.XjsAgentIDs(); len(nodes) > 0 {
 		edge := &sqlgraph.EdgeSpec{
 			Rel:     sqlgraph.O2M,
@@ -1339,6 +1478,18 @@ func (auo *AgentUpdateOne) sqlSave(ctx context.Context) (_node *Agent, err error
 				IDSpec: sqlgraph.NewFieldSpec(xunjiservice.FieldID, field.TypeUint64),
 			},
 		}
+	}
+	if nodes := auo.mutation.KeyAgentIDs(); len(nodes) > 0 {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.O2M,
+			Inverse: false,
+			Table:   agent.KeyAgentTable,
+			Columns: []string{agent.KeyAgentColumn},
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: sqlgraph.NewFieldSpec(apikey.FieldID, field.TypeUint64),
+			},
+		}
 		for _, k := range nodes {
 			edge.Target.Nodes = append(edge.Target.Nodes, k)
 		}

+ 245 - 0
ent/apikey.go

@@ -0,0 +1,245 @@
+// Code generated by ent, DO NOT EDIT.
+
+package ent
+
+import (
+	"fmt"
+	"strings"
+	"time"
+	"wechat-api/ent/agent"
+	"wechat-api/ent/apikey"
+
+	"entgo.io/ent"
+	"entgo.io/ent/dialect/sql"
+)
+
+// ApiKey is the model entity for the ApiKey schema.
+type ApiKey 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"`
+	// Title
+	Title string `json:"title,omitempty"`
+	// Key
+	Key string `json:"key,omitempty"`
+	// 租户ID
+	OrganizationID uint64 `json:"organization_id,omitempty"`
+	// 智能体ID
+	AgentID uint64 `json:"agent_id,omitempty"`
+	// 定制agent服务地址
+	CustomAgentBase string `json:"custom_agent_base,omitempty"`
+	// 定制agent服务密钥
+	CustomAgentKey string `json:"custom_agent_key,omitempty"`
+	// 大模型服务地址
+	OpenaiBase string `json:"openai_base,omitempty"`
+	// 大模型服务密钥
+	OpenaiKey string `json:"openai_key,omitempty"`
+	// Edges holds the relations/edges for other nodes in the graph.
+	// The values are being populated by the ApiKeyQuery when eager-loading is set.
+	Edges        ApiKeyEdges `json:"edges"`
+	selectValues sql.SelectValues
+}
+
+// ApiKeyEdges holds the relations/edges for other nodes in the graph.
+type ApiKeyEdges struct {
+	// Agent holds the value of the agent edge.
+	Agent *Agent `json:"agent,omitempty"`
+	// loadedTypes holds the information for reporting if a
+	// type was loaded (or requested) in eager-loading or not.
+	loadedTypes [1]bool
+}
+
+// AgentOrErr returns the Agent value or an error if the edge
+// was not loaded in eager-loading, or loaded but was not found.
+func (e ApiKeyEdges) AgentOrErr() (*Agent, error) {
+	if e.Agent != nil {
+		return e.Agent, nil
+	} else if e.loadedTypes[0] {
+		return nil, &NotFoundError{label: agent.Label}
+	}
+	return nil, &NotLoadedError{edge: "agent"}
+}
+
+// scanValues returns the types for scanning values from sql.Rows.
+func (*ApiKey) scanValues(columns []string) ([]any, error) {
+	values := make([]any, len(columns))
+	for i := range columns {
+		switch columns[i] {
+		case apikey.FieldID, apikey.FieldOrganizationID, apikey.FieldAgentID:
+			values[i] = new(sql.NullInt64)
+		case apikey.FieldTitle, apikey.FieldKey, apikey.FieldCustomAgentBase, apikey.FieldCustomAgentKey, apikey.FieldOpenaiBase, apikey.FieldOpenaiKey:
+			values[i] = new(sql.NullString)
+		case apikey.FieldCreatedAt, apikey.FieldUpdatedAt, apikey.FieldDeletedAt:
+			values[i] = new(sql.NullTime)
+		default:
+			values[i] = new(sql.UnknownType)
+		}
+	}
+	return values, nil
+}
+
+// assignValues assigns the values that were returned from sql.Rows (after scanning)
+// to the ApiKey fields.
+func (ak *ApiKey) 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 apikey.FieldID:
+			value, ok := values[i].(*sql.NullInt64)
+			if !ok {
+				return fmt.Errorf("unexpected type %T for field id", value)
+			}
+			ak.ID = uint64(value.Int64)
+		case apikey.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 {
+				ak.CreatedAt = value.Time
+			}
+		case apikey.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 {
+				ak.UpdatedAt = value.Time
+			}
+		case apikey.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 {
+				ak.DeletedAt = value.Time
+			}
+		case apikey.FieldTitle:
+			if value, ok := values[i].(*sql.NullString); !ok {
+				return fmt.Errorf("unexpected type %T for field title", values[i])
+			} else if value.Valid {
+				ak.Title = value.String
+			}
+		case apikey.FieldKey:
+			if value, ok := values[i].(*sql.NullString); !ok {
+				return fmt.Errorf("unexpected type %T for field key", values[i])
+			} else if value.Valid {
+				ak.Key = value.String
+			}
+		case apikey.FieldOrganizationID:
+			if value, ok := values[i].(*sql.NullInt64); !ok {
+				return fmt.Errorf("unexpected type %T for field organization_id", values[i])
+			} else if value.Valid {
+				ak.OrganizationID = uint64(value.Int64)
+			}
+		case apikey.FieldAgentID:
+			if value, ok := values[i].(*sql.NullInt64); !ok {
+				return fmt.Errorf("unexpected type %T for field agent_id", values[i])
+			} else if value.Valid {
+				ak.AgentID = uint64(value.Int64)
+			}
+		case apikey.FieldCustomAgentBase:
+			if value, ok := values[i].(*sql.NullString); !ok {
+				return fmt.Errorf("unexpected type %T for field custom_agent_base", values[i])
+			} else if value.Valid {
+				ak.CustomAgentBase = value.String
+			}
+		case apikey.FieldCustomAgentKey:
+			if value, ok := values[i].(*sql.NullString); !ok {
+				return fmt.Errorf("unexpected type %T for field custom_agent_key", values[i])
+			} else if value.Valid {
+				ak.CustomAgentKey = value.String
+			}
+		case apikey.FieldOpenaiBase:
+			if value, ok := values[i].(*sql.NullString); !ok {
+				return fmt.Errorf("unexpected type %T for field openai_base", values[i])
+			} else if value.Valid {
+				ak.OpenaiBase = value.String
+			}
+		case apikey.FieldOpenaiKey:
+			if value, ok := values[i].(*sql.NullString); !ok {
+				return fmt.Errorf("unexpected type %T for field openai_key", values[i])
+			} else if value.Valid {
+				ak.OpenaiKey = value.String
+			}
+		default:
+			ak.selectValues.Set(columns[i], values[i])
+		}
+	}
+	return nil
+}
+
+// Value returns the ent.Value that was dynamically selected and assigned to the ApiKey.
+// This includes values selected through modifiers, order, etc.
+func (ak *ApiKey) Value(name string) (ent.Value, error) {
+	return ak.selectValues.Get(name)
+}
+
+// QueryAgent queries the "agent" edge of the ApiKey entity.
+func (ak *ApiKey) QueryAgent() *AgentQuery {
+	return NewApiKeyClient(ak.config).QueryAgent(ak)
+}
+
+// Update returns a builder for updating this ApiKey.
+// Note that you need to call ApiKey.Unwrap() before calling this method if this ApiKey
+// was returned from a transaction, and the transaction was committed or rolled back.
+func (ak *ApiKey) Update() *ApiKeyUpdateOne {
+	return NewApiKeyClient(ak.config).UpdateOne(ak)
+}
+
+// Unwrap unwraps the ApiKey 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 (ak *ApiKey) Unwrap() *ApiKey {
+	_tx, ok := ak.config.driver.(*txDriver)
+	if !ok {
+		panic("ent: ApiKey is not a transactional entity")
+	}
+	ak.config.driver = _tx.drv
+	return ak
+}
+
+// String implements the fmt.Stringer.
+func (ak *ApiKey) String() string {
+	var builder strings.Builder
+	builder.WriteString("ApiKey(")
+	builder.WriteString(fmt.Sprintf("id=%v, ", ak.ID))
+	builder.WriteString("created_at=")
+	builder.WriteString(ak.CreatedAt.Format(time.ANSIC))
+	builder.WriteString(", ")
+	builder.WriteString("updated_at=")
+	builder.WriteString(ak.UpdatedAt.Format(time.ANSIC))
+	builder.WriteString(", ")
+	builder.WriteString("deleted_at=")
+	builder.WriteString(ak.DeletedAt.Format(time.ANSIC))
+	builder.WriteString(", ")
+	builder.WriteString("title=")
+	builder.WriteString(ak.Title)
+	builder.WriteString(", ")
+	builder.WriteString("key=")
+	builder.WriteString(ak.Key)
+	builder.WriteString(", ")
+	builder.WriteString("organization_id=")
+	builder.WriteString(fmt.Sprintf("%v", ak.OrganizationID))
+	builder.WriteString(", ")
+	builder.WriteString("agent_id=")
+	builder.WriteString(fmt.Sprintf("%v", ak.AgentID))
+	builder.WriteString(", ")
+	builder.WriteString("custom_agent_base=")
+	builder.WriteString(ak.CustomAgentBase)
+	builder.WriteString(", ")
+	builder.WriteString("custom_agent_key=")
+	builder.WriteString(ak.CustomAgentKey)
+	builder.WriteString(", ")
+	builder.WriteString("openai_base=")
+	builder.WriteString(ak.OpenaiBase)
+	builder.WriteString(", ")
+	builder.WriteString("openai_key=")
+	builder.WriteString(ak.OpenaiKey)
+	builder.WriteByte(')')
+	return builder.String()
+}
+
+// ApiKeys is a parsable slice of ApiKey.
+type ApiKeys []*ApiKey

+ 186 - 0
ent/apikey/apikey.go

@@ -0,0 +1,186 @@
+// Code generated by ent, DO NOT EDIT.
+
+package apikey
+
+import (
+	"time"
+
+	"entgo.io/ent"
+	"entgo.io/ent/dialect/sql"
+	"entgo.io/ent/dialect/sql/sqlgraph"
+)
+
+const (
+	// Label holds the string label denoting the apikey type in the database.
+	Label = "api_key"
+	// 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"
+	// FieldTitle holds the string denoting the title field in the database.
+	FieldTitle = "title"
+	// FieldKey holds the string denoting the key field in the database.
+	FieldKey = "key"
+	// FieldOrganizationID holds the string denoting the organization_id field in the database.
+	FieldOrganizationID = "organization_id"
+	// FieldAgentID holds the string denoting the agent_id field in the database.
+	FieldAgentID = "agent_id"
+	// FieldCustomAgentBase holds the string denoting the custom_agent_base field in the database.
+	FieldCustomAgentBase = "custom_agent_base"
+	// FieldCustomAgentKey holds the string denoting the custom_agent_key field in the database.
+	FieldCustomAgentKey = "custom_agent_key"
+	// FieldOpenaiBase holds the string denoting the openai_base field in the database.
+	FieldOpenaiBase = "openai_base"
+	// FieldOpenaiKey holds the string denoting the openai_key field in the database.
+	FieldOpenaiKey = "openai_key"
+	// EdgeAgent holds the string denoting the agent edge name in mutations.
+	EdgeAgent = "agent"
+	// Table holds the table name of the apikey in the database.
+	Table = "api_key"
+	// AgentTable is the table that holds the agent relation/edge.
+	AgentTable = "api_key"
+	// AgentInverseTable is the table name for the Agent entity.
+	// It exists in this package in order to avoid circular dependency with the "agent" package.
+	AgentInverseTable = "agent"
+	// AgentColumn is the table column denoting the agent relation/edge.
+	AgentColumn = "agent_id"
+)
+
+// Columns holds all SQL columns for apikey fields.
+var Columns = []string{
+	FieldID,
+	FieldCreatedAt,
+	FieldUpdatedAt,
+	FieldDeletedAt,
+	FieldTitle,
+	FieldKey,
+	FieldOrganizationID,
+	FieldAgentID,
+	FieldCustomAgentBase,
+	FieldCustomAgentKey,
+	FieldOpenaiBase,
+	FieldOpenaiKey,
+}
+
+// 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
+	// DefaultTitle holds the default value on creation for the "title" field.
+	DefaultTitle string
+	// DefaultKey holds the default value on creation for the "key" field.
+	DefaultKey string
+	// DefaultOrganizationID holds the default value on creation for the "organization_id" field.
+	DefaultOrganizationID uint64
+	// DefaultAgentID holds the default value on creation for the "agent_id" field.
+	DefaultAgentID uint64
+	// DefaultCustomAgentBase holds the default value on creation for the "custom_agent_base" field.
+	DefaultCustomAgentBase string
+	// DefaultCustomAgentKey holds the default value on creation for the "custom_agent_key" field.
+	DefaultCustomAgentKey string
+	// DefaultOpenaiBase holds the default value on creation for the "openai_base" field.
+	DefaultOpenaiBase string
+	// DefaultOpenaiKey holds the default value on creation for the "openai_key" field.
+	DefaultOpenaiKey string
+)
+
+// OrderOption defines the ordering options for the ApiKey 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()
+}
+
+// ByTitle orders the results by the title field.
+func ByTitle(opts ...sql.OrderTermOption) OrderOption {
+	return sql.OrderByField(FieldTitle, opts...).ToFunc()
+}
+
+// ByKey orders the results by the key field.
+func ByKey(opts ...sql.OrderTermOption) OrderOption {
+	return sql.OrderByField(FieldKey, opts...).ToFunc()
+}
+
+// ByOrganizationID orders the results by the organization_id field.
+func ByOrganizationID(opts ...sql.OrderTermOption) OrderOption {
+	return sql.OrderByField(FieldOrganizationID, opts...).ToFunc()
+}
+
+// ByAgentID orders the results by the agent_id field.
+func ByAgentID(opts ...sql.OrderTermOption) OrderOption {
+	return sql.OrderByField(FieldAgentID, opts...).ToFunc()
+}
+
+// ByCustomAgentBase orders the results by the custom_agent_base field.
+func ByCustomAgentBase(opts ...sql.OrderTermOption) OrderOption {
+	return sql.OrderByField(FieldCustomAgentBase, opts...).ToFunc()
+}
+
+// ByCustomAgentKey orders the results by the custom_agent_key field.
+func ByCustomAgentKey(opts ...sql.OrderTermOption) OrderOption {
+	return sql.OrderByField(FieldCustomAgentKey, opts...).ToFunc()
+}
+
+// ByOpenaiBase orders the results by the openai_base field.
+func ByOpenaiBase(opts ...sql.OrderTermOption) OrderOption {
+	return sql.OrderByField(FieldOpenaiBase, opts...).ToFunc()
+}
+
+// ByOpenaiKey orders the results by the openai_key field.
+func ByOpenaiKey(opts ...sql.OrderTermOption) OrderOption {
+	return sql.OrderByField(FieldOpenaiKey, opts...).ToFunc()
+}
+
+// ByAgentField orders the results by agent field.
+func ByAgentField(field string, opts ...sql.OrderTermOption) OrderOption {
+	return func(s *sql.Selector) {
+		sqlgraph.OrderByNeighborTerms(s, newAgentStep(), sql.OrderByField(field, opts...))
+	}
+}
+func newAgentStep() *sqlgraph.Step {
+	return sqlgraph.NewStep(
+		sqlgraph.From(Table, FieldID),
+		sqlgraph.To(AgentInverseTable, FieldID),
+		sqlgraph.Edge(sqlgraph.M2O, true, AgentTable, AgentColumn),
+	)
+}

+ 789 - 0
ent/apikey/where.go

@@ -0,0 +1,789 @@
+// Code generated by ent, DO NOT EDIT.
+
+package apikey
+
+import (
+	"time"
+	"wechat-api/ent/predicate"
+
+	"entgo.io/ent/dialect/sql"
+	"entgo.io/ent/dialect/sql/sqlgraph"
+)
+
+// ID filters vertices based on their ID field.
+func ID(id uint64) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldEQ(FieldID, id))
+}
+
+// IDEQ applies the EQ predicate on the ID field.
+func IDEQ(id uint64) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldEQ(FieldID, id))
+}
+
+// IDNEQ applies the NEQ predicate on the ID field.
+func IDNEQ(id uint64) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldNEQ(FieldID, id))
+}
+
+// IDIn applies the In predicate on the ID field.
+func IDIn(ids ...uint64) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldIn(FieldID, ids...))
+}
+
+// IDNotIn applies the NotIn predicate on the ID field.
+func IDNotIn(ids ...uint64) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldNotIn(FieldID, ids...))
+}
+
+// IDGT applies the GT predicate on the ID field.
+func IDGT(id uint64) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldGT(FieldID, id))
+}
+
+// IDGTE applies the GTE predicate on the ID field.
+func IDGTE(id uint64) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldGTE(FieldID, id))
+}
+
+// IDLT applies the LT predicate on the ID field.
+func IDLT(id uint64) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldLT(FieldID, id))
+}
+
+// IDLTE applies the LTE predicate on the ID field.
+func IDLTE(id uint64) predicate.ApiKey {
+	return predicate.ApiKey(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.ApiKey {
+	return predicate.ApiKey(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.ApiKey {
+	return predicate.ApiKey(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.ApiKey {
+	return predicate.ApiKey(sql.FieldEQ(FieldDeletedAt, v))
+}
+
+// Title applies equality check predicate on the "title" field. It's identical to TitleEQ.
+func Title(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldEQ(FieldTitle, v))
+}
+
+// Key applies equality check predicate on the "key" field. It's identical to KeyEQ.
+func Key(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldEQ(FieldKey, v))
+}
+
+// OrganizationID applies equality check predicate on the "organization_id" field. It's identical to OrganizationIDEQ.
+func OrganizationID(v uint64) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldEQ(FieldOrganizationID, v))
+}
+
+// AgentID applies equality check predicate on the "agent_id" field. It's identical to AgentIDEQ.
+func AgentID(v uint64) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldEQ(FieldAgentID, v))
+}
+
+// CustomAgentBase applies equality check predicate on the "custom_agent_base" field. It's identical to CustomAgentBaseEQ.
+func CustomAgentBase(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldEQ(FieldCustomAgentBase, v))
+}
+
+// CustomAgentKey applies equality check predicate on the "custom_agent_key" field. It's identical to CustomAgentKeyEQ.
+func CustomAgentKey(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldEQ(FieldCustomAgentKey, v))
+}
+
+// OpenaiBase applies equality check predicate on the "openai_base" field. It's identical to OpenaiBaseEQ.
+func OpenaiBase(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldEQ(FieldOpenaiBase, v))
+}
+
+// OpenaiKey applies equality check predicate on the "openai_key" field. It's identical to OpenaiKeyEQ.
+func OpenaiKey(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldEQ(FieldOpenaiKey, v))
+}
+
+// CreatedAtEQ applies the EQ predicate on the "created_at" field.
+func CreatedAtEQ(v time.Time) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldEQ(FieldCreatedAt, v))
+}
+
+// CreatedAtNEQ applies the NEQ predicate on the "created_at" field.
+func CreatedAtNEQ(v time.Time) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldNEQ(FieldCreatedAt, v))
+}
+
+// CreatedAtIn applies the In predicate on the "created_at" field.
+func CreatedAtIn(vs ...time.Time) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldIn(FieldCreatedAt, vs...))
+}
+
+// CreatedAtNotIn applies the NotIn predicate on the "created_at" field.
+func CreatedAtNotIn(vs ...time.Time) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldNotIn(FieldCreatedAt, vs...))
+}
+
+// CreatedAtGT applies the GT predicate on the "created_at" field.
+func CreatedAtGT(v time.Time) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldGT(FieldCreatedAt, v))
+}
+
+// CreatedAtGTE applies the GTE predicate on the "created_at" field.
+func CreatedAtGTE(v time.Time) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldGTE(FieldCreatedAt, v))
+}
+
+// CreatedAtLT applies the LT predicate on the "created_at" field.
+func CreatedAtLT(v time.Time) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldLT(FieldCreatedAt, v))
+}
+
+// CreatedAtLTE applies the LTE predicate on the "created_at" field.
+func CreatedAtLTE(v time.Time) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldLTE(FieldCreatedAt, v))
+}
+
+// UpdatedAtEQ applies the EQ predicate on the "updated_at" field.
+func UpdatedAtEQ(v time.Time) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldEQ(FieldUpdatedAt, v))
+}
+
+// UpdatedAtNEQ applies the NEQ predicate on the "updated_at" field.
+func UpdatedAtNEQ(v time.Time) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldNEQ(FieldUpdatedAt, v))
+}
+
+// UpdatedAtIn applies the In predicate on the "updated_at" field.
+func UpdatedAtIn(vs ...time.Time) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldIn(FieldUpdatedAt, vs...))
+}
+
+// UpdatedAtNotIn applies the NotIn predicate on the "updated_at" field.
+func UpdatedAtNotIn(vs ...time.Time) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldNotIn(FieldUpdatedAt, vs...))
+}
+
+// UpdatedAtGT applies the GT predicate on the "updated_at" field.
+func UpdatedAtGT(v time.Time) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldGT(FieldUpdatedAt, v))
+}
+
+// UpdatedAtGTE applies the GTE predicate on the "updated_at" field.
+func UpdatedAtGTE(v time.Time) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldGTE(FieldUpdatedAt, v))
+}
+
+// UpdatedAtLT applies the LT predicate on the "updated_at" field.
+func UpdatedAtLT(v time.Time) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldLT(FieldUpdatedAt, v))
+}
+
+// UpdatedAtLTE applies the LTE predicate on the "updated_at" field.
+func UpdatedAtLTE(v time.Time) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldLTE(FieldUpdatedAt, v))
+}
+
+// DeletedAtEQ applies the EQ predicate on the "deleted_at" field.
+func DeletedAtEQ(v time.Time) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldEQ(FieldDeletedAt, v))
+}
+
+// DeletedAtNEQ applies the NEQ predicate on the "deleted_at" field.
+func DeletedAtNEQ(v time.Time) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldNEQ(FieldDeletedAt, v))
+}
+
+// DeletedAtIn applies the In predicate on the "deleted_at" field.
+func DeletedAtIn(vs ...time.Time) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldIn(FieldDeletedAt, vs...))
+}
+
+// DeletedAtNotIn applies the NotIn predicate on the "deleted_at" field.
+func DeletedAtNotIn(vs ...time.Time) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldNotIn(FieldDeletedAt, vs...))
+}
+
+// DeletedAtGT applies the GT predicate on the "deleted_at" field.
+func DeletedAtGT(v time.Time) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldGT(FieldDeletedAt, v))
+}
+
+// DeletedAtGTE applies the GTE predicate on the "deleted_at" field.
+func DeletedAtGTE(v time.Time) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldGTE(FieldDeletedAt, v))
+}
+
+// DeletedAtLT applies the LT predicate on the "deleted_at" field.
+func DeletedAtLT(v time.Time) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldLT(FieldDeletedAt, v))
+}
+
+// DeletedAtLTE applies the LTE predicate on the "deleted_at" field.
+func DeletedAtLTE(v time.Time) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldLTE(FieldDeletedAt, v))
+}
+
+// DeletedAtIsNil applies the IsNil predicate on the "deleted_at" field.
+func DeletedAtIsNil() predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldIsNull(FieldDeletedAt))
+}
+
+// DeletedAtNotNil applies the NotNil predicate on the "deleted_at" field.
+func DeletedAtNotNil() predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldNotNull(FieldDeletedAt))
+}
+
+// TitleEQ applies the EQ predicate on the "title" field.
+func TitleEQ(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldEQ(FieldTitle, v))
+}
+
+// TitleNEQ applies the NEQ predicate on the "title" field.
+func TitleNEQ(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldNEQ(FieldTitle, v))
+}
+
+// TitleIn applies the In predicate on the "title" field.
+func TitleIn(vs ...string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldIn(FieldTitle, vs...))
+}
+
+// TitleNotIn applies the NotIn predicate on the "title" field.
+func TitleNotIn(vs ...string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldNotIn(FieldTitle, vs...))
+}
+
+// TitleGT applies the GT predicate on the "title" field.
+func TitleGT(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldGT(FieldTitle, v))
+}
+
+// TitleGTE applies the GTE predicate on the "title" field.
+func TitleGTE(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldGTE(FieldTitle, v))
+}
+
+// TitleLT applies the LT predicate on the "title" field.
+func TitleLT(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldLT(FieldTitle, v))
+}
+
+// TitleLTE applies the LTE predicate on the "title" field.
+func TitleLTE(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldLTE(FieldTitle, v))
+}
+
+// TitleContains applies the Contains predicate on the "title" field.
+func TitleContains(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldContains(FieldTitle, v))
+}
+
+// TitleHasPrefix applies the HasPrefix predicate on the "title" field.
+func TitleHasPrefix(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldHasPrefix(FieldTitle, v))
+}
+
+// TitleHasSuffix applies the HasSuffix predicate on the "title" field.
+func TitleHasSuffix(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldHasSuffix(FieldTitle, v))
+}
+
+// TitleIsNil applies the IsNil predicate on the "title" field.
+func TitleIsNil() predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldIsNull(FieldTitle))
+}
+
+// TitleNotNil applies the NotNil predicate on the "title" field.
+func TitleNotNil() predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldNotNull(FieldTitle))
+}
+
+// TitleEqualFold applies the EqualFold predicate on the "title" field.
+func TitleEqualFold(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldEqualFold(FieldTitle, v))
+}
+
+// TitleContainsFold applies the ContainsFold predicate on the "title" field.
+func TitleContainsFold(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldContainsFold(FieldTitle, v))
+}
+
+// KeyEQ applies the EQ predicate on the "key" field.
+func KeyEQ(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldEQ(FieldKey, v))
+}
+
+// KeyNEQ applies the NEQ predicate on the "key" field.
+func KeyNEQ(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldNEQ(FieldKey, v))
+}
+
+// KeyIn applies the In predicate on the "key" field.
+func KeyIn(vs ...string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldIn(FieldKey, vs...))
+}
+
+// KeyNotIn applies the NotIn predicate on the "key" field.
+func KeyNotIn(vs ...string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldNotIn(FieldKey, vs...))
+}
+
+// KeyGT applies the GT predicate on the "key" field.
+func KeyGT(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldGT(FieldKey, v))
+}
+
+// KeyGTE applies the GTE predicate on the "key" field.
+func KeyGTE(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldGTE(FieldKey, v))
+}
+
+// KeyLT applies the LT predicate on the "key" field.
+func KeyLT(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldLT(FieldKey, v))
+}
+
+// KeyLTE applies the LTE predicate on the "key" field.
+func KeyLTE(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldLTE(FieldKey, v))
+}
+
+// KeyContains applies the Contains predicate on the "key" field.
+func KeyContains(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldContains(FieldKey, v))
+}
+
+// KeyHasPrefix applies the HasPrefix predicate on the "key" field.
+func KeyHasPrefix(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldHasPrefix(FieldKey, v))
+}
+
+// KeyHasSuffix applies the HasSuffix predicate on the "key" field.
+func KeyHasSuffix(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldHasSuffix(FieldKey, v))
+}
+
+// KeyIsNil applies the IsNil predicate on the "key" field.
+func KeyIsNil() predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldIsNull(FieldKey))
+}
+
+// KeyNotNil applies the NotNil predicate on the "key" field.
+func KeyNotNil() predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldNotNull(FieldKey))
+}
+
+// KeyEqualFold applies the EqualFold predicate on the "key" field.
+func KeyEqualFold(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldEqualFold(FieldKey, v))
+}
+
+// KeyContainsFold applies the ContainsFold predicate on the "key" field.
+func KeyContainsFold(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldContainsFold(FieldKey, v))
+}
+
+// OrganizationIDEQ applies the EQ predicate on the "organization_id" field.
+func OrganizationIDEQ(v uint64) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldEQ(FieldOrganizationID, v))
+}
+
+// OrganizationIDNEQ applies the NEQ predicate on the "organization_id" field.
+func OrganizationIDNEQ(v uint64) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldNEQ(FieldOrganizationID, v))
+}
+
+// OrganizationIDIn applies the In predicate on the "organization_id" field.
+func OrganizationIDIn(vs ...uint64) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldIn(FieldOrganizationID, vs...))
+}
+
+// OrganizationIDNotIn applies the NotIn predicate on the "organization_id" field.
+func OrganizationIDNotIn(vs ...uint64) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldNotIn(FieldOrganizationID, vs...))
+}
+
+// OrganizationIDGT applies the GT predicate on the "organization_id" field.
+func OrganizationIDGT(v uint64) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldGT(FieldOrganizationID, v))
+}
+
+// OrganizationIDGTE applies the GTE predicate on the "organization_id" field.
+func OrganizationIDGTE(v uint64) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldGTE(FieldOrganizationID, v))
+}
+
+// OrganizationIDLT applies the LT predicate on the "organization_id" field.
+func OrganizationIDLT(v uint64) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldLT(FieldOrganizationID, v))
+}
+
+// OrganizationIDLTE applies the LTE predicate on the "organization_id" field.
+func OrganizationIDLTE(v uint64) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldLTE(FieldOrganizationID, v))
+}
+
+// AgentIDEQ applies the EQ predicate on the "agent_id" field.
+func AgentIDEQ(v uint64) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldEQ(FieldAgentID, v))
+}
+
+// AgentIDNEQ applies the NEQ predicate on the "agent_id" field.
+func AgentIDNEQ(v uint64) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldNEQ(FieldAgentID, v))
+}
+
+// AgentIDIn applies the In predicate on the "agent_id" field.
+func AgentIDIn(vs ...uint64) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldIn(FieldAgentID, vs...))
+}
+
+// AgentIDNotIn applies the NotIn predicate on the "agent_id" field.
+func AgentIDNotIn(vs ...uint64) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldNotIn(FieldAgentID, vs...))
+}
+
+// CustomAgentBaseEQ applies the EQ predicate on the "custom_agent_base" field.
+func CustomAgentBaseEQ(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldEQ(FieldCustomAgentBase, v))
+}
+
+// CustomAgentBaseNEQ applies the NEQ predicate on the "custom_agent_base" field.
+func CustomAgentBaseNEQ(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldNEQ(FieldCustomAgentBase, v))
+}
+
+// CustomAgentBaseIn applies the In predicate on the "custom_agent_base" field.
+func CustomAgentBaseIn(vs ...string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldIn(FieldCustomAgentBase, vs...))
+}
+
+// CustomAgentBaseNotIn applies the NotIn predicate on the "custom_agent_base" field.
+func CustomAgentBaseNotIn(vs ...string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldNotIn(FieldCustomAgentBase, vs...))
+}
+
+// CustomAgentBaseGT applies the GT predicate on the "custom_agent_base" field.
+func CustomAgentBaseGT(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldGT(FieldCustomAgentBase, v))
+}
+
+// CustomAgentBaseGTE applies the GTE predicate on the "custom_agent_base" field.
+func CustomAgentBaseGTE(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldGTE(FieldCustomAgentBase, v))
+}
+
+// CustomAgentBaseLT applies the LT predicate on the "custom_agent_base" field.
+func CustomAgentBaseLT(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldLT(FieldCustomAgentBase, v))
+}
+
+// CustomAgentBaseLTE applies the LTE predicate on the "custom_agent_base" field.
+func CustomAgentBaseLTE(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldLTE(FieldCustomAgentBase, v))
+}
+
+// CustomAgentBaseContains applies the Contains predicate on the "custom_agent_base" field.
+func CustomAgentBaseContains(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldContains(FieldCustomAgentBase, v))
+}
+
+// CustomAgentBaseHasPrefix applies the HasPrefix predicate on the "custom_agent_base" field.
+func CustomAgentBaseHasPrefix(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldHasPrefix(FieldCustomAgentBase, v))
+}
+
+// CustomAgentBaseHasSuffix applies the HasSuffix predicate on the "custom_agent_base" field.
+func CustomAgentBaseHasSuffix(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldHasSuffix(FieldCustomAgentBase, v))
+}
+
+// CustomAgentBaseIsNil applies the IsNil predicate on the "custom_agent_base" field.
+func CustomAgentBaseIsNil() predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldIsNull(FieldCustomAgentBase))
+}
+
+// CustomAgentBaseNotNil applies the NotNil predicate on the "custom_agent_base" field.
+func CustomAgentBaseNotNil() predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldNotNull(FieldCustomAgentBase))
+}
+
+// CustomAgentBaseEqualFold applies the EqualFold predicate on the "custom_agent_base" field.
+func CustomAgentBaseEqualFold(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldEqualFold(FieldCustomAgentBase, v))
+}
+
+// CustomAgentBaseContainsFold applies the ContainsFold predicate on the "custom_agent_base" field.
+func CustomAgentBaseContainsFold(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldContainsFold(FieldCustomAgentBase, v))
+}
+
+// CustomAgentKeyEQ applies the EQ predicate on the "custom_agent_key" field.
+func CustomAgentKeyEQ(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldEQ(FieldCustomAgentKey, v))
+}
+
+// CustomAgentKeyNEQ applies the NEQ predicate on the "custom_agent_key" field.
+func CustomAgentKeyNEQ(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldNEQ(FieldCustomAgentKey, v))
+}
+
+// CustomAgentKeyIn applies the In predicate on the "custom_agent_key" field.
+func CustomAgentKeyIn(vs ...string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldIn(FieldCustomAgentKey, vs...))
+}
+
+// CustomAgentKeyNotIn applies the NotIn predicate on the "custom_agent_key" field.
+func CustomAgentKeyNotIn(vs ...string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldNotIn(FieldCustomAgentKey, vs...))
+}
+
+// CustomAgentKeyGT applies the GT predicate on the "custom_agent_key" field.
+func CustomAgentKeyGT(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldGT(FieldCustomAgentKey, v))
+}
+
+// CustomAgentKeyGTE applies the GTE predicate on the "custom_agent_key" field.
+func CustomAgentKeyGTE(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldGTE(FieldCustomAgentKey, v))
+}
+
+// CustomAgentKeyLT applies the LT predicate on the "custom_agent_key" field.
+func CustomAgentKeyLT(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldLT(FieldCustomAgentKey, v))
+}
+
+// CustomAgentKeyLTE applies the LTE predicate on the "custom_agent_key" field.
+func CustomAgentKeyLTE(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldLTE(FieldCustomAgentKey, v))
+}
+
+// CustomAgentKeyContains applies the Contains predicate on the "custom_agent_key" field.
+func CustomAgentKeyContains(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldContains(FieldCustomAgentKey, v))
+}
+
+// CustomAgentKeyHasPrefix applies the HasPrefix predicate on the "custom_agent_key" field.
+func CustomAgentKeyHasPrefix(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldHasPrefix(FieldCustomAgentKey, v))
+}
+
+// CustomAgentKeyHasSuffix applies the HasSuffix predicate on the "custom_agent_key" field.
+func CustomAgentKeyHasSuffix(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldHasSuffix(FieldCustomAgentKey, v))
+}
+
+// CustomAgentKeyIsNil applies the IsNil predicate on the "custom_agent_key" field.
+func CustomAgentKeyIsNil() predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldIsNull(FieldCustomAgentKey))
+}
+
+// CustomAgentKeyNotNil applies the NotNil predicate on the "custom_agent_key" field.
+func CustomAgentKeyNotNil() predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldNotNull(FieldCustomAgentKey))
+}
+
+// CustomAgentKeyEqualFold applies the EqualFold predicate on the "custom_agent_key" field.
+func CustomAgentKeyEqualFold(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldEqualFold(FieldCustomAgentKey, v))
+}
+
+// CustomAgentKeyContainsFold applies the ContainsFold predicate on the "custom_agent_key" field.
+func CustomAgentKeyContainsFold(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldContainsFold(FieldCustomAgentKey, v))
+}
+
+// OpenaiBaseEQ applies the EQ predicate on the "openai_base" field.
+func OpenaiBaseEQ(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldEQ(FieldOpenaiBase, v))
+}
+
+// OpenaiBaseNEQ applies the NEQ predicate on the "openai_base" field.
+func OpenaiBaseNEQ(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldNEQ(FieldOpenaiBase, v))
+}
+
+// OpenaiBaseIn applies the In predicate on the "openai_base" field.
+func OpenaiBaseIn(vs ...string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldIn(FieldOpenaiBase, vs...))
+}
+
+// OpenaiBaseNotIn applies the NotIn predicate on the "openai_base" field.
+func OpenaiBaseNotIn(vs ...string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldNotIn(FieldOpenaiBase, vs...))
+}
+
+// OpenaiBaseGT applies the GT predicate on the "openai_base" field.
+func OpenaiBaseGT(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldGT(FieldOpenaiBase, v))
+}
+
+// OpenaiBaseGTE applies the GTE predicate on the "openai_base" field.
+func OpenaiBaseGTE(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldGTE(FieldOpenaiBase, v))
+}
+
+// OpenaiBaseLT applies the LT predicate on the "openai_base" field.
+func OpenaiBaseLT(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldLT(FieldOpenaiBase, v))
+}
+
+// OpenaiBaseLTE applies the LTE predicate on the "openai_base" field.
+func OpenaiBaseLTE(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldLTE(FieldOpenaiBase, v))
+}
+
+// OpenaiBaseContains applies the Contains predicate on the "openai_base" field.
+func OpenaiBaseContains(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldContains(FieldOpenaiBase, v))
+}
+
+// OpenaiBaseHasPrefix applies the HasPrefix predicate on the "openai_base" field.
+func OpenaiBaseHasPrefix(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldHasPrefix(FieldOpenaiBase, v))
+}
+
+// OpenaiBaseHasSuffix applies the HasSuffix predicate on the "openai_base" field.
+func OpenaiBaseHasSuffix(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldHasSuffix(FieldOpenaiBase, v))
+}
+
+// OpenaiBaseIsNil applies the IsNil predicate on the "openai_base" field.
+func OpenaiBaseIsNil() predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldIsNull(FieldOpenaiBase))
+}
+
+// OpenaiBaseNotNil applies the NotNil predicate on the "openai_base" field.
+func OpenaiBaseNotNil() predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldNotNull(FieldOpenaiBase))
+}
+
+// OpenaiBaseEqualFold applies the EqualFold predicate on the "openai_base" field.
+func OpenaiBaseEqualFold(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldEqualFold(FieldOpenaiBase, v))
+}
+
+// OpenaiBaseContainsFold applies the ContainsFold predicate on the "openai_base" field.
+func OpenaiBaseContainsFold(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldContainsFold(FieldOpenaiBase, v))
+}
+
+// OpenaiKeyEQ applies the EQ predicate on the "openai_key" field.
+func OpenaiKeyEQ(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldEQ(FieldOpenaiKey, v))
+}
+
+// OpenaiKeyNEQ applies the NEQ predicate on the "openai_key" field.
+func OpenaiKeyNEQ(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldNEQ(FieldOpenaiKey, v))
+}
+
+// OpenaiKeyIn applies the In predicate on the "openai_key" field.
+func OpenaiKeyIn(vs ...string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldIn(FieldOpenaiKey, vs...))
+}
+
+// OpenaiKeyNotIn applies the NotIn predicate on the "openai_key" field.
+func OpenaiKeyNotIn(vs ...string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldNotIn(FieldOpenaiKey, vs...))
+}
+
+// OpenaiKeyGT applies the GT predicate on the "openai_key" field.
+func OpenaiKeyGT(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldGT(FieldOpenaiKey, v))
+}
+
+// OpenaiKeyGTE applies the GTE predicate on the "openai_key" field.
+func OpenaiKeyGTE(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldGTE(FieldOpenaiKey, v))
+}
+
+// OpenaiKeyLT applies the LT predicate on the "openai_key" field.
+func OpenaiKeyLT(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldLT(FieldOpenaiKey, v))
+}
+
+// OpenaiKeyLTE applies the LTE predicate on the "openai_key" field.
+func OpenaiKeyLTE(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldLTE(FieldOpenaiKey, v))
+}
+
+// OpenaiKeyContains applies the Contains predicate on the "openai_key" field.
+func OpenaiKeyContains(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldContains(FieldOpenaiKey, v))
+}
+
+// OpenaiKeyHasPrefix applies the HasPrefix predicate on the "openai_key" field.
+func OpenaiKeyHasPrefix(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldHasPrefix(FieldOpenaiKey, v))
+}
+
+// OpenaiKeyHasSuffix applies the HasSuffix predicate on the "openai_key" field.
+func OpenaiKeyHasSuffix(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldHasSuffix(FieldOpenaiKey, v))
+}
+
+// OpenaiKeyIsNil applies the IsNil predicate on the "openai_key" field.
+func OpenaiKeyIsNil() predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldIsNull(FieldOpenaiKey))
+}
+
+// OpenaiKeyNotNil applies the NotNil predicate on the "openai_key" field.
+func OpenaiKeyNotNil() predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldNotNull(FieldOpenaiKey))
+}
+
+// OpenaiKeyEqualFold applies the EqualFold predicate on the "openai_key" field.
+func OpenaiKeyEqualFold(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldEqualFold(FieldOpenaiKey, v))
+}
+
+// OpenaiKeyContainsFold applies the ContainsFold predicate on the "openai_key" field.
+func OpenaiKeyContainsFold(v string) predicate.ApiKey {
+	return predicate.ApiKey(sql.FieldContainsFold(FieldOpenaiKey, v))
+}
+
+// HasAgent applies the HasEdge predicate on the "agent" edge.
+func HasAgent() predicate.ApiKey {
+	return predicate.ApiKey(func(s *sql.Selector) {
+		step := sqlgraph.NewStep(
+			sqlgraph.From(Table, FieldID),
+			sqlgraph.Edge(sqlgraph.M2O, true, AgentTable, AgentColumn),
+		)
+		sqlgraph.HasNeighbors(s, step)
+	})
+}
+
+// HasAgentWith applies the HasEdge predicate on the "agent" edge with a given conditions (other predicates).
+func HasAgentWith(preds ...predicate.Agent) predicate.ApiKey {
+	return predicate.ApiKey(func(s *sql.Selector) {
+		step := newAgentStep()
+		sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
+			for _, p := range preds {
+				p(s)
+			}
+		})
+	})
+}
+
+// And groups predicates with the AND operator between them.
+func And(predicates ...predicate.ApiKey) predicate.ApiKey {
+	return predicate.ApiKey(sql.AndPredicates(predicates...))
+}
+
+// Or groups predicates with the OR operator between them.
+func Or(predicates ...predicate.ApiKey) predicate.ApiKey {
+	return predicate.ApiKey(sql.OrPredicates(predicates...))
+}
+
+// Not applies the not operator on the given predicate.
+func Not(p predicate.ApiKey) predicate.ApiKey {
+	return predicate.ApiKey(sql.NotPredicates(p))
+}

+ 1285 - 0
ent/apikey_create.go

@@ -0,0 +1,1285 @@
+// Code generated by ent, DO NOT EDIT.
+
+package ent
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"time"
+	"wechat-api/ent/agent"
+	"wechat-api/ent/apikey"
+
+	"entgo.io/ent/dialect/sql"
+	"entgo.io/ent/dialect/sql/sqlgraph"
+	"entgo.io/ent/schema/field"
+)
+
+// ApiKeyCreate is the builder for creating a ApiKey entity.
+type ApiKeyCreate struct {
+	config
+	mutation *ApiKeyMutation
+	hooks    []Hook
+	conflict []sql.ConflictOption
+}
+
+// SetCreatedAt sets the "created_at" field.
+func (akc *ApiKeyCreate) SetCreatedAt(t time.Time) *ApiKeyCreate {
+	akc.mutation.SetCreatedAt(t)
+	return akc
+}
+
+// SetNillableCreatedAt sets the "created_at" field if the given value is not nil.
+func (akc *ApiKeyCreate) SetNillableCreatedAt(t *time.Time) *ApiKeyCreate {
+	if t != nil {
+		akc.SetCreatedAt(*t)
+	}
+	return akc
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (akc *ApiKeyCreate) SetUpdatedAt(t time.Time) *ApiKeyCreate {
+	akc.mutation.SetUpdatedAt(t)
+	return akc
+}
+
+// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil.
+func (akc *ApiKeyCreate) SetNillableUpdatedAt(t *time.Time) *ApiKeyCreate {
+	if t != nil {
+		akc.SetUpdatedAt(*t)
+	}
+	return akc
+}
+
+// SetDeletedAt sets the "deleted_at" field.
+func (akc *ApiKeyCreate) SetDeletedAt(t time.Time) *ApiKeyCreate {
+	akc.mutation.SetDeletedAt(t)
+	return akc
+}
+
+// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil.
+func (akc *ApiKeyCreate) SetNillableDeletedAt(t *time.Time) *ApiKeyCreate {
+	if t != nil {
+		akc.SetDeletedAt(*t)
+	}
+	return akc
+}
+
+// SetTitle sets the "title" field.
+func (akc *ApiKeyCreate) SetTitle(s string) *ApiKeyCreate {
+	akc.mutation.SetTitle(s)
+	return akc
+}
+
+// SetNillableTitle sets the "title" field if the given value is not nil.
+func (akc *ApiKeyCreate) SetNillableTitle(s *string) *ApiKeyCreate {
+	if s != nil {
+		akc.SetTitle(*s)
+	}
+	return akc
+}
+
+// SetKey sets the "key" field.
+func (akc *ApiKeyCreate) SetKey(s string) *ApiKeyCreate {
+	akc.mutation.SetKey(s)
+	return akc
+}
+
+// SetNillableKey sets the "key" field if the given value is not nil.
+func (akc *ApiKeyCreate) SetNillableKey(s *string) *ApiKeyCreate {
+	if s != nil {
+		akc.SetKey(*s)
+	}
+	return akc
+}
+
+// SetOrganizationID sets the "organization_id" field.
+func (akc *ApiKeyCreate) SetOrganizationID(u uint64) *ApiKeyCreate {
+	akc.mutation.SetOrganizationID(u)
+	return akc
+}
+
+// SetNillableOrganizationID sets the "organization_id" field if the given value is not nil.
+func (akc *ApiKeyCreate) SetNillableOrganizationID(u *uint64) *ApiKeyCreate {
+	if u != nil {
+		akc.SetOrganizationID(*u)
+	}
+	return akc
+}
+
+// SetAgentID sets the "agent_id" field.
+func (akc *ApiKeyCreate) SetAgentID(u uint64) *ApiKeyCreate {
+	akc.mutation.SetAgentID(u)
+	return akc
+}
+
+// SetNillableAgentID sets the "agent_id" field if the given value is not nil.
+func (akc *ApiKeyCreate) SetNillableAgentID(u *uint64) *ApiKeyCreate {
+	if u != nil {
+		akc.SetAgentID(*u)
+	}
+	return akc
+}
+
+// SetCustomAgentBase sets the "custom_agent_base" field.
+func (akc *ApiKeyCreate) SetCustomAgentBase(s string) *ApiKeyCreate {
+	akc.mutation.SetCustomAgentBase(s)
+	return akc
+}
+
+// SetNillableCustomAgentBase sets the "custom_agent_base" field if the given value is not nil.
+func (akc *ApiKeyCreate) SetNillableCustomAgentBase(s *string) *ApiKeyCreate {
+	if s != nil {
+		akc.SetCustomAgentBase(*s)
+	}
+	return akc
+}
+
+// SetCustomAgentKey sets the "custom_agent_key" field.
+func (akc *ApiKeyCreate) SetCustomAgentKey(s string) *ApiKeyCreate {
+	akc.mutation.SetCustomAgentKey(s)
+	return akc
+}
+
+// SetNillableCustomAgentKey sets the "custom_agent_key" field if the given value is not nil.
+func (akc *ApiKeyCreate) SetNillableCustomAgentKey(s *string) *ApiKeyCreate {
+	if s != nil {
+		akc.SetCustomAgentKey(*s)
+	}
+	return akc
+}
+
+// SetOpenaiBase sets the "openai_base" field.
+func (akc *ApiKeyCreate) SetOpenaiBase(s string) *ApiKeyCreate {
+	akc.mutation.SetOpenaiBase(s)
+	return akc
+}
+
+// SetNillableOpenaiBase sets the "openai_base" field if the given value is not nil.
+func (akc *ApiKeyCreate) SetNillableOpenaiBase(s *string) *ApiKeyCreate {
+	if s != nil {
+		akc.SetOpenaiBase(*s)
+	}
+	return akc
+}
+
+// SetOpenaiKey sets the "openai_key" field.
+func (akc *ApiKeyCreate) SetOpenaiKey(s string) *ApiKeyCreate {
+	akc.mutation.SetOpenaiKey(s)
+	return akc
+}
+
+// SetNillableOpenaiKey sets the "openai_key" field if the given value is not nil.
+func (akc *ApiKeyCreate) SetNillableOpenaiKey(s *string) *ApiKeyCreate {
+	if s != nil {
+		akc.SetOpenaiKey(*s)
+	}
+	return akc
+}
+
+// SetID sets the "id" field.
+func (akc *ApiKeyCreate) SetID(u uint64) *ApiKeyCreate {
+	akc.mutation.SetID(u)
+	return akc
+}
+
+// SetAgent sets the "agent" edge to the Agent entity.
+func (akc *ApiKeyCreate) SetAgent(a *Agent) *ApiKeyCreate {
+	return akc.SetAgentID(a.ID)
+}
+
+// Mutation returns the ApiKeyMutation object of the builder.
+func (akc *ApiKeyCreate) Mutation() *ApiKeyMutation {
+	return akc.mutation
+}
+
+// Save creates the ApiKey in the database.
+func (akc *ApiKeyCreate) Save(ctx context.Context) (*ApiKey, error) {
+	if err := akc.defaults(); err != nil {
+		return nil, err
+	}
+	return withHooks(ctx, akc.sqlSave, akc.mutation, akc.hooks)
+}
+
+// SaveX calls Save and panics if Save returns an error.
+func (akc *ApiKeyCreate) SaveX(ctx context.Context) *ApiKey {
+	v, err := akc.Save(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return v
+}
+
+// Exec executes the query.
+func (akc *ApiKeyCreate) Exec(ctx context.Context) error {
+	_, err := akc.Save(ctx)
+	return err
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (akc *ApiKeyCreate) ExecX(ctx context.Context) {
+	if err := akc.Exec(ctx); err != nil {
+		panic(err)
+	}
+}
+
+// defaults sets the default values of the builder before save.
+func (akc *ApiKeyCreate) defaults() error {
+	if _, ok := akc.mutation.CreatedAt(); !ok {
+		if apikey.DefaultCreatedAt == nil {
+			return fmt.Errorf("ent: uninitialized apikey.DefaultCreatedAt (forgotten import ent/runtime?)")
+		}
+		v := apikey.DefaultCreatedAt()
+		akc.mutation.SetCreatedAt(v)
+	}
+	if _, ok := akc.mutation.UpdatedAt(); !ok {
+		if apikey.DefaultUpdatedAt == nil {
+			return fmt.Errorf("ent: uninitialized apikey.DefaultUpdatedAt (forgotten import ent/runtime?)")
+		}
+		v := apikey.DefaultUpdatedAt()
+		akc.mutation.SetUpdatedAt(v)
+	}
+	if _, ok := akc.mutation.Title(); !ok {
+		v := apikey.DefaultTitle
+		akc.mutation.SetTitle(v)
+	}
+	if _, ok := akc.mutation.Key(); !ok {
+		v := apikey.DefaultKey
+		akc.mutation.SetKey(v)
+	}
+	if _, ok := akc.mutation.OrganizationID(); !ok {
+		v := apikey.DefaultOrganizationID
+		akc.mutation.SetOrganizationID(v)
+	}
+	if _, ok := akc.mutation.AgentID(); !ok {
+		v := apikey.DefaultAgentID
+		akc.mutation.SetAgentID(v)
+	}
+	if _, ok := akc.mutation.CustomAgentBase(); !ok {
+		v := apikey.DefaultCustomAgentBase
+		akc.mutation.SetCustomAgentBase(v)
+	}
+	if _, ok := akc.mutation.CustomAgentKey(); !ok {
+		v := apikey.DefaultCustomAgentKey
+		akc.mutation.SetCustomAgentKey(v)
+	}
+	if _, ok := akc.mutation.OpenaiBase(); !ok {
+		v := apikey.DefaultOpenaiBase
+		akc.mutation.SetOpenaiBase(v)
+	}
+	if _, ok := akc.mutation.OpenaiKey(); !ok {
+		v := apikey.DefaultOpenaiKey
+		akc.mutation.SetOpenaiKey(v)
+	}
+	return nil
+}
+
+// check runs all checks and user-defined validators on the builder.
+func (akc *ApiKeyCreate) check() error {
+	if _, ok := akc.mutation.CreatedAt(); !ok {
+		return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "ApiKey.created_at"`)}
+	}
+	if _, ok := akc.mutation.UpdatedAt(); !ok {
+		return &ValidationError{Name: "updated_at", err: errors.New(`ent: missing required field "ApiKey.updated_at"`)}
+	}
+	if _, ok := akc.mutation.OrganizationID(); !ok {
+		return &ValidationError{Name: "organization_id", err: errors.New(`ent: missing required field "ApiKey.organization_id"`)}
+	}
+	if _, ok := akc.mutation.AgentID(); !ok {
+		return &ValidationError{Name: "agent_id", err: errors.New(`ent: missing required field "ApiKey.agent_id"`)}
+	}
+	if _, ok := akc.mutation.AgentID(); !ok {
+		return &ValidationError{Name: "agent", err: errors.New(`ent: missing required edge "ApiKey.agent"`)}
+	}
+	return nil
+}
+
+func (akc *ApiKeyCreate) sqlSave(ctx context.Context) (*ApiKey, error) {
+	if err := akc.check(); err != nil {
+		return nil, err
+	}
+	_node, _spec := akc.createSpec()
+	if err := sqlgraph.CreateNode(ctx, akc.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)
+	}
+	akc.mutation.id = &_node.ID
+	akc.mutation.done = true
+	return _node, nil
+}
+
+func (akc *ApiKeyCreate) createSpec() (*ApiKey, *sqlgraph.CreateSpec) {
+	var (
+		_node = &ApiKey{config: akc.config}
+		_spec = sqlgraph.NewCreateSpec(apikey.Table, sqlgraph.NewFieldSpec(apikey.FieldID, field.TypeUint64))
+	)
+	_spec.OnConflict = akc.conflict
+	if id, ok := akc.mutation.ID(); ok {
+		_node.ID = id
+		_spec.ID.Value = id
+	}
+	if value, ok := akc.mutation.CreatedAt(); ok {
+		_spec.SetField(apikey.FieldCreatedAt, field.TypeTime, value)
+		_node.CreatedAt = value
+	}
+	if value, ok := akc.mutation.UpdatedAt(); ok {
+		_spec.SetField(apikey.FieldUpdatedAt, field.TypeTime, value)
+		_node.UpdatedAt = value
+	}
+	if value, ok := akc.mutation.DeletedAt(); ok {
+		_spec.SetField(apikey.FieldDeletedAt, field.TypeTime, value)
+		_node.DeletedAt = value
+	}
+	if value, ok := akc.mutation.Title(); ok {
+		_spec.SetField(apikey.FieldTitle, field.TypeString, value)
+		_node.Title = value
+	}
+	if value, ok := akc.mutation.Key(); ok {
+		_spec.SetField(apikey.FieldKey, field.TypeString, value)
+		_node.Key = value
+	}
+	if value, ok := akc.mutation.OrganizationID(); ok {
+		_spec.SetField(apikey.FieldOrganizationID, field.TypeUint64, value)
+		_node.OrganizationID = value
+	}
+	if value, ok := akc.mutation.CustomAgentBase(); ok {
+		_spec.SetField(apikey.FieldCustomAgentBase, field.TypeString, value)
+		_node.CustomAgentBase = value
+	}
+	if value, ok := akc.mutation.CustomAgentKey(); ok {
+		_spec.SetField(apikey.FieldCustomAgentKey, field.TypeString, value)
+		_node.CustomAgentKey = value
+	}
+	if value, ok := akc.mutation.OpenaiBase(); ok {
+		_spec.SetField(apikey.FieldOpenaiBase, field.TypeString, value)
+		_node.OpenaiBase = value
+	}
+	if value, ok := akc.mutation.OpenaiKey(); ok {
+		_spec.SetField(apikey.FieldOpenaiKey, field.TypeString, value)
+		_node.OpenaiKey = value
+	}
+	if nodes := akc.mutation.AgentIDs(); len(nodes) > 0 {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.M2O,
+			Inverse: true,
+			Table:   apikey.AgentTable,
+			Columns: []string{apikey.AgentColumn},
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: sqlgraph.NewFieldSpec(agent.FieldID, field.TypeUint64),
+			},
+		}
+		for _, k := range nodes {
+			edge.Target.Nodes = append(edge.Target.Nodes, k)
+		}
+		_node.AgentID = nodes[0]
+		_spec.Edges = append(_spec.Edges, edge)
+	}
+	return _node, _spec
+}
+
+// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause
+// of the `INSERT` statement. For example:
+//
+//	client.ApiKey.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.ApiKeyUpsert) {
+//			SetCreatedAt(v+v).
+//		}).
+//		Exec(ctx)
+func (akc *ApiKeyCreate) OnConflict(opts ...sql.ConflictOption) *ApiKeyUpsertOne {
+	akc.conflict = opts
+	return &ApiKeyUpsertOne{
+		create: akc,
+	}
+}
+
+// OnConflictColumns calls `OnConflict` and configures the columns
+// as conflict target. Using this option is equivalent to using:
+//
+//	client.ApiKey.Create().
+//		OnConflict(sql.ConflictColumns(columns...)).
+//		Exec(ctx)
+func (akc *ApiKeyCreate) OnConflictColumns(columns ...string) *ApiKeyUpsertOne {
+	akc.conflict = append(akc.conflict, sql.ConflictColumns(columns...))
+	return &ApiKeyUpsertOne{
+		create: akc,
+	}
+}
+
+type (
+	// ApiKeyUpsertOne is the builder for "upsert"-ing
+	//  one ApiKey node.
+	ApiKeyUpsertOne struct {
+		create *ApiKeyCreate
+	}
+
+	// ApiKeyUpsert is the "OnConflict" setter.
+	ApiKeyUpsert struct {
+		*sql.UpdateSet
+	}
+)
+
+// SetUpdatedAt sets the "updated_at" field.
+func (u *ApiKeyUpsert) SetUpdatedAt(v time.Time) *ApiKeyUpsert {
+	u.Set(apikey.FieldUpdatedAt, v)
+	return u
+}
+
+// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create.
+func (u *ApiKeyUpsert) UpdateUpdatedAt() *ApiKeyUpsert {
+	u.SetExcluded(apikey.FieldUpdatedAt)
+	return u
+}
+
+// SetDeletedAt sets the "deleted_at" field.
+func (u *ApiKeyUpsert) SetDeletedAt(v time.Time) *ApiKeyUpsert {
+	u.Set(apikey.FieldDeletedAt, v)
+	return u
+}
+
+// UpdateDeletedAt sets the "deleted_at" field to the value that was provided on create.
+func (u *ApiKeyUpsert) UpdateDeletedAt() *ApiKeyUpsert {
+	u.SetExcluded(apikey.FieldDeletedAt)
+	return u
+}
+
+// ClearDeletedAt clears the value of the "deleted_at" field.
+func (u *ApiKeyUpsert) ClearDeletedAt() *ApiKeyUpsert {
+	u.SetNull(apikey.FieldDeletedAt)
+	return u
+}
+
+// SetTitle sets the "title" field.
+func (u *ApiKeyUpsert) SetTitle(v string) *ApiKeyUpsert {
+	u.Set(apikey.FieldTitle, v)
+	return u
+}
+
+// UpdateTitle sets the "title" field to the value that was provided on create.
+func (u *ApiKeyUpsert) UpdateTitle() *ApiKeyUpsert {
+	u.SetExcluded(apikey.FieldTitle)
+	return u
+}
+
+// ClearTitle clears the value of the "title" field.
+func (u *ApiKeyUpsert) ClearTitle() *ApiKeyUpsert {
+	u.SetNull(apikey.FieldTitle)
+	return u
+}
+
+// SetKey sets the "key" field.
+func (u *ApiKeyUpsert) SetKey(v string) *ApiKeyUpsert {
+	u.Set(apikey.FieldKey, v)
+	return u
+}
+
+// UpdateKey sets the "key" field to the value that was provided on create.
+func (u *ApiKeyUpsert) UpdateKey() *ApiKeyUpsert {
+	u.SetExcluded(apikey.FieldKey)
+	return u
+}
+
+// ClearKey clears the value of the "key" field.
+func (u *ApiKeyUpsert) ClearKey() *ApiKeyUpsert {
+	u.SetNull(apikey.FieldKey)
+	return u
+}
+
+// SetOrganizationID sets the "organization_id" field.
+func (u *ApiKeyUpsert) SetOrganizationID(v uint64) *ApiKeyUpsert {
+	u.Set(apikey.FieldOrganizationID, v)
+	return u
+}
+
+// UpdateOrganizationID sets the "organization_id" field to the value that was provided on create.
+func (u *ApiKeyUpsert) UpdateOrganizationID() *ApiKeyUpsert {
+	u.SetExcluded(apikey.FieldOrganizationID)
+	return u
+}
+
+// AddOrganizationID adds v to the "organization_id" field.
+func (u *ApiKeyUpsert) AddOrganizationID(v uint64) *ApiKeyUpsert {
+	u.Add(apikey.FieldOrganizationID, v)
+	return u
+}
+
+// SetAgentID sets the "agent_id" field.
+func (u *ApiKeyUpsert) SetAgentID(v uint64) *ApiKeyUpsert {
+	u.Set(apikey.FieldAgentID, v)
+	return u
+}
+
+// UpdateAgentID sets the "agent_id" field to the value that was provided on create.
+func (u *ApiKeyUpsert) UpdateAgentID() *ApiKeyUpsert {
+	u.SetExcluded(apikey.FieldAgentID)
+	return u
+}
+
+// SetCustomAgentBase sets the "custom_agent_base" field.
+func (u *ApiKeyUpsert) SetCustomAgentBase(v string) *ApiKeyUpsert {
+	u.Set(apikey.FieldCustomAgentBase, v)
+	return u
+}
+
+// UpdateCustomAgentBase sets the "custom_agent_base" field to the value that was provided on create.
+func (u *ApiKeyUpsert) UpdateCustomAgentBase() *ApiKeyUpsert {
+	u.SetExcluded(apikey.FieldCustomAgentBase)
+	return u
+}
+
+// ClearCustomAgentBase clears the value of the "custom_agent_base" field.
+func (u *ApiKeyUpsert) ClearCustomAgentBase() *ApiKeyUpsert {
+	u.SetNull(apikey.FieldCustomAgentBase)
+	return u
+}
+
+// SetCustomAgentKey sets the "custom_agent_key" field.
+func (u *ApiKeyUpsert) SetCustomAgentKey(v string) *ApiKeyUpsert {
+	u.Set(apikey.FieldCustomAgentKey, v)
+	return u
+}
+
+// UpdateCustomAgentKey sets the "custom_agent_key" field to the value that was provided on create.
+func (u *ApiKeyUpsert) UpdateCustomAgentKey() *ApiKeyUpsert {
+	u.SetExcluded(apikey.FieldCustomAgentKey)
+	return u
+}
+
+// ClearCustomAgentKey clears the value of the "custom_agent_key" field.
+func (u *ApiKeyUpsert) ClearCustomAgentKey() *ApiKeyUpsert {
+	u.SetNull(apikey.FieldCustomAgentKey)
+	return u
+}
+
+// SetOpenaiBase sets the "openai_base" field.
+func (u *ApiKeyUpsert) SetOpenaiBase(v string) *ApiKeyUpsert {
+	u.Set(apikey.FieldOpenaiBase, v)
+	return u
+}
+
+// UpdateOpenaiBase sets the "openai_base" field to the value that was provided on create.
+func (u *ApiKeyUpsert) UpdateOpenaiBase() *ApiKeyUpsert {
+	u.SetExcluded(apikey.FieldOpenaiBase)
+	return u
+}
+
+// ClearOpenaiBase clears the value of the "openai_base" field.
+func (u *ApiKeyUpsert) ClearOpenaiBase() *ApiKeyUpsert {
+	u.SetNull(apikey.FieldOpenaiBase)
+	return u
+}
+
+// SetOpenaiKey sets the "openai_key" field.
+func (u *ApiKeyUpsert) SetOpenaiKey(v string) *ApiKeyUpsert {
+	u.Set(apikey.FieldOpenaiKey, v)
+	return u
+}
+
+// UpdateOpenaiKey sets the "openai_key" field to the value that was provided on create.
+func (u *ApiKeyUpsert) UpdateOpenaiKey() *ApiKeyUpsert {
+	u.SetExcluded(apikey.FieldOpenaiKey)
+	return u
+}
+
+// ClearOpenaiKey clears the value of the "openai_key" field.
+func (u *ApiKeyUpsert) ClearOpenaiKey() *ApiKeyUpsert {
+	u.SetNull(apikey.FieldOpenaiKey)
+	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.ApiKey.Create().
+//		OnConflict(
+//			sql.ResolveWithNewValues(),
+//			sql.ResolveWith(func(u *sql.UpdateSet) {
+//				u.SetIgnore(apikey.FieldID)
+//			}),
+//		).
+//		Exec(ctx)
+func (u *ApiKeyUpsertOne) UpdateNewValues() *ApiKeyUpsertOne {
+	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(apikey.FieldID)
+		}
+		if _, exists := u.create.mutation.CreatedAt(); exists {
+			s.SetIgnore(apikey.FieldCreatedAt)
+		}
+	}))
+	return u
+}
+
+// Ignore sets each column to itself in case of conflict.
+// Using this option is equivalent to using:
+//
+//	client.ApiKey.Create().
+//	    OnConflict(sql.ResolveWithIgnore()).
+//	    Exec(ctx)
+func (u *ApiKeyUpsertOne) Ignore() *ApiKeyUpsertOne {
+	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 *ApiKeyUpsertOne) DoNothing() *ApiKeyUpsertOne {
+	u.create.conflict = append(u.create.conflict, sql.DoNothing())
+	return u
+}
+
+// Update allows overriding fields `UPDATE` values. See the ApiKeyCreate.OnConflict
+// documentation for more info.
+func (u *ApiKeyUpsertOne) Update(set func(*ApiKeyUpsert)) *ApiKeyUpsertOne {
+	u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) {
+		set(&ApiKeyUpsert{UpdateSet: update})
+	}))
+	return u
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (u *ApiKeyUpsertOne) SetUpdatedAt(v time.Time) *ApiKeyUpsertOne {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.SetUpdatedAt(v)
+	})
+}
+
+// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create.
+func (u *ApiKeyUpsertOne) UpdateUpdatedAt() *ApiKeyUpsertOne {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.UpdateUpdatedAt()
+	})
+}
+
+// SetDeletedAt sets the "deleted_at" field.
+func (u *ApiKeyUpsertOne) SetDeletedAt(v time.Time) *ApiKeyUpsertOne {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.SetDeletedAt(v)
+	})
+}
+
+// UpdateDeletedAt sets the "deleted_at" field to the value that was provided on create.
+func (u *ApiKeyUpsertOne) UpdateDeletedAt() *ApiKeyUpsertOne {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.UpdateDeletedAt()
+	})
+}
+
+// ClearDeletedAt clears the value of the "deleted_at" field.
+func (u *ApiKeyUpsertOne) ClearDeletedAt() *ApiKeyUpsertOne {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.ClearDeletedAt()
+	})
+}
+
+// SetTitle sets the "title" field.
+func (u *ApiKeyUpsertOne) SetTitle(v string) *ApiKeyUpsertOne {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.SetTitle(v)
+	})
+}
+
+// UpdateTitle sets the "title" field to the value that was provided on create.
+func (u *ApiKeyUpsertOne) UpdateTitle() *ApiKeyUpsertOne {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.UpdateTitle()
+	})
+}
+
+// ClearTitle clears the value of the "title" field.
+func (u *ApiKeyUpsertOne) ClearTitle() *ApiKeyUpsertOne {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.ClearTitle()
+	})
+}
+
+// SetKey sets the "key" field.
+func (u *ApiKeyUpsertOne) SetKey(v string) *ApiKeyUpsertOne {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.SetKey(v)
+	})
+}
+
+// UpdateKey sets the "key" field to the value that was provided on create.
+func (u *ApiKeyUpsertOne) UpdateKey() *ApiKeyUpsertOne {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.UpdateKey()
+	})
+}
+
+// ClearKey clears the value of the "key" field.
+func (u *ApiKeyUpsertOne) ClearKey() *ApiKeyUpsertOne {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.ClearKey()
+	})
+}
+
+// SetOrganizationID sets the "organization_id" field.
+func (u *ApiKeyUpsertOne) SetOrganizationID(v uint64) *ApiKeyUpsertOne {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.SetOrganizationID(v)
+	})
+}
+
+// AddOrganizationID adds v to the "organization_id" field.
+func (u *ApiKeyUpsertOne) AddOrganizationID(v uint64) *ApiKeyUpsertOne {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.AddOrganizationID(v)
+	})
+}
+
+// UpdateOrganizationID sets the "organization_id" field to the value that was provided on create.
+func (u *ApiKeyUpsertOne) UpdateOrganizationID() *ApiKeyUpsertOne {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.UpdateOrganizationID()
+	})
+}
+
+// SetAgentID sets the "agent_id" field.
+func (u *ApiKeyUpsertOne) SetAgentID(v uint64) *ApiKeyUpsertOne {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.SetAgentID(v)
+	})
+}
+
+// UpdateAgentID sets the "agent_id" field to the value that was provided on create.
+func (u *ApiKeyUpsertOne) UpdateAgentID() *ApiKeyUpsertOne {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.UpdateAgentID()
+	})
+}
+
+// SetCustomAgentBase sets the "custom_agent_base" field.
+func (u *ApiKeyUpsertOne) SetCustomAgentBase(v string) *ApiKeyUpsertOne {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.SetCustomAgentBase(v)
+	})
+}
+
+// UpdateCustomAgentBase sets the "custom_agent_base" field to the value that was provided on create.
+func (u *ApiKeyUpsertOne) UpdateCustomAgentBase() *ApiKeyUpsertOne {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.UpdateCustomAgentBase()
+	})
+}
+
+// ClearCustomAgentBase clears the value of the "custom_agent_base" field.
+func (u *ApiKeyUpsertOne) ClearCustomAgentBase() *ApiKeyUpsertOne {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.ClearCustomAgentBase()
+	})
+}
+
+// SetCustomAgentKey sets the "custom_agent_key" field.
+func (u *ApiKeyUpsertOne) SetCustomAgentKey(v string) *ApiKeyUpsertOne {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.SetCustomAgentKey(v)
+	})
+}
+
+// UpdateCustomAgentKey sets the "custom_agent_key" field to the value that was provided on create.
+func (u *ApiKeyUpsertOne) UpdateCustomAgentKey() *ApiKeyUpsertOne {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.UpdateCustomAgentKey()
+	})
+}
+
+// ClearCustomAgentKey clears the value of the "custom_agent_key" field.
+func (u *ApiKeyUpsertOne) ClearCustomAgentKey() *ApiKeyUpsertOne {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.ClearCustomAgentKey()
+	})
+}
+
+// SetOpenaiBase sets the "openai_base" field.
+func (u *ApiKeyUpsertOne) SetOpenaiBase(v string) *ApiKeyUpsertOne {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.SetOpenaiBase(v)
+	})
+}
+
+// UpdateOpenaiBase sets the "openai_base" field to the value that was provided on create.
+func (u *ApiKeyUpsertOne) UpdateOpenaiBase() *ApiKeyUpsertOne {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.UpdateOpenaiBase()
+	})
+}
+
+// ClearOpenaiBase clears the value of the "openai_base" field.
+func (u *ApiKeyUpsertOne) ClearOpenaiBase() *ApiKeyUpsertOne {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.ClearOpenaiBase()
+	})
+}
+
+// SetOpenaiKey sets the "openai_key" field.
+func (u *ApiKeyUpsertOne) SetOpenaiKey(v string) *ApiKeyUpsertOne {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.SetOpenaiKey(v)
+	})
+}
+
+// UpdateOpenaiKey sets the "openai_key" field to the value that was provided on create.
+func (u *ApiKeyUpsertOne) UpdateOpenaiKey() *ApiKeyUpsertOne {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.UpdateOpenaiKey()
+	})
+}
+
+// ClearOpenaiKey clears the value of the "openai_key" field.
+func (u *ApiKeyUpsertOne) ClearOpenaiKey() *ApiKeyUpsertOne {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.ClearOpenaiKey()
+	})
+}
+
+// Exec executes the query.
+func (u *ApiKeyUpsertOne) Exec(ctx context.Context) error {
+	if len(u.create.conflict) == 0 {
+		return errors.New("ent: missing options for ApiKeyCreate.OnConflict")
+	}
+	return u.create.Exec(ctx)
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (u *ApiKeyUpsertOne) 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 *ApiKeyUpsertOne) 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 *ApiKeyUpsertOne) IDX(ctx context.Context) uint64 {
+	id, err := u.ID(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return id
+}
+
+// ApiKeyCreateBulk is the builder for creating many ApiKey entities in bulk.
+type ApiKeyCreateBulk struct {
+	config
+	err      error
+	builders []*ApiKeyCreate
+	conflict []sql.ConflictOption
+}
+
+// Save creates the ApiKey entities in the database.
+func (akcb *ApiKeyCreateBulk) Save(ctx context.Context) ([]*ApiKey, error) {
+	if akcb.err != nil {
+		return nil, akcb.err
+	}
+	specs := make([]*sqlgraph.CreateSpec, len(akcb.builders))
+	nodes := make([]*ApiKey, len(akcb.builders))
+	mutators := make([]Mutator, len(akcb.builders))
+	for i := range akcb.builders {
+		func(i int, root context.Context) {
+			builder := akcb.builders[i]
+			builder.defaults()
+			var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
+				mutation, ok := m.(*ApiKeyMutation)
+				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, akcb.builders[i+1].mutation)
+				} else {
+					spec := &sqlgraph.BatchCreateSpec{Nodes: specs}
+					spec.OnConflict = akcb.conflict
+					// Invoke the actual operation on the latest mutation in the chain.
+					if err = sqlgraph.BatchCreate(ctx, akcb.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, akcb.builders[0].mutation); err != nil {
+			return nil, err
+		}
+	}
+	return nodes, nil
+}
+
+// SaveX is like Save, but panics if an error occurs.
+func (akcb *ApiKeyCreateBulk) SaveX(ctx context.Context) []*ApiKey {
+	v, err := akcb.Save(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return v
+}
+
+// Exec executes the query.
+func (akcb *ApiKeyCreateBulk) Exec(ctx context.Context) error {
+	_, err := akcb.Save(ctx)
+	return err
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (akcb *ApiKeyCreateBulk) ExecX(ctx context.Context) {
+	if err := akcb.Exec(ctx); err != nil {
+		panic(err)
+	}
+}
+
+// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause
+// of the `INSERT` statement. For example:
+//
+//	client.ApiKey.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.ApiKeyUpsert) {
+//			SetCreatedAt(v+v).
+//		}).
+//		Exec(ctx)
+func (akcb *ApiKeyCreateBulk) OnConflict(opts ...sql.ConflictOption) *ApiKeyUpsertBulk {
+	akcb.conflict = opts
+	return &ApiKeyUpsertBulk{
+		create: akcb,
+	}
+}
+
+// OnConflictColumns calls `OnConflict` and configures the columns
+// as conflict target. Using this option is equivalent to using:
+//
+//	client.ApiKey.Create().
+//		OnConflict(sql.ConflictColumns(columns...)).
+//		Exec(ctx)
+func (akcb *ApiKeyCreateBulk) OnConflictColumns(columns ...string) *ApiKeyUpsertBulk {
+	akcb.conflict = append(akcb.conflict, sql.ConflictColumns(columns...))
+	return &ApiKeyUpsertBulk{
+		create: akcb,
+	}
+}
+
+// ApiKeyUpsertBulk is the builder for "upsert"-ing
+// a bulk of ApiKey nodes.
+type ApiKeyUpsertBulk struct {
+	create *ApiKeyCreateBulk
+}
+
+// UpdateNewValues updates the mutable fields using the new values that
+// were set on create. Using this option is equivalent to using:
+//
+//	client.ApiKey.Create().
+//		OnConflict(
+//			sql.ResolveWithNewValues(),
+//			sql.ResolveWith(func(u *sql.UpdateSet) {
+//				u.SetIgnore(apikey.FieldID)
+//			}),
+//		).
+//		Exec(ctx)
+func (u *ApiKeyUpsertBulk) UpdateNewValues() *ApiKeyUpsertBulk {
+	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(apikey.FieldID)
+			}
+			if _, exists := b.mutation.CreatedAt(); exists {
+				s.SetIgnore(apikey.FieldCreatedAt)
+			}
+		}
+	}))
+	return u
+}
+
+// Ignore sets each column to itself in case of conflict.
+// Using this option is equivalent to using:
+//
+//	client.ApiKey.Create().
+//		OnConflict(sql.ResolveWithIgnore()).
+//		Exec(ctx)
+func (u *ApiKeyUpsertBulk) Ignore() *ApiKeyUpsertBulk {
+	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 *ApiKeyUpsertBulk) DoNothing() *ApiKeyUpsertBulk {
+	u.create.conflict = append(u.create.conflict, sql.DoNothing())
+	return u
+}
+
+// Update allows overriding fields `UPDATE` values. See the ApiKeyCreateBulk.OnConflict
+// documentation for more info.
+func (u *ApiKeyUpsertBulk) Update(set func(*ApiKeyUpsert)) *ApiKeyUpsertBulk {
+	u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) {
+		set(&ApiKeyUpsert{UpdateSet: update})
+	}))
+	return u
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (u *ApiKeyUpsertBulk) SetUpdatedAt(v time.Time) *ApiKeyUpsertBulk {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.SetUpdatedAt(v)
+	})
+}
+
+// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create.
+func (u *ApiKeyUpsertBulk) UpdateUpdatedAt() *ApiKeyUpsertBulk {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.UpdateUpdatedAt()
+	})
+}
+
+// SetDeletedAt sets the "deleted_at" field.
+func (u *ApiKeyUpsertBulk) SetDeletedAt(v time.Time) *ApiKeyUpsertBulk {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.SetDeletedAt(v)
+	})
+}
+
+// UpdateDeletedAt sets the "deleted_at" field to the value that was provided on create.
+func (u *ApiKeyUpsertBulk) UpdateDeletedAt() *ApiKeyUpsertBulk {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.UpdateDeletedAt()
+	})
+}
+
+// ClearDeletedAt clears the value of the "deleted_at" field.
+func (u *ApiKeyUpsertBulk) ClearDeletedAt() *ApiKeyUpsertBulk {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.ClearDeletedAt()
+	})
+}
+
+// SetTitle sets the "title" field.
+func (u *ApiKeyUpsertBulk) SetTitle(v string) *ApiKeyUpsertBulk {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.SetTitle(v)
+	})
+}
+
+// UpdateTitle sets the "title" field to the value that was provided on create.
+func (u *ApiKeyUpsertBulk) UpdateTitle() *ApiKeyUpsertBulk {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.UpdateTitle()
+	})
+}
+
+// ClearTitle clears the value of the "title" field.
+func (u *ApiKeyUpsertBulk) ClearTitle() *ApiKeyUpsertBulk {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.ClearTitle()
+	})
+}
+
+// SetKey sets the "key" field.
+func (u *ApiKeyUpsertBulk) SetKey(v string) *ApiKeyUpsertBulk {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.SetKey(v)
+	})
+}
+
+// UpdateKey sets the "key" field to the value that was provided on create.
+func (u *ApiKeyUpsertBulk) UpdateKey() *ApiKeyUpsertBulk {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.UpdateKey()
+	})
+}
+
+// ClearKey clears the value of the "key" field.
+func (u *ApiKeyUpsertBulk) ClearKey() *ApiKeyUpsertBulk {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.ClearKey()
+	})
+}
+
+// SetOrganizationID sets the "organization_id" field.
+func (u *ApiKeyUpsertBulk) SetOrganizationID(v uint64) *ApiKeyUpsertBulk {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.SetOrganizationID(v)
+	})
+}
+
+// AddOrganizationID adds v to the "organization_id" field.
+func (u *ApiKeyUpsertBulk) AddOrganizationID(v uint64) *ApiKeyUpsertBulk {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.AddOrganizationID(v)
+	})
+}
+
+// UpdateOrganizationID sets the "organization_id" field to the value that was provided on create.
+func (u *ApiKeyUpsertBulk) UpdateOrganizationID() *ApiKeyUpsertBulk {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.UpdateOrganizationID()
+	})
+}
+
+// SetAgentID sets the "agent_id" field.
+func (u *ApiKeyUpsertBulk) SetAgentID(v uint64) *ApiKeyUpsertBulk {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.SetAgentID(v)
+	})
+}
+
+// UpdateAgentID sets the "agent_id" field to the value that was provided on create.
+func (u *ApiKeyUpsertBulk) UpdateAgentID() *ApiKeyUpsertBulk {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.UpdateAgentID()
+	})
+}
+
+// SetCustomAgentBase sets the "custom_agent_base" field.
+func (u *ApiKeyUpsertBulk) SetCustomAgentBase(v string) *ApiKeyUpsertBulk {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.SetCustomAgentBase(v)
+	})
+}
+
+// UpdateCustomAgentBase sets the "custom_agent_base" field to the value that was provided on create.
+func (u *ApiKeyUpsertBulk) UpdateCustomAgentBase() *ApiKeyUpsertBulk {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.UpdateCustomAgentBase()
+	})
+}
+
+// ClearCustomAgentBase clears the value of the "custom_agent_base" field.
+func (u *ApiKeyUpsertBulk) ClearCustomAgentBase() *ApiKeyUpsertBulk {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.ClearCustomAgentBase()
+	})
+}
+
+// SetCustomAgentKey sets the "custom_agent_key" field.
+func (u *ApiKeyUpsertBulk) SetCustomAgentKey(v string) *ApiKeyUpsertBulk {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.SetCustomAgentKey(v)
+	})
+}
+
+// UpdateCustomAgentKey sets the "custom_agent_key" field to the value that was provided on create.
+func (u *ApiKeyUpsertBulk) UpdateCustomAgentKey() *ApiKeyUpsertBulk {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.UpdateCustomAgentKey()
+	})
+}
+
+// ClearCustomAgentKey clears the value of the "custom_agent_key" field.
+func (u *ApiKeyUpsertBulk) ClearCustomAgentKey() *ApiKeyUpsertBulk {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.ClearCustomAgentKey()
+	})
+}
+
+// SetOpenaiBase sets the "openai_base" field.
+func (u *ApiKeyUpsertBulk) SetOpenaiBase(v string) *ApiKeyUpsertBulk {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.SetOpenaiBase(v)
+	})
+}
+
+// UpdateOpenaiBase sets the "openai_base" field to the value that was provided on create.
+func (u *ApiKeyUpsertBulk) UpdateOpenaiBase() *ApiKeyUpsertBulk {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.UpdateOpenaiBase()
+	})
+}
+
+// ClearOpenaiBase clears the value of the "openai_base" field.
+func (u *ApiKeyUpsertBulk) ClearOpenaiBase() *ApiKeyUpsertBulk {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.ClearOpenaiBase()
+	})
+}
+
+// SetOpenaiKey sets the "openai_key" field.
+func (u *ApiKeyUpsertBulk) SetOpenaiKey(v string) *ApiKeyUpsertBulk {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.SetOpenaiKey(v)
+	})
+}
+
+// UpdateOpenaiKey sets the "openai_key" field to the value that was provided on create.
+func (u *ApiKeyUpsertBulk) UpdateOpenaiKey() *ApiKeyUpsertBulk {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.UpdateOpenaiKey()
+	})
+}
+
+// ClearOpenaiKey clears the value of the "openai_key" field.
+func (u *ApiKeyUpsertBulk) ClearOpenaiKey() *ApiKeyUpsertBulk {
+	return u.Update(func(s *ApiKeyUpsert) {
+		s.ClearOpenaiKey()
+	})
+}
+
+// Exec executes the query.
+func (u *ApiKeyUpsertBulk) 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 ApiKeyCreateBulk instead", i)
+		}
+	}
+	if len(u.create.conflict) == 0 {
+		return errors.New("ent: missing options for ApiKeyCreateBulk.OnConflict")
+	}
+	return u.create.Exec(ctx)
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (u *ApiKeyUpsertBulk) ExecX(ctx context.Context) {
+	if err := u.create.Exec(ctx); err != nil {
+		panic(err)
+	}
+}

+ 88 - 0
ent/apikey_delete.go

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

+ 605 - 0
ent/apikey_query.go

@@ -0,0 +1,605 @@
+// Code generated by ent, DO NOT EDIT.
+
+package ent
+
+import (
+	"context"
+	"fmt"
+	"math"
+	"wechat-api/ent/agent"
+	"wechat-api/ent/apikey"
+	"wechat-api/ent/predicate"
+
+	"entgo.io/ent/dialect/sql"
+	"entgo.io/ent/dialect/sql/sqlgraph"
+	"entgo.io/ent/schema/field"
+)
+
+// ApiKeyQuery is the builder for querying ApiKey entities.
+type ApiKeyQuery struct {
+	config
+	ctx        *QueryContext
+	order      []apikey.OrderOption
+	inters     []Interceptor
+	predicates []predicate.ApiKey
+	withAgent  *AgentQuery
+	// intermediate query (i.e. traversal path).
+	sql  *sql.Selector
+	path func(context.Context) (*sql.Selector, error)
+}
+
+// Where adds a new predicate for the ApiKeyQuery builder.
+func (akq *ApiKeyQuery) Where(ps ...predicate.ApiKey) *ApiKeyQuery {
+	akq.predicates = append(akq.predicates, ps...)
+	return akq
+}
+
+// Limit the number of records to be returned by this query.
+func (akq *ApiKeyQuery) Limit(limit int) *ApiKeyQuery {
+	akq.ctx.Limit = &limit
+	return akq
+}
+
+// Offset to start from.
+func (akq *ApiKeyQuery) Offset(offset int) *ApiKeyQuery {
+	akq.ctx.Offset = &offset
+	return akq
+}
+
+// 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 (akq *ApiKeyQuery) Unique(unique bool) *ApiKeyQuery {
+	akq.ctx.Unique = &unique
+	return akq
+}
+
+// Order specifies how the records should be ordered.
+func (akq *ApiKeyQuery) Order(o ...apikey.OrderOption) *ApiKeyQuery {
+	akq.order = append(akq.order, o...)
+	return akq
+}
+
+// QueryAgent chains the current query on the "agent" edge.
+func (akq *ApiKeyQuery) QueryAgent() *AgentQuery {
+	query := (&AgentClient{config: akq.config}).Query()
+	query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
+		if err := akq.prepareQuery(ctx); err != nil {
+			return nil, err
+		}
+		selector := akq.sqlQuery(ctx)
+		if err := selector.Err(); err != nil {
+			return nil, err
+		}
+		step := sqlgraph.NewStep(
+			sqlgraph.From(apikey.Table, apikey.FieldID, selector),
+			sqlgraph.To(agent.Table, agent.FieldID),
+			sqlgraph.Edge(sqlgraph.M2O, true, apikey.AgentTable, apikey.AgentColumn),
+		)
+		fromU = sqlgraph.SetNeighbors(akq.driver.Dialect(), step)
+		return fromU, nil
+	}
+	return query
+}
+
+// First returns the first ApiKey entity from the query.
+// Returns a *NotFoundError when no ApiKey was found.
+func (akq *ApiKeyQuery) First(ctx context.Context) (*ApiKey, error) {
+	nodes, err := akq.Limit(1).All(setContextOp(ctx, akq.ctx, "First"))
+	if err != nil {
+		return nil, err
+	}
+	if len(nodes) == 0 {
+		return nil, &NotFoundError{apikey.Label}
+	}
+	return nodes[0], nil
+}
+
+// FirstX is like First, but panics if an error occurs.
+func (akq *ApiKeyQuery) FirstX(ctx context.Context) *ApiKey {
+	node, err := akq.First(ctx)
+	if err != nil && !IsNotFound(err) {
+		panic(err)
+	}
+	return node
+}
+
+// FirstID returns the first ApiKey ID from the query.
+// Returns a *NotFoundError when no ApiKey ID was found.
+func (akq *ApiKeyQuery) FirstID(ctx context.Context) (id uint64, err error) {
+	var ids []uint64
+	if ids, err = akq.Limit(1).IDs(setContextOp(ctx, akq.ctx, "FirstID")); err != nil {
+		return
+	}
+	if len(ids) == 0 {
+		err = &NotFoundError{apikey.Label}
+		return
+	}
+	return ids[0], nil
+}
+
+// FirstIDX is like FirstID, but panics if an error occurs.
+func (akq *ApiKeyQuery) FirstIDX(ctx context.Context) uint64 {
+	id, err := akq.FirstID(ctx)
+	if err != nil && !IsNotFound(err) {
+		panic(err)
+	}
+	return id
+}
+
+// Only returns a single ApiKey entity found by the query, ensuring it only returns one.
+// Returns a *NotSingularError when more than one ApiKey entity is found.
+// Returns a *NotFoundError when no ApiKey entities are found.
+func (akq *ApiKeyQuery) Only(ctx context.Context) (*ApiKey, error) {
+	nodes, err := akq.Limit(2).All(setContextOp(ctx, akq.ctx, "Only"))
+	if err != nil {
+		return nil, err
+	}
+	switch len(nodes) {
+	case 1:
+		return nodes[0], nil
+	case 0:
+		return nil, &NotFoundError{apikey.Label}
+	default:
+		return nil, &NotSingularError{apikey.Label}
+	}
+}
+
+// OnlyX is like Only, but panics if an error occurs.
+func (akq *ApiKeyQuery) OnlyX(ctx context.Context) *ApiKey {
+	node, err := akq.Only(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return node
+}
+
+// OnlyID is like Only, but returns the only ApiKey ID in the query.
+// Returns a *NotSingularError when more than one ApiKey ID is found.
+// Returns a *NotFoundError when no entities are found.
+func (akq *ApiKeyQuery) OnlyID(ctx context.Context) (id uint64, err error) {
+	var ids []uint64
+	if ids, err = akq.Limit(2).IDs(setContextOp(ctx, akq.ctx, "OnlyID")); err != nil {
+		return
+	}
+	switch len(ids) {
+	case 1:
+		id = ids[0]
+	case 0:
+		err = &NotFoundError{apikey.Label}
+	default:
+		err = &NotSingularError{apikey.Label}
+	}
+	return
+}
+
+// OnlyIDX is like OnlyID, but panics if an error occurs.
+func (akq *ApiKeyQuery) OnlyIDX(ctx context.Context) uint64 {
+	id, err := akq.OnlyID(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return id
+}
+
+// All executes the query and returns a list of ApiKeys.
+func (akq *ApiKeyQuery) All(ctx context.Context) ([]*ApiKey, error) {
+	ctx = setContextOp(ctx, akq.ctx, "All")
+	if err := akq.prepareQuery(ctx); err != nil {
+		return nil, err
+	}
+	qr := querierAll[[]*ApiKey, *ApiKeyQuery]()
+	return withInterceptors[[]*ApiKey](ctx, akq, qr, akq.inters)
+}
+
+// AllX is like All, but panics if an error occurs.
+func (akq *ApiKeyQuery) AllX(ctx context.Context) []*ApiKey {
+	nodes, err := akq.All(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return nodes
+}
+
+// IDs executes the query and returns a list of ApiKey IDs.
+func (akq *ApiKeyQuery) IDs(ctx context.Context) (ids []uint64, err error) {
+	if akq.ctx.Unique == nil && akq.path != nil {
+		akq.Unique(true)
+	}
+	ctx = setContextOp(ctx, akq.ctx, "IDs")
+	if err = akq.Select(apikey.FieldID).Scan(ctx, &ids); err != nil {
+		return nil, err
+	}
+	return ids, nil
+}
+
+// IDsX is like IDs, but panics if an error occurs.
+func (akq *ApiKeyQuery) IDsX(ctx context.Context) []uint64 {
+	ids, err := akq.IDs(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return ids
+}
+
+// Count returns the count of the given query.
+func (akq *ApiKeyQuery) Count(ctx context.Context) (int, error) {
+	ctx = setContextOp(ctx, akq.ctx, "Count")
+	if err := akq.prepareQuery(ctx); err != nil {
+		return 0, err
+	}
+	return withInterceptors[int](ctx, akq, querierCount[*ApiKeyQuery](), akq.inters)
+}
+
+// CountX is like Count, but panics if an error occurs.
+func (akq *ApiKeyQuery) CountX(ctx context.Context) int {
+	count, err := akq.Count(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return count
+}
+
+// Exist returns true if the query has elements in the graph.
+func (akq *ApiKeyQuery) Exist(ctx context.Context) (bool, error) {
+	ctx = setContextOp(ctx, akq.ctx, "Exist")
+	switch _, err := akq.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 (akq *ApiKeyQuery) ExistX(ctx context.Context) bool {
+	exist, err := akq.Exist(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return exist
+}
+
+// Clone returns a duplicate of the ApiKeyQuery builder, including all associated steps. It can be
+// used to prepare common query builders and use them differently after the clone is made.
+func (akq *ApiKeyQuery) Clone() *ApiKeyQuery {
+	if akq == nil {
+		return nil
+	}
+	return &ApiKeyQuery{
+		config:     akq.config,
+		ctx:        akq.ctx.Clone(),
+		order:      append([]apikey.OrderOption{}, akq.order...),
+		inters:     append([]Interceptor{}, akq.inters...),
+		predicates: append([]predicate.ApiKey{}, akq.predicates...),
+		withAgent:  akq.withAgent.Clone(),
+		// clone intermediate query.
+		sql:  akq.sql.Clone(),
+		path: akq.path,
+	}
+}
+
+// WithAgent tells the query-builder to eager-load the nodes that are connected to
+// the "agent" edge. The optional arguments are used to configure the query builder of the edge.
+func (akq *ApiKeyQuery) WithAgent(opts ...func(*AgentQuery)) *ApiKeyQuery {
+	query := (&AgentClient{config: akq.config}).Query()
+	for _, opt := range opts {
+		opt(query)
+	}
+	akq.withAgent = query
+	return akq
+}
+
+// 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.ApiKey.Query().
+//		GroupBy(apikey.FieldCreatedAt).
+//		Aggregate(ent.Count()).
+//		Scan(ctx, &v)
+func (akq *ApiKeyQuery) GroupBy(field string, fields ...string) *ApiKeyGroupBy {
+	akq.ctx.Fields = append([]string{field}, fields...)
+	grbuild := &ApiKeyGroupBy{build: akq}
+	grbuild.flds = &akq.ctx.Fields
+	grbuild.label = apikey.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.ApiKey.Query().
+//		Select(apikey.FieldCreatedAt).
+//		Scan(ctx, &v)
+func (akq *ApiKeyQuery) Select(fields ...string) *ApiKeySelect {
+	akq.ctx.Fields = append(akq.ctx.Fields, fields...)
+	sbuild := &ApiKeySelect{ApiKeyQuery: akq}
+	sbuild.label = apikey.Label
+	sbuild.flds, sbuild.scan = &akq.ctx.Fields, sbuild.Scan
+	return sbuild
+}
+
+// Aggregate returns a ApiKeySelect configured with the given aggregations.
+func (akq *ApiKeyQuery) Aggregate(fns ...AggregateFunc) *ApiKeySelect {
+	return akq.Select().Aggregate(fns...)
+}
+
+func (akq *ApiKeyQuery) prepareQuery(ctx context.Context) error {
+	for _, inter := range akq.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, akq); err != nil {
+				return err
+			}
+		}
+	}
+	for _, f := range akq.ctx.Fields {
+		if !apikey.ValidColumn(f) {
+			return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
+		}
+	}
+	if akq.path != nil {
+		prev, err := akq.path(ctx)
+		if err != nil {
+			return err
+		}
+		akq.sql = prev
+	}
+	return nil
+}
+
+func (akq *ApiKeyQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*ApiKey, error) {
+	var (
+		nodes       = []*ApiKey{}
+		_spec       = akq.querySpec()
+		loadedTypes = [1]bool{
+			akq.withAgent != nil,
+		}
+	)
+	_spec.ScanValues = func(columns []string) ([]any, error) {
+		return (*ApiKey).scanValues(nil, columns)
+	}
+	_spec.Assign = func(columns []string, values []any) error {
+		node := &ApiKey{config: akq.config}
+		nodes = append(nodes, node)
+		node.Edges.loadedTypes = loadedTypes
+		return node.assignValues(columns, values)
+	}
+	for i := range hooks {
+		hooks[i](ctx, _spec)
+	}
+	if err := sqlgraph.QueryNodes(ctx, akq.driver, _spec); err != nil {
+		return nil, err
+	}
+	if len(nodes) == 0 {
+		return nodes, nil
+	}
+	if query := akq.withAgent; query != nil {
+		if err := akq.loadAgent(ctx, query, nodes, nil,
+			func(n *ApiKey, e *Agent) { n.Edges.Agent = e }); err != nil {
+			return nil, err
+		}
+	}
+	return nodes, nil
+}
+
+func (akq *ApiKeyQuery) loadAgent(ctx context.Context, query *AgentQuery, nodes []*ApiKey, init func(*ApiKey), assign func(*ApiKey, *Agent)) error {
+	ids := make([]uint64, 0, len(nodes))
+	nodeids := make(map[uint64][]*ApiKey)
+	for i := range nodes {
+		fk := nodes[i].AgentID
+		if _, ok := nodeids[fk]; !ok {
+			ids = append(ids, fk)
+		}
+		nodeids[fk] = append(nodeids[fk], nodes[i])
+	}
+	if len(ids) == 0 {
+		return nil
+	}
+	query.Where(agent.IDIn(ids...))
+	neighbors, err := query.All(ctx)
+	if err != nil {
+		return err
+	}
+	for _, n := range neighbors {
+		nodes, ok := nodeids[n.ID]
+		if !ok {
+			return fmt.Errorf(`unexpected foreign-key "agent_id" returned %v`, n.ID)
+		}
+		for i := range nodes {
+			assign(nodes[i], n)
+		}
+	}
+	return nil
+}
+
+func (akq *ApiKeyQuery) sqlCount(ctx context.Context) (int, error) {
+	_spec := akq.querySpec()
+	_spec.Node.Columns = akq.ctx.Fields
+	if len(akq.ctx.Fields) > 0 {
+		_spec.Unique = akq.ctx.Unique != nil && *akq.ctx.Unique
+	}
+	return sqlgraph.CountNodes(ctx, akq.driver, _spec)
+}
+
+func (akq *ApiKeyQuery) querySpec() *sqlgraph.QuerySpec {
+	_spec := sqlgraph.NewQuerySpec(apikey.Table, apikey.Columns, sqlgraph.NewFieldSpec(apikey.FieldID, field.TypeUint64))
+	_spec.From = akq.sql
+	if unique := akq.ctx.Unique; unique != nil {
+		_spec.Unique = *unique
+	} else if akq.path != nil {
+		_spec.Unique = true
+	}
+	if fields := akq.ctx.Fields; len(fields) > 0 {
+		_spec.Node.Columns = make([]string, 0, len(fields))
+		_spec.Node.Columns = append(_spec.Node.Columns, apikey.FieldID)
+		for i := range fields {
+			if fields[i] != apikey.FieldID {
+				_spec.Node.Columns = append(_spec.Node.Columns, fields[i])
+			}
+		}
+		if akq.withAgent != nil {
+			_spec.Node.AddColumnOnce(apikey.FieldAgentID)
+		}
+	}
+	if ps := akq.predicates; len(ps) > 0 {
+		_spec.Predicate = func(selector *sql.Selector) {
+			for i := range ps {
+				ps[i](selector)
+			}
+		}
+	}
+	if limit := akq.ctx.Limit; limit != nil {
+		_spec.Limit = *limit
+	}
+	if offset := akq.ctx.Offset; offset != nil {
+		_spec.Offset = *offset
+	}
+	if ps := akq.order; len(ps) > 0 {
+		_spec.Order = func(selector *sql.Selector) {
+			for i := range ps {
+				ps[i](selector)
+			}
+		}
+	}
+	return _spec
+}
+
+func (akq *ApiKeyQuery) sqlQuery(ctx context.Context) *sql.Selector {
+	builder := sql.Dialect(akq.driver.Dialect())
+	t1 := builder.Table(apikey.Table)
+	columns := akq.ctx.Fields
+	if len(columns) == 0 {
+		columns = apikey.Columns
+	}
+	selector := builder.Select(t1.Columns(columns...)...).From(t1)
+	if akq.sql != nil {
+		selector = akq.sql
+		selector.Select(selector.Columns(columns...)...)
+	}
+	if akq.ctx.Unique != nil && *akq.ctx.Unique {
+		selector.Distinct()
+	}
+	for _, p := range akq.predicates {
+		p(selector)
+	}
+	for _, p := range akq.order {
+		p(selector)
+	}
+	if offset := akq.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 := akq.ctx.Limit; limit != nil {
+		selector.Limit(*limit)
+	}
+	return selector
+}
+
+// ApiKeyGroupBy is the group-by builder for ApiKey entities.
+type ApiKeyGroupBy struct {
+	selector
+	build *ApiKeyQuery
+}
+
+// Aggregate adds the given aggregation functions to the group-by query.
+func (akgb *ApiKeyGroupBy) Aggregate(fns ...AggregateFunc) *ApiKeyGroupBy {
+	akgb.fns = append(akgb.fns, fns...)
+	return akgb
+}
+
+// Scan applies the selector query and scans the result into the given value.
+func (akgb *ApiKeyGroupBy) Scan(ctx context.Context, v any) error {
+	ctx = setContextOp(ctx, akgb.build.ctx, "GroupBy")
+	if err := akgb.build.prepareQuery(ctx); err != nil {
+		return err
+	}
+	return scanWithInterceptors[*ApiKeyQuery, *ApiKeyGroupBy](ctx, akgb.build, akgb, akgb.build.inters, v)
+}
+
+func (akgb *ApiKeyGroupBy) sqlScan(ctx context.Context, root *ApiKeyQuery, v any) error {
+	selector := root.sqlQuery(ctx).Select()
+	aggregation := make([]string, 0, len(akgb.fns))
+	for _, fn := range akgb.fns {
+		aggregation = append(aggregation, fn(selector))
+	}
+	if len(selector.SelectedColumns()) == 0 {
+		columns := make([]string, 0, len(*akgb.flds)+len(akgb.fns))
+		for _, f := range *akgb.flds {
+			columns = append(columns, selector.C(f))
+		}
+		columns = append(columns, aggregation...)
+		selector.Select(columns...)
+	}
+	selector.GroupBy(selector.Columns(*akgb.flds...)...)
+	if err := selector.Err(); err != nil {
+		return err
+	}
+	rows := &sql.Rows{}
+	query, args := selector.Query()
+	if err := akgb.build.driver.Query(ctx, query, args, rows); err != nil {
+		return err
+	}
+	defer rows.Close()
+	return sql.ScanSlice(rows, v)
+}
+
+// ApiKeySelect is the builder for selecting fields of ApiKey entities.
+type ApiKeySelect struct {
+	*ApiKeyQuery
+	selector
+}
+
+// Aggregate adds the given aggregation functions to the selector query.
+func (aks *ApiKeySelect) Aggregate(fns ...AggregateFunc) *ApiKeySelect {
+	aks.fns = append(aks.fns, fns...)
+	return aks
+}
+
+// Scan applies the selector query and scans the result into the given value.
+func (aks *ApiKeySelect) Scan(ctx context.Context, v any) error {
+	ctx = setContextOp(ctx, aks.ctx, "Select")
+	if err := aks.prepareQuery(ctx); err != nil {
+		return err
+	}
+	return scanWithInterceptors[*ApiKeyQuery, *ApiKeySelect](ctx, aks.ApiKeyQuery, aks, aks.inters, v)
+}
+
+func (aks *ApiKeySelect) sqlScan(ctx context.Context, root *ApiKeyQuery, v any) error {
+	selector := root.sqlQuery(ctx)
+	aggregation := make([]string, 0, len(aks.fns))
+	for _, fn := range aks.fns {
+		aggregation = append(aggregation, fn(selector))
+	}
+	switch n := len(*aks.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 := aks.driver.Query(ctx, query, args, rows); err != nil {
+		return err
+	}
+	defer rows.Close()
+	return sql.ScanSlice(rows, v)
+}

+ 773 - 0
ent/apikey_update.go

@@ -0,0 +1,773 @@
+// Code generated by ent, DO NOT EDIT.
+
+package ent
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"time"
+	"wechat-api/ent/agent"
+	"wechat-api/ent/apikey"
+	"wechat-api/ent/predicate"
+
+	"entgo.io/ent/dialect/sql"
+	"entgo.io/ent/dialect/sql/sqlgraph"
+	"entgo.io/ent/schema/field"
+)
+
+// ApiKeyUpdate is the builder for updating ApiKey entities.
+type ApiKeyUpdate struct {
+	config
+	hooks    []Hook
+	mutation *ApiKeyMutation
+}
+
+// Where appends a list predicates to the ApiKeyUpdate builder.
+func (aku *ApiKeyUpdate) Where(ps ...predicate.ApiKey) *ApiKeyUpdate {
+	aku.mutation.Where(ps...)
+	return aku
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (aku *ApiKeyUpdate) SetUpdatedAt(t time.Time) *ApiKeyUpdate {
+	aku.mutation.SetUpdatedAt(t)
+	return aku
+}
+
+// SetDeletedAt sets the "deleted_at" field.
+func (aku *ApiKeyUpdate) SetDeletedAt(t time.Time) *ApiKeyUpdate {
+	aku.mutation.SetDeletedAt(t)
+	return aku
+}
+
+// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil.
+func (aku *ApiKeyUpdate) SetNillableDeletedAt(t *time.Time) *ApiKeyUpdate {
+	if t != nil {
+		aku.SetDeletedAt(*t)
+	}
+	return aku
+}
+
+// ClearDeletedAt clears the value of the "deleted_at" field.
+func (aku *ApiKeyUpdate) ClearDeletedAt() *ApiKeyUpdate {
+	aku.mutation.ClearDeletedAt()
+	return aku
+}
+
+// SetTitle sets the "title" field.
+func (aku *ApiKeyUpdate) SetTitle(s string) *ApiKeyUpdate {
+	aku.mutation.SetTitle(s)
+	return aku
+}
+
+// SetNillableTitle sets the "title" field if the given value is not nil.
+func (aku *ApiKeyUpdate) SetNillableTitle(s *string) *ApiKeyUpdate {
+	if s != nil {
+		aku.SetTitle(*s)
+	}
+	return aku
+}
+
+// ClearTitle clears the value of the "title" field.
+func (aku *ApiKeyUpdate) ClearTitle() *ApiKeyUpdate {
+	aku.mutation.ClearTitle()
+	return aku
+}
+
+// SetKey sets the "key" field.
+func (aku *ApiKeyUpdate) SetKey(s string) *ApiKeyUpdate {
+	aku.mutation.SetKey(s)
+	return aku
+}
+
+// SetNillableKey sets the "key" field if the given value is not nil.
+func (aku *ApiKeyUpdate) SetNillableKey(s *string) *ApiKeyUpdate {
+	if s != nil {
+		aku.SetKey(*s)
+	}
+	return aku
+}
+
+// ClearKey clears the value of the "key" field.
+func (aku *ApiKeyUpdate) ClearKey() *ApiKeyUpdate {
+	aku.mutation.ClearKey()
+	return aku
+}
+
+// SetOrganizationID sets the "organization_id" field.
+func (aku *ApiKeyUpdate) SetOrganizationID(u uint64) *ApiKeyUpdate {
+	aku.mutation.ResetOrganizationID()
+	aku.mutation.SetOrganizationID(u)
+	return aku
+}
+
+// SetNillableOrganizationID sets the "organization_id" field if the given value is not nil.
+func (aku *ApiKeyUpdate) SetNillableOrganizationID(u *uint64) *ApiKeyUpdate {
+	if u != nil {
+		aku.SetOrganizationID(*u)
+	}
+	return aku
+}
+
+// AddOrganizationID adds u to the "organization_id" field.
+func (aku *ApiKeyUpdate) AddOrganizationID(u int64) *ApiKeyUpdate {
+	aku.mutation.AddOrganizationID(u)
+	return aku
+}
+
+// SetAgentID sets the "agent_id" field.
+func (aku *ApiKeyUpdate) SetAgentID(u uint64) *ApiKeyUpdate {
+	aku.mutation.SetAgentID(u)
+	return aku
+}
+
+// SetNillableAgentID sets the "agent_id" field if the given value is not nil.
+func (aku *ApiKeyUpdate) SetNillableAgentID(u *uint64) *ApiKeyUpdate {
+	if u != nil {
+		aku.SetAgentID(*u)
+	}
+	return aku
+}
+
+// SetCustomAgentBase sets the "custom_agent_base" field.
+func (aku *ApiKeyUpdate) SetCustomAgentBase(s string) *ApiKeyUpdate {
+	aku.mutation.SetCustomAgentBase(s)
+	return aku
+}
+
+// SetNillableCustomAgentBase sets the "custom_agent_base" field if the given value is not nil.
+func (aku *ApiKeyUpdate) SetNillableCustomAgentBase(s *string) *ApiKeyUpdate {
+	if s != nil {
+		aku.SetCustomAgentBase(*s)
+	}
+	return aku
+}
+
+// ClearCustomAgentBase clears the value of the "custom_agent_base" field.
+func (aku *ApiKeyUpdate) ClearCustomAgentBase() *ApiKeyUpdate {
+	aku.mutation.ClearCustomAgentBase()
+	return aku
+}
+
+// SetCustomAgentKey sets the "custom_agent_key" field.
+func (aku *ApiKeyUpdate) SetCustomAgentKey(s string) *ApiKeyUpdate {
+	aku.mutation.SetCustomAgentKey(s)
+	return aku
+}
+
+// SetNillableCustomAgentKey sets the "custom_agent_key" field if the given value is not nil.
+func (aku *ApiKeyUpdate) SetNillableCustomAgentKey(s *string) *ApiKeyUpdate {
+	if s != nil {
+		aku.SetCustomAgentKey(*s)
+	}
+	return aku
+}
+
+// ClearCustomAgentKey clears the value of the "custom_agent_key" field.
+func (aku *ApiKeyUpdate) ClearCustomAgentKey() *ApiKeyUpdate {
+	aku.mutation.ClearCustomAgentKey()
+	return aku
+}
+
+// SetOpenaiBase sets the "openai_base" field.
+func (aku *ApiKeyUpdate) SetOpenaiBase(s string) *ApiKeyUpdate {
+	aku.mutation.SetOpenaiBase(s)
+	return aku
+}
+
+// SetNillableOpenaiBase sets the "openai_base" field if the given value is not nil.
+func (aku *ApiKeyUpdate) SetNillableOpenaiBase(s *string) *ApiKeyUpdate {
+	if s != nil {
+		aku.SetOpenaiBase(*s)
+	}
+	return aku
+}
+
+// ClearOpenaiBase clears the value of the "openai_base" field.
+func (aku *ApiKeyUpdate) ClearOpenaiBase() *ApiKeyUpdate {
+	aku.mutation.ClearOpenaiBase()
+	return aku
+}
+
+// SetOpenaiKey sets the "openai_key" field.
+func (aku *ApiKeyUpdate) SetOpenaiKey(s string) *ApiKeyUpdate {
+	aku.mutation.SetOpenaiKey(s)
+	return aku
+}
+
+// SetNillableOpenaiKey sets the "openai_key" field if the given value is not nil.
+func (aku *ApiKeyUpdate) SetNillableOpenaiKey(s *string) *ApiKeyUpdate {
+	if s != nil {
+		aku.SetOpenaiKey(*s)
+	}
+	return aku
+}
+
+// ClearOpenaiKey clears the value of the "openai_key" field.
+func (aku *ApiKeyUpdate) ClearOpenaiKey() *ApiKeyUpdate {
+	aku.mutation.ClearOpenaiKey()
+	return aku
+}
+
+// SetAgent sets the "agent" edge to the Agent entity.
+func (aku *ApiKeyUpdate) SetAgent(a *Agent) *ApiKeyUpdate {
+	return aku.SetAgentID(a.ID)
+}
+
+// Mutation returns the ApiKeyMutation object of the builder.
+func (aku *ApiKeyUpdate) Mutation() *ApiKeyMutation {
+	return aku.mutation
+}
+
+// ClearAgent clears the "agent" edge to the Agent entity.
+func (aku *ApiKeyUpdate) ClearAgent() *ApiKeyUpdate {
+	aku.mutation.ClearAgent()
+	return aku
+}
+
+// Save executes the query and returns the number of nodes affected by the update operation.
+func (aku *ApiKeyUpdate) Save(ctx context.Context) (int, error) {
+	if err := aku.defaults(); err != nil {
+		return 0, err
+	}
+	return withHooks(ctx, aku.sqlSave, aku.mutation, aku.hooks)
+}
+
+// SaveX is like Save, but panics if an error occurs.
+func (aku *ApiKeyUpdate) SaveX(ctx context.Context) int {
+	affected, err := aku.Save(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return affected
+}
+
+// Exec executes the query.
+func (aku *ApiKeyUpdate) Exec(ctx context.Context) error {
+	_, err := aku.Save(ctx)
+	return err
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (aku *ApiKeyUpdate) ExecX(ctx context.Context) {
+	if err := aku.Exec(ctx); err != nil {
+		panic(err)
+	}
+}
+
+// defaults sets the default values of the builder before save.
+func (aku *ApiKeyUpdate) defaults() error {
+	if _, ok := aku.mutation.UpdatedAt(); !ok {
+		if apikey.UpdateDefaultUpdatedAt == nil {
+			return fmt.Errorf("ent: uninitialized apikey.UpdateDefaultUpdatedAt (forgotten import ent/runtime?)")
+		}
+		v := apikey.UpdateDefaultUpdatedAt()
+		aku.mutation.SetUpdatedAt(v)
+	}
+	return nil
+}
+
+// check runs all checks and user-defined validators on the builder.
+func (aku *ApiKeyUpdate) check() error {
+	if _, ok := aku.mutation.AgentID(); aku.mutation.AgentCleared() && !ok {
+		return errors.New(`ent: clearing a required unique edge "ApiKey.agent"`)
+	}
+	return nil
+}
+
+func (aku *ApiKeyUpdate) sqlSave(ctx context.Context) (n int, err error) {
+	if err := aku.check(); err != nil {
+		return n, err
+	}
+	_spec := sqlgraph.NewUpdateSpec(apikey.Table, apikey.Columns, sqlgraph.NewFieldSpec(apikey.FieldID, field.TypeUint64))
+	if ps := aku.mutation.predicates; len(ps) > 0 {
+		_spec.Predicate = func(selector *sql.Selector) {
+			for i := range ps {
+				ps[i](selector)
+			}
+		}
+	}
+	if value, ok := aku.mutation.UpdatedAt(); ok {
+		_spec.SetField(apikey.FieldUpdatedAt, field.TypeTime, value)
+	}
+	if value, ok := aku.mutation.DeletedAt(); ok {
+		_spec.SetField(apikey.FieldDeletedAt, field.TypeTime, value)
+	}
+	if aku.mutation.DeletedAtCleared() {
+		_spec.ClearField(apikey.FieldDeletedAt, field.TypeTime)
+	}
+	if value, ok := aku.mutation.Title(); ok {
+		_spec.SetField(apikey.FieldTitle, field.TypeString, value)
+	}
+	if aku.mutation.TitleCleared() {
+		_spec.ClearField(apikey.FieldTitle, field.TypeString)
+	}
+	if value, ok := aku.mutation.Key(); ok {
+		_spec.SetField(apikey.FieldKey, field.TypeString, value)
+	}
+	if aku.mutation.KeyCleared() {
+		_spec.ClearField(apikey.FieldKey, field.TypeString)
+	}
+	if value, ok := aku.mutation.OrganizationID(); ok {
+		_spec.SetField(apikey.FieldOrganizationID, field.TypeUint64, value)
+	}
+	if value, ok := aku.mutation.AddedOrganizationID(); ok {
+		_spec.AddField(apikey.FieldOrganizationID, field.TypeUint64, value)
+	}
+	if value, ok := aku.mutation.CustomAgentBase(); ok {
+		_spec.SetField(apikey.FieldCustomAgentBase, field.TypeString, value)
+	}
+	if aku.mutation.CustomAgentBaseCleared() {
+		_spec.ClearField(apikey.FieldCustomAgentBase, field.TypeString)
+	}
+	if value, ok := aku.mutation.CustomAgentKey(); ok {
+		_spec.SetField(apikey.FieldCustomAgentKey, field.TypeString, value)
+	}
+	if aku.mutation.CustomAgentKeyCleared() {
+		_spec.ClearField(apikey.FieldCustomAgentKey, field.TypeString)
+	}
+	if value, ok := aku.mutation.OpenaiBase(); ok {
+		_spec.SetField(apikey.FieldOpenaiBase, field.TypeString, value)
+	}
+	if aku.mutation.OpenaiBaseCleared() {
+		_spec.ClearField(apikey.FieldOpenaiBase, field.TypeString)
+	}
+	if value, ok := aku.mutation.OpenaiKey(); ok {
+		_spec.SetField(apikey.FieldOpenaiKey, field.TypeString, value)
+	}
+	if aku.mutation.OpenaiKeyCleared() {
+		_spec.ClearField(apikey.FieldOpenaiKey, field.TypeString)
+	}
+	if aku.mutation.AgentCleared() {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.M2O,
+			Inverse: true,
+			Table:   apikey.AgentTable,
+			Columns: []string{apikey.AgentColumn},
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: sqlgraph.NewFieldSpec(agent.FieldID, field.TypeUint64),
+			},
+		}
+		_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
+	}
+	if nodes := aku.mutation.AgentIDs(); len(nodes) > 0 {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.M2O,
+			Inverse: true,
+			Table:   apikey.AgentTable,
+			Columns: []string{apikey.AgentColumn},
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: sqlgraph.NewFieldSpec(agent.FieldID, field.TypeUint64),
+			},
+		}
+		for _, k := range nodes {
+			edge.Target.Nodes = append(edge.Target.Nodes, k)
+		}
+		_spec.Edges.Add = append(_spec.Edges.Add, edge)
+	}
+	if n, err = sqlgraph.UpdateNodes(ctx, aku.driver, _spec); err != nil {
+		if _, ok := err.(*sqlgraph.NotFoundError); ok {
+			err = &NotFoundError{apikey.Label}
+		} else if sqlgraph.IsConstraintError(err) {
+			err = &ConstraintError{msg: err.Error(), wrap: err}
+		}
+		return 0, err
+	}
+	aku.mutation.done = true
+	return n, nil
+}
+
+// ApiKeyUpdateOne is the builder for updating a single ApiKey entity.
+type ApiKeyUpdateOne struct {
+	config
+	fields   []string
+	hooks    []Hook
+	mutation *ApiKeyMutation
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (akuo *ApiKeyUpdateOne) SetUpdatedAt(t time.Time) *ApiKeyUpdateOne {
+	akuo.mutation.SetUpdatedAt(t)
+	return akuo
+}
+
+// SetDeletedAt sets the "deleted_at" field.
+func (akuo *ApiKeyUpdateOne) SetDeletedAt(t time.Time) *ApiKeyUpdateOne {
+	akuo.mutation.SetDeletedAt(t)
+	return akuo
+}
+
+// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil.
+func (akuo *ApiKeyUpdateOne) SetNillableDeletedAt(t *time.Time) *ApiKeyUpdateOne {
+	if t != nil {
+		akuo.SetDeletedAt(*t)
+	}
+	return akuo
+}
+
+// ClearDeletedAt clears the value of the "deleted_at" field.
+func (akuo *ApiKeyUpdateOne) ClearDeletedAt() *ApiKeyUpdateOne {
+	akuo.mutation.ClearDeletedAt()
+	return akuo
+}
+
+// SetTitle sets the "title" field.
+func (akuo *ApiKeyUpdateOne) SetTitle(s string) *ApiKeyUpdateOne {
+	akuo.mutation.SetTitle(s)
+	return akuo
+}
+
+// SetNillableTitle sets the "title" field if the given value is not nil.
+func (akuo *ApiKeyUpdateOne) SetNillableTitle(s *string) *ApiKeyUpdateOne {
+	if s != nil {
+		akuo.SetTitle(*s)
+	}
+	return akuo
+}
+
+// ClearTitle clears the value of the "title" field.
+func (akuo *ApiKeyUpdateOne) ClearTitle() *ApiKeyUpdateOne {
+	akuo.mutation.ClearTitle()
+	return akuo
+}
+
+// SetKey sets the "key" field.
+func (akuo *ApiKeyUpdateOne) SetKey(s string) *ApiKeyUpdateOne {
+	akuo.mutation.SetKey(s)
+	return akuo
+}
+
+// SetNillableKey sets the "key" field if the given value is not nil.
+func (akuo *ApiKeyUpdateOne) SetNillableKey(s *string) *ApiKeyUpdateOne {
+	if s != nil {
+		akuo.SetKey(*s)
+	}
+	return akuo
+}
+
+// ClearKey clears the value of the "key" field.
+func (akuo *ApiKeyUpdateOne) ClearKey() *ApiKeyUpdateOne {
+	akuo.mutation.ClearKey()
+	return akuo
+}
+
+// SetOrganizationID sets the "organization_id" field.
+func (akuo *ApiKeyUpdateOne) SetOrganizationID(u uint64) *ApiKeyUpdateOne {
+	akuo.mutation.ResetOrganizationID()
+	akuo.mutation.SetOrganizationID(u)
+	return akuo
+}
+
+// SetNillableOrganizationID sets the "organization_id" field if the given value is not nil.
+func (akuo *ApiKeyUpdateOne) SetNillableOrganizationID(u *uint64) *ApiKeyUpdateOne {
+	if u != nil {
+		akuo.SetOrganizationID(*u)
+	}
+	return akuo
+}
+
+// AddOrganizationID adds u to the "organization_id" field.
+func (akuo *ApiKeyUpdateOne) AddOrganizationID(u int64) *ApiKeyUpdateOne {
+	akuo.mutation.AddOrganizationID(u)
+	return akuo
+}
+
+// SetAgentID sets the "agent_id" field.
+func (akuo *ApiKeyUpdateOne) SetAgentID(u uint64) *ApiKeyUpdateOne {
+	akuo.mutation.SetAgentID(u)
+	return akuo
+}
+
+// SetNillableAgentID sets the "agent_id" field if the given value is not nil.
+func (akuo *ApiKeyUpdateOne) SetNillableAgentID(u *uint64) *ApiKeyUpdateOne {
+	if u != nil {
+		akuo.SetAgentID(*u)
+	}
+	return akuo
+}
+
+// SetCustomAgentBase sets the "custom_agent_base" field.
+func (akuo *ApiKeyUpdateOne) SetCustomAgentBase(s string) *ApiKeyUpdateOne {
+	akuo.mutation.SetCustomAgentBase(s)
+	return akuo
+}
+
+// SetNillableCustomAgentBase sets the "custom_agent_base" field if the given value is not nil.
+func (akuo *ApiKeyUpdateOne) SetNillableCustomAgentBase(s *string) *ApiKeyUpdateOne {
+	if s != nil {
+		akuo.SetCustomAgentBase(*s)
+	}
+	return akuo
+}
+
+// ClearCustomAgentBase clears the value of the "custom_agent_base" field.
+func (akuo *ApiKeyUpdateOne) ClearCustomAgentBase() *ApiKeyUpdateOne {
+	akuo.mutation.ClearCustomAgentBase()
+	return akuo
+}
+
+// SetCustomAgentKey sets the "custom_agent_key" field.
+func (akuo *ApiKeyUpdateOne) SetCustomAgentKey(s string) *ApiKeyUpdateOne {
+	akuo.mutation.SetCustomAgentKey(s)
+	return akuo
+}
+
+// SetNillableCustomAgentKey sets the "custom_agent_key" field if the given value is not nil.
+func (akuo *ApiKeyUpdateOne) SetNillableCustomAgentKey(s *string) *ApiKeyUpdateOne {
+	if s != nil {
+		akuo.SetCustomAgentKey(*s)
+	}
+	return akuo
+}
+
+// ClearCustomAgentKey clears the value of the "custom_agent_key" field.
+func (akuo *ApiKeyUpdateOne) ClearCustomAgentKey() *ApiKeyUpdateOne {
+	akuo.mutation.ClearCustomAgentKey()
+	return akuo
+}
+
+// SetOpenaiBase sets the "openai_base" field.
+func (akuo *ApiKeyUpdateOne) SetOpenaiBase(s string) *ApiKeyUpdateOne {
+	akuo.mutation.SetOpenaiBase(s)
+	return akuo
+}
+
+// SetNillableOpenaiBase sets the "openai_base" field if the given value is not nil.
+func (akuo *ApiKeyUpdateOne) SetNillableOpenaiBase(s *string) *ApiKeyUpdateOne {
+	if s != nil {
+		akuo.SetOpenaiBase(*s)
+	}
+	return akuo
+}
+
+// ClearOpenaiBase clears the value of the "openai_base" field.
+func (akuo *ApiKeyUpdateOne) ClearOpenaiBase() *ApiKeyUpdateOne {
+	akuo.mutation.ClearOpenaiBase()
+	return akuo
+}
+
+// SetOpenaiKey sets the "openai_key" field.
+func (akuo *ApiKeyUpdateOne) SetOpenaiKey(s string) *ApiKeyUpdateOne {
+	akuo.mutation.SetOpenaiKey(s)
+	return akuo
+}
+
+// SetNillableOpenaiKey sets the "openai_key" field if the given value is not nil.
+func (akuo *ApiKeyUpdateOne) SetNillableOpenaiKey(s *string) *ApiKeyUpdateOne {
+	if s != nil {
+		akuo.SetOpenaiKey(*s)
+	}
+	return akuo
+}
+
+// ClearOpenaiKey clears the value of the "openai_key" field.
+func (akuo *ApiKeyUpdateOne) ClearOpenaiKey() *ApiKeyUpdateOne {
+	akuo.mutation.ClearOpenaiKey()
+	return akuo
+}
+
+// SetAgent sets the "agent" edge to the Agent entity.
+func (akuo *ApiKeyUpdateOne) SetAgent(a *Agent) *ApiKeyUpdateOne {
+	return akuo.SetAgentID(a.ID)
+}
+
+// Mutation returns the ApiKeyMutation object of the builder.
+func (akuo *ApiKeyUpdateOne) Mutation() *ApiKeyMutation {
+	return akuo.mutation
+}
+
+// ClearAgent clears the "agent" edge to the Agent entity.
+func (akuo *ApiKeyUpdateOne) ClearAgent() *ApiKeyUpdateOne {
+	akuo.mutation.ClearAgent()
+	return akuo
+}
+
+// Where appends a list predicates to the ApiKeyUpdate builder.
+func (akuo *ApiKeyUpdateOne) Where(ps ...predicate.ApiKey) *ApiKeyUpdateOne {
+	akuo.mutation.Where(ps...)
+	return akuo
+}
+
+// Select allows selecting one or more fields (columns) of the returned entity.
+// The default is selecting all fields defined in the entity schema.
+func (akuo *ApiKeyUpdateOne) Select(field string, fields ...string) *ApiKeyUpdateOne {
+	akuo.fields = append([]string{field}, fields...)
+	return akuo
+}
+
+// Save executes the query and returns the updated ApiKey entity.
+func (akuo *ApiKeyUpdateOne) Save(ctx context.Context) (*ApiKey, error) {
+	if err := akuo.defaults(); err != nil {
+		return nil, err
+	}
+	return withHooks(ctx, akuo.sqlSave, akuo.mutation, akuo.hooks)
+}
+
+// SaveX is like Save, but panics if an error occurs.
+func (akuo *ApiKeyUpdateOne) SaveX(ctx context.Context) *ApiKey {
+	node, err := akuo.Save(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return node
+}
+
+// Exec executes the query on the entity.
+func (akuo *ApiKeyUpdateOne) Exec(ctx context.Context) error {
+	_, err := akuo.Save(ctx)
+	return err
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (akuo *ApiKeyUpdateOne) ExecX(ctx context.Context) {
+	if err := akuo.Exec(ctx); err != nil {
+		panic(err)
+	}
+}
+
+// defaults sets the default values of the builder before save.
+func (akuo *ApiKeyUpdateOne) defaults() error {
+	if _, ok := akuo.mutation.UpdatedAt(); !ok {
+		if apikey.UpdateDefaultUpdatedAt == nil {
+			return fmt.Errorf("ent: uninitialized apikey.UpdateDefaultUpdatedAt (forgotten import ent/runtime?)")
+		}
+		v := apikey.UpdateDefaultUpdatedAt()
+		akuo.mutation.SetUpdatedAt(v)
+	}
+	return nil
+}
+
+// check runs all checks and user-defined validators on the builder.
+func (akuo *ApiKeyUpdateOne) check() error {
+	if _, ok := akuo.mutation.AgentID(); akuo.mutation.AgentCleared() && !ok {
+		return errors.New(`ent: clearing a required unique edge "ApiKey.agent"`)
+	}
+	return nil
+}
+
+func (akuo *ApiKeyUpdateOne) sqlSave(ctx context.Context) (_node *ApiKey, err error) {
+	if err := akuo.check(); err != nil {
+		return _node, err
+	}
+	_spec := sqlgraph.NewUpdateSpec(apikey.Table, apikey.Columns, sqlgraph.NewFieldSpec(apikey.FieldID, field.TypeUint64))
+	id, ok := akuo.mutation.ID()
+	if !ok {
+		return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "ApiKey.id" for update`)}
+	}
+	_spec.Node.ID.Value = id
+	if fields := akuo.fields; len(fields) > 0 {
+		_spec.Node.Columns = make([]string, 0, len(fields))
+		_spec.Node.Columns = append(_spec.Node.Columns, apikey.FieldID)
+		for _, f := range fields {
+			if !apikey.ValidColumn(f) {
+				return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
+			}
+			if f != apikey.FieldID {
+				_spec.Node.Columns = append(_spec.Node.Columns, f)
+			}
+		}
+	}
+	if ps := akuo.mutation.predicates; len(ps) > 0 {
+		_spec.Predicate = func(selector *sql.Selector) {
+			for i := range ps {
+				ps[i](selector)
+			}
+		}
+	}
+	if value, ok := akuo.mutation.UpdatedAt(); ok {
+		_spec.SetField(apikey.FieldUpdatedAt, field.TypeTime, value)
+	}
+	if value, ok := akuo.mutation.DeletedAt(); ok {
+		_spec.SetField(apikey.FieldDeletedAt, field.TypeTime, value)
+	}
+	if akuo.mutation.DeletedAtCleared() {
+		_spec.ClearField(apikey.FieldDeletedAt, field.TypeTime)
+	}
+	if value, ok := akuo.mutation.Title(); ok {
+		_spec.SetField(apikey.FieldTitle, field.TypeString, value)
+	}
+	if akuo.mutation.TitleCleared() {
+		_spec.ClearField(apikey.FieldTitle, field.TypeString)
+	}
+	if value, ok := akuo.mutation.Key(); ok {
+		_spec.SetField(apikey.FieldKey, field.TypeString, value)
+	}
+	if akuo.mutation.KeyCleared() {
+		_spec.ClearField(apikey.FieldKey, field.TypeString)
+	}
+	if value, ok := akuo.mutation.OrganizationID(); ok {
+		_spec.SetField(apikey.FieldOrganizationID, field.TypeUint64, value)
+	}
+	if value, ok := akuo.mutation.AddedOrganizationID(); ok {
+		_spec.AddField(apikey.FieldOrganizationID, field.TypeUint64, value)
+	}
+	if value, ok := akuo.mutation.CustomAgentBase(); ok {
+		_spec.SetField(apikey.FieldCustomAgentBase, field.TypeString, value)
+	}
+	if akuo.mutation.CustomAgentBaseCleared() {
+		_spec.ClearField(apikey.FieldCustomAgentBase, field.TypeString)
+	}
+	if value, ok := akuo.mutation.CustomAgentKey(); ok {
+		_spec.SetField(apikey.FieldCustomAgentKey, field.TypeString, value)
+	}
+	if akuo.mutation.CustomAgentKeyCleared() {
+		_spec.ClearField(apikey.FieldCustomAgentKey, field.TypeString)
+	}
+	if value, ok := akuo.mutation.OpenaiBase(); ok {
+		_spec.SetField(apikey.FieldOpenaiBase, field.TypeString, value)
+	}
+	if akuo.mutation.OpenaiBaseCleared() {
+		_spec.ClearField(apikey.FieldOpenaiBase, field.TypeString)
+	}
+	if value, ok := akuo.mutation.OpenaiKey(); ok {
+		_spec.SetField(apikey.FieldOpenaiKey, field.TypeString, value)
+	}
+	if akuo.mutation.OpenaiKeyCleared() {
+		_spec.ClearField(apikey.FieldOpenaiKey, field.TypeString)
+	}
+	if akuo.mutation.AgentCleared() {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.M2O,
+			Inverse: true,
+			Table:   apikey.AgentTable,
+			Columns: []string{apikey.AgentColumn},
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: sqlgraph.NewFieldSpec(agent.FieldID, field.TypeUint64),
+			},
+		}
+		_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
+	}
+	if nodes := akuo.mutation.AgentIDs(); len(nodes) > 0 {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.M2O,
+			Inverse: true,
+			Table:   apikey.AgentTable,
+			Columns: []string{apikey.AgentColumn},
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: sqlgraph.NewFieldSpec(agent.FieldID, field.TypeUint64),
+			},
+		}
+		for _, k := range nodes {
+			edge.Target.Nodes = append(edge.Target.Nodes, k)
+		}
+		_spec.Edges.Add = append(_spec.Edges.Add, edge)
+	}
+	_node = &ApiKey{config: akuo.config}
+	_spec.Assign = _node.assignValues
+	_spec.ScanValues = _node.scanValues
+	if err = sqlgraph.UpdateNode(ctx, akuo.driver, _spec); err != nil {
+		if _, ok := err.(*sqlgraph.NotFoundError); ok {
+			err = &NotFoundError{apikey.Label}
+		} else if sqlgraph.IsConstraintError(err) {
+			err = &ConstraintError{msg: err.Error(), wrap: err}
+		}
+		return nil, err
+	}
+	akuo.mutation.done = true
+	return _node, nil
+}

+ 180 - 4
ent/client.go

@@ -15,6 +15,7 @@ import (
 	"wechat-api/ent/agentbase"
 	"wechat-api/ent/aliyunavatar"
 	"wechat-api/ent/allocagent"
+	"wechat-api/ent/apikey"
 	"wechat-api/ent/batchmsg"
 	"wechat-api/ent/category"
 	"wechat-api/ent/chatrecords"
@@ -75,6 +76,8 @@ type Client struct {
 	AliyunAvatar *AliyunAvatarClient
 	// AllocAgent is the client for interacting with the AllocAgent builders.
 	AllocAgent *AllocAgentClient
+	// ApiKey is the client for interacting with the ApiKey builders.
+	ApiKey *ApiKeyClient
 	// BatchMsg is the client for interacting with the BatchMsg builders.
 	BatchMsg *BatchMsgClient
 	// Category is the client for interacting with the Category builders.
@@ -166,6 +169,7 @@ func (c *Client) init() {
 	c.AgentBase = NewAgentBaseClient(c.config)
 	c.AliyunAvatar = NewAliyunAvatarClient(c.config)
 	c.AllocAgent = NewAllocAgentClient(c.config)
+	c.ApiKey = NewApiKeyClient(c.config)
 	c.BatchMsg = NewBatchMsgClient(c.config)
 	c.Category = NewCategoryClient(c.config)
 	c.ChatRecords = NewChatRecordsClient(c.config)
@@ -300,6 +304,7 @@ func (c *Client) Tx(ctx context.Context) (*Tx, error) {
 		AgentBase:           NewAgentBaseClient(cfg),
 		AliyunAvatar:        NewAliyunAvatarClient(cfg),
 		AllocAgent:          NewAllocAgentClient(cfg),
+		ApiKey:              NewApiKeyClient(cfg),
 		BatchMsg:            NewBatchMsgClient(cfg),
 		Category:            NewCategoryClient(cfg),
 		ChatRecords:         NewChatRecordsClient(cfg),
@@ -361,6 +366,7 @@ func (c *Client) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error)
 		AgentBase:           NewAgentBaseClient(cfg),
 		AliyunAvatar:        NewAliyunAvatarClient(cfg),
 		AllocAgent:          NewAllocAgentClient(cfg),
+		ApiKey:              NewApiKeyClient(cfg),
 		BatchMsg:            NewBatchMsgClient(cfg),
 		Category:            NewCategoryClient(cfg),
 		ChatRecords:         NewChatRecordsClient(cfg),
@@ -428,7 +434,7 @@ func (c *Client) Close() error {
 // In order to add hooks to a specific client, call: `client.Node.Use(...)`.
 func (c *Client) Use(hooks ...Hook) {
 	for _, n := range []interface{ Use(...Hook) }{
-		c.Agent, c.AgentBase, c.AliyunAvatar, c.AllocAgent, c.BatchMsg, c.Category,
+		c.Agent, c.AgentBase, c.AliyunAvatar, c.AllocAgent, c.ApiKey, c.BatchMsg, c.Category,
 		c.ChatRecords, c.ChatSession, c.Contact, c.CreditBalance, c.CreditUsage,
 		c.Employee, c.EmployeeConfig, c.Label, c.LabelRelationship, c.LabelTagging,
 		c.Message, c.MessageRecords, c.Msg, c.PayRecharge, c.Server, c.SopNode,
@@ -445,7 +451,7 @@ func (c *Client) Use(hooks ...Hook) {
 // In order to add interceptors to a specific client, call: `client.Node.Intercept(...)`.
 func (c *Client) Intercept(interceptors ...Interceptor) {
 	for _, n := range []interface{ Intercept(...Interceptor) }{
-		c.Agent, c.AgentBase, c.AliyunAvatar, c.AllocAgent, c.BatchMsg, c.Category,
+		c.Agent, c.AgentBase, c.AliyunAvatar, c.AllocAgent, c.ApiKey, c.BatchMsg, c.Category,
 		c.ChatRecords, c.ChatSession, c.Contact, c.CreditBalance, c.CreditUsage,
 		c.Employee, c.EmployeeConfig, c.Label, c.LabelRelationship, c.LabelTagging,
 		c.Message, c.MessageRecords, c.Msg, c.PayRecharge, c.Server, c.SopNode,
@@ -469,6 +475,8 @@ func (c *Client) Mutate(ctx context.Context, m Mutation) (Value, error) {
 		return c.AliyunAvatar.mutate(ctx, m)
 	case *AllocAgentMutation:
 		return c.AllocAgent.mutate(ctx, m)
+	case *ApiKeyMutation:
+		return c.ApiKey.mutate(ctx, m)
 	case *BatchMsgMutation:
 		return c.BatchMsg.mutate(ctx, m)
 	case *CategoryMutation:
@@ -706,6 +714,7 @@ func (c *AgentClient) QueryWaAgent(a *Agent) *WhatsappQuery {
 	return query
 }
 
+
 // QueryXjsAgent queries the xjs_agent edge of a Agent.
 func (c *AgentClient) QueryXjsAgent(a *Agent) *XunjiServiceQuery {
 	query := (&XunjiServiceClient{config: c.config}).Query()
@@ -721,6 +730,22 @@ func (c *AgentClient) QueryXjsAgent(a *Agent) *XunjiServiceQuery {
 	}
 	return query
 }
+// QueryKeyAgent queries the key_agent edge of a Agent.
+func (c *AgentClient) QueryKeyAgent(a *Agent) *ApiKeyQuery {
+	query := (&ApiKeyClient{config: c.config}).Query()
+
+	query.path = func(context.Context) (fromV *sql.Selector, _ error) {
+		id := a.ID
+		step := sqlgraph.NewStep(
+			sqlgraph.From(agent.Table, agent.FieldID, id),
+			sqlgraph.To(apikey.Table, apikey.FieldID),
+			sqlgraph.Edge(sqlgraph.O2M, false, agent.KeyAgentTable, agent.KeyAgentColumn),
+		)
+		fromV = sqlgraph.Neighbors(a.driver.Dialect(), step)
+		return fromV, nil
+	}
+	return query
+}
 
 // Hooks returns the client hooks.
 func (c *AgentClient) Hooks() []Hook {
@@ -1168,6 +1193,157 @@ func (c *AllocAgentClient) mutate(ctx context.Context, m *AllocAgentMutation) (V
 	}
 }
 
+// ApiKeyClient is a client for the ApiKey schema.
+type ApiKeyClient struct {
+	config
+}
+
+// NewApiKeyClient returns a client for the ApiKey from the given config.
+func NewApiKeyClient(c config) *ApiKeyClient {
+	return &ApiKeyClient{config: c}
+}
+
+// Use adds a list of mutation hooks to the hooks stack.
+// A call to `Use(f, g, h)` equals to `apikey.Hooks(f(g(h())))`.
+func (c *ApiKeyClient) Use(hooks ...Hook) {
+	c.hooks.ApiKey = append(c.hooks.ApiKey, hooks...)
+}
+
+// Intercept adds a list of query interceptors to the interceptors stack.
+// A call to `Intercept(f, g, h)` equals to `apikey.Intercept(f(g(h())))`.
+func (c *ApiKeyClient) Intercept(interceptors ...Interceptor) {
+	c.inters.ApiKey = append(c.inters.ApiKey, interceptors...)
+}
+
+// Create returns a builder for creating a ApiKey entity.
+func (c *ApiKeyClient) Create() *ApiKeyCreate {
+	mutation := newApiKeyMutation(c.config, OpCreate)
+	return &ApiKeyCreate{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// CreateBulk returns a builder for creating a bulk of ApiKey entities.
+func (c *ApiKeyClient) CreateBulk(builders ...*ApiKeyCreate) *ApiKeyCreateBulk {
+	return &ApiKeyCreateBulk{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 *ApiKeyClient) MapCreateBulk(slice any, setFunc func(*ApiKeyCreate, int)) *ApiKeyCreateBulk {
+	rv := reflect.ValueOf(slice)
+	if rv.Kind() != reflect.Slice {
+		return &ApiKeyCreateBulk{err: fmt.Errorf("calling to ApiKeyClient.MapCreateBulk with wrong type %T, need slice", slice)}
+	}
+	builders := make([]*ApiKeyCreate, rv.Len())
+	for i := 0; i < rv.Len(); i++ {
+		builders[i] = c.Create()
+		setFunc(builders[i], i)
+	}
+	return &ApiKeyCreateBulk{config: c.config, builders: builders}
+}
+
+// Update returns an update builder for ApiKey.
+func (c *ApiKeyClient) Update() *ApiKeyUpdate {
+	mutation := newApiKeyMutation(c.config, OpUpdate)
+	return &ApiKeyUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// UpdateOne returns an update builder for the given entity.
+func (c *ApiKeyClient) UpdateOne(ak *ApiKey) *ApiKeyUpdateOne {
+	mutation := newApiKeyMutation(c.config, OpUpdateOne, withApiKey(ak))
+	return &ApiKeyUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// UpdateOneID returns an update builder for the given id.
+func (c *ApiKeyClient) UpdateOneID(id uint64) *ApiKeyUpdateOne {
+	mutation := newApiKeyMutation(c.config, OpUpdateOne, withApiKeyID(id))
+	return &ApiKeyUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// Delete returns a delete builder for ApiKey.
+func (c *ApiKeyClient) Delete() *ApiKeyDelete {
+	mutation := newApiKeyMutation(c.config, OpDelete)
+	return &ApiKeyDelete{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// DeleteOne returns a builder for deleting the given entity.
+func (c *ApiKeyClient) DeleteOne(ak *ApiKey) *ApiKeyDeleteOne {
+	return c.DeleteOneID(ak.ID)
+}
+
+// DeleteOneID returns a builder for deleting the given entity by its id.
+func (c *ApiKeyClient) DeleteOneID(id uint64) *ApiKeyDeleteOne {
+	builder := c.Delete().Where(apikey.ID(id))
+	builder.mutation.id = &id
+	builder.mutation.op = OpDeleteOne
+	return &ApiKeyDeleteOne{builder}
+}
+
+// Query returns a query builder for ApiKey.
+func (c *ApiKeyClient) Query() *ApiKeyQuery {
+	return &ApiKeyQuery{
+		config: c.config,
+		ctx:    &QueryContext{Type: TypeApiKey},
+		inters: c.Interceptors(),
+	}
+}
+
+// Get returns a ApiKey entity by its id.
+func (c *ApiKeyClient) Get(ctx context.Context, id uint64) (*ApiKey, error) {
+	return c.Query().Where(apikey.ID(id)).Only(ctx)
+}
+
+// GetX is like Get, but panics if an error occurs.
+func (c *ApiKeyClient) GetX(ctx context.Context, id uint64) *ApiKey {
+	obj, err := c.Get(ctx, id)
+	if err != nil {
+		panic(err)
+	}
+	return obj
+}
+
+// QueryAgent queries the agent edge of a ApiKey.
+func (c *ApiKeyClient) QueryAgent(ak *ApiKey) *AgentQuery {
+	query := (&AgentClient{config: c.config}).Query()
+	query.path = func(context.Context) (fromV *sql.Selector, _ error) {
+		id := ak.ID
+		step := sqlgraph.NewStep(
+			sqlgraph.From(apikey.Table, apikey.FieldID, id),
+			sqlgraph.To(agent.Table, agent.FieldID),
+			sqlgraph.Edge(sqlgraph.M2O, true, apikey.AgentTable, apikey.AgentColumn),
+		)
+		fromV = sqlgraph.Neighbors(ak.driver.Dialect(), step)
+		return fromV, nil
+	}
+	return query
+}
+
+// Hooks returns the client hooks.
+func (c *ApiKeyClient) Hooks() []Hook {
+	hooks := c.hooks.ApiKey
+	return append(hooks[:len(hooks):len(hooks)], apikey.Hooks[:]...)
+}
+
+// Interceptors returns the client interceptors.
+func (c *ApiKeyClient) Interceptors() []Interceptor {
+	inters := c.inters.ApiKey
+	return append(inters[:len(inters):len(inters)], apikey.Interceptors[:]...)
+}
+
+func (c *ApiKeyClient) mutate(ctx context.Context, m *ApiKeyMutation) (Value, error) {
+	switch m.Op() {
+	case OpCreate:
+		return (&ApiKeyCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
+	case OpUpdate:
+		return (&ApiKeyUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
+	case OpUpdateOne:
+		return (&ApiKeyUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
+	case OpDelete, OpDeleteOne:
+		return (&ApiKeyDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx)
+	default:
+		return nil, fmt.Errorf("ent: unknown ApiKey mutation op: %q", m.Op())
+	}
+}
+
 // BatchMsgClient is a client for the BatchMsg schema.
 type BatchMsgClient struct {
 	config
@@ -6669,7 +6845,7 @@ func (c *XunjiServiceClient) mutate(ctx context.Context, m *XunjiServiceMutation
 // hooks and interceptors per client, for fast access.
 type (
 	hooks struct {
-		Agent, AgentBase, AliyunAvatar, AllocAgent, BatchMsg, Category, ChatRecords,
+		Agent, AgentBase, AliyunAvatar, AllocAgent, ApiKey, BatchMsg, Category, ChatRecords,
 		ChatSession, Contact, CreditBalance, CreditUsage, Employee, EmployeeConfig,
 		Label, LabelRelationship, LabelTagging, Message, MessageRecords, Msg,
 		PayRecharge, Server, SopNode, SopStage, SopTask, Token, Tutorial, UsageDetail,
@@ -6678,7 +6854,7 @@ type (
 		WxCard, WxCardUser, WxCardVisit, Xunji, XunjiService []ent.Hook
 	}
 	inters struct {
-		Agent, AgentBase, AliyunAvatar, AllocAgent, BatchMsg, Category, ChatRecords,
+		Agent, AgentBase, AliyunAvatar, AllocAgent, ApiKey, BatchMsg, Category, ChatRecords,
 		ChatSession, Contact, CreditBalance, CreditUsage, Employee, EmployeeConfig,
 		Label, LabelRelationship, LabelTagging, Message, MessageRecords, Msg,
 		PayRecharge, Server, SopNode, SopStage, SopTask, Token, Tutorial, UsageDetail,

+ 2 - 0
ent/ent.go

@@ -12,6 +12,7 @@ import (
 	"wechat-api/ent/agentbase"
 	"wechat-api/ent/aliyunavatar"
 	"wechat-api/ent/allocagent"
+	"wechat-api/ent/apikey"
 	"wechat-api/ent/batchmsg"
 	"wechat-api/ent/category"
 	"wechat-api/ent/chatrecords"
@@ -118,6 +119,7 @@ func checkColumn(table, column string) error {
 			agentbase.Table:           agentbase.ValidColumn,
 			aliyunavatar.Table:        aliyunavatar.ValidColumn,
 			allocagent.Table:          allocagent.ValidColumn,
+			apikey.Table:              apikey.ValidColumn,
 			batchmsg.Table:            batchmsg.ValidColumn,
 			category.Table:            category.ValidColumn,
 			chatrecords.Table:         chatrecords.ValidColumn,

+ 12 - 0
ent/hook/hook.go

@@ -56,6 +56,18 @@ func (f AllocAgentFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value,
 	return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.AllocAgentMutation", m)
 }
 
+// The ApiKeyFunc type is an adapter to allow the use of ordinary
+// function as ApiKey mutator.
+type ApiKeyFunc func(context.Context, *ent.ApiKeyMutation) (ent.Value, error)
+
+// Mutate calls f(ctx, m).
+func (f ApiKeyFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) {
+	if mv, ok := m.(*ent.ApiKeyMutation); ok {
+		return f(ctx, mv)
+	}
+	return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.ApiKeyMutation", m)
+}
+
 // The BatchMsgFunc type is an adapter to allow the use of ordinary
 // function as BatchMsg mutator.
 type BatchMsgFunc func(context.Context, *ent.BatchMsgMutation) (ent.Value, error)

+ 30 - 0
ent/intercept/intercept.go

@@ -10,6 +10,7 @@ import (
 	"wechat-api/ent/agentbase"
 	"wechat-api/ent/aliyunavatar"
 	"wechat-api/ent/allocagent"
+	"wechat-api/ent/apikey"
 	"wechat-api/ent/batchmsg"
 	"wechat-api/ent/category"
 	"wechat-api/ent/chatrecords"
@@ -217,6 +218,33 @@ func (f TraverseAllocAgent) Traverse(ctx context.Context, q ent.Query) error {
 	return fmt.Errorf("unexpected query type %T. expect *ent.AllocAgentQuery", q)
 }
 
+// The ApiKeyFunc type is an adapter to allow the use of ordinary function as a Querier.
+type ApiKeyFunc func(context.Context, *ent.ApiKeyQuery) (ent.Value, error)
+
+// Query calls f(ctx, q).
+func (f ApiKeyFunc) Query(ctx context.Context, q ent.Query) (ent.Value, error) {
+	if q, ok := q.(*ent.ApiKeyQuery); ok {
+		return f(ctx, q)
+	}
+	return nil, fmt.Errorf("unexpected query type %T. expect *ent.ApiKeyQuery", q)
+}
+
+// The TraverseApiKey type is an adapter to allow the use of ordinary function as Traverser.
+type TraverseApiKey func(context.Context, *ent.ApiKeyQuery) error
+
+// Intercept is a dummy implementation of Intercept that returns the next Querier in the pipeline.
+func (f TraverseApiKey) Intercept(next ent.Querier) ent.Querier {
+	return next
+}
+
+// Traverse calls f(ctx, q).
+func (f TraverseApiKey) Traverse(ctx context.Context, q ent.Query) error {
+	if q, ok := q.(*ent.ApiKeyQuery); ok {
+		return f(ctx, q)
+	}
+	return fmt.Errorf("unexpected query type %T. expect *ent.ApiKeyQuery", q)
+}
+
 // The BatchMsgFunc type is an adapter to allow the use of ordinary function as a Querier.
 type BatchMsgFunc func(context.Context, *ent.BatchMsgQuery) (ent.Value, error)
 
@@ -1254,6 +1282,8 @@ func NewQuery(q ent.Query) (Query, error) {
 		return &query[*ent.AliyunAvatarQuery, predicate.AliyunAvatar, aliyunavatar.OrderOption]{typ: ent.TypeAliyunAvatar, tq: q}, nil
 	case *ent.AllocAgentQuery:
 		return &query[*ent.AllocAgentQuery, predicate.AllocAgent, allocagent.OrderOption]{typ: ent.TypeAllocAgent, tq: q}, nil
+	case *ent.ApiKeyQuery:
+		return &query[*ent.ApiKeyQuery, predicate.ApiKey, apikey.OrderOption]{typ: ent.TypeApiKey, tq: q}, nil
 	case *ent.BatchMsgQuery:
 		return &query[*ent.BatchMsgQuery, predicate.BatchMsg, batchmsg.OrderOption]{typ: ent.TypeBatchMsg, tq: q}, nil
 	case *ent.CategoryQuery:

+ 41 - 0
ent/migrate/schema.go

@@ -114,6 +114,42 @@ var (
 			},
 		},
 	}
+	// APIKeyColumns holds the columns for the "api_key" table.
+	APIKeyColumns = []*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: "title", Type: field.TypeString, Nullable: true, Comment: "Title", Default: ""},
+		{Name: "key", Type: field.TypeString, Nullable: true, Comment: "Key", Default: ""},
+		{Name: "organization_id", Type: field.TypeUint64, Comment: "租户ID", Default: 0},
+		{Name: "custom_agent_base", Type: field.TypeString, Nullable: true, Comment: "定制agent服务地址", Default: ""},
+		{Name: "custom_agent_key", Type: field.TypeString, Nullable: true, Comment: "定制agent服务密钥", Default: ""},
+		{Name: "openai_base", Type: field.TypeString, Nullable: true, Comment: "大模型服务地址", Default: ""},
+		{Name: "openai_key", Type: field.TypeString, Nullable: true, Comment: "大模型服务密钥", Default: ""},
+		{Name: "agent_id", Type: field.TypeUint64, Comment: "智能体ID", Default: 0},
+	}
+	// APIKeyTable holds the schema information for the "api_key" table.
+	APIKeyTable = &schema.Table{
+		Name:       "api_key",
+		Columns:    APIKeyColumns,
+		PrimaryKey: []*schema.Column{APIKeyColumns[0]},
+		ForeignKeys: []*schema.ForeignKey{
+			{
+				Symbol:     "api_key_agent_key_agent",
+				Columns:    []*schema.Column{APIKeyColumns[11]},
+				RefColumns: []*schema.Column{AgentColumns[0]},
+				OnDelete:   schema.NoAction,
+			},
+		},
+		Indexes: []*schema.Index{
+			{
+				Name:    "apikey_key",
+				Unique:  true,
+				Columns: []*schema.Column{APIKeyColumns[5]},
+			},
+		},
+	}
 	// BatchMsgColumns holds the columns for the "batch_msg" table.
 	BatchMsgColumns = []*schema.Column{
 		{Name: "id", Type: field.TypeUint64, Increment: true},
@@ -1449,6 +1485,7 @@ var (
 		AgentBaseTable,
 		AliyunAvatarTable,
 		AllocAgentTable,
+		APIKeyTable,
 		BatchMsgTable,
 		CategoryTable,
 		ChatRecordsTable,
@@ -1503,6 +1540,10 @@ func init() {
 	AllocAgentTable.Annotation = &entsql.Annotation{
 		Table: "alloc_agent",
 	}
+	APIKeyTable.ForeignKeys[0].RefTable = AgentTable
+	APIKeyTable.Annotation = &entsql.Annotation{
+		Table: "api_key",
+	}
 	BatchMsgTable.Annotation = &entsql.Annotation{
 		Table: "batch_msg",
 	}

+ 1188 - 0
ent/mutation.go

@@ -12,6 +12,7 @@ import (
 	"wechat-api/ent/agentbase"
 	"wechat-api/ent/aliyunavatar"
 	"wechat-api/ent/allocagent"
+	"wechat-api/ent/apikey"
 	"wechat-api/ent/batchmsg"
 	"wechat-api/ent/category"
 	"wechat-api/ent/chatrecords"
@@ -70,6 +71,7 @@ const (
 	TypeAgentBase           = "AgentBase"
 	TypeAliyunAvatar        = "AliyunAvatar"
 	TypeAllocAgent          = "AllocAgent"
+	TypeApiKey              = "ApiKey"
 	TypeBatchMsg            = "BatchMsg"
 	TypeCategory            = "Category"
 	TypeChatRecords         = "ChatRecords"
@@ -139,9 +141,15 @@ type AgentMutation struct {
 	wa_agent           map[uint64]struct{}
 	removedwa_agent    map[uint64]struct{}
 	clearedwa_agent    bool
+
 	xjs_agent          map[uint64]struct{}
 	removedxjs_agent   map[uint64]struct{}
 	clearedxjs_agent   bool
+
+	key_agent          map[uint64]struct{}
+	removedkey_agent   map[uint64]struct{}
+	clearedkey_agent   bool
+
 	done               bool
 	oldValue           func(context.Context) (*Agent, error)
 	predicates         []predicate.Agent
@@ -902,6 +910,7 @@ func (m *AgentMutation) ResetWaAgent() {
 	m.removedwa_agent = nil
 }
 
+
 // AddXjsAgentIDs adds the "xjs_agent" edge to the XunjiService entity by ids.
 func (m *AgentMutation) AddXjsAgentIDs(ids ...uint64) {
 	if m.xjs_agent == nil {
@@ -938,9 +947,47 @@ func (m *AgentMutation) RemovedXjsAgentIDs() (ids []uint64) {
 	for id := range m.removedxjs_agent {
 		ids = append(ids, id)
 	}
+}
+// AddKeyAgentIDs adds the "key_agent" edge to the ApiKey entity by ids.
+func (m *AgentMutation) AddKeyAgentIDs(ids ...uint64) {
+	if m.key_agent == nil {
+		m.key_agent = make(map[uint64]struct{})
+	}
+	for i := range ids {
+		m.key_agent[ids[i]] = struct{}{}
+	}
+}
+
+// ClearKeyAgent clears the "key_agent" edge to the ApiKey entity.
+func (m *AgentMutation) ClearKeyAgent() {
+	m.clearedkey_agent = true
+}
+
+// KeyAgentCleared reports if the "key_agent" edge to the ApiKey entity was cleared.
+func (m *AgentMutation) KeyAgentCleared() bool {
+	return m.clearedkey_agent
+}
+
+// RemoveKeyAgentIDs removes the "key_agent" edge to the ApiKey entity by IDs.
+func (m *AgentMutation) RemoveKeyAgentIDs(ids ...uint64) {
+	if m.removedkey_agent == nil {
+		m.removedkey_agent = make(map[uint64]struct{})
+	}
+	for i := range ids {
+		delete(m.key_agent, ids[i])
+		m.removedkey_agent[ids[i]] = struct{}{}
+	}
+}
+
+// RemovedKeyAgent returns the removed IDs of the "key_agent" edge to the ApiKey entity.
+func (m *AgentMutation) RemovedKeyAgentIDs() (ids []uint64) {
+	for id := range m.removedkey_agent {
+		ids = append(ids, id)
+	}
 	return
 }
 
+
 // XjsAgentIDs returns the "xjs_agent" edge IDs in the mutation.
 func (m *AgentMutation) XjsAgentIDs() (ids []uint64) {
 	for id := range m.xjs_agent {
@@ -948,6 +995,14 @@ func (m *AgentMutation) XjsAgentIDs() (ids []uint64) {
 	}
 	return
 }
+// KeyAgentIDs returns the "key_agent" edge IDs in the mutation.
+func (m *AgentMutation) KeyAgentIDs() (ids []uint64) {
+	for id := range m.key_agent {
+		ids = append(ids, id)
+	}
+	return
+}
+
 
 // ResetXjsAgent resets all changes to the "xjs_agent" edge.
 func (m *AgentMutation) ResetXjsAgent() {
@@ -955,6 +1010,12 @@ func (m *AgentMutation) ResetXjsAgent() {
 	m.clearedxjs_agent = false
 	m.removedxjs_agent = nil
 }
+// ResetKeyAgent resets all changes to the "key_agent" edge.
+func (m *AgentMutation) ResetKeyAgent() {
+	m.key_agent = nil
+	m.clearedkey_agent = false
+	m.removedkey_agent = nil
+}
 
 // Where appends a list predicates to the AgentMutation builder.
 func (m *AgentMutation) Where(ps ...predicate.Agent) {
@@ -1323,9 +1384,13 @@ func (m *AgentMutation) AddedEdges() []string {
 	if m.wa_agent != nil {
 		edges = append(edges, agent.EdgeWaAgent)
 	}
+
 	if m.xjs_agent != nil {
 		edges = append(edges, agent.EdgeXjsAgent)
 	}
+	if m.key_agent != nil {
+		edges = append(edges, agent.EdgeKeyAgent)
+	}
 	return edges
 }
 
@@ -1357,6 +1422,12 @@ func (m *AgentMutation) AddedIDs(name string) []ent.Value {
 			ids = append(ids, id)
 		}
 		return ids
+	case agent.EdgeKeyAgent:
+		ids := make([]ent.Value, 0, len(m.key_agent))
+		for id := range m.key_agent {
+			ids = append(ids, id)
+		}
+		return ids
 	}
 	return nil
 }
@@ -1373,9 +1444,13 @@ func (m *AgentMutation) RemovedEdges() []string {
 	if m.removedwa_agent != nil {
 		edges = append(edges, agent.EdgeWaAgent)
 	}
+
 	if m.removedxjs_agent != nil {
 		edges = append(edges, agent.EdgeXjsAgent)
 	}
+	if m.removedkey_agent != nil {
+		edges = append(edges, agent.EdgeKeyAgent)
+	}
 	return edges
 }
 
@@ -1401,12 +1476,19 @@ func (m *AgentMutation) RemovedIDs(name string) []ent.Value {
 			ids = append(ids, id)
 		}
 		return ids
+
 	case agent.EdgeXjsAgent:
 		ids := make([]ent.Value, 0, len(m.removedxjs_agent))
 		for id := range m.removedxjs_agent {
 			ids = append(ids, id)
 		}
 		return ids
+	case agent.EdgeKeyAgent:
+		ids := make([]ent.Value, 0, len(m.removedkey_agent))
+		for id := range m.removedkey_agent {
+			ids = append(ids, id)
+		}
+		return ids
 	}
 	return nil
 }
@@ -1423,9 +1505,13 @@ func (m *AgentMutation) ClearedEdges() []string {
 	if m.clearedwa_agent {
 		edges = append(edges, agent.EdgeWaAgent)
 	}
+
 	if m.clearedxjs_agent {
 		edges = append(edges, agent.EdgeXjsAgent)
 	}
+	if m.clearedkey_agent {
+		edges = append(edges, agent.EdgeKeyAgent)
+	}
 	return edges
 }
 
@@ -1441,6 +1527,8 @@ func (m *AgentMutation) EdgeCleared(name string) bool {
 		return m.clearedwa_agent
 	case agent.EdgeXjsAgent:
 		return m.clearedxjs_agent
+	case agent.EdgeKeyAgent:
+		return m.clearedkey_agent
 	}
 	return false
 }
@@ -1468,6 +1556,8 @@ func (m *AgentMutation) ResetEdge(name string) error {
 		return nil
 	case agent.EdgeXjsAgent:
 		m.ResetXjsAgent()
+	case agent.EdgeKeyAgent:
+		m.ResetKeyAgent()
 		return nil
 	}
 	return fmt.Errorf("unknown Agent edge %s", name)
@@ -4430,6 +4520,1104 @@ func (m *AllocAgentMutation) ResetEdge(name string) error {
 	return fmt.Errorf("unknown AllocAgent edge %s", name)
 }
 
+// ApiKeyMutation represents an operation that mutates the ApiKey nodes in the graph.
+type ApiKeyMutation struct {
+	config
+	op                 Op
+	typ                string
+	id                 *uint64
+	created_at         *time.Time
+	updated_at         *time.Time
+	deleted_at         *time.Time
+	title              *string
+	key                *string
+	organization_id    *uint64
+	addorganization_id *int64
+	custom_agent_base  *string
+	custom_agent_key   *string
+	openai_base        *string
+	openai_key         *string
+	clearedFields      map[string]struct{}
+	agent              *uint64
+	clearedagent       bool
+	done               bool
+	oldValue           func(context.Context) (*ApiKey, error)
+	predicates         []predicate.ApiKey
+}
+
+var _ ent.Mutation = (*ApiKeyMutation)(nil)
+
+// apikeyOption allows management of the mutation configuration using functional options.
+type apikeyOption func(*ApiKeyMutation)
+
+// newApiKeyMutation creates new mutation for the ApiKey entity.
+func newApiKeyMutation(c config, op Op, opts ...apikeyOption) *ApiKeyMutation {
+	m := &ApiKeyMutation{
+		config:        c,
+		op:            op,
+		typ:           TypeApiKey,
+		clearedFields: make(map[string]struct{}),
+	}
+	for _, opt := range opts {
+		opt(m)
+	}
+	return m
+}
+
+// withApiKeyID sets the ID field of the mutation.
+func withApiKeyID(id uint64) apikeyOption {
+	return func(m *ApiKeyMutation) {
+		var (
+			err   error
+			once  sync.Once
+			value *ApiKey
+		)
+		m.oldValue = func(ctx context.Context) (*ApiKey, error) {
+			once.Do(func() {
+				if m.done {
+					err = errors.New("querying old values post mutation is not allowed")
+				} else {
+					value, err = m.Client().ApiKey.Get(ctx, id)
+				}
+			})
+			return value, err
+		}
+		m.id = &id
+	}
+}
+
+// withApiKey sets the old ApiKey of the mutation.
+func withApiKey(node *ApiKey) apikeyOption {
+	return func(m *ApiKeyMutation) {
+		m.oldValue = func(context.Context) (*ApiKey, 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 ApiKeyMutation) 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 ApiKeyMutation) 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 ApiKey entities.
+func (m *ApiKeyMutation) 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 *ApiKeyMutation) 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 *ApiKeyMutation) 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().ApiKey.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 *ApiKeyMutation) SetCreatedAt(t time.Time) {
+	m.created_at = &t
+}
+
+// CreatedAt returns the value of the "created_at" field in the mutation.
+func (m *ApiKeyMutation) 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 ApiKey entity.
+// If the ApiKey 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 *ApiKeyMutation) 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 *ApiKeyMutation) ResetCreatedAt() {
+	m.created_at = nil
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (m *ApiKeyMutation) SetUpdatedAt(t time.Time) {
+	m.updated_at = &t
+}
+
+// UpdatedAt returns the value of the "updated_at" field in the mutation.
+func (m *ApiKeyMutation) 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 ApiKey entity.
+// If the ApiKey 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 *ApiKeyMutation) 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 *ApiKeyMutation) ResetUpdatedAt() {
+	m.updated_at = nil
+}
+
+// SetDeletedAt sets the "deleted_at" field.
+func (m *ApiKeyMutation) SetDeletedAt(t time.Time) {
+	m.deleted_at = &t
+}
+
+// DeletedAt returns the value of the "deleted_at" field in the mutation.
+func (m *ApiKeyMutation) 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 ApiKey entity.
+// If the ApiKey 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 *ApiKeyMutation) 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 *ApiKeyMutation) ClearDeletedAt() {
+	m.deleted_at = nil
+	m.clearedFields[apikey.FieldDeletedAt] = struct{}{}
+}
+
+// DeletedAtCleared returns if the "deleted_at" field was cleared in this mutation.
+func (m *ApiKeyMutation) DeletedAtCleared() bool {
+	_, ok := m.clearedFields[apikey.FieldDeletedAt]
+	return ok
+}
+
+// ResetDeletedAt resets all changes to the "deleted_at" field.
+func (m *ApiKeyMutation) ResetDeletedAt() {
+	m.deleted_at = nil
+	delete(m.clearedFields, apikey.FieldDeletedAt)
+}
+
+// SetTitle sets the "title" field.
+func (m *ApiKeyMutation) SetTitle(s string) {
+	m.title = &s
+}
+
+// Title returns the value of the "title" field in the mutation.
+func (m *ApiKeyMutation) Title() (r string, exists bool) {
+	v := m.title
+	if v == nil {
+		return
+	}
+	return *v, true
+}
+
+// OldTitle returns the old "title" field's value of the ApiKey entity.
+// If the ApiKey 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 *ApiKeyMutation) OldTitle(ctx context.Context) (v string, err error) {
+	if !m.op.Is(OpUpdateOne) {
+		return v, errors.New("OldTitle is only allowed on UpdateOne operations")
+	}
+	if m.id == nil || m.oldValue == nil {
+		return v, errors.New("OldTitle requires an ID field in the mutation")
+	}
+	oldValue, err := m.oldValue(ctx)
+	if err != nil {
+		return v, fmt.Errorf("querying old value for OldTitle: %w", err)
+	}
+	return oldValue.Title, nil
+}
+
+// ClearTitle clears the value of the "title" field.
+func (m *ApiKeyMutation) ClearTitle() {
+	m.title = nil
+	m.clearedFields[apikey.FieldTitle] = struct{}{}
+}
+
+// TitleCleared returns if the "title" field was cleared in this mutation.
+func (m *ApiKeyMutation) TitleCleared() bool {
+	_, ok := m.clearedFields[apikey.FieldTitle]
+	return ok
+}
+
+// ResetTitle resets all changes to the "title" field.
+func (m *ApiKeyMutation) ResetTitle() {
+	m.title = nil
+	delete(m.clearedFields, apikey.FieldTitle)
+}
+
+// SetKey sets the "key" field.
+func (m *ApiKeyMutation) SetKey(s string) {
+	m.key = &s
+}
+
+// Key returns the value of the "key" field in the mutation.
+func (m *ApiKeyMutation) Key() (r string, exists bool) {
+	v := m.key
+	if v == nil {
+		return
+	}
+	return *v, true
+}
+
+// OldKey returns the old "key" field's value of the ApiKey entity.
+// If the ApiKey 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 *ApiKeyMutation) OldKey(ctx context.Context) (v string, err error) {
+	if !m.op.Is(OpUpdateOne) {
+		return v, errors.New("OldKey is only allowed on UpdateOne operations")
+	}
+	if m.id == nil || m.oldValue == nil {
+		return v, errors.New("OldKey requires an ID field in the mutation")
+	}
+	oldValue, err := m.oldValue(ctx)
+	if err != nil {
+		return v, fmt.Errorf("querying old value for OldKey: %w", err)
+	}
+	return oldValue.Key, nil
+}
+
+// ClearKey clears the value of the "key" field.
+func (m *ApiKeyMutation) ClearKey() {
+	m.key = nil
+	m.clearedFields[apikey.FieldKey] = struct{}{}
+}
+
+// KeyCleared returns if the "key" field was cleared in this mutation.
+func (m *ApiKeyMutation) KeyCleared() bool {
+	_, ok := m.clearedFields[apikey.FieldKey]
+	return ok
+}
+
+// ResetKey resets all changes to the "key" field.
+func (m *ApiKeyMutation) ResetKey() {
+	m.key = nil
+	delete(m.clearedFields, apikey.FieldKey)
+}
+
+// SetOrganizationID sets the "organization_id" field.
+func (m *ApiKeyMutation) SetOrganizationID(u uint64) {
+	m.organization_id = &u
+	m.addorganization_id = nil
+}
+
+// OrganizationID returns the value of the "organization_id" field in the mutation.
+func (m *ApiKeyMutation) OrganizationID() (r uint64, exists bool) {
+	v := m.organization_id
+	if v == nil {
+		return
+	}
+	return *v, true
+}
+
+// OldOrganizationID returns the old "organization_id" field's value of the ApiKey entity.
+// If the ApiKey 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 *ApiKeyMutation) OldOrganizationID(ctx context.Context) (v uint64, err error) {
+	if !m.op.Is(OpUpdateOne) {
+		return v, errors.New("OldOrganizationID is only allowed on UpdateOne operations")
+	}
+	if m.id == nil || m.oldValue == nil {
+		return v, errors.New("OldOrganizationID requires an ID field in the mutation")
+	}
+	oldValue, err := m.oldValue(ctx)
+	if err != nil {
+		return v, fmt.Errorf("querying old value for OldOrganizationID: %w", err)
+	}
+	return oldValue.OrganizationID, nil
+}
+
+// AddOrganizationID adds u to the "organization_id" field.
+func (m *ApiKeyMutation) AddOrganizationID(u int64) {
+	if m.addorganization_id != nil {
+		*m.addorganization_id += u
+	} else {
+		m.addorganization_id = &u
+	}
+}
+
+// AddedOrganizationID returns the value that was added to the "organization_id" field in this mutation.
+func (m *ApiKeyMutation) AddedOrganizationID() (r int64, exists bool) {
+	v := m.addorganization_id
+	if v == nil {
+		return
+	}
+	return *v, true
+}
+
+// ResetOrganizationID resets all changes to the "organization_id" field.
+func (m *ApiKeyMutation) ResetOrganizationID() {
+	m.organization_id = nil
+	m.addorganization_id = nil
+}
+
+// SetAgentID sets the "agent_id" field.
+func (m *ApiKeyMutation) SetAgentID(u uint64) {
+	m.agent = &u
+}
+
+// AgentID returns the value of the "agent_id" field in the mutation.
+func (m *ApiKeyMutation) AgentID() (r uint64, exists bool) {
+	v := m.agent
+	if v == nil {
+		return
+	}
+	return *v, true
+}
+
+// OldAgentID returns the old "agent_id" field's value of the ApiKey entity.
+// If the ApiKey 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 *ApiKeyMutation) OldAgentID(ctx context.Context) (v uint64, err error) {
+	if !m.op.Is(OpUpdateOne) {
+		return v, errors.New("OldAgentID is only allowed on UpdateOne operations")
+	}
+	if m.id == nil || m.oldValue == nil {
+		return v, errors.New("OldAgentID requires an ID field in the mutation")
+	}
+	oldValue, err := m.oldValue(ctx)
+	if err != nil {
+		return v, fmt.Errorf("querying old value for OldAgentID: %w", err)
+	}
+	return oldValue.AgentID, nil
+}
+
+// ResetAgentID resets all changes to the "agent_id" field.
+func (m *ApiKeyMutation) ResetAgentID() {
+	m.agent = nil
+}
+
+// SetCustomAgentBase sets the "custom_agent_base" field.
+func (m *ApiKeyMutation) SetCustomAgentBase(s string) {
+	m.custom_agent_base = &s
+}
+
+// CustomAgentBase returns the value of the "custom_agent_base" field in the mutation.
+func (m *ApiKeyMutation) CustomAgentBase() (r string, exists bool) {
+	v := m.custom_agent_base
+	if v == nil {
+		return
+	}
+	return *v, true
+}
+
+// OldCustomAgentBase returns the old "custom_agent_base" field's value of the ApiKey entity.
+// If the ApiKey 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 *ApiKeyMutation) OldCustomAgentBase(ctx context.Context) (v string, err error) {
+	if !m.op.Is(OpUpdateOne) {
+		return v, errors.New("OldCustomAgentBase is only allowed on UpdateOne operations")
+	}
+	if m.id == nil || m.oldValue == nil {
+		return v, errors.New("OldCustomAgentBase requires an ID field in the mutation")
+	}
+	oldValue, err := m.oldValue(ctx)
+	if err != nil {
+		return v, fmt.Errorf("querying old value for OldCustomAgentBase: %w", err)
+	}
+	return oldValue.CustomAgentBase, nil
+}
+
+// ClearCustomAgentBase clears the value of the "custom_agent_base" field.
+func (m *ApiKeyMutation) ClearCustomAgentBase() {
+	m.custom_agent_base = nil
+	m.clearedFields[apikey.FieldCustomAgentBase] = struct{}{}
+}
+
+// CustomAgentBaseCleared returns if the "custom_agent_base" field was cleared in this mutation.
+func (m *ApiKeyMutation) CustomAgentBaseCleared() bool {
+	_, ok := m.clearedFields[apikey.FieldCustomAgentBase]
+	return ok
+}
+
+// ResetCustomAgentBase resets all changes to the "custom_agent_base" field.
+func (m *ApiKeyMutation) ResetCustomAgentBase() {
+	m.custom_agent_base = nil
+	delete(m.clearedFields, apikey.FieldCustomAgentBase)
+}
+
+// SetCustomAgentKey sets the "custom_agent_key" field.
+func (m *ApiKeyMutation) SetCustomAgentKey(s string) {
+	m.custom_agent_key = &s
+}
+
+// CustomAgentKey returns the value of the "custom_agent_key" field in the mutation.
+func (m *ApiKeyMutation) CustomAgentKey() (r string, exists bool) {
+	v := m.custom_agent_key
+	if v == nil {
+		return
+	}
+	return *v, true
+}
+
+// OldCustomAgentKey returns the old "custom_agent_key" field's value of the ApiKey entity.
+// If the ApiKey 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 *ApiKeyMutation) OldCustomAgentKey(ctx context.Context) (v string, err error) {
+	if !m.op.Is(OpUpdateOne) {
+		return v, errors.New("OldCustomAgentKey is only allowed on UpdateOne operations")
+	}
+	if m.id == nil || m.oldValue == nil {
+		return v, errors.New("OldCustomAgentKey requires an ID field in the mutation")
+	}
+	oldValue, err := m.oldValue(ctx)
+	if err != nil {
+		return v, fmt.Errorf("querying old value for OldCustomAgentKey: %w", err)
+	}
+	return oldValue.CustomAgentKey, nil
+}
+
+// ClearCustomAgentKey clears the value of the "custom_agent_key" field.
+func (m *ApiKeyMutation) ClearCustomAgentKey() {
+	m.custom_agent_key = nil
+	m.clearedFields[apikey.FieldCustomAgentKey] = struct{}{}
+}
+
+// CustomAgentKeyCleared returns if the "custom_agent_key" field was cleared in this mutation.
+func (m *ApiKeyMutation) CustomAgentKeyCleared() bool {
+	_, ok := m.clearedFields[apikey.FieldCustomAgentKey]
+	return ok
+}
+
+// ResetCustomAgentKey resets all changes to the "custom_agent_key" field.
+func (m *ApiKeyMutation) ResetCustomAgentKey() {
+	m.custom_agent_key = nil
+	delete(m.clearedFields, apikey.FieldCustomAgentKey)
+}
+
+// SetOpenaiBase sets the "openai_base" field.
+func (m *ApiKeyMutation) SetOpenaiBase(s string) {
+	m.openai_base = &s
+}
+
+// OpenaiBase returns the value of the "openai_base" field in the mutation.
+func (m *ApiKeyMutation) OpenaiBase() (r string, exists bool) {
+	v := m.openai_base
+	if v == nil {
+		return
+	}
+	return *v, true
+}
+
+// OldOpenaiBase returns the old "openai_base" field's value of the ApiKey entity.
+// If the ApiKey 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 *ApiKeyMutation) OldOpenaiBase(ctx context.Context) (v string, err error) {
+	if !m.op.Is(OpUpdateOne) {
+		return v, errors.New("OldOpenaiBase is only allowed on UpdateOne operations")
+	}
+	if m.id == nil || m.oldValue == nil {
+		return v, errors.New("OldOpenaiBase requires an ID field in the mutation")
+	}
+	oldValue, err := m.oldValue(ctx)
+	if err != nil {
+		return v, fmt.Errorf("querying old value for OldOpenaiBase: %w", err)
+	}
+	return oldValue.OpenaiBase, nil
+}
+
+// ClearOpenaiBase clears the value of the "openai_base" field.
+func (m *ApiKeyMutation) ClearOpenaiBase() {
+	m.openai_base = nil
+	m.clearedFields[apikey.FieldOpenaiBase] = struct{}{}
+}
+
+// OpenaiBaseCleared returns if the "openai_base" field was cleared in this mutation.
+func (m *ApiKeyMutation) OpenaiBaseCleared() bool {
+	_, ok := m.clearedFields[apikey.FieldOpenaiBase]
+	return ok
+}
+
+// ResetOpenaiBase resets all changes to the "openai_base" field.
+func (m *ApiKeyMutation) ResetOpenaiBase() {
+	m.openai_base = nil
+	delete(m.clearedFields, apikey.FieldOpenaiBase)
+}
+
+// SetOpenaiKey sets the "openai_key" field.
+func (m *ApiKeyMutation) SetOpenaiKey(s string) {
+	m.openai_key = &s
+}
+
+// OpenaiKey returns the value of the "openai_key" field in the mutation.
+func (m *ApiKeyMutation) OpenaiKey() (r string, exists bool) {
+	v := m.openai_key
+	if v == nil {
+		return
+	}
+	return *v, true
+}
+
+// OldOpenaiKey returns the old "openai_key" field's value of the ApiKey entity.
+// If the ApiKey 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 *ApiKeyMutation) OldOpenaiKey(ctx context.Context) (v string, err error) {
+	if !m.op.Is(OpUpdateOne) {
+		return v, errors.New("OldOpenaiKey is only allowed on UpdateOne operations")
+	}
+	if m.id == nil || m.oldValue == nil {
+		return v, errors.New("OldOpenaiKey requires an ID field in the mutation")
+	}
+	oldValue, err := m.oldValue(ctx)
+	if err != nil {
+		return v, fmt.Errorf("querying old value for OldOpenaiKey: %w", err)
+	}
+	return oldValue.OpenaiKey, nil
+}
+
+// ClearOpenaiKey clears the value of the "openai_key" field.
+func (m *ApiKeyMutation) ClearOpenaiKey() {
+	m.openai_key = nil
+	m.clearedFields[apikey.FieldOpenaiKey] = struct{}{}
+}
+
+// OpenaiKeyCleared returns if the "openai_key" field was cleared in this mutation.
+func (m *ApiKeyMutation) OpenaiKeyCleared() bool {
+	_, ok := m.clearedFields[apikey.FieldOpenaiKey]
+	return ok
+}
+
+// ResetOpenaiKey resets all changes to the "openai_key" field.
+func (m *ApiKeyMutation) ResetOpenaiKey() {
+	m.openai_key = nil
+	delete(m.clearedFields, apikey.FieldOpenaiKey)
+}
+
+// ClearAgent clears the "agent" edge to the Agent entity.
+func (m *ApiKeyMutation) ClearAgent() {
+	m.clearedagent = true
+	m.clearedFields[apikey.FieldAgentID] = struct{}{}
+}
+
+// AgentCleared reports if the "agent" edge to the Agent entity was cleared.
+func (m *ApiKeyMutation) AgentCleared() bool {
+	return m.clearedagent
+}
+
+// AgentIDs returns the "agent" edge IDs in the mutation.
+// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use
+// AgentID instead. It exists only for internal usage by the builders.
+func (m *ApiKeyMutation) AgentIDs() (ids []uint64) {
+	if id := m.agent; id != nil {
+		ids = append(ids, *id)
+	}
+	return
+}
+
+// ResetAgent resets all changes to the "agent" edge.
+func (m *ApiKeyMutation) ResetAgent() {
+	m.agent = nil
+	m.clearedagent = false
+}
+
+// Where appends a list predicates to the ApiKeyMutation builder.
+func (m *ApiKeyMutation) Where(ps ...predicate.ApiKey) {
+	m.predicates = append(m.predicates, ps...)
+}
+
+// WhereP appends storage-level predicates to the ApiKeyMutation builder. Using this method,
+// users can use type-assertion to append predicates that do not depend on any generated package.
+func (m *ApiKeyMutation) WhereP(ps ...func(*sql.Selector)) {
+	p := make([]predicate.ApiKey, len(ps))
+	for i := range ps {
+		p[i] = ps[i]
+	}
+	m.Where(p...)
+}
+
+// Op returns the operation name.
+func (m *ApiKeyMutation) Op() Op {
+	return m.op
+}
+
+// SetOp allows setting the mutation operation.
+func (m *ApiKeyMutation) SetOp(op Op) {
+	m.op = op
+}
+
+// Type returns the node type of this mutation (ApiKey).
+func (m *ApiKeyMutation) 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 *ApiKeyMutation) Fields() []string {
+	fields := make([]string, 0, 11)
+	if m.created_at != nil {
+		fields = append(fields, apikey.FieldCreatedAt)
+	}
+	if m.updated_at != nil {
+		fields = append(fields, apikey.FieldUpdatedAt)
+	}
+	if m.deleted_at != nil {
+		fields = append(fields, apikey.FieldDeletedAt)
+	}
+	if m.title != nil {
+		fields = append(fields, apikey.FieldTitle)
+	}
+	if m.key != nil {
+		fields = append(fields, apikey.FieldKey)
+	}
+	if m.organization_id != nil {
+		fields = append(fields, apikey.FieldOrganizationID)
+	}
+	if m.agent != nil {
+		fields = append(fields, apikey.FieldAgentID)
+	}
+	if m.custom_agent_base != nil {
+		fields = append(fields, apikey.FieldCustomAgentBase)
+	}
+	if m.custom_agent_key != nil {
+		fields = append(fields, apikey.FieldCustomAgentKey)
+	}
+	if m.openai_base != nil {
+		fields = append(fields, apikey.FieldOpenaiBase)
+	}
+	if m.openai_key != nil {
+		fields = append(fields, apikey.FieldOpenaiKey)
+	}
+	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 *ApiKeyMutation) Field(name string) (ent.Value, bool) {
+	switch name {
+	case apikey.FieldCreatedAt:
+		return m.CreatedAt()
+	case apikey.FieldUpdatedAt:
+		return m.UpdatedAt()
+	case apikey.FieldDeletedAt:
+		return m.DeletedAt()
+	case apikey.FieldTitle:
+		return m.Title()
+	case apikey.FieldKey:
+		return m.Key()
+	case apikey.FieldOrganizationID:
+		return m.OrganizationID()
+	case apikey.FieldAgentID:
+		return m.AgentID()
+	case apikey.FieldCustomAgentBase:
+		return m.CustomAgentBase()
+	case apikey.FieldCustomAgentKey:
+		return m.CustomAgentKey()
+	case apikey.FieldOpenaiBase:
+		return m.OpenaiBase()
+	case apikey.FieldOpenaiKey:
+		return m.OpenaiKey()
+	}
+	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 *ApiKeyMutation) OldField(ctx context.Context, name string) (ent.Value, error) {
+	switch name {
+	case apikey.FieldCreatedAt:
+		return m.OldCreatedAt(ctx)
+	case apikey.FieldUpdatedAt:
+		return m.OldUpdatedAt(ctx)
+	case apikey.FieldDeletedAt:
+		return m.OldDeletedAt(ctx)
+	case apikey.FieldTitle:
+		return m.OldTitle(ctx)
+	case apikey.FieldKey:
+		return m.OldKey(ctx)
+	case apikey.FieldOrganizationID:
+		return m.OldOrganizationID(ctx)
+	case apikey.FieldAgentID:
+		return m.OldAgentID(ctx)
+	case apikey.FieldCustomAgentBase:
+		return m.OldCustomAgentBase(ctx)
+	case apikey.FieldCustomAgentKey:
+		return m.OldCustomAgentKey(ctx)
+	case apikey.FieldOpenaiBase:
+		return m.OldOpenaiBase(ctx)
+	case apikey.FieldOpenaiKey:
+		return m.OldOpenaiKey(ctx)
+	}
+	return nil, fmt.Errorf("unknown ApiKey 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 *ApiKeyMutation) SetField(name string, value ent.Value) error {
+	switch name {
+	case apikey.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 apikey.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 apikey.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 apikey.FieldTitle:
+		v, ok := value.(string)
+		if !ok {
+			return fmt.Errorf("unexpected type %T for field %s", value, name)
+		}
+		m.SetTitle(v)
+		return nil
+	case apikey.FieldKey:
+		v, ok := value.(string)
+		if !ok {
+			return fmt.Errorf("unexpected type %T for field %s", value, name)
+		}
+		m.SetKey(v)
+		return nil
+	case apikey.FieldOrganizationID:
+		v, ok := value.(uint64)
+		if !ok {
+			return fmt.Errorf("unexpected type %T for field %s", value, name)
+		}
+		m.SetOrganizationID(v)
+		return nil
+	case apikey.FieldAgentID:
+		v, ok := value.(uint64)
+		if !ok {
+			return fmt.Errorf("unexpected type %T for field %s", value, name)
+		}
+		m.SetAgentID(v)
+		return nil
+	case apikey.FieldCustomAgentBase:
+		v, ok := value.(string)
+		if !ok {
+			return fmt.Errorf("unexpected type %T for field %s", value, name)
+		}
+		m.SetCustomAgentBase(v)
+		return nil
+	case apikey.FieldCustomAgentKey:
+		v, ok := value.(string)
+		if !ok {
+			return fmt.Errorf("unexpected type %T for field %s", value, name)
+		}
+		m.SetCustomAgentKey(v)
+		return nil
+	case apikey.FieldOpenaiBase:
+		v, ok := value.(string)
+		if !ok {
+			return fmt.Errorf("unexpected type %T for field %s", value, name)
+		}
+		m.SetOpenaiBase(v)
+		return nil
+	case apikey.FieldOpenaiKey:
+		v, ok := value.(string)
+		if !ok {
+			return fmt.Errorf("unexpected type %T for field %s", value, name)
+		}
+		m.SetOpenaiKey(v)
+		return nil
+	}
+	return fmt.Errorf("unknown ApiKey field %s", name)
+}
+
+// AddedFields returns all numeric fields that were incremented/decremented during
+// this mutation.
+func (m *ApiKeyMutation) AddedFields() []string {
+	var fields []string
+	if m.addorganization_id != nil {
+		fields = append(fields, apikey.FieldOrganizationID)
+	}
+	return fields
+}
+
+// AddedField returns the numeric value that was incremented/decremented on a field
+// with the given name. The second boolean return value indicates that this field
+// was not set, or was not defined in the schema.
+func (m *ApiKeyMutation) AddedField(name string) (ent.Value, bool) {
+	switch name {
+	case apikey.FieldOrganizationID:
+		return m.AddedOrganizationID()
+	}
+	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 *ApiKeyMutation) AddField(name string, value ent.Value) error {
+	switch name {
+	case apikey.FieldOrganizationID:
+		v, ok := value.(int64)
+		if !ok {
+			return fmt.Errorf("unexpected type %T for field %s", value, name)
+		}
+		m.AddOrganizationID(v)
+		return nil
+	}
+	return fmt.Errorf("unknown ApiKey numeric field %s", name)
+}
+
+// ClearedFields returns all nullable fields that were cleared during this
+// mutation.
+func (m *ApiKeyMutation) ClearedFields() []string {
+	var fields []string
+	if m.FieldCleared(apikey.FieldDeletedAt) {
+		fields = append(fields, apikey.FieldDeletedAt)
+	}
+	if m.FieldCleared(apikey.FieldTitle) {
+		fields = append(fields, apikey.FieldTitle)
+	}
+	if m.FieldCleared(apikey.FieldKey) {
+		fields = append(fields, apikey.FieldKey)
+	}
+	if m.FieldCleared(apikey.FieldCustomAgentBase) {
+		fields = append(fields, apikey.FieldCustomAgentBase)
+	}
+	if m.FieldCleared(apikey.FieldCustomAgentKey) {
+		fields = append(fields, apikey.FieldCustomAgentKey)
+	}
+	if m.FieldCleared(apikey.FieldOpenaiBase) {
+		fields = append(fields, apikey.FieldOpenaiBase)
+	}
+	if m.FieldCleared(apikey.FieldOpenaiKey) {
+		fields = append(fields, apikey.FieldOpenaiKey)
+	}
+	return fields
+}
+
+// FieldCleared returns a boolean indicating if a field with the given name was
+// cleared in this mutation.
+func (m *ApiKeyMutation) 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 *ApiKeyMutation) ClearField(name string) error {
+	switch name {
+	case apikey.FieldDeletedAt:
+		m.ClearDeletedAt()
+		return nil
+	case apikey.FieldTitle:
+		m.ClearTitle()
+		return nil
+	case apikey.FieldKey:
+		m.ClearKey()
+		return nil
+	case apikey.FieldCustomAgentBase:
+		m.ClearCustomAgentBase()
+		return nil
+	case apikey.FieldCustomAgentKey:
+		m.ClearCustomAgentKey()
+		return nil
+	case apikey.FieldOpenaiBase:
+		m.ClearOpenaiBase()
+		return nil
+	case apikey.FieldOpenaiKey:
+		m.ClearOpenaiKey()
+		return nil
+	}
+	return fmt.Errorf("unknown ApiKey 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 *ApiKeyMutation) ResetField(name string) error {
+	switch name {
+	case apikey.FieldCreatedAt:
+		m.ResetCreatedAt()
+		return nil
+	case apikey.FieldUpdatedAt:
+		m.ResetUpdatedAt()
+		return nil
+	case apikey.FieldDeletedAt:
+		m.ResetDeletedAt()
+		return nil
+	case apikey.FieldTitle:
+		m.ResetTitle()
+		return nil
+	case apikey.FieldKey:
+		m.ResetKey()
+		return nil
+	case apikey.FieldOrganizationID:
+		m.ResetOrganizationID()
+		return nil
+	case apikey.FieldAgentID:
+		m.ResetAgentID()
+		return nil
+	case apikey.FieldCustomAgentBase:
+		m.ResetCustomAgentBase()
+		return nil
+	case apikey.FieldCustomAgentKey:
+		m.ResetCustomAgentKey()
+		return nil
+	case apikey.FieldOpenaiBase:
+		m.ResetOpenaiBase()
+		return nil
+	case apikey.FieldOpenaiKey:
+		m.ResetOpenaiKey()
+		return nil
+	}
+	return fmt.Errorf("unknown ApiKey field %s", name)
+}
+
+// AddedEdges returns all edge names that were set/added in this mutation.
+func (m *ApiKeyMutation) AddedEdges() []string {
+	edges := make([]string, 0, 1)
+	if m.agent != nil {
+		edges = append(edges, apikey.EdgeAgent)
+	}
+	return edges
+}
+
+// AddedIDs returns all IDs (to other nodes) that were added for the given edge
+// name in this mutation.
+func (m *ApiKeyMutation) AddedIDs(name string) []ent.Value {
+	switch name {
+	case apikey.EdgeAgent:
+		if id := m.agent; id != nil {
+			return []ent.Value{*id}
+		}
+	}
+	return nil
+}
+
+// RemovedEdges returns all edge names that were removed in this mutation.
+func (m *ApiKeyMutation) RemovedEdges() []string {
+	edges := make([]string, 0, 1)
+	return edges
+}
+
+// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with
+// the given name in this mutation.
+func (m *ApiKeyMutation) RemovedIDs(name string) []ent.Value {
+	return nil
+}
+
+// ClearedEdges returns all edge names that were cleared in this mutation.
+func (m *ApiKeyMutation) ClearedEdges() []string {
+	edges := make([]string, 0, 1)
+	if m.clearedagent {
+		edges = append(edges, apikey.EdgeAgent)
+	}
+	return edges
+}
+
+// EdgeCleared returns a boolean which indicates if the edge with the given name
+// was cleared in this mutation.
+func (m *ApiKeyMutation) EdgeCleared(name string) bool {
+	switch name {
+	case apikey.EdgeAgent:
+		return m.clearedagent
+	}
+	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 *ApiKeyMutation) ClearEdge(name string) error {
+	switch name {
+	case apikey.EdgeAgent:
+		m.ClearAgent()
+		return nil
+	}
+	return fmt.Errorf("unknown ApiKey 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 *ApiKeyMutation) ResetEdge(name string) error {
+	switch name {
+	case apikey.EdgeAgent:
+		m.ResetAgent()
+		return nil
+	}
+	return fmt.Errorf("unknown ApiKey edge %s", name)
+}
+
 // BatchMsgMutation represents an operation that mutates the BatchMsg nodes in the graph.
 type BatchMsgMutation struct {
 	config

+ 82 - 0
ent/pagination.go

@@ -9,6 +9,7 @@ import (
 	"wechat-api/ent/agentbase"
 	"wechat-api/ent/aliyunavatar"
 	"wechat-api/ent/allocagent"
+	"wechat-api/ent/apikey"
 	"wechat-api/ent/batchmsg"
 	"wechat-api/ent/category"
 	"wechat-api/ent/chatrecords"
@@ -419,6 +420,87 @@ func (aa *AllocAgentQuery) Page(
 	return ret, nil
 }
 
+type ApiKeyPager struct {
+	Order  apikey.OrderOption
+	Filter func(*ApiKeyQuery) (*ApiKeyQuery, error)
+}
+
+// ApiKeyPaginateOption enables pagination customization.
+type ApiKeyPaginateOption func(*ApiKeyPager)
+
+// DefaultApiKeyOrder is the default ordering of ApiKey.
+var DefaultApiKeyOrder = Desc(apikey.FieldID)
+
+func newApiKeyPager(opts []ApiKeyPaginateOption) (*ApiKeyPager, error) {
+	pager := &ApiKeyPager{}
+	for _, opt := range opts {
+		opt(pager)
+	}
+	if pager.Order == nil {
+		pager.Order = DefaultApiKeyOrder
+	}
+	return pager, nil
+}
+
+func (p *ApiKeyPager) ApplyFilter(query *ApiKeyQuery) (*ApiKeyQuery, error) {
+	if p.Filter != nil {
+		return p.Filter(query)
+	}
+	return query, nil
+}
+
+// ApiKeyPageList is ApiKey PageList result.
+type ApiKeyPageList struct {
+	List        []*ApiKey    `json:"list"`
+	PageDetails *PageDetails `json:"pageDetails"`
+}
+
+func (ak *ApiKeyQuery) Page(
+	ctx context.Context, pageNum uint64, pageSize uint64, opts ...ApiKeyPaginateOption,
+) (*ApiKeyPageList, error) {
+
+	pager, err := newApiKeyPager(opts)
+	if err != nil {
+		return nil, err
+	}
+
+	if ak, err = pager.ApplyFilter(ak); err != nil {
+		return nil, err
+	}
+
+	ret := &ApiKeyPageList{}
+
+	ret.PageDetails = &PageDetails{
+		Page: pageNum,
+		Size: pageSize,
+	}
+
+	query := ak.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 {
+		ak = ak.Order(pager.Order)
+	} else {
+		ak = ak.Order(DefaultApiKeyOrder)
+	}
+
+	ak = ak.Offset(int((pageNum - 1) * pageSize)).Limit(int(pageSize))
+	list, err := ak.All(ctx)
+	if err != nil {
+		return nil, err
+	}
+	ret.List = list
+
+	return ret, nil
+}
+
 type BatchMsgPager struct {
 	Order  batchmsg.OrderOption
 	Filter func(*BatchMsgQuery) (*BatchMsgQuery, error)

+ 3 - 0
ent/predicate/predicate.go

@@ -18,6 +18,9 @@ type AliyunAvatar func(*sql.Selector)
 // AllocAgent is the predicate function for allocagent builders.
 type AllocAgent func(*sql.Selector)
 
+// ApiKey is the predicate function for apikey builders.
+type ApiKey func(*sql.Selector)
+
 // BatchMsg is the predicate function for batchmsg builders.
 type BatchMsg func(*sql.Selector)
 

+ 52 - 0
ent/runtime/runtime.go

@@ -8,6 +8,7 @@ import (
 	"wechat-api/ent/agentbase"
 	"wechat-api/ent/aliyunavatar"
 	"wechat-api/ent/allocagent"
+	"wechat-api/ent/apikey"
 	"wechat-api/ent/batchmsg"
 	"wechat-api/ent/category"
 	"wechat-api/ent/chatrecords"
@@ -192,6 +193,57 @@ func init() {
 	allocagent.DefaultStatus = allocagentDescStatus.Default.(int)
 	// allocagent.StatusValidator is a validator for the "status" field. It is called by the builders before save.
 	allocagent.StatusValidator = allocagentDescStatus.Validators[0].(func(int) error)
+	apikeyMixin := schema.ApiKey{}.Mixin()
+	apikeyMixinHooks1 := apikeyMixin[1].Hooks()
+	apikey.Hooks[0] = apikeyMixinHooks1[0]
+	apikeyMixinInters1 := apikeyMixin[1].Interceptors()
+	apikey.Interceptors[0] = apikeyMixinInters1[0]
+	apikeyMixinFields0 := apikeyMixin[0].Fields()
+	_ = apikeyMixinFields0
+	apikeyFields := schema.ApiKey{}.Fields()
+	_ = apikeyFields
+	// apikeyDescCreatedAt is the schema descriptor for created_at field.
+	apikeyDescCreatedAt := apikeyMixinFields0[1].Descriptor()
+	// apikey.DefaultCreatedAt holds the default value on creation for the created_at field.
+	apikey.DefaultCreatedAt = apikeyDescCreatedAt.Default.(func() time.Time)
+	// apikeyDescUpdatedAt is the schema descriptor for updated_at field.
+	apikeyDescUpdatedAt := apikeyMixinFields0[2].Descriptor()
+	// apikey.DefaultUpdatedAt holds the default value on creation for the updated_at field.
+	apikey.DefaultUpdatedAt = apikeyDescUpdatedAt.Default.(func() time.Time)
+	// apikey.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field.
+	apikey.UpdateDefaultUpdatedAt = apikeyDescUpdatedAt.UpdateDefault.(func() time.Time)
+	// apikeyDescTitle is the schema descriptor for title field.
+	apikeyDescTitle := apikeyFields[0].Descriptor()
+	// apikey.DefaultTitle holds the default value on creation for the title field.
+	apikey.DefaultTitle = apikeyDescTitle.Default.(string)
+	// apikeyDescKey is the schema descriptor for key field.
+	apikeyDescKey := apikeyFields[1].Descriptor()
+	// apikey.DefaultKey holds the default value on creation for the key field.
+	apikey.DefaultKey = apikeyDescKey.Default.(string)
+	// apikeyDescOrganizationID is the schema descriptor for organization_id field.
+	apikeyDescOrganizationID := apikeyFields[2].Descriptor()
+	// apikey.DefaultOrganizationID holds the default value on creation for the organization_id field.
+	apikey.DefaultOrganizationID = apikeyDescOrganizationID.Default.(uint64)
+	// apikeyDescAgentID is the schema descriptor for agent_id field.
+	apikeyDescAgentID := apikeyFields[3].Descriptor()
+	// apikey.DefaultAgentID holds the default value on creation for the agent_id field.
+	apikey.DefaultAgentID = apikeyDescAgentID.Default.(uint64)
+	// apikeyDescCustomAgentBase is the schema descriptor for custom_agent_base field.
+	apikeyDescCustomAgentBase := apikeyFields[4].Descriptor()
+	// apikey.DefaultCustomAgentBase holds the default value on creation for the custom_agent_base field.
+	apikey.DefaultCustomAgentBase = apikeyDescCustomAgentBase.Default.(string)
+	// apikeyDescCustomAgentKey is the schema descriptor for custom_agent_key field.
+	apikeyDescCustomAgentKey := apikeyFields[5].Descriptor()
+	// apikey.DefaultCustomAgentKey holds the default value on creation for the custom_agent_key field.
+	apikey.DefaultCustomAgentKey = apikeyDescCustomAgentKey.Default.(string)
+	// apikeyDescOpenaiBase is the schema descriptor for openai_base field.
+	apikeyDescOpenaiBase := apikeyFields[6].Descriptor()
+	// apikey.DefaultOpenaiBase holds the default value on creation for the openai_base field.
+	apikey.DefaultOpenaiBase = apikeyDescOpenaiBase.Default.(string)
+	// apikeyDescOpenaiKey is the schema descriptor for openai_key field.
+	apikeyDescOpenaiKey := apikeyFields[7].Descriptor()
+	// apikey.DefaultOpenaiKey holds the default value on creation for the openai_key field.
+	apikey.DefaultOpenaiKey = apikeyDescOpenaiKey.Default.(string)
 	batchmsgMixin := schema.BatchMsg{}.Mixin()
 	batchmsgMixinHooks1 := batchmsgMixin[1].Hooks()
 	batchmsg.Hooks[0] = batchmsgMixinHooks1[0]

+ 1 - 0
ent/schema/agent.go

@@ -42,6 +42,7 @@ func (Agent) Edges() []ent.Edge {
 		edge.To("token_agent", Token.Type),
 		edge.To("wa_agent", Whatsapp.Type),
 		edge.To("xjs_agent", XunjiService.Type),
+		edge.To("key_agent", ApiKey.Type),
 	}
 }
 

+ 60 - 0
ent/schema/api_key.go

@@ -0,0 +1,60 @@
+package schema
+
+import (
+	"entgo.io/ent/schema/edge"
+	"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 ApiKey struct {
+	ent.Schema
+}
+
+func (ApiKey) Fields() []ent.Field {
+	return []ent.Field{
+		field.String("title").Optional().Default("").Comment("Title"),
+		field.String("key").Optional().Default("").Comment("Key"),
+		field.Uint64("organization_id").Default(0).Comment("租户ID"),
+		field.Uint64("agent_id").Default(0).Comment("智能体ID"),
+		field.String("custom_agent_base").Optional().Default("").Comment("定制agent服务地址"),
+		field.String("custom_agent_key").Optional().Default("").Comment("定制agent服务密钥"),
+		field.String("openai_base").Optional().Default("").Comment("大模型服务地址"),
+		field.String("openai_key").Optional().Default("").Comment("大模型服务密钥"),
+	}
+}
+
+func (ApiKey) Mixin() []ent.Mixin {
+	return []ent.Mixin{
+		mixins.IDMixin{},
+		localmixin.SoftDeleteMixin{},
+	}
+}
+
+func (ApiKey) Indexes() []ent.Index {
+	return []ent.Index{
+		index.Fields("key").Unique(),
+	}
+}
+
+func (ApiKey) Edges() []ent.Edge {
+	return []ent.Edge{
+		edge.From("agent", Agent.Type).
+			Ref("key_agent").
+			Unique().
+			Field("agent_id").
+			Required(),
+	}
+}
+
+func (ApiKey) Annotations() []schema.Annotation {
+	return []schema.Annotation{
+		entsql.WithComments(true),
+		entsql.Annotation{Table: "api_key"},
+	}
+}

+ 240 - 0
ent/set_not_nil.go

@@ -872,6 +872,246 @@ func (aa *AllocAgentCreate) SetNotNilStatus(value *int) *AllocAgentCreate {
 }
 
 // set field if value's pointer is not nil.
+func (ak *ApiKeyUpdate) SetNotNilUpdatedAt(value *time.Time) *ApiKeyUpdate {
+	if value != nil {
+		return ak.SetUpdatedAt(*value)
+	}
+	return ak
+}
+
+// set field if value's pointer is not nil.
+func (ak *ApiKeyUpdateOne) SetNotNilUpdatedAt(value *time.Time) *ApiKeyUpdateOne {
+	if value != nil {
+		return ak.SetUpdatedAt(*value)
+	}
+	return ak
+}
+
+// set field if value's pointer is not nil.
+func (ak *ApiKeyCreate) SetNotNilUpdatedAt(value *time.Time) *ApiKeyCreate {
+	if value != nil {
+		return ak.SetUpdatedAt(*value)
+	}
+	return ak
+}
+
+// set field if value's pointer is not nil.
+func (ak *ApiKeyUpdate) SetNotNilDeletedAt(value *time.Time) *ApiKeyUpdate {
+	if value != nil {
+		return ak.SetDeletedAt(*value)
+	}
+	return ak
+}
+
+// set field if value's pointer is not nil.
+func (ak *ApiKeyUpdateOne) SetNotNilDeletedAt(value *time.Time) *ApiKeyUpdateOne {
+	if value != nil {
+		return ak.SetDeletedAt(*value)
+	}
+	return ak
+}
+
+// set field if value's pointer is not nil.
+func (ak *ApiKeyCreate) SetNotNilDeletedAt(value *time.Time) *ApiKeyCreate {
+	if value != nil {
+		return ak.SetDeletedAt(*value)
+	}
+	return ak
+}
+
+// set field if value's pointer is not nil.
+func (ak *ApiKeyUpdate) SetNotNilTitle(value *string) *ApiKeyUpdate {
+	if value != nil {
+		return ak.SetTitle(*value)
+	}
+	return ak
+}
+
+// set field if value's pointer is not nil.
+func (ak *ApiKeyUpdateOne) SetNotNilTitle(value *string) *ApiKeyUpdateOne {
+	if value != nil {
+		return ak.SetTitle(*value)
+	}
+	return ak
+}
+
+// set field if value's pointer is not nil.
+func (ak *ApiKeyCreate) SetNotNilTitle(value *string) *ApiKeyCreate {
+	if value != nil {
+		return ak.SetTitle(*value)
+	}
+	return ak
+}
+
+// set field if value's pointer is not nil.
+func (ak *ApiKeyUpdate) SetNotNilKey(value *string) *ApiKeyUpdate {
+	if value != nil {
+		return ak.SetKey(*value)
+	}
+	return ak
+}
+
+// set field if value's pointer is not nil.
+func (ak *ApiKeyUpdateOne) SetNotNilKey(value *string) *ApiKeyUpdateOne {
+	if value != nil {
+		return ak.SetKey(*value)
+	}
+	return ak
+}
+
+// set field if value's pointer is not nil.
+func (ak *ApiKeyCreate) SetNotNilKey(value *string) *ApiKeyCreate {
+	if value != nil {
+		return ak.SetKey(*value)
+	}
+	return ak
+}
+
+// set field if value's pointer is not nil.
+func (ak *ApiKeyUpdate) SetNotNilOrganizationID(value *uint64) *ApiKeyUpdate {
+	if value != nil {
+		return ak.SetOrganizationID(*value)
+	}
+	return ak
+}
+
+// set field if value's pointer is not nil.
+func (ak *ApiKeyUpdateOne) SetNotNilOrganizationID(value *uint64) *ApiKeyUpdateOne {
+	if value != nil {
+		return ak.SetOrganizationID(*value)
+	}
+	return ak
+}
+
+// set field if value's pointer is not nil.
+func (ak *ApiKeyCreate) SetNotNilOrganizationID(value *uint64) *ApiKeyCreate {
+	if value != nil {
+		return ak.SetOrganizationID(*value)
+	}
+	return ak
+}
+
+// set field if value's pointer is not nil.
+func (ak *ApiKeyUpdate) SetNotNilAgentID(value *uint64) *ApiKeyUpdate {
+	if value != nil {
+		return ak.SetAgentID(*value)
+	}
+	return ak
+}
+
+// set field if value's pointer is not nil.
+func (ak *ApiKeyUpdateOne) SetNotNilAgentID(value *uint64) *ApiKeyUpdateOne {
+	if value != nil {
+		return ak.SetAgentID(*value)
+	}
+	return ak
+}
+
+// set field if value's pointer is not nil.
+func (ak *ApiKeyCreate) SetNotNilAgentID(value *uint64) *ApiKeyCreate {
+	if value != nil {
+		return ak.SetAgentID(*value)
+	}
+	return ak
+}
+
+// set field if value's pointer is not nil.
+func (ak *ApiKeyUpdate) SetNotNilCustomAgentBase(value *string) *ApiKeyUpdate {
+	if value != nil {
+		return ak.SetCustomAgentBase(*value)
+	}
+	return ak
+}
+
+// set field if value's pointer is not nil.
+func (ak *ApiKeyUpdateOne) SetNotNilCustomAgentBase(value *string) *ApiKeyUpdateOne {
+	if value != nil {
+		return ak.SetCustomAgentBase(*value)
+	}
+	return ak
+}
+
+// set field if value's pointer is not nil.
+func (ak *ApiKeyCreate) SetNotNilCustomAgentBase(value *string) *ApiKeyCreate {
+	if value != nil {
+		return ak.SetCustomAgentBase(*value)
+	}
+	return ak
+}
+
+// set field if value's pointer is not nil.
+func (ak *ApiKeyUpdate) SetNotNilCustomAgentKey(value *string) *ApiKeyUpdate {
+	if value != nil {
+		return ak.SetCustomAgentKey(*value)
+	}
+	return ak
+}
+
+// set field if value's pointer is not nil.
+func (ak *ApiKeyUpdateOne) SetNotNilCustomAgentKey(value *string) *ApiKeyUpdateOne {
+	if value != nil {
+		return ak.SetCustomAgentKey(*value)
+	}
+	return ak
+}
+
+// set field if value's pointer is not nil.
+func (ak *ApiKeyCreate) SetNotNilCustomAgentKey(value *string) *ApiKeyCreate {
+	if value != nil {
+		return ak.SetCustomAgentKey(*value)
+	}
+	return ak
+}
+
+// set field if value's pointer is not nil.
+func (ak *ApiKeyUpdate) SetNotNilOpenaiBase(value *string) *ApiKeyUpdate {
+	if value != nil {
+		return ak.SetOpenaiBase(*value)
+	}
+	return ak
+}
+
+// set field if value's pointer is not nil.
+func (ak *ApiKeyUpdateOne) SetNotNilOpenaiBase(value *string) *ApiKeyUpdateOne {
+	if value != nil {
+		return ak.SetOpenaiBase(*value)
+	}
+	return ak
+}
+
+// set field if value's pointer is not nil.
+func (ak *ApiKeyCreate) SetNotNilOpenaiBase(value *string) *ApiKeyCreate {
+	if value != nil {
+		return ak.SetOpenaiBase(*value)
+	}
+	return ak
+}
+
+// set field if value's pointer is not nil.
+func (ak *ApiKeyUpdate) SetNotNilOpenaiKey(value *string) *ApiKeyUpdate {
+	if value != nil {
+		return ak.SetOpenaiKey(*value)
+	}
+	return ak
+}
+
+// set field if value's pointer is not nil.
+func (ak *ApiKeyUpdateOne) SetNotNilOpenaiKey(value *string) *ApiKeyUpdateOne {
+	if value != nil {
+		return ak.SetOpenaiKey(*value)
+	}
+	return ak
+}
+
+// set field if value's pointer is not nil.
+func (ak *ApiKeyCreate) SetNotNilOpenaiKey(value *string) *ApiKeyCreate {
+	if value != nil {
+		return ak.SetOpenaiKey(*value)
+	}
+	return ak
+}
+
+// set field if value's pointer is not nil.
 func (bm *BatchMsgUpdate) SetNotNilUpdatedAt(value *time.Time) *BatchMsgUpdate {
 	if value != nil {
 		return bm.SetUpdatedAt(*value)

+ 3 - 0
ent/tx.go

@@ -22,6 +22,8 @@ type Tx struct {
 	AliyunAvatar *AliyunAvatarClient
 	// AllocAgent is the client for interacting with the AllocAgent builders.
 	AllocAgent *AllocAgentClient
+	// ApiKey is the client for interacting with the ApiKey builders.
+	ApiKey *ApiKeyClient
 	// BatchMsg is the client for interacting with the BatchMsg builders.
 	BatchMsg *BatchMsgClient
 	// Category is the client for interacting with the Category builders.
@@ -233,6 +235,7 @@ func (tx *Tx) init() {
 	tx.AgentBase = NewAgentBaseClient(tx.config)
 	tx.AliyunAvatar = NewAliyunAvatarClient(tx.config)
 	tx.AllocAgent = NewAllocAgentClient(tx.config)
+	tx.ApiKey = NewApiKeyClient(tx.config)
 	tx.BatchMsg = NewBatchMsgClient(tx.config)
 	tx.Category = NewCategoryClient(tx.config)
 	tx.ChatRecords = NewChatRecordsClient(tx.config)

+ 8 - 0
etc/wechat.yaml

@@ -96,3 +96,11 @@ WebSocket:
       Name: default
       Url: ws://chat.gkscrm.com:13088
 
+FastgptMongoConf:
+  Url: mongodb://myusername:mypassword@47.251.25.21:27017/?connect=direct
+  DBName: fastgpt
+
+WeChatWs:
+  -
+    Url: ws://chat.gkscrm.com:15088
+    Appid: wechat

+ 7 - 0
go.mod

@@ -31,6 +31,7 @@ require (
 	github.com/suyuan32/simple-admin-common v1.3.11
 	github.com/suyuan32/simple-admin-core v1.3.11
 	github.com/zeromicro/go-zero v1.6.3
+	go.mongodb.org/mongo-driver v1.14.0
 	golang.org/x/crypto v0.27.0
 	golang.org/x/text v0.18.0
 	google.golang.org/protobuf v1.35.2
@@ -83,6 +84,7 @@ require (
 	github.com/gogo/protobuf v1.3.2 // indirect
 	github.com/golang/mock v1.6.0 // indirect
 	github.com/golang/protobuf v1.5.4 // indirect
+	github.com/golang/snappy v0.0.4 // indirect
 	github.com/google/gnostic-models v0.6.8 // indirect
 	github.com/google/go-cmp v0.6.0 // indirect
 	github.com/google/gofuzz v1.2.0 // indirect
@@ -115,6 +117,7 @@ require (
 	github.com/mitchellh/go-wordwrap v1.0.1 // indirect
 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 	github.com/modern-go/reflect2 v1.0.2 // indirect
+	github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
 	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
 	github.com/nicksnyder/go-i18n/v2 v2.4.0 // indirect
 	github.com/onsi/ginkgo/v2 v2.17.1 // indirect
@@ -135,6 +138,10 @@ require (
 	github.com/tidwall/pretty v1.2.1 // indirect
 	github.com/tidwall/sjson v1.2.5 // indirect
 	github.com/tjfoc/gmsm v1.4.1 // indirect
+	github.com/xdg-go/pbkdf2 v1.0.0 // indirect
+	github.com/xdg-go/scram v1.1.2 // indirect
+	github.com/xdg-go/stringprep v1.0.4 // indirect
+	github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect
 	github.com/zclconf/go-cty v1.14.3 // indirect
 	go.etcd.io/etcd/api/v3 v3.5.12 // indirect
 	go.etcd.io/etcd/client/pkg/v3 v3.5.12 // indirect

+ 15 - 0
go.sum

@@ -275,6 +275,8 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS
 github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
 github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
 github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
+github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
+github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
@@ -496,6 +498,8 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
 github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
 github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0=
+github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
 github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
@@ -655,7 +659,15 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1
 github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
 github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
 github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
+github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
+github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
+github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
+github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
+github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
+github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
 github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
+github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a h1:fZHgsYlfvtyqToslyjUt3VOPF4J7aK/3MPcK7xp3PDk=
+github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4=
 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.1.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@@ -673,6 +685,8 @@ go.etcd.io/etcd/client/pkg/v3 v3.5.12 h1:EYDL6pWwyOsylrQyLp2w+HkQ46ATiOvoEdMarin
 go.etcd.io/etcd/client/pkg/v3 v3.5.12/go.mod h1:seTzl2d9APP8R5Y2hFL3NVlD6qC/dOT+3kvrqPyTas4=
 go.etcd.io/etcd/client/v3 v3.5.12 h1:v5lCPXn1pf1Uu3M4laUE2hp/geOTc5uPcYYsNe1lDxg=
 go.etcd.io/etcd/client/v3 v3.5.12/go.mod h1:tSbBCakoWmmddL+BKVAJHa9km+O/E+bumDe9mSbPiqw=
+go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80=
+go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c=
 go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
 go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
 go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
@@ -726,6 +740,7 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
 golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=

+ 1 - 0
internal/config/config.go

@@ -23,4 +23,5 @@ type Config struct {
 	Xiaoice            types.Xiaoice
 	WebSocket          []types.WebSocketConfig
 	OpenAI             types.OpenAI
+	FastgptMongoConf   types.MongoDB
 }

+ 44 - 0
internal/handler/api_key/create_api_key_handler.go

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

+ 44 - 0
internal/handler/api_key/delete_api_key_handler.go

@@ -0,0 +1,44 @@
+package api_key
+
+import (
+	"net/http"
+
+	"github.com/zeromicro/go-zero/rest/httpx"
+
+	"wechat-api/internal/logic/api_key"
+	"wechat-api/internal/svc"
+	"wechat-api/internal/types"
+)
+
+// swagger:route post /api_key/delete api_key DeleteApiKey
+//
+// Delete api_key information | 删除ApiKey信息
+//
+// Delete api_key information | 删除ApiKey信息
+//
+// Parameters:
+//  + name: body
+//    require: true
+//    in: body
+//    type: IDsReq
+//
+// Responses:
+//  200: BaseMsgResp
+
+func DeleteApiKeyHandler(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 := api_key.NewDeleteApiKeyLogic(r.Context(), svcCtx)
+		resp, err := l.DeleteApiKey(&req)
+		if err != nil {
+			httpx.ErrorCtx(r.Context(), w, err)
+		} else {
+			httpx.OkJsonCtx(r.Context(), w, resp)
+		}
+	}
+}

+ 44 - 0
internal/handler/api_key/get_api_key_list_handler.go

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

+ 44 - 0
internal/handler/api_key/update_api_key_handler.go

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

+ 44 - 0
internal/handler/department/create_department_handler.go

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

+ 44 - 0
internal/handler/department/delete_department_handler.go

@@ -0,0 +1,44 @@
+package department
+
+import (
+	"net/http"
+
+	"github.com/zeromicro/go-zero/rest/httpx"
+
+	"wechat-api/internal/logic/department"
+	"wechat-api/internal/svc"
+	"wechat-api/internal/types"
+)
+
+// swagger:route post /department/delete department DeleteDepartment
+//
+// Delete department information | 删除部门信息
+//
+// Delete department information | 删除部门信息
+//
+// Parameters:
+//  + name: body
+//    require: true
+//    in: body
+//    type: IDsReq
+//
+// Responses:
+//  200: BaseMsgResp
+
+func DeleteDepartmentHandler(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 := department.NewDeleteDepartmentLogic(r.Context(), svcCtx)
+		resp, err := l.DeleteDepartment(&req)
+		if err != nil {
+			httpx.ErrorCtx(r.Context(), w, err)
+		} else {
+			httpx.OkJsonCtx(r.Context(), w, resp)
+		}
+	}
+}

+ 44 - 0
internal/handler/department/get_department_by_id_handler.go

@@ -0,0 +1,44 @@
+package department
+
+import (
+	"net/http"
+
+	"github.com/zeromicro/go-zero/rest/httpx"
+
+	"wechat-api/internal/logic/department"
+	"wechat-api/internal/svc"
+	"wechat-api/internal/types"
+)
+
+// swagger:route post /department department GetDepartmentById
+//
+// Get Department by ID | 通过ID获取部门
+//
+// Get Department by ID | 通过ID获取部门
+//
+// Parameters:
+//  + name: body
+//    require: true
+//    in: body
+//    type: IDReq
+//
+// Responses:
+//  200: DepartmentInfoResp
+
+func GetDepartmentByIdHandler(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 := department.NewGetDepartmentByIdLogic(r.Context(), svcCtx)
+		resp, err := l.GetDepartmentById(&req)
+		if err != nil {
+			httpx.ErrorCtx(r.Context(), w, err)
+		} else {
+			httpx.OkJsonCtx(r.Context(), w, resp)
+		}
+	}
+}

+ 44 - 0
internal/handler/department/get_department_list_handler.go

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

+ 44 - 0
internal/handler/department/update_department_handler.go

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

+ 44 - 0
internal/handler/fastgpt/create_fastgpt_handler.go

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

+ 32 - 0
internal/handler/fastgpt/set_token_handler.go

@@ -0,0 +1,32 @@
+package fastgpt
+
+import (
+	"net/http"
+
+	"github.com/zeromicro/go-zero/rest/httpx"
+
+	"wechat-api/internal/logic/fastgpt"
+	"wechat-api/internal/svc"
+)
+
+// swagger:route get /api/fastgpt/set_token fastgpt SetToken
+//
+
+//
+
+//
+// Responses:
+//  200: BaseMsgResp
+
+func SetTokenHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+		username := r.URL.Query().Get("username")
+		l := fastgpt.NewSetTokenLogic(r.Context(), svcCtx, w)
+		resp, err := l.SetToken(username)
+		if err != nil {
+			httpx.ErrorCtx(r.Context(), w, err)
+		} else {
+			httpx.OkJsonCtx(r.Context(), w, resp)
+		}
+	}
+}

+ 86 - 0
internal/handler/routes.go

@@ -1,4 +1,6 @@
 // Code generated by goctl. DO NOT EDIT.
+// goctls v1.10.1
+
 package handler
 
 import (
@@ -18,6 +20,7 @@ import (
 	agent_base "wechat-api/internal/handler/agent_base"
 	aliyun_avatar "wechat-api/internal/handler/aliyun_avatar"
 	allocagent "wechat-api/internal/handler/allocagent"
+	api_key "wechat-api/internal/handler/api_key"
 	avatar "wechat-api/internal/handler/avatar"
 	base "wechat-api/internal/handler/base"
 	batch_msg "wechat-api/internal/handler/batch_msg"
@@ -28,8 +31,10 @@ import (
 	credit_balance "wechat-api/internal/handler/credit_balance"
 	credit_usage "wechat-api/internal/handler/credit_usage"
 	dashboard "wechat-api/internal/handler/dashboard"
+	department "wechat-api/internal/handler/department"
 	employee "wechat-api/internal/handler/employee"
 	employee_config "wechat-api/internal/handler/employee_config"
+	fastgpt "wechat-api/internal/handler/fastgpt"
 	label "wechat-api/internal/handler/label"
 	label_relationship "wechat-api/internal/handler/label_relationship"
 	label_tagging "wechat-api/internal/handler/label_tagging"
@@ -44,6 +49,7 @@ import (
 	whatsapp "wechat-api/internal/handler/whatsapp"
 	whatsapp_channel "wechat-api/internal/handler/whatsapp_channel"
 	work_experience "wechat-api/internal/handler/work_experience"
+	wp_wecom "wechat-api/internal/handler/wp_wecom"
 	wxcard "wechat-api/internal/handler/wxcard"
 	wxcarduser "wechat-api/internal/handler/wxcarduser"
 	wxcardvisit "wechat-api/internal/handler/wxcardvisit"
@@ -2041,6 +2047,21 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
 	)
 
 	server.AddRoutes(
+		[]rest.Route{
+			{
+				Method:  http.MethodPost,
+				Path:    "/wp/wecom/send_msg",
+				Handler: wp_wecom.SendMsgHandler(serverCtx),
+			},
+			{
+				Method:  http.MethodPost,
+				Path:    "/wp/wecom/send_msg_by_chan",
+				Handler: wp_wecom.SendMsgByChanHandler(serverCtx),
+			},
+		},
+	)
+
+	server.AddRoutes(
 		rest.WithMiddlewares(
 			[]rest.Middleware{serverCtx.Authority},
 			[]rest.Route{
@@ -2075,6 +2096,22 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
 	)
 
 	server.AddRoutes(
+		[]rest.Route{
+			{
+				Method:  http.MethodGet,
+				Path:    "/api/fastgpt/set_token",
+				Handler: fastgpt.SetTokenHandler(serverCtx),
+			},
+			{
+				Method:  http.MethodPost,
+				Path:    "/api/fastgpt/create",
+				Handler: fastgpt.CreateFastgptHandler(serverCtx),
+			},
+		},
+	)
+
+	server.AddRoutes(
+
 		rest.WithMiddlewares(
 			[]rest.Middleware{serverCtx.Authority},
 			[]rest.Route{
@@ -2103,6 +2140,55 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
 					Path:    "/xunji_service",
 					Handler: xunji_service.GetXunjiServiceByIdHandler(serverCtx),
 				},
+				{
+					Method:  http.MethodPost,
+					Path:    "/department/update",
+					Handler: department.UpdateDepartmentHandler(serverCtx),
+				},
+				{
+					Method:  http.MethodPost,
+					Path:    "/department/delete",
+					Handler: department.DeleteDepartmentHandler(serverCtx),
+				},
+				{
+					Method:  http.MethodPost,
+					Path:    "/department/list",
+					Handler: department.GetDepartmentListHandler(serverCtx),
+				},
+				{
+					Method:  http.MethodPost,
+					Path:    "/department",
+					Handler: department.GetDepartmentByIdHandler(serverCtx),
+				},
+			}...,
+		),
+		rest.WithJwt(serverCtx.Config.Auth.AccessSecret),
+	)
+
+	server.AddRoutes(
+		rest.WithMiddlewares(
+			[]rest.Middleware{serverCtx.Authority},
+			[]rest.Route{
+				{
+					Method:  http.MethodPost,
+					Path:    "/api_key/create",
+					Handler: api_key.CreateApiKeyHandler(serverCtx),
+				},
+				{
+					Method:  http.MethodPost,
+					Path:    "/api_key/update",
+					Handler: api_key.UpdateApiKeyHandler(serverCtx),
+				},
+				{
+					Method:  http.MethodPost,
+					Path:    "/api_key/delete",
+					Handler: api_key.DeleteApiKeyHandler(serverCtx),
+				},
+				{
+					Method:  http.MethodPost,
+					Path:    "/api_key/list",
+					Handler: api_key.GetApiKeyListHandler(serverCtx),
+				},
 			}...,
 		),
 		rest.WithJwt(serverCtx.Config.Auth.AccessSecret),

+ 44 - 0
internal/handler/wp_wecom/send_msg_by_chan_handler.go

@@ -0,0 +1,44 @@
+package wp_wecom
+
+import (
+	"net/http"
+
+	"github.com/zeromicro/go-zero/rest/httpx"
+
+	"wechat-api/internal/logic/wp_wecom"
+	"wechat-api/internal/svc"
+	"wechat-api/internal/types"
+)
+
+// swagger:route post /wp/wecom/send_msg_by_chan wp_wecom SendMsgByChan
+//
+
+//
+
+//
+// Parameters:
+//  + name: body
+//    require: true
+//    in: body
+//    type: WpWecomMsgReq
+//
+// Responses:
+//  200: BaseMsgResp
+
+func SendMsgByChanHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+		var req types.WpWecomMsgReq
+		if err := httpx.Parse(r, &req, true); err != nil {
+			httpx.ErrorCtx(r.Context(), w, err)
+			return
+		}
+
+		l := wp_wecom.NewSendMsgByChanLogic(r.Context(), svcCtx)
+		resp, err := l.SendMsgByChan(&req)
+		if err != nil {
+			httpx.ErrorCtx(r.Context(), w, err)
+		} else {
+			httpx.OkJsonCtx(r.Context(), w, resp)
+		}
+	}
+}

+ 44 - 0
internal/handler/wp_wecom/send_msg_handler.go

@@ -0,0 +1,44 @@
+package wp_wecom
+
+import (
+	"net/http"
+
+	"github.com/zeromicro/go-zero/rest/httpx"
+
+	"wechat-api/internal/logic/wp_wecom"
+	"wechat-api/internal/svc"
+	"wechat-api/internal/types"
+)
+
+// swagger:route get /wp/wecom/send_msg wp_wecom SendMsg
+//
+
+//
+
+//
+// Parameters:
+//  + name: body
+//    require: true
+//    in: body
+//    type: WpWecomMsgReq
+//
+// Responses:
+//  200: BaseMsgResp
+
+func SendMsgHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+		var req types.WpWecomMsgReq
+		if err := httpx.Parse(r, &req, true); err != nil {
+			httpx.ErrorCtx(r.Context(), w, err)
+			return
+		}
+
+		l := wp_wecom.NewSendMsgLogic(r.Context(), svcCtx)
+		resp, err := l.SendMsg(&req)
+		if err != nil {
+			httpx.ErrorCtx(r.Context(), w, err)
+		} else {
+			httpx.OkJsonCtx(r.Context(), w, resp)
+		}
+	}
+}

+ 1 - 1
internal/logic/Wxhook/send_text_msg_logic.go

@@ -76,7 +76,7 @@ func (l *SendTextMsgLogic) SendTextMsg(req *types.SendTextMsgReq) (resp *types.B
 	err = hookClient.SendTextMsg(*req.Wxid, *req.Msg, wxInfo.Wxid)
 
 	if err != nil {
-		l.Error("发送微信文本消息失败", err)
+		l.Errorf("发送微信文本消息失败:%v\n", err)
 		return nil, err
 	}
 

+ 65 - 0
internal/logic/api_key/create_api_key_logic.go

@@ -0,0 +1,65 @@
+package api_key
+
+import (
+	"context"
+	"crypto/rand"
+	"encoding/base64"
+	"fmt"
+	"github.com/suyuan32/simple-admin-common/msg/errormsg"
+	"github.com/zeromicro/go-zero/core/errorx"
+	"wechat-api/internal/svc"
+	"wechat-api/internal/types"
+	"wechat-api/internal/utils/dberrorhandler"
+
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type CreateApiKeyLogic struct {
+	logx.Logger
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+}
+
+func NewCreateApiKeyLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateApiKeyLogic {
+	return &CreateApiKeyLogic{
+		Logger: logx.WithContext(ctx),
+		ctx:    ctx,
+		svcCtx: svcCtx}
+}
+
+func (l *CreateApiKeyLogic) CreateApiKey(req *types.ApiKeyInfo) (resp *types.BaseMsgResp, err error) {
+	organizationId := l.ctx.Value("organizationId").(uint64)
+	apiKey, err := GenerateAPIKey("sk-", 32)
+	if err != nil {
+		return nil, errorx.NewCodeInternalError("Failed to generate API key")
+	}
+
+	_, err = l.svcCtx.DB.ApiKey.Create().
+		SetTitle(*req.Title).
+		SetKey(apiKey).
+		SetOrganizationID(organizationId).
+		Save(l.ctx)
+
+	if err != nil {
+		return nil, dberrorhandler.DefaultEntError(l.Logger, err, req)
+	}
+	l.svcCtx.Rds.HSet(l.ctx, "api_key", apiKey, req)
+	return &types.BaseMsgResp{Msg: errormsg.CreateSuccess}, nil
+}
+
+// GenerateAPIKey generates a random API key with a specified prefix.
+func GenerateAPIKey(prefix string, length int) (string, error) {
+	randomBytes := make([]byte, length)
+	_, err := rand.Read(randomBytes)
+	if err != nil {
+		return "", err
+	}
+
+	// Use base64 to encode the random bytes to a string
+	encoded := base64.URLEncoding.EncodeToString(randomBytes)
+
+	// Trim the encoded string to the desired length and prepend the prefix
+	apiKey := fmt.Sprintf("%s%s", prefix, encoded[:length])
+
+	return apiKey, nil
+}

+ 36 - 0
internal/logic/api_key/delete_api_key_logic.go

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

+ 89 - 0
internal/logic/api_key/get_api_key_list_logic.go

@@ -0,0 +1,89 @@
+package api_key
+
+import (
+	"context"
+	"github.com/suyuan32/simple-admin-common/msg/errormsg"
+	"github.com/suyuan32/simple-admin-common/utils/pointy"
+	"wechat-api/ent/apikey"
+	"wechat-api/ent/predicate"
+	"wechat-api/internal/utils/dberrorhandler"
+
+	"wechat-api/internal/svc"
+	"wechat-api/internal/types"
+
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type GetApiKeyListLogic struct {
+	logx.Logger
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+}
+
+func NewGetApiKeyListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetApiKeyListLogic {
+	return &GetApiKeyListLogic{
+		Logger: logx.WithContext(ctx),
+		ctx:    ctx,
+		svcCtx: svcCtx}
+}
+
+func (l *GetApiKeyListLogic) GetApiKeyList(req *types.ApiKeyListReq) (*types.ApiKeyListResp, error) {
+	organizationId := l.ctx.Value("organizationId").(uint64)
+
+	var predicates []predicate.ApiKey
+	predicates = append(predicates, apikey.OrganizationIDEQ(organizationId))
+
+	data, err := l.svcCtx.DB.ApiKey.Query().Where(predicates...).WithAgent().Page(l.ctx, req.Page, req.PageSize)
+
+	if err != nil {
+		return nil, dberrorhandler.DefaultEntError(l.Logger, err, req)
+	}
+
+	resp := &types.ApiKeyListResp{}
+	resp.Msg = errormsg.Success
+	resp.Data.Total = data.PageDetails.Total
+
+	for _, v := range data.List {
+		var agent types.AgentInfo
+		if v.AgentID == 0 {
+			agentName := "定制 AI 角色"
+			agent = types.AgentInfo{
+				Name: &agentName,
+			}
+		} else {
+			if v.Edges.Agent != nil {
+				agent = types.AgentInfo{
+					BaseIDInfo: types.BaseIDInfo{
+						Id:        &v.AgentID,
+						CreatedAt: pointy.GetPointer(v.Edges.Agent.CreatedAt.UnixMilli()),
+						UpdatedAt: pointy.GetPointer(v.Edges.Agent.UpdatedAt.UnixMilli()),
+					},
+					Name:       &v.Edges.Agent.Name,
+					Role:       &v.Edges.Agent.Role,
+					Status:     &v.Edges.Agent.Status,
+					Background: &v.Edges.Agent.Background,
+					Examples:   &v.Edges.Agent.Examples,
+				}
+			}
+		}
+		resp.Data.Data = append(resp.Data.Data,
+			types.ApiKeyInfo{
+				BaseIDInfo: types.BaseIDInfo{
+					Id:        &v.ID,
+					CreatedAt: pointy.GetPointer(v.CreatedAt.UnixMilli()),
+					UpdatedAt: pointy.GetPointer(v.UpdatedAt.UnixMilli()),
+				},
+				Title:           &v.Title,
+				Key:             &v.Key,
+				OrganizationId:  &v.OrganizationID,
+				AgentId:         &v.AgentID,
+				AgentInfo:       &agent,
+				CustomAgentBase: &v.CustomAgentBase,
+				CustomAgentKey:  &v.CustomAgentKey,
+				OpenaiBase:      &v.OpenaiBase,
+				OpenaiKey:       &v.OpenaiKey,
+			})
+	}
+
+	return resp, nil
+}

+ 42 - 0
internal/logic/api_key/update_api_key_logic.go

@@ -0,0 +1,42 @@
+package api_key
+
+import (
+	"context"
+	"github.com/suyuan32/simple-admin-common/msg/errormsg"
+	"wechat-api/internal/utils/dberrorhandler"
+
+	"wechat-api/internal/svc"
+	"wechat-api/internal/types"
+
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type UpdateApiKeyLogic struct {
+	logx.Logger
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+}
+
+func NewUpdateApiKeyLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateApiKeyLogic {
+	return &UpdateApiKeyLogic{
+		Logger: logx.WithContext(ctx),
+		ctx:    ctx,
+		svcCtx: svcCtx}
+}
+
+func (l *UpdateApiKeyLogic) UpdateApiKey(req *types.ApiKeyInfo) (resp *types.BaseMsgResp, err error) {
+	err = l.svcCtx.DB.ApiKey.UpdateOneID(*req.Id).
+		SetNotNilTitle(req.Title).
+		SetNotNilAgentID(req.AgentId).
+		SetNotNilCustomAgentBase(req.CustomAgentBase).
+		SetNotNilCustomAgentKey(req.CustomAgentKey).
+		SetNotNilOpenaiBase(req.OpenaiBase).
+		SetNotNilOpenaiKey(req.OpenaiKey).
+		Exec(l.ctx)
+
+	if err != nil {
+		return nil, dberrorhandler.DefaultEntError(l.Logger, err, req)
+	}
+	l.svcCtx.Rds.Del(l.ctx, "api_key")
+	return &types.BaseMsgResp{Msg: errormsg.UpdateSuccess}, nil
+}

+ 96 - 0
internal/logic/department/create_department_logic.go

@@ -0,0 +1,96 @@
+package department
+
+import (
+	"context"
+	"fmt"
+	"github.com/suyuan32/simple-admin-common/msg/errormsg"
+	"github.com/suyuan32/simple-admin-core/rpc/types/core"
+	"github.com/zeromicro/go-zero/core/errorx"
+	"time"
+	"wechat-api/internal/svc"
+	"wechat-api/internal/types"
+	team_members "wechat-api/mongo_model/team_members"
+	teams "wechat-api/mongo_model/teams"
+	users "wechat-api/mongo_model/users"
+
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type CreateDepartmentLogic struct {
+	logx.Logger
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+}
+
+func NewCreateDepartmentLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateDepartmentLogic {
+	return &CreateDepartmentLogic{
+		Logger: logx.WithContext(ctx),
+		ctx:    ctx,
+		svcCtx: svcCtx}
+}
+
+func (l *CreateDepartmentLogic) CreateDepartment(req *types.DepartmentInfo) (resp *types.BaseMsgResp, err error) {
+	department_info, err := l.svcCtx.CoreRpc.CreateDepartment(l.ctx, &core.DepartmentInfo{
+		Id:        req.Id,
+		Sort:      req.Sort,
+		Name:      req.Name,
+		Ancestors: req.Ancestors,
+		Leader:    req.Leader,
+		Phone:     req.Phone,
+		Email:     req.Email,
+		Remark:    req.Remark,
+		ParentId:  req.ParentId,
+	})
+	if err != nil {
+		return nil, err
+	}
+
+	//创建 fastgpt 用户
+	user_info := &users.Users{
+		Status:        "active",
+		Username:      fmt.Sprintf("%d", department_info.Id),
+		Password:      "838b0ad79fa89e8b3a0cfdc6eab90ac24d20e30ca862855b93a4c736f8cdfedf",
+		Avatar:        "/icon/human.svg",
+		Balance:       int32(200000),
+		PromotionRate: int32(15),
+		Timezone:      "Asia/Shanghai",
+	}
+	err = l.svcCtx.MongoModel.UsersModel.Insert(context.TODO(), user_info)
+	if err != nil {
+		return nil, errorx.NewInvalidArgumentError("fastgpt create failed " + err.Error())
+	}
+
+	// 创建团队
+	teams_info := &teams.Teams{
+		Name:              *req.Name,
+		OwnerID:           user_info.ID,
+		DefaultPermission: int32(0),
+		Avatar:            "/icon/logo.svg",
+		CreateTime:        time.Date(2024, 7, 10, 12, 0, 18, 197000000, time.UTC),
+		Balance:           int32(999900000),
+	}
+
+	err = l.svcCtx.MongoModel.TeamsModel.Insert(context.TODO(), teams_info)
+	if err != nil {
+		return nil, errorx.NewInvalidArgumentError("fastgpt create failed " + err.Error())
+	}
+
+	// 创建团队关系
+	team_members_info := &team_members.TeamMembers{
+		TeamID:      teams_info.ID,
+		UserID:      user_info.ID,
+		Name:        "Owner",
+		Role:        "owner",
+		Status:      "active",
+		CreateTime:  time.Date(2024, 7, 10, 12, 0, 18, 197000000, time.UTC),
+		DefaultTeam: true,
+		Version:     int32(0),
+	}
+
+	err = l.svcCtx.MongoModel.TeamMembersModel.Insert(context.TODO(), team_members_info)
+	if err != nil {
+		return nil, errorx.NewInvalidArgumentError("fastgpt create failed " + err.Error())
+	}
+
+	return &types.BaseMsgResp{Msg: errormsg.Success}, nil
+}

+ 29 - 0
internal/logic/department/delete_department_logic.go

@@ -0,0 +1,29 @@
+package department
+
+import (
+	"context"
+
+	"wechat-api/internal/svc"
+	"wechat-api/internal/types"
+
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type DeleteDepartmentLogic struct {
+	logx.Logger
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+}
+
+func NewDeleteDepartmentLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteDepartmentLogic {
+	return &DeleteDepartmentLogic{
+		Logger: logx.WithContext(ctx),
+		ctx:    ctx,
+		svcCtx: svcCtx}
+}
+
+func (l *DeleteDepartmentLogic) DeleteDepartment(req *types.IDsReq) (resp *types.BaseMsgResp, err error) {
+	// todo: add your logic here and delete this line
+
+	return
+}

+ 29 - 0
internal/logic/department/get_department_by_id_logic.go

@@ -0,0 +1,29 @@
+package department
+
+import (
+	"context"
+
+	"wechat-api/internal/svc"
+	"wechat-api/internal/types"
+
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type GetDepartmentByIdLogic struct {
+	logx.Logger
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+}
+
+func NewGetDepartmentByIdLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetDepartmentByIdLogic {
+	return &GetDepartmentByIdLogic{
+		Logger: logx.WithContext(ctx),
+		ctx:    ctx,
+		svcCtx: svcCtx}
+}
+
+func (l *GetDepartmentByIdLogic) GetDepartmentById(req *types.IDReq) (resp *types.DepartmentInfoResp, err error) {
+	// todo: add your logic here and delete this line
+
+	return
+}

+ 29 - 0
internal/logic/department/get_department_list_logic.go

@@ -0,0 +1,29 @@
+package department
+
+import (
+	"context"
+
+	"wechat-api/internal/svc"
+	"wechat-api/internal/types"
+
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type GetDepartmentListLogic struct {
+	logx.Logger
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+}
+
+func NewGetDepartmentListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetDepartmentListLogic {
+	return &GetDepartmentListLogic{
+		Logger: logx.WithContext(ctx),
+		ctx:    ctx,
+		svcCtx: svcCtx}
+}
+
+func (l *GetDepartmentListLogic) GetDepartmentList(req *types.DepartmentListReq) (resp *types.DepartmentListResp, err error) {
+	// todo: add your logic here and delete this line
+
+	return
+}

+ 29 - 0
internal/logic/department/update_department_logic.go

@@ -0,0 +1,29 @@
+package department
+
+import (
+	"context"
+
+	"wechat-api/internal/svc"
+	"wechat-api/internal/types"
+
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type UpdateDepartmentLogic struct {
+	logx.Logger
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+}
+
+func NewUpdateDepartmentLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateDepartmentLogic {
+	return &UpdateDepartmentLogic{
+		Logger: logx.WithContext(ctx),
+		ctx:    ctx,
+		svcCtx: svcCtx}
+}
+
+func (l *UpdateDepartmentLogic) UpdateDepartment(req *types.DepartmentInfo) (resp *types.BaseMsgResp, err error) {
+	// todo: add your logic here and delete this line
+
+	return
+}

+ 80 - 0
internal/logic/fastgpt/create_fastgpt_logic.go

@@ -0,0 +1,80 @@
+package fastgpt
+
+import (
+	"context"
+	"github.com/suyuan32/simple-admin-common/msg/errormsg"
+	"github.com/zeromicro/go-zero/core/errorx"
+	"time"
+	team_members "wechat-api/mongo_model/team_members"
+	teams "wechat-api/mongo_model/teams"
+	users "wechat-api/mongo_model/users"
+
+	"wechat-api/internal/svc"
+	"wechat-api/internal/types"
+
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type CreateFastgptLogic struct {
+	logx.Logger
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+}
+
+func NewCreateFastgptLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateFastgptLogic {
+	return &CreateFastgptLogic{
+		Logger: logx.WithContext(ctx),
+		ctx:    ctx,
+		svcCtx: svcCtx}
+}
+
+func (l *CreateFastgptLogic) CreateFastgpt(req *types.CreateInfo) (resp *types.BaseMsgResp, err error) {
+	//创建 fastgpt 用户
+	user_info := &users.Users{
+		Status:        "active",
+		Username:      req.UserName,
+		Password:      "838b0ad79fa89e8b3a0cfdc6eab90ac24d20e30ca862855b93a4c736f8cdfedf",
+		Avatar:        "/icon/human.svg",
+		Balance:       int32(200000),
+		PromotionRate: int32(15),
+		Timezone:      "Asia/Shanghai",
+	}
+	err = l.svcCtx.MongoModel.UsersModel.Insert(context.TODO(), user_info)
+	if err != nil {
+		return nil, errorx.NewInvalidArgumentError("fastgpt create failed " + err.Error())
+	}
+
+	// 创建团队
+	teams_info := &teams.Teams{
+		Name:              *req.Title,
+		OwnerID:           user_info.ID,
+		DefaultPermission: int32(0),
+		Avatar:            "/icon/logo.svg",
+		CreateTime:        time.Date(2024, 7, 10, 12, 0, 18, 197000000, time.UTC),
+		Balance:           int32(999900000),
+	}
+
+	err = l.svcCtx.MongoModel.TeamsModel.Insert(context.TODO(), teams_info)
+	if err != nil {
+		return nil, errorx.NewInvalidArgumentError("fastgpt create failed " + err.Error())
+	}
+
+	// 创建团队关系
+	team_members_info := &team_members.TeamMembers{
+		TeamID:      teams_info.ID,
+		UserID:      user_info.ID,
+		Name:        "Owner",
+		Role:        "owner",
+		Status:      "active",
+		CreateTime:  time.Date(2024, 7, 10, 12, 0, 18, 197000000, time.UTC),
+		DefaultTeam: true,
+		Version:     int32(0),
+	}
+
+	err = l.svcCtx.MongoModel.TeamMembersModel.Insert(context.TODO(), team_members_info)
+	if err != nil {
+		return nil, errorx.NewInvalidArgumentError("fastgpt create failed " + err.Error())
+	}
+
+	return &types.BaseMsgResp{Msg: errormsg.Success}, nil
+}

+ 149 - 0
internal/logic/fastgpt/set_token_logic.go

@@ -0,0 +1,149 @@
+package fastgpt
+
+import (
+	"bytes"
+	"context"
+	"encoding/json"
+	"fmt"
+	"github.com/golang-jwt/jwt/v5"
+	"github.com/suyuan32/simple-admin-core/rpc/types/core"
+	"net/http"
+	"strconv"
+
+	"github.com/zeromicro/go-zero/core/logx"
+	"wechat-api/internal/svc"
+	"wechat-api/internal/types"
+)
+
+type SetTokenLogic struct {
+	logx.Logger
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	rw     http.ResponseWriter
+}
+
+func NewSetTokenLogic(ctx context.Context, svcCtx *svc.ServiceContext, rw http.ResponseWriter) *SetTokenLogic {
+	return &SetTokenLogic{
+		Logger: logx.WithContext(ctx),
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		rw:     rw,
+	}
+}
+
+func (l *SetTokenLogic) SetToken(username string) (resp *types.BaseMsgResp, err error) {
+	claims, err := ParseJWT(username, l.svcCtx.Config.Auth.AccessSecret)
+	if err != nil {
+		return nil, fmt.Errorf("invalid token")
+	}
+	data, err := l.svcCtx.CoreRpc.GetUserById(context.TODO(), &core.UUIDReq{Id: claims.UserId})
+	token, err := l.getToken(strconv.FormatUint(*data.DepartmentId, 10))
+	if err != nil {
+		return nil, fmt.Errorf("invalid token")
+	}
+	//if err != nil {
+	//	return nil, err
+	//}
+	// 创建一个新的 Cookie
+	cookie := &http.Cookie{
+		Name:     "fastgpt_token",
+		Value:    token, // 假设 req.Token 是你要设置的 Cookie 值
+		Domain:   ".gkscrm.com",
+		SameSite: http.SameSiteNoneMode,
+		Secure:   true, // 如果 SameSite 设置为 None,必须设置 Secure 为 true
+		HttpOnly: false,
+		Path:     "/",
+	}
+
+	// 设置 Cookie 到响应中
+	http.SetCookie(l.rw, cookie)
+
+	// 返回响应消息
+	resp = &types.BaseMsgResp{
+		Code: 0,
+		Msg:  "Cookie set successfully",
+	}
+
+	return
+}
+
+func (l *SetTokenLogic) getToken(username string) (string, error) {
+	// 设置请求的 URL 和请求体
+	url := "https://agent.gkscrm.com/api/support/user/account/loginByPassword"
+	payload := map[string]string{
+		"username": username,
+		"password": "578fd6dfa3f71a8fadf5dc60d0e7115881db4c36504f83c4a0f4422107162c36",
+	}
+
+	// 将请求体编码为 JSON
+	jsonPayload, err := json.Marshal(payload)
+	if err != nil {
+		return "", err
+	}
+
+	// 创建 HTTP 请求
+	req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonPayload))
+	if err != nil {
+		return "", err
+	}
+	req.Header.Set("Content-Type", "application/json")
+
+	// 发送请求
+	client := &http.Client{}
+	resp, err := client.Do(req)
+	if err != nil {
+		return "", err
+	}
+	defer resp.Body.Close()
+
+	// 检查响应状态码
+	if resp.StatusCode != http.StatusOK {
+		return "", fmt.Errorf("failed to login, status code: %d", resp.StatusCode)
+	}
+
+	// 解析响应体
+	var response map[string]interface{}
+	err = json.NewDecoder(resp.Body).Decode(&response)
+	if err != nil {
+		return "", err
+	}
+
+	// 提取 token
+	data, ok := response["data"].(map[string]interface{})
+	if !ok {
+		return "", fmt.Errorf("invalid response format")
+	}
+	token, ok := data["token"].(string)
+	if !ok {
+		return "", fmt.Errorf("token not found in response")
+	}
+
+	return token, nil
+}
+
+type Claims struct {
+	RoleId string `json:"roleId"`
+	UserId string `json:"userId"`
+	jwt.RegisteredClaims
+}
+
+func ParseJWT(tokenString, accessSecret string) (*Claims, error) {
+	claims := &Claims{}
+
+	token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
+		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
+			return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
+		}
+		return []byte(accessSecret), nil
+	})
+
+	if err != nil {
+		return nil, fmt.Errorf("invalid token")
+	}
+
+	if !token.Valid {
+		return nil, fmt.Errorf("invalid token")
+	}
+
+	return claims, nil
+}

+ 29 - 0
internal/logic/wp_wecom/send_msg_by_chan_logic.go

@@ -0,0 +1,29 @@
+package wp_wecom
+
+import (
+	"context"
+
+	"wechat-api/internal/svc"
+	"wechat-api/internal/types"
+
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type SendMsgByChanLogic struct {
+	logx.Logger
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+}
+
+func NewSendMsgByChanLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SendMsgByChanLogic {
+	return &SendMsgByChanLogic{
+		Logger: logx.WithContext(ctx),
+		ctx:    ctx,
+		svcCtx: svcCtx}
+}
+
+func (l *SendMsgByChanLogic) SendMsgByChan(req *types.WpWecomMsgReq) (resp *types.BaseMsgResp, err error) {
+	// todo: add your logic here and delete this line
+
+	return
+}

+ 44 - 0
internal/logic/wp_wecom/send_msg_logic.go

@@ -0,0 +1,44 @@
+package wp_wecom
+
+import (
+	"context"
+	"encoding/base64"
+	"encoding/json"
+
+	"wechat-api/internal/svc"
+	"wechat-api/internal/types"
+
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type SendMsgLogic struct {
+	logx.Logger
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+}
+
+func NewSendMsgLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SendMsgLogic {
+	return &SendMsgLogic{
+		Logger: logx.WithContext(ctx),
+		ctx:    ctx,
+		svcCtx: svcCtx}
+}
+
+func (l *SendMsgLogic) SendMsg(req *types.WpWecomMsgReq) (resp *types.BaseMsgResp, err error) {
+	data := map[string]interface{}{
+		"MsgType": "TalkToFriendTask",
+		"Content": map[string]interface{}{
+			"WxId":        req.WxId,
+			"ConvId":      req.ConvId,
+			"ContentType": req.ContentType,
+			"Content":     base64.StdEncoding.EncodeToString([]byte(*req.Content)),
+		},
+	}
+	jsonStr, err := json.Marshal(data)
+	err = l.svcCtx.WechatWs["wecom"].SendMsg([]byte(jsonStr))
+	if err != nil {
+		return nil, err
+	}
+
+	return
+}

+ 0 - 1
internal/pkg/wechat_ws/wechat_ws_client.go

@@ -236,7 +236,6 @@ func (c *WechatWsClient) ReadPump() {
 						}
 					}()
 				}
-
 			}
 		}
 

+ 12 - 7
internal/svc/service_context.go

@@ -2,24 +2,24 @@ package svc
 
 import (
 	"github.com/bwmarrin/snowflake"
+	"github.com/casbin/casbin/v2"
 	"github.com/redis/go-redis/v9"
 	"github.com/zeromicro/go-zero/core/collection"
 	"gorm.io/gorm"
 	"time"
 	"wechat-api/database"
 	"wechat-api/database/dao/wechat/query"
-	"wechat-api/internal/config"
-	"wechat-api/internal/middleware"
-	"wechat-api/internal/pkg/wechat_ws"
 
 	"github.com/suyuan32/simple-admin-core/rpc/coreclient"
 	"github.com/zeromicro/go-zero/core/logx"
-	"wechat-api/ent"
-	_ "wechat-api/ent/runtime"
-
-	"github.com/casbin/casbin/v2"
 	"github.com/zeromicro/go-zero/rest"
 	"github.com/zeromicro/go-zero/zrpc"
+	"wechat-api/ent"
+	_ "wechat-api/ent/runtime"
+	"wechat-api/internal/config"
+	"wechat-api/internal/middleware"
+	"wechat-api/internal/pkg/wechat_ws"
+	mongo_model "wechat-api/mongo_model"
 )
 
 type ServiceContext struct {
@@ -35,6 +35,7 @@ type ServiceContext struct {
 	WechatWs    map[string]*wechat_ws.WechatWsClient
 	Cache       *collection.Cache
 	NodeID      *snowflake.Node
+	MongoModel  *mongo_model.AllMongoModel
 }
 
 func NewServiceContext(c config.Config) *ServiceContext {
@@ -57,6 +58,9 @@ func NewServiceContext(c config.Config) *ServiceContext {
 	}
 	wechatQ := query.Use(wechatDb)
 
+	// 初始化 MongoDB 客户端
+	all_mongo_model := mongo_model.SetupMongoModel(c.FastgptMongoConf.Url, c.FastgptMongoConf.DBName)
+
 	coreRpc := coreclient.NewCore(zrpc.NewClientIfEnable(c.CoreRpc))
 
 	wechatWs := make(map[string]*wechat_ws.WechatWsClient)
@@ -99,5 +103,6 @@ func NewServiceContext(c config.Config) *ServiceContext {
 		WechatWs:    wechatWs,
 		Cache:       cache,
 		NodeID:      node,
+		MongoModel:  all_mongo_model,
 	}
 }

+ 6 - 0
internal/types/mongo.go

@@ -0,0 +1,6 @@
+package types
+
+type MongoDB struct {
+	Url    string `json:",env=MONGO_URL"`
+	DBName string `json:",default=simple_admin,env=MONGO_DBNAME"`
+}

+ 133 - 0
internal/types/types.go

@@ -4018,6 +4018,18 @@ type WhatsappChannelInfoResp struct {
 	Data WhatsappChannelInfo `json:"data"`
 }
 
+// swagger:model WpWecomMsgReq
+type WpWecomMsgReq struct {
+	// 机器人微信id
+	WxId *string `json:"wx_id"`
+	// 接收方微信 id
+	ConvId *string `json:"conv_id"`
+	// 消息类型
+	ContentType *string `json:"content_type"`
+	// 消息内容
+	Content *string `json:"content"`
+}
+
 // The data of xunji information | Xunji信息
 // swagger:model XunjiInfo
 type XunjiInfo struct {
@@ -4132,3 +4144,124 @@ type XunjiServiceInfoResp struct {
 	// XunjiService information | XunjiService数据
 	Data XunjiServiceInfo `json:"data"`
 }
+// swagger:model CreateInfo
+type CreateInfo struct {
+	// Translated Name | 展示名称
+	UserName string `json:"username"`
+	// Name | 部门名称
+	Title *string `json:"title"`
+}
+
+// The response data of department information | 部门信息
+// swagger:model DepartmentInfo
+type DepartmentInfo struct {
+	BaseIDInfo
+	// Translated Name | 展示名称
+	Trans string `json:"trans,optional"`
+	// Status | 状态
+	// max : 20
+	Status *uint32 `json:"status,optional" validate:"omitempty,lt=20"`
+	// Sort | 排序
+	// max : 10000
+	Sort *uint32 `json:"sort,optional" validate:"omitempty,lt=10000"`
+	// Name | 部门名称
+	// min length : 1
+	// max length : 50
+	Name *string `json:"name,optional" validate:"omitempty,min=1,max=50"`
+	// Ancestors | 父级部门列表
+	// max length : 200
+	Ancestors *string `json:"ancestors,optional" validate:"omitempty,max=200"`
+	// Leader | 部门负责人
+	// max length : 20
+	Leader *string `json:"leader,optional" validate:"omitempty,max=20"`
+	// Phone | 电话号码
+	// max length : 18
+	Phone *string `json:"phone,optional" validate:"omitempty,max=18"`
+	// Email | 邮箱
+	// max length : 70
+	Email *string `json:"email,optional" validate:"omitempty,max=70"`
+	// Remark | 备注
+	// max length : 200
+	Remark *string `json:"remark,optional" validate:"omitempty,max=200"`
+	// ParentId | 父级 ID
+	ParentId *uint64 `json:"parentId,optional"`
+}
+
+// The response data of department list | 部门列表数据
+// swagger:model DepartmentListResp
+type DepartmentListResp struct {
+	BaseDataInfo
+	// Department list data | 部门列表数据
+	Data DepartmentListInfo `json:"data"`
+}
+
+// Department list data | 部门列表数据
+// swagger:model DepartmentListInfo
+type DepartmentListInfo struct {
+	BaseListInfo
+	// The API list data | 部门列表数据
+	Data []DepartmentInfo `json:"data"`
+}
+
+// Get department list request params | 部门列表请求参数
+// swagger:model DepartmentListReq
+type DepartmentListReq struct {
+	PageInfo
+	// Name | 部门名称
+	// max length : 50
+	Name *string `json:"name,optional" validate:"omitempty,max=50"`
+	// Leader | 部门负责人
+	// max length : 20
+	Leader *string `json:"leader,optional" validate:"omitempty,max=20"`
+}
+
+// Department information response | 部门信息返回体
+// swagger:model DepartmentInfoResp
+type DepartmentInfoResp struct {
+	BaseDataInfo
+	// Department information | 部门数据
+	Data DepartmentInfo `json:"data"`
+}
+
+// The data of api_key information | ApiKey信息
+// swagger:model ApiKeyInfo
+type ApiKeyInfo struct {
+	BaseIDInfo
+	// Title
+	Title *string `json:"title,optional"`
+	// Key
+	Key *string `json:"key,optional"`
+	// 租户ID
+	OrganizationId  *uint64    `json:"organization_id,optional"`
+	AgentId         *uint64    `json:"agent_id,optional"`
+	AgentInfo       *AgentInfo `json:"agent_info,optional"`
+	CustomAgentBase *string    `json:"custom_agent_base,optional"`
+	CustomAgentKey  *string    `json:"custom_agent_key,optional"`
+	OpenaiBase      *string    `json:"openai_base,optional"`
+	OpenaiKey       *string    `json:"openai_key,optional"`
+}
+
+// The response data of api_key list | ApiKey列表数据
+// swagger:model ApiKeyListResp
+type ApiKeyListResp struct {
+	BaseDataInfo
+	// ApiKey list data | ApiKey列表数据
+	Data ApiKeyListInfo `json:"data"`
+}
+
+// ApiKey list data | ApiKey列表数据
+// swagger:model ApiKeyListInfo
+type ApiKeyListInfo struct {
+	BaseListInfo
+	// The API list data | ApiKey列表数据
+	Data []ApiKeyInfo `json:"data"`
+}
+
+// Get ApiKey list request params | ApiKey列表请求参数
+// swagger:model ApiKeyListReq
+type ApiKeyListReq struct {
+	PageInfo
+	// Key
+	Key            *string `json:"key,optional"`
+	OrganizationId *uint64 `json:"organization_id,optional"`
+}

+ 6 - 0
internal/types/ws.go

@@ -0,0 +1,6 @@
+package types
+
+type WsConfig struct {
+	Url   string
+	Appid string
+}

+ 21 - 0
mongo_model/allmodel.go

@@ -0,0 +1,21 @@
+package mongo_model
+
+import (
+	team_members "wechat-api/mongo_model/team_members"
+	teams "wechat-api/mongo_model/teams"
+	users "wechat-api/mongo_model/users"
+)
+
+type AllMongoModel struct {
+	UsersModel       users.UsersModel
+	TeamsModel       teams.TeamsModel
+	TeamMembersModel team_members.TeamMembersModel
+}
+
+func SetupMongoModel(url, db string) *AllMongoModel {
+	return &AllMongoModel{
+		UsersModel:       users.NewUsersModel(url, db, "users"),
+		TeamsModel:       teams.NewTeamsModel(url, db, "teams"),
+		TeamMembersModel: team_members.NewTeamMembersModel(url, db, "team_members"),
+	}
+}

+ 12 - 0
mongo_model/team_members/error.go

@@ -0,0 +1,12 @@
+package model
+
+import (
+	"errors"
+
+	"github.com/zeromicro/go-zero/core/stores/mon"
+)
+
+var (
+	ErrNotFound        = mon.ErrNotFound
+	ErrInvalidObjectId = errors.New("invalid objectId")
+)

+ 25 - 0
mongo_model/team_members/teammembersmodel.go

@@ -0,0 +1,25 @@
+package model
+
+import "github.com/zeromicro/go-zero/core/stores/mon"
+
+var _ TeamMembersModel = (*customTeamMembersModel)(nil)
+
+type (
+	// TeamMembersModel is an interface to be customized, add more methods here,
+	// and implement the added methods in customTeamMembersModel.
+	TeamMembersModel interface {
+		teamMembersModel
+	}
+
+	customTeamMembersModel struct {
+		*defaultTeamMembersModel
+	}
+)
+
+// NewTeamMembersModel returns a model for the mongo_model.
+func NewTeamMembersModel(url, db, collection string) TeamMembersModel {
+	conn := mon.MustNewModel(url, db, collection)
+	return &customTeamMembersModel{
+		defaultTeamMembersModel: newDefaultTeamMembersModel(conn),
+	}
+}

+ 73 - 0
mongo_model/team_members/teammembersmodelgen.go

@@ -0,0 +1,73 @@
+// Code generated by goctl. DO NOT EDIT.
+// goctl 1.8.1
+
+package model
+
+import (
+	"context"
+	"time"
+
+	"github.com/zeromicro/go-zero/core/stores/mon"
+	"go.mongodb.org/mongo-driver/bson"
+	"go.mongodb.org/mongo-driver/bson/primitive"
+	"go.mongodb.org/mongo-driver/mongo"
+)
+
+type teamMembersModel interface {
+	Insert(ctx context.Context, data *TeamMembers) error
+	FindOne(ctx context.Context, id string) (*TeamMembers, error)
+	Update(ctx context.Context, data *TeamMembers) (*mongo.UpdateResult, error)
+	Delete(ctx context.Context, id string) (int64, error)
+}
+
+type defaultTeamMembersModel struct {
+	conn *mon.Model
+}
+
+func newDefaultTeamMembersModel(conn *mon.Model) *defaultTeamMembersModel {
+	return &defaultTeamMembersModel{conn: conn}
+}
+
+func (m *defaultTeamMembersModel) Insert(ctx context.Context, data *TeamMembers) error {
+	if data.ID.IsZero() {
+		data.ID = primitive.NewObjectID()
+		data.CreateTime = time.Now()
+	}
+
+	_, err := m.conn.InsertOne(ctx, data)
+	return err
+}
+
+func (m *defaultTeamMembersModel) FindOne(ctx context.Context, id string) (*TeamMembers, error) {
+	oid, err := primitive.ObjectIDFromHex(id)
+	if err != nil {
+		return nil, ErrInvalidObjectId
+	}
+
+	var data TeamMembers
+
+	err = m.conn.FindOne(ctx, &data, bson.M{"_id": oid})
+	switch err {
+	case nil:
+		return &data, nil
+	case mon.ErrNotFound:
+		return nil, ErrNotFound
+	default:
+		return nil, err
+	}
+}
+
+func (m *defaultTeamMembersModel) Update(ctx context.Context, data *TeamMembers) (*mongo.UpdateResult, error) {
+	res, err := m.conn.UpdateOne(ctx, bson.M{"_id": data.ID}, bson.M{"$set": data})
+	return res, err
+}
+
+func (m *defaultTeamMembersModel) Delete(ctx context.Context, id string) (int64, error) {
+	oid, err := primitive.ObjectIDFromHex(id)
+	if err != nil {
+		return 0, ErrInvalidObjectId
+	}
+
+	res, err := m.conn.DeleteOne(ctx, bson.M{"_id": oid})
+	return res, err
+}

+ 19 - 0
mongo_model/team_members/teammemberstypes.go

@@ -0,0 +1,19 @@
+package model
+
+import (
+	"time"
+
+	"go.mongodb.org/mongo-driver/bson/primitive"
+)
+
+type TeamMembers struct {
+	ID          primitive.ObjectID `bson:"_id,omitempty" json:"id,omitempty"`
+	TeamID      primitive.ObjectID `bson:"teamId,omitempty" json:"teamId,omitempty"`
+	UserID      primitive.ObjectID `bson:"userId,omitempty" json:"userId,omitempty"`
+	Name        string             `bson:"name,omitempty" json:"name,omitempty"`
+	Role        string             `bson:"role,omitempty" json:"role,omitempty"`
+	Status      string             `bson:"status,omitempty" json:"status,omitempty"`
+	CreateTime  time.Time          `bson:"createTime,omitempty" json:"createTime,omitempty"`
+	DefaultTeam bool               `bson:"defaultTeam,omitempty" json:"defaultTeam,omitempty"`
+	Version     int32              `bson:"__v,omitempty" json:"version,omitempty"`
+}

+ 12 - 0
mongo_model/teams/error.go

@@ -0,0 +1,12 @@
+package model
+
+import (
+	"errors"
+
+	"github.com/zeromicro/go-zero/core/stores/mon"
+)
+
+var (
+	ErrNotFound        = mon.ErrNotFound
+	ErrInvalidObjectId = errors.New("invalid objectId")
+)

+ 25 - 0
mongo_model/teams/teamsmodel.go

@@ -0,0 +1,25 @@
+package model
+
+import "github.com/zeromicro/go-zero/core/stores/mon"
+
+var _ TeamsModel = (*customTeamsModel)(nil)
+
+type (
+	// TeamsModel is an interface to be customized, add more methods here,
+	// and implement the added methods in customTeamsModel.
+	TeamsModel interface {
+		teamsModel
+	}
+
+	customTeamsModel struct {
+		*defaultTeamsModel
+	}
+)
+
+// NewTeamsModel returns a model for the mongo_model.
+func NewTeamsModel(url, db, collection string) TeamsModel {
+	conn := mon.MustNewModel(url, db, collection)
+	return &customTeamsModel{
+		defaultTeamsModel: newDefaultTeamsModel(conn),
+	}
+}

+ 73 - 0
mongo_model/teams/teamsmodelgen.go

@@ -0,0 +1,73 @@
+// Code generated by goctl. DO NOT EDIT.
+// goctl 1.8.1
+
+package model
+
+import (
+	"context"
+	"time"
+
+	"github.com/zeromicro/go-zero/core/stores/mon"
+	"go.mongodb.org/mongo-driver/bson"
+	"go.mongodb.org/mongo-driver/bson/primitive"
+	"go.mongodb.org/mongo-driver/mongo"
+)
+
+type teamsModel interface {
+	Insert(ctx context.Context, data *Teams) error
+	FindOne(ctx context.Context, id string) (*Teams, error)
+	Update(ctx context.Context, data *Teams) (*mongo.UpdateResult, error)
+	Delete(ctx context.Context, id string) (int64, error)
+}
+
+type defaultTeamsModel struct {
+	conn *mon.Model
+}
+
+func newDefaultTeamsModel(conn *mon.Model) *defaultTeamsModel {
+	return &defaultTeamsModel{conn: conn}
+}
+
+func (m *defaultTeamsModel) Insert(ctx context.Context, data *Teams) error {
+	if data.ID.IsZero() {
+		data.ID = primitive.NewObjectID()
+		data.CreateTime = time.Now()
+	}
+
+	_, err := m.conn.InsertOne(ctx, data)
+	return err
+}
+
+func (m *defaultTeamsModel) FindOne(ctx context.Context, id string) (*Teams, error) {
+	oid, err := primitive.ObjectIDFromHex(id)
+	if err != nil {
+		return nil, ErrInvalidObjectId
+	}
+
+	var data Teams
+
+	err = m.conn.FindOne(ctx, &data, bson.M{"_id": oid})
+	switch err {
+	case nil:
+		return &data, nil
+	case mon.ErrNotFound:
+		return nil, ErrNotFound
+	default:
+		return nil, err
+	}
+}
+
+func (m *defaultTeamsModel) Update(ctx context.Context, data *Teams) (*mongo.UpdateResult, error) {
+	res, err := m.conn.UpdateOne(ctx, bson.M{"_id": data.ID}, bson.M{"$set": data})
+	return res, err
+}
+
+func (m *defaultTeamsModel) Delete(ctx context.Context, id string) (int64, error) {
+	oid, err := primitive.ObjectIDFromHex(id)
+	if err != nil {
+		return 0, ErrInvalidObjectId
+	}
+
+	res, err := m.conn.DeleteOne(ctx, bson.M{"_id": oid})
+	return res, err
+}

+ 18 - 0
mongo_model/teams/teamstypes.go

@@ -0,0 +1,18 @@
+package model
+
+import (
+	"time"
+
+	"go.mongodb.org/mongo-driver/bson/primitive"
+)
+
+type Teams struct {
+	ID                primitive.ObjectID `bson:"_id,omitempty" json:"id,omitempty"`
+	Name              string             `bson:"name" json:"name"`
+	OwnerID           primitive.ObjectID `bson:"ownerId" json:"ownerId"`
+	DefaultPermission int32              `bson:"defaultPermission" json:"defaultPermission"`
+	Avatar            string             `bson:"avatar" json:"avatar"`
+	CreateTime        time.Time          `bson:"createTime" json:"createTime"`
+	Balance           int32              `bson:"balance" json:"balance"`
+	Version           int32              `bson:"__v" json:"version"`
+}

+ 12 - 0
mongo_model/users/error.go

@@ -0,0 +1,12 @@
+package model
+
+import (
+	"errors"
+
+	"github.com/zeromicro/go-zero/core/stores/mon"
+)
+
+var (
+	ErrNotFound        = mon.ErrNotFound
+	ErrInvalidObjectId = errors.New("invalid objectId")
+)

+ 27 - 0
mongo_model/users/usersmodel.go

@@ -0,0 +1,27 @@
+package model
+
+import (
+	"github.com/zeromicro/go-zero/core/stores/mon"
+)
+
+var _ UsersModel = (*customUsersModel)(nil)
+
+type (
+	// UsersModel is an interface to be customized, add more methods here,
+	// and implement the added methods in customUsersModel.
+	UsersModel interface {
+		usersModel
+	}
+
+	customUsersModel struct {
+		*defaultUsersModel
+	}
+)
+
+// NewUsersModel returns a model for the mongo_model.
+func NewUsersModel(url, db, collection string) UsersModel {
+	conn := mon.MustNewModel(url, db, collection)
+	return &customUsersModel{
+		defaultUsersModel: newDefaultUsersModel(conn),
+	}
+}

+ 70 - 0
mongo_model/users/usersmodelgen.go

@@ -0,0 +1,70 @@
+// Code generated by goctl. DO NOT EDIT.
+// goctl 1.8.1
+
+package model
+
+import (
+	"context"
+	"github.com/zeromicro/go-zero/core/stores/mon"
+	"go.mongodb.org/mongo-driver/bson"
+	"go.mongodb.org/mongo-driver/bson/primitive"
+	"go.mongodb.org/mongo-driver/mongo"
+)
+
+type usersModel interface {
+	Insert(ctx context.Context, data *Users) error
+	FindOne(ctx context.Context, id string) (*Users, error)
+	Update(ctx context.Context, data *Users) (*mongo.UpdateResult, error)
+	Delete(ctx context.Context, id string) (int64, error)
+}
+
+type defaultUsersModel struct {
+	conn *mon.Model
+}
+
+func newDefaultUsersModel(conn *mon.Model) *defaultUsersModel {
+	return &defaultUsersModel{conn: conn}
+}
+
+func (m *defaultUsersModel) Insert(ctx context.Context, data *Users) error {
+	if data.ID.IsZero() {
+		data.ID = primitive.NewObjectID()
+	}
+
+	_, err := m.conn.InsertOne(ctx, data)
+	return err
+}
+
+func (m *defaultUsersModel) FindOne(ctx context.Context, id string) (*Users, error) {
+	oid, err := primitive.ObjectIDFromHex(id)
+	if err != nil {
+		return nil, ErrInvalidObjectId
+	}
+
+	var data Users
+
+	err = m.conn.FindOne(ctx, &data, bson.M{"_id": oid})
+	switch err {
+	case nil:
+		return &data, nil
+	case mon.ErrNotFound:
+		return nil, ErrNotFound
+	default:
+		return nil, err
+	}
+}
+
+func (m *defaultUsersModel) Update(ctx context.Context, data *Users) (*mongo.UpdateResult, error) {
+	res, err := m.conn.UpdateOne(ctx, bson.M{"_id": data.ID}, bson.M{"$set": data})
+	return res, err
+}
+
+func (m *defaultUsersModel) Delete(ctx context.Context, id string) (int64, error) {
+	oid, err := primitive.ObjectIDFromHex(id)
+	if err != nil {
+		return 0, ErrInvalidObjectId
+	}
+
+	res, err := m.conn.DeleteOne(ctx, bson.M{"_id": oid})
+	return res, err
+}

+ 16 - 0
mongo_model/users/userstypes.go

@@ -0,0 +1,16 @@
+package model
+
+import (
+	"go.mongodb.org/mongo-driver/bson/primitive"
+)
+
+type Users struct {
+	ID            primitive.ObjectID `bson:"_id,omitempty" json:"id,omitempty"`
+	Status        string             `bson:"status" json:"status"`
+	Username      string             `bson:"username" json:"username"`
+	Password      string             `bson:"password" json:"password"`
+	Avatar        string             `bson:"avatar" json:"avatar"`
+	Balance       int32              `bson:"balance" json:"balance"`
+	PromotionRate int32              `bson:"promotionRate" json:"promotionRate"`
+	Timezone      string             `bson:"timezone" json:"timezone"`
+}

+ 12 - 0
teams/error.go

@@ -0,0 +1,12 @@
+package model
+
+import (
+	"errors"
+
+	"github.com/zeromicro/go-zero/core/stores/mon"
+)
+
+var (
+	ErrNotFound        = mon.ErrNotFound
+	ErrInvalidObjectId = errors.New("invalid objectId")
+)

+ 25 - 0
teams/teamsmodel.go

@@ -0,0 +1,25 @@
+package model
+
+import "github.com/zeromicro/go-zero/core/stores/mon"
+
+var _ TeamsModel = (*customTeamsModel)(nil)
+
+type (
+	// TeamsModel is an interface to be customized, add more methods here,
+	// and implement the added methods in customTeamsModel.
+	TeamsModel interface {
+		teamsModel
+	}
+
+	customTeamsModel struct {
+		*defaultTeamsModel
+	}
+)
+
+// NewTeamsModel returns a model for the mongo.
+func NewTeamsModel(url, db, collection string) TeamsModel {
+	conn := mon.MustNewModel(url, db, collection)
+	return &customTeamsModel{
+		defaultTeamsModel: newDefaultTeamsModel(conn),
+	}
+}

+ 76 - 0
teams/teamsmodelgen.go

@@ -0,0 +1,76 @@
+// Code generated by goctl. DO NOT EDIT.
+// goctl 1.8.1
+
+package model
+
+import (
+	"context"
+	"time"
+
+	"github.com/zeromicro/go-zero/core/stores/mon"
+	"go.mongodb.org/mongo-driver/bson"
+	"go.mongodb.org/mongo-driver/bson/primitive"
+	"go.mongodb.org/mongo-driver/mongo"
+)
+
+type teamsModel interface {
+	Insert(ctx context.Context, data *Teams) error
+	FindOne(ctx context.Context, id string) (*Teams, error)
+	Update(ctx context.Context, data *Teams) (*mongo.UpdateResult, error)
+	Delete(ctx context.Context, id string) (int64, error)
+}
+
+type defaultTeamsModel struct {
+	conn *mon.Model
+}
+
+func newDefaultTeamsModel(conn *mon.Model) *defaultTeamsModel {
+	return &defaultTeamsModel{conn: conn}
+}
+
+func (m *defaultTeamsModel) Insert(ctx context.Context, data *Teams) error {
+	if data.ID.IsZero() {
+		data.ID = primitive.NewObjectID()
+		data.CreateAt = time.Now()
+		data.UpdateAt = time.Now()
+	}
+
+	_, err := m.conn.InsertOne(ctx, data)
+	return err
+}
+
+func (m *defaultTeamsModel) FindOne(ctx context.Context, id string) (*Teams, error) {
+	oid, err := primitive.ObjectIDFromHex(id)
+	if err != nil {
+		return nil, ErrInvalidObjectId
+	}
+
+	var data Teams
+
+	err = m.conn.FindOne(ctx, &data, bson.M{"_id": oid})
+	switch err {
+	case nil:
+		return &data, nil
+	case mon.ErrNotFound:
+		return nil, ErrNotFound
+	default:
+		return nil, err
+	}
+}
+
+func (m *defaultTeamsModel) Update(ctx context.Context, data *Teams) (*mongo.UpdateResult, error) {
+	data.UpdateAt = time.Now()
+
+	res, err := m.conn.UpdateOne(ctx, bson.M{"_id": data.ID}, bson.M{"$set": data})
+	return res, err
+}
+
+func (m *defaultTeamsModel) Delete(ctx context.Context, id string) (int64, error) {
+	oid, err := primitive.ObjectIDFromHex(id)
+	if err != nil {
+		return 0, ErrInvalidObjectId
+	}
+
+	res, err := m.conn.DeleteOne(ctx, bson.M{"_id": oid})
+	return res, err
+}

+ 14 - 0
teams/teamstypes.go

@@ -0,0 +1,14 @@
+package model
+
+import (
+	"time"
+
+	"go.mongodb.org/mongo-driver/bson/primitive"
+)
+
+type Teams struct {
+	ID primitive.ObjectID `bson:"_id,omitempty" json:"id,omitempty"`
+	// TODO: Fill your own fields
+	UpdateAt time.Time `bson:"updateAt,omitempty" json:"updateAt,omitempty"`
+	CreateAt time.Time `bson:"createAt,omitempty" json:"createAt,omitempty"`
+}