boweniac 9 сар өмнө
parent
commit
b8bf7b7a97
93 өөрчлөгдсөн 26391 нэмэгдсэн , 34 устгасан
  1. 5 1
      desc/all.api
  2. 10 0
      desc/base.api
  3. 110 0
      desc/wechat/message_records.api
  4. 92 0
      desc/wechat/sop_node.api
  5. 95 0
      desc/wechat/sop_stage.api
  6. 92 0
      desc/wechat/sop_task.api
  7. 644 4
      ent/client.go
  8. 11 0
      ent/custom_types/types.go
  9. 8 0
      ent/ent.go
  10. 48 0
      ent/hook/hook.go
  11. 120 0
      ent/intercept/intercept.go
  12. 260 0
      ent/messagerecords.go
  13. 196 0
      ent/messagerecords/messagerecords.go
  14. 875 0
      ent/messagerecords/where.go
  15. 1547 0
      ent/messagerecords_create.go
  16. 88 0
      ent/messagerecords_delete.go
  17. 526 0
      ent/messagerecords_query.go
  18. 860 0
      ent/messagerecords_update.go
  19. 150 0
      ent/migrate/schema.go
  20. 4739 28
      ent/mutation.go
  21. 328 0
      ent/pagination.go
  22. 12 0
      ent/predicate/predicate.go
  23. 168 0
      ent/runtime/runtime.go
  24. 75 0
      ent/schema/message_records.go
  25. 71 0
      ent/schema/sop_node.go
  26. 74 0
      ent/schema/sop_stage.go
  27. 63 0
      ent/schema/sop_task.go
  28. 1060 1
      ent/set_not_nil.go
  29. 255 0
      ent/sopnode.go
  30. 161 0
      ent/sopnode/sopnode.go
  31. 479 0
      ent/sopnode/where.go
  32. 1187 0
      ent/sopnode_create.go
  33. 88 0
      ent/sopnode_delete.go
  34. 605 0
      ent/sopnode_query.go
  35. 755 0
      ent/sopnode_update.go
  36. 282 0
      ent/sopstage.go
  37. 203 0
      ent/sopstage/sopstage.go
  38. 547 0
      ent/sopstage/where.go
  39. 1316 0
      ent/sopstage_create.go
  40. 88 0
      ent/sopstage_delete.go
  41. 680 0
      ent/sopstage_query.go
  42. 972 0
      ent/sopstage_update.go
  43. 236 0
      ent/soptask.go
  44. 170 0
      ent/soptask/soptask.go
  45. 609 0
      ent/soptask/where.go
  46. 1202 0
      ent/soptask_create.go
  47. 88 0
      ent/soptask_delete.go
  48. 605 0
      ent/soptask_query.go
  49. 840 0
      ent/soptask_update.go
  50. 12 0
      ent/tx.go
  51. 44 0
      internal/handler/message_records/create_message_records_handler.go
  52. 44 0
      internal/handler/message_records/delete_message_records_handler.go
  53. 44 0
      internal/handler/message_records/get_message_records_by_id_handler.go
  54. 44 0
      internal/handler/message_records/get_message_records_list_handler.go
  55. 44 0
      internal/handler/message_records/update_message_records_handler.go
  56. 140 0
      internal/handler/routes.go
  57. 44 0
      internal/handler/sop_node/create_sop_node_handler.go
  58. 44 0
      internal/handler/sop_node/delete_sop_node_handler.go
  59. 44 0
      internal/handler/sop_node/get_sop_node_by_id_handler.go
  60. 44 0
      internal/handler/sop_node/get_sop_node_list_handler.go
  61. 44 0
      internal/handler/sop_node/update_sop_node_handler.go
  62. 44 0
      internal/handler/sop_stage/create_sop_stage_handler.go
  63. 44 0
      internal/handler/sop_stage/delete_sop_stage_handler.go
  64. 44 0
      internal/handler/sop_stage/get_sop_stage_by_id_handler.go
  65. 44 0
      internal/handler/sop_stage/get_sop_stage_list_handler.go
  66. 44 0
      internal/handler/sop_stage/update_sop_stage_handler.go
  67. 44 0
      internal/handler/sop_task/create_sop_task_handler.go
  68. 44 0
      internal/handler/sop_task/delete_sop_task_handler.go
  69. 44 0
      internal/handler/sop_task/get_sop_task_by_id_handler.go
  70. 44 0
      internal/handler/sop_task/get_sop_task_list_handler.go
  71. 44 0
      internal/handler/sop_task/update_sop_task_handler.go
  72. 248 0
      internal/logic/base/init_api_data.go
  73. 50 0
      internal/logic/message_records/create_message_records_logic.go
  74. 37 0
      internal/logic/message_records/delete_message_records_logic.go
  75. 62 0
      internal/logic/message_records/get_message_records_by_id_logic.go
  76. 77 0
      internal/logic/message_records/get_message_records_list_logic.go
  77. 51 0
      internal/logic/message_records/update_message_records_logic.go
  78. 63 0
      internal/logic/sop_node/create_sop_node_logic.go
  79. 37 0
      internal/logic/sop_node/delete_sop_node_logic.go
  80. 73 0
      internal/logic/sop_node/get_sop_node_by_id_logic.go
  81. 83 0
      internal/logic/sop_node/get_sop_node_list_logic.go
  82. 62 0
      internal/logic/sop_node/update_sop_node_logic.go
  83. 64 0
      internal/logic/sop_stage/create_sop_stage_logic.go
  84. 37 0
      internal/logic/sop_stage/delete_sop_stage_logic.go
  85. 74 0
      internal/logic/sop_stage/get_sop_stage_by_id_logic.go
  86. 84 0
      internal/logic/sop_stage/get_sop_stage_list_logic.go
  87. 63 0
      internal/logic/sop_stage/update_sop_stage_logic.go
  88. 47 0
      internal/logic/sop_task/create_sop_task_logic.go
  89. 37 0
      internal/logic/sop_task/delete_sop_task_logic.go
  90. 60 0
      internal/logic/sop_task/get_sop_task_by_id_logic.go
  91. 69 0
      internal/logic/sop_task/get_sop_task_list_logic.go
  92. 46 0
      internal/logic/sop_task/update_sop_task_logic.go
  93. 240 0
      internal/types/types.go

+ 5 - 1
desc/all.api

@@ -5,4 +5,8 @@ import "./wechat/wxhook.api"
 import "./wechat/contact.api"
 import "./wechat/message.api"
 import "./wechat/label.api"
-import "./wechat/label_relationship.api"
+import "./wechat/label_relationship.api"
+import "./wechat/sop_task.api"
+import "./wechat/sop_stage.api"
+import "./wechat/sop_node.api"
+import "./wechat/message_records.api"

+ 10 - 0
desc/base.api

@@ -206,6 +206,16 @@ type BaseUUIDInfo {
     UpdatedAt *int64     `json:"updatedAt,optional"`
 }
 
+type Condition struct {
+	Equal       int   `json:"equal"`
+	LabelIdList []int `json:"labelIdList"`
+}
+
+type Action struct {
+	Type    int    `json:"type"`
+	Content string `json:"content"`
+}
+
 @server(
 	group: base
 )

+ 110 - 0
desc/wechat/message_records.api

@@ -0,0 +1,110 @@
+import "../base.api"
+
+type (
+    // The response data of message records information | MessageRecords信息
+    MessageRecordsInfo {
+        BaseIDInfo
+
+        // Status 1: normal 2: ban | 状态 1 正常 2 禁用 
+        Status  *uint8 `json:"status,optional"`
+
+        // 机器人微信 id 
+        BotWxid  *string `json:"botWxid,optional"`
+
+        // 联系人 id 
+        ContactId  *int `json:"contactId,optional"`
+
+        // 类型:1好友,2群组,3企业微信联系人 
+        ContactType  *int `json:"contactType,optional"`
+
+        // 接收方微信 id 
+        ContactWxid  *string `json:"contactWxid,optional"`
+
+        // 内容类型 1 文本 2 文件 
+        ContentType  *int `json:"contentType,optional"`
+
+        // 发送内容 
+        Content  *string `json:"content,optional"`
+
+        // 异常原因 
+        ErrorDetail  *string `json:"errorDetail,optional"`
+
+        // 发送时间 
+        SendTime  *int64 `json:"sendTime,optional"`
+
+        // 源类型 1 点发 2 群发 3 SOP 
+        SourceType  *int `json:"sourceType,optional"`
+
+        // 源 ID 
+        SourceId  *int `json:"sourceId,optional"`
+
+        // 次源 ID 
+        SubSourceId  *int `json:"subSourceId,optional"`
+    }
+
+    // The response data of message records list | MessageRecords列表数据
+    MessageRecordsListResp {
+        BaseDataInfo
+
+        // MessageRecords list data | MessageRecords列表数据
+        Data MessageRecordsListInfo `json:"data"`
+    }
+
+    // MessageRecords list data | MessageRecords列表数据
+    MessageRecordsListInfo {
+        BaseListInfo
+
+        // The API list data | MessageRecords列表数据
+        Data  []MessageRecordsInfo  `json:"data"`
+    }
+
+    // Get message records list request params | MessageRecords列表请求参数
+    MessageRecordsListReq {
+        PageInfo
+
+        // 机器人微信 id 
+        BotWxid  *string `json:"botWxid,optional"`
+
+        // 接收方微信 id 
+        ContactWxid  *string `json:"contactWxid,optional"`
+
+        // 发送内容 
+        Content  *string `json:"content,optional"`
+    }
+
+    // MessageRecords information response | MessageRecords信息返回体
+    MessageRecordsInfoResp {
+        BaseDataInfo
+
+        // MessageRecords information | MessageRecords数据
+        Data MessageRecordsInfo `json:"data"`
+    }
+)
+
+@server(
+    jwt: Auth
+    group: message_records
+    middleware: Authority
+)
+
+service Wechat {
+    // Create message records information | 创建MessageRecords
+    @handler createMessageRecords
+    post /message_records/create (MessageRecordsInfo) returns (BaseMsgResp)
+
+    // Update message records information | 更新MessageRecords
+    @handler updateMessageRecords
+    post /message_records/update (MessageRecordsInfo) returns (BaseMsgResp)
+
+    // Delete message records information | 删除MessageRecords信息
+    @handler deleteMessageRecords
+    post /message_records/delete (IDsReq) returns (BaseMsgResp)
+
+    // Get message records list | 获取MessageRecords列表
+    @handler getMessageRecordsList
+    post /message_records/list (MessageRecordsListReq) returns (MessageRecordsListResp)
+
+    // Get message records by ID | 通过ID获取MessageRecords
+    @handler getMessageRecordsById
+    post /message_records (IDReq) returns (MessageRecordsInfoResp)
+}

+ 92 - 0
desc/wechat/sop_node.api

@@ -0,0 +1,92 @@
+import "../base.api"
+
+type (
+    // The response data of sop node information | SopNode信息
+    SopNodeInfo {
+        BaseIDInfo
+
+        // Status 1: normal 2: ban | 状态 1 正常 2 禁用 
+        Status  *uint8 `json:"status,optional"`
+
+        // 阶段 ID 
+        StageId  *uint64 `json:"stageId,optional"`
+
+        // 父节点 ID 
+        ParentId  *int `json:"parentId,optional"`
+
+        // 节点名称 
+        Name  *string `json:"name,optional"`
+
+        // 触发条件类型 1 客户回复后触发 2 超时后触发 
+        ConditionType  *int `json:"conditionType,optional"`
+
+        // 触发语义列表 当为空时则代表用户回复任意内容后触发 
+        ConditionList  []Condition `json:"conditionList,optional"`
+
+        // 命中后发送的消息内容 
+        ActionMessage  []Action `json:"actionMessage,optional"`
+
+        // 命中后需要打的标签 
+        ActionLabel  []int `json:"actionLabel,optional"`
+    }
+
+    // The response data of sop node list | SopNode列表数据
+    SopNodeListResp {
+        BaseDataInfo
+
+        // SopNode list data | SopNode列表数据
+        Data SopNodeListInfo `json:"data"`
+    }
+
+    // SopNode list data | SopNode列表数据
+    SopNodeListInfo {
+        BaseListInfo
+
+        // The API list data | SopNode列表数据
+        Data  []SopNodeInfo  `json:"data"`
+    }
+
+    // Get sop node list request params | SopNode列表请求参数
+    SopNodeListReq {
+        PageInfo
+
+        // 节点名称 
+        Name  *string `json:"name,optional"`
+    }
+
+    // SopNode information response | SopNode信息返回体
+    SopNodeInfoResp {
+        BaseDataInfo
+
+        // SopNode information | SopNode数据
+        Data SopNodeInfo `json:"data"`
+    }
+)
+
+@server(
+    jwt: Auth
+    group: sop_node
+    middleware: Authority
+)
+
+service Wechat {
+    // Create sop node information | 创建SopNode
+    @handler createSopNode
+    post /sop_node/create (SopNodeInfo) returns (BaseMsgResp)
+
+    // Update sop node information | 更新SopNode
+    @handler updateSopNode
+    post /sop_node/update (SopNodeInfo) returns (BaseMsgResp)
+
+    // Delete sop node information | 删除SopNode信息
+    @handler deleteSopNode
+    post /sop_node/delete (IDsReq) returns (BaseMsgResp)
+
+    // Get sop node list | 获取SopNode列表
+    @handler getSopNodeList
+    post /sop_node/list (SopNodeListReq) returns (SopNodeListResp)
+
+    // Get sop node by ID | 通过ID获取SopNode
+    @handler getSopNodeById
+    post /sop_node (IDReq) returns (SopNodeInfoResp)
+}

+ 95 - 0
desc/wechat/sop_stage.api

@@ -0,0 +1,95 @@
+import "../base.api"
+
+type (
+    // The response data of sop stage information | SopStage信息
+    SopStageInfo {
+        BaseIDInfo
+
+        // Status 1: normal 2: ban | 状态 1 正常 2 禁用 
+        Status  *uint8 `json:"status,optional"`
+
+        // SOP 任务 ID 
+        TaskId  *uint64 `json:"taskId,optional"`
+
+        // 阶段名称 
+        Name  *string `json:"name,optional"`
+
+        // 客群筛选条件类型  1 按标签筛选 2 按客户基本信息筛选 
+        ConditionType  *int `json:"conditionType,optional"`
+
+        // 筛选条件关系  1 满足所有条件(and) 2 满足任意条件(or) 
+        ConditionOperator  *int `json:"conditionOperator,optional"`
+
+        // 筛选条件列表 
+        ConditionList  []Condition `json:"conditionList,optional"`
+
+        // 命中后发送的消息内容 
+        ActionMessage  []Action `json:"actionMessage,optional"`
+
+        // 命中后需要打的标签 
+        ActionLabel  []int `json:"actionLabel,optional"`
+
+        // 阶段顺序 
+        IndexSort  *int `json:"indexSort,optional"`
+    }
+
+    // The response data of sop stage list | SopStage列表数据
+    SopStageListResp {
+        BaseDataInfo
+
+        // SopStage list data | SopStage列表数据
+        Data SopStageListInfo `json:"data"`
+    }
+
+    // SopStage list data | SopStage列表数据
+    SopStageListInfo {
+        BaseListInfo
+
+        // The API list data | SopStage列表数据
+        Data  []SopStageInfo  `json:"data"`
+    }
+
+    // Get sop stage list request params | SopStage列表请求参数
+    SopStageListReq {
+        PageInfo
+
+        // 阶段名称 
+        Name  *string `json:"name,optional"`
+    }
+
+    // SopStage information response | SopStage信息返回体
+    SopStageInfoResp {
+        BaseDataInfo
+
+        // SopStage information | SopStage数据
+        Data SopStageInfo `json:"data"`
+    }
+)
+
+@server(
+    jwt: Auth
+    group: sop_stage
+    middleware: Authority
+)
+
+service Wechat {
+    // Create sop stage information | 创建SopStage
+    @handler createSopStage
+    post /sop_stage/create (SopStageInfo) returns (BaseMsgResp)
+
+    // Update sop stage information | 更新SopStage
+    @handler updateSopStage
+    post /sop_stage/update (SopStageInfo) returns (BaseMsgResp)
+
+    // Delete sop stage information | 删除SopStage信息
+    @handler deleteSopStage
+    post /sop_stage/delete (IDsReq) returns (BaseMsgResp)
+
+    // Get sop stage list | 获取SopStage列表
+    @handler getSopStageList
+    post /sop_stage/list (SopStageListReq) returns (SopStageListResp)
+
+    // Get sop stage by ID | 通过ID获取SopStage
+    @handler getSopStageById
+    post /sop_stage (IDReq) returns (SopStageInfoResp)
+}

+ 92 - 0
desc/wechat/sop_task.api

@@ -0,0 +1,92 @@
+import "../base.api"
+
+type (
+    // The response data of sop task information | SopTask信息
+    SopTaskInfo {
+        BaseIDInfo
+
+        // Status 1: normal 2: ban | 状态 1 正常 2 禁用 
+        Status  *uint8 `json:"status,optional"`
+
+        // SOP 任务名称 
+        Name  *string `json:"name,optional"`
+
+        // 机器人微信 id 列表 
+        BotWxidList  []string `json:"botWxidList,optional"`
+
+        // 标签类型:1好友,2群组,3企业微信联系人 
+        Type  *int `json:"type,optional"`
+
+        // 任务计划开始时间 
+        PlanStartTime  *int64 `json:"planStartTime,optional"`
+
+        // 任务计划结束时间 
+        PlanEndTime  *int64 `json:"planEndTime,optional"`
+
+        // 创建者 id 
+        CreatorId  *string `json:"creatorId,optional"`
+    }
+
+    // The response data of sop task list | SopTask列表数据
+    SopTaskListResp {
+        BaseDataInfo
+
+        // SopTask list data | SopTask列表数据
+        Data SopTaskListInfo `json:"data"`
+    }
+
+    // SopTask list data | SopTask列表数据
+    SopTaskListInfo {
+        BaseListInfo
+
+        // The API list data | SopTask列表数据
+        Data  []SopTaskInfo  `json:"data"`
+    }
+
+    // Get sop task list request params | SopTask列表请求参数
+    SopTaskListReq {
+        PageInfo
+
+        // SOP 任务名称 
+        Name  *string `json:"name,optional"`
+
+        // 创建者 id 
+        CreatorId  *string `json:"creatorId,optional"`
+    }
+
+    // SopTask information response | SopTask信息返回体
+    SopTaskInfoResp {
+        BaseDataInfo
+
+        // SopTask information | SopTask数据
+        Data SopTaskInfo `json:"data"`
+    }
+)
+
+@server(
+    jwt: Auth
+    group: sop_task
+    middleware: Authority
+)
+
+service Wechat {
+    // Create sop task information | 创建SopTask
+    @handler createSopTask
+    post /sop_task/create (SopTaskInfo) returns (BaseMsgResp)
+
+    // Update sop task information | 更新SopTask
+    @handler updateSopTask
+    post /sop_task/update (SopTaskInfo) returns (BaseMsgResp)
+
+    // Delete sop task information | 删除SopTask信息
+    @handler deleteSopTask
+    post /sop_task/delete (IDsReq) returns (BaseMsgResp)
+
+    // Get sop task list | 获取SopTask列表
+    @handler getSopTaskList
+    post /sop_task/list (SopTaskListReq) returns (SopTaskListResp)
+
+    // Get sop task by ID | 通过ID获取SopTask
+    @handler getSopTaskById
+    post /sop_task (IDReq) returns (SopTaskInfoResp)
+}

+ 644 - 4
ent/client.go

@@ -15,7 +15,11 @@ import (
 	"wechat-api/ent/label"
 	"wechat-api/ent/labelrelationship"
 	"wechat-api/ent/message"
+	"wechat-api/ent/messagerecords"
 	"wechat-api/ent/server"
+	"wechat-api/ent/sopnode"
+	"wechat-api/ent/sopstage"
+	"wechat-api/ent/soptask"
 	"wechat-api/ent/wx"
 
 	"entgo.io/ent"
@@ -39,8 +43,16 @@ type Client struct {
 	LabelRelationship *LabelRelationshipClient
 	// Message is the client for interacting with the Message builders.
 	Message *MessageClient
+	// MessageRecords is the client for interacting with the MessageRecords builders.
+	MessageRecords *MessageRecordsClient
 	// Server is the client for interacting with the Server builders.
 	Server *ServerClient
+	// SopNode is the client for interacting with the SopNode builders.
+	SopNode *SopNodeClient
+	// SopStage is the client for interacting with the SopStage builders.
+	SopStage *SopStageClient
+	// SopTask is the client for interacting with the SopTask builders.
+	SopTask *SopTaskClient
 	// Wx is the client for interacting with the Wx builders.
 	Wx *WxClient
 }
@@ -58,7 +70,11 @@ func (c *Client) init() {
 	c.Label = NewLabelClient(c.config)
 	c.LabelRelationship = NewLabelRelationshipClient(c.config)
 	c.Message = NewMessageClient(c.config)
+	c.MessageRecords = NewMessageRecordsClient(c.config)
 	c.Server = NewServerClient(c.config)
+	c.SopNode = NewSopNodeClient(c.config)
+	c.SopStage = NewSopStageClient(c.config)
+	c.SopTask = NewSopTaskClient(c.config)
 	c.Wx = NewWxClient(c.config)
 }
 
@@ -156,7 +172,11 @@ func (c *Client) Tx(ctx context.Context) (*Tx, error) {
 		Label:             NewLabelClient(cfg),
 		LabelRelationship: NewLabelRelationshipClient(cfg),
 		Message:           NewMessageClient(cfg),
+		MessageRecords:    NewMessageRecordsClient(cfg),
 		Server:            NewServerClient(cfg),
+		SopNode:           NewSopNodeClient(cfg),
+		SopStage:          NewSopStageClient(cfg),
+		SopTask:           NewSopTaskClient(cfg),
 		Wx:                NewWxClient(cfg),
 	}, nil
 }
@@ -181,7 +201,11 @@ func (c *Client) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error)
 		Label:             NewLabelClient(cfg),
 		LabelRelationship: NewLabelRelationshipClient(cfg),
 		Message:           NewMessageClient(cfg),
+		MessageRecords:    NewMessageRecordsClient(cfg),
 		Server:            NewServerClient(cfg),
+		SopNode:           NewSopNodeClient(cfg),
+		SopStage:          NewSopStageClient(cfg),
+		SopTask:           NewSopTaskClient(cfg),
 		Wx:                NewWxClient(cfg),
 	}, nil
 }
@@ -212,7 +236,8 @@ 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.Contact, c.Label, c.LabelRelationship, c.Message, c.Server, c.Wx,
+		c.Contact, c.Label, c.LabelRelationship, c.Message, c.MessageRecords, c.Server,
+		c.SopNode, c.SopStage, c.SopTask, c.Wx,
 	} {
 		n.Use(hooks...)
 	}
@@ -222,7 +247,8 @@ 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.Contact, c.Label, c.LabelRelationship, c.Message, c.Server, c.Wx,
+		c.Contact, c.Label, c.LabelRelationship, c.Message, c.MessageRecords, c.Server,
+		c.SopNode, c.SopStage, c.SopTask, c.Wx,
 	} {
 		n.Intercept(interceptors...)
 	}
@@ -239,8 +265,16 @@ func (c *Client) Mutate(ctx context.Context, m Mutation) (Value, error) {
 		return c.LabelRelationship.mutate(ctx, m)
 	case *MessageMutation:
 		return c.Message.mutate(ctx, m)
+	case *MessageRecordsMutation:
+		return c.MessageRecords.mutate(ctx, m)
 	case *ServerMutation:
 		return c.Server.mutate(ctx, m)
+	case *SopNodeMutation:
+		return c.SopNode.mutate(ctx, m)
+	case *SopStageMutation:
+		return c.SopStage.mutate(ctx, m)
+	case *SopTaskMutation:
+		return c.SopTask.mutate(ctx, m)
 	case *WxMutation:
 		return c.Wx.mutate(ctx, m)
 	default:
@@ -850,6 +884,141 @@ func (c *MessageClient) mutate(ctx context.Context, m *MessageMutation) (Value,
 	}
 }
 
+// MessageRecordsClient is a client for the MessageRecords schema.
+type MessageRecordsClient struct {
+	config
+}
+
+// NewMessageRecordsClient returns a client for the MessageRecords from the given config.
+func NewMessageRecordsClient(c config) *MessageRecordsClient {
+	return &MessageRecordsClient{config: c}
+}
+
+// Use adds a list of mutation hooks to the hooks stack.
+// A call to `Use(f, g, h)` equals to `messagerecords.Hooks(f(g(h())))`.
+func (c *MessageRecordsClient) Use(hooks ...Hook) {
+	c.hooks.MessageRecords = append(c.hooks.MessageRecords, hooks...)
+}
+
+// Intercept adds a list of query interceptors to the interceptors stack.
+// A call to `Intercept(f, g, h)` equals to `messagerecords.Intercept(f(g(h())))`.
+func (c *MessageRecordsClient) Intercept(interceptors ...Interceptor) {
+	c.inters.MessageRecords = append(c.inters.MessageRecords, interceptors...)
+}
+
+// Create returns a builder for creating a MessageRecords entity.
+func (c *MessageRecordsClient) Create() *MessageRecordsCreate {
+	mutation := newMessageRecordsMutation(c.config, OpCreate)
+	return &MessageRecordsCreate{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// CreateBulk returns a builder for creating a bulk of MessageRecords entities.
+func (c *MessageRecordsClient) CreateBulk(builders ...*MessageRecordsCreate) *MessageRecordsCreateBulk {
+	return &MessageRecordsCreateBulk{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 *MessageRecordsClient) MapCreateBulk(slice any, setFunc func(*MessageRecordsCreate, int)) *MessageRecordsCreateBulk {
+	rv := reflect.ValueOf(slice)
+	if rv.Kind() != reflect.Slice {
+		return &MessageRecordsCreateBulk{err: fmt.Errorf("calling to MessageRecordsClient.MapCreateBulk with wrong type %T, need slice", slice)}
+	}
+	builders := make([]*MessageRecordsCreate, rv.Len())
+	for i := 0; i < rv.Len(); i++ {
+		builders[i] = c.Create()
+		setFunc(builders[i], i)
+	}
+	return &MessageRecordsCreateBulk{config: c.config, builders: builders}
+}
+
+// Update returns an update builder for MessageRecords.
+func (c *MessageRecordsClient) Update() *MessageRecordsUpdate {
+	mutation := newMessageRecordsMutation(c.config, OpUpdate)
+	return &MessageRecordsUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// UpdateOne returns an update builder for the given entity.
+func (c *MessageRecordsClient) UpdateOne(mr *MessageRecords) *MessageRecordsUpdateOne {
+	mutation := newMessageRecordsMutation(c.config, OpUpdateOne, withMessageRecords(mr))
+	return &MessageRecordsUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// UpdateOneID returns an update builder for the given id.
+func (c *MessageRecordsClient) UpdateOneID(id uint64) *MessageRecordsUpdateOne {
+	mutation := newMessageRecordsMutation(c.config, OpUpdateOne, withMessageRecordsID(id))
+	return &MessageRecordsUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// Delete returns a delete builder for MessageRecords.
+func (c *MessageRecordsClient) Delete() *MessageRecordsDelete {
+	mutation := newMessageRecordsMutation(c.config, OpDelete)
+	return &MessageRecordsDelete{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// DeleteOne returns a builder for deleting the given entity.
+func (c *MessageRecordsClient) DeleteOne(mr *MessageRecords) *MessageRecordsDeleteOne {
+	return c.DeleteOneID(mr.ID)
+}
+
+// DeleteOneID returns a builder for deleting the given entity by its id.
+func (c *MessageRecordsClient) DeleteOneID(id uint64) *MessageRecordsDeleteOne {
+	builder := c.Delete().Where(messagerecords.ID(id))
+	builder.mutation.id = &id
+	builder.mutation.op = OpDeleteOne
+	return &MessageRecordsDeleteOne{builder}
+}
+
+// Query returns a query builder for MessageRecords.
+func (c *MessageRecordsClient) Query() *MessageRecordsQuery {
+	return &MessageRecordsQuery{
+		config: c.config,
+		ctx:    &QueryContext{Type: TypeMessageRecords},
+		inters: c.Interceptors(),
+	}
+}
+
+// Get returns a MessageRecords entity by its id.
+func (c *MessageRecordsClient) Get(ctx context.Context, id uint64) (*MessageRecords, error) {
+	return c.Query().Where(messagerecords.ID(id)).Only(ctx)
+}
+
+// GetX is like Get, but panics if an error occurs.
+func (c *MessageRecordsClient) GetX(ctx context.Context, id uint64) *MessageRecords {
+	obj, err := c.Get(ctx, id)
+	if err != nil {
+		panic(err)
+	}
+	return obj
+}
+
+// Hooks returns the client hooks.
+func (c *MessageRecordsClient) Hooks() []Hook {
+	hooks := c.hooks.MessageRecords
+	return append(hooks[:len(hooks):len(hooks)], messagerecords.Hooks[:]...)
+}
+
+// Interceptors returns the client interceptors.
+func (c *MessageRecordsClient) Interceptors() []Interceptor {
+	inters := c.inters.MessageRecords
+	return append(inters[:len(inters):len(inters)], messagerecords.Interceptors[:]...)
+}
+
+func (c *MessageRecordsClient) mutate(ctx context.Context, m *MessageRecordsMutation) (Value, error) {
+	switch m.Op() {
+	case OpCreate:
+		return (&MessageRecordsCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
+	case OpUpdate:
+		return (&MessageRecordsUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
+	case OpUpdateOne:
+		return (&MessageRecordsUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
+	case OpDelete, OpDeleteOne:
+		return (&MessageRecordsDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx)
+	default:
+		return nil, fmt.Errorf("ent: unknown MessageRecords mutation op: %q", m.Op())
+	}
+}
+
 // ServerClient is a client for the Server schema.
 type ServerClient struct {
 	config
@@ -1001,6 +1170,475 @@ func (c *ServerClient) mutate(ctx context.Context, m *ServerMutation) (Value, er
 	}
 }
 
+// SopNodeClient is a client for the SopNode schema.
+type SopNodeClient struct {
+	config
+}
+
+// NewSopNodeClient returns a client for the SopNode from the given config.
+func NewSopNodeClient(c config) *SopNodeClient {
+	return &SopNodeClient{config: c}
+}
+
+// Use adds a list of mutation hooks to the hooks stack.
+// A call to `Use(f, g, h)` equals to `sopnode.Hooks(f(g(h())))`.
+func (c *SopNodeClient) Use(hooks ...Hook) {
+	c.hooks.SopNode = append(c.hooks.SopNode, hooks...)
+}
+
+// Intercept adds a list of query interceptors to the interceptors stack.
+// A call to `Intercept(f, g, h)` equals to `sopnode.Intercept(f(g(h())))`.
+func (c *SopNodeClient) Intercept(interceptors ...Interceptor) {
+	c.inters.SopNode = append(c.inters.SopNode, interceptors...)
+}
+
+// Create returns a builder for creating a SopNode entity.
+func (c *SopNodeClient) Create() *SopNodeCreate {
+	mutation := newSopNodeMutation(c.config, OpCreate)
+	return &SopNodeCreate{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// CreateBulk returns a builder for creating a bulk of SopNode entities.
+func (c *SopNodeClient) CreateBulk(builders ...*SopNodeCreate) *SopNodeCreateBulk {
+	return &SopNodeCreateBulk{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 *SopNodeClient) MapCreateBulk(slice any, setFunc func(*SopNodeCreate, int)) *SopNodeCreateBulk {
+	rv := reflect.ValueOf(slice)
+	if rv.Kind() != reflect.Slice {
+		return &SopNodeCreateBulk{err: fmt.Errorf("calling to SopNodeClient.MapCreateBulk with wrong type %T, need slice", slice)}
+	}
+	builders := make([]*SopNodeCreate, rv.Len())
+	for i := 0; i < rv.Len(); i++ {
+		builders[i] = c.Create()
+		setFunc(builders[i], i)
+	}
+	return &SopNodeCreateBulk{config: c.config, builders: builders}
+}
+
+// Update returns an update builder for SopNode.
+func (c *SopNodeClient) Update() *SopNodeUpdate {
+	mutation := newSopNodeMutation(c.config, OpUpdate)
+	return &SopNodeUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// UpdateOne returns an update builder for the given entity.
+func (c *SopNodeClient) UpdateOne(sn *SopNode) *SopNodeUpdateOne {
+	mutation := newSopNodeMutation(c.config, OpUpdateOne, withSopNode(sn))
+	return &SopNodeUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// UpdateOneID returns an update builder for the given id.
+func (c *SopNodeClient) UpdateOneID(id uint64) *SopNodeUpdateOne {
+	mutation := newSopNodeMutation(c.config, OpUpdateOne, withSopNodeID(id))
+	return &SopNodeUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// Delete returns a delete builder for SopNode.
+func (c *SopNodeClient) Delete() *SopNodeDelete {
+	mutation := newSopNodeMutation(c.config, OpDelete)
+	return &SopNodeDelete{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// DeleteOne returns a builder for deleting the given entity.
+func (c *SopNodeClient) DeleteOne(sn *SopNode) *SopNodeDeleteOne {
+	return c.DeleteOneID(sn.ID)
+}
+
+// DeleteOneID returns a builder for deleting the given entity by its id.
+func (c *SopNodeClient) DeleteOneID(id uint64) *SopNodeDeleteOne {
+	builder := c.Delete().Where(sopnode.ID(id))
+	builder.mutation.id = &id
+	builder.mutation.op = OpDeleteOne
+	return &SopNodeDeleteOne{builder}
+}
+
+// Query returns a query builder for SopNode.
+func (c *SopNodeClient) Query() *SopNodeQuery {
+	return &SopNodeQuery{
+		config: c.config,
+		ctx:    &QueryContext{Type: TypeSopNode},
+		inters: c.Interceptors(),
+	}
+}
+
+// Get returns a SopNode entity by its id.
+func (c *SopNodeClient) Get(ctx context.Context, id uint64) (*SopNode, error) {
+	return c.Query().Where(sopnode.ID(id)).Only(ctx)
+}
+
+// GetX is like Get, but panics if an error occurs.
+func (c *SopNodeClient) GetX(ctx context.Context, id uint64) *SopNode {
+	obj, err := c.Get(ctx, id)
+	if err != nil {
+		panic(err)
+	}
+	return obj
+}
+
+// QuerySopStage queries the sop_stage edge of a SopNode.
+func (c *SopNodeClient) QuerySopStage(sn *SopNode) *SopStageQuery {
+	query := (&SopStageClient{config: c.config}).Query()
+	query.path = func(context.Context) (fromV *sql.Selector, _ error) {
+		id := sn.ID
+		step := sqlgraph.NewStep(
+			sqlgraph.From(sopnode.Table, sopnode.FieldID, id),
+			sqlgraph.To(sopstage.Table, sopstage.FieldID),
+			sqlgraph.Edge(sqlgraph.M2O, true, sopnode.SopStageTable, sopnode.SopStageColumn),
+		)
+		fromV = sqlgraph.Neighbors(sn.driver.Dialect(), step)
+		return fromV, nil
+	}
+	return query
+}
+
+// Hooks returns the client hooks.
+func (c *SopNodeClient) Hooks() []Hook {
+	hooks := c.hooks.SopNode
+	return append(hooks[:len(hooks):len(hooks)], sopnode.Hooks[:]...)
+}
+
+// Interceptors returns the client interceptors.
+func (c *SopNodeClient) Interceptors() []Interceptor {
+	inters := c.inters.SopNode
+	return append(inters[:len(inters):len(inters)], sopnode.Interceptors[:]...)
+}
+
+func (c *SopNodeClient) mutate(ctx context.Context, m *SopNodeMutation) (Value, error) {
+	switch m.Op() {
+	case OpCreate:
+		return (&SopNodeCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
+	case OpUpdate:
+		return (&SopNodeUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
+	case OpUpdateOne:
+		return (&SopNodeUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
+	case OpDelete, OpDeleteOne:
+		return (&SopNodeDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx)
+	default:
+		return nil, fmt.Errorf("ent: unknown SopNode mutation op: %q", m.Op())
+	}
+}
+
+// SopStageClient is a client for the SopStage schema.
+type SopStageClient struct {
+	config
+}
+
+// NewSopStageClient returns a client for the SopStage from the given config.
+func NewSopStageClient(c config) *SopStageClient {
+	return &SopStageClient{config: c}
+}
+
+// Use adds a list of mutation hooks to the hooks stack.
+// A call to `Use(f, g, h)` equals to `sopstage.Hooks(f(g(h())))`.
+func (c *SopStageClient) Use(hooks ...Hook) {
+	c.hooks.SopStage = append(c.hooks.SopStage, hooks...)
+}
+
+// Intercept adds a list of query interceptors to the interceptors stack.
+// A call to `Intercept(f, g, h)` equals to `sopstage.Intercept(f(g(h())))`.
+func (c *SopStageClient) Intercept(interceptors ...Interceptor) {
+	c.inters.SopStage = append(c.inters.SopStage, interceptors...)
+}
+
+// Create returns a builder for creating a SopStage entity.
+func (c *SopStageClient) Create() *SopStageCreate {
+	mutation := newSopStageMutation(c.config, OpCreate)
+	return &SopStageCreate{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// CreateBulk returns a builder for creating a bulk of SopStage entities.
+func (c *SopStageClient) CreateBulk(builders ...*SopStageCreate) *SopStageCreateBulk {
+	return &SopStageCreateBulk{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 *SopStageClient) MapCreateBulk(slice any, setFunc func(*SopStageCreate, int)) *SopStageCreateBulk {
+	rv := reflect.ValueOf(slice)
+	if rv.Kind() != reflect.Slice {
+		return &SopStageCreateBulk{err: fmt.Errorf("calling to SopStageClient.MapCreateBulk with wrong type %T, need slice", slice)}
+	}
+	builders := make([]*SopStageCreate, rv.Len())
+	for i := 0; i < rv.Len(); i++ {
+		builders[i] = c.Create()
+		setFunc(builders[i], i)
+	}
+	return &SopStageCreateBulk{config: c.config, builders: builders}
+}
+
+// Update returns an update builder for SopStage.
+func (c *SopStageClient) Update() *SopStageUpdate {
+	mutation := newSopStageMutation(c.config, OpUpdate)
+	return &SopStageUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// UpdateOne returns an update builder for the given entity.
+func (c *SopStageClient) UpdateOne(ss *SopStage) *SopStageUpdateOne {
+	mutation := newSopStageMutation(c.config, OpUpdateOne, withSopStage(ss))
+	return &SopStageUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// UpdateOneID returns an update builder for the given id.
+func (c *SopStageClient) UpdateOneID(id uint64) *SopStageUpdateOne {
+	mutation := newSopStageMutation(c.config, OpUpdateOne, withSopStageID(id))
+	return &SopStageUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// Delete returns a delete builder for SopStage.
+func (c *SopStageClient) Delete() *SopStageDelete {
+	mutation := newSopStageMutation(c.config, OpDelete)
+	return &SopStageDelete{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// DeleteOne returns a builder for deleting the given entity.
+func (c *SopStageClient) DeleteOne(ss *SopStage) *SopStageDeleteOne {
+	return c.DeleteOneID(ss.ID)
+}
+
+// DeleteOneID returns a builder for deleting the given entity by its id.
+func (c *SopStageClient) DeleteOneID(id uint64) *SopStageDeleteOne {
+	builder := c.Delete().Where(sopstage.ID(id))
+	builder.mutation.id = &id
+	builder.mutation.op = OpDeleteOne
+	return &SopStageDeleteOne{builder}
+}
+
+// Query returns a query builder for SopStage.
+func (c *SopStageClient) Query() *SopStageQuery {
+	return &SopStageQuery{
+		config: c.config,
+		ctx:    &QueryContext{Type: TypeSopStage},
+		inters: c.Interceptors(),
+	}
+}
+
+// Get returns a SopStage entity by its id.
+func (c *SopStageClient) Get(ctx context.Context, id uint64) (*SopStage, error) {
+	return c.Query().Where(sopstage.ID(id)).Only(ctx)
+}
+
+// GetX is like Get, but panics if an error occurs.
+func (c *SopStageClient) GetX(ctx context.Context, id uint64) *SopStage {
+	obj, err := c.Get(ctx, id)
+	if err != nil {
+		panic(err)
+	}
+	return obj
+}
+
+// QuerySopTask queries the sop_task edge of a SopStage.
+func (c *SopStageClient) QuerySopTask(ss *SopStage) *SopTaskQuery {
+	query := (&SopTaskClient{config: c.config}).Query()
+	query.path = func(context.Context) (fromV *sql.Selector, _ error) {
+		id := ss.ID
+		step := sqlgraph.NewStep(
+			sqlgraph.From(sopstage.Table, sopstage.FieldID, id),
+			sqlgraph.To(soptask.Table, soptask.FieldID),
+			sqlgraph.Edge(sqlgraph.M2O, true, sopstage.SopTaskTable, sopstage.SopTaskColumn),
+		)
+		fromV = sqlgraph.Neighbors(ss.driver.Dialect(), step)
+		return fromV, nil
+	}
+	return query
+}
+
+// QueryStageNodes queries the stage_nodes edge of a SopStage.
+func (c *SopStageClient) QueryStageNodes(ss *SopStage) *SopNodeQuery {
+	query := (&SopNodeClient{config: c.config}).Query()
+	query.path = func(context.Context) (fromV *sql.Selector, _ error) {
+		id := ss.ID
+		step := sqlgraph.NewStep(
+			sqlgraph.From(sopstage.Table, sopstage.FieldID, id),
+			sqlgraph.To(sopnode.Table, sopnode.FieldID),
+			sqlgraph.Edge(sqlgraph.O2M, false, sopstage.StageNodesTable, sopstage.StageNodesColumn),
+		)
+		fromV = sqlgraph.Neighbors(ss.driver.Dialect(), step)
+		return fromV, nil
+	}
+	return query
+}
+
+// Hooks returns the client hooks.
+func (c *SopStageClient) Hooks() []Hook {
+	hooks := c.hooks.SopStage
+	return append(hooks[:len(hooks):len(hooks)], sopstage.Hooks[:]...)
+}
+
+// Interceptors returns the client interceptors.
+func (c *SopStageClient) Interceptors() []Interceptor {
+	inters := c.inters.SopStage
+	return append(inters[:len(inters):len(inters)], sopstage.Interceptors[:]...)
+}
+
+func (c *SopStageClient) mutate(ctx context.Context, m *SopStageMutation) (Value, error) {
+	switch m.Op() {
+	case OpCreate:
+		return (&SopStageCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
+	case OpUpdate:
+		return (&SopStageUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
+	case OpUpdateOne:
+		return (&SopStageUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
+	case OpDelete, OpDeleteOne:
+		return (&SopStageDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx)
+	default:
+		return nil, fmt.Errorf("ent: unknown SopStage mutation op: %q", m.Op())
+	}
+}
+
+// SopTaskClient is a client for the SopTask schema.
+type SopTaskClient struct {
+	config
+}
+
+// NewSopTaskClient returns a client for the SopTask from the given config.
+func NewSopTaskClient(c config) *SopTaskClient {
+	return &SopTaskClient{config: c}
+}
+
+// Use adds a list of mutation hooks to the hooks stack.
+// A call to `Use(f, g, h)` equals to `soptask.Hooks(f(g(h())))`.
+func (c *SopTaskClient) Use(hooks ...Hook) {
+	c.hooks.SopTask = append(c.hooks.SopTask, hooks...)
+}
+
+// Intercept adds a list of query interceptors to the interceptors stack.
+// A call to `Intercept(f, g, h)` equals to `soptask.Intercept(f(g(h())))`.
+func (c *SopTaskClient) Intercept(interceptors ...Interceptor) {
+	c.inters.SopTask = append(c.inters.SopTask, interceptors...)
+}
+
+// Create returns a builder for creating a SopTask entity.
+func (c *SopTaskClient) Create() *SopTaskCreate {
+	mutation := newSopTaskMutation(c.config, OpCreate)
+	return &SopTaskCreate{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// CreateBulk returns a builder for creating a bulk of SopTask entities.
+func (c *SopTaskClient) CreateBulk(builders ...*SopTaskCreate) *SopTaskCreateBulk {
+	return &SopTaskCreateBulk{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 *SopTaskClient) MapCreateBulk(slice any, setFunc func(*SopTaskCreate, int)) *SopTaskCreateBulk {
+	rv := reflect.ValueOf(slice)
+	if rv.Kind() != reflect.Slice {
+		return &SopTaskCreateBulk{err: fmt.Errorf("calling to SopTaskClient.MapCreateBulk with wrong type %T, need slice", slice)}
+	}
+	builders := make([]*SopTaskCreate, rv.Len())
+	for i := 0; i < rv.Len(); i++ {
+		builders[i] = c.Create()
+		setFunc(builders[i], i)
+	}
+	return &SopTaskCreateBulk{config: c.config, builders: builders}
+}
+
+// Update returns an update builder for SopTask.
+func (c *SopTaskClient) Update() *SopTaskUpdate {
+	mutation := newSopTaskMutation(c.config, OpUpdate)
+	return &SopTaskUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// UpdateOne returns an update builder for the given entity.
+func (c *SopTaskClient) UpdateOne(st *SopTask) *SopTaskUpdateOne {
+	mutation := newSopTaskMutation(c.config, OpUpdateOne, withSopTask(st))
+	return &SopTaskUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// UpdateOneID returns an update builder for the given id.
+func (c *SopTaskClient) UpdateOneID(id uint64) *SopTaskUpdateOne {
+	mutation := newSopTaskMutation(c.config, OpUpdateOne, withSopTaskID(id))
+	return &SopTaskUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// Delete returns a delete builder for SopTask.
+func (c *SopTaskClient) Delete() *SopTaskDelete {
+	mutation := newSopTaskMutation(c.config, OpDelete)
+	return &SopTaskDelete{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// DeleteOne returns a builder for deleting the given entity.
+func (c *SopTaskClient) DeleteOne(st *SopTask) *SopTaskDeleteOne {
+	return c.DeleteOneID(st.ID)
+}
+
+// DeleteOneID returns a builder for deleting the given entity by its id.
+func (c *SopTaskClient) DeleteOneID(id uint64) *SopTaskDeleteOne {
+	builder := c.Delete().Where(soptask.ID(id))
+	builder.mutation.id = &id
+	builder.mutation.op = OpDeleteOne
+	return &SopTaskDeleteOne{builder}
+}
+
+// Query returns a query builder for SopTask.
+func (c *SopTaskClient) Query() *SopTaskQuery {
+	return &SopTaskQuery{
+		config: c.config,
+		ctx:    &QueryContext{Type: TypeSopTask},
+		inters: c.Interceptors(),
+	}
+}
+
+// Get returns a SopTask entity by its id.
+func (c *SopTaskClient) Get(ctx context.Context, id uint64) (*SopTask, error) {
+	return c.Query().Where(soptask.ID(id)).Only(ctx)
+}
+
+// GetX is like Get, but panics if an error occurs.
+func (c *SopTaskClient) GetX(ctx context.Context, id uint64) *SopTask {
+	obj, err := c.Get(ctx, id)
+	if err != nil {
+		panic(err)
+	}
+	return obj
+}
+
+// QueryTaskStages queries the task_stages edge of a SopTask.
+func (c *SopTaskClient) QueryTaskStages(st *SopTask) *SopStageQuery {
+	query := (&SopStageClient{config: c.config}).Query()
+	query.path = func(context.Context) (fromV *sql.Selector, _ error) {
+		id := st.ID
+		step := sqlgraph.NewStep(
+			sqlgraph.From(soptask.Table, soptask.FieldID, id),
+			sqlgraph.To(sopstage.Table, sopstage.FieldID),
+			sqlgraph.Edge(sqlgraph.O2M, false, soptask.TaskStagesTable, soptask.TaskStagesColumn),
+		)
+		fromV = sqlgraph.Neighbors(st.driver.Dialect(), step)
+		return fromV, nil
+	}
+	return query
+}
+
+// Hooks returns the client hooks.
+func (c *SopTaskClient) Hooks() []Hook {
+	hooks := c.hooks.SopTask
+	return append(hooks[:len(hooks):len(hooks)], soptask.Hooks[:]...)
+}
+
+// Interceptors returns the client interceptors.
+func (c *SopTaskClient) Interceptors() []Interceptor {
+	inters := c.inters.SopTask
+	return append(inters[:len(inters):len(inters)], soptask.Interceptors[:]...)
+}
+
+func (c *SopTaskClient) mutate(ctx context.Context, m *SopTaskMutation) (Value, error) {
+	switch m.Op() {
+	case OpCreate:
+		return (&SopTaskCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
+	case OpUpdate:
+		return (&SopTaskUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
+	case OpUpdateOne:
+		return (&SopTaskUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
+	case OpDelete, OpDeleteOne:
+		return (&SopTaskDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx)
+	default:
+		return nil, fmt.Errorf("ent: unknown SopTask mutation op: %q", m.Op())
+	}
+}
+
 // WxClient is a client for the Wx schema.
 type WxClient struct {
 	config
@@ -1155,10 +1793,12 @@ func (c *WxClient) mutate(ctx context.Context, m *WxMutation) (Value, error) {
 // hooks and interceptors per client, for fast access.
 type (
 	hooks struct {
-		Contact, Label, LabelRelationship, Message, Server, Wx []ent.Hook
+		Contact, Label, LabelRelationship, Message, MessageRecords, Server, SopNode,
+		SopStage, SopTask, Wx []ent.Hook
 	}
 	inters struct {
-		Contact, Label, LabelRelationship, Message, Server, Wx []ent.Interceptor
+		Contact, Label, LabelRelationship, Message, MessageRecords, Server, SopNode,
+		SopStage, SopTask, Wx []ent.Interceptor
 	}
 )
 

+ 11 - 0
ent/custom_types/types.go

@@ -0,0 +1,11 @@
+package custom_types
+
+type Condition struct {
+	Equal       int   `json:"equal"`
+	LabelIdList []int `json:"labelIdList"`
+}
+
+type Action struct {
+	Type    int    `json:"type"`
+	Content string `json:"content"`
+}

+ 8 - 0
ent/ent.go

@@ -12,7 +12,11 @@ import (
 	"wechat-api/ent/label"
 	"wechat-api/ent/labelrelationship"
 	"wechat-api/ent/message"
+	"wechat-api/ent/messagerecords"
 	"wechat-api/ent/server"
+	"wechat-api/ent/sopnode"
+	"wechat-api/ent/sopstage"
+	"wechat-api/ent/soptask"
 	"wechat-api/ent/wx"
 
 	"entgo.io/ent"
@@ -82,7 +86,11 @@ func checkColumn(table, column string) error {
 			label.Table:             label.ValidColumn,
 			labelrelationship.Table: labelrelationship.ValidColumn,
 			message.Table:           message.ValidColumn,
+			messagerecords.Table:    messagerecords.ValidColumn,
 			server.Table:            server.ValidColumn,
+			sopnode.Table:           sopnode.ValidColumn,
+			sopstage.Table:          sopstage.ValidColumn,
+			soptask.Table:           soptask.ValidColumn,
 			wx.Table:                wx.ValidColumn,
 		})
 	})

+ 48 - 0
ent/hook/hook.go

@@ -56,6 +56,18 @@ func (f MessageFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, err
 	return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.MessageMutation", m)
 }
 
+// The MessageRecordsFunc type is an adapter to allow the use of ordinary
+// function as MessageRecords mutator.
+type MessageRecordsFunc func(context.Context, *ent.MessageRecordsMutation) (ent.Value, error)
+
+// Mutate calls f(ctx, m).
+func (f MessageRecordsFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) {
+	if mv, ok := m.(*ent.MessageRecordsMutation); ok {
+		return f(ctx, mv)
+	}
+	return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.MessageRecordsMutation", m)
+}
+
 // The ServerFunc type is an adapter to allow the use of ordinary
 // function as Server mutator.
 type ServerFunc func(context.Context, *ent.ServerMutation) (ent.Value, error)
@@ -68,6 +80,42 @@ func (f ServerFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, erro
 	return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.ServerMutation", m)
 }
 
+// The SopNodeFunc type is an adapter to allow the use of ordinary
+// function as SopNode mutator.
+type SopNodeFunc func(context.Context, *ent.SopNodeMutation) (ent.Value, error)
+
+// Mutate calls f(ctx, m).
+func (f SopNodeFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) {
+	if mv, ok := m.(*ent.SopNodeMutation); ok {
+		return f(ctx, mv)
+	}
+	return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.SopNodeMutation", m)
+}
+
+// The SopStageFunc type is an adapter to allow the use of ordinary
+// function as SopStage mutator.
+type SopStageFunc func(context.Context, *ent.SopStageMutation) (ent.Value, error)
+
+// Mutate calls f(ctx, m).
+func (f SopStageFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) {
+	if mv, ok := m.(*ent.SopStageMutation); ok {
+		return f(ctx, mv)
+	}
+	return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.SopStageMutation", m)
+}
+
+// The SopTaskFunc type is an adapter to allow the use of ordinary
+// function as SopTask mutator.
+type SopTaskFunc func(context.Context, *ent.SopTaskMutation) (ent.Value, error)
+
+// Mutate calls f(ctx, m).
+func (f SopTaskFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) {
+	if mv, ok := m.(*ent.SopTaskMutation); ok {
+		return f(ctx, mv)
+	}
+	return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.SopTaskMutation", m)
+}
+
 // The WxFunc type is an adapter to allow the use of ordinary
 // function as Wx mutator.
 type WxFunc func(context.Context, *ent.WxMutation) (ent.Value, error)

+ 120 - 0
ent/intercept/intercept.go

@@ -10,8 +10,12 @@ import (
 	"wechat-api/ent/label"
 	"wechat-api/ent/labelrelationship"
 	"wechat-api/ent/message"
+	"wechat-api/ent/messagerecords"
 	"wechat-api/ent/predicate"
 	"wechat-api/ent/server"
+	"wechat-api/ent/sopnode"
+	"wechat-api/ent/sopstage"
+	"wechat-api/ent/soptask"
 	"wechat-api/ent/wx"
 
 	"entgo.io/ent/dialect/sql"
@@ -181,6 +185,33 @@ func (f TraverseMessage) Traverse(ctx context.Context, q ent.Query) error {
 	return fmt.Errorf("unexpected query type %T. expect *ent.MessageQuery", q)
 }
 
+// The MessageRecordsFunc type is an adapter to allow the use of ordinary function as a Querier.
+type MessageRecordsFunc func(context.Context, *ent.MessageRecordsQuery) (ent.Value, error)
+
+// Query calls f(ctx, q).
+func (f MessageRecordsFunc) Query(ctx context.Context, q ent.Query) (ent.Value, error) {
+	if q, ok := q.(*ent.MessageRecordsQuery); ok {
+		return f(ctx, q)
+	}
+	return nil, fmt.Errorf("unexpected query type %T. expect *ent.MessageRecordsQuery", q)
+}
+
+// The TraverseMessageRecords type is an adapter to allow the use of ordinary function as Traverser.
+type TraverseMessageRecords func(context.Context, *ent.MessageRecordsQuery) error
+
+// Intercept is a dummy implementation of Intercept that returns the next Querier in the pipeline.
+func (f TraverseMessageRecords) Intercept(next ent.Querier) ent.Querier {
+	return next
+}
+
+// Traverse calls f(ctx, q).
+func (f TraverseMessageRecords) Traverse(ctx context.Context, q ent.Query) error {
+	if q, ok := q.(*ent.MessageRecordsQuery); ok {
+		return f(ctx, q)
+	}
+	return fmt.Errorf("unexpected query type %T. expect *ent.MessageRecordsQuery", q)
+}
+
 // The ServerFunc type is an adapter to allow the use of ordinary function as a Querier.
 type ServerFunc func(context.Context, *ent.ServerQuery) (ent.Value, error)
 
@@ -208,6 +239,87 @@ func (f TraverseServer) Traverse(ctx context.Context, q ent.Query) error {
 	return fmt.Errorf("unexpected query type %T. expect *ent.ServerQuery", q)
 }
 
+// The SopNodeFunc type is an adapter to allow the use of ordinary function as a Querier.
+type SopNodeFunc func(context.Context, *ent.SopNodeQuery) (ent.Value, error)
+
+// Query calls f(ctx, q).
+func (f SopNodeFunc) Query(ctx context.Context, q ent.Query) (ent.Value, error) {
+	if q, ok := q.(*ent.SopNodeQuery); ok {
+		return f(ctx, q)
+	}
+	return nil, fmt.Errorf("unexpected query type %T. expect *ent.SopNodeQuery", q)
+}
+
+// The TraverseSopNode type is an adapter to allow the use of ordinary function as Traverser.
+type TraverseSopNode func(context.Context, *ent.SopNodeQuery) error
+
+// Intercept is a dummy implementation of Intercept that returns the next Querier in the pipeline.
+func (f TraverseSopNode) Intercept(next ent.Querier) ent.Querier {
+	return next
+}
+
+// Traverse calls f(ctx, q).
+func (f TraverseSopNode) Traverse(ctx context.Context, q ent.Query) error {
+	if q, ok := q.(*ent.SopNodeQuery); ok {
+		return f(ctx, q)
+	}
+	return fmt.Errorf("unexpected query type %T. expect *ent.SopNodeQuery", q)
+}
+
+// The SopStageFunc type is an adapter to allow the use of ordinary function as a Querier.
+type SopStageFunc func(context.Context, *ent.SopStageQuery) (ent.Value, error)
+
+// Query calls f(ctx, q).
+func (f SopStageFunc) Query(ctx context.Context, q ent.Query) (ent.Value, error) {
+	if q, ok := q.(*ent.SopStageQuery); ok {
+		return f(ctx, q)
+	}
+	return nil, fmt.Errorf("unexpected query type %T. expect *ent.SopStageQuery", q)
+}
+
+// The TraverseSopStage type is an adapter to allow the use of ordinary function as Traverser.
+type TraverseSopStage func(context.Context, *ent.SopStageQuery) error
+
+// Intercept is a dummy implementation of Intercept that returns the next Querier in the pipeline.
+func (f TraverseSopStage) Intercept(next ent.Querier) ent.Querier {
+	return next
+}
+
+// Traverse calls f(ctx, q).
+func (f TraverseSopStage) Traverse(ctx context.Context, q ent.Query) error {
+	if q, ok := q.(*ent.SopStageQuery); ok {
+		return f(ctx, q)
+	}
+	return fmt.Errorf("unexpected query type %T. expect *ent.SopStageQuery", q)
+}
+
+// The SopTaskFunc type is an adapter to allow the use of ordinary function as a Querier.
+type SopTaskFunc func(context.Context, *ent.SopTaskQuery) (ent.Value, error)
+
+// Query calls f(ctx, q).
+func (f SopTaskFunc) Query(ctx context.Context, q ent.Query) (ent.Value, error) {
+	if q, ok := q.(*ent.SopTaskQuery); ok {
+		return f(ctx, q)
+	}
+	return nil, fmt.Errorf("unexpected query type %T. expect *ent.SopTaskQuery", q)
+}
+
+// The TraverseSopTask type is an adapter to allow the use of ordinary function as Traverser.
+type TraverseSopTask func(context.Context, *ent.SopTaskQuery) error
+
+// Intercept is a dummy implementation of Intercept that returns the next Querier in the pipeline.
+func (f TraverseSopTask) Intercept(next ent.Querier) ent.Querier {
+	return next
+}
+
+// Traverse calls f(ctx, q).
+func (f TraverseSopTask) Traverse(ctx context.Context, q ent.Query) error {
+	if q, ok := q.(*ent.SopTaskQuery); ok {
+		return f(ctx, q)
+	}
+	return fmt.Errorf("unexpected query type %T. expect *ent.SopTaskQuery", q)
+}
+
 // The WxFunc type is an adapter to allow the use of ordinary function as a Querier.
 type WxFunc func(context.Context, *ent.WxQuery) (ent.Value, error)
 
@@ -246,8 +358,16 @@ func NewQuery(q ent.Query) (Query, error) {
 		return &query[*ent.LabelRelationshipQuery, predicate.LabelRelationship, labelrelationship.OrderOption]{typ: ent.TypeLabelRelationship, tq: q}, nil
 	case *ent.MessageQuery:
 		return &query[*ent.MessageQuery, predicate.Message, message.OrderOption]{typ: ent.TypeMessage, tq: q}, nil
+	case *ent.MessageRecordsQuery:
+		return &query[*ent.MessageRecordsQuery, predicate.MessageRecords, messagerecords.OrderOption]{typ: ent.TypeMessageRecords, tq: q}, nil
 	case *ent.ServerQuery:
 		return &query[*ent.ServerQuery, predicate.Server, server.OrderOption]{typ: ent.TypeServer, tq: q}, nil
+	case *ent.SopNodeQuery:
+		return &query[*ent.SopNodeQuery, predicate.SopNode, sopnode.OrderOption]{typ: ent.TypeSopNode, tq: q}, nil
+	case *ent.SopStageQuery:
+		return &query[*ent.SopStageQuery, predicate.SopStage, sopstage.OrderOption]{typ: ent.TypeSopStage, tq: q}, nil
+	case *ent.SopTaskQuery:
+		return &query[*ent.SopTaskQuery, predicate.SopTask, soptask.OrderOption]{typ: ent.TypeSopTask, tq: q}, nil
 	case *ent.WxQuery:
 		return &query[*ent.WxQuery, predicate.Wx, wx.OrderOption]{typ: ent.TypeWx, tq: q}, nil
 	default:

+ 260 - 0
ent/messagerecords.go

@@ -0,0 +1,260 @@
+// Code generated by ent, DO NOT EDIT.
+
+package ent
+
+import (
+	"fmt"
+	"strings"
+	"time"
+	"wechat-api/ent/messagerecords"
+
+	"entgo.io/ent"
+	"entgo.io/ent/dialect/sql"
+)
+
+// MessageRecords is the model entity for the MessageRecords schema.
+type MessageRecords 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"`
+	// Status 1: normal 2: ban | 状态 1 正常 2 禁用
+	Status uint8 `json:"status,omitempty"`
+	// Delete Time | 删除日期
+	DeletedAt time.Time `json:"deleted_at,omitempty"`
+	// 机器人微信 id
+	BotWxid string `json:"bot_wxid,omitempty"`
+	// 联系人 id
+	ContactID int `json:"contact_id,omitempty"`
+	// 类型:1好友,2群组,3企业微信联系人
+	ContactType int `json:"contact_type,omitempty"`
+	// 接收方微信 id
+	ContactWxid string `json:"contact_wxid,omitempty"`
+	// 内容类型 1 文本 2 文件
+	ContentType int `json:"content_type,omitempty"`
+	// 发送内容
+	Content string `json:"content,omitempty"`
+	// 异常原因
+	ErrorDetail string `json:"error_detail,omitempty"`
+	// 发送时间
+	SendTime time.Time `json:"send_time,omitempty"`
+	// 源类型 1 点发 2 群发 3 SOP
+	SourceType int `json:"source_type,omitempty"`
+	// 源 ID
+	SourceID int `json:"source_id,omitempty"`
+	// 次源 ID
+	SubSourceID  int `json:"sub_source_id,omitempty"`
+	selectValues sql.SelectValues
+}
+
+// scanValues returns the types for scanning values from sql.Rows.
+func (*MessageRecords) scanValues(columns []string) ([]any, error) {
+	values := make([]any, len(columns))
+	for i := range columns {
+		switch columns[i] {
+		case messagerecords.FieldID, messagerecords.FieldStatus, messagerecords.FieldContactID, messagerecords.FieldContactType, messagerecords.FieldContentType, messagerecords.FieldSourceType, messagerecords.FieldSourceID, messagerecords.FieldSubSourceID:
+			values[i] = new(sql.NullInt64)
+		case messagerecords.FieldBotWxid, messagerecords.FieldContactWxid, messagerecords.FieldContent, messagerecords.FieldErrorDetail:
+			values[i] = new(sql.NullString)
+		case messagerecords.FieldCreatedAt, messagerecords.FieldUpdatedAt, messagerecords.FieldDeletedAt, messagerecords.FieldSendTime:
+			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 MessageRecords fields.
+func (mr *MessageRecords) 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 messagerecords.FieldID:
+			value, ok := values[i].(*sql.NullInt64)
+			if !ok {
+				return fmt.Errorf("unexpected type %T for field id", value)
+			}
+			mr.ID = uint64(value.Int64)
+		case messagerecords.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 {
+				mr.CreatedAt = value.Time
+			}
+		case messagerecords.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 {
+				mr.UpdatedAt = value.Time
+			}
+		case messagerecords.FieldStatus:
+			if value, ok := values[i].(*sql.NullInt64); !ok {
+				return fmt.Errorf("unexpected type %T for field status", values[i])
+			} else if value.Valid {
+				mr.Status = uint8(value.Int64)
+			}
+		case messagerecords.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 {
+				mr.DeletedAt = value.Time
+			}
+		case messagerecords.FieldBotWxid:
+			if value, ok := values[i].(*sql.NullString); !ok {
+				return fmt.Errorf("unexpected type %T for field bot_wxid", values[i])
+			} else if value.Valid {
+				mr.BotWxid = value.String
+			}
+		case messagerecords.FieldContactID:
+			if value, ok := values[i].(*sql.NullInt64); !ok {
+				return fmt.Errorf("unexpected type %T for field contact_id", values[i])
+			} else if value.Valid {
+				mr.ContactID = int(value.Int64)
+			}
+		case messagerecords.FieldContactType:
+			if value, ok := values[i].(*sql.NullInt64); !ok {
+				return fmt.Errorf("unexpected type %T for field contact_type", values[i])
+			} else if value.Valid {
+				mr.ContactType = int(value.Int64)
+			}
+		case messagerecords.FieldContactWxid:
+			if value, ok := values[i].(*sql.NullString); !ok {
+				return fmt.Errorf("unexpected type %T for field contact_wxid", values[i])
+			} else if value.Valid {
+				mr.ContactWxid = value.String
+			}
+		case messagerecords.FieldContentType:
+			if value, ok := values[i].(*sql.NullInt64); !ok {
+				return fmt.Errorf("unexpected type %T for field content_type", values[i])
+			} else if value.Valid {
+				mr.ContentType = int(value.Int64)
+			}
+		case messagerecords.FieldContent:
+			if value, ok := values[i].(*sql.NullString); !ok {
+				return fmt.Errorf("unexpected type %T for field content", values[i])
+			} else if value.Valid {
+				mr.Content = value.String
+			}
+		case messagerecords.FieldErrorDetail:
+			if value, ok := values[i].(*sql.NullString); !ok {
+				return fmt.Errorf("unexpected type %T for field error_detail", values[i])
+			} else if value.Valid {
+				mr.ErrorDetail = value.String
+			}
+		case messagerecords.FieldSendTime:
+			if value, ok := values[i].(*sql.NullTime); !ok {
+				return fmt.Errorf("unexpected type %T for field send_time", values[i])
+			} else if value.Valid {
+				mr.SendTime = value.Time
+			}
+		case messagerecords.FieldSourceType:
+			if value, ok := values[i].(*sql.NullInt64); !ok {
+				return fmt.Errorf("unexpected type %T for field source_type", values[i])
+			} else if value.Valid {
+				mr.SourceType = int(value.Int64)
+			}
+		case messagerecords.FieldSourceID:
+			if value, ok := values[i].(*sql.NullInt64); !ok {
+				return fmt.Errorf("unexpected type %T for field source_id", values[i])
+			} else if value.Valid {
+				mr.SourceID = int(value.Int64)
+			}
+		case messagerecords.FieldSubSourceID:
+			if value, ok := values[i].(*sql.NullInt64); !ok {
+				return fmt.Errorf("unexpected type %T for field sub_source_id", values[i])
+			} else if value.Valid {
+				mr.SubSourceID = int(value.Int64)
+			}
+		default:
+			mr.selectValues.Set(columns[i], values[i])
+		}
+	}
+	return nil
+}
+
+// Value returns the ent.Value that was dynamically selected and assigned to the MessageRecords.
+// This includes values selected through modifiers, order, etc.
+func (mr *MessageRecords) Value(name string) (ent.Value, error) {
+	return mr.selectValues.Get(name)
+}
+
+// Update returns a builder for updating this MessageRecords.
+// Note that you need to call MessageRecords.Unwrap() before calling this method if this MessageRecords
+// was returned from a transaction, and the transaction was committed or rolled back.
+func (mr *MessageRecords) Update() *MessageRecordsUpdateOne {
+	return NewMessageRecordsClient(mr.config).UpdateOne(mr)
+}
+
+// Unwrap unwraps the MessageRecords 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 (mr *MessageRecords) Unwrap() *MessageRecords {
+	_tx, ok := mr.config.driver.(*txDriver)
+	if !ok {
+		panic("ent: MessageRecords is not a transactional entity")
+	}
+	mr.config.driver = _tx.drv
+	return mr
+}
+
+// String implements the fmt.Stringer.
+func (mr *MessageRecords) String() string {
+	var builder strings.Builder
+	builder.WriteString("MessageRecords(")
+	builder.WriteString(fmt.Sprintf("id=%v, ", mr.ID))
+	builder.WriteString("created_at=")
+	builder.WriteString(mr.CreatedAt.Format(time.ANSIC))
+	builder.WriteString(", ")
+	builder.WriteString("updated_at=")
+	builder.WriteString(mr.UpdatedAt.Format(time.ANSIC))
+	builder.WriteString(", ")
+	builder.WriteString("status=")
+	builder.WriteString(fmt.Sprintf("%v", mr.Status))
+	builder.WriteString(", ")
+	builder.WriteString("deleted_at=")
+	builder.WriteString(mr.DeletedAt.Format(time.ANSIC))
+	builder.WriteString(", ")
+	builder.WriteString("bot_wxid=")
+	builder.WriteString(mr.BotWxid)
+	builder.WriteString(", ")
+	builder.WriteString("contact_id=")
+	builder.WriteString(fmt.Sprintf("%v", mr.ContactID))
+	builder.WriteString(", ")
+	builder.WriteString("contact_type=")
+	builder.WriteString(fmt.Sprintf("%v", mr.ContactType))
+	builder.WriteString(", ")
+	builder.WriteString("contact_wxid=")
+	builder.WriteString(mr.ContactWxid)
+	builder.WriteString(", ")
+	builder.WriteString("content_type=")
+	builder.WriteString(fmt.Sprintf("%v", mr.ContentType))
+	builder.WriteString(", ")
+	builder.WriteString("content=")
+	builder.WriteString(mr.Content)
+	builder.WriteString(", ")
+	builder.WriteString("error_detail=")
+	builder.WriteString(mr.ErrorDetail)
+	builder.WriteString(", ")
+	builder.WriteString("send_time=")
+	builder.WriteString(mr.SendTime.Format(time.ANSIC))
+	builder.WriteString(", ")
+	builder.WriteString("source_type=")
+	builder.WriteString(fmt.Sprintf("%v", mr.SourceType))
+	builder.WriteString(", ")
+	builder.WriteString("source_id=")
+	builder.WriteString(fmt.Sprintf("%v", mr.SourceID))
+	builder.WriteString(", ")
+	builder.WriteString("sub_source_id=")
+	builder.WriteString(fmt.Sprintf("%v", mr.SubSourceID))
+	builder.WriteByte(')')
+	return builder.String()
+}
+
+// MessageRecordsSlice is a parsable slice of MessageRecords.
+type MessageRecordsSlice []*MessageRecords

+ 196 - 0
ent/messagerecords/messagerecords.go

@@ -0,0 +1,196 @@
+// Code generated by ent, DO NOT EDIT.
+
+package messagerecords
+
+import (
+	"time"
+
+	"entgo.io/ent"
+	"entgo.io/ent/dialect/sql"
+)
+
+const (
+	// Label holds the string label denoting the messagerecords type in the database.
+	Label = "message_records"
+	// 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"
+	// FieldStatus holds the string denoting the status field in the database.
+	FieldStatus = "status"
+	// FieldDeletedAt holds the string denoting the deleted_at field in the database.
+	FieldDeletedAt = "deleted_at"
+	// FieldBotWxid holds the string denoting the bot_wxid field in the database.
+	FieldBotWxid = "bot_wxid"
+	// FieldContactID holds the string denoting the contact_id field in the database.
+	FieldContactID = "contact_id"
+	// FieldContactType holds the string denoting the contact_type field in the database.
+	FieldContactType = "contact_type"
+	// FieldContactWxid holds the string denoting the contact_wxid field in the database.
+	FieldContactWxid = "contact_wxid"
+	// FieldContentType holds the string denoting the content_type field in the database.
+	FieldContentType = "content_type"
+	// FieldContent holds the string denoting the content field in the database.
+	FieldContent = "content"
+	// FieldErrorDetail holds the string denoting the error_detail field in the database.
+	FieldErrorDetail = "error_detail"
+	// FieldSendTime holds the string denoting the send_time field in the database.
+	FieldSendTime = "send_time"
+	// FieldSourceType holds the string denoting the source_type field in the database.
+	FieldSourceType = "source_type"
+	// FieldSourceID holds the string denoting the source_id field in the database.
+	FieldSourceID = "source_id"
+	// FieldSubSourceID holds the string denoting the sub_source_id field in the database.
+	FieldSubSourceID = "sub_source_id"
+	// Table holds the table name of the messagerecords in the database.
+	Table = "message_records"
+)
+
+// Columns holds all SQL columns for messagerecords fields.
+var Columns = []string{
+	FieldID,
+	FieldCreatedAt,
+	FieldUpdatedAt,
+	FieldStatus,
+	FieldDeletedAt,
+	FieldBotWxid,
+	FieldContactID,
+	FieldContactType,
+	FieldContactWxid,
+	FieldContentType,
+	FieldContent,
+	FieldErrorDetail,
+	FieldSendTime,
+	FieldSourceType,
+	FieldSourceID,
+	FieldSubSourceID,
+}
+
+// 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
+	// DefaultStatus holds the default value on creation for the "status" field.
+	DefaultStatus uint8
+	// DefaultContactType holds the default value on creation for the "contact_type" field.
+	DefaultContactType int
+	// DefaultContactWxid holds the default value on creation for the "contact_wxid" field.
+	DefaultContactWxid string
+	// DefaultContentType holds the default value on creation for the "content_type" field.
+	DefaultContentType int
+	// DefaultContent holds the default value on creation for the "content" field.
+	DefaultContent string
+	// DefaultErrorDetail holds the default value on creation for the "error_detail" field.
+	DefaultErrorDetail string
+	// DefaultSourceType holds the default value on creation for the "source_type" field.
+	DefaultSourceType int
+	// DefaultSourceID holds the default value on creation for the "source_id" field.
+	DefaultSourceID int
+	// DefaultSubSourceID holds the default value on creation for the "sub_source_id" field.
+	DefaultSubSourceID int
+)
+
+// OrderOption defines the ordering options for the MessageRecords 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()
+}
+
+// ByStatus orders the results by the status field.
+func ByStatus(opts ...sql.OrderTermOption) OrderOption {
+	return sql.OrderByField(FieldStatus, opts...).ToFunc()
+}
+
+// ByDeletedAt orders the results by the deleted_at field.
+func ByDeletedAt(opts ...sql.OrderTermOption) OrderOption {
+	return sql.OrderByField(FieldDeletedAt, opts...).ToFunc()
+}
+
+// ByBotWxid orders the results by the bot_wxid field.
+func ByBotWxid(opts ...sql.OrderTermOption) OrderOption {
+	return sql.OrderByField(FieldBotWxid, opts...).ToFunc()
+}
+
+// ByContactID orders the results by the contact_id field.
+func ByContactID(opts ...sql.OrderTermOption) OrderOption {
+	return sql.OrderByField(FieldContactID, opts...).ToFunc()
+}
+
+// ByContactType orders the results by the contact_type field.
+func ByContactType(opts ...sql.OrderTermOption) OrderOption {
+	return sql.OrderByField(FieldContactType, opts...).ToFunc()
+}
+
+// ByContactWxid orders the results by the contact_wxid field.
+func ByContactWxid(opts ...sql.OrderTermOption) OrderOption {
+	return sql.OrderByField(FieldContactWxid, opts...).ToFunc()
+}
+
+// ByContentType orders the results by the content_type field.
+func ByContentType(opts ...sql.OrderTermOption) OrderOption {
+	return sql.OrderByField(FieldContentType, opts...).ToFunc()
+}
+
+// ByContent orders the results by the content field.
+func ByContent(opts ...sql.OrderTermOption) OrderOption {
+	return sql.OrderByField(FieldContent, opts...).ToFunc()
+}
+
+// ByErrorDetail orders the results by the error_detail field.
+func ByErrorDetail(opts ...sql.OrderTermOption) OrderOption {
+	return sql.OrderByField(FieldErrorDetail, opts...).ToFunc()
+}
+
+// BySendTime orders the results by the send_time field.
+func BySendTime(opts ...sql.OrderTermOption) OrderOption {
+	return sql.OrderByField(FieldSendTime, opts...).ToFunc()
+}
+
+// BySourceType orders the results by the source_type field.
+func BySourceType(opts ...sql.OrderTermOption) OrderOption {
+	return sql.OrderByField(FieldSourceType, opts...).ToFunc()
+}
+
+// BySourceID orders the results by the source_id field.
+func BySourceID(opts ...sql.OrderTermOption) OrderOption {
+	return sql.OrderByField(FieldSourceID, opts...).ToFunc()
+}
+
+// BySubSourceID orders the results by the sub_source_id field.
+func BySubSourceID(opts ...sql.OrderTermOption) OrderOption {
+	return sql.OrderByField(FieldSubSourceID, opts...).ToFunc()
+}

+ 875 - 0
ent/messagerecords/where.go

@@ -0,0 +1,875 @@
+// Code generated by ent, DO NOT EDIT.
+
+package messagerecords
+
+import (
+	"time"
+	"wechat-api/ent/predicate"
+
+	"entgo.io/ent/dialect/sql"
+)
+
+// ID filters vertices based on their ID field.
+func ID(id uint64) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldEQ(FieldID, id))
+}
+
+// IDEQ applies the EQ predicate on the ID field.
+func IDEQ(id uint64) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldEQ(FieldID, id))
+}
+
+// IDNEQ applies the NEQ predicate on the ID field.
+func IDNEQ(id uint64) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldNEQ(FieldID, id))
+}
+
+// IDIn applies the In predicate on the ID field.
+func IDIn(ids ...uint64) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldIn(FieldID, ids...))
+}
+
+// IDNotIn applies the NotIn predicate on the ID field.
+func IDNotIn(ids ...uint64) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldNotIn(FieldID, ids...))
+}
+
+// IDGT applies the GT predicate on the ID field.
+func IDGT(id uint64) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldGT(FieldID, id))
+}
+
+// IDGTE applies the GTE predicate on the ID field.
+func IDGTE(id uint64) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldGTE(FieldID, id))
+}
+
+// IDLT applies the LT predicate on the ID field.
+func IDLT(id uint64) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldLT(FieldID, id))
+}
+
+// IDLTE applies the LTE predicate on the ID field.
+func IDLTE(id uint64) predicate.MessageRecords {
+	return predicate.MessageRecords(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.MessageRecords {
+	return predicate.MessageRecords(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.MessageRecords {
+	return predicate.MessageRecords(sql.FieldEQ(FieldUpdatedAt, v))
+}
+
+// Status applies equality check predicate on the "status" field. It's identical to StatusEQ.
+func Status(v uint8) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldEQ(FieldStatus, v))
+}
+
+// DeletedAt applies equality check predicate on the "deleted_at" field. It's identical to DeletedAtEQ.
+func DeletedAt(v time.Time) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldEQ(FieldDeletedAt, v))
+}
+
+// BotWxid applies equality check predicate on the "bot_wxid" field. It's identical to BotWxidEQ.
+func BotWxid(v string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldEQ(FieldBotWxid, v))
+}
+
+// ContactID applies equality check predicate on the "contact_id" field. It's identical to ContactIDEQ.
+func ContactID(v int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldEQ(FieldContactID, v))
+}
+
+// ContactType applies equality check predicate on the "contact_type" field. It's identical to ContactTypeEQ.
+func ContactType(v int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldEQ(FieldContactType, v))
+}
+
+// ContactWxid applies equality check predicate on the "contact_wxid" field. It's identical to ContactWxidEQ.
+func ContactWxid(v string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldEQ(FieldContactWxid, v))
+}
+
+// ContentType applies equality check predicate on the "content_type" field. It's identical to ContentTypeEQ.
+func ContentType(v int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldEQ(FieldContentType, v))
+}
+
+// Content applies equality check predicate on the "content" field. It's identical to ContentEQ.
+func Content(v string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldEQ(FieldContent, v))
+}
+
+// ErrorDetail applies equality check predicate on the "error_detail" field. It's identical to ErrorDetailEQ.
+func ErrorDetail(v string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldEQ(FieldErrorDetail, v))
+}
+
+// SendTime applies equality check predicate on the "send_time" field. It's identical to SendTimeEQ.
+func SendTime(v time.Time) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldEQ(FieldSendTime, v))
+}
+
+// SourceType applies equality check predicate on the "source_type" field. It's identical to SourceTypeEQ.
+func SourceType(v int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldEQ(FieldSourceType, v))
+}
+
+// SourceID applies equality check predicate on the "source_id" field. It's identical to SourceIDEQ.
+func SourceID(v int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldEQ(FieldSourceID, v))
+}
+
+// SubSourceID applies equality check predicate on the "sub_source_id" field. It's identical to SubSourceIDEQ.
+func SubSourceID(v int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldEQ(FieldSubSourceID, v))
+}
+
+// CreatedAtEQ applies the EQ predicate on the "created_at" field.
+func CreatedAtEQ(v time.Time) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldEQ(FieldCreatedAt, v))
+}
+
+// CreatedAtNEQ applies the NEQ predicate on the "created_at" field.
+func CreatedAtNEQ(v time.Time) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldNEQ(FieldCreatedAt, v))
+}
+
+// CreatedAtIn applies the In predicate on the "created_at" field.
+func CreatedAtIn(vs ...time.Time) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldIn(FieldCreatedAt, vs...))
+}
+
+// CreatedAtNotIn applies the NotIn predicate on the "created_at" field.
+func CreatedAtNotIn(vs ...time.Time) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldNotIn(FieldCreatedAt, vs...))
+}
+
+// CreatedAtGT applies the GT predicate on the "created_at" field.
+func CreatedAtGT(v time.Time) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldGT(FieldCreatedAt, v))
+}
+
+// CreatedAtGTE applies the GTE predicate on the "created_at" field.
+func CreatedAtGTE(v time.Time) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldGTE(FieldCreatedAt, v))
+}
+
+// CreatedAtLT applies the LT predicate on the "created_at" field.
+func CreatedAtLT(v time.Time) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldLT(FieldCreatedAt, v))
+}
+
+// CreatedAtLTE applies the LTE predicate on the "created_at" field.
+func CreatedAtLTE(v time.Time) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldLTE(FieldCreatedAt, v))
+}
+
+// UpdatedAtEQ applies the EQ predicate on the "updated_at" field.
+func UpdatedAtEQ(v time.Time) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldEQ(FieldUpdatedAt, v))
+}
+
+// UpdatedAtNEQ applies the NEQ predicate on the "updated_at" field.
+func UpdatedAtNEQ(v time.Time) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldNEQ(FieldUpdatedAt, v))
+}
+
+// UpdatedAtIn applies the In predicate on the "updated_at" field.
+func UpdatedAtIn(vs ...time.Time) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldIn(FieldUpdatedAt, vs...))
+}
+
+// UpdatedAtNotIn applies the NotIn predicate on the "updated_at" field.
+func UpdatedAtNotIn(vs ...time.Time) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldNotIn(FieldUpdatedAt, vs...))
+}
+
+// UpdatedAtGT applies the GT predicate on the "updated_at" field.
+func UpdatedAtGT(v time.Time) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldGT(FieldUpdatedAt, v))
+}
+
+// UpdatedAtGTE applies the GTE predicate on the "updated_at" field.
+func UpdatedAtGTE(v time.Time) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldGTE(FieldUpdatedAt, v))
+}
+
+// UpdatedAtLT applies the LT predicate on the "updated_at" field.
+func UpdatedAtLT(v time.Time) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldLT(FieldUpdatedAt, v))
+}
+
+// UpdatedAtLTE applies the LTE predicate on the "updated_at" field.
+func UpdatedAtLTE(v time.Time) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldLTE(FieldUpdatedAt, v))
+}
+
+// StatusEQ applies the EQ predicate on the "status" field.
+func StatusEQ(v uint8) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldEQ(FieldStatus, v))
+}
+
+// StatusNEQ applies the NEQ predicate on the "status" field.
+func StatusNEQ(v uint8) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldNEQ(FieldStatus, v))
+}
+
+// StatusIn applies the In predicate on the "status" field.
+func StatusIn(vs ...uint8) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldIn(FieldStatus, vs...))
+}
+
+// StatusNotIn applies the NotIn predicate on the "status" field.
+func StatusNotIn(vs ...uint8) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldNotIn(FieldStatus, vs...))
+}
+
+// StatusGT applies the GT predicate on the "status" field.
+func StatusGT(v uint8) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldGT(FieldStatus, v))
+}
+
+// StatusGTE applies the GTE predicate on the "status" field.
+func StatusGTE(v uint8) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldGTE(FieldStatus, v))
+}
+
+// StatusLT applies the LT predicate on the "status" field.
+func StatusLT(v uint8) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldLT(FieldStatus, v))
+}
+
+// StatusLTE applies the LTE predicate on the "status" field.
+func StatusLTE(v uint8) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldLTE(FieldStatus, v))
+}
+
+// StatusIsNil applies the IsNil predicate on the "status" field.
+func StatusIsNil() predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldIsNull(FieldStatus))
+}
+
+// StatusNotNil applies the NotNil predicate on the "status" field.
+func StatusNotNil() predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldNotNull(FieldStatus))
+}
+
+// DeletedAtEQ applies the EQ predicate on the "deleted_at" field.
+func DeletedAtEQ(v time.Time) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldEQ(FieldDeletedAt, v))
+}
+
+// DeletedAtNEQ applies the NEQ predicate on the "deleted_at" field.
+func DeletedAtNEQ(v time.Time) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldNEQ(FieldDeletedAt, v))
+}
+
+// DeletedAtIn applies the In predicate on the "deleted_at" field.
+func DeletedAtIn(vs ...time.Time) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldIn(FieldDeletedAt, vs...))
+}
+
+// DeletedAtNotIn applies the NotIn predicate on the "deleted_at" field.
+func DeletedAtNotIn(vs ...time.Time) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldNotIn(FieldDeletedAt, vs...))
+}
+
+// DeletedAtGT applies the GT predicate on the "deleted_at" field.
+func DeletedAtGT(v time.Time) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldGT(FieldDeletedAt, v))
+}
+
+// DeletedAtGTE applies the GTE predicate on the "deleted_at" field.
+func DeletedAtGTE(v time.Time) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldGTE(FieldDeletedAt, v))
+}
+
+// DeletedAtLT applies the LT predicate on the "deleted_at" field.
+func DeletedAtLT(v time.Time) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldLT(FieldDeletedAt, v))
+}
+
+// DeletedAtLTE applies the LTE predicate on the "deleted_at" field.
+func DeletedAtLTE(v time.Time) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldLTE(FieldDeletedAt, v))
+}
+
+// DeletedAtIsNil applies the IsNil predicate on the "deleted_at" field.
+func DeletedAtIsNil() predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldIsNull(FieldDeletedAt))
+}
+
+// DeletedAtNotNil applies the NotNil predicate on the "deleted_at" field.
+func DeletedAtNotNil() predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldNotNull(FieldDeletedAt))
+}
+
+// BotWxidEQ applies the EQ predicate on the "bot_wxid" field.
+func BotWxidEQ(v string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldEQ(FieldBotWxid, v))
+}
+
+// BotWxidNEQ applies the NEQ predicate on the "bot_wxid" field.
+func BotWxidNEQ(v string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldNEQ(FieldBotWxid, v))
+}
+
+// BotWxidIn applies the In predicate on the "bot_wxid" field.
+func BotWxidIn(vs ...string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldIn(FieldBotWxid, vs...))
+}
+
+// BotWxidNotIn applies the NotIn predicate on the "bot_wxid" field.
+func BotWxidNotIn(vs ...string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldNotIn(FieldBotWxid, vs...))
+}
+
+// BotWxidGT applies the GT predicate on the "bot_wxid" field.
+func BotWxidGT(v string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldGT(FieldBotWxid, v))
+}
+
+// BotWxidGTE applies the GTE predicate on the "bot_wxid" field.
+func BotWxidGTE(v string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldGTE(FieldBotWxid, v))
+}
+
+// BotWxidLT applies the LT predicate on the "bot_wxid" field.
+func BotWxidLT(v string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldLT(FieldBotWxid, v))
+}
+
+// BotWxidLTE applies the LTE predicate on the "bot_wxid" field.
+func BotWxidLTE(v string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldLTE(FieldBotWxid, v))
+}
+
+// BotWxidContains applies the Contains predicate on the "bot_wxid" field.
+func BotWxidContains(v string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldContains(FieldBotWxid, v))
+}
+
+// BotWxidHasPrefix applies the HasPrefix predicate on the "bot_wxid" field.
+func BotWxidHasPrefix(v string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldHasPrefix(FieldBotWxid, v))
+}
+
+// BotWxidHasSuffix applies the HasSuffix predicate on the "bot_wxid" field.
+func BotWxidHasSuffix(v string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldHasSuffix(FieldBotWxid, v))
+}
+
+// BotWxidEqualFold applies the EqualFold predicate on the "bot_wxid" field.
+func BotWxidEqualFold(v string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldEqualFold(FieldBotWxid, v))
+}
+
+// BotWxidContainsFold applies the ContainsFold predicate on the "bot_wxid" field.
+func BotWxidContainsFold(v string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldContainsFold(FieldBotWxid, v))
+}
+
+// ContactIDEQ applies the EQ predicate on the "contact_id" field.
+func ContactIDEQ(v int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldEQ(FieldContactID, v))
+}
+
+// ContactIDNEQ applies the NEQ predicate on the "contact_id" field.
+func ContactIDNEQ(v int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldNEQ(FieldContactID, v))
+}
+
+// ContactIDIn applies the In predicate on the "contact_id" field.
+func ContactIDIn(vs ...int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldIn(FieldContactID, vs...))
+}
+
+// ContactIDNotIn applies the NotIn predicate on the "contact_id" field.
+func ContactIDNotIn(vs ...int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldNotIn(FieldContactID, vs...))
+}
+
+// ContactIDGT applies the GT predicate on the "contact_id" field.
+func ContactIDGT(v int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldGT(FieldContactID, v))
+}
+
+// ContactIDGTE applies the GTE predicate on the "contact_id" field.
+func ContactIDGTE(v int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldGTE(FieldContactID, v))
+}
+
+// ContactIDLT applies the LT predicate on the "contact_id" field.
+func ContactIDLT(v int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldLT(FieldContactID, v))
+}
+
+// ContactIDLTE applies the LTE predicate on the "contact_id" field.
+func ContactIDLTE(v int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldLTE(FieldContactID, v))
+}
+
+// ContactTypeEQ applies the EQ predicate on the "contact_type" field.
+func ContactTypeEQ(v int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldEQ(FieldContactType, v))
+}
+
+// ContactTypeNEQ applies the NEQ predicate on the "contact_type" field.
+func ContactTypeNEQ(v int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldNEQ(FieldContactType, v))
+}
+
+// ContactTypeIn applies the In predicate on the "contact_type" field.
+func ContactTypeIn(vs ...int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldIn(FieldContactType, vs...))
+}
+
+// ContactTypeNotIn applies the NotIn predicate on the "contact_type" field.
+func ContactTypeNotIn(vs ...int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldNotIn(FieldContactType, vs...))
+}
+
+// ContactTypeGT applies the GT predicate on the "contact_type" field.
+func ContactTypeGT(v int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldGT(FieldContactType, v))
+}
+
+// ContactTypeGTE applies the GTE predicate on the "contact_type" field.
+func ContactTypeGTE(v int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldGTE(FieldContactType, v))
+}
+
+// ContactTypeLT applies the LT predicate on the "contact_type" field.
+func ContactTypeLT(v int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldLT(FieldContactType, v))
+}
+
+// ContactTypeLTE applies the LTE predicate on the "contact_type" field.
+func ContactTypeLTE(v int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldLTE(FieldContactType, v))
+}
+
+// ContactWxidEQ applies the EQ predicate on the "contact_wxid" field.
+func ContactWxidEQ(v string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldEQ(FieldContactWxid, v))
+}
+
+// ContactWxidNEQ applies the NEQ predicate on the "contact_wxid" field.
+func ContactWxidNEQ(v string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldNEQ(FieldContactWxid, v))
+}
+
+// ContactWxidIn applies the In predicate on the "contact_wxid" field.
+func ContactWxidIn(vs ...string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldIn(FieldContactWxid, vs...))
+}
+
+// ContactWxidNotIn applies the NotIn predicate on the "contact_wxid" field.
+func ContactWxidNotIn(vs ...string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldNotIn(FieldContactWxid, vs...))
+}
+
+// ContactWxidGT applies the GT predicate on the "contact_wxid" field.
+func ContactWxidGT(v string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldGT(FieldContactWxid, v))
+}
+
+// ContactWxidGTE applies the GTE predicate on the "contact_wxid" field.
+func ContactWxidGTE(v string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldGTE(FieldContactWxid, v))
+}
+
+// ContactWxidLT applies the LT predicate on the "contact_wxid" field.
+func ContactWxidLT(v string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldLT(FieldContactWxid, v))
+}
+
+// ContactWxidLTE applies the LTE predicate on the "contact_wxid" field.
+func ContactWxidLTE(v string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldLTE(FieldContactWxid, v))
+}
+
+// ContactWxidContains applies the Contains predicate on the "contact_wxid" field.
+func ContactWxidContains(v string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldContains(FieldContactWxid, v))
+}
+
+// ContactWxidHasPrefix applies the HasPrefix predicate on the "contact_wxid" field.
+func ContactWxidHasPrefix(v string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldHasPrefix(FieldContactWxid, v))
+}
+
+// ContactWxidHasSuffix applies the HasSuffix predicate on the "contact_wxid" field.
+func ContactWxidHasSuffix(v string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldHasSuffix(FieldContactWxid, v))
+}
+
+// ContactWxidEqualFold applies the EqualFold predicate on the "contact_wxid" field.
+func ContactWxidEqualFold(v string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldEqualFold(FieldContactWxid, v))
+}
+
+// ContactWxidContainsFold applies the ContainsFold predicate on the "contact_wxid" field.
+func ContactWxidContainsFold(v string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldContainsFold(FieldContactWxid, v))
+}
+
+// ContentTypeEQ applies the EQ predicate on the "content_type" field.
+func ContentTypeEQ(v int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldEQ(FieldContentType, v))
+}
+
+// ContentTypeNEQ applies the NEQ predicate on the "content_type" field.
+func ContentTypeNEQ(v int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldNEQ(FieldContentType, v))
+}
+
+// ContentTypeIn applies the In predicate on the "content_type" field.
+func ContentTypeIn(vs ...int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldIn(FieldContentType, vs...))
+}
+
+// ContentTypeNotIn applies the NotIn predicate on the "content_type" field.
+func ContentTypeNotIn(vs ...int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldNotIn(FieldContentType, vs...))
+}
+
+// ContentTypeGT applies the GT predicate on the "content_type" field.
+func ContentTypeGT(v int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldGT(FieldContentType, v))
+}
+
+// ContentTypeGTE applies the GTE predicate on the "content_type" field.
+func ContentTypeGTE(v int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldGTE(FieldContentType, v))
+}
+
+// ContentTypeLT applies the LT predicate on the "content_type" field.
+func ContentTypeLT(v int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldLT(FieldContentType, v))
+}
+
+// ContentTypeLTE applies the LTE predicate on the "content_type" field.
+func ContentTypeLTE(v int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldLTE(FieldContentType, v))
+}
+
+// ContentEQ applies the EQ predicate on the "content" field.
+func ContentEQ(v string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldEQ(FieldContent, v))
+}
+
+// ContentNEQ applies the NEQ predicate on the "content" field.
+func ContentNEQ(v string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldNEQ(FieldContent, v))
+}
+
+// ContentIn applies the In predicate on the "content" field.
+func ContentIn(vs ...string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldIn(FieldContent, vs...))
+}
+
+// ContentNotIn applies the NotIn predicate on the "content" field.
+func ContentNotIn(vs ...string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldNotIn(FieldContent, vs...))
+}
+
+// ContentGT applies the GT predicate on the "content" field.
+func ContentGT(v string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldGT(FieldContent, v))
+}
+
+// ContentGTE applies the GTE predicate on the "content" field.
+func ContentGTE(v string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldGTE(FieldContent, v))
+}
+
+// ContentLT applies the LT predicate on the "content" field.
+func ContentLT(v string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldLT(FieldContent, v))
+}
+
+// ContentLTE applies the LTE predicate on the "content" field.
+func ContentLTE(v string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldLTE(FieldContent, v))
+}
+
+// ContentContains applies the Contains predicate on the "content" field.
+func ContentContains(v string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldContains(FieldContent, v))
+}
+
+// ContentHasPrefix applies the HasPrefix predicate on the "content" field.
+func ContentHasPrefix(v string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldHasPrefix(FieldContent, v))
+}
+
+// ContentHasSuffix applies the HasSuffix predicate on the "content" field.
+func ContentHasSuffix(v string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldHasSuffix(FieldContent, v))
+}
+
+// ContentEqualFold applies the EqualFold predicate on the "content" field.
+func ContentEqualFold(v string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldEqualFold(FieldContent, v))
+}
+
+// ContentContainsFold applies the ContainsFold predicate on the "content" field.
+func ContentContainsFold(v string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldContainsFold(FieldContent, v))
+}
+
+// ErrorDetailEQ applies the EQ predicate on the "error_detail" field.
+func ErrorDetailEQ(v string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldEQ(FieldErrorDetail, v))
+}
+
+// ErrorDetailNEQ applies the NEQ predicate on the "error_detail" field.
+func ErrorDetailNEQ(v string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldNEQ(FieldErrorDetail, v))
+}
+
+// ErrorDetailIn applies the In predicate on the "error_detail" field.
+func ErrorDetailIn(vs ...string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldIn(FieldErrorDetail, vs...))
+}
+
+// ErrorDetailNotIn applies the NotIn predicate on the "error_detail" field.
+func ErrorDetailNotIn(vs ...string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldNotIn(FieldErrorDetail, vs...))
+}
+
+// ErrorDetailGT applies the GT predicate on the "error_detail" field.
+func ErrorDetailGT(v string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldGT(FieldErrorDetail, v))
+}
+
+// ErrorDetailGTE applies the GTE predicate on the "error_detail" field.
+func ErrorDetailGTE(v string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldGTE(FieldErrorDetail, v))
+}
+
+// ErrorDetailLT applies the LT predicate on the "error_detail" field.
+func ErrorDetailLT(v string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldLT(FieldErrorDetail, v))
+}
+
+// ErrorDetailLTE applies the LTE predicate on the "error_detail" field.
+func ErrorDetailLTE(v string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldLTE(FieldErrorDetail, v))
+}
+
+// ErrorDetailContains applies the Contains predicate on the "error_detail" field.
+func ErrorDetailContains(v string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldContains(FieldErrorDetail, v))
+}
+
+// ErrorDetailHasPrefix applies the HasPrefix predicate on the "error_detail" field.
+func ErrorDetailHasPrefix(v string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldHasPrefix(FieldErrorDetail, v))
+}
+
+// ErrorDetailHasSuffix applies the HasSuffix predicate on the "error_detail" field.
+func ErrorDetailHasSuffix(v string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldHasSuffix(FieldErrorDetail, v))
+}
+
+// ErrorDetailEqualFold applies the EqualFold predicate on the "error_detail" field.
+func ErrorDetailEqualFold(v string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldEqualFold(FieldErrorDetail, v))
+}
+
+// ErrorDetailContainsFold applies the ContainsFold predicate on the "error_detail" field.
+func ErrorDetailContainsFold(v string) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldContainsFold(FieldErrorDetail, v))
+}
+
+// SendTimeEQ applies the EQ predicate on the "send_time" field.
+func SendTimeEQ(v time.Time) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldEQ(FieldSendTime, v))
+}
+
+// SendTimeNEQ applies the NEQ predicate on the "send_time" field.
+func SendTimeNEQ(v time.Time) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldNEQ(FieldSendTime, v))
+}
+
+// SendTimeIn applies the In predicate on the "send_time" field.
+func SendTimeIn(vs ...time.Time) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldIn(FieldSendTime, vs...))
+}
+
+// SendTimeNotIn applies the NotIn predicate on the "send_time" field.
+func SendTimeNotIn(vs ...time.Time) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldNotIn(FieldSendTime, vs...))
+}
+
+// SendTimeGT applies the GT predicate on the "send_time" field.
+func SendTimeGT(v time.Time) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldGT(FieldSendTime, v))
+}
+
+// SendTimeGTE applies the GTE predicate on the "send_time" field.
+func SendTimeGTE(v time.Time) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldGTE(FieldSendTime, v))
+}
+
+// SendTimeLT applies the LT predicate on the "send_time" field.
+func SendTimeLT(v time.Time) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldLT(FieldSendTime, v))
+}
+
+// SendTimeLTE applies the LTE predicate on the "send_time" field.
+func SendTimeLTE(v time.Time) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldLTE(FieldSendTime, v))
+}
+
+// SendTimeIsNil applies the IsNil predicate on the "send_time" field.
+func SendTimeIsNil() predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldIsNull(FieldSendTime))
+}
+
+// SendTimeNotNil applies the NotNil predicate on the "send_time" field.
+func SendTimeNotNil() predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldNotNull(FieldSendTime))
+}
+
+// SourceTypeEQ applies the EQ predicate on the "source_type" field.
+func SourceTypeEQ(v int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldEQ(FieldSourceType, v))
+}
+
+// SourceTypeNEQ applies the NEQ predicate on the "source_type" field.
+func SourceTypeNEQ(v int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldNEQ(FieldSourceType, v))
+}
+
+// SourceTypeIn applies the In predicate on the "source_type" field.
+func SourceTypeIn(vs ...int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldIn(FieldSourceType, vs...))
+}
+
+// SourceTypeNotIn applies the NotIn predicate on the "source_type" field.
+func SourceTypeNotIn(vs ...int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldNotIn(FieldSourceType, vs...))
+}
+
+// SourceTypeGT applies the GT predicate on the "source_type" field.
+func SourceTypeGT(v int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldGT(FieldSourceType, v))
+}
+
+// SourceTypeGTE applies the GTE predicate on the "source_type" field.
+func SourceTypeGTE(v int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldGTE(FieldSourceType, v))
+}
+
+// SourceTypeLT applies the LT predicate on the "source_type" field.
+func SourceTypeLT(v int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldLT(FieldSourceType, v))
+}
+
+// SourceTypeLTE applies the LTE predicate on the "source_type" field.
+func SourceTypeLTE(v int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldLTE(FieldSourceType, v))
+}
+
+// SourceIDEQ applies the EQ predicate on the "source_id" field.
+func SourceIDEQ(v int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldEQ(FieldSourceID, v))
+}
+
+// SourceIDNEQ applies the NEQ predicate on the "source_id" field.
+func SourceIDNEQ(v int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldNEQ(FieldSourceID, v))
+}
+
+// SourceIDIn applies the In predicate on the "source_id" field.
+func SourceIDIn(vs ...int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldIn(FieldSourceID, vs...))
+}
+
+// SourceIDNotIn applies the NotIn predicate on the "source_id" field.
+func SourceIDNotIn(vs ...int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldNotIn(FieldSourceID, vs...))
+}
+
+// SourceIDGT applies the GT predicate on the "source_id" field.
+func SourceIDGT(v int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldGT(FieldSourceID, v))
+}
+
+// SourceIDGTE applies the GTE predicate on the "source_id" field.
+func SourceIDGTE(v int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldGTE(FieldSourceID, v))
+}
+
+// SourceIDLT applies the LT predicate on the "source_id" field.
+func SourceIDLT(v int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldLT(FieldSourceID, v))
+}
+
+// SourceIDLTE applies the LTE predicate on the "source_id" field.
+func SourceIDLTE(v int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldLTE(FieldSourceID, v))
+}
+
+// SubSourceIDEQ applies the EQ predicate on the "sub_source_id" field.
+func SubSourceIDEQ(v int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldEQ(FieldSubSourceID, v))
+}
+
+// SubSourceIDNEQ applies the NEQ predicate on the "sub_source_id" field.
+func SubSourceIDNEQ(v int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldNEQ(FieldSubSourceID, v))
+}
+
+// SubSourceIDIn applies the In predicate on the "sub_source_id" field.
+func SubSourceIDIn(vs ...int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldIn(FieldSubSourceID, vs...))
+}
+
+// SubSourceIDNotIn applies the NotIn predicate on the "sub_source_id" field.
+func SubSourceIDNotIn(vs ...int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldNotIn(FieldSubSourceID, vs...))
+}
+
+// SubSourceIDGT applies the GT predicate on the "sub_source_id" field.
+func SubSourceIDGT(v int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldGT(FieldSubSourceID, v))
+}
+
+// SubSourceIDGTE applies the GTE predicate on the "sub_source_id" field.
+func SubSourceIDGTE(v int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldGTE(FieldSubSourceID, v))
+}
+
+// SubSourceIDLT applies the LT predicate on the "sub_source_id" field.
+func SubSourceIDLT(v int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldLT(FieldSubSourceID, v))
+}
+
+// SubSourceIDLTE applies the LTE predicate on the "sub_source_id" field.
+func SubSourceIDLTE(v int) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.FieldLTE(FieldSubSourceID, v))
+}
+
+// And groups predicates with the AND operator between them.
+func And(predicates ...predicate.MessageRecords) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.AndPredicates(predicates...))
+}
+
+// Or groups predicates with the OR operator between them.
+func Or(predicates ...predicate.MessageRecords) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.OrPredicates(predicates...))
+}
+
+// Not applies the not operator on the given predicate.
+func Not(p predicate.MessageRecords) predicate.MessageRecords {
+	return predicate.MessageRecords(sql.NotPredicates(p))
+}

+ 1547 - 0
ent/messagerecords_create.go

@@ -0,0 +1,1547 @@
+// Code generated by ent, DO NOT EDIT.
+
+package ent
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"time"
+	"wechat-api/ent/messagerecords"
+
+	"entgo.io/ent/dialect/sql"
+	"entgo.io/ent/dialect/sql/sqlgraph"
+	"entgo.io/ent/schema/field"
+)
+
+// MessageRecordsCreate is the builder for creating a MessageRecords entity.
+type MessageRecordsCreate struct {
+	config
+	mutation *MessageRecordsMutation
+	hooks    []Hook
+	conflict []sql.ConflictOption
+}
+
+// SetCreatedAt sets the "created_at" field.
+func (mrc *MessageRecordsCreate) SetCreatedAt(t time.Time) *MessageRecordsCreate {
+	mrc.mutation.SetCreatedAt(t)
+	return mrc
+}
+
+// SetNillableCreatedAt sets the "created_at" field if the given value is not nil.
+func (mrc *MessageRecordsCreate) SetNillableCreatedAt(t *time.Time) *MessageRecordsCreate {
+	if t != nil {
+		mrc.SetCreatedAt(*t)
+	}
+	return mrc
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (mrc *MessageRecordsCreate) SetUpdatedAt(t time.Time) *MessageRecordsCreate {
+	mrc.mutation.SetUpdatedAt(t)
+	return mrc
+}
+
+// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil.
+func (mrc *MessageRecordsCreate) SetNillableUpdatedAt(t *time.Time) *MessageRecordsCreate {
+	if t != nil {
+		mrc.SetUpdatedAt(*t)
+	}
+	return mrc
+}
+
+// SetStatus sets the "status" field.
+func (mrc *MessageRecordsCreate) SetStatus(u uint8) *MessageRecordsCreate {
+	mrc.mutation.SetStatus(u)
+	return mrc
+}
+
+// SetNillableStatus sets the "status" field if the given value is not nil.
+func (mrc *MessageRecordsCreate) SetNillableStatus(u *uint8) *MessageRecordsCreate {
+	if u != nil {
+		mrc.SetStatus(*u)
+	}
+	return mrc
+}
+
+// SetDeletedAt sets the "deleted_at" field.
+func (mrc *MessageRecordsCreate) SetDeletedAt(t time.Time) *MessageRecordsCreate {
+	mrc.mutation.SetDeletedAt(t)
+	return mrc
+}
+
+// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil.
+func (mrc *MessageRecordsCreate) SetNillableDeletedAt(t *time.Time) *MessageRecordsCreate {
+	if t != nil {
+		mrc.SetDeletedAt(*t)
+	}
+	return mrc
+}
+
+// SetBotWxid sets the "bot_wxid" field.
+func (mrc *MessageRecordsCreate) SetBotWxid(s string) *MessageRecordsCreate {
+	mrc.mutation.SetBotWxid(s)
+	return mrc
+}
+
+// SetContactID sets the "contact_id" field.
+func (mrc *MessageRecordsCreate) SetContactID(i int) *MessageRecordsCreate {
+	mrc.mutation.SetContactID(i)
+	return mrc
+}
+
+// SetContactType sets the "contact_type" field.
+func (mrc *MessageRecordsCreate) SetContactType(i int) *MessageRecordsCreate {
+	mrc.mutation.SetContactType(i)
+	return mrc
+}
+
+// SetNillableContactType sets the "contact_type" field if the given value is not nil.
+func (mrc *MessageRecordsCreate) SetNillableContactType(i *int) *MessageRecordsCreate {
+	if i != nil {
+		mrc.SetContactType(*i)
+	}
+	return mrc
+}
+
+// SetContactWxid sets the "contact_wxid" field.
+func (mrc *MessageRecordsCreate) SetContactWxid(s string) *MessageRecordsCreate {
+	mrc.mutation.SetContactWxid(s)
+	return mrc
+}
+
+// SetNillableContactWxid sets the "contact_wxid" field if the given value is not nil.
+func (mrc *MessageRecordsCreate) SetNillableContactWxid(s *string) *MessageRecordsCreate {
+	if s != nil {
+		mrc.SetContactWxid(*s)
+	}
+	return mrc
+}
+
+// SetContentType sets the "content_type" field.
+func (mrc *MessageRecordsCreate) SetContentType(i int) *MessageRecordsCreate {
+	mrc.mutation.SetContentType(i)
+	return mrc
+}
+
+// SetNillableContentType sets the "content_type" field if the given value is not nil.
+func (mrc *MessageRecordsCreate) SetNillableContentType(i *int) *MessageRecordsCreate {
+	if i != nil {
+		mrc.SetContentType(*i)
+	}
+	return mrc
+}
+
+// SetContent sets the "content" field.
+func (mrc *MessageRecordsCreate) SetContent(s string) *MessageRecordsCreate {
+	mrc.mutation.SetContent(s)
+	return mrc
+}
+
+// SetNillableContent sets the "content" field if the given value is not nil.
+func (mrc *MessageRecordsCreate) SetNillableContent(s *string) *MessageRecordsCreate {
+	if s != nil {
+		mrc.SetContent(*s)
+	}
+	return mrc
+}
+
+// SetErrorDetail sets the "error_detail" field.
+func (mrc *MessageRecordsCreate) SetErrorDetail(s string) *MessageRecordsCreate {
+	mrc.mutation.SetErrorDetail(s)
+	return mrc
+}
+
+// SetNillableErrorDetail sets the "error_detail" field if the given value is not nil.
+func (mrc *MessageRecordsCreate) SetNillableErrorDetail(s *string) *MessageRecordsCreate {
+	if s != nil {
+		mrc.SetErrorDetail(*s)
+	}
+	return mrc
+}
+
+// SetSendTime sets the "send_time" field.
+func (mrc *MessageRecordsCreate) SetSendTime(t time.Time) *MessageRecordsCreate {
+	mrc.mutation.SetSendTime(t)
+	return mrc
+}
+
+// SetNillableSendTime sets the "send_time" field if the given value is not nil.
+func (mrc *MessageRecordsCreate) SetNillableSendTime(t *time.Time) *MessageRecordsCreate {
+	if t != nil {
+		mrc.SetSendTime(*t)
+	}
+	return mrc
+}
+
+// SetSourceType sets the "source_type" field.
+func (mrc *MessageRecordsCreate) SetSourceType(i int) *MessageRecordsCreate {
+	mrc.mutation.SetSourceType(i)
+	return mrc
+}
+
+// SetNillableSourceType sets the "source_type" field if the given value is not nil.
+func (mrc *MessageRecordsCreate) SetNillableSourceType(i *int) *MessageRecordsCreate {
+	if i != nil {
+		mrc.SetSourceType(*i)
+	}
+	return mrc
+}
+
+// SetSourceID sets the "source_id" field.
+func (mrc *MessageRecordsCreate) SetSourceID(i int) *MessageRecordsCreate {
+	mrc.mutation.SetSourceID(i)
+	return mrc
+}
+
+// SetNillableSourceID sets the "source_id" field if the given value is not nil.
+func (mrc *MessageRecordsCreate) SetNillableSourceID(i *int) *MessageRecordsCreate {
+	if i != nil {
+		mrc.SetSourceID(*i)
+	}
+	return mrc
+}
+
+// SetSubSourceID sets the "sub_source_id" field.
+func (mrc *MessageRecordsCreate) SetSubSourceID(i int) *MessageRecordsCreate {
+	mrc.mutation.SetSubSourceID(i)
+	return mrc
+}
+
+// SetNillableSubSourceID sets the "sub_source_id" field if the given value is not nil.
+func (mrc *MessageRecordsCreate) SetNillableSubSourceID(i *int) *MessageRecordsCreate {
+	if i != nil {
+		mrc.SetSubSourceID(*i)
+	}
+	return mrc
+}
+
+// SetID sets the "id" field.
+func (mrc *MessageRecordsCreate) SetID(u uint64) *MessageRecordsCreate {
+	mrc.mutation.SetID(u)
+	return mrc
+}
+
+// Mutation returns the MessageRecordsMutation object of the builder.
+func (mrc *MessageRecordsCreate) Mutation() *MessageRecordsMutation {
+	return mrc.mutation
+}
+
+// Save creates the MessageRecords in the database.
+func (mrc *MessageRecordsCreate) Save(ctx context.Context) (*MessageRecords, error) {
+	if err := mrc.defaults(); err != nil {
+		return nil, err
+	}
+	return withHooks(ctx, mrc.sqlSave, mrc.mutation, mrc.hooks)
+}
+
+// SaveX calls Save and panics if Save returns an error.
+func (mrc *MessageRecordsCreate) SaveX(ctx context.Context) *MessageRecords {
+	v, err := mrc.Save(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return v
+}
+
+// Exec executes the query.
+func (mrc *MessageRecordsCreate) Exec(ctx context.Context) error {
+	_, err := mrc.Save(ctx)
+	return err
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (mrc *MessageRecordsCreate) ExecX(ctx context.Context) {
+	if err := mrc.Exec(ctx); err != nil {
+		panic(err)
+	}
+}
+
+// defaults sets the default values of the builder before save.
+func (mrc *MessageRecordsCreate) defaults() error {
+	if _, ok := mrc.mutation.CreatedAt(); !ok {
+		if messagerecords.DefaultCreatedAt == nil {
+			return fmt.Errorf("ent: uninitialized messagerecords.DefaultCreatedAt (forgotten import ent/runtime?)")
+		}
+		v := messagerecords.DefaultCreatedAt()
+		mrc.mutation.SetCreatedAt(v)
+	}
+	if _, ok := mrc.mutation.UpdatedAt(); !ok {
+		if messagerecords.DefaultUpdatedAt == nil {
+			return fmt.Errorf("ent: uninitialized messagerecords.DefaultUpdatedAt (forgotten import ent/runtime?)")
+		}
+		v := messagerecords.DefaultUpdatedAt()
+		mrc.mutation.SetUpdatedAt(v)
+	}
+	if _, ok := mrc.mutation.Status(); !ok {
+		v := messagerecords.DefaultStatus
+		mrc.mutation.SetStatus(v)
+	}
+	if _, ok := mrc.mutation.ContactType(); !ok {
+		v := messagerecords.DefaultContactType
+		mrc.mutation.SetContactType(v)
+	}
+	if _, ok := mrc.mutation.ContactWxid(); !ok {
+		v := messagerecords.DefaultContactWxid
+		mrc.mutation.SetContactWxid(v)
+	}
+	if _, ok := mrc.mutation.ContentType(); !ok {
+		v := messagerecords.DefaultContentType
+		mrc.mutation.SetContentType(v)
+	}
+	if _, ok := mrc.mutation.Content(); !ok {
+		v := messagerecords.DefaultContent
+		mrc.mutation.SetContent(v)
+	}
+	if _, ok := mrc.mutation.ErrorDetail(); !ok {
+		v := messagerecords.DefaultErrorDetail
+		mrc.mutation.SetErrorDetail(v)
+	}
+	if _, ok := mrc.mutation.SourceType(); !ok {
+		v := messagerecords.DefaultSourceType
+		mrc.mutation.SetSourceType(v)
+	}
+	if _, ok := mrc.mutation.SourceID(); !ok {
+		v := messagerecords.DefaultSourceID
+		mrc.mutation.SetSourceID(v)
+	}
+	if _, ok := mrc.mutation.SubSourceID(); !ok {
+		v := messagerecords.DefaultSubSourceID
+		mrc.mutation.SetSubSourceID(v)
+	}
+	return nil
+}
+
+// check runs all checks and user-defined validators on the builder.
+func (mrc *MessageRecordsCreate) check() error {
+	if _, ok := mrc.mutation.CreatedAt(); !ok {
+		return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "MessageRecords.created_at"`)}
+	}
+	if _, ok := mrc.mutation.UpdatedAt(); !ok {
+		return &ValidationError{Name: "updated_at", err: errors.New(`ent: missing required field "MessageRecords.updated_at"`)}
+	}
+	if _, ok := mrc.mutation.BotWxid(); !ok {
+		return &ValidationError{Name: "bot_wxid", err: errors.New(`ent: missing required field "MessageRecords.bot_wxid"`)}
+	}
+	if _, ok := mrc.mutation.ContactID(); !ok {
+		return &ValidationError{Name: "contact_id", err: errors.New(`ent: missing required field "MessageRecords.contact_id"`)}
+	}
+	if _, ok := mrc.mutation.ContactType(); !ok {
+		return &ValidationError{Name: "contact_type", err: errors.New(`ent: missing required field "MessageRecords.contact_type"`)}
+	}
+	if _, ok := mrc.mutation.ContactWxid(); !ok {
+		return &ValidationError{Name: "contact_wxid", err: errors.New(`ent: missing required field "MessageRecords.contact_wxid"`)}
+	}
+	if _, ok := mrc.mutation.ContentType(); !ok {
+		return &ValidationError{Name: "content_type", err: errors.New(`ent: missing required field "MessageRecords.content_type"`)}
+	}
+	if _, ok := mrc.mutation.Content(); !ok {
+		return &ValidationError{Name: "content", err: errors.New(`ent: missing required field "MessageRecords.content"`)}
+	}
+	if _, ok := mrc.mutation.ErrorDetail(); !ok {
+		return &ValidationError{Name: "error_detail", err: errors.New(`ent: missing required field "MessageRecords.error_detail"`)}
+	}
+	if _, ok := mrc.mutation.SourceType(); !ok {
+		return &ValidationError{Name: "source_type", err: errors.New(`ent: missing required field "MessageRecords.source_type"`)}
+	}
+	if _, ok := mrc.mutation.SourceID(); !ok {
+		return &ValidationError{Name: "source_id", err: errors.New(`ent: missing required field "MessageRecords.source_id"`)}
+	}
+	if _, ok := mrc.mutation.SubSourceID(); !ok {
+		return &ValidationError{Name: "sub_source_id", err: errors.New(`ent: missing required field "MessageRecords.sub_source_id"`)}
+	}
+	return nil
+}
+
+func (mrc *MessageRecordsCreate) sqlSave(ctx context.Context) (*MessageRecords, error) {
+	if err := mrc.check(); err != nil {
+		return nil, err
+	}
+	_node, _spec := mrc.createSpec()
+	if err := sqlgraph.CreateNode(ctx, mrc.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)
+	}
+	mrc.mutation.id = &_node.ID
+	mrc.mutation.done = true
+	return _node, nil
+}
+
+func (mrc *MessageRecordsCreate) createSpec() (*MessageRecords, *sqlgraph.CreateSpec) {
+	var (
+		_node = &MessageRecords{config: mrc.config}
+		_spec = sqlgraph.NewCreateSpec(messagerecords.Table, sqlgraph.NewFieldSpec(messagerecords.FieldID, field.TypeUint64))
+	)
+	_spec.OnConflict = mrc.conflict
+	if id, ok := mrc.mutation.ID(); ok {
+		_node.ID = id
+		_spec.ID.Value = id
+	}
+	if value, ok := mrc.mutation.CreatedAt(); ok {
+		_spec.SetField(messagerecords.FieldCreatedAt, field.TypeTime, value)
+		_node.CreatedAt = value
+	}
+	if value, ok := mrc.mutation.UpdatedAt(); ok {
+		_spec.SetField(messagerecords.FieldUpdatedAt, field.TypeTime, value)
+		_node.UpdatedAt = value
+	}
+	if value, ok := mrc.mutation.Status(); ok {
+		_spec.SetField(messagerecords.FieldStatus, field.TypeUint8, value)
+		_node.Status = value
+	}
+	if value, ok := mrc.mutation.DeletedAt(); ok {
+		_spec.SetField(messagerecords.FieldDeletedAt, field.TypeTime, value)
+		_node.DeletedAt = value
+	}
+	if value, ok := mrc.mutation.BotWxid(); ok {
+		_spec.SetField(messagerecords.FieldBotWxid, field.TypeString, value)
+		_node.BotWxid = value
+	}
+	if value, ok := mrc.mutation.ContactID(); ok {
+		_spec.SetField(messagerecords.FieldContactID, field.TypeInt, value)
+		_node.ContactID = value
+	}
+	if value, ok := mrc.mutation.ContactType(); ok {
+		_spec.SetField(messagerecords.FieldContactType, field.TypeInt, value)
+		_node.ContactType = value
+	}
+	if value, ok := mrc.mutation.ContactWxid(); ok {
+		_spec.SetField(messagerecords.FieldContactWxid, field.TypeString, value)
+		_node.ContactWxid = value
+	}
+	if value, ok := mrc.mutation.ContentType(); ok {
+		_spec.SetField(messagerecords.FieldContentType, field.TypeInt, value)
+		_node.ContentType = value
+	}
+	if value, ok := mrc.mutation.Content(); ok {
+		_spec.SetField(messagerecords.FieldContent, field.TypeString, value)
+		_node.Content = value
+	}
+	if value, ok := mrc.mutation.ErrorDetail(); ok {
+		_spec.SetField(messagerecords.FieldErrorDetail, field.TypeString, value)
+		_node.ErrorDetail = value
+	}
+	if value, ok := mrc.mutation.SendTime(); ok {
+		_spec.SetField(messagerecords.FieldSendTime, field.TypeTime, value)
+		_node.SendTime = value
+	}
+	if value, ok := mrc.mutation.SourceType(); ok {
+		_spec.SetField(messagerecords.FieldSourceType, field.TypeInt, value)
+		_node.SourceType = value
+	}
+	if value, ok := mrc.mutation.SourceID(); ok {
+		_spec.SetField(messagerecords.FieldSourceID, field.TypeInt, value)
+		_node.SourceID = value
+	}
+	if value, ok := mrc.mutation.SubSourceID(); ok {
+		_spec.SetField(messagerecords.FieldSubSourceID, field.TypeInt, value)
+		_node.SubSourceID = value
+	}
+	return _node, _spec
+}
+
+// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause
+// of the `INSERT` statement. For example:
+//
+//	client.MessageRecords.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.MessageRecordsUpsert) {
+//			SetCreatedAt(v+v).
+//		}).
+//		Exec(ctx)
+func (mrc *MessageRecordsCreate) OnConflict(opts ...sql.ConflictOption) *MessageRecordsUpsertOne {
+	mrc.conflict = opts
+	return &MessageRecordsUpsertOne{
+		create: mrc,
+	}
+}
+
+// OnConflictColumns calls `OnConflict` and configures the columns
+// as conflict target. Using this option is equivalent to using:
+//
+//	client.MessageRecords.Create().
+//		OnConflict(sql.ConflictColumns(columns...)).
+//		Exec(ctx)
+func (mrc *MessageRecordsCreate) OnConflictColumns(columns ...string) *MessageRecordsUpsertOne {
+	mrc.conflict = append(mrc.conflict, sql.ConflictColumns(columns...))
+	return &MessageRecordsUpsertOne{
+		create: mrc,
+	}
+}
+
+type (
+	// MessageRecordsUpsertOne is the builder for "upsert"-ing
+	//  one MessageRecords node.
+	MessageRecordsUpsertOne struct {
+		create *MessageRecordsCreate
+	}
+
+	// MessageRecordsUpsert is the "OnConflict" setter.
+	MessageRecordsUpsert struct {
+		*sql.UpdateSet
+	}
+)
+
+// SetUpdatedAt sets the "updated_at" field.
+func (u *MessageRecordsUpsert) SetUpdatedAt(v time.Time) *MessageRecordsUpsert {
+	u.Set(messagerecords.FieldUpdatedAt, v)
+	return u
+}
+
+// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create.
+func (u *MessageRecordsUpsert) UpdateUpdatedAt() *MessageRecordsUpsert {
+	u.SetExcluded(messagerecords.FieldUpdatedAt)
+	return u
+}
+
+// SetStatus sets the "status" field.
+func (u *MessageRecordsUpsert) SetStatus(v uint8) *MessageRecordsUpsert {
+	u.Set(messagerecords.FieldStatus, v)
+	return u
+}
+
+// UpdateStatus sets the "status" field to the value that was provided on create.
+func (u *MessageRecordsUpsert) UpdateStatus() *MessageRecordsUpsert {
+	u.SetExcluded(messagerecords.FieldStatus)
+	return u
+}
+
+// AddStatus adds v to the "status" field.
+func (u *MessageRecordsUpsert) AddStatus(v uint8) *MessageRecordsUpsert {
+	u.Add(messagerecords.FieldStatus, v)
+	return u
+}
+
+// ClearStatus clears the value of the "status" field.
+func (u *MessageRecordsUpsert) ClearStatus() *MessageRecordsUpsert {
+	u.SetNull(messagerecords.FieldStatus)
+	return u
+}
+
+// SetDeletedAt sets the "deleted_at" field.
+func (u *MessageRecordsUpsert) SetDeletedAt(v time.Time) *MessageRecordsUpsert {
+	u.Set(messagerecords.FieldDeletedAt, v)
+	return u
+}
+
+// UpdateDeletedAt sets the "deleted_at" field to the value that was provided on create.
+func (u *MessageRecordsUpsert) UpdateDeletedAt() *MessageRecordsUpsert {
+	u.SetExcluded(messagerecords.FieldDeletedAt)
+	return u
+}
+
+// ClearDeletedAt clears the value of the "deleted_at" field.
+func (u *MessageRecordsUpsert) ClearDeletedAt() *MessageRecordsUpsert {
+	u.SetNull(messagerecords.FieldDeletedAt)
+	return u
+}
+
+// SetBotWxid sets the "bot_wxid" field.
+func (u *MessageRecordsUpsert) SetBotWxid(v string) *MessageRecordsUpsert {
+	u.Set(messagerecords.FieldBotWxid, v)
+	return u
+}
+
+// UpdateBotWxid sets the "bot_wxid" field to the value that was provided on create.
+func (u *MessageRecordsUpsert) UpdateBotWxid() *MessageRecordsUpsert {
+	u.SetExcluded(messagerecords.FieldBotWxid)
+	return u
+}
+
+// SetContactID sets the "contact_id" field.
+func (u *MessageRecordsUpsert) SetContactID(v int) *MessageRecordsUpsert {
+	u.Set(messagerecords.FieldContactID, v)
+	return u
+}
+
+// UpdateContactID sets the "contact_id" field to the value that was provided on create.
+func (u *MessageRecordsUpsert) UpdateContactID() *MessageRecordsUpsert {
+	u.SetExcluded(messagerecords.FieldContactID)
+	return u
+}
+
+// AddContactID adds v to the "contact_id" field.
+func (u *MessageRecordsUpsert) AddContactID(v int) *MessageRecordsUpsert {
+	u.Add(messagerecords.FieldContactID, v)
+	return u
+}
+
+// SetContactType sets the "contact_type" field.
+func (u *MessageRecordsUpsert) SetContactType(v int) *MessageRecordsUpsert {
+	u.Set(messagerecords.FieldContactType, v)
+	return u
+}
+
+// UpdateContactType sets the "contact_type" field to the value that was provided on create.
+func (u *MessageRecordsUpsert) UpdateContactType() *MessageRecordsUpsert {
+	u.SetExcluded(messagerecords.FieldContactType)
+	return u
+}
+
+// AddContactType adds v to the "contact_type" field.
+func (u *MessageRecordsUpsert) AddContactType(v int) *MessageRecordsUpsert {
+	u.Add(messagerecords.FieldContactType, v)
+	return u
+}
+
+// SetContactWxid sets the "contact_wxid" field.
+func (u *MessageRecordsUpsert) SetContactWxid(v string) *MessageRecordsUpsert {
+	u.Set(messagerecords.FieldContactWxid, v)
+	return u
+}
+
+// UpdateContactWxid sets the "contact_wxid" field to the value that was provided on create.
+func (u *MessageRecordsUpsert) UpdateContactWxid() *MessageRecordsUpsert {
+	u.SetExcluded(messagerecords.FieldContactWxid)
+	return u
+}
+
+// SetContentType sets the "content_type" field.
+func (u *MessageRecordsUpsert) SetContentType(v int) *MessageRecordsUpsert {
+	u.Set(messagerecords.FieldContentType, v)
+	return u
+}
+
+// UpdateContentType sets the "content_type" field to the value that was provided on create.
+func (u *MessageRecordsUpsert) UpdateContentType() *MessageRecordsUpsert {
+	u.SetExcluded(messagerecords.FieldContentType)
+	return u
+}
+
+// AddContentType adds v to the "content_type" field.
+func (u *MessageRecordsUpsert) AddContentType(v int) *MessageRecordsUpsert {
+	u.Add(messagerecords.FieldContentType, v)
+	return u
+}
+
+// SetContent sets the "content" field.
+func (u *MessageRecordsUpsert) SetContent(v string) *MessageRecordsUpsert {
+	u.Set(messagerecords.FieldContent, v)
+	return u
+}
+
+// UpdateContent sets the "content" field to the value that was provided on create.
+func (u *MessageRecordsUpsert) UpdateContent() *MessageRecordsUpsert {
+	u.SetExcluded(messagerecords.FieldContent)
+	return u
+}
+
+// SetErrorDetail sets the "error_detail" field.
+func (u *MessageRecordsUpsert) SetErrorDetail(v string) *MessageRecordsUpsert {
+	u.Set(messagerecords.FieldErrorDetail, v)
+	return u
+}
+
+// UpdateErrorDetail sets the "error_detail" field to the value that was provided on create.
+func (u *MessageRecordsUpsert) UpdateErrorDetail() *MessageRecordsUpsert {
+	u.SetExcluded(messagerecords.FieldErrorDetail)
+	return u
+}
+
+// SetSendTime sets the "send_time" field.
+func (u *MessageRecordsUpsert) SetSendTime(v time.Time) *MessageRecordsUpsert {
+	u.Set(messagerecords.FieldSendTime, v)
+	return u
+}
+
+// UpdateSendTime sets the "send_time" field to the value that was provided on create.
+func (u *MessageRecordsUpsert) UpdateSendTime() *MessageRecordsUpsert {
+	u.SetExcluded(messagerecords.FieldSendTime)
+	return u
+}
+
+// ClearSendTime clears the value of the "send_time" field.
+func (u *MessageRecordsUpsert) ClearSendTime() *MessageRecordsUpsert {
+	u.SetNull(messagerecords.FieldSendTime)
+	return u
+}
+
+// SetSourceType sets the "source_type" field.
+func (u *MessageRecordsUpsert) SetSourceType(v int) *MessageRecordsUpsert {
+	u.Set(messagerecords.FieldSourceType, v)
+	return u
+}
+
+// UpdateSourceType sets the "source_type" field to the value that was provided on create.
+func (u *MessageRecordsUpsert) UpdateSourceType() *MessageRecordsUpsert {
+	u.SetExcluded(messagerecords.FieldSourceType)
+	return u
+}
+
+// AddSourceType adds v to the "source_type" field.
+func (u *MessageRecordsUpsert) AddSourceType(v int) *MessageRecordsUpsert {
+	u.Add(messagerecords.FieldSourceType, v)
+	return u
+}
+
+// SetSourceID sets the "source_id" field.
+func (u *MessageRecordsUpsert) SetSourceID(v int) *MessageRecordsUpsert {
+	u.Set(messagerecords.FieldSourceID, v)
+	return u
+}
+
+// UpdateSourceID sets the "source_id" field to the value that was provided on create.
+func (u *MessageRecordsUpsert) UpdateSourceID() *MessageRecordsUpsert {
+	u.SetExcluded(messagerecords.FieldSourceID)
+	return u
+}
+
+// AddSourceID adds v to the "source_id" field.
+func (u *MessageRecordsUpsert) AddSourceID(v int) *MessageRecordsUpsert {
+	u.Add(messagerecords.FieldSourceID, v)
+	return u
+}
+
+// SetSubSourceID sets the "sub_source_id" field.
+func (u *MessageRecordsUpsert) SetSubSourceID(v int) *MessageRecordsUpsert {
+	u.Set(messagerecords.FieldSubSourceID, v)
+	return u
+}
+
+// UpdateSubSourceID sets the "sub_source_id" field to the value that was provided on create.
+func (u *MessageRecordsUpsert) UpdateSubSourceID() *MessageRecordsUpsert {
+	u.SetExcluded(messagerecords.FieldSubSourceID)
+	return u
+}
+
+// AddSubSourceID adds v to the "sub_source_id" field.
+func (u *MessageRecordsUpsert) AddSubSourceID(v int) *MessageRecordsUpsert {
+	u.Add(messagerecords.FieldSubSourceID, v)
+	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.MessageRecords.Create().
+//		OnConflict(
+//			sql.ResolveWithNewValues(),
+//			sql.ResolveWith(func(u *sql.UpdateSet) {
+//				u.SetIgnore(messagerecords.FieldID)
+//			}),
+//		).
+//		Exec(ctx)
+func (u *MessageRecordsUpsertOne) UpdateNewValues() *MessageRecordsUpsertOne {
+	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(messagerecords.FieldID)
+		}
+		if _, exists := u.create.mutation.CreatedAt(); exists {
+			s.SetIgnore(messagerecords.FieldCreatedAt)
+		}
+	}))
+	return u
+}
+
+// Ignore sets each column to itself in case of conflict.
+// Using this option is equivalent to using:
+//
+//	client.MessageRecords.Create().
+//	    OnConflict(sql.ResolveWithIgnore()).
+//	    Exec(ctx)
+func (u *MessageRecordsUpsertOne) Ignore() *MessageRecordsUpsertOne {
+	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 *MessageRecordsUpsertOne) DoNothing() *MessageRecordsUpsertOne {
+	u.create.conflict = append(u.create.conflict, sql.DoNothing())
+	return u
+}
+
+// Update allows overriding fields `UPDATE` values. See the MessageRecordsCreate.OnConflict
+// documentation for more info.
+func (u *MessageRecordsUpsertOne) Update(set func(*MessageRecordsUpsert)) *MessageRecordsUpsertOne {
+	u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) {
+		set(&MessageRecordsUpsert{UpdateSet: update})
+	}))
+	return u
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (u *MessageRecordsUpsertOne) SetUpdatedAt(v time.Time) *MessageRecordsUpsertOne {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.SetUpdatedAt(v)
+	})
+}
+
+// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create.
+func (u *MessageRecordsUpsertOne) UpdateUpdatedAt() *MessageRecordsUpsertOne {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.UpdateUpdatedAt()
+	})
+}
+
+// SetStatus sets the "status" field.
+func (u *MessageRecordsUpsertOne) SetStatus(v uint8) *MessageRecordsUpsertOne {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.SetStatus(v)
+	})
+}
+
+// AddStatus adds v to the "status" field.
+func (u *MessageRecordsUpsertOne) AddStatus(v uint8) *MessageRecordsUpsertOne {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.AddStatus(v)
+	})
+}
+
+// UpdateStatus sets the "status" field to the value that was provided on create.
+func (u *MessageRecordsUpsertOne) UpdateStatus() *MessageRecordsUpsertOne {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.UpdateStatus()
+	})
+}
+
+// ClearStatus clears the value of the "status" field.
+func (u *MessageRecordsUpsertOne) ClearStatus() *MessageRecordsUpsertOne {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.ClearStatus()
+	})
+}
+
+// SetDeletedAt sets the "deleted_at" field.
+func (u *MessageRecordsUpsertOne) SetDeletedAt(v time.Time) *MessageRecordsUpsertOne {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.SetDeletedAt(v)
+	})
+}
+
+// UpdateDeletedAt sets the "deleted_at" field to the value that was provided on create.
+func (u *MessageRecordsUpsertOne) UpdateDeletedAt() *MessageRecordsUpsertOne {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.UpdateDeletedAt()
+	})
+}
+
+// ClearDeletedAt clears the value of the "deleted_at" field.
+func (u *MessageRecordsUpsertOne) ClearDeletedAt() *MessageRecordsUpsertOne {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.ClearDeletedAt()
+	})
+}
+
+// SetBotWxid sets the "bot_wxid" field.
+func (u *MessageRecordsUpsertOne) SetBotWxid(v string) *MessageRecordsUpsertOne {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.SetBotWxid(v)
+	})
+}
+
+// UpdateBotWxid sets the "bot_wxid" field to the value that was provided on create.
+func (u *MessageRecordsUpsertOne) UpdateBotWxid() *MessageRecordsUpsertOne {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.UpdateBotWxid()
+	})
+}
+
+// SetContactID sets the "contact_id" field.
+func (u *MessageRecordsUpsertOne) SetContactID(v int) *MessageRecordsUpsertOne {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.SetContactID(v)
+	})
+}
+
+// AddContactID adds v to the "contact_id" field.
+func (u *MessageRecordsUpsertOne) AddContactID(v int) *MessageRecordsUpsertOne {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.AddContactID(v)
+	})
+}
+
+// UpdateContactID sets the "contact_id" field to the value that was provided on create.
+func (u *MessageRecordsUpsertOne) UpdateContactID() *MessageRecordsUpsertOne {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.UpdateContactID()
+	})
+}
+
+// SetContactType sets the "contact_type" field.
+func (u *MessageRecordsUpsertOne) SetContactType(v int) *MessageRecordsUpsertOne {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.SetContactType(v)
+	})
+}
+
+// AddContactType adds v to the "contact_type" field.
+func (u *MessageRecordsUpsertOne) AddContactType(v int) *MessageRecordsUpsertOne {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.AddContactType(v)
+	})
+}
+
+// UpdateContactType sets the "contact_type" field to the value that was provided on create.
+func (u *MessageRecordsUpsertOne) UpdateContactType() *MessageRecordsUpsertOne {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.UpdateContactType()
+	})
+}
+
+// SetContactWxid sets the "contact_wxid" field.
+func (u *MessageRecordsUpsertOne) SetContactWxid(v string) *MessageRecordsUpsertOne {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.SetContactWxid(v)
+	})
+}
+
+// UpdateContactWxid sets the "contact_wxid" field to the value that was provided on create.
+func (u *MessageRecordsUpsertOne) UpdateContactWxid() *MessageRecordsUpsertOne {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.UpdateContactWxid()
+	})
+}
+
+// SetContentType sets the "content_type" field.
+func (u *MessageRecordsUpsertOne) SetContentType(v int) *MessageRecordsUpsertOne {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.SetContentType(v)
+	})
+}
+
+// AddContentType adds v to the "content_type" field.
+func (u *MessageRecordsUpsertOne) AddContentType(v int) *MessageRecordsUpsertOne {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.AddContentType(v)
+	})
+}
+
+// UpdateContentType sets the "content_type" field to the value that was provided on create.
+func (u *MessageRecordsUpsertOne) UpdateContentType() *MessageRecordsUpsertOne {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.UpdateContentType()
+	})
+}
+
+// SetContent sets the "content" field.
+func (u *MessageRecordsUpsertOne) SetContent(v string) *MessageRecordsUpsertOne {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.SetContent(v)
+	})
+}
+
+// UpdateContent sets the "content" field to the value that was provided on create.
+func (u *MessageRecordsUpsertOne) UpdateContent() *MessageRecordsUpsertOne {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.UpdateContent()
+	})
+}
+
+// SetErrorDetail sets the "error_detail" field.
+func (u *MessageRecordsUpsertOne) SetErrorDetail(v string) *MessageRecordsUpsertOne {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.SetErrorDetail(v)
+	})
+}
+
+// UpdateErrorDetail sets the "error_detail" field to the value that was provided on create.
+func (u *MessageRecordsUpsertOne) UpdateErrorDetail() *MessageRecordsUpsertOne {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.UpdateErrorDetail()
+	})
+}
+
+// SetSendTime sets the "send_time" field.
+func (u *MessageRecordsUpsertOne) SetSendTime(v time.Time) *MessageRecordsUpsertOne {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.SetSendTime(v)
+	})
+}
+
+// UpdateSendTime sets the "send_time" field to the value that was provided on create.
+func (u *MessageRecordsUpsertOne) UpdateSendTime() *MessageRecordsUpsertOne {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.UpdateSendTime()
+	})
+}
+
+// ClearSendTime clears the value of the "send_time" field.
+func (u *MessageRecordsUpsertOne) ClearSendTime() *MessageRecordsUpsertOne {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.ClearSendTime()
+	})
+}
+
+// SetSourceType sets the "source_type" field.
+func (u *MessageRecordsUpsertOne) SetSourceType(v int) *MessageRecordsUpsertOne {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.SetSourceType(v)
+	})
+}
+
+// AddSourceType adds v to the "source_type" field.
+func (u *MessageRecordsUpsertOne) AddSourceType(v int) *MessageRecordsUpsertOne {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.AddSourceType(v)
+	})
+}
+
+// UpdateSourceType sets the "source_type" field to the value that was provided on create.
+func (u *MessageRecordsUpsertOne) UpdateSourceType() *MessageRecordsUpsertOne {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.UpdateSourceType()
+	})
+}
+
+// SetSourceID sets the "source_id" field.
+func (u *MessageRecordsUpsertOne) SetSourceID(v int) *MessageRecordsUpsertOne {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.SetSourceID(v)
+	})
+}
+
+// AddSourceID adds v to the "source_id" field.
+func (u *MessageRecordsUpsertOne) AddSourceID(v int) *MessageRecordsUpsertOne {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.AddSourceID(v)
+	})
+}
+
+// UpdateSourceID sets the "source_id" field to the value that was provided on create.
+func (u *MessageRecordsUpsertOne) UpdateSourceID() *MessageRecordsUpsertOne {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.UpdateSourceID()
+	})
+}
+
+// SetSubSourceID sets the "sub_source_id" field.
+func (u *MessageRecordsUpsertOne) SetSubSourceID(v int) *MessageRecordsUpsertOne {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.SetSubSourceID(v)
+	})
+}
+
+// AddSubSourceID adds v to the "sub_source_id" field.
+func (u *MessageRecordsUpsertOne) AddSubSourceID(v int) *MessageRecordsUpsertOne {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.AddSubSourceID(v)
+	})
+}
+
+// UpdateSubSourceID sets the "sub_source_id" field to the value that was provided on create.
+func (u *MessageRecordsUpsertOne) UpdateSubSourceID() *MessageRecordsUpsertOne {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.UpdateSubSourceID()
+	})
+}
+
+// Exec executes the query.
+func (u *MessageRecordsUpsertOne) Exec(ctx context.Context) error {
+	if len(u.create.conflict) == 0 {
+		return errors.New("ent: missing options for MessageRecordsCreate.OnConflict")
+	}
+	return u.create.Exec(ctx)
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (u *MessageRecordsUpsertOne) 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 *MessageRecordsUpsertOne) 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 *MessageRecordsUpsertOne) IDX(ctx context.Context) uint64 {
+	id, err := u.ID(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return id
+}
+
+// MessageRecordsCreateBulk is the builder for creating many MessageRecords entities in bulk.
+type MessageRecordsCreateBulk struct {
+	config
+	err      error
+	builders []*MessageRecordsCreate
+	conflict []sql.ConflictOption
+}
+
+// Save creates the MessageRecords entities in the database.
+func (mrcb *MessageRecordsCreateBulk) Save(ctx context.Context) ([]*MessageRecords, error) {
+	if mrcb.err != nil {
+		return nil, mrcb.err
+	}
+	specs := make([]*sqlgraph.CreateSpec, len(mrcb.builders))
+	nodes := make([]*MessageRecords, len(mrcb.builders))
+	mutators := make([]Mutator, len(mrcb.builders))
+	for i := range mrcb.builders {
+		func(i int, root context.Context) {
+			builder := mrcb.builders[i]
+			builder.defaults()
+			var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
+				mutation, ok := m.(*MessageRecordsMutation)
+				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, mrcb.builders[i+1].mutation)
+				} else {
+					spec := &sqlgraph.BatchCreateSpec{Nodes: specs}
+					spec.OnConflict = mrcb.conflict
+					// Invoke the actual operation on the latest mutation in the chain.
+					if err = sqlgraph.BatchCreate(ctx, mrcb.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, mrcb.builders[0].mutation); err != nil {
+			return nil, err
+		}
+	}
+	return nodes, nil
+}
+
+// SaveX is like Save, but panics if an error occurs.
+func (mrcb *MessageRecordsCreateBulk) SaveX(ctx context.Context) []*MessageRecords {
+	v, err := mrcb.Save(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return v
+}
+
+// Exec executes the query.
+func (mrcb *MessageRecordsCreateBulk) Exec(ctx context.Context) error {
+	_, err := mrcb.Save(ctx)
+	return err
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (mrcb *MessageRecordsCreateBulk) ExecX(ctx context.Context) {
+	if err := mrcb.Exec(ctx); err != nil {
+		panic(err)
+	}
+}
+
+// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause
+// of the `INSERT` statement. For example:
+//
+//	client.MessageRecords.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.MessageRecordsUpsert) {
+//			SetCreatedAt(v+v).
+//		}).
+//		Exec(ctx)
+func (mrcb *MessageRecordsCreateBulk) OnConflict(opts ...sql.ConflictOption) *MessageRecordsUpsertBulk {
+	mrcb.conflict = opts
+	return &MessageRecordsUpsertBulk{
+		create: mrcb,
+	}
+}
+
+// OnConflictColumns calls `OnConflict` and configures the columns
+// as conflict target. Using this option is equivalent to using:
+//
+//	client.MessageRecords.Create().
+//		OnConflict(sql.ConflictColumns(columns...)).
+//		Exec(ctx)
+func (mrcb *MessageRecordsCreateBulk) OnConflictColumns(columns ...string) *MessageRecordsUpsertBulk {
+	mrcb.conflict = append(mrcb.conflict, sql.ConflictColumns(columns...))
+	return &MessageRecordsUpsertBulk{
+		create: mrcb,
+	}
+}
+
+// MessageRecordsUpsertBulk is the builder for "upsert"-ing
+// a bulk of MessageRecords nodes.
+type MessageRecordsUpsertBulk struct {
+	create *MessageRecordsCreateBulk
+}
+
+// UpdateNewValues updates the mutable fields using the new values that
+// were set on create. Using this option is equivalent to using:
+//
+//	client.MessageRecords.Create().
+//		OnConflict(
+//			sql.ResolveWithNewValues(),
+//			sql.ResolveWith(func(u *sql.UpdateSet) {
+//				u.SetIgnore(messagerecords.FieldID)
+//			}),
+//		).
+//		Exec(ctx)
+func (u *MessageRecordsUpsertBulk) UpdateNewValues() *MessageRecordsUpsertBulk {
+	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(messagerecords.FieldID)
+			}
+			if _, exists := b.mutation.CreatedAt(); exists {
+				s.SetIgnore(messagerecords.FieldCreatedAt)
+			}
+		}
+	}))
+	return u
+}
+
+// Ignore sets each column to itself in case of conflict.
+// Using this option is equivalent to using:
+//
+//	client.MessageRecords.Create().
+//		OnConflict(sql.ResolveWithIgnore()).
+//		Exec(ctx)
+func (u *MessageRecordsUpsertBulk) Ignore() *MessageRecordsUpsertBulk {
+	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 *MessageRecordsUpsertBulk) DoNothing() *MessageRecordsUpsertBulk {
+	u.create.conflict = append(u.create.conflict, sql.DoNothing())
+	return u
+}
+
+// Update allows overriding fields `UPDATE` values. See the MessageRecordsCreateBulk.OnConflict
+// documentation for more info.
+func (u *MessageRecordsUpsertBulk) Update(set func(*MessageRecordsUpsert)) *MessageRecordsUpsertBulk {
+	u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) {
+		set(&MessageRecordsUpsert{UpdateSet: update})
+	}))
+	return u
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (u *MessageRecordsUpsertBulk) SetUpdatedAt(v time.Time) *MessageRecordsUpsertBulk {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.SetUpdatedAt(v)
+	})
+}
+
+// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create.
+func (u *MessageRecordsUpsertBulk) UpdateUpdatedAt() *MessageRecordsUpsertBulk {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.UpdateUpdatedAt()
+	})
+}
+
+// SetStatus sets the "status" field.
+func (u *MessageRecordsUpsertBulk) SetStatus(v uint8) *MessageRecordsUpsertBulk {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.SetStatus(v)
+	})
+}
+
+// AddStatus adds v to the "status" field.
+func (u *MessageRecordsUpsertBulk) AddStatus(v uint8) *MessageRecordsUpsertBulk {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.AddStatus(v)
+	})
+}
+
+// UpdateStatus sets the "status" field to the value that was provided on create.
+func (u *MessageRecordsUpsertBulk) UpdateStatus() *MessageRecordsUpsertBulk {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.UpdateStatus()
+	})
+}
+
+// ClearStatus clears the value of the "status" field.
+func (u *MessageRecordsUpsertBulk) ClearStatus() *MessageRecordsUpsertBulk {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.ClearStatus()
+	})
+}
+
+// SetDeletedAt sets the "deleted_at" field.
+func (u *MessageRecordsUpsertBulk) SetDeletedAt(v time.Time) *MessageRecordsUpsertBulk {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.SetDeletedAt(v)
+	})
+}
+
+// UpdateDeletedAt sets the "deleted_at" field to the value that was provided on create.
+func (u *MessageRecordsUpsertBulk) UpdateDeletedAt() *MessageRecordsUpsertBulk {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.UpdateDeletedAt()
+	})
+}
+
+// ClearDeletedAt clears the value of the "deleted_at" field.
+func (u *MessageRecordsUpsertBulk) ClearDeletedAt() *MessageRecordsUpsertBulk {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.ClearDeletedAt()
+	})
+}
+
+// SetBotWxid sets the "bot_wxid" field.
+func (u *MessageRecordsUpsertBulk) SetBotWxid(v string) *MessageRecordsUpsertBulk {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.SetBotWxid(v)
+	})
+}
+
+// UpdateBotWxid sets the "bot_wxid" field to the value that was provided on create.
+func (u *MessageRecordsUpsertBulk) UpdateBotWxid() *MessageRecordsUpsertBulk {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.UpdateBotWxid()
+	})
+}
+
+// SetContactID sets the "contact_id" field.
+func (u *MessageRecordsUpsertBulk) SetContactID(v int) *MessageRecordsUpsertBulk {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.SetContactID(v)
+	})
+}
+
+// AddContactID adds v to the "contact_id" field.
+func (u *MessageRecordsUpsertBulk) AddContactID(v int) *MessageRecordsUpsertBulk {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.AddContactID(v)
+	})
+}
+
+// UpdateContactID sets the "contact_id" field to the value that was provided on create.
+func (u *MessageRecordsUpsertBulk) UpdateContactID() *MessageRecordsUpsertBulk {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.UpdateContactID()
+	})
+}
+
+// SetContactType sets the "contact_type" field.
+func (u *MessageRecordsUpsertBulk) SetContactType(v int) *MessageRecordsUpsertBulk {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.SetContactType(v)
+	})
+}
+
+// AddContactType adds v to the "contact_type" field.
+func (u *MessageRecordsUpsertBulk) AddContactType(v int) *MessageRecordsUpsertBulk {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.AddContactType(v)
+	})
+}
+
+// UpdateContactType sets the "contact_type" field to the value that was provided on create.
+func (u *MessageRecordsUpsertBulk) UpdateContactType() *MessageRecordsUpsertBulk {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.UpdateContactType()
+	})
+}
+
+// SetContactWxid sets the "contact_wxid" field.
+func (u *MessageRecordsUpsertBulk) SetContactWxid(v string) *MessageRecordsUpsertBulk {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.SetContactWxid(v)
+	})
+}
+
+// UpdateContactWxid sets the "contact_wxid" field to the value that was provided on create.
+func (u *MessageRecordsUpsertBulk) UpdateContactWxid() *MessageRecordsUpsertBulk {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.UpdateContactWxid()
+	})
+}
+
+// SetContentType sets the "content_type" field.
+func (u *MessageRecordsUpsertBulk) SetContentType(v int) *MessageRecordsUpsertBulk {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.SetContentType(v)
+	})
+}
+
+// AddContentType adds v to the "content_type" field.
+func (u *MessageRecordsUpsertBulk) AddContentType(v int) *MessageRecordsUpsertBulk {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.AddContentType(v)
+	})
+}
+
+// UpdateContentType sets the "content_type" field to the value that was provided on create.
+func (u *MessageRecordsUpsertBulk) UpdateContentType() *MessageRecordsUpsertBulk {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.UpdateContentType()
+	})
+}
+
+// SetContent sets the "content" field.
+func (u *MessageRecordsUpsertBulk) SetContent(v string) *MessageRecordsUpsertBulk {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.SetContent(v)
+	})
+}
+
+// UpdateContent sets the "content" field to the value that was provided on create.
+func (u *MessageRecordsUpsertBulk) UpdateContent() *MessageRecordsUpsertBulk {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.UpdateContent()
+	})
+}
+
+// SetErrorDetail sets the "error_detail" field.
+func (u *MessageRecordsUpsertBulk) SetErrorDetail(v string) *MessageRecordsUpsertBulk {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.SetErrorDetail(v)
+	})
+}
+
+// UpdateErrorDetail sets the "error_detail" field to the value that was provided on create.
+func (u *MessageRecordsUpsertBulk) UpdateErrorDetail() *MessageRecordsUpsertBulk {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.UpdateErrorDetail()
+	})
+}
+
+// SetSendTime sets the "send_time" field.
+func (u *MessageRecordsUpsertBulk) SetSendTime(v time.Time) *MessageRecordsUpsertBulk {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.SetSendTime(v)
+	})
+}
+
+// UpdateSendTime sets the "send_time" field to the value that was provided on create.
+func (u *MessageRecordsUpsertBulk) UpdateSendTime() *MessageRecordsUpsertBulk {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.UpdateSendTime()
+	})
+}
+
+// ClearSendTime clears the value of the "send_time" field.
+func (u *MessageRecordsUpsertBulk) ClearSendTime() *MessageRecordsUpsertBulk {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.ClearSendTime()
+	})
+}
+
+// SetSourceType sets the "source_type" field.
+func (u *MessageRecordsUpsertBulk) SetSourceType(v int) *MessageRecordsUpsertBulk {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.SetSourceType(v)
+	})
+}
+
+// AddSourceType adds v to the "source_type" field.
+func (u *MessageRecordsUpsertBulk) AddSourceType(v int) *MessageRecordsUpsertBulk {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.AddSourceType(v)
+	})
+}
+
+// UpdateSourceType sets the "source_type" field to the value that was provided on create.
+func (u *MessageRecordsUpsertBulk) UpdateSourceType() *MessageRecordsUpsertBulk {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.UpdateSourceType()
+	})
+}
+
+// SetSourceID sets the "source_id" field.
+func (u *MessageRecordsUpsertBulk) SetSourceID(v int) *MessageRecordsUpsertBulk {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.SetSourceID(v)
+	})
+}
+
+// AddSourceID adds v to the "source_id" field.
+func (u *MessageRecordsUpsertBulk) AddSourceID(v int) *MessageRecordsUpsertBulk {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.AddSourceID(v)
+	})
+}
+
+// UpdateSourceID sets the "source_id" field to the value that was provided on create.
+func (u *MessageRecordsUpsertBulk) UpdateSourceID() *MessageRecordsUpsertBulk {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.UpdateSourceID()
+	})
+}
+
+// SetSubSourceID sets the "sub_source_id" field.
+func (u *MessageRecordsUpsertBulk) SetSubSourceID(v int) *MessageRecordsUpsertBulk {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.SetSubSourceID(v)
+	})
+}
+
+// AddSubSourceID adds v to the "sub_source_id" field.
+func (u *MessageRecordsUpsertBulk) AddSubSourceID(v int) *MessageRecordsUpsertBulk {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.AddSubSourceID(v)
+	})
+}
+
+// UpdateSubSourceID sets the "sub_source_id" field to the value that was provided on create.
+func (u *MessageRecordsUpsertBulk) UpdateSubSourceID() *MessageRecordsUpsertBulk {
+	return u.Update(func(s *MessageRecordsUpsert) {
+		s.UpdateSubSourceID()
+	})
+}
+
+// Exec executes the query.
+func (u *MessageRecordsUpsertBulk) 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 MessageRecordsCreateBulk instead", i)
+		}
+	}
+	if len(u.create.conflict) == 0 {
+		return errors.New("ent: missing options for MessageRecordsCreateBulk.OnConflict")
+	}
+	return u.create.Exec(ctx)
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (u *MessageRecordsUpsertBulk) ExecX(ctx context.Context) {
+	if err := u.create.Exec(ctx); err != nil {
+		panic(err)
+	}
+}

+ 88 - 0
ent/messagerecords_delete.go

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

+ 526 - 0
ent/messagerecords_query.go

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

+ 860 - 0
ent/messagerecords_update.go

@@ -0,0 +1,860 @@
+// Code generated by ent, DO NOT EDIT.
+
+package ent
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"time"
+	"wechat-api/ent/messagerecords"
+	"wechat-api/ent/predicate"
+
+	"entgo.io/ent/dialect/sql"
+	"entgo.io/ent/dialect/sql/sqlgraph"
+	"entgo.io/ent/schema/field"
+)
+
+// MessageRecordsUpdate is the builder for updating MessageRecords entities.
+type MessageRecordsUpdate struct {
+	config
+	hooks    []Hook
+	mutation *MessageRecordsMutation
+}
+
+// Where appends a list predicates to the MessageRecordsUpdate builder.
+func (mru *MessageRecordsUpdate) Where(ps ...predicate.MessageRecords) *MessageRecordsUpdate {
+	mru.mutation.Where(ps...)
+	return mru
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (mru *MessageRecordsUpdate) SetUpdatedAt(t time.Time) *MessageRecordsUpdate {
+	mru.mutation.SetUpdatedAt(t)
+	return mru
+}
+
+// SetStatus sets the "status" field.
+func (mru *MessageRecordsUpdate) SetStatus(u uint8) *MessageRecordsUpdate {
+	mru.mutation.ResetStatus()
+	mru.mutation.SetStatus(u)
+	return mru
+}
+
+// SetNillableStatus sets the "status" field if the given value is not nil.
+func (mru *MessageRecordsUpdate) SetNillableStatus(u *uint8) *MessageRecordsUpdate {
+	if u != nil {
+		mru.SetStatus(*u)
+	}
+	return mru
+}
+
+// AddStatus adds u to the "status" field.
+func (mru *MessageRecordsUpdate) AddStatus(u int8) *MessageRecordsUpdate {
+	mru.mutation.AddStatus(u)
+	return mru
+}
+
+// ClearStatus clears the value of the "status" field.
+func (mru *MessageRecordsUpdate) ClearStatus() *MessageRecordsUpdate {
+	mru.mutation.ClearStatus()
+	return mru
+}
+
+// SetDeletedAt sets the "deleted_at" field.
+func (mru *MessageRecordsUpdate) SetDeletedAt(t time.Time) *MessageRecordsUpdate {
+	mru.mutation.SetDeletedAt(t)
+	return mru
+}
+
+// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil.
+func (mru *MessageRecordsUpdate) SetNillableDeletedAt(t *time.Time) *MessageRecordsUpdate {
+	if t != nil {
+		mru.SetDeletedAt(*t)
+	}
+	return mru
+}
+
+// ClearDeletedAt clears the value of the "deleted_at" field.
+func (mru *MessageRecordsUpdate) ClearDeletedAt() *MessageRecordsUpdate {
+	mru.mutation.ClearDeletedAt()
+	return mru
+}
+
+// SetBotWxid sets the "bot_wxid" field.
+func (mru *MessageRecordsUpdate) SetBotWxid(s string) *MessageRecordsUpdate {
+	mru.mutation.SetBotWxid(s)
+	return mru
+}
+
+// SetNillableBotWxid sets the "bot_wxid" field if the given value is not nil.
+func (mru *MessageRecordsUpdate) SetNillableBotWxid(s *string) *MessageRecordsUpdate {
+	if s != nil {
+		mru.SetBotWxid(*s)
+	}
+	return mru
+}
+
+// SetContactID sets the "contact_id" field.
+func (mru *MessageRecordsUpdate) SetContactID(i int) *MessageRecordsUpdate {
+	mru.mutation.ResetContactID()
+	mru.mutation.SetContactID(i)
+	return mru
+}
+
+// SetNillableContactID sets the "contact_id" field if the given value is not nil.
+func (mru *MessageRecordsUpdate) SetNillableContactID(i *int) *MessageRecordsUpdate {
+	if i != nil {
+		mru.SetContactID(*i)
+	}
+	return mru
+}
+
+// AddContactID adds i to the "contact_id" field.
+func (mru *MessageRecordsUpdate) AddContactID(i int) *MessageRecordsUpdate {
+	mru.mutation.AddContactID(i)
+	return mru
+}
+
+// SetContactType sets the "contact_type" field.
+func (mru *MessageRecordsUpdate) SetContactType(i int) *MessageRecordsUpdate {
+	mru.mutation.ResetContactType()
+	mru.mutation.SetContactType(i)
+	return mru
+}
+
+// SetNillableContactType sets the "contact_type" field if the given value is not nil.
+func (mru *MessageRecordsUpdate) SetNillableContactType(i *int) *MessageRecordsUpdate {
+	if i != nil {
+		mru.SetContactType(*i)
+	}
+	return mru
+}
+
+// AddContactType adds i to the "contact_type" field.
+func (mru *MessageRecordsUpdate) AddContactType(i int) *MessageRecordsUpdate {
+	mru.mutation.AddContactType(i)
+	return mru
+}
+
+// SetContactWxid sets the "contact_wxid" field.
+func (mru *MessageRecordsUpdate) SetContactWxid(s string) *MessageRecordsUpdate {
+	mru.mutation.SetContactWxid(s)
+	return mru
+}
+
+// SetNillableContactWxid sets the "contact_wxid" field if the given value is not nil.
+func (mru *MessageRecordsUpdate) SetNillableContactWxid(s *string) *MessageRecordsUpdate {
+	if s != nil {
+		mru.SetContactWxid(*s)
+	}
+	return mru
+}
+
+// SetContentType sets the "content_type" field.
+func (mru *MessageRecordsUpdate) SetContentType(i int) *MessageRecordsUpdate {
+	mru.mutation.ResetContentType()
+	mru.mutation.SetContentType(i)
+	return mru
+}
+
+// SetNillableContentType sets the "content_type" field if the given value is not nil.
+func (mru *MessageRecordsUpdate) SetNillableContentType(i *int) *MessageRecordsUpdate {
+	if i != nil {
+		mru.SetContentType(*i)
+	}
+	return mru
+}
+
+// AddContentType adds i to the "content_type" field.
+func (mru *MessageRecordsUpdate) AddContentType(i int) *MessageRecordsUpdate {
+	mru.mutation.AddContentType(i)
+	return mru
+}
+
+// SetContent sets the "content" field.
+func (mru *MessageRecordsUpdate) SetContent(s string) *MessageRecordsUpdate {
+	mru.mutation.SetContent(s)
+	return mru
+}
+
+// SetNillableContent sets the "content" field if the given value is not nil.
+func (mru *MessageRecordsUpdate) SetNillableContent(s *string) *MessageRecordsUpdate {
+	if s != nil {
+		mru.SetContent(*s)
+	}
+	return mru
+}
+
+// SetErrorDetail sets the "error_detail" field.
+func (mru *MessageRecordsUpdate) SetErrorDetail(s string) *MessageRecordsUpdate {
+	mru.mutation.SetErrorDetail(s)
+	return mru
+}
+
+// SetNillableErrorDetail sets the "error_detail" field if the given value is not nil.
+func (mru *MessageRecordsUpdate) SetNillableErrorDetail(s *string) *MessageRecordsUpdate {
+	if s != nil {
+		mru.SetErrorDetail(*s)
+	}
+	return mru
+}
+
+// SetSendTime sets the "send_time" field.
+func (mru *MessageRecordsUpdate) SetSendTime(t time.Time) *MessageRecordsUpdate {
+	mru.mutation.SetSendTime(t)
+	return mru
+}
+
+// SetNillableSendTime sets the "send_time" field if the given value is not nil.
+func (mru *MessageRecordsUpdate) SetNillableSendTime(t *time.Time) *MessageRecordsUpdate {
+	if t != nil {
+		mru.SetSendTime(*t)
+	}
+	return mru
+}
+
+// ClearSendTime clears the value of the "send_time" field.
+func (mru *MessageRecordsUpdate) ClearSendTime() *MessageRecordsUpdate {
+	mru.mutation.ClearSendTime()
+	return mru
+}
+
+// SetSourceType sets the "source_type" field.
+func (mru *MessageRecordsUpdate) SetSourceType(i int) *MessageRecordsUpdate {
+	mru.mutation.ResetSourceType()
+	mru.mutation.SetSourceType(i)
+	return mru
+}
+
+// SetNillableSourceType sets the "source_type" field if the given value is not nil.
+func (mru *MessageRecordsUpdate) SetNillableSourceType(i *int) *MessageRecordsUpdate {
+	if i != nil {
+		mru.SetSourceType(*i)
+	}
+	return mru
+}
+
+// AddSourceType adds i to the "source_type" field.
+func (mru *MessageRecordsUpdate) AddSourceType(i int) *MessageRecordsUpdate {
+	mru.mutation.AddSourceType(i)
+	return mru
+}
+
+// SetSourceID sets the "source_id" field.
+func (mru *MessageRecordsUpdate) SetSourceID(i int) *MessageRecordsUpdate {
+	mru.mutation.ResetSourceID()
+	mru.mutation.SetSourceID(i)
+	return mru
+}
+
+// SetNillableSourceID sets the "source_id" field if the given value is not nil.
+func (mru *MessageRecordsUpdate) SetNillableSourceID(i *int) *MessageRecordsUpdate {
+	if i != nil {
+		mru.SetSourceID(*i)
+	}
+	return mru
+}
+
+// AddSourceID adds i to the "source_id" field.
+func (mru *MessageRecordsUpdate) AddSourceID(i int) *MessageRecordsUpdate {
+	mru.mutation.AddSourceID(i)
+	return mru
+}
+
+// SetSubSourceID sets the "sub_source_id" field.
+func (mru *MessageRecordsUpdate) SetSubSourceID(i int) *MessageRecordsUpdate {
+	mru.mutation.ResetSubSourceID()
+	mru.mutation.SetSubSourceID(i)
+	return mru
+}
+
+// SetNillableSubSourceID sets the "sub_source_id" field if the given value is not nil.
+func (mru *MessageRecordsUpdate) SetNillableSubSourceID(i *int) *MessageRecordsUpdate {
+	if i != nil {
+		mru.SetSubSourceID(*i)
+	}
+	return mru
+}
+
+// AddSubSourceID adds i to the "sub_source_id" field.
+func (mru *MessageRecordsUpdate) AddSubSourceID(i int) *MessageRecordsUpdate {
+	mru.mutation.AddSubSourceID(i)
+	return mru
+}
+
+// Mutation returns the MessageRecordsMutation object of the builder.
+func (mru *MessageRecordsUpdate) Mutation() *MessageRecordsMutation {
+	return mru.mutation
+}
+
+// Save executes the query and returns the number of nodes affected by the update operation.
+func (mru *MessageRecordsUpdate) Save(ctx context.Context) (int, error) {
+	if err := mru.defaults(); err != nil {
+		return 0, err
+	}
+	return withHooks(ctx, mru.sqlSave, mru.mutation, mru.hooks)
+}
+
+// SaveX is like Save, but panics if an error occurs.
+func (mru *MessageRecordsUpdate) SaveX(ctx context.Context) int {
+	affected, err := mru.Save(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return affected
+}
+
+// Exec executes the query.
+func (mru *MessageRecordsUpdate) Exec(ctx context.Context) error {
+	_, err := mru.Save(ctx)
+	return err
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (mru *MessageRecordsUpdate) ExecX(ctx context.Context) {
+	if err := mru.Exec(ctx); err != nil {
+		panic(err)
+	}
+}
+
+// defaults sets the default values of the builder before save.
+func (mru *MessageRecordsUpdate) defaults() error {
+	if _, ok := mru.mutation.UpdatedAt(); !ok {
+		if messagerecords.UpdateDefaultUpdatedAt == nil {
+			return fmt.Errorf("ent: uninitialized messagerecords.UpdateDefaultUpdatedAt (forgotten import ent/runtime?)")
+		}
+		v := messagerecords.UpdateDefaultUpdatedAt()
+		mru.mutation.SetUpdatedAt(v)
+	}
+	return nil
+}
+
+func (mru *MessageRecordsUpdate) sqlSave(ctx context.Context) (n int, err error) {
+	_spec := sqlgraph.NewUpdateSpec(messagerecords.Table, messagerecords.Columns, sqlgraph.NewFieldSpec(messagerecords.FieldID, field.TypeUint64))
+	if ps := mru.mutation.predicates; len(ps) > 0 {
+		_spec.Predicate = func(selector *sql.Selector) {
+			for i := range ps {
+				ps[i](selector)
+			}
+		}
+	}
+	if value, ok := mru.mutation.UpdatedAt(); ok {
+		_spec.SetField(messagerecords.FieldUpdatedAt, field.TypeTime, value)
+	}
+	if value, ok := mru.mutation.Status(); ok {
+		_spec.SetField(messagerecords.FieldStatus, field.TypeUint8, value)
+	}
+	if value, ok := mru.mutation.AddedStatus(); ok {
+		_spec.AddField(messagerecords.FieldStatus, field.TypeUint8, value)
+	}
+	if mru.mutation.StatusCleared() {
+		_spec.ClearField(messagerecords.FieldStatus, field.TypeUint8)
+	}
+	if value, ok := mru.mutation.DeletedAt(); ok {
+		_spec.SetField(messagerecords.FieldDeletedAt, field.TypeTime, value)
+	}
+	if mru.mutation.DeletedAtCleared() {
+		_spec.ClearField(messagerecords.FieldDeletedAt, field.TypeTime)
+	}
+	if value, ok := mru.mutation.BotWxid(); ok {
+		_spec.SetField(messagerecords.FieldBotWxid, field.TypeString, value)
+	}
+	if value, ok := mru.mutation.ContactID(); ok {
+		_spec.SetField(messagerecords.FieldContactID, field.TypeInt, value)
+	}
+	if value, ok := mru.mutation.AddedContactID(); ok {
+		_spec.AddField(messagerecords.FieldContactID, field.TypeInt, value)
+	}
+	if value, ok := mru.mutation.ContactType(); ok {
+		_spec.SetField(messagerecords.FieldContactType, field.TypeInt, value)
+	}
+	if value, ok := mru.mutation.AddedContactType(); ok {
+		_spec.AddField(messagerecords.FieldContactType, field.TypeInt, value)
+	}
+	if value, ok := mru.mutation.ContactWxid(); ok {
+		_spec.SetField(messagerecords.FieldContactWxid, field.TypeString, value)
+	}
+	if value, ok := mru.mutation.ContentType(); ok {
+		_spec.SetField(messagerecords.FieldContentType, field.TypeInt, value)
+	}
+	if value, ok := mru.mutation.AddedContentType(); ok {
+		_spec.AddField(messagerecords.FieldContentType, field.TypeInt, value)
+	}
+	if value, ok := mru.mutation.Content(); ok {
+		_spec.SetField(messagerecords.FieldContent, field.TypeString, value)
+	}
+	if value, ok := mru.mutation.ErrorDetail(); ok {
+		_spec.SetField(messagerecords.FieldErrorDetail, field.TypeString, value)
+	}
+	if value, ok := mru.mutation.SendTime(); ok {
+		_spec.SetField(messagerecords.FieldSendTime, field.TypeTime, value)
+	}
+	if mru.mutation.SendTimeCleared() {
+		_spec.ClearField(messagerecords.FieldSendTime, field.TypeTime)
+	}
+	if value, ok := mru.mutation.SourceType(); ok {
+		_spec.SetField(messagerecords.FieldSourceType, field.TypeInt, value)
+	}
+	if value, ok := mru.mutation.AddedSourceType(); ok {
+		_spec.AddField(messagerecords.FieldSourceType, field.TypeInt, value)
+	}
+	if value, ok := mru.mutation.SourceID(); ok {
+		_spec.SetField(messagerecords.FieldSourceID, field.TypeInt, value)
+	}
+	if value, ok := mru.mutation.AddedSourceID(); ok {
+		_spec.AddField(messagerecords.FieldSourceID, field.TypeInt, value)
+	}
+	if value, ok := mru.mutation.SubSourceID(); ok {
+		_spec.SetField(messagerecords.FieldSubSourceID, field.TypeInt, value)
+	}
+	if value, ok := mru.mutation.AddedSubSourceID(); ok {
+		_spec.AddField(messagerecords.FieldSubSourceID, field.TypeInt, value)
+	}
+	if n, err = sqlgraph.UpdateNodes(ctx, mru.driver, _spec); err != nil {
+		if _, ok := err.(*sqlgraph.NotFoundError); ok {
+			err = &NotFoundError{messagerecords.Label}
+		} else if sqlgraph.IsConstraintError(err) {
+			err = &ConstraintError{msg: err.Error(), wrap: err}
+		}
+		return 0, err
+	}
+	mru.mutation.done = true
+	return n, nil
+}
+
+// MessageRecordsUpdateOne is the builder for updating a single MessageRecords entity.
+type MessageRecordsUpdateOne struct {
+	config
+	fields   []string
+	hooks    []Hook
+	mutation *MessageRecordsMutation
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (mruo *MessageRecordsUpdateOne) SetUpdatedAt(t time.Time) *MessageRecordsUpdateOne {
+	mruo.mutation.SetUpdatedAt(t)
+	return mruo
+}
+
+// SetStatus sets the "status" field.
+func (mruo *MessageRecordsUpdateOne) SetStatus(u uint8) *MessageRecordsUpdateOne {
+	mruo.mutation.ResetStatus()
+	mruo.mutation.SetStatus(u)
+	return mruo
+}
+
+// SetNillableStatus sets the "status" field if the given value is not nil.
+func (mruo *MessageRecordsUpdateOne) SetNillableStatus(u *uint8) *MessageRecordsUpdateOne {
+	if u != nil {
+		mruo.SetStatus(*u)
+	}
+	return mruo
+}
+
+// AddStatus adds u to the "status" field.
+func (mruo *MessageRecordsUpdateOne) AddStatus(u int8) *MessageRecordsUpdateOne {
+	mruo.mutation.AddStatus(u)
+	return mruo
+}
+
+// ClearStatus clears the value of the "status" field.
+func (mruo *MessageRecordsUpdateOne) ClearStatus() *MessageRecordsUpdateOne {
+	mruo.mutation.ClearStatus()
+	return mruo
+}
+
+// SetDeletedAt sets the "deleted_at" field.
+func (mruo *MessageRecordsUpdateOne) SetDeletedAt(t time.Time) *MessageRecordsUpdateOne {
+	mruo.mutation.SetDeletedAt(t)
+	return mruo
+}
+
+// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil.
+func (mruo *MessageRecordsUpdateOne) SetNillableDeletedAt(t *time.Time) *MessageRecordsUpdateOne {
+	if t != nil {
+		mruo.SetDeletedAt(*t)
+	}
+	return mruo
+}
+
+// ClearDeletedAt clears the value of the "deleted_at" field.
+func (mruo *MessageRecordsUpdateOne) ClearDeletedAt() *MessageRecordsUpdateOne {
+	mruo.mutation.ClearDeletedAt()
+	return mruo
+}
+
+// SetBotWxid sets the "bot_wxid" field.
+func (mruo *MessageRecordsUpdateOne) SetBotWxid(s string) *MessageRecordsUpdateOne {
+	mruo.mutation.SetBotWxid(s)
+	return mruo
+}
+
+// SetNillableBotWxid sets the "bot_wxid" field if the given value is not nil.
+func (mruo *MessageRecordsUpdateOne) SetNillableBotWxid(s *string) *MessageRecordsUpdateOne {
+	if s != nil {
+		mruo.SetBotWxid(*s)
+	}
+	return mruo
+}
+
+// SetContactID sets the "contact_id" field.
+func (mruo *MessageRecordsUpdateOne) SetContactID(i int) *MessageRecordsUpdateOne {
+	mruo.mutation.ResetContactID()
+	mruo.mutation.SetContactID(i)
+	return mruo
+}
+
+// SetNillableContactID sets the "contact_id" field if the given value is not nil.
+func (mruo *MessageRecordsUpdateOne) SetNillableContactID(i *int) *MessageRecordsUpdateOne {
+	if i != nil {
+		mruo.SetContactID(*i)
+	}
+	return mruo
+}
+
+// AddContactID adds i to the "contact_id" field.
+func (mruo *MessageRecordsUpdateOne) AddContactID(i int) *MessageRecordsUpdateOne {
+	mruo.mutation.AddContactID(i)
+	return mruo
+}
+
+// SetContactType sets the "contact_type" field.
+func (mruo *MessageRecordsUpdateOne) SetContactType(i int) *MessageRecordsUpdateOne {
+	mruo.mutation.ResetContactType()
+	mruo.mutation.SetContactType(i)
+	return mruo
+}
+
+// SetNillableContactType sets the "contact_type" field if the given value is not nil.
+func (mruo *MessageRecordsUpdateOne) SetNillableContactType(i *int) *MessageRecordsUpdateOne {
+	if i != nil {
+		mruo.SetContactType(*i)
+	}
+	return mruo
+}
+
+// AddContactType adds i to the "contact_type" field.
+func (mruo *MessageRecordsUpdateOne) AddContactType(i int) *MessageRecordsUpdateOne {
+	mruo.mutation.AddContactType(i)
+	return mruo
+}
+
+// SetContactWxid sets the "contact_wxid" field.
+func (mruo *MessageRecordsUpdateOne) SetContactWxid(s string) *MessageRecordsUpdateOne {
+	mruo.mutation.SetContactWxid(s)
+	return mruo
+}
+
+// SetNillableContactWxid sets the "contact_wxid" field if the given value is not nil.
+func (mruo *MessageRecordsUpdateOne) SetNillableContactWxid(s *string) *MessageRecordsUpdateOne {
+	if s != nil {
+		mruo.SetContactWxid(*s)
+	}
+	return mruo
+}
+
+// SetContentType sets the "content_type" field.
+func (mruo *MessageRecordsUpdateOne) SetContentType(i int) *MessageRecordsUpdateOne {
+	mruo.mutation.ResetContentType()
+	mruo.mutation.SetContentType(i)
+	return mruo
+}
+
+// SetNillableContentType sets the "content_type" field if the given value is not nil.
+func (mruo *MessageRecordsUpdateOne) SetNillableContentType(i *int) *MessageRecordsUpdateOne {
+	if i != nil {
+		mruo.SetContentType(*i)
+	}
+	return mruo
+}
+
+// AddContentType adds i to the "content_type" field.
+func (mruo *MessageRecordsUpdateOne) AddContentType(i int) *MessageRecordsUpdateOne {
+	mruo.mutation.AddContentType(i)
+	return mruo
+}
+
+// SetContent sets the "content" field.
+func (mruo *MessageRecordsUpdateOne) SetContent(s string) *MessageRecordsUpdateOne {
+	mruo.mutation.SetContent(s)
+	return mruo
+}
+
+// SetNillableContent sets the "content" field if the given value is not nil.
+func (mruo *MessageRecordsUpdateOne) SetNillableContent(s *string) *MessageRecordsUpdateOne {
+	if s != nil {
+		mruo.SetContent(*s)
+	}
+	return mruo
+}
+
+// SetErrorDetail sets the "error_detail" field.
+func (mruo *MessageRecordsUpdateOne) SetErrorDetail(s string) *MessageRecordsUpdateOne {
+	mruo.mutation.SetErrorDetail(s)
+	return mruo
+}
+
+// SetNillableErrorDetail sets the "error_detail" field if the given value is not nil.
+func (mruo *MessageRecordsUpdateOne) SetNillableErrorDetail(s *string) *MessageRecordsUpdateOne {
+	if s != nil {
+		mruo.SetErrorDetail(*s)
+	}
+	return mruo
+}
+
+// SetSendTime sets the "send_time" field.
+func (mruo *MessageRecordsUpdateOne) SetSendTime(t time.Time) *MessageRecordsUpdateOne {
+	mruo.mutation.SetSendTime(t)
+	return mruo
+}
+
+// SetNillableSendTime sets the "send_time" field if the given value is not nil.
+func (mruo *MessageRecordsUpdateOne) SetNillableSendTime(t *time.Time) *MessageRecordsUpdateOne {
+	if t != nil {
+		mruo.SetSendTime(*t)
+	}
+	return mruo
+}
+
+// ClearSendTime clears the value of the "send_time" field.
+func (mruo *MessageRecordsUpdateOne) ClearSendTime() *MessageRecordsUpdateOne {
+	mruo.mutation.ClearSendTime()
+	return mruo
+}
+
+// SetSourceType sets the "source_type" field.
+func (mruo *MessageRecordsUpdateOne) SetSourceType(i int) *MessageRecordsUpdateOne {
+	mruo.mutation.ResetSourceType()
+	mruo.mutation.SetSourceType(i)
+	return mruo
+}
+
+// SetNillableSourceType sets the "source_type" field if the given value is not nil.
+func (mruo *MessageRecordsUpdateOne) SetNillableSourceType(i *int) *MessageRecordsUpdateOne {
+	if i != nil {
+		mruo.SetSourceType(*i)
+	}
+	return mruo
+}
+
+// AddSourceType adds i to the "source_type" field.
+func (mruo *MessageRecordsUpdateOne) AddSourceType(i int) *MessageRecordsUpdateOne {
+	mruo.mutation.AddSourceType(i)
+	return mruo
+}
+
+// SetSourceID sets the "source_id" field.
+func (mruo *MessageRecordsUpdateOne) SetSourceID(i int) *MessageRecordsUpdateOne {
+	mruo.mutation.ResetSourceID()
+	mruo.mutation.SetSourceID(i)
+	return mruo
+}
+
+// SetNillableSourceID sets the "source_id" field if the given value is not nil.
+func (mruo *MessageRecordsUpdateOne) SetNillableSourceID(i *int) *MessageRecordsUpdateOne {
+	if i != nil {
+		mruo.SetSourceID(*i)
+	}
+	return mruo
+}
+
+// AddSourceID adds i to the "source_id" field.
+func (mruo *MessageRecordsUpdateOne) AddSourceID(i int) *MessageRecordsUpdateOne {
+	mruo.mutation.AddSourceID(i)
+	return mruo
+}
+
+// SetSubSourceID sets the "sub_source_id" field.
+func (mruo *MessageRecordsUpdateOne) SetSubSourceID(i int) *MessageRecordsUpdateOne {
+	mruo.mutation.ResetSubSourceID()
+	mruo.mutation.SetSubSourceID(i)
+	return mruo
+}
+
+// SetNillableSubSourceID sets the "sub_source_id" field if the given value is not nil.
+func (mruo *MessageRecordsUpdateOne) SetNillableSubSourceID(i *int) *MessageRecordsUpdateOne {
+	if i != nil {
+		mruo.SetSubSourceID(*i)
+	}
+	return mruo
+}
+
+// AddSubSourceID adds i to the "sub_source_id" field.
+func (mruo *MessageRecordsUpdateOne) AddSubSourceID(i int) *MessageRecordsUpdateOne {
+	mruo.mutation.AddSubSourceID(i)
+	return mruo
+}
+
+// Mutation returns the MessageRecordsMutation object of the builder.
+func (mruo *MessageRecordsUpdateOne) Mutation() *MessageRecordsMutation {
+	return mruo.mutation
+}
+
+// Where appends a list predicates to the MessageRecordsUpdate builder.
+func (mruo *MessageRecordsUpdateOne) Where(ps ...predicate.MessageRecords) *MessageRecordsUpdateOne {
+	mruo.mutation.Where(ps...)
+	return mruo
+}
+
+// Select allows selecting one or more fields (columns) of the returned entity.
+// The default is selecting all fields defined in the entity schema.
+func (mruo *MessageRecordsUpdateOne) Select(field string, fields ...string) *MessageRecordsUpdateOne {
+	mruo.fields = append([]string{field}, fields...)
+	return mruo
+}
+
+// Save executes the query and returns the updated MessageRecords entity.
+func (mruo *MessageRecordsUpdateOne) Save(ctx context.Context) (*MessageRecords, error) {
+	if err := mruo.defaults(); err != nil {
+		return nil, err
+	}
+	return withHooks(ctx, mruo.sqlSave, mruo.mutation, mruo.hooks)
+}
+
+// SaveX is like Save, but panics if an error occurs.
+func (mruo *MessageRecordsUpdateOne) SaveX(ctx context.Context) *MessageRecords {
+	node, err := mruo.Save(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return node
+}
+
+// Exec executes the query on the entity.
+func (mruo *MessageRecordsUpdateOne) Exec(ctx context.Context) error {
+	_, err := mruo.Save(ctx)
+	return err
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (mruo *MessageRecordsUpdateOne) ExecX(ctx context.Context) {
+	if err := mruo.Exec(ctx); err != nil {
+		panic(err)
+	}
+}
+
+// defaults sets the default values of the builder before save.
+func (mruo *MessageRecordsUpdateOne) defaults() error {
+	if _, ok := mruo.mutation.UpdatedAt(); !ok {
+		if messagerecords.UpdateDefaultUpdatedAt == nil {
+			return fmt.Errorf("ent: uninitialized messagerecords.UpdateDefaultUpdatedAt (forgotten import ent/runtime?)")
+		}
+		v := messagerecords.UpdateDefaultUpdatedAt()
+		mruo.mutation.SetUpdatedAt(v)
+	}
+	return nil
+}
+
+func (mruo *MessageRecordsUpdateOne) sqlSave(ctx context.Context) (_node *MessageRecords, err error) {
+	_spec := sqlgraph.NewUpdateSpec(messagerecords.Table, messagerecords.Columns, sqlgraph.NewFieldSpec(messagerecords.FieldID, field.TypeUint64))
+	id, ok := mruo.mutation.ID()
+	if !ok {
+		return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "MessageRecords.id" for update`)}
+	}
+	_spec.Node.ID.Value = id
+	if fields := mruo.fields; len(fields) > 0 {
+		_spec.Node.Columns = make([]string, 0, len(fields))
+		_spec.Node.Columns = append(_spec.Node.Columns, messagerecords.FieldID)
+		for _, f := range fields {
+			if !messagerecords.ValidColumn(f) {
+				return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
+			}
+			if f != messagerecords.FieldID {
+				_spec.Node.Columns = append(_spec.Node.Columns, f)
+			}
+		}
+	}
+	if ps := mruo.mutation.predicates; len(ps) > 0 {
+		_spec.Predicate = func(selector *sql.Selector) {
+			for i := range ps {
+				ps[i](selector)
+			}
+		}
+	}
+	if value, ok := mruo.mutation.UpdatedAt(); ok {
+		_spec.SetField(messagerecords.FieldUpdatedAt, field.TypeTime, value)
+	}
+	if value, ok := mruo.mutation.Status(); ok {
+		_spec.SetField(messagerecords.FieldStatus, field.TypeUint8, value)
+	}
+	if value, ok := mruo.mutation.AddedStatus(); ok {
+		_spec.AddField(messagerecords.FieldStatus, field.TypeUint8, value)
+	}
+	if mruo.mutation.StatusCleared() {
+		_spec.ClearField(messagerecords.FieldStatus, field.TypeUint8)
+	}
+	if value, ok := mruo.mutation.DeletedAt(); ok {
+		_spec.SetField(messagerecords.FieldDeletedAt, field.TypeTime, value)
+	}
+	if mruo.mutation.DeletedAtCleared() {
+		_spec.ClearField(messagerecords.FieldDeletedAt, field.TypeTime)
+	}
+	if value, ok := mruo.mutation.BotWxid(); ok {
+		_spec.SetField(messagerecords.FieldBotWxid, field.TypeString, value)
+	}
+	if value, ok := mruo.mutation.ContactID(); ok {
+		_spec.SetField(messagerecords.FieldContactID, field.TypeInt, value)
+	}
+	if value, ok := mruo.mutation.AddedContactID(); ok {
+		_spec.AddField(messagerecords.FieldContactID, field.TypeInt, value)
+	}
+	if value, ok := mruo.mutation.ContactType(); ok {
+		_spec.SetField(messagerecords.FieldContactType, field.TypeInt, value)
+	}
+	if value, ok := mruo.mutation.AddedContactType(); ok {
+		_spec.AddField(messagerecords.FieldContactType, field.TypeInt, value)
+	}
+	if value, ok := mruo.mutation.ContactWxid(); ok {
+		_spec.SetField(messagerecords.FieldContactWxid, field.TypeString, value)
+	}
+	if value, ok := mruo.mutation.ContentType(); ok {
+		_spec.SetField(messagerecords.FieldContentType, field.TypeInt, value)
+	}
+	if value, ok := mruo.mutation.AddedContentType(); ok {
+		_spec.AddField(messagerecords.FieldContentType, field.TypeInt, value)
+	}
+	if value, ok := mruo.mutation.Content(); ok {
+		_spec.SetField(messagerecords.FieldContent, field.TypeString, value)
+	}
+	if value, ok := mruo.mutation.ErrorDetail(); ok {
+		_spec.SetField(messagerecords.FieldErrorDetail, field.TypeString, value)
+	}
+	if value, ok := mruo.mutation.SendTime(); ok {
+		_spec.SetField(messagerecords.FieldSendTime, field.TypeTime, value)
+	}
+	if mruo.mutation.SendTimeCleared() {
+		_spec.ClearField(messagerecords.FieldSendTime, field.TypeTime)
+	}
+	if value, ok := mruo.mutation.SourceType(); ok {
+		_spec.SetField(messagerecords.FieldSourceType, field.TypeInt, value)
+	}
+	if value, ok := mruo.mutation.AddedSourceType(); ok {
+		_spec.AddField(messagerecords.FieldSourceType, field.TypeInt, value)
+	}
+	if value, ok := mruo.mutation.SourceID(); ok {
+		_spec.SetField(messagerecords.FieldSourceID, field.TypeInt, value)
+	}
+	if value, ok := mruo.mutation.AddedSourceID(); ok {
+		_spec.AddField(messagerecords.FieldSourceID, field.TypeInt, value)
+	}
+	if value, ok := mruo.mutation.SubSourceID(); ok {
+		_spec.SetField(messagerecords.FieldSubSourceID, field.TypeInt, value)
+	}
+	if value, ok := mruo.mutation.AddedSubSourceID(); ok {
+		_spec.AddField(messagerecords.FieldSubSourceID, field.TypeInt, value)
+	}
+	_node = &MessageRecords{config: mruo.config}
+	_spec.Assign = _node.assignValues
+	_spec.ScanValues = _node.scanValues
+	if err = sqlgraph.UpdateNode(ctx, mruo.driver, _spec); err != nil {
+		if _, ok := err.(*sqlgraph.NotFoundError); ok {
+			err = &NotFoundError{messagerecords.Label}
+		} else if sqlgraph.IsConstraintError(err) {
+			err = &ConstraintError{msg: err.Error(), wrap: err}
+		}
+		return nil, err
+	}
+	mruo.mutation.done = true
+	return _node, nil
+}

+ 150 - 0
ent/migrate/schema.go

@@ -141,6 +141,38 @@ var (
 		Columns:    MessageColumns,
 		PrimaryKey: []*schema.Column{MessageColumns[0]},
 	}
+	// MessageRecordsColumns holds the columns for the "message_records" table.
+	MessageRecordsColumns = []*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: "status", Type: field.TypeUint8, Nullable: true, Comment: "Status 1: normal 2: ban | 状态 1 正常 2 禁用", Default: 1},
+		{Name: "deleted_at", Type: field.TypeTime, Nullable: true, Comment: "Delete Time | 删除日期"},
+		{Name: "bot_wxid", Type: field.TypeString, Comment: "机器人微信 id"},
+		{Name: "contact_id", Type: field.TypeInt, Comment: "联系人 id"},
+		{Name: "contact_type", Type: field.TypeInt, Comment: "类型:1好友,2群组,3企业微信联系人", Default: 1},
+		{Name: "contact_wxid", Type: field.TypeString, Comment: "接收方微信 id", Default: ""},
+		{Name: "content_type", Type: field.TypeInt, Comment: "内容类型 1 文本 2 文件", Default: 1},
+		{Name: "content", Type: field.TypeString, Comment: "发送内容", Default: ""},
+		{Name: "error_detail", Type: field.TypeString, Comment: "异常原因", Default: ""},
+		{Name: "send_time", Type: field.TypeTime, Nullable: true, Comment: "发送时间"},
+		{Name: "source_type", Type: field.TypeInt, Comment: "源类型 1 点发 2 群发 3 SOP", Default: 1},
+		{Name: "source_id", Type: field.TypeInt, Comment: "源 ID", Default: 1},
+		{Name: "sub_source_id", Type: field.TypeInt, Comment: "次源 ID", Default: 1},
+	}
+	// MessageRecordsTable holds the schema information for the "message_records" table.
+	MessageRecordsTable = &schema.Table{
+		Name:       "message_records",
+		Columns:    MessageRecordsColumns,
+		PrimaryKey: []*schema.Column{MessageRecordsColumns[0]},
+		Indexes: []*schema.Index{
+			{
+				Name:    "messagerecords_source_type",
+				Unique:  false,
+				Columns: []*schema.Column{MessageRecordsColumns[13]},
+			},
+		},
+	}
 	// ServerColumns holds the columns for the "server" table.
 	ServerColumns = []*schema.Column{
 		{Name: "id", Type: field.TypeUint64, Increment: true},
@@ -176,6 +208,106 @@ var (
 			},
 		},
 	}
+	// SopNodeColumns holds the columns for the "sop_node" table.
+	SopNodeColumns = []*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: "status", Type: field.TypeUint8, Nullable: true, Comment: "Status 1: normal 2: ban | 状态 1 正常 2 禁用", Default: 1},
+		{Name: "deleted_at", Type: field.TypeTime, Nullable: true, Comment: "Delete Time | 删除日期"},
+		{Name: "parent_id", Type: field.TypeInt, Comment: "父节点 ID"},
+		{Name: "name", Type: field.TypeString, Comment: "节点名称", Default: ""},
+		{Name: "condition_type", Type: field.TypeInt, Comment: "触发条件类型 1 客户回复后触发 2 超时后触发", Default: 1},
+		{Name: "condition_list", Type: field.TypeJSON, Comment: "触发语义列表 当为空时则代表用户回复任意内容后触发"},
+		{Name: "action_message", Type: field.TypeJSON, Comment: "命中后发送的消息内容"},
+		{Name: "action_label", Type: field.TypeJSON, Comment: "命中后需要打的标签"},
+		{Name: "stage_id", Type: field.TypeUint64, Comment: "阶段 ID"},
+	}
+	// SopNodeTable holds the schema information for the "sop_node" table.
+	SopNodeTable = &schema.Table{
+		Name:       "sop_node",
+		Columns:    SopNodeColumns,
+		PrimaryKey: []*schema.Column{SopNodeColumns[0]},
+		ForeignKeys: []*schema.ForeignKey{
+			{
+				Symbol:     "sop_node_sop_stage_stage_nodes",
+				Columns:    []*schema.Column{SopNodeColumns[11]},
+				RefColumns: []*schema.Column{SopStageColumns[0]},
+				OnDelete:   schema.NoAction,
+			},
+		},
+		Indexes: []*schema.Index{
+			{
+				Name:    "sopnode_name",
+				Unique:  false,
+				Columns: []*schema.Column{SopNodeColumns[6]},
+			},
+		},
+	}
+	// SopStageColumns holds the columns for the "sop_stage" table.
+	SopStageColumns = []*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: "status", Type: field.TypeUint8, Nullable: true, Comment: "Status 1: normal 2: ban | 状态 1 正常 2 禁用", Default: 1},
+		{Name: "deleted_at", Type: field.TypeTime, Nullable: true, Comment: "Delete Time | 删除日期"},
+		{Name: "name", Type: field.TypeString, Comment: "阶段名称", Default: ""},
+		{Name: "condition_type", Type: field.TypeInt, Comment: "客群筛选条件类型  1 按标签筛选 2 按客户基本信息筛选", Default: 1},
+		{Name: "condition_operator", Type: field.TypeInt, Comment: "筛选条件关系  1 满足所有条件(and) 2 满足任意条件(or)", Default: 1},
+		{Name: "condition_list", Type: field.TypeJSON, Comment: "筛选条件列表"},
+		{Name: "action_message", Type: field.TypeJSON, Comment: "命中后发送的消息内容"},
+		{Name: "action_label", Type: field.TypeJSON, Comment: "命中后需要打的标签"},
+		{Name: "index_sort", Type: field.TypeInt, Comment: "阶段顺序", Default: 1},
+		{Name: "task_id", Type: field.TypeUint64, Comment: "SOP 任务 ID"},
+	}
+	// SopStageTable holds the schema information for the "sop_stage" table.
+	SopStageTable = &schema.Table{
+		Name:       "sop_stage",
+		Columns:    SopStageColumns,
+		PrimaryKey: []*schema.Column{SopStageColumns[0]},
+		ForeignKeys: []*schema.ForeignKey{
+			{
+				Symbol:     "sop_stage_sop_task_task_stages",
+				Columns:    []*schema.Column{SopStageColumns[12]},
+				RefColumns: []*schema.Column{SopTaskColumns[0]},
+				OnDelete:   schema.NoAction,
+			},
+		},
+		Indexes: []*schema.Index{
+			{
+				Name:    "sopstage_name",
+				Unique:  false,
+				Columns: []*schema.Column{SopStageColumns[5]},
+			},
+		},
+	}
+	// SopTaskColumns holds the columns for the "sop_task" table.
+	SopTaskColumns = []*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: "status", Type: field.TypeUint8, Nullable: true, Comment: "Status 1: normal 2: ban | 状态 1 正常 2 禁用", Default: 1},
+		{Name: "deleted_at", Type: field.TypeTime, Nullable: true, Comment: "Delete Time | 删除日期"},
+		{Name: "name", Type: field.TypeString, Comment: "SOP 任务名称"},
+		{Name: "bot_wxid_list", Type: field.TypeJSON, Nullable: true, Comment: "机器人微信 id 列表"},
+		{Name: "type", Type: field.TypeInt, Comment: "标签类型:1好友,2群组,3企业微信联系人", Default: 1},
+		{Name: "plan_start_time", Type: field.TypeTime, Nullable: true, Comment: "任务计划开始时间"},
+		{Name: "plan_end_time", Type: field.TypeTime, Nullable: true, Comment: "任务计划结束时间"},
+		{Name: "creator_id", Type: field.TypeString, Nullable: true, Comment: "创建者 id"},
+	}
+	// SopTaskTable holds the schema information for the "sop_task" table.
+	SopTaskTable = &schema.Table{
+		Name:       "sop_task",
+		Columns:    SopTaskColumns,
+		PrimaryKey: []*schema.Column{SopTaskColumns[0]},
+		Indexes: []*schema.Index{
+			{
+				Name:    "soptask_name",
+				Unique:  false,
+				Columns: []*schema.Column{SopTaskColumns[5]},
+			},
+		},
+	}
 	// WxColumns holds the columns for the "wx" table.
 	WxColumns = []*schema.Column{
 		{Name: "id", Type: field.TypeUint64, Increment: true},
@@ -240,7 +372,11 @@ var (
 		LabelTable,
 		LabelRelationshipTable,
 		MessageTable,
+		MessageRecordsTable,
 		ServerTable,
+		SopNodeTable,
+		SopStageTable,
+		SopTaskTable,
 		WxTable,
 	}
 )
@@ -260,9 +396,23 @@ func init() {
 	MessageTable.Annotation = &entsql.Annotation{
 		Table: "message",
 	}
+	MessageRecordsTable.Annotation = &entsql.Annotation{
+		Table: "message_records",
+	}
 	ServerTable.Annotation = &entsql.Annotation{
 		Table: "server",
 	}
+	SopNodeTable.ForeignKeys[0].RefTable = SopStageTable
+	SopNodeTable.Annotation = &entsql.Annotation{
+		Table: "sop_node",
+	}
+	SopStageTable.ForeignKeys[0].RefTable = SopTaskTable
+	SopStageTable.Annotation = &entsql.Annotation{
+		Table: "sop_stage",
+	}
+	SopTaskTable.Annotation = &entsql.Annotation{
+		Table: "sop_task",
+	}
 	WxTable.ForeignKeys[0].RefTable = ServerTable
 	WxTable.Annotation = &entsql.Annotation{
 		Table: "wx",

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 4739 - 28
ent/mutation.go


+ 328 - 0
ent/pagination.go

@@ -9,7 +9,11 @@ import (
 	"wechat-api/ent/label"
 	"wechat-api/ent/labelrelationship"
 	"wechat-api/ent/message"
+	"wechat-api/ent/messagerecords"
 	"wechat-api/ent/server"
+	"wechat-api/ent/sopnode"
+	"wechat-api/ent/sopstage"
+	"wechat-api/ent/soptask"
 	"wechat-api/ent/wx"
 )
 
@@ -383,6 +387,87 @@ func (m *MessageQuery) Page(
 	return ret, nil
 }
 
+type MessageRecordsPager struct {
+	Order  messagerecords.OrderOption
+	Filter func(*MessageRecordsQuery) (*MessageRecordsQuery, error)
+}
+
+// MessageRecordsPaginateOption enables pagination customization.
+type MessageRecordsPaginateOption func(*MessageRecordsPager)
+
+// DefaultMessageRecordsOrder is the default ordering of MessageRecords.
+var DefaultMessageRecordsOrder = Desc(messagerecords.FieldID)
+
+func newMessageRecordsPager(opts []MessageRecordsPaginateOption) (*MessageRecordsPager, error) {
+	pager := &MessageRecordsPager{}
+	for _, opt := range opts {
+		opt(pager)
+	}
+	if pager.Order == nil {
+		pager.Order = DefaultMessageRecordsOrder
+	}
+	return pager, nil
+}
+
+func (p *MessageRecordsPager) ApplyFilter(query *MessageRecordsQuery) (*MessageRecordsQuery, error) {
+	if p.Filter != nil {
+		return p.Filter(query)
+	}
+	return query, nil
+}
+
+// MessageRecordsPageList is MessageRecords PageList result.
+type MessageRecordsPageList struct {
+	List        []*MessageRecords `json:"list"`
+	PageDetails *PageDetails      `json:"pageDetails"`
+}
+
+func (mr *MessageRecordsQuery) Page(
+	ctx context.Context, pageNum uint64, pageSize uint64, opts ...MessageRecordsPaginateOption,
+) (*MessageRecordsPageList, error) {
+
+	pager, err := newMessageRecordsPager(opts)
+	if err != nil {
+		return nil, err
+	}
+
+	if mr, err = pager.ApplyFilter(mr); err != nil {
+		return nil, err
+	}
+
+	ret := &MessageRecordsPageList{}
+
+	ret.PageDetails = &PageDetails{
+		Page: pageNum,
+		Size: pageSize,
+	}
+
+	query := mr.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 {
+		mr = mr.Order(pager.Order)
+	} else {
+		mr = mr.Order(DefaultMessageRecordsOrder)
+	}
+
+	mr = mr.Offset(int((pageNum - 1) * pageSize)).Limit(int(pageSize))
+	list, err := mr.All(ctx)
+	if err != nil {
+		return nil, err
+	}
+	ret.List = list
+
+	return ret, nil
+}
+
 type ServerPager struct {
 	Order  server.OrderOption
 	Filter func(*ServerQuery) (*ServerQuery, error)
@@ -464,6 +549,249 @@ func (s *ServerQuery) Page(
 	return ret, nil
 }
 
+type SopNodePager struct {
+	Order  sopnode.OrderOption
+	Filter func(*SopNodeQuery) (*SopNodeQuery, error)
+}
+
+// SopNodePaginateOption enables pagination customization.
+type SopNodePaginateOption func(*SopNodePager)
+
+// DefaultSopNodeOrder is the default ordering of SopNode.
+var DefaultSopNodeOrder = Desc(sopnode.FieldID)
+
+func newSopNodePager(opts []SopNodePaginateOption) (*SopNodePager, error) {
+	pager := &SopNodePager{}
+	for _, opt := range opts {
+		opt(pager)
+	}
+	if pager.Order == nil {
+		pager.Order = DefaultSopNodeOrder
+	}
+	return pager, nil
+}
+
+func (p *SopNodePager) ApplyFilter(query *SopNodeQuery) (*SopNodeQuery, error) {
+	if p.Filter != nil {
+		return p.Filter(query)
+	}
+	return query, nil
+}
+
+// SopNodePageList is SopNode PageList result.
+type SopNodePageList struct {
+	List        []*SopNode   `json:"list"`
+	PageDetails *PageDetails `json:"pageDetails"`
+}
+
+func (sn *SopNodeQuery) Page(
+	ctx context.Context, pageNum uint64, pageSize uint64, opts ...SopNodePaginateOption,
+) (*SopNodePageList, error) {
+
+	pager, err := newSopNodePager(opts)
+	if err != nil {
+		return nil, err
+	}
+
+	if sn, err = pager.ApplyFilter(sn); err != nil {
+		return nil, err
+	}
+
+	ret := &SopNodePageList{}
+
+	ret.PageDetails = &PageDetails{
+		Page: pageNum,
+		Size: pageSize,
+	}
+
+	query := sn.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 {
+		sn = sn.Order(pager.Order)
+	} else {
+		sn = sn.Order(DefaultSopNodeOrder)
+	}
+
+	sn = sn.Offset(int((pageNum - 1) * pageSize)).Limit(int(pageSize))
+	list, err := sn.All(ctx)
+	if err != nil {
+		return nil, err
+	}
+	ret.List = list
+
+	return ret, nil
+}
+
+type SopStagePager struct {
+	Order  sopstage.OrderOption
+	Filter func(*SopStageQuery) (*SopStageQuery, error)
+}
+
+// SopStagePaginateOption enables pagination customization.
+type SopStagePaginateOption func(*SopStagePager)
+
+// DefaultSopStageOrder is the default ordering of SopStage.
+var DefaultSopStageOrder = Desc(sopstage.FieldID)
+
+func newSopStagePager(opts []SopStagePaginateOption) (*SopStagePager, error) {
+	pager := &SopStagePager{}
+	for _, opt := range opts {
+		opt(pager)
+	}
+	if pager.Order == nil {
+		pager.Order = DefaultSopStageOrder
+	}
+	return pager, nil
+}
+
+func (p *SopStagePager) ApplyFilter(query *SopStageQuery) (*SopStageQuery, error) {
+	if p.Filter != nil {
+		return p.Filter(query)
+	}
+	return query, nil
+}
+
+// SopStagePageList is SopStage PageList result.
+type SopStagePageList struct {
+	List        []*SopStage  `json:"list"`
+	PageDetails *PageDetails `json:"pageDetails"`
+}
+
+func (ss *SopStageQuery) Page(
+	ctx context.Context, pageNum uint64, pageSize uint64, opts ...SopStagePaginateOption,
+) (*SopStagePageList, error) {
+
+	pager, err := newSopStagePager(opts)
+	if err != nil {
+		return nil, err
+	}
+
+	if ss, err = pager.ApplyFilter(ss); err != nil {
+		return nil, err
+	}
+
+	ret := &SopStagePageList{}
+
+	ret.PageDetails = &PageDetails{
+		Page: pageNum,
+		Size: pageSize,
+	}
+
+	query := ss.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 {
+		ss = ss.Order(pager.Order)
+	} else {
+		ss = ss.Order(DefaultSopStageOrder)
+	}
+
+	ss = ss.Offset(int((pageNum - 1) * pageSize)).Limit(int(pageSize))
+	list, err := ss.All(ctx)
+	if err != nil {
+		return nil, err
+	}
+	ret.List = list
+
+	return ret, nil
+}
+
+type SopTaskPager struct {
+	Order  soptask.OrderOption
+	Filter func(*SopTaskQuery) (*SopTaskQuery, error)
+}
+
+// SopTaskPaginateOption enables pagination customization.
+type SopTaskPaginateOption func(*SopTaskPager)
+
+// DefaultSopTaskOrder is the default ordering of SopTask.
+var DefaultSopTaskOrder = Desc(soptask.FieldID)
+
+func newSopTaskPager(opts []SopTaskPaginateOption) (*SopTaskPager, error) {
+	pager := &SopTaskPager{}
+	for _, opt := range opts {
+		opt(pager)
+	}
+	if pager.Order == nil {
+		pager.Order = DefaultSopTaskOrder
+	}
+	return pager, nil
+}
+
+func (p *SopTaskPager) ApplyFilter(query *SopTaskQuery) (*SopTaskQuery, error) {
+	if p.Filter != nil {
+		return p.Filter(query)
+	}
+	return query, nil
+}
+
+// SopTaskPageList is SopTask PageList result.
+type SopTaskPageList struct {
+	List        []*SopTask   `json:"list"`
+	PageDetails *PageDetails `json:"pageDetails"`
+}
+
+func (st *SopTaskQuery) Page(
+	ctx context.Context, pageNum uint64, pageSize uint64, opts ...SopTaskPaginateOption,
+) (*SopTaskPageList, error) {
+
+	pager, err := newSopTaskPager(opts)
+	if err != nil {
+		return nil, err
+	}
+
+	if st, err = pager.ApplyFilter(st); err != nil {
+		return nil, err
+	}
+
+	ret := &SopTaskPageList{}
+
+	ret.PageDetails = &PageDetails{
+		Page: pageNum,
+		Size: pageSize,
+	}
+
+	query := st.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 {
+		st = st.Order(pager.Order)
+	} else {
+		st = st.Order(DefaultSopTaskOrder)
+	}
+
+	st = st.Offset(int((pageNum - 1) * pageSize)).Limit(int(pageSize))
+	list, err := st.All(ctx)
+	if err != nil {
+		return nil, err
+	}
+	ret.List = list
+
+	return ret, nil
+}
+
 type WxPager struct {
 	Order  wx.OrderOption
 	Filter func(*WxQuery) (*WxQuery, error)

+ 12 - 0
ent/predicate/predicate.go

@@ -18,8 +18,20 @@ type LabelRelationship func(*sql.Selector)
 // Message is the predicate function for message builders.
 type Message func(*sql.Selector)
 
+// MessageRecords is the predicate function for messagerecords builders.
+type MessageRecords func(*sql.Selector)
+
 // Server is the predicate function for server builders.
 type Server func(*sql.Selector)
 
+// SopNode is the predicate function for sopnode builders.
+type SopNode func(*sql.Selector)
+
+// SopStage is the predicate function for sopstage builders.
+type SopStage func(*sql.Selector)
+
+// SopTask is the predicate function for soptask builders.
+type SopTask func(*sql.Selector)
+
 // Wx is the predicate function for wx builders.
 type Wx func(*sql.Selector)

+ 168 - 0
ent/runtime/runtime.go

@@ -8,8 +8,12 @@ import (
 	"wechat-api/ent/label"
 	"wechat-api/ent/labelrelationship"
 	"wechat-api/ent/message"
+	"wechat-api/ent/messagerecords"
 	"wechat-api/ent/schema"
 	"wechat-api/ent/server"
+	"wechat-api/ent/sopnode"
+	"wechat-api/ent/sopstage"
+	"wechat-api/ent/soptask"
 	"wechat-api/ent/wx"
 )
 
@@ -194,6 +198,63 @@ func init() {
 	messageDescContent := messageFields[2].Descriptor()
 	// message.DefaultContent holds the default value on creation for the content field.
 	message.DefaultContent = messageDescContent.Default.(string)
+	messagerecordsMixin := schema.MessageRecords{}.Mixin()
+	messagerecordsMixinHooks2 := messagerecordsMixin[2].Hooks()
+	messagerecords.Hooks[0] = messagerecordsMixinHooks2[0]
+	messagerecordsMixinInters2 := messagerecordsMixin[2].Interceptors()
+	messagerecords.Interceptors[0] = messagerecordsMixinInters2[0]
+	messagerecordsMixinFields0 := messagerecordsMixin[0].Fields()
+	_ = messagerecordsMixinFields0
+	messagerecordsMixinFields1 := messagerecordsMixin[1].Fields()
+	_ = messagerecordsMixinFields1
+	messagerecordsFields := schema.MessageRecords{}.Fields()
+	_ = messagerecordsFields
+	// messagerecordsDescCreatedAt is the schema descriptor for created_at field.
+	messagerecordsDescCreatedAt := messagerecordsMixinFields0[1].Descriptor()
+	// messagerecords.DefaultCreatedAt holds the default value on creation for the created_at field.
+	messagerecords.DefaultCreatedAt = messagerecordsDescCreatedAt.Default.(func() time.Time)
+	// messagerecordsDescUpdatedAt is the schema descriptor for updated_at field.
+	messagerecordsDescUpdatedAt := messagerecordsMixinFields0[2].Descriptor()
+	// messagerecords.DefaultUpdatedAt holds the default value on creation for the updated_at field.
+	messagerecords.DefaultUpdatedAt = messagerecordsDescUpdatedAt.Default.(func() time.Time)
+	// messagerecords.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field.
+	messagerecords.UpdateDefaultUpdatedAt = messagerecordsDescUpdatedAt.UpdateDefault.(func() time.Time)
+	// messagerecordsDescStatus is the schema descriptor for status field.
+	messagerecordsDescStatus := messagerecordsMixinFields1[0].Descriptor()
+	// messagerecords.DefaultStatus holds the default value on creation for the status field.
+	messagerecords.DefaultStatus = messagerecordsDescStatus.Default.(uint8)
+	// messagerecordsDescContactType is the schema descriptor for contact_type field.
+	messagerecordsDescContactType := messagerecordsFields[2].Descriptor()
+	// messagerecords.DefaultContactType holds the default value on creation for the contact_type field.
+	messagerecords.DefaultContactType = messagerecordsDescContactType.Default.(int)
+	// messagerecordsDescContactWxid is the schema descriptor for contact_wxid field.
+	messagerecordsDescContactWxid := messagerecordsFields[3].Descriptor()
+	// messagerecords.DefaultContactWxid holds the default value on creation for the contact_wxid field.
+	messagerecords.DefaultContactWxid = messagerecordsDescContactWxid.Default.(string)
+	// messagerecordsDescContentType is the schema descriptor for content_type field.
+	messagerecordsDescContentType := messagerecordsFields[4].Descriptor()
+	// messagerecords.DefaultContentType holds the default value on creation for the content_type field.
+	messagerecords.DefaultContentType = messagerecordsDescContentType.Default.(int)
+	// messagerecordsDescContent is the schema descriptor for content field.
+	messagerecordsDescContent := messagerecordsFields[5].Descriptor()
+	// messagerecords.DefaultContent holds the default value on creation for the content field.
+	messagerecords.DefaultContent = messagerecordsDescContent.Default.(string)
+	// messagerecordsDescErrorDetail is the schema descriptor for error_detail field.
+	messagerecordsDescErrorDetail := messagerecordsFields[6].Descriptor()
+	// messagerecords.DefaultErrorDetail holds the default value on creation for the error_detail field.
+	messagerecords.DefaultErrorDetail = messagerecordsDescErrorDetail.Default.(string)
+	// messagerecordsDescSourceType is the schema descriptor for source_type field.
+	messagerecordsDescSourceType := messagerecordsFields[8].Descriptor()
+	// messagerecords.DefaultSourceType holds the default value on creation for the source_type field.
+	messagerecords.DefaultSourceType = messagerecordsDescSourceType.Default.(int)
+	// messagerecordsDescSourceID is the schema descriptor for source_id field.
+	messagerecordsDescSourceID := messagerecordsFields[9].Descriptor()
+	// messagerecords.DefaultSourceID holds the default value on creation for the source_id field.
+	messagerecords.DefaultSourceID = messagerecordsDescSourceID.Default.(int)
+	// messagerecordsDescSubSourceID is the schema descriptor for sub_source_id field.
+	messagerecordsDescSubSourceID := messagerecordsFields[10].Descriptor()
+	// messagerecords.DefaultSubSourceID holds the default value on creation for the sub_source_id field.
+	messagerecords.DefaultSubSourceID = messagerecordsDescSubSourceID.Default.(int)
 	serverMixin := schema.Server{}.Mixin()
 	serverMixinHooks2 := serverMixin[2].Hooks()
 	server.Hooks[0] = serverMixinHooks2[0]
@@ -219,6 +280,113 @@ func init() {
 	serverDescStatus := serverMixinFields1[0].Descriptor()
 	// server.DefaultStatus holds the default value on creation for the status field.
 	server.DefaultStatus = serverDescStatus.Default.(uint8)
+	sopnodeMixin := schema.SopNode{}.Mixin()
+	sopnodeMixinHooks2 := sopnodeMixin[2].Hooks()
+	sopnode.Hooks[0] = sopnodeMixinHooks2[0]
+	sopnodeMixinInters2 := sopnodeMixin[2].Interceptors()
+	sopnode.Interceptors[0] = sopnodeMixinInters2[0]
+	sopnodeMixinFields0 := sopnodeMixin[0].Fields()
+	_ = sopnodeMixinFields0
+	sopnodeMixinFields1 := sopnodeMixin[1].Fields()
+	_ = sopnodeMixinFields1
+	sopnodeFields := schema.SopNode{}.Fields()
+	_ = sopnodeFields
+	// sopnodeDescCreatedAt is the schema descriptor for created_at field.
+	sopnodeDescCreatedAt := sopnodeMixinFields0[1].Descriptor()
+	// sopnode.DefaultCreatedAt holds the default value on creation for the created_at field.
+	sopnode.DefaultCreatedAt = sopnodeDescCreatedAt.Default.(func() time.Time)
+	// sopnodeDescUpdatedAt is the schema descriptor for updated_at field.
+	sopnodeDescUpdatedAt := sopnodeMixinFields0[2].Descriptor()
+	// sopnode.DefaultUpdatedAt holds the default value on creation for the updated_at field.
+	sopnode.DefaultUpdatedAt = sopnodeDescUpdatedAt.Default.(func() time.Time)
+	// sopnode.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field.
+	sopnode.UpdateDefaultUpdatedAt = sopnodeDescUpdatedAt.UpdateDefault.(func() time.Time)
+	// sopnodeDescStatus is the schema descriptor for status field.
+	sopnodeDescStatus := sopnodeMixinFields1[0].Descriptor()
+	// sopnode.DefaultStatus holds the default value on creation for the status field.
+	sopnode.DefaultStatus = sopnodeDescStatus.Default.(uint8)
+	// sopnodeDescName is the schema descriptor for name field.
+	sopnodeDescName := sopnodeFields[2].Descriptor()
+	// sopnode.DefaultName holds the default value on creation for the name field.
+	sopnode.DefaultName = sopnodeDescName.Default.(string)
+	// sopnodeDescConditionType is the schema descriptor for condition_type field.
+	sopnodeDescConditionType := sopnodeFields[3].Descriptor()
+	// sopnode.DefaultConditionType holds the default value on creation for the condition_type field.
+	sopnode.DefaultConditionType = sopnodeDescConditionType.Default.(int)
+	sopstageMixin := schema.SopStage{}.Mixin()
+	sopstageMixinHooks2 := sopstageMixin[2].Hooks()
+	sopstage.Hooks[0] = sopstageMixinHooks2[0]
+	sopstageMixinInters2 := sopstageMixin[2].Interceptors()
+	sopstage.Interceptors[0] = sopstageMixinInters2[0]
+	sopstageMixinFields0 := sopstageMixin[0].Fields()
+	_ = sopstageMixinFields0
+	sopstageMixinFields1 := sopstageMixin[1].Fields()
+	_ = sopstageMixinFields1
+	sopstageFields := schema.SopStage{}.Fields()
+	_ = sopstageFields
+	// sopstageDescCreatedAt is the schema descriptor for created_at field.
+	sopstageDescCreatedAt := sopstageMixinFields0[1].Descriptor()
+	// sopstage.DefaultCreatedAt holds the default value on creation for the created_at field.
+	sopstage.DefaultCreatedAt = sopstageDescCreatedAt.Default.(func() time.Time)
+	// sopstageDescUpdatedAt is the schema descriptor for updated_at field.
+	sopstageDescUpdatedAt := sopstageMixinFields0[2].Descriptor()
+	// sopstage.DefaultUpdatedAt holds the default value on creation for the updated_at field.
+	sopstage.DefaultUpdatedAt = sopstageDescUpdatedAt.Default.(func() time.Time)
+	// sopstage.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field.
+	sopstage.UpdateDefaultUpdatedAt = sopstageDescUpdatedAt.UpdateDefault.(func() time.Time)
+	// sopstageDescStatus is the schema descriptor for status field.
+	sopstageDescStatus := sopstageMixinFields1[0].Descriptor()
+	// sopstage.DefaultStatus holds the default value on creation for the status field.
+	sopstage.DefaultStatus = sopstageDescStatus.Default.(uint8)
+	// sopstageDescName is the schema descriptor for name field.
+	sopstageDescName := sopstageFields[1].Descriptor()
+	// sopstage.DefaultName holds the default value on creation for the name field.
+	sopstage.DefaultName = sopstageDescName.Default.(string)
+	// sopstageDescConditionType is the schema descriptor for condition_type field.
+	sopstageDescConditionType := sopstageFields[2].Descriptor()
+	// sopstage.DefaultConditionType holds the default value on creation for the condition_type field.
+	sopstage.DefaultConditionType = sopstageDescConditionType.Default.(int)
+	// sopstageDescConditionOperator is the schema descriptor for condition_operator field.
+	sopstageDescConditionOperator := sopstageFields[3].Descriptor()
+	// sopstage.DefaultConditionOperator holds the default value on creation for the condition_operator field.
+	sopstage.DefaultConditionOperator = sopstageDescConditionOperator.Default.(int)
+	// sopstageDescIndexSort is the schema descriptor for index_sort field.
+	sopstageDescIndexSort := sopstageFields[7].Descriptor()
+	// sopstage.DefaultIndexSort holds the default value on creation for the index_sort field.
+	sopstage.DefaultIndexSort = sopstageDescIndexSort.Default.(int)
+	soptaskMixin := schema.SopTask{}.Mixin()
+	soptaskMixinHooks2 := soptaskMixin[2].Hooks()
+	soptask.Hooks[0] = soptaskMixinHooks2[0]
+	soptaskMixinInters2 := soptaskMixin[2].Interceptors()
+	soptask.Interceptors[0] = soptaskMixinInters2[0]
+	soptaskMixinFields0 := soptaskMixin[0].Fields()
+	_ = soptaskMixinFields0
+	soptaskMixinFields1 := soptaskMixin[1].Fields()
+	_ = soptaskMixinFields1
+	soptaskFields := schema.SopTask{}.Fields()
+	_ = soptaskFields
+	// soptaskDescCreatedAt is the schema descriptor for created_at field.
+	soptaskDescCreatedAt := soptaskMixinFields0[1].Descriptor()
+	// soptask.DefaultCreatedAt holds the default value on creation for the created_at field.
+	soptask.DefaultCreatedAt = soptaskDescCreatedAt.Default.(func() time.Time)
+	// soptaskDescUpdatedAt is the schema descriptor for updated_at field.
+	soptaskDescUpdatedAt := soptaskMixinFields0[2].Descriptor()
+	// soptask.DefaultUpdatedAt holds the default value on creation for the updated_at field.
+	soptask.DefaultUpdatedAt = soptaskDescUpdatedAt.Default.(func() time.Time)
+	// soptask.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field.
+	soptask.UpdateDefaultUpdatedAt = soptaskDescUpdatedAt.UpdateDefault.(func() time.Time)
+	// soptaskDescStatus is the schema descriptor for status field.
+	soptaskDescStatus := soptaskMixinFields1[0].Descriptor()
+	// soptask.DefaultStatus holds the default value on creation for the status field.
+	soptask.DefaultStatus = soptaskDescStatus.Default.(uint8)
+	// soptaskDescName is the schema descriptor for name field.
+	soptaskDescName := soptaskFields[0].Descriptor()
+	// soptask.NameValidator is a validator for the "name" field. It is called by the builders before save.
+	soptask.NameValidator = soptaskDescName.Validators[0].(func(string) error)
+	// soptaskDescType is the schema descriptor for type field.
+	soptaskDescType := soptaskFields[2].Descriptor()
+	// soptask.DefaultType holds the default value on creation for the type field.
+	soptask.DefaultType = soptaskDescType.Default.(int)
 	wxMixin := schema.Wx{}.Mixin()
 	wxMixinHooks2 := wxMixin[2].Hooks()
 	wx.Hooks[0] = wxMixinHooks2[0]

+ 75 - 0
ent/schema/message_records.go

@@ -0,0 +1,75 @@
+package schema
+
+import (
+	"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"
+	"wechat-api/ent/schema/localmixin"
+)
+
+type MessageRecords struct {
+	ent.Schema
+}
+
+func (MessageRecords) Fields() []ent.Field {
+	return []ent.Field{
+		field.String("bot_wxid").
+			Annotations(entsql.WithComments(true)).
+			Comment("机器人微信 id"),
+		field.Int("contact_id").
+			Annotations(entsql.WithComments(true)).
+			Comment("联系人 id"),
+		field.Int("contact_type").Default(1).
+			Annotations(entsql.WithComments(true)).
+			Comment("类型:1好友,2群组,3企业微信联系人"),
+		field.String("contact_wxid").Default("").
+			Annotations(entsql.WithComments(true)).
+			Comment("接收方微信 id"),
+		field.Int("content_type").Default(1).
+			Annotations(entsql.WithComments(true)).
+			Comment("内容类型 1 文本 2 文件"),
+		field.String("content").Default("").
+			Annotations(entsql.WithComments(true)).
+			Comment("发送内容"),
+		field.String("error_detail").Default("").
+			Annotations(entsql.WithComments(true)).
+			Comment("异常原因"),
+		field.Time("send_time").Optional().
+			Annotations(entsql.WithComments(true)).
+			Comment("发送时间"),
+		field.Int("source_type").Default(1).
+			Annotations(entsql.WithComments(true)).
+			Comment("源类型 1 点发 2 群发 3 SOP"),
+		field.Int("source_id").Default(1).
+			Annotations(entsql.WithComments(true)).
+			Comment("源 ID"),
+		field.Int("sub_source_id").Default(1).
+			Annotations(entsql.WithComments(true)).
+			Comment("次源 ID"),
+	}
+}
+
+func (MessageRecords) Mixin() []ent.Mixin {
+	return []ent.Mixin{
+		mixins.IDMixin{},
+		mixins.StatusMixin{},
+		localmixin.SoftDeleteMixin{},
+	}
+}
+
+func (MessageRecords) Indexes() []ent.Index {
+	return []ent.Index{
+		index.Fields("source_type"),
+	}
+}
+
+func (MessageRecords) Edges() []ent.Edge {
+	return nil
+}
+
+func (MessageRecords) Annotations() []schema.Annotation {
+	return []schema.Annotation{entsql.Annotation{Table: "message_records"}}
+}

+ 71 - 0
ent/schema/sop_node.go

@@ -0,0 +1,71 @@
+package schema
+
+import (
+	"entgo.io/ent"
+	"entgo.io/ent/dialect/entsql"
+	"entgo.io/ent/schema"
+	"entgo.io/ent/schema/edge"
+	"entgo.io/ent/schema/field"
+	"entgo.io/ent/schema/index"
+	"github.com/suyuan32/simple-admin-common/orm/ent/mixins"
+	"wechat-api/ent/custom_types"
+	"wechat-api/ent/schema/localmixin"
+)
+
+type SopNode struct {
+	ent.Schema
+}
+
+func (SopNode) Fields() []ent.Field {
+	return []ent.Field{
+		field.Uint64("stage_id").
+			Annotations(entsql.WithComments(true)).
+			Comment("阶段 ID"),
+		field.Int("parent_id").
+			Annotations(entsql.WithComments(true)).
+			Comment("父节点 ID"),
+		field.String("name").Default("").
+			Annotations(entsql.WithComments(true)).
+			Comment("节点名称"),
+		field.Int("condition_type").Default(1).
+			Annotations(entsql.WithComments(true)).
+			Comment("触发条件类型 1 客户回复后触发 2 超时后触发"),
+		field.JSON("condition_list", []custom_types.Condition{}).
+			Annotations(entsql.WithComments(true)).
+			Comment("触发语义列表 当为空时则代表用户回复任意内容后触发"),
+		field.JSON("action_message", []custom_types.Action{}).
+			Annotations(entsql.WithComments(true)).
+			Comment("命中后发送的消息内容"),
+		field.JSON("action_label", []int{}).
+			Annotations(entsql.WithComments(true)).
+			Comment("命中后需要打的标签"),
+	}
+}
+
+func (SopNode) Mixin() []ent.Mixin {
+	return []ent.Mixin{
+		mixins.IDMixin{},
+		mixins.StatusMixin{},
+		localmixin.SoftDeleteMixin{},
+	}
+}
+
+func (SopNode) Indexes() []ent.Index {
+	return []ent.Index{
+		index.Fields("name"),
+	}
+}
+
+func (SopNode) Edges() []ent.Edge {
+	return []ent.Edge{
+		edge.From("sop_stage", SopStage.Type).
+			Ref("stage_nodes").
+			Unique().
+			Field("stage_id").
+			Required(),
+	}
+}
+
+func (SopNode) Annotations() []schema.Annotation {
+	return []schema.Annotation{entsql.Annotation{Table: "sop_node"}}
+}

+ 74 - 0
ent/schema/sop_stage.go

@@ -0,0 +1,74 @@
+package schema
+
+import (
+	"entgo.io/ent"
+	"entgo.io/ent/dialect/entsql"
+	"entgo.io/ent/schema"
+	"entgo.io/ent/schema/edge"
+	"entgo.io/ent/schema/field"
+	"entgo.io/ent/schema/index"
+	"github.com/suyuan32/simple-admin-common/orm/ent/mixins"
+	"wechat-api/ent/custom_types"
+	"wechat-api/ent/schema/localmixin"
+)
+
+type SopStage struct {
+	ent.Schema
+}
+
+func (SopStage) Fields() []ent.Field {
+	return []ent.Field{
+		field.Uint64("task_id").
+			Annotations(entsql.WithComments(true)).
+			Comment("SOP 任务 ID"),
+		field.String("name").Default("").
+			Annotations(entsql.WithComments(true)).
+			Comment("阶段名称"),
+		field.Int("condition_type").Default(1).
+			Annotations(entsql.WithComments(true)).
+			Comment("客群筛选条件类型  1 按标签筛选 2 按客户基本信息筛选"),
+		field.Int("condition_operator").Default(1).
+			Annotations(entsql.WithComments(true)).
+			Comment("筛选条件关系  1 满足所有条件(and) 2 满足任意条件(or)"),
+		field.JSON("condition_list", []custom_types.Condition{}).
+			Annotations(entsql.WithComments(true)).
+			Comment("筛选条件列表"),
+		field.JSON("action_message", []custom_types.Action{}).
+			Annotations(entsql.WithComments(true)).
+			Comment("命中后发送的消息内容"),
+		field.JSON("action_label", []int{}).
+			Annotations(entsql.WithComments(true)).
+			Comment("命中后需要打的标签"),
+		field.Int("index_sort").Default(1).
+			Annotations(entsql.WithComments(true)).
+			Comment("阶段顺序"),
+	}
+}
+
+func (SopStage) Mixin() []ent.Mixin {
+	return []ent.Mixin{
+		mixins.IDMixin{},
+		mixins.StatusMixin{},
+		localmixin.SoftDeleteMixin{},
+	}
+}
+
+func (SopStage) Indexes() []ent.Index {
+	return []ent.Index{
+		index.Fields("name"),
+	}
+}
+
+func (SopStage) Edges() []ent.Edge {
+	return []ent.Edge{
+		edge.From("sop_task", SopTask.Type).
+			Ref("task_stages").
+			Unique().
+			Field("task_id").
+			Required(),
+		edge.To("stage_nodes", SopNode.Type),
+	}
+}
+func (SopStage) Annotations() []schema.Annotation {
+	return []schema.Annotation{entsql.Annotation{Table: "sop_stage"}}
+}

+ 63 - 0
ent/schema/sop_task.go

@@ -0,0 +1,63 @@
+package schema
+
+import (
+	"entgo.io/ent"
+	"entgo.io/ent/dialect/entsql"
+	"entgo.io/ent/schema"
+	"entgo.io/ent/schema/edge"
+	"entgo.io/ent/schema/field"
+	"entgo.io/ent/schema/index"
+	"github.com/suyuan32/simple-admin-common/orm/ent/mixins"
+	"wechat-api/ent/schema/localmixin"
+)
+
+type SopTask struct {
+	ent.Schema
+}
+
+func (SopTask) Fields() []ent.Field {
+	return []ent.Field{
+		field.String("name").NotEmpty().
+			Annotations(entsql.WithComments(true)).
+			Comment("SOP 任务名称"),
+		field.JSON("bot_wxid_list", []string{}).Optional().
+			Annotations(entsql.WithComments(true)).
+			Comment("机器人微信 id 列表"),
+		field.Int("type").Default(1).
+			Annotations(entsql.WithComments(true)).
+			Comment("标签类型:1好友,2群组,3企业微信联系人"),
+		field.Time("plan_start_time").Optional().
+			Annotations(entsql.WithComments(true)).
+			Comment("任务计划开始时间"),
+		field.Time("plan_end_time").Optional().
+			Annotations(entsql.WithComments(true)).
+			Comment("任务计划结束时间"),
+		field.String("creator_id").Optional().
+			Annotations(entsql.WithComments(true)).
+			Comment("创建者 id"),
+	}
+}
+
+func (SopTask) Mixin() []ent.Mixin {
+	return []ent.Mixin{
+		mixins.IDMixin{},
+		mixins.StatusMixin{},
+		localmixin.SoftDeleteMixin{},
+	}
+}
+
+func (SopTask) Indexes() []ent.Index {
+	return []ent.Index{
+		index.Fields("name"),
+	}
+}
+
+func (SopTask) Edges() []ent.Edge {
+	return []ent.Edge{
+		edge.To("task_stages", SopStage.Type),
+	}
+}
+
+func (SopTask) Annotations() []schema.Annotation {
+	return []schema.Annotation{entsql.Annotation{Table: "sop_task"}}
+}

+ 1060 - 1
ent/set_not_nil.go

@@ -2,7 +2,10 @@
 
 package ent
 
-import "time"
+import (
+	"time"
+	"wechat-api/ent/custom_types"
+)
 
 // set field if value's pointer is not nil.
 func (c *ContactUpdate) SetNotNilUpdatedAt(value *time.Time) *ContactUpdate {
@@ -821,6 +824,342 @@ func (m *MessageCreate) SetNotNilContent(value *string) *MessageCreate {
 }
 
 // set field if value's pointer is not nil.
+func (mr *MessageRecordsUpdate) SetNotNilUpdatedAt(value *time.Time) *MessageRecordsUpdate {
+	if value != nil {
+		return mr.SetUpdatedAt(*value)
+	}
+	return mr
+}
+
+// set field if value's pointer is not nil.
+func (mr *MessageRecordsUpdateOne) SetNotNilUpdatedAt(value *time.Time) *MessageRecordsUpdateOne {
+	if value != nil {
+		return mr.SetUpdatedAt(*value)
+	}
+	return mr
+}
+
+// set field if value's pointer is not nil.
+func (mr *MessageRecordsCreate) SetNotNilUpdatedAt(value *time.Time) *MessageRecordsCreate {
+	if value != nil {
+		return mr.SetUpdatedAt(*value)
+	}
+	return mr
+}
+
+// set field if value's pointer is not nil.
+func (mr *MessageRecordsUpdate) SetNotNilStatus(value *uint8) *MessageRecordsUpdate {
+	if value != nil {
+		return mr.SetStatus(*value)
+	}
+	return mr
+}
+
+// set field if value's pointer is not nil.
+func (mr *MessageRecordsUpdateOne) SetNotNilStatus(value *uint8) *MessageRecordsUpdateOne {
+	if value != nil {
+		return mr.SetStatus(*value)
+	}
+	return mr
+}
+
+// set field if value's pointer is not nil.
+func (mr *MessageRecordsCreate) SetNotNilStatus(value *uint8) *MessageRecordsCreate {
+	if value != nil {
+		return mr.SetStatus(*value)
+	}
+	return mr
+}
+
+// set field if value's pointer is not nil.
+func (mr *MessageRecordsUpdate) SetNotNilDeletedAt(value *time.Time) *MessageRecordsUpdate {
+	if value != nil {
+		return mr.SetDeletedAt(*value)
+	}
+	return mr
+}
+
+// set field if value's pointer is not nil.
+func (mr *MessageRecordsUpdateOne) SetNotNilDeletedAt(value *time.Time) *MessageRecordsUpdateOne {
+	if value != nil {
+		return mr.SetDeletedAt(*value)
+	}
+	return mr
+}
+
+// set field if value's pointer is not nil.
+func (mr *MessageRecordsCreate) SetNotNilDeletedAt(value *time.Time) *MessageRecordsCreate {
+	if value != nil {
+		return mr.SetDeletedAt(*value)
+	}
+	return mr
+}
+
+// set field if value's pointer is not nil.
+func (mr *MessageRecordsUpdate) SetNotNilBotWxid(value *string) *MessageRecordsUpdate {
+	if value != nil {
+		return mr.SetBotWxid(*value)
+	}
+	return mr
+}
+
+// set field if value's pointer is not nil.
+func (mr *MessageRecordsUpdateOne) SetNotNilBotWxid(value *string) *MessageRecordsUpdateOne {
+	if value != nil {
+		return mr.SetBotWxid(*value)
+	}
+	return mr
+}
+
+// set field if value's pointer is not nil.
+func (mr *MessageRecordsCreate) SetNotNilBotWxid(value *string) *MessageRecordsCreate {
+	if value != nil {
+		return mr.SetBotWxid(*value)
+	}
+	return mr
+}
+
+// set field if value's pointer is not nil.
+func (mr *MessageRecordsUpdate) SetNotNilContactID(value *int) *MessageRecordsUpdate {
+	if value != nil {
+		return mr.SetContactID(*value)
+	}
+	return mr
+}
+
+// set field if value's pointer is not nil.
+func (mr *MessageRecordsUpdateOne) SetNotNilContactID(value *int) *MessageRecordsUpdateOne {
+	if value != nil {
+		return mr.SetContactID(*value)
+	}
+	return mr
+}
+
+// set field if value's pointer is not nil.
+func (mr *MessageRecordsCreate) SetNotNilContactID(value *int) *MessageRecordsCreate {
+	if value != nil {
+		return mr.SetContactID(*value)
+	}
+	return mr
+}
+
+// set field if value's pointer is not nil.
+func (mr *MessageRecordsUpdate) SetNotNilContactType(value *int) *MessageRecordsUpdate {
+	if value != nil {
+		return mr.SetContactType(*value)
+	}
+	return mr
+}
+
+// set field if value's pointer is not nil.
+func (mr *MessageRecordsUpdateOne) SetNotNilContactType(value *int) *MessageRecordsUpdateOne {
+	if value != nil {
+		return mr.SetContactType(*value)
+	}
+	return mr
+}
+
+// set field if value's pointer is not nil.
+func (mr *MessageRecordsCreate) SetNotNilContactType(value *int) *MessageRecordsCreate {
+	if value != nil {
+		return mr.SetContactType(*value)
+	}
+	return mr
+}
+
+// set field if value's pointer is not nil.
+func (mr *MessageRecordsUpdate) SetNotNilContactWxid(value *string) *MessageRecordsUpdate {
+	if value != nil {
+		return mr.SetContactWxid(*value)
+	}
+	return mr
+}
+
+// set field if value's pointer is not nil.
+func (mr *MessageRecordsUpdateOne) SetNotNilContactWxid(value *string) *MessageRecordsUpdateOne {
+	if value != nil {
+		return mr.SetContactWxid(*value)
+	}
+	return mr
+}
+
+// set field if value's pointer is not nil.
+func (mr *MessageRecordsCreate) SetNotNilContactWxid(value *string) *MessageRecordsCreate {
+	if value != nil {
+		return mr.SetContactWxid(*value)
+	}
+	return mr
+}
+
+// set field if value's pointer is not nil.
+func (mr *MessageRecordsUpdate) SetNotNilContentType(value *int) *MessageRecordsUpdate {
+	if value != nil {
+		return mr.SetContentType(*value)
+	}
+	return mr
+}
+
+// set field if value's pointer is not nil.
+func (mr *MessageRecordsUpdateOne) SetNotNilContentType(value *int) *MessageRecordsUpdateOne {
+	if value != nil {
+		return mr.SetContentType(*value)
+	}
+	return mr
+}
+
+// set field if value's pointer is not nil.
+func (mr *MessageRecordsCreate) SetNotNilContentType(value *int) *MessageRecordsCreate {
+	if value != nil {
+		return mr.SetContentType(*value)
+	}
+	return mr
+}
+
+// set field if value's pointer is not nil.
+func (mr *MessageRecordsUpdate) SetNotNilContent(value *string) *MessageRecordsUpdate {
+	if value != nil {
+		return mr.SetContent(*value)
+	}
+	return mr
+}
+
+// set field if value's pointer is not nil.
+func (mr *MessageRecordsUpdateOne) SetNotNilContent(value *string) *MessageRecordsUpdateOne {
+	if value != nil {
+		return mr.SetContent(*value)
+	}
+	return mr
+}
+
+// set field if value's pointer is not nil.
+func (mr *MessageRecordsCreate) SetNotNilContent(value *string) *MessageRecordsCreate {
+	if value != nil {
+		return mr.SetContent(*value)
+	}
+	return mr
+}
+
+// set field if value's pointer is not nil.
+func (mr *MessageRecordsUpdate) SetNotNilErrorDetail(value *string) *MessageRecordsUpdate {
+	if value != nil {
+		return mr.SetErrorDetail(*value)
+	}
+	return mr
+}
+
+// set field if value's pointer is not nil.
+func (mr *MessageRecordsUpdateOne) SetNotNilErrorDetail(value *string) *MessageRecordsUpdateOne {
+	if value != nil {
+		return mr.SetErrorDetail(*value)
+	}
+	return mr
+}
+
+// set field if value's pointer is not nil.
+func (mr *MessageRecordsCreate) SetNotNilErrorDetail(value *string) *MessageRecordsCreate {
+	if value != nil {
+		return mr.SetErrorDetail(*value)
+	}
+	return mr
+}
+
+// set field if value's pointer is not nil.
+func (mr *MessageRecordsUpdate) SetNotNilSendTime(value *time.Time) *MessageRecordsUpdate {
+	if value != nil {
+		return mr.SetSendTime(*value)
+	}
+	return mr
+}
+
+// set field if value's pointer is not nil.
+func (mr *MessageRecordsUpdateOne) SetNotNilSendTime(value *time.Time) *MessageRecordsUpdateOne {
+	if value != nil {
+		return mr.SetSendTime(*value)
+	}
+	return mr
+}
+
+// set field if value's pointer is not nil.
+func (mr *MessageRecordsCreate) SetNotNilSendTime(value *time.Time) *MessageRecordsCreate {
+	if value != nil {
+		return mr.SetSendTime(*value)
+	}
+	return mr
+}
+
+// set field if value's pointer is not nil.
+func (mr *MessageRecordsUpdate) SetNotNilSourceType(value *int) *MessageRecordsUpdate {
+	if value != nil {
+		return mr.SetSourceType(*value)
+	}
+	return mr
+}
+
+// set field if value's pointer is not nil.
+func (mr *MessageRecordsUpdateOne) SetNotNilSourceType(value *int) *MessageRecordsUpdateOne {
+	if value != nil {
+		return mr.SetSourceType(*value)
+	}
+	return mr
+}
+
+// set field if value's pointer is not nil.
+func (mr *MessageRecordsCreate) SetNotNilSourceType(value *int) *MessageRecordsCreate {
+	if value != nil {
+		return mr.SetSourceType(*value)
+	}
+	return mr
+}
+
+// set field if value's pointer is not nil.
+func (mr *MessageRecordsUpdate) SetNotNilSourceID(value *int) *MessageRecordsUpdate {
+	if value != nil {
+		return mr.SetSourceID(*value)
+	}
+	return mr
+}
+
+// set field if value's pointer is not nil.
+func (mr *MessageRecordsUpdateOne) SetNotNilSourceID(value *int) *MessageRecordsUpdateOne {
+	if value != nil {
+		return mr.SetSourceID(*value)
+	}
+	return mr
+}
+
+// set field if value's pointer is not nil.
+func (mr *MessageRecordsCreate) SetNotNilSourceID(value *int) *MessageRecordsCreate {
+	if value != nil {
+		return mr.SetSourceID(*value)
+	}
+	return mr
+}
+
+// set field if value's pointer is not nil.
+func (mr *MessageRecordsUpdate) SetNotNilSubSourceID(value *int) *MessageRecordsUpdate {
+	if value != nil {
+		return mr.SetSubSourceID(*value)
+	}
+	return mr
+}
+
+// set field if value's pointer is not nil.
+func (mr *MessageRecordsUpdateOne) SetNotNilSubSourceID(value *int) *MessageRecordsUpdateOne {
+	if value != nil {
+		return mr.SetSubSourceID(*value)
+	}
+	return mr
+}
+
+// set field if value's pointer is not nil.
+func (mr *MessageRecordsCreate) SetNotNilSubSourceID(value *int) *MessageRecordsCreate {
+	if value != nil {
+		return mr.SetSubSourceID(*value)
+	}
+	return mr
+}
+
+// set field if value's pointer is not nil.
 func (s *ServerUpdate) SetNotNilUpdatedAt(value *time.Time) *ServerUpdate {
 	if value != nil {
 		return s.SetUpdatedAt(*value)
@@ -989,6 +1328,726 @@ func (s *ServerCreate) SetNotNilAdminPort(value *string) *ServerCreate {
 }
 
 // set field if value's pointer is not nil.
+func (sn *SopNodeUpdate) SetNotNilUpdatedAt(value *time.Time) *SopNodeUpdate {
+	if value != nil {
+		return sn.SetUpdatedAt(*value)
+	}
+	return sn
+}
+
+// set field if value's pointer is not nil.
+func (sn *SopNodeUpdateOne) SetNotNilUpdatedAt(value *time.Time) *SopNodeUpdateOne {
+	if value != nil {
+		return sn.SetUpdatedAt(*value)
+	}
+	return sn
+}
+
+// set field if value's pointer is not nil.
+func (sn *SopNodeCreate) SetNotNilUpdatedAt(value *time.Time) *SopNodeCreate {
+	if value != nil {
+		return sn.SetUpdatedAt(*value)
+	}
+	return sn
+}
+
+// set field if value's pointer is not nil.
+func (sn *SopNodeUpdate) SetNotNilStatus(value *uint8) *SopNodeUpdate {
+	if value != nil {
+		return sn.SetStatus(*value)
+	}
+	return sn
+}
+
+// set field if value's pointer is not nil.
+func (sn *SopNodeUpdateOne) SetNotNilStatus(value *uint8) *SopNodeUpdateOne {
+	if value != nil {
+		return sn.SetStatus(*value)
+	}
+	return sn
+}
+
+// set field if value's pointer is not nil.
+func (sn *SopNodeCreate) SetNotNilStatus(value *uint8) *SopNodeCreate {
+	if value != nil {
+		return sn.SetStatus(*value)
+	}
+	return sn
+}
+
+// set field if value's pointer is not nil.
+func (sn *SopNodeUpdate) SetNotNilDeletedAt(value *time.Time) *SopNodeUpdate {
+	if value != nil {
+		return sn.SetDeletedAt(*value)
+	}
+	return sn
+}
+
+// set field if value's pointer is not nil.
+func (sn *SopNodeUpdateOne) SetNotNilDeletedAt(value *time.Time) *SopNodeUpdateOne {
+	if value != nil {
+		return sn.SetDeletedAt(*value)
+	}
+	return sn
+}
+
+// set field if value's pointer is not nil.
+func (sn *SopNodeCreate) SetNotNilDeletedAt(value *time.Time) *SopNodeCreate {
+	if value != nil {
+		return sn.SetDeletedAt(*value)
+	}
+	return sn
+}
+
+// set field if value's pointer is not nil.
+func (sn *SopNodeUpdate) SetNotNilStageID(value *uint64) *SopNodeUpdate {
+	if value != nil {
+		return sn.SetStageID(*value)
+	}
+	return sn
+}
+
+// set field if value's pointer is not nil.
+func (sn *SopNodeUpdateOne) SetNotNilStageID(value *uint64) *SopNodeUpdateOne {
+	if value != nil {
+		return sn.SetStageID(*value)
+	}
+	return sn
+}
+
+// set field if value's pointer is not nil.
+func (sn *SopNodeCreate) SetNotNilStageID(value *uint64) *SopNodeCreate {
+	if value != nil {
+		return sn.SetStageID(*value)
+	}
+	return sn
+}
+
+// set field if value's pointer is not nil.
+func (sn *SopNodeUpdate) SetNotNilParentID(value *int) *SopNodeUpdate {
+	if value != nil {
+		return sn.SetParentID(*value)
+	}
+	return sn
+}
+
+// set field if value's pointer is not nil.
+func (sn *SopNodeUpdateOne) SetNotNilParentID(value *int) *SopNodeUpdateOne {
+	if value != nil {
+		return sn.SetParentID(*value)
+	}
+	return sn
+}
+
+// set field if value's pointer is not nil.
+func (sn *SopNodeCreate) SetNotNilParentID(value *int) *SopNodeCreate {
+	if value != nil {
+		return sn.SetParentID(*value)
+	}
+	return sn
+}
+
+// set field if value's pointer is not nil.
+func (sn *SopNodeUpdate) SetNotNilName(value *string) *SopNodeUpdate {
+	if value != nil {
+		return sn.SetName(*value)
+	}
+	return sn
+}
+
+// set field if value's pointer is not nil.
+func (sn *SopNodeUpdateOne) SetNotNilName(value *string) *SopNodeUpdateOne {
+	if value != nil {
+		return sn.SetName(*value)
+	}
+	return sn
+}
+
+// set field if value's pointer is not nil.
+func (sn *SopNodeCreate) SetNotNilName(value *string) *SopNodeCreate {
+	if value != nil {
+		return sn.SetName(*value)
+	}
+	return sn
+}
+
+// set field if value's pointer is not nil.
+func (sn *SopNodeUpdate) SetNotNilConditionType(value *int) *SopNodeUpdate {
+	if value != nil {
+		return sn.SetConditionType(*value)
+	}
+	return sn
+}
+
+// set field if value's pointer is not nil.
+func (sn *SopNodeUpdateOne) SetNotNilConditionType(value *int) *SopNodeUpdateOne {
+	if value != nil {
+		return sn.SetConditionType(*value)
+	}
+	return sn
+}
+
+// set field if value's pointer is not nil.
+func (sn *SopNodeCreate) SetNotNilConditionType(value *int) *SopNodeCreate {
+	if value != nil {
+		return sn.SetConditionType(*value)
+	}
+	return sn
+}
+
+// set field if value's pointer is not nil.
+func (sn *SopNodeUpdate) SetNotNilConditionList(value []custom_types.Condition) *SopNodeUpdate {
+	if value != nil {
+		return sn.SetConditionList(value)
+	}
+	return sn
+}
+
+// set field if value's pointer is not nil.
+func (sn *SopNodeUpdateOne) SetNotNilConditionList(value []custom_types.Condition) *SopNodeUpdateOne {
+	if value != nil {
+		return sn.SetConditionList(value)
+	}
+	return sn
+}
+
+// set field if value's pointer is not nil.
+func (sn *SopNodeCreate) SetNotNilConditionList(value []custom_types.Condition) *SopNodeCreate {
+	if value != nil {
+		return sn.SetConditionList(value)
+	}
+	return sn
+}
+
+// set field if value's pointer is not nil.
+func (sn *SopNodeUpdate) SetNotNilActionMessage(value []custom_types.Action) *SopNodeUpdate {
+	if value != nil {
+		return sn.SetActionMessage(value)
+	}
+	return sn
+}
+
+// set field if value's pointer is not nil.
+func (sn *SopNodeUpdateOne) SetNotNilActionMessage(value []custom_types.Action) *SopNodeUpdateOne {
+	if value != nil {
+		return sn.SetActionMessage(value)
+	}
+	return sn
+}
+
+// set field if value's pointer is not nil.
+func (sn *SopNodeCreate) SetNotNilActionMessage(value []custom_types.Action) *SopNodeCreate {
+	if value != nil {
+		return sn.SetActionMessage(value)
+	}
+	return sn
+}
+
+// set field if value's pointer is not nil.
+func (sn *SopNodeUpdate) SetNotNilActionLabel(value []int) *SopNodeUpdate {
+	if value != nil {
+		return sn.SetActionLabel(value)
+	}
+	return sn
+}
+
+// set field if value's pointer is not nil.
+func (sn *SopNodeUpdateOne) SetNotNilActionLabel(value []int) *SopNodeUpdateOne {
+	if value != nil {
+		return sn.SetActionLabel(value)
+	}
+	return sn
+}
+
+// set field if value's pointer is not nil.
+func (sn *SopNodeCreate) SetNotNilActionLabel(value []int) *SopNodeCreate {
+	if value != nil {
+		return sn.SetActionLabel(value)
+	}
+	return sn
+}
+
+// set field if value's pointer is not nil.
+func (ss *SopStageUpdate) SetNotNilUpdatedAt(value *time.Time) *SopStageUpdate {
+	if value != nil {
+		return ss.SetUpdatedAt(*value)
+	}
+	return ss
+}
+
+// set field if value's pointer is not nil.
+func (ss *SopStageUpdateOne) SetNotNilUpdatedAt(value *time.Time) *SopStageUpdateOne {
+	if value != nil {
+		return ss.SetUpdatedAt(*value)
+	}
+	return ss
+}
+
+// set field if value's pointer is not nil.
+func (ss *SopStageCreate) SetNotNilUpdatedAt(value *time.Time) *SopStageCreate {
+	if value != nil {
+		return ss.SetUpdatedAt(*value)
+	}
+	return ss
+}
+
+// set field if value's pointer is not nil.
+func (ss *SopStageUpdate) SetNotNilStatus(value *uint8) *SopStageUpdate {
+	if value != nil {
+		return ss.SetStatus(*value)
+	}
+	return ss
+}
+
+// set field if value's pointer is not nil.
+func (ss *SopStageUpdateOne) SetNotNilStatus(value *uint8) *SopStageUpdateOne {
+	if value != nil {
+		return ss.SetStatus(*value)
+	}
+	return ss
+}
+
+// set field if value's pointer is not nil.
+func (ss *SopStageCreate) SetNotNilStatus(value *uint8) *SopStageCreate {
+	if value != nil {
+		return ss.SetStatus(*value)
+	}
+	return ss
+}
+
+// set field if value's pointer is not nil.
+func (ss *SopStageUpdate) SetNotNilDeletedAt(value *time.Time) *SopStageUpdate {
+	if value != nil {
+		return ss.SetDeletedAt(*value)
+	}
+	return ss
+}
+
+// set field if value's pointer is not nil.
+func (ss *SopStageUpdateOne) SetNotNilDeletedAt(value *time.Time) *SopStageUpdateOne {
+	if value != nil {
+		return ss.SetDeletedAt(*value)
+	}
+	return ss
+}
+
+// set field if value's pointer is not nil.
+func (ss *SopStageCreate) SetNotNilDeletedAt(value *time.Time) *SopStageCreate {
+	if value != nil {
+		return ss.SetDeletedAt(*value)
+	}
+	return ss
+}
+
+// set field if value's pointer is not nil.
+func (ss *SopStageUpdate) SetNotNilTaskID(value *uint64) *SopStageUpdate {
+	if value != nil {
+		return ss.SetTaskID(*value)
+	}
+	return ss
+}
+
+// set field if value's pointer is not nil.
+func (ss *SopStageUpdateOne) SetNotNilTaskID(value *uint64) *SopStageUpdateOne {
+	if value != nil {
+		return ss.SetTaskID(*value)
+	}
+	return ss
+}
+
+// set field if value's pointer is not nil.
+func (ss *SopStageCreate) SetNotNilTaskID(value *uint64) *SopStageCreate {
+	if value != nil {
+		return ss.SetTaskID(*value)
+	}
+	return ss
+}
+
+// set field if value's pointer is not nil.
+func (ss *SopStageUpdate) SetNotNilName(value *string) *SopStageUpdate {
+	if value != nil {
+		return ss.SetName(*value)
+	}
+	return ss
+}
+
+// set field if value's pointer is not nil.
+func (ss *SopStageUpdateOne) SetNotNilName(value *string) *SopStageUpdateOne {
+	if value != nil {
+		return ss.SetName(*value)
+	}
+	return ss
+}
+
+// set field if value's pointer is not nil.
+func (ss *SopStageCreate) SetNotNilName(value *string) *SopStageCreate {
+	if value != nil {
+		return ss.SetName(*value)
+	}
+	return ss
+}
+
+// set field if value's pointer is not nil.
+func (ss *SopStageUpdate) SetNotNilConditionType(value *int) *SopStageUpdate {
+	if value != nil {
+		return ss.SetConditionType(*value)
+	}
+	return ss
+}
+
+// set field if value's pointer is not nil.
+func (ss *SopStageUpdateOne) SetNotNilConditionType(value *int) *SopStageUpdateOne {
+	if value != nil {
+		return ss.SetConditionType(*value)
+	}
+	return ss
+}
+
+// set field if value's pointer is not nil.
+func (ss *SopStageCreate) SetNotNilConditionType(value *int) *SopStageCreate {
+	if value != nil {
+		return ss.SetConditionType(*value)
+	}
+	return ss
+}
+
+// set field if value's pointer is not nil.
+func (ss *SopStageUpdate) SetNotNilConditionOperator(value *int) *SopStageUpdate {
+	if value != nil {
+		return ss.SetConditionOperator(*value)
+	}
+	return ss
+}
+
+// set field if value's pointer is not nil.
+func (ss *SopStageUpdateOne) SetNotNilConditionOperator(value *int) *SopStageUpdateOne {
+	if value != nil {
+		return ss.SetConditionOperator(*value)
+	}
+	return ss
+}
+
+// set field if value's pointer is not nil.
+func (ss *SopStageCreate) SetNotNilConditionOperator(value *int) *SopStageCreate {
+	if value != nil {
+		return ss.SetConditionOperator(*value)
+	}
+	return ss
+}
+
+// set field if value's pointer is not nil.
+func (ss *SopStageUpdate) SetNotNilConditionList(value []custom_types.Condition) *SopStageUpdate {
+	if value != nil {
+		return ss.SetConditionList(value)
+	}
+	return ss
+}
+
+// set field if value's pointer is not nil.
+func (ss *SopStageUpdateOne) SetNotNilConditionList(value []custom_types.Condition) *SopStageUpdateOne {
+	if value != nil {
+		return ss.SetConditionList(value)
+	}
+	return ss
+}
+
+// set field if value's pointer is not nil.
+func (ss *SopStageCreate) SetNotNilConditionList(value []custom_types.Condition) *SopStageCreate {
+	if value != nil {
+		return ss.SetConditionList(value)
+	}
+	return ss
+}
+
+// set field if value's pointer is not nil.
+func (ss *SopStageUpdate) SetNotNilActionMessage(value []custom_types.Action) *SopStageUpdate {
+	if value != nil {
+		return ss.SetActionMessage(value)
+	}
+	return ss
+}
+
+// set field if value's pointer is not nil.
+func (ss *SopStageUpdateOne) SetNotNilActionMessage(value []custom_types.Action) *SopStageUpdateOne {
+	if value != nil {
+		return ss.SetActionMessage(value)
+	}
+	return ss
+}
+
+// set field if value's pointer is not nil.
+func (ss *SopStageCreate) SetNotNilActionMessage(value []custom_types.Action) *SopStageCreate {
+	if value != nil {
+		return ss.SetActionMessage(value)
+	}
+	return ss
+}
+
+// set field if value's pointer is not nil.
+func (ss *SopStageUpdate) SetNotNilActionLabel(value []int) *SopStageUpdate {
+	if value != nil {
+		return ss.SetActionLabel(value)
+	}
+	return ss
+}
+
+// set field if value's pointer is not nil.
+func (ss *SopStageUpdateOne) SetNotNilActionLabel(value []int) *SopStageUpdateOne {
+	if value != nil {
+		return ss.SetActionLabel(value)
+	}
+	return ss
+}
+
+// set field if value's pointer is not nil.
+func (ss *SopStageCreate) SetNotNilActionLabel(value []int) *SopStageCreate {
+	if value != nil {
+		return ss.SetActionLabel(value)
+	}
+	return ss
+}
+
+// set field if value's pointer is not nil.
+func (ss *SopStageUpdate) SetNotNilIndexSort(value *int) *SopStageUpdate {
+	if value != nil {
+		return ss.SetIndexSort(*value)
+	}
+	return ss
+}
+
+// set field if value's pointer is not nil.
+func (ss *SopStageUpdateOne) SetNotNilIndexSort(value *int) *SopStageUpdateOne {
+	if value != nil {
+		return ss.SetIndexSort(*value)
+	}
+	return ss
+}
+
+// set field if value's pointer is not nil.
+func (ss *SopStageCreate) SetNotNilIndexSort(value *int) *SopStageCreate {
+	if value != nil {
+		return ss.SetIndexSort(*value)
+	}
+	return ss
+}
+
+// set field if value's pointer is not nil.
+func (st *SopTaskUpdate) SetNotNilUpdatedAt(value *time.Time) *SopTaskUpdate {
+	if value != nil {
+		return st.SetUpdatedAt(*value)
+	}
+	return st
+}
+
+// set field if value's pointer is not nil.
+func (st *SopTaskUpdateOne) SetNotNilUpdatedAt(value *time.Time) *SopTaskUpdateOne {
+	if value != nil {
+		return st.SetUpdatedAt(*value)
+	}
+	return st
+}
+
+// set field if value's pointer is not nil.
+func (st *SopTaskCreate) SetNotNilUpdatedAt(value *time.Time) *SopTaskCreate {
+	if value != nil {
+		return st.SetUpdatedAt(*value)
+	}
+	return st
+}
+
+// set field if value's pointer is not nil.
+func (st *SopTaskUpdate) SetNotNilStatus(value *uint8) *SopTaskUpdate {
+	if value != nil {
+		return st.SetStatus(*value)
+	}
+	return st
+}
+
+// set field if value's pointer is not nil.
+func (st *SopTaskUpdateOne) SetNotNilStatus(value *uint8) *SopTaskUpdateOne {
+	if value != nil {
+		return st.SetStatus(*value)
+	}
+	return st
+}
+
+// set field if value's pointer is not nil.
+func (st *SopTaskCreate) SetNotNilStatus(value *uint8) *SopTaskCreate {
+	if value != nil {
+		return st.SetStatus(*value)
+	}
+	return st
+}
+
+// set field if value's pointer is not nil.
+func (st *SopTaskUpdate) SetNotNilDeletedAt(value *time.Time) *SopTaskUpdate {
+	if value != nil {
+		return st.SetDeletedAt(*value)
+	}
+	return st
+}
+
+// set field if value's pointer is not nil.
+func (st *SopTaskUpdateOne) SetNotNilDeletedAt(value *time.Time) *SopTaskUpdateOne {
+	if value != nil {
+		return st.SetDeletedAt(*value)
+	}
+	return st
+}
+
+// set field if value's pointer is not nil.
+func (st *SopTaskCreate) SetNotNilDeletedAt(value *time.Time) *SopTaskCreate {
+	if value != nil {
+		return st.SetDeletedAt(*value)
+	}
+	return st
+}
+
+// set field if value's pointer is not nil.
+func (st *SopTaskUpdate) SetNotNilName(value *string) *SopTaskUpdate {
+	if value != nil {
+		return st.SetName(*value)
+	}
+	return st
+}
+
+// set field if value's pointer is not nil.
+func (st *SopTaskUpdateOne) SetNotNilName(value *string) *SopTaskUpdateOne {
+	if value != nil {
+		return st.SetName(*value)
+	}
+	return st
+}
+
+// set field if value's pointer is not nil.
+func (st *SopTaskCreate) SetNotNilName(value *string) *SopTaskCreate {
+	if value != nil {
+		return st.SetName(*value)
+	}
+	return st
+}
+
+// set field if value's pointer is not nil.
+func (st *SopTaskUpdate) SetNotNilBotWxidList(value []string) *SopTaskUpdate {
+	if value != nil {
+		return st.SetBotWxidList(value)
+	}
+	return st
+}
+
+// set field if value's pointer is not nil.
+func (st *SopTaskUpdateOne) SetNotNilBotWxidList(value []string) *SopTaskUpdateOne {
+	if value != nil {
+		return st.SetBotWxidList(value)
+	}
+	return st
+}
+
+// set field if value's pointer is not nil.
+func (st *SopTaskCreate) SetNotNilBotWxidList(value []string) *SopTaskCreate {
+	if value != nil {
+		return st.SetBotWxidList(value)
+	}
+	return st
+}
+
+// set field if value's pointer is not nil.
+func (st *SopTaskUpdate) SetNotNilType(value *int) *SopTaskUpdate {
+	if value != nil {
+		return st.SetType(*value)
+	}
+	return st
+}
+
+// set field if value's pointer is not nil.
+func (st *SopTaskUpdateOne) SetNotNilType(value *int) *SopTaskUpdateOne {
+	if value != nil {
+		return st.SetType(*value)
+	}
+	return st
+}
+
+// set field if value's pointer is not nil.
+func (st *SopTaskCreate) SetNotNilType(value *int) *SopTaskCreate {
+	if value != nil {
+		return st.SetType(*value)
+	}
+	return st
+}
+
+// set field if value's pointer is not nil.
+func (st *SopTaskUpdate) SetNotNilPlanStartTime(value *time.Time) *SopTaskUpdate {
+	if value != nil {
+		return st.SetPlanStartTime(*value)
+	}
+	return st
+}
+
+// set field if value's pointer is not nil.
+func (st *SopTaskUpdateOne) SetNotNilPlanStartTime(value *time.Time) *SopTaskUpdateOne {
+	if value != nil {
+		return st.SetPlanStartTime(*value)
+	}
+	return st
+}
+
+// set field if value's pointer is not nil.
+func (st *SopTaskCreate) SetNotNilPlanStartTime(value *time.Time) *SopTaskCreate {
+	if value != nil {
+		return st.SetPlanStartTime(*value)
+	}
+	return st
+}
+
+// set field if value's pointer is not nil.
+func (st *SopTaskUpdate) SetNotNilPlanEndTime(value *time.Time) *SopTaskUpdate {
+	if value != nil {
+		return st.SetPlanEndTime(*value)
+	}
+	return st
+}
+
+// set field if value's pointer is not nil.
+func (st *SopTaskUpdateOne) SetNotNilPlanEndTime(value *time.Time) *SopTaskUpdateOne {
+	if value != nil {
+		return st.SetPlanEndTime(*value)
+	}
+	return st
+}
+
+// set field if value's pointer is not nil.
+func (st *SopTaskCreate) SetNotNilPlanEndTime(value *time.Time) *SopTaskCreate {
+	if value != nil {
+		return st.SetPlanEndTime(*value)
+	}
+	return st
+}
+
+// set field if value's pointer is not nil.
+func (st *SopTaskUpdate) SetNotNilCreatorID(value *string) *SopTaskUpdate {
+	if value != nil {
+		return st.SetCreatorID(*value)
+	}
+	return st
+}
+
+// set field if value's pointer is not nil.
+func (st *SopTaskUpdateOne) SetNotNilCreatorID(value *string) *SopTaskUpdateOne {
+	if value != nil {
+		return st.SetCreatorID(*value)
+	}
+	return st
+}
+
+// set field if value's pointer is not nil.
+func (st *SopTaskCreate) SetNotNilCreatorID(value *string) *SopTaskCreate {
+	if value != nil {
+		return st.SetCreatorID(*value)
+	}
+	return st
+}
+
+// set field if value's pointer is not nil.
 func (w *WxUpdate) SetNotNilUpdatedAt(value *time.Time) *WxUpdate {
 	if value != nil {
 		return w.SetUpdatedAt(*value)

+ 255 - 0
ent/sopnode.go

@@ -0,0 +1,255 @@
+// Code generated by ent, DO NOT EDIT.
+
+package ent
+
+import (
+	"encoding/json"
+	"fmt"
+	"strings"
+	"time"
+	"wechat-api/ent/custom_types"
+	"wechat-api/ent/sopnode"
+	"wechat-api/ent/sopstage"
+
+	"entgo.io/ent"
+	"entgo.io/ent/dialect/sql"
+)
+
+// SopNode is the model entity for the SopNode schema.
+type SopNode 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"`
+	// Status 1: normal 2: ban | 状态 1 正常 2 禁用
+	Status uint8 `json:"status,omitempty"`
+	// Delete Time | 删除日期
+	DeletedAt time.Time `json:"deleted_at,omitempty"`
+	// 阶段 ID
+	StageID uint64 `json:"stage_id,omitempty"`
+	// 父节点 ID
+	ParentID int `json:"parent_id,omitempty"`
+	// 节点名称
+	Name string `json:"name,omitempty"`
+	// 触发条件类型 1 客户回复后触发 2 超时后触发
+	ConditionType int `json:"condition_type,omitempty"`
+	// 触发语义列表 当为空时则代表用户回复任意内容后触发
+	ConditionList []custom_types.Condition `json:"condition_list,omitempty"`
+	// 命中后发送的消息内容
+	ActionMessage []custom_types.Action `json:"action_message,omitempty"`
+	// 命中后需要打的标签
+	ActionLabel []int `json:"action_label,omitempty"`
+	// Edges holds the relations/edges for other nodes in the graph.
+	// The values are being populated by the SopNodeQuery when eager-loading is set.
+	Edges        SopNodeEdges `json:"edges"`
+	selectValues sql.SelectValues
+}
+
+// SopNodeEdges holds the relations/edges for other nodes in the graph.
+type SopNodeEdges struct {
+	// SopStage holds the value of the sop_stage edge.
+	SopStage *SopStage `json:"sop_stage,omitempty"`
+	// loadedTypes holds the information for reporting if a
+	// type was loaded (or requested) in eager-loading or not.
+	loadedTypes [1]bool
+}
+
+// SopStageOrErr returns the SopStage value or an error if the edge
+// was not loaded in eager-loading, or loaded but was not found.
+func (e SopNodeEdges) SopStageOrErr() (*SopStage, error) {
+	if e.SopStage != nil {
+		return e.SopStage, nil
+	} else if e.loadedTypes[0] {
+		return nil, &NotFoundError{label: sopstage.Label}
+	}
+	return nil, &NotLoadedError{edge: "sop_stage"}
+}
+
+// scanValues returns the types for scanning values from sql.Rows.
+func (*SopNode) scanValues(columns []string) ([]any, error) {
+	values := make([]any, len(columns))
+	for i := range columns {
+		switch columns[i] {
+		case sopnode.FieldConditionList, sopnode.FieldActionMessage, sopnode.FieldActionLabel:
+			values[i] = new([]byte)
+		case sopnode.FieldID, sopnode.FieldStatus, sopnode.FieldStageID, sopnode.FieldParentID, sopnode.FieldConditionType:
+			values[i] = new(sql.NullInt64)
+		case sopnode.FieldName:
+			values[i] = new(sql.NullString)
+		case sopnode.FieldCreatedAt, sopnode.FieldUpdatedAt, sopnode.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 SopNode fields.
+func (sn *SopNode) 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 sopnode.FieldID:
+			value, ok := values[i].(*sql.NullInt64)
+			if !ok {
+				return fmt.Errorf("unexpected type %T for field id", value)
+			}
+			sn.ID = uint64(value.Int64)
+		case sopnode.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 {
+				sn.CreatedAt = value.Time
+			}
+		case sopnode.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 {
+				sn.UpdatedAt = value.Time
+			}
+		case sopnode.FieldStatus:
+			if value, ok := values[i].(*sql.NullInt64); !ok {
+				return fmt.Errorf("unexpected type %T for field status", values[i])
+			} else if value.Valid {
+				sn.Status = uint8(value.Int64)
+			}
+		case sopnode.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 {
+				sn.DeletedAt = value.Time
+			}
+		case sopnode.FieldStageID:
+			if value, ok := values[i].(*sql.NullInt64); !ok {
+				return fmt.Errorf("unexpected type %T for field stage_id", values[i])
+			} else if value.Valid {
+				sn.StageID = uint64(value.Int64)
+			}
+		case sopnode.FieldParentID:
+			if value, ok := values[i].(*sql.NullInt64); !ok {
+				return fmt.Errorf("unexpected type %T for field parent_id", values[i])
+			} else if value.Valid {
+				sn.ParentID = int(value.Int64)
+			}
+		case sopnode.FieldName:
+			if value, ok := values[i].(*sql.NullString); !ok {
+				return fmt.Errorf("unexpected type %T for field name", values[i])
+			} else if value.Valid {
+				sn.Name = value.String
+			}
+		case sopnode.FieldConditionType:
+			if value, ok := values[i].(*sql.NullInt64); !ok {
+				return fmt.Errorf("unexpected type %T for field condition_type", values[i])
+			} else if value.Valid {
+				sn.ConditionType = int(value.Int64)
+			}
+		case sopnode.FieldConditionList:
+			if value, ok := values[i].(*[]byte); !ok {
+				return fmt.Errorf("unexpected type %T for field condition_list", values[i])
+			} else if value != nil && len(*value) > 0 {
+				if err := json.Unmarshal(*value, &sn.ConditionList); err != nil {
+					return fmt.Errorf("unmarshal field condition_list: %w", err)
+				}
+			}
+		case sopnode.FieldActionMessage:
+			if value, ok := values[i].(*[]byte); !ok {
+				return fmt.Errorf("unexpected type %T for field action_message", values[i])
+			} else if value != nil && len(*value) > 0 {
+				if err := json.Unmarshal(*value, &sn.ActionMessage); err != nil {
+					return fmt.Errorf("unmarshal field action_message: %w", err)
+				}
+			}
+		case sopnode.FieldActionLabel:
+			if value, ok := values[i].(*[]byte); !ok {
+				return fmt.Errorf("unexpected type %T for field action_label", values[i])
+			} else if value != nil && len(*value) > 0 {
+				if err := json.Unmarshal(*value, &sn.ActionLabel); err != nil {
+					return fmt.Errorf("unmarshal field action_label: %w", err)
+				}
+			}
+		default:
+			sn.selectValues.Set(columns[i], values[i])
+		}
+	}
+	return nil
+}
+
+// Value returns the ent.Value that was dynamically selected and assigned to the SopNode.
+// This includes values selected through modifiers, order, etc.
+func (sn *SopNode) Value(name string) (ent.Value, error) {
+	return sn.selectValues.Get(name)
+}
+
+// QuerySopStage queries the "sop_stage" edge of the SopNode entity.
+func (sn *SopNode) QuerySopStage() *SopStageQuery {
+	return NewSopNodeClient(sn.config).QuerySopStage(sn)
+}
+
+// Update returns a builder for updating this SopNode.
+// Note that you need to call SopNode.Unwrap() before calling this method if this SopNode
+// was returned from a transaction, and the transaction was committed or rolled back.
+func (sn *SopNode) Update() *SopNodeUpdateOne {
+	return NewSopNodeClient(sn.config).UpdateOne(sn)
+}
+
+// Unwrap unwraps the SopNode 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 (sn *SopNode) Unwrap() *SopNode {
+	_tx, ok := sn.config.driver.(*txDriver)
+	if !ok {
+		panic("ent: SopNode is not a transactional entity")
+	}
+	sn.config.driver = _tx.drv
+	return sn
+}
+
+// String implements the fmt.Stringer.
+func (sn *SopNode) String() string {
+	var builder strings.Builder
+	builder.WriteString("SopNode(")
+	builder.WriteString(fmt.Sprintf("id=%v, ", sn.ID))
+	builder.WriteString("created_at=")
+	builder.WriteString(sn.CreatedAt.Format(time.ANSIC))
+	builder.WriteString(", ")
+	builder.WriteString("updated_at=")
+	builder.WriteString(sn.UpdatedAt.Format(time.ANSIC))
+	builder.WriteString(", ")
+	builder.WriteString("status=")
+	builder.WriteString(fmt.Sprintf("%v", sn.Status))
+	builder.WriteString(", ")
+	builder.WriteString("deleted_at=")
+	builder.WriteString(sn.DeletedAt.Format(time.ANSIC))
+	builder.WriteString(", ")
+	builder.WriteString("stage_id=")
+	builder.WriteString(fmt.Sprintf("%v", sn.StageID))
+	builder.WriteString(", ")
+	builder.WriteString("parent_id=")
+	builder.WriteString(fmt.Sprintf("%v", sn.ParentID))
+	builder.WriteString(", ")
+	builder.WriteString("name=")
+	builder.WriteString(sn.Name)
+	builder.WriteString(", ")
+	builder.WriteString("condition_type=")
+	builder.WriteString(fmt.Sprintf("%v", sn.ConditionType))
+	builder.WriteString(", ")
+	builder.WriteString("condition_list=")
+	builder.WriteString(fmt.Sprintf("%v", sn.ConditionList))
+	builder.WriteString(", ")
+	builder.WriteString("action_message=")
+	builder.WriteString(fmt.Sprintf("%v", sn.ActionMessage))
+	builder.WriteString(", ")
+	builder.WriteString("action_label=")
+	builder.WriteString(fmt.Sprintf("%v", sn.ActionLabel))
+	builder.WriteByte(')')
+	return builder.String()
+}
+
+// SopNodes is a parsable slice of SopNode.
+type SopNodes []*SopNode

+ 161 - 0
ent/sopnode/sopnode.go

@@ -0,0 +1,161 @@
+// Code generated by ent, DO NOT EDIT.
+
+package sopnode
+
+import (
+	"time"
+
+	"entgo.io/ent"
+	"entgo.io/ent/dialect/sql"
+	"entgo.io/ent/dialect/sql/sqlgraph"
+)
+
+const (
+	// Label holds the string label denoting the sopnode type in the database.
+	Label = "sop_node"
+	// 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"
+	// FieldStatus holds the string denoting the status field in the database.
+	FieldStatus = "status"
+	// FieldDeletedAt holds the string denoting the deleted_at field in the database.
+	FieldDeletedAt = "deleted_at"
+	// FieldStageID holds the string denoting the stage_id field in the database.
+	FieldStageID = "stage_id"
+	// FieldParentID holds the string denoting the parent_id field in the database.
+	FieldParentID = "parent_id"
+	// FieldName holds the string denoting the name field in the database.
+	FieldName = "name"
+	// FieldConditionType holds the string denoting the condition_type field in the database.
+	FieldConditionType = "condition_type"
+	// FieldConditionList holds the string denoting the condition_list field in the database.
+	FieldConditionList = "condition_list"
+	// FieldActionMessage holds the string denoting the action_message field in the database.
+	FieldActionMessage = "action_message"
+	// FieldActionLabel holds the string denoting the action_label field in the database.
+	FieldActionLabel = "action_label"
+	// EdgeSopStage holds the string denoting the sop_stage edge name in mutations.
+	EdgeSopStage = "sop_stage"
+	// Table holds the table name of the sopnode in the database.
+	Table = "sop_node"
+	// SopStageTable is the table that holds the sop_stage relation/edge.
+	SopStageTable = "sop_node"
+	// SopStageInverseTable is the table name for the SopStage entity.
+	// It exists in this package in order to avoid circular dependency with the "sopstage" package.
+	SopStageInverseTable = "sop_stage"
+	// SopStageColumn is the table column denoting the sop_stage relation/edge.
+	SopStageColumn = "stage_id"
+)
+
+// Columns holds all SQL columns for sopnode fields.
+var Columns = []string{
+	FieldID,
+	FieldCreatedAt,
+	FieldUpdatedAt,
+	FieldStatus,
+	FieldDeletedAt,
+	FieldStageID,
+	FieldParentID,
+	FieldName,
+	FieldConditionType,
+	FieldConditionList,
+	FieldActionMessage,
+	FieldActionLabel,
+}
+
+// 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
+	// DefaultStatus holds the default value on creation for the "status" field.
+	DefaultStatus uint8
+	// DefaultName holds the default value on creation for the "name" field.
+	DefaultName string
+	// DefaultConditionType holds the default value on creation for the "condition_type" field.
+	DefaultConditionType int
+)
+
+// OrderOption defines the ordering options for the SopNode 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()
+}
+
+// ByStatus orders the results by the status field.
+func ByStatus(opts ...sql.OrderTermOption) OrderOption {
+	return sql.OrderByField(FieldStatus, opts...).ToFunc()
+}
+
+// ByDeletedAt orders the results by the deleted_at field.
+func ByDeletedAt(opts ...sql.OrderTermOption) OrderOption {
+	return sql.OrderByField(FieldDeletedAt, opts...).ToFunc()
+}
+
+// ByStageID orders the results by the stage_id field.
+func ByStageID(opts ...sql.OrderTermOption) OrderOption {
+	return sql.OrderByField(FieldStageID, opts...).ToFunc()
+}
+
+// ByParentID orders the results by the parent_id field.
+func ByParentID(opts ...sql.OrderTermOption) OrderOption {
+	return sql.OrderByField(FieldParentID, opts...).ToFunc()
+}
+
+// ByName orders the results by the name field.
+func ByName(opts ...sql.OrderTermOption) OrderOption {
+	return sql.OrderByField(FieldName, opts...).ToFunc()
+}
+
+// ByConditionType orders the results by the condition_type field.
+func ByConditionType(opts ...sql.OrderTermOption) OrderOption {
+	return sql.OrderByField(FieldConditionType, opts...).ToFunc()
+}
+
+// BySopStageField orders the results by sop_stage field.
+func BySopStageField(field string, opts ...sql.OrderTermOption) OrderOption {
+	return func(s *sql.Selector) {
+		sqlgraph.OrderByNeighborTerms(s, newSopStageStep(), sql.OrderByField(field, opts...))
+	}
+}
+func newSopStageStep() *sqlgraph.Step {
+	return sqlgraph.NewStep(
+		sqlgraph.From(Table, FieldID),
+		sqlgraph.To(SopStageInverseTable, FieldID),
+		sqlgraph.Edge(sqlgraph.M2O, true, SopStageTable, SopStageColumn),
+	)
+}

+ 479 - 0
ent/sopnode/where.go

@@ -0,0 +1,479 @@
+// Code generated by ent, DO NOT EDIT.
+
+package sopnode
+
+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.SopNode {
+	return predicate.SopNode(sql.FieldEQ(FieldID, id))
+}
+
+// IDEQ applies the EQ predicate on the ID field.
+func IDEQ(id uint64) predicate.SopNode {
+	return predicate.SopNode(sql.FieldEQ(FieldID, id))
+}
+
+// IDNEQ applies the NEQ predicate on the ID field.
+func IDNEQ(id uint64) predicate.SopNode {
+	return predicate.SopNode(sql.FieldNEQ(FieldID, id))
+}
+
+// IDIn applies the In predicate on the ID field.
+func IDIn(ids ...uint64) predicate.SopNode {
+	return predicate.SopNode(sql.FieldIn(FieldID, ids...))
+}
+
+// IDNotIn applies the NotIn predicate on the ID field.
+func IDNotIn(ids ...uint64) predicate.SopNode {
+	return predicate.SopNode(sql.FieldNotIn(FieldID, ids...))
+}
+
+// IDGT applies the GT predicate on the ID field.
+func IDGT(id uint64) predicate.SopNode {
+	return predicate.SopNode(sql.FieldGT(FieldID, id))
+}
+
+// IDGTE applies the GTE predicate on the ID field.
+func IDGTE(id uint64) predicate.SopNode {
+	return predicate.SopNode(sql.FieldGTE(FieldID, id))
+}
+
+// IDLT applies the LT predicate on the ID field.
+func IDLT(id uint64) predicate.SopNode {
+	return predicate.SopNode(sql.FieldLT(FieldID, id))
+}
+
+// IDLTE applies the LTE predicate on the ID field.
+func IDLTE(id uint64) predicate.SopNode {
+	return predicate.SopNode(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.SopNode {
+	return predicate.SopNode(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.SopNode {
+	return predicate.SopNode(sql.FieldEQ(FieldUpdatedAt, v))
+}
+
+// Status applies equality check predicate on the "status" field. It's identical to StatusEQ.
+func Status(v uint8) predicate.SopNode {
+	return predicate.SopNode(sql.FieldEQ(FieldStatus, v))
+}
+
+// DeletedAt applies equality check predicate on the "deleted_at" field. It's identical to DeletedAtEQ.
+func DeletedAt(v time.Time) predicate.SopNode {
+	return predicate.SopNode(sql.FieldEQ(FieldDeletedAt, v))
+}
+
+// StageID applies equality check predicate on the "stage_id" field. It's identical to StageIDEQ.
+func StageID(v uint64) predicate.SopNode {
+	return predicate.SopNode(sql.FieldEQ(FieldStageID, v))
+}
+
+// ParentID applies equality check predicate on the "parent_id" field. It's identical to ParentIDEQ.
+func ParentID(v int) predicate.SopNode {
+	return predicate.SopNode(sql.FieldEQ(FieldParentID, v))
+}
+
+// Name applies equality check predicate on the "name" field. It's identical to NameEQ.
+func Name(v string) predicate.SopNode {
+	return predicate.SopNode(sql.FieldEQ(FieldName, v))
+}
+
+// ConditionType applies equality check predicate on the "condition_type" field. It's identical to ConditionTypeEQ.
+func ConditionType(v int) predicate.SopNode {
+	return predicate.SopNode(sql.FieldEQ(FieldConditionType, v))
+}
+
+// CreatedAtEQ applies the EQ predicate on the "created_at" field.
+func CreatedAtEQ(v time.Time) predicate.SopNode {
+	return predicate.SopNode(sql.FieldEQ(FieldCreatedAt, v))
+}
+
+// CreatedAtNEQ applies the NEQ predicate on the "created_at" field.
+func CreatedAtNEQ(v time.Time) predicate.SopNode {
+	return predicate.SopNode(sql.FieldNEQ(FieldCreatedAt, v))
+}
+
+// CreatedAtIn applies the In predicate on the "created_at" field.
+func CreatedAtIn(vs ...time.Time) predicate.SopNode {
+	return predicate.SopNode(sql.FieldIn(FieldCreatedAt, vs...))
+}
+
+// CreatedAtNotIn applies the NotIn predicate on the "created_at" field.
+func CreatedAtNotIn(vs ...time.Time) predicate.SopNode {
+	return predicate.SopNode(sql.FieldNotIn(FieldCreatedAt, vs...))
+}
+
+// CreatedAtGT applies the GT predicate on the "created_at" field.
+func CreatedAtGT(v time.Time) predicate.SopNode {
+	return predicate.SopNode(sql.FieldGT(FieldCreatedAt, v))
+}
+
+// CreatedAtGTE applies the GTE predicate on the "created_at" field.
+func CreatedAtGTE(v time.Time) predicate.SopNode {
+	return predicate.SopNode(sql.FieldGTE(FieldCreatedAt, v))
+}
+
+// CreatedAtLT applies the LT predicate on the "created_at" field.
+func CreatedAtLT(v time.Time) predicate.SopNode {
+	return predicate.SopNode(sql.FieldLT(FieldCreatedAt, v))
+}
+
+// CreatedAtLTE applies the LTE predicate on the "created_at" field.
+func CreatedAtLTE(v time.Time) predicate.SopNode {
+	return predicate.SopNode(sql.FieldLTE(FieldCreatedAt, v))
+}
+
+// UpdatedAtEQ applies the EQ predicate on the "updated_at" field.
+func UpdatedAtEQ(v time.Time) predicate.SopNode {
+	return predicate.SopNode(sql.FieldEQ(FieldUpdatedAt, v))
+}
+
+// UpdatedAtNEQ applies the NEQ predicate on the "updated_at" field.
+func UpdatedAtNEQ(v time.Time) predicate.SopNode {
+	return predicate.SopNode(sql.FieldNEQ(FieldUpdatedAt, v))
+}
+
+// UpdatedAtIn applies the In predicate on the "updated_at" field.
+func UpdatedAtIn(vs ...time.Time) predicate.SopNode {
+	return predicate.SopNode(sql.FieldIn(FieldUpdatedAt, vs...))
+}
+
+// UpdatedAtNotIn applies the NotIn predicate on the "updated_at" field.
+func UpdatedAtNotIn(vs ...time.Time) predicate.SopNode {
+	return predicate.SopNode(sql.FieldNotIn(FieldUpdatedAt, vs...))
+}
+
+// UpdatedAtGT applies the GT predicate on the "updated_at" field.
+func UpdatedAtGT(v time.Time) predicate.SopNode {
+	return predicate.SopNode(sql.FieldGT(FieldUpdatedAt, v))
+}
+
+// UpdatedAtGTE applies the GTE predicate on the "updated_at" field.
+func UpdatedAtGTE(v time.Time) predicate.SopNode {
+	return predicate.SopNode(sql.FieldGTE(FieldUpdatedAt, v))
+}
+
+// UpdatedAtLT applies the LT predicate on the "updated_at" field.
+func UpdatedAtLT(v time.Time) predicate.SopNode {
+	return predicate.SopNode(sql.FieldLT(FieldUpdatedAt, v))
+}
+
+// UpdatedAtLTE applies the LTE predicate on the "updated_at" field.
+func UpdatedAtLTE(v time.Time) predicate.SopNode {
+	return predicate.SopNode(sql.FieldLTE(FieldUpdatedAt, v))
+}
+
+// StatusEQ applies the EQ predicate on the "status" field.
+func StatusEQ(v uint8) predicate.SopNode {
+	return predicate.SopNode(sql.FieldEQ(FieldStatus, v))
+}
+
+// StatusNEQ applies the NEQ predicate on the "status" field.
+func StatusNEQ(v uint8) predicate.SopNode {
+	return predicate.SopNode(sql.FieldNEQ(FieldStatus, v))
+}
+
+// StatusIn applies the In predicate on the "status" field.
+func StatusIn(vs ...uint8) predicate.SopNode {
+	return predicate.SopNode(sql.FieldIn(FieldStatus, vs...))
+}
+
+// StatusNotIn applies the NotIn predicate on the "status" field.
+func StatusNotIn(vs ...uint8) predicate.SopNode {
+	return predicate.SopNode(sql.FieldNotIn(FieldStatus, vs...))
+}
+
+// StatusGT applies the GT predicate on the "status" field.
+func StatusGT(v uint8) predicate.SopNode {
+	return predicate.SopNode(sql.FieldGT(FieldStatus, v))
+}
+
+// StatusGTE applies the GTE predicate on the "status" field.
+func StatusGTE(v uint8) predicate.SopNode {
+	return predicate.SopNode(sql.FieldGTE(FieldStatus, v))
+}
+
+// StatusLT applies the LT predicate on the "status" field.
+func StatusLT(v uint8) predicate.SopNode {
+	return predicate.SopNode(sql.FieldLT(FieldStatus, v))
+}
+
+// StatusLTE applies the LTE predicate on the "status" field.
+func StatusLTE(v uint8) predicate.SopNode {
+	return predicate.SopNode(sql.FieldLTE(FieldStatus, v))
+}
+
+// StatusIsNil applies the IsNil predicate on the "status" field.
+func StatusIsNil() predicate.SopNode {
+	return predicate.SopNode(sql.FieldIsNull(FieldStatus))
+}
+
+// StatusNotNil applies the NotNil predicate on the "status" field.
+func StatusNotNil() predicate.SopNode {
+	return predicate.SopNode(sql.FieldNotNull(FieldStatus))
+}
+
+// DeletedAtEQ applies the EQ predicate on the "deleted_at" field.
+func DeletedAtEQ(v time.Time) predicate.SopNode {
+	return predicate.SopNode(sql.FieldEQ(FieldDeletedAt, v))
+}
+
+// DeletedAtNEQ applies the NEQ predicate on the "deleted_at" field.
+func DeletedAtNEQ(v time.Time) predicate.SopNode {
+	return predicate.SopNode(sql.FieldNEQ(FieldDeletedAt, v))
+}
+
+// DeletedAtIn applies the In predicate on the "deleted_at" field.
+func DeletedAtIn(vs ...time.Time) predicate.SopNode {
+	return predicate.SopNode(sql.FieldIn(FieldDeletedAt, vs...))
+}
+
+// DeletedAtNotIn applies the NotIn predicate on the "deleted_at" field.
+func DeletedAtNotIn(vs ...time.Time) predicate.SopNode {
+	return predicate.SopNode(sql.FieldNotIn(FieldDeletedAt, vs...))
+}
+
+// DeletedAtGT applies the GT predicate on the "deleted_at" field.
+func DeletedAtGT(v time.Time) predicate.SopNode {
+	return predicate.SopNode(sql.FieldGT(FieldDeletedAt, v))
+}
+
+// DeletedAtGTE applies the GTE predicate on the "deleted_at" field.
+func DeletedAtGTE(v time.Time) predicate.SopNode {
+	return predicate.SopNode(sql.FieldGTE(FieldDeletedAt, v))
+}
+
+// DeletedAtLT applies the LT predicate on the "deleted_at" field.
+func DeletedAtLT(v time.Time) predicate.SopNode {
+	return predicate.SopNode(sql.FieldLT(FieldDeletedAt, v))
+}
+
+// DeletedAtLTE applies the LTE predicate on the "deleted_at" field.
+func DeletedAtLTE(v time.Time) predicate.SopNode {
+	return predicate.SopNode(sql.FieldLTE(FieldDeletedAt, v))
+}
+
+// DeletedAtIsNil applies the IsNil predicate on the "deleted_at" field.
+func DeletedAtIsNil() predicate.SopNode {
+	return predicate.SopNode(sql.FieldIsNull(FieldDeletedAt))
+}
+
+// DeletedAtNotNil applies the NotNil predicate on the "deleted_at" field.
+func DeletedAtNotNil() predicate.SopNode {
+	return predicate.SopNode(sql.FieldNotNull(FieldDeletedAt))
+}
+
+// StageIDEQ applies the EQ predicate on the "stage_id" field.
+func StageIDEQ(v uint64) predicate.SopNode {
+	return predicate.SopNode(sql.FieldEQ(FieldStageID, v))
+}
+
+// StageIDNEQ applies the NEQ predicate on the "stage_id" field.
+func StageIDNEQ(v uint64) predicate.SopNode {
+	return predicate.SopNode(sql.FieldNEQ(FieldStageID, v))
+}
+
+// StageIDIn applies the In predicate on the "stage_id" field.
+func StageIDIn(vs ...uint64) predicate.SopNode {
+	return predicate.SopNode(sql.FieldIn(FieldStageID, vs...))
+}
+
+// StageIDNotIn applies the NotIn predicate on the "stage_id" field.
+func StageIDNotIn(vs ...uint64) predicate.SopNode {
+	return predicate.SopNode(sql.FieldNotIn(FieldStageID, vs...))
+}
+
+// ParentIDEQ applies the EQ predicate on the "parent_id" field.
+func ParentIDEQ(v int) predicate.SopNode {
+	return predicate.SopNode(sql.FieldEQ(FieldParentID, v))
+}
+
+// ParentIDNEQ applies the NEQ predicate on the "parent_id" field.
+func ParentIDNEQ(v int) predicate.SopNode {
+	return predicate.SopNode(sql.FieldNEQ(FieldParentID, v))
+}
+
+// ParentIDIn applies the In predicate on the "parent_id" field.
+func ParentIDIn(vs ...int) predicate.SopNode {
+	return predicate.SopNode(sql.FieldIn(FieldParentID, vs...))
+}
+
+// ParentIDNotIn applies the NotIn predicate on the "parent_id" field.
+func ParentIDNotIn(vs ...int) predicate.SopNode {
+	return predicate.SopNode(sql.FieldNotIn(FieldParentID, vs...))
+}
+
+// ParentIDGT applies the GT predicate on the "parent_id" field.
+func ParentIDGT(v int) predicate.SopNode {
+	return predicate.SopNode(sql.FieldGT(FieldParentID, v))
+}
+
+// ParentIDGTE applies the GTE predicate on the "parent_id" field.
+func ParentIDGTE(v int) predicate.SopNode {
+	return predicate.SopNode(sql.FieldGTE(FieldParentID, v))
+}
+
+// ParentIDLT applies the LT predicate on the "parent_id" field.
+func ParentIDLT(v int) predicate.SopNode {
+	return predicate.SopNode(sql.FieldLT(FieldParentID, v))
+}
+
+// ParentIDLTE applies the LTE predicate on the "parent_id" field.
+func ParentIDLTE(v int) predicate.SopNode {
+	return predicate.SopNode(sql.FieldLTE(FieldParentID, v))
+}
+
+// NameEQ applies the EQ predicate on the "name" field.
+func NameEQ(v string) predicate.SopNode {
+	return predicate.SopNode(sql.FieldEQ(FieldName, v))
+}
+
+// NameNEQ applies the NEQ predicate on the "name" field.
+func NameNEQ(v string) predicate.SopNode {
+	return predicate.SopNode(sql.FieldNEQ(FieldName, v))
+}
+
+// NameIn applies the In predicate on the "name" field.
+func NameIn(vs ...string) predicate.SopNode {
+	return predicate.SopNode(sql.FieldIn(FieldName, vs...))
+}
+
+// NameNotIn applies the NotIn predicate on the "name" field.
+func NameNotIn(vs ...string) predicate.SopNode {
+	return predicate.SopNode(sql.FieldNotIn(FieldName, vs...))
+}
+
+// NameGT applies the GT predicate on the "name" field.
+func NameGT(v string) predicate.SopNode {
+	return predicate.SopNode(sql.FieldGT(FieldName, v))
+}
+
+// NameGTE applies the GTE predicate on the "name" field.
+func NameGTE(v string) predicate.SopNode {
+	return predicate.SopNode(sql.FieldGTE(FieldName, v))
+}
+
+// NameLT applies the LT predicate on the "name" field.
+func NameLT(v string) predicate.SopNode {
+	return predicate.SopNode(sql.FieldLT(FieldName, v))
+}
+
+// NameLTE applies the LTE predicate on the "name" field.
+func NameLTE(v string) predicate.SopNode {
+	return predicate.SopNode(sql.FieldLTE(FieldName, v))
+}
+
+// NameContains applies the Contains predicate on the "name" field.
+func NameContains(v string) predicate.SopNode {
+	return predicate.SopNode(sql.FieldContains(FieldName, v))
+}
+
+// NameHasPrefix applies the HasPrefix predicate on the "name" field.
+func NameHasPrefix(v string) predicate.SopNode {
+	return predicate.SopNode(sql.FieldHasPrefix(FieldName, v))
+}
+
+// NameHasSuffix applies the HasSuffix predicate on the "name" field.
+func NameHasSuffix(v string) predicate.SopNode {
+	return predicate.SopNode(sql.FieldHasSuffix(FieldName, v))
+}
+
+// NameEqualFold applies the EqualFold predicate on the "name" field.
+func NameEqualFold(v string) predicate.SopNode {
+	return predicate.SopNode(sql.FieldEqualFold(FieldName, v))
+}
+
+// NameContainsFold applies the ContainsFold predicate on the "name" field.
+func NameContainsFold(v string) predicate.SopNode {
+	return predicate.SopNode(sql.FieldContainsFold(FieldName, v))
+}
+
+// ConditionTypeEQ applies the EQ predicate on the "condition_type" field.
+func ConditionTypeEQ(v int) predicate.SopNode {
+	return predicate.SopNode(sql.FieldEQ(FieldConditionType, v))
+}
+
+// ConditionTypeNEQ applies the NEQ predicate on the "condition_type" field.
+func ConditionTypeNEQ(v int) predicate.SopNode {
+	return predicate.SopNode(sql.FieldNEQ(FieldConditionType, v))
+}
+
+// ConditionTypeIn applies the In predicate on the "condition_type" field.
+func ConditionTypeIn(vs ...int) predicate.SopNode {
+	return predicate.SopNode(sql.FieldIn(FieldConditionType, vs...))
+}
+
+// ConditionTypeNotIn applies the NotIn predicate on the "condition_type" field.
+func ConditionTypeNotIn(vs ...int) predicate.SopNode {
+	return predicate.SopNode(sql.FieldNotIn(FieldConditionType, vs...))
+}
+
+// ConditionTypeGT applies the GT predicate on the "condition_type" field.
+func ConditionTypeGT(v int) predicate.SopNode {
+	return predicate.SopNode(sql.FieldGT(FieldConditionType, v))
+}
+
+// ConditionTypeGTE applies the GTE predicate on the "condition_type" field.
+func ConditionTypeGTE(v int) predicate.SopNode {
+	return predicate.SopNode(sql.FieldGTE(FieldConditionType, v))
+}
+
+// ConditionTypeLT applies the LT predicate on the "condition_type" field.
+func ConditionTypeLT(v int) predicate.SopNode {
+	return predicate.SopNode(sql.FieldLT(FieldConditionType, v))
+}
+
+// ConditionTypeLTE applies the LTE predicate on the "condition_type" field.
+func ConditionTypeLTE(v int) predicate.SopNode {
+	return predicate.SopNode(sql.FieldLTE(FieldConditionType, v))
+}
+
+// HasSopStage applies the HasEdge predicate on the "sop_stage" edge.
+func HasSopStage() predicate.SopNode {
+	return predicate.SopNode(func(s *sql.Selector) {
+		step := sqlgraph.NewStep(
+			sqlgraph.From(Table, FieldID),
+			sqlgraph.Edge(sqlgraph.M2O, true, SopStageTable, SopStageColumn),
+		)
+		sqlgraph.HasNeighbors(s, step)
+	})
+}
+
+// HasSopStageWith applies the HasEdge predicate on the "sop_stage" edge with a given conditions (other predicates).
+func HasSopStageWith(preds ...predicate.SopStage) predicate.SopNode {
+	return predicate.SopNode(func(s *sql.Selector) {
+		step := newSopStageStep()
+		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.SopNode) predicate.SopNode {
+	return predicate.SopNode(sql.AndPredicates(predicates...))
+}
+
+// Or groups predicates with the OR operator between them.
+func Or(predicates ...predicate.SopNode) predicate.SopNode {
+	return predicate.SopNode(sql.OrPredicates(predicates...))
+}
+
+// Not applies the not operator on the given predicate.
+func Not(p predicate.SopNode) predicate.SopNode {
+	return predicate.SopNode(sql.NotPredicates(p))
+}

+ 1187 - 0
ent/sopnode_create.go

@@ -0,0 +1,1187 @@
+// Code generated by ent, DO NOT EDIT.
+
+package ent
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"time"
+	"wechat-api/ent/custom_types"
+	"wechat-api/ent/sopnode"
+	"wechat-api/ent/sopstage"
+
+	"entgo.io/ent/dialect/sql"
+	"entgo.io/ent/dialect/sql/sqlgraph"
+	"entgo.io/ent/schema/field"
+)
+
+// SopNodeCreate is the builder for creating a SopNode entity.
+type SopNodeCreate struct {
+	config
+	mutation *SopNodeMutation
+	hooks    []Hook
+	conflict []sql.ConflictOption
+}
+
+// SetCreatedAt sets the "created_at" field.
+func (snc *SopNodeCreate) SetCreatedAt(t time.Time) *SopNodeCreate {
+	snc.mutation.SetCreatedAt(t)
+	return snc
+}
+
+// SetNillableCreatedAt sets the "created_at" field if the given value is not nil.
+func (snc *SopNodeCreate) SetNillableCreatedAt(t *time.Time) *SopNodeCreate {
+	if t != nil {
+		snc.SetCreatedAt(*t)
+	}
+	return snc
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (snc *SopNodeCreate) SetUpdatedAt(t time.Time) *SopNodeCreate {
+	snc.mutation.SetUpdatedAt(t)
+	return snc
+}
+
+// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil.
+func (snc *SopNodeCreate) SetNillableUpdatedAt(t *time.Time) *SopNodeCreate {
+	if t != nil {
+		snc.SetUpdatedAt(*t)
+	}
+	return snc
+}
+
+// SetStatus sets the "status" field.
+func (snc *SopNodeCreate) SetStatus(u uint8) *SopNodeCreate {
+	snc.mutation.SetStatus(u)
+	return snc
+}
+
+// SetNillableStatus sets the "status" field if the given value is not nil.
+func (snc *SopNodeCreate) SetNillableStatus(u *uint8) *SopNodeCreate {
+	if u != nil {
+		snc.SetStatus(*u)
+	}
+	return snc
+}
+
+// SetDeletedAt sets the "deleted_at" field.
+func (snc *SopNodeCreate) SetDeletedAt(t time.Time) *SopNodeCreate {
+	snc.mutation.SetDeletedAt(t)
+	return snc
+}
+
+// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil.
+func (snc *SopNodeCreate) SetNillableDeletedAt(t *time.Time) *SopNodeCreate {
+	if t != nil {
+		snc.SetDeletedAt(*t)
+	}
+	return snc
+}
+
+// SetStageID sets the "stage_id" field.
+func (snc *SopNodeCreate) SetStageID(u uint64) *SopNodeCreate {
+	snc.mutation.SetStageID(u)
+	return snc
+}
+
+// SetParentID sets the "parent_id" field.
+func (snc *SopNodeCreate) SetParentID(i int) *SopNodeCreate {
+	snc.mutation.SetParentID(i)
+	return snc
+}
+
+// SetName sets the "name" field.
+func (snc *SopNodeCreate) SetName(s string) *SopNodeCreate {
+	snc.mutation.SetName(s)
+	return snc
+}
+
+// SetNillableName sets the "name" field if the given value is not nil.
+func (snc *SopNodeCreate) SetNillableName(s *string) *SopNodeCreate {
+	if s != nil {
+		snc.SetName(*s)
+	}
+	return snc
+}
+
+// SetConditionType sets the "condition_type" field.
+func (snc *SopNodeCreate) SetConditionType(i int) *SopNodeCreate {
+	snc.mutation.SetConditionType(i)
+	return snc
+}
+
+// SetNillableConditionType sets the "condition_type" field if the given value is not nil.
+func (snc *SopNodeCreate) SetNillableConditionType(i *int) *SopNodeCreate {
+	if i != nil {
+		snc.SetConditionType(*i)
+	}
+	return snc
+}
+
+// SetConditionList sets the "condition_list" field.
+func (snc *SopNodeCreate) SetConditionList(ct []custom_types.Condition) *SopNodeCreate {
+	snc.mutation.SetConditionList(ct)
+	return snc
+}
+
+// SetActionMessage sets the "action_message" field.
+func (snc *SopNodeCreate) SetActionMessage(ct []custom_types.Action) *SopNodeCreate {
+	snc.mutation.SetActionMessage(ct)
+	return snc
+}
+
+// SetActionLabel sets the "action_label" field.
+func (snc *SopNodeCreate) SetActionLabel(i []int) *SopNodeCreate {
+	snc.mutation.SetActionLabel(i)
+	return snc
+}
+
+// SetID sets the "id" field.
+func (snc *SopNodeCreate) SetID(u uint64) *SopNodeCreate {
+	snc.mutation.SetID(u)
+	return snc
+}
+
+// SetSopStageID sets the "sop_stage" edge to the SopStage entity by ID.
+func (snc *SopNodeCreate) SetSopStageID(id uint64) *SopNodeCreate {
+	snc.mutation.SetSopStageID(id)
+	return snc
+}
+
+// SetSopStage sets the "sop_stage" edge to the SopStage entity.
+func (snc *SopNodeCreate) SetSopStage(s *SopStage) *SopNodeCreate {
+	return snc.SetSopStageID(s.ID)
+}
+
+// Mutation returns the SopNodeMutation object of the builder.
+func (snc *SopNodeCreate) Mutation() *SopNodeMutation {
+	return snc.mutation
+}
+
+// Save creates the SopNode in the database.
+func (snc *SopNodeCreate) Save(ctx context.Context) (*SopNode, error) {
+	if err := snc.defaults(); err != nil {
+		return nil, err
+	}
+	return withHooks(ctx, snc.sqlSave, snc.mutation, snc.hooks)
+}
+
+// SaveX calls Save and panics if Save returns an error.
+func (snc *SopNodeCreate) SaveX(ctx context.Context) *SopNode {
+	v, err := snc.Save(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return v
+}
+
+// Exec executes the query.
+func (snc *SopNodeCreate) Exec(ctx context.Context) error {
+	_, err := snc.Save(ctx)
+	return err
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (snc *SopNodeCreate) ExecX(ctx context.Context) {
+	if err := snc.Exec(ctx); err != nil {
+		panic(err)
+	}
+}
+
+// defaults sets the default values of the builder before save.
+func (snc *SopNodeCreate) defaults() error {
+	if _, ok := snc.mutation.CreatedAt(); !ok {
+		if sopnode.DefaultCreatedAt == nil {
+			return fmt.Errorf("ent: uninitialized sopnode.DefaultCreatedAt (forgotten import ent/runtime?)")
+		}
+		v := sopnode.DefaultCreatedAt()
+		snc.mutation.SetCreatedAt(v)
+	}
+	if _, ok := snc.mutation.UpdatedAt(); !ok {
+		if sopnode.DefaultUpdatedAt == nil {
+			return fmt.Errorf("ent: uninitialized sopnode.DefaultUpdatedAt (forgotten import ent/runtime?)")
+		}
+		v := sopnode.DefaultUpdatedAt()
+		snc.mutation.SetUpdatedAt(v)
+	}
+	if _, ok := snc.mutation.Status(); !ok {
+		v := sopnode.DefaultStatus
+		snc.mutation.SetStatus(v)
+	}
+	if _, ok := snc.mutation.Name(); !ok {
+		v := sopnode.DefaultName
+		snc.mutation.SetName(v)
+	}
+	if _, ok := snc.mutation.ConditionType(); !ok {
+		v := sopnode.DefaultConditionType
+		snc.mutation.SetConditionType(v)
+	}
+	return nil
+}
+
+// check runs all checks and user-defined validators on the builder.
+func (snc *SopNodeCreate) check() error {
+	if _, ok := snc.mutation.CreatedAt(); !ok {
+		return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "SopNode.created_at"`)}
+	}
+	if _, ok := snc.mutation.UpdatedAt(); !ok {
+		return &ValidationError{Name: "updated_at", err: errors.New(`ent: missing required field "SopNode.updated_at"`)}
+	}
+	if _, ok := snc.mutation.StageID(); !ok {
+		return &ValidationError{Name: "stage_id", err: errors.New(`ent: missing required field "SopNode.stage_id"`)}
+	}
+	if _, ok := snc.mutation.ParentID(); !ok {
+		return &ValidationError{Name: "parent_id", err: errors.New(`ent: missing required field "SopNode.parent_id"`)}
+	}
+	if _, ok := snc.mutation.Name(); !ok {
+		return &ValidationError{Name: "name", err: errors.New(`ent: missing required field "SopNode.name"`)}
+	}
+	if _, ok := snc.mutation.ConditionType(); !ok {
+		return &ValidationError{Name: "condition_type", err: errors.New(`ent: missing required field "SopNode.condition_type"`)}
+	}
+	if _, ok := snc.mutation.ConditionList(); !ok {
+		return &ValidationError{Name: "condition_list", err: errors.New(`ent: missing required field "SopNode.condition_list"`)}
+	}
+	if _, ok := snc.mutation.ActionMessage(); !ok {
+		return &ValidationError{Name: "action_message", err: errors.New(`ent: missing required field "SopNode.action_message"`)}
+	}
+	if _, ok := snc.mutation.ActionLabel(); !ok {
+		return &ValidationError{Name: "action_label", err: errors.New(`ent: missing required field "SopNode.action_label"`)}
+	}
+	if _, ok := snc.mutation.SopStageID(); !ok {
+		return &ValidationError{Name: "sop_stage", err: errors.New(`ent: missing required edge "SopNode.sop_stage"`)}
+	}
+	return nil
+}
+
+func (snc *SopNodeCreate) sqlSave(ctx context.Context) (*SopNode, error) {
+	if err := snc.check(); err != nil {
+		return nil, err
+	}
+	_node, _spec := snc.createSpec()
+	if err := sqlgraph.CreateNode(ctx, snc.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)
+	}
+	snc.mutation.id = &_node.ID
+	snc.mutation.done = true
+	return _node, nil
+}
+
+func (snc *SopNodeCreate) createSpec() (*SopNode, *sqlgraph.CreateSpec) {
+	var (
+		_node = &SopNode{config: snc.config}
+		_spec = sqlgraph.NewCreateSpec(sopnode.Table, sqlgraph.NewFieldSpec(sopnode.FieldID, field.TypeUint64))
+	)
+	_spec.OnConflict = snc.conflict
+	if id, ok := snc.mutation.ID(); ok {
+		_node.ID = id
+		_spec.ID.Value = id
+	}
+	if value, ok := snc.mutation.CreatedAt(); ok {
+		_spec.SetField(sopnode.FieldCreatedAt, field.TypeTime, value)
+		_node.CreatedAt = value
+	}
+	if value, ok := snc.mutation.UpdatedAt(); ok {
+		_spec.SetField(sopnode.FieldUpdatedAt, field.TypeTime, value)
+		_node.UpdatedAt = value
+	}
+	if value, ok := snc.mutation.Status(); ok {
+		_spec.SetField(sopnode.FieldStatus, field.TypeUint8, value)
+		_node.Status = value
+	}
+	if value, ok := snc.mutation.DeletedAt(); ok {
+		_spec.SetField(sopnode.FieldDeletedAt, field.TypeTime, value)
+		_node.DeletedAt = value
+	}
+	if value, ok := snc.mutation.ParentID(); ok {
+		_spec.SetField(sopnode.FieldParentID, field.TypeInt, value)
+		_node.ParentID = value
+	}
+	if value, ok := snc.mutation.Name(); ok {
+		_spec.SetField(sopnode.FieldName, field.TypeString, value)
+		_node.Name = value
+	}
+	if value, ok := snc.mutation.ConditionType(); ok {
+		_spec.SetField(sopnode.FieldConditionType, field.TypeInt, value)
+		_node.ConditionType = value
+	}
+	if value, ok := snc.mutation.ConditionList(); ok {
+		_spec.SetField(sopnode.FieldConditionList, field.TypeJSON, value)
+		_node.ConditionList = value
+	}
+	if value, ok := snc.mutation.ActionMessage(); ok {
+		_spec.SetField(sopnode.FieldActionMessage, field.TypeJSON, value)
+		_node.ActionMessage = value
+	}
+	if value, ok := snc.mutation.ActionLabel(); ok {
+		_spec.SetField(sopnode.FieldActionLabel, field.TypeJSON, value)
+		_node.ActionLabel = value
+	}
+	if nodes := snc.mutation.SopStageIDs(); len(nodes) > 0 {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.M2O,
+			Inverse: true,
+			Table:   sopnode.SopStageTable,
+			Columns: []string{sopnode.SopStageColumn},
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: sqlgraph.NewFieldSpec(sopstage.FieldID, field.TypeUint64),
+			},
+		}
+		for _, k := range nodes {
+			edge.Target.Nodes = append(edge.Target.Nodes, k)
+		}
+		_node.StageID = 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.SopNode.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.SopNodeUpsert) {
+//			SetCreatedAt(v+v).
+//		}).
+//		Exec(ctx)
+func (snc *SopNodeCreate) OnConflict(opts ...sql.ConflictOption) *SopNodeUpsertOne {
+	snc.conflict = opts
+	return &SopNodeUpsertOne{
+		create: snc,
+	}
+}
+
+// OnConflictColumns calls `OnConflict` and configures the columns
+// as conflict target. Using this option is equivalent to using:
+//
+//	client.SopNode.Create().
+//		OnConflict(sql.ConflictColumns(columns...)).
+//		Exec(ctx)
+func (snc *SopNodeCreate) OnConflictColumns(columns ...string) *SopNodeUpsertOne {
+	snc.conflict = append(snc.conflict, sql.ConflictColumns(columns...))
+	return &SopNodeUpsertOne{
+		create: snc,
+	}
+}
+
+type (
+	// SopNodeUpsertOne is the builder for "upsert"-ing
+	//  one SopNode node.
+	SopNodeUpsertOne struct {
+		create *SopNodeCreate
+	}
+
+	// SopNodeUpsert is the "OnConflict" setter.
+	SopNodeUpsert struct {
+		*sql.UpdateSet
+	}
+)
+
+// SetUpdatedAt sets the "updated_at" field.
+func (u *SopNodeUpsert) SetUpdatedAt(v time.Time) *SopNodeUpsert {
+	u.Set(sopnode.FieldUpdatedAt, v)
+	return u
+}
+
+// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create.
+func (u *SopNodeUpsert) UpdateUpdatedAt() *SopNodeUpsert {
+	u.SetExcluded(sopnode.FieldUpdatedAt)
+	return u
+}
+
+// SetStatus sets the "status" field.
+func (u *SopNodeUpsert) SetStatus(v uint8) *SopNodeUpsert {
+	u.Set(sopnode.FieldStatus, v)
+	return u
+}
+
+// UpdateStatus sets the "status" field to the value that was provided on create.
+func (u *SopNodeUpsert) UpdateStatus() *SopNodeUpsert {
+	u.SetExcluded(sopnode.FieldStatus)
+	return u
+}
+
+// AddStatus adds v to the "status" field.
+func (u *SopNodeUpsert) AddStatus(v uint8) *SopNodeUpsert {
+	u.Add(sopnode.FieldStatus, v)
+	return u
+}
+
+// ClearStatus clears the value of the "status" field.
+func (u *SopNodeUpsert) ClearStatus() *SopNodeUpsert {
+	u.SetNull(sopnode.FieldStatus)
+	return u
+}
+
+// SetDeletedAt sets the "deleted_at" field.
+func (u *SopNodeUpsert) SetDeletedAt(v time.Time) *SopNodeUpsert {
+	u.Set(sopnode.FieldDeletedAt, v)
+	return u
+}
+
+// UpdateDeletedAt sets the "deleted_at" field to the value that was provided on create.
+func (u *SopNodeUpsert) UpdateDeletedAt() *SopNodeUpsert {
+	u.SetExcluded(sopnode.FieldDeletedAt)
+	return u
+}
+
+// ClearDeletedAt clears the value of the "deleted_at" field.
+func (u *SopNodeUpsert) ClearDeletedAt() *SopNodeUpsert {
+	u.SetNull(sopnode.FieldDeletedAt)
+	return u
+}
+
+// SetStageID sets the "stage_id" field.
+func (u *SopNodeUpsert) SetStageID(v uint64) *SopNodeUpsert {
+	u.Set(sopnode.FieldStageID, v)
+	return u
+}
+
+// UpdateStageID sets the "stage_id" field to the value that was provided on create.
+func (u *SopNodeUpsert) UpdateStageID() *SopNodeUpsert {
+	u.SetExcluded(sopnode.FieldStageID)
+	return u
+}
+
+// SetParentID sets the "parent_id" field.
+func (u *SopNodeUpsert) SetParentID(v int) *SopNodeUpsert {
+	u.Set(sopnode.FieldParentID, v)
+	return u
+}
+
+// UpdateParentID sets the "parent_id" field to the value that was provided on create.
+func (u *SopNodeUpsert) UpdateParentID() *SopNodeUpsert {
+	u.SetExcluded(sopnode.FieldParentID)
+	return u
+}
+
+// AddParentID adds v to the "parent_id" field.
+func (u *SopNodeUpsert) AddParentID(v int) *SopNodeUpsert {
+	u.Add(sopnode.FieldParentID, v)
+	return u
+}
+
+// SetName sets the "name" field.
+func (u *SopNodeUpsert) SetName(v string) *SopNodeUpsert {
+	u.Set(sopnode.FieldName, v)
+	return u
+}
+
+// UpdateName sets the "name" field to the value that was provided on create.
+func (u *SopNodeUpsert) UpdateName() *SopNodeUpsert {
+	u.SetExcluded(sopnode.FieldName)
+	return u
+}
+
+// SetConditionType sets the "condition_type" field.
+func (u *SopNodeUpsert) SetConditionType(v int) *SopNodeUpsert {
+	u.Set(sopnode.FieldConditionType, v)
+	return u
+}
+
+// UpdateConditionType sets the "condition_type" field to the value that was provided on create.
+func (u *SopNodeUpsert) UpdateConditionType() *SopNodeUpsert {
+	u.SetExcluded(sopnode.FieldConditionType)
+	return u
+}
+
+// AddConditionType adds v to the "condition_type" field.
+func (u *SopNodeUpsert) AddConditionType(v int) *SopNodeUpsert {
+	u.Add(sopnode.FieldConditionType, v)
+	return u
+}
+
+// SetConditionList sets the "condition_list" field.
+func (u *SopNodeUpsert) SetConditionList(v []custom_types.Condition) *SopNodeUpsert {
+	u.Set(sopnode.FieldConditionList, v)
+	return u
+}
+
+// UpdateConditionList sets the "condition_list" field to the value that was provided on create.
+func (u *SopNodeUpsert) UpdateConditionList() *SopNodeUpsert {
+	u.SetExcluded(sopnode.FieldConditionList)
+	return u
+}
+
+// SetActionMessage sets the "action_message" field.
+func (u *SopNodeUpsert) SetActionMessage(v []custom_types.Action) *SopNodeUpsert {
+	u.Set(sopnode.FieldActionMessage, v)
+	return u
+}
+
+// UpdateActionMessage sets the "action_message" field to the value that was provided on create.
+func (u *SopNodeUpsert) UpdateActionMessage() *SopNodeUpsert {
+	u.SetExcluded(sopnode.FieldActionMessage)
+	return u
+}
+
+// SetActionLabel sets the "action_label" field.
+func (u *SopNodeUpsert) SetActionLabel(v []int) *SopNodeUpsert {
+	u.Set(sopnode.FieldActionLabel, v)
+	return u
+}
+
+// UpdateActionLabel sets the "action_label" field to the value that was provided on create.
+func (u *SopNodeUpsert) UpdateActionLabel() *SopNodeUpsert {
+	u.SetExcluded(sopnode.FieldActionLabel)
+	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.SopNode.Create().
+//		OnConflict(
+//			sql.ResolveWithNewValues(),
+//			sql.ResolveWith(func(u *sql.UpdateSet) {
+//				u.SetIgnore(sopnode.FieldID)
+//			}),
+//		).
+//		Exec(ctx)
+func (u *SopNodeUpsertOne) UpdateNewValues() *SopNodeUpsertOne {
+	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(sopnode.FieldID)
+		}
+		if _, exists := u.create.mutation.CreatedAt(); exists {
+			s.SetIgnore(sopnode.FieldCreatedAt)
+		}
+	}))
+	return u
+}
+
+// Ignore sets each column to itself in case of conflict.
+// Using this option is equivalent to using:
+//
+//	client.SopNode.Create().
+//	    OnConflict(sql.ResolveWithIgnore()).
+//	    Exec(ctx)
+func (u *SopNodeUpsertOne) Ignore() *SopNodeUpsertOne {
+	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 *SopNodeUpsertOne) DoNothing() *SopNodeUpsertOne {
+	u.create.conflict = append(u.create.conflict, sql.DoNothing())
+	return u
+}
+
+// Update allows overriding fields `UPDATE` values. See the SopNodeCreate.OnConflict
+// documentation for more info.
+func (u *SopNodeUpsertOne) Update(set func(*SopNodeUpsert)) *SopNodeUpsertOne {
+	u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) {
+		set(&SopNodeUpsert{UpdateSet: update})
+	}))
+	return u
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (u *SopNodeUpsertOne) SetUpdatedAt(v time.Time) *SopNodeUpsertOne {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.SetUpdatedAt(v)
+	})
+}
+
+// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create.
+func (u *SopNodeUpsertOne) UpdateUpdatedAt() *SopNodeUpsertOne {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.UpdateUpdatedAt()
+	})
+}
+
+// SetStatus sets the "status" field.
+func (u *SopNodeUpsertOne) SetStatus(v uint8) *SopNodeUpsertOne {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.SetStatus(v)
+	})
+}
+
+// AddStatus adds v to the "status" field.
+func (u *SopNodeUpsertOne) AddStatus(v uint8) *SopNodeUpsertOne {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.AddStatus(v)
+	})
+}
+
+// UpdateStatus sets the "status" field to the value that was provided on create.
+func (u *SopNodeUpsertOne) UpdateStatus() *SopNodeUpsertOne {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.UpdateStatus()
+	})
+}
+
+// ClearStatus clears the value of the "status" field.
+func (u *SopNodeUpsertOne) ClearStatus() *SopNodeUpsertOne {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.ClearStatus()
+	})
+}
+
+// SetDeletedAt sets the "deleted_at" field.
+func (u *SopNodeUpsertOne) SetDeletedAt(v time.Time) *SopNodeUpsertOne {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.SetDeletedAt(v)
+	})
+}
+
+// UpdateDeletedAt sets the "deleted_at" field to the value that was provided on create.
+func (u *SopNodeUpsertOne) UpdateDeletedAt() *SopNodeUpsertOne {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.UpdateDeletedAt()
+	})
+}
+
+// ClearDeletedAt clears the value of the "deleted_at" field.
+func (u *SopNodeUpsertOne) ClearDeletedAt() *SopNodeUpsertOne {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.ClearDeletedAt()
+	})
+}
+
+// SetStageID sets the "stage_id" field.
+func (u *SopNodeUpsertOne) SetStageID(v uint64) *SopNodeUpsertOne {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.SetStageID(v)
+	})
+}
+
+// UpdateStageID sets the "stage_id" field to the value that was provided on create.
+func (u *SopNodeUpsertOne) UpdateStageID() *SopNodeUpsertOne {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.UpdateStageID()
+	})
+}
+
+// SetParentID sets the "parent_id" field.
+func (u *SopNodeUpsertOne) SetParentID(v int) *SopNodeUpsertOne {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.SetParentID(v)
+	})
+}
+
+// AddParentID adds v to the "parent_id" field.
+func (u *SopNodeUpsertOne) AddParentID(v int) *SopNodeUpsertOne {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.AddParentID(v)
+	})
+}
+
+// UpdateParentID sets the "parent_id" field to the value that was provided on create.
+func (u *SopNodeUpsertOne) UpdateParentID() *SopNodeUpsertOne {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.UpdateParentID()
+	})
+}
+
+// SetName sets the "name" field.
+func (u *SopNodeUpsertOne) SetName(v string) *SopNodeUpsertOne {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.SetName(v)
+	})
+}
+
+// UpdateName sets the "name" field to the value that was provided on create.
+func (u *SopNodeUpsertOne) UpdateName() *SopNodeUpsertOne {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.UpdateName()
+	})
+}
+
+// SetConditionType sets the "condition_type" field.
+func (u *SopNodeUpsertOne) SetConditionType(v int) *SopNodeUpsertOne {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.SetConditionType(v)
+	})
+}
+
+// AddConditionType adds v to the "condition_type" field.
+func (u *SopNodeUpsertOne) AddConditionType(v int) *SopNodeUpsertOne {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.AddConditionType(v)
+	})
+}
+
+// UpdateConditionType sets the "condition_type" field to the value that was provided on create.
+func (u *SopNodeUpsertOne) UpdateConditionType() *SopNodeUpsertOne {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.UpdateConditionType()
+	})
+}
+
+// SetConditionList sets the "condition_list" field.
+func (u *SopNodeUpsertOne) SetConditionList(v []custom_types.Condition) *SopNodeUpsertOne {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.SetConditionList(v)
+	})
+}
+
+// UpdateConditionList sets the "condition_list" field to the value that was provided on create.
+func (u *SopNodeUpsertOne) UpdateConditionList() *SopNodeUpsertOne {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.UpdateConditionList()
+	})
+}
+
+// SetActionMessage sets the "action_message" field.
+func (u *SopNodeUpsertOne) SetActionMessage(v []custom_types.Action) *SopNodeUpsertOne {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.SetActionMessage(v)
+	})
+}
+
+// UpdateActionMessage sets the "action_message" field to the value that was provided on create.
+func (u *SopNodeUpsertOne) UpdateActionMessage() *SopNodeUpsertOne {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.UpdateActionMessage()
+	})
+}
+
+// SetActionLabel sets the "action_label" field.
+func (u *SopNodeUpsertOne) SetActionLabel(v []int) *SopNodeUpsertOne {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.SetActionLabel(v)
+	})
+}
+
+// UpdateActionLabel sets the "action_label" field to the value that was provided on create.
+func (u *SopNodeUpsertOne) UpdateActionLabel() *SopNodeUpsertOne {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.UpdateActionLabel()
+	})
+}
+
+// Exec executes the query.
+func (u *SopNodeUpsertOne) Exec(ctx context.Context) error {
+	if len(u.create.conflict) == 0 {
+		return errors.New("ent: missing options for SopNodeCreate.OnConflict")
+	}
+	return u.create.Exec(ctx)
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (u *SopNodeUpsertOne) 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 *SopNodeUpsertOne) 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 *SopNodeUpsertOne) IDX(ctx context.Context) uint64 {
+	id, err := u.ID(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return id
+}
+
+// SopNodeCreateBulk is the builder for creating many SopNode entities in bulk.
+type SopNodeCreateBulk struct {
+	config
+	err      error
+	builders []*SopNodeCreate
+	conflict []sql.ConflictOption
+}
+
+// Save creates the SopNode entities in the database.
+func (sncb *SopNodeCreateBulk) Save(ctx context.Context) ([]*SopNode, error) {
+	if sncb.err != nil {
+		return nil, sncb.err
+	}
+	specs := make([]*sqlgraph.CreateSpec, len(sncb.builders))
+	nodes := make([]*SopNode, len(sncb.builders))
+	mutators := make([]Mutator, len(sncb.builders))
+	for i := range sncb.builders {
+		func(i int, root context.Context) {
+			builder := sncb.builders[i]
+			builder.defaults()
+			var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
+				mutation, ok := m.(*SopNodeMutation)
+				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, sncb.builders[i+1].mutation)
+				} else {
+					spec := &sqlgraph.BatchCreateSpec{Nodes: specs}
+					spec.OnConflict = sncb.conflict
+					// Invoke the actual operation on the latest mutation in the chain.
+					if err = sqlgraph.BatchCreate(ctx, sncb.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, sncb.builders[0].mutation); err != nil {
+			return nil, err
+		}
+	}
+	return nodes, nil
+}
+
+// SaveX is like Save, but panics if an error occurs.
+func (sncb *SopNodeCreateBulk) SaveX(ctx context.Context) []*SopNode {
+	v, err := sncb.Save(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return v
+}
+
+// Exec executes the query.
+func (sncb *SopNodeCreateBulk) Exec(ctx context.Context) error {
+	_, err := sncb.Save(ctx)
+	return err
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (sncb *SopNodeCreateBulk) ExecX(ctx context.Context) {
+	if err := sncb.Exec(ctx); err != nil {
+		panic(err)
+	}
+}
+
+// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause
+// of the `INSERT` statement. For example:
+//
+//	client.SopNode.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.SopNodeUpsert) {
+//			SetCreatedAt(v+v).
+//		}).
+//		Exec(ctx)
+func (sncb *SopNodeCreateBulk) OnConflict(opts ...sql.ConflictOption) *SopNodeUpsertBulk {
+	sncb.conflict = opts
+	return &SopNodeUpsertBulk{
+		create: sncb,
+	}
+}
+
+// OnConflictColumns calls `OnConflict` and configures the columns
+// as conflict target. Using this option is equivalent to using:
+//
+//	client.SopNode.Create().
+//		OnConflict(sql.ConflictColumns(columns...)).
+//		Exec(ctx)
+func (sncb *SopNodeCreateBulk) OnConflictColumns(columns ...string) *SopNodeUpsertBulk {
+	sncb.conflict = append(sncb.conflict, sql.ConflictColumns(columns...))
+	return &SopNodeUpsertBulk{
+		create: sncb,
+	}
+}
+
+// SopNodeUpsertBulk is the builder for "upsert"-ing
+// a bulk of SopNode nodes.
+type SopNodeUpsertBulk struct {
+	create *SopNodeCreateBulk
+}
+
+// UpdateNewValues updates the mutable fields using the new values that
+// were set on create. Using this option is equivalent to using:
+//
+//	client.SopNode.Create().
+//		OnConflict(
+//			sql.ResolveWithNewValues(),
+//			sql.ResolveWith(func(u *sql.UpdateSet) {
+//				u.SetIgnore(sopnode.FieldID)
+//			}),
+//		).
+//		Exec(ctx)
+func (u *SopNodeUpsertBulk) UpdateNewValues() *SopNodeUpsertBulk {
+	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(sopnode.FieldID)
+			}
+			if _, exists := b.mutation.CreatedAt(); exists {
+				s.SetIgnore(sopnode.FieldCreatedAt)
+			}
+		}
+	}))
+	return u
+}
+
+// Ignore sets each column to itself in case of conflict.
+// Using this option is equivalent to using:
+//
+//	client.SopNode.Create().
+//		OnConflict(sql.ResolveWithIgnore()).
+//		Exec(ctx)
+func (u *SopNodeUpsertBulk) Ignore() *SopNodeUpsertBulk {
+	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 *SopNodeUpsertBulk) DoNothing() *SopNodeUpsertBulk {
+	u.create.conflict = append(u.create.conflict, sql.DoNothing())
+	return u
+}
+
+// Update allows overriding fields `UPDATE` values. See the SopNodeCreateBulk.OnConflict
+// documentation for more info.
+func (u *SopNodeUpsertBulk) Update(set func(*SopNodeUpsert)) *SopNodeUpsertBulk {
+	u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) {
+		set(&SopNodeUpsert{UpdateSet: update})
+	}))
+	return u
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (u *SopNodeUpsertBulk) SetUpdatedAt(v time.Time) *SopNodeUpsertBulk {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.SetUpdatedAt(v)
+	})
+}
+
+// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create.
+func (u *SopNodeUpsertBulk) UpdateUpdatedAt() *SopNodeUpsertBulk {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.UpdateUpdatedAt()
+	})
+}
+
+// SetStatus sets the "status" field.
+func (u *SopNodeUpsertBulk) SetStatus(v uint8) *SopNodeUpsertBulk {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.SetStatus(v)
+	})
+}
+
+// AddStatus adds v to the "status" field.
+func (u *SopNodeUpsertBulk) AddStatus(v uint8) *SopNodeUpsertBulk {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.AddStatus(v)
+	})
+}
+
+// UpdateStatus sets the "status" field to the value that was provided on create.
+func (u *SopNodeUpsertBulk) UpdateStatus() *SopNodeUpsertBulk {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.UpdateStatus()
+	})
+}
+
+// ClearStatus clears the value of the "status" field.
+func (u *SopNodeUpsertBulk) ClearStatus() *SopNodeUpsertBulk {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.ClearStatus()
+	})
+}
+
+// SetDeletedAt sets the "deleted_at" field.
+func (u *SopNodeUpsertBulk) SetDeletedAt(v time.Time) *SopNodeUpsertBulk {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.SetDeletedAt(v)
+	})
+}
+
+// UpdateDeletedAt sets the "deleted_at" field to the value that was provided on create.
+func (u *SopNodeUpsertBulk) UpdateDeletedAt() *SopNodeUpsertBulk {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.UpdateDeletedAt()
+	})
+}
+
+// ClearDeletedAt clears the value of the "deleted_at" field.
+func (u *SopNodeUpsertBulk) ClearDeletedAt() *SopNodeUpsertBulk {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.ClearDeletedAt()
+	})
+}
+
+// SetStageID sets the "stage_id" field.
+func (u *SopNodeUpsertBulk) SetStageID(v uint64) *SopNodeUpsertBulk {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.SetStageID(v)
+	})
+}
+
+// UpdateStageID sets the "stage_id" field to the value that was provided on create.
+func (u *SopNodeUpsertBulk) UpdateStageID() *SopNodeUpsertBulk {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.UpdateStageID()
+	})
+}
+
+// SetParentID sets the "parent_id" field.
+func (u *SopNodeUpsertBulk) SetParentID(v int) *SopNodeUpsertBulk {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.SetParentID(v)
+	})
+}
+
+// AddParentID adds v to the "parent_id" field.
+func (u *SopNodeUpsertBulk) AddParentID(v int) *SopNodeUpsertBulk {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.AddParentID(v)
+	})
+}
+
+// UpdateParentID sets the "parent_id" field to the value that was provided on create.
+func (u *SopNodeUpsertBulk) UpdateParentID() *SopNodeUpsertBulk {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.UpdateParentID()
+	})
+}
+
+// SetName sets the "name" field.
+func (u *SopNodeUpsertBulk) SetName(v string) *SopNodeUpsertBulk {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.SetName(v)
+	})
+}
+
+// UpdateName sets the "name" field to the value that was provided on create.
+func (u *SopNodeUpsertBulk) UpdateName() *SopNodeUpsertBulk {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.UpdateName()
+	})
+}
+
+// SetConditionType sets the "condition_type" field.
+func (u *SopNodeUpsertBulk) SetConditionType(v int) *SopNodeUpsertBulk {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.SetConditionType(v)
+	})
+}
+
+// AddConditionType adds v to the "condition_type" field.
+func (u *SopNodeUpsertBulk) AddConditionType(v int) *SopNodeUpsertBulk {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.AddConditionType(v)
+	})
+}
+
+// UpdateConditionType sets the "condition_type" field to the value that was provided on create.
+func (u *SopNodeUpsertBulk) UpdateConditionType() *SopNodeUpsertBulk {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.UpdateConditionType()
+	})
+}
+
+// SetConditionList sets the "condition_list" field.
+func (u *SopNodeUpsertBulk) SetConditionList(v []custom_types.Condition) *SopNodeUpsertBulk {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.SetConditionList(v)
+	})
+}
+
+// UpdateConditionList sets the "condition_list" field to the value that was provided on create.
+func (u *SopNodeUpsertBulk) UpdateConditionList() *SopNodeUpsertBulk {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.UpdateConditionList()
+	})
+}
+
+// SetActionMessage sets the "action_message" field.
+func (u *SopNodeUpsertBulk) SetActionMessage(v []custom_types.Action) *SopNodeUpsertBulk {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.SetActionMessage(v)
+	})
+}
+
+// UpdateActionMessage sets the "action_message" field to the value that was provided on create.
+func (u *SopNodeUpsertBulk) UpdateActionMessage() *SopNodeUpsertBulk {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.UpdateActionMessage()
+	})
+}
+
+// SetActionLabel sets the "action_label" field.
+func (u *SopNodeUpsertBulk) SetActionLabel(v []int) *SopNodeUpsertBulk {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.SetActionLabel(v)
+	})
+}
+
+// UpdateActionLabel sets the "action_label" field to the value that was provided on create.
+func (u *SopNodeUpsertBulk) UpdateActionLabel() *SopNodeUpsertBulk {
+	return u.Update(func(s *SopNodeUpsert) {
+		s.UpdateActionLabel()
+	})
+}
+
+// Exec executes the query.
+func (u *SopNodeUpsertBulk) 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 SopNodeCreateBulk instead", i)
+		}
+	}
+	if len(u.create.conflict) == 0 {
+		return errors.New("ent: missing options for SopNodeCreateBulk.OnConflict")
+	}
+	return u.create.Exec(ctx)
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (u *SopNodeUpsertBulk) ExecX(ctx context.Context) {
+	if err := u.create.Exec(ctx); err != nil {
+		panic(err)
+	}
+}

+ 88 - 0
ent/sopnode_delete.go

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

+ 605 - 0
ent/sopnode_query.go

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

+ 755 - 0
ent/sopnode_update.go

@@ -0,0 +1,755 @@
+// Code generated by ent, DO NOT EDIT.
+
+package ent
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"time"
+	"wechat-api/ent/custom_types"
+	"wechat-api/ent/predicate"
+	"wechat-api/ent/sopnode"
+	"wechat-api/ent/sopstage"
+
+	"entgo.io/ent/dialect/sql"
+	"entgo.io/ent/dialect/sql/sqlgraph"
+	"entgo.io/ent/dialect/sql/sqljson"
+	"entgo.io/ent/schema/field"
+)
+
+// SopNodeUpdate is the builder for updating SopNode entities.
+type SopNodeUpdate struct {
+	config
+	hooks    []Hook
+	mutation *SopNodeMutation
+}
+
+// Where appends a list predicates to the SopNodeUpdate builder.
+func (snu *SopNodeUpdate) Where(ps ...predicate.SopNode) *SopNodeUpdate {
+	snu.mutation.Where(ps...)
+	return snu
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (snu *SopNodeUpdate) SetUpdatedAt(t time.Time) *SopNodeUpdate {
+	snu.mutation.SetUpdatedAt(t)
+	return snu
+}
+
+// SetStatus sets the "status" field.
+func (snu *SopNodeUpdate) SetStatus(u uint8) *SopNodeUpdate {
+	snu.mutation.ResetStatus()
+	snu.mutation.SetStatus(u)
+	return snu
+}
+
+// SetNillableStatus sets the "status" field if the given value is not nil.
+func (snu *SopNodeUpdate) SetNillableStatus(u *uint8) *SopNodeUpdate {
+	if u != nil {
+		snu.SetStatus(*u)
+	}
+	return snu
+}
+
+// AddStatus adds u to the "status" field.
+func (snu *SopNodeUpdate) AddStatus(u int8) *SopNodeUpdate {
+	snu.mutation.AddStatus(u)
+	return snu
+}
+
+// ClearStatus clears the value of the "status" field.
+func (snu *SopNodeUpdate) ClearStatus() *SopNodeUpdate {
+	snu.mutation.ClearStatus()
+	return snu
+}
+
+// SetDeletedAt sets the "deleted_at" field.
+func (snu *SopNodeUpdate) SetDeletedAt(t time.Time) *SopNodeUpdate {
+	snu.mutation.SetDeletedAt(t)
+	return snu
+}
+
+// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil.
+func (snu *SopNodeUpdate) SetNillableDeletedAt(t *time.Time) *SopNodeUpdate {
+	if t != nil {
+		snu.SetDeletedAt(*t)
+	}
+	return snu
+}
+
+// ClearDeletedAt clears the value of the "deleted_at" field.
+func (snu *SopNodeUpdate) ClearDeletedAt() *SopNodeUpdate {
+	snu.mutation.ClearDeletedAt()
+	return snu
+}
+
+// SetStageID sets the "stage_id" field.
+func (snu *SopNodeUpdate) SetStageID(u uint64) *SopNodeUpdate {
+	snu.mutation.SetStageID(u)
+	return snu
+}
+
+// SetNillableStageID sets the "stage_id" field if the given value is not nil.
+func (snu *SopNodeUpdate) SetNillableStageID(u *uint64) *SopNodeUpdate {
+	if u != nil {
+		snu.SetStageID(*u)
+	}
+	return snu
+}
+
+// SetParentID sets the "parent_id" field.
+func (snu *SopNodeUpdate) SetParentID(i int) *SopNodeUpdate {
+	snu.mutation.ResetParentID()
+	snu.mutation.SetParentID(i)
+	return snu
+}
+
+// SetNillableParentID sets the "parent_id" field if the given value is not nil.
+func (snu *SopNodeUpdate) SetNillableParentID(i *int) *SopNodeUpdate {
+	if i != nil {
+		snu.SetParentID(*i)
+	}
+	return snu
+}
+
+// AddParentID adds i to the "parent_id" field.
+func (snu *SopNodeUpdate) AddParentID(i int) *SopNodeUpdate {
+	snu.mutation.AddParentID(i)
+	return snu
+}
+
+// SetName sets the "name" field.
+func (snu *SopNodeUpdate) SetName(s string) *SopNodeUpdate {
+	snu.mutation.SetName(s)
+	return snu
+}
+
+// SetNillableName sets the "name" field if the given value is not nil.
+func (snu *SopNodeUpdate) SetNillableName(s *string) *SopNodeUpdate {
+	if s != nil {
+		snu.SetName(*s)
+	}
+	return snu
+}
+
+// SetConditionType sets the "condition_type" field.
+func (snu *SopNodeUpdate) SetConditionType(i int) *SopNodeUpdate {
+	snu.mutation.ResetConditionType()
+	snu.mutation.SetConditionType(i)
+	return snu
+}
+
+// SetNillableConditionType sets the "condition_type" field if the given value is not nil.
+func (snu *SopNodeUpdate) SetNillableConditionType(i *int) *SopNodeUpdate {
+	if i != nil {
+		snu.SetConditionType(*i)
+	}
+	return snu
+}
+
+// AddConditionType adds i to the "condition_type" field.
+func (snu *SopNodeUpdate) AddConditionType(i int) *SopNodeUpdate {
+	snu.mutation.AddConditionType(i)
+	return snu
+}
+
+// SetConditionList sets the "condition_list" field.
+func (snu *SopNodeUpdate) SetConditionList(ct []custom_types.Condition) *SopNodeUpdate {
+	snu.mutation.SetConditionList(ct)
+	return snu
+}
+
+// AppendConditionList appends ct to the "condition_list" field.
+func (snu *SopNodeUpdate) AppendConditionList(ct []custom_types.Condition) *SopNodeUpdate {
+	snu.mutation.AppendConditionList(ct)
+	return snu
+}
+
+// SetActionMessage sets the "action_message" field.
+func (snu *SopNodeUpdate) SetActionMessage(ct []custom_types.Action) *SopNodeUpdate {
+	snu.mutation.SetActionMessage(ct)
+	return snu
+}
+
+// AppendActionMessage appends ct to the "action_message" field.
+func (snu *SopNodeUpdate) AppendActionMessage(ct []custom_types.Action) *SopNodeUpdate {
+	snu.mutation.AppendActionMessage(ct)
+	return snu
+}
+
+// SetActionLabel sets the "action_label" field.
+func (snu *SopNodeUpdate) SetActionLabel(i []int) *SopNodeUpdate {
+	snu.mutation.SetActionLabel(i)
+	return snu
+}
+
+// AppendActionLabel appends i to the "action_label" field.
+func (snu *SopNodeUpdate) AppendActionLabel(i []int) *SopNodeUpdate {
+	snu.mutation.AppendActionLabel(i)
+	return snu
+}
+
+// SetSopStageID sets the "sop_stage" edge to the SopStage entity by ID.
+func (snu *SopNodeUpdate) SetSopStageID(id uint64) *SopNodeUpdate {
+	snu.mutation.SetSopStageID(id)
+	return snu
+}
+
+// SetSopStage sets the "sop_stage" edge to the SopStage entity.
+func (snu *SopNodeUpdate) SetSopStage(s *SopStage) *SopNodeUpdate {
+	return snu.SetSopStageID(s.ID)
+}
+
+// Mutation returns the SopNodeMutation object of the builder.
+func (snu *SopNodeUpdate) Mutation() *SopNodeMutation {
+	return snu.mutation
+}
+
+// ClearSopStage clears the "sop_stage" edge to the SopStage entity.
+func (snu *SopNodeUpdate) ClearSopStage() *SopNodeUpdate {
+	snu.mutation.ClearSopStage()
+	return snu
+}
+
+// Save executes the query and returns the number of nodes affected by the update operation.
+func (snu *SopNodeUpdate) Save(ctx context.Context) (int, error) {
+	if err := snu.defaults(); err != nil {
+		return 0, err
+	}
+	return withHooks(ctx, snu.sqlSave, snu.mutation, snu.hooks)
+}
+
+// SaveX is like Save, but panics if an error occurs.
+func (snu *SopNodeUpdate) SaveX(ctx context.Context) int {
+	affected, err := snu.Save(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return affected
+}
+
+// Exec executes the query.
+func (snu *SopNodeUpdate) Exec(ctx context.Context) error {
+	_, err := snu.Save(ctx)
+	return err
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (snu *SopNodeUpdate) ExecX(ctx context.Context) {
+	if err := snu.Exec(ctx); err != nil {
+		panic(err)
+	}
+}
+
+// defaults sets the default values of the builder before save.
+func (snu *SopNodeUpdate) defaults() error {
+	if _, ok := snu.mutation.UpdatedAt(); !ok {
+		if sopnode.UpdateDefaultUpdatedAt == nil {
+			return fmt.Errorf("ent: uninitialized sopnode.UpdateDefaultUpdatedAt (forgotten import ent/runtime?)")
+		}
+		v := sopnode.UpdateDefaultUpdatedAt()
+		snu.mutation.SetUpdatedAt(v)
+	}
+	return nil
+}
+
+// check runs all checks and user-defined validators on the builder.
+func (snu *SopNodeUpdate) check() error {
+	if _, ok := snu.mutation.SopStageID(); snu.mutation.SopStageCleared() && !ok {
+		return errors.New(`ent: clearing a required unique edge "SopNode.sop_stage"`)
+	}
+	return nil
+}
+
+func (snu *SopNodeUpdate) sqlSave(ctx context.Context) (n int, err error) {
+	if err := snu.check(); err != nil {
+		return n, err
+	}
+	_spec := sqlgraph.NewUpdateSpec(sopnode.Table, sopnode.Columns, sqlgraph.NewFieldSpec(sopnode.FieldID, field.TypeUint64))
+	if ps := snu.mutation.predicates; len(ps) > 0 {
+		_spec.Predicate = func(selector *sql.Selector) {
+			for i := range ps {
+				ps[i](selector)
+			}
+		}
+	}
+	if value, ok := snu.mutation.UpdatedAt(); ok {
+		_spec.SetField(sopnode.FieldUpdatedAt, field.TypeTime, value)
+	}
+	if value, ok := snu.mutation.Status(); ok {
+		_spec.SetField(sopnode.FieldStatus, field.TypeUint8, value)
+	}
+	if value, ok := snu.mutation.AddedStatus(); ok {
+		_spec.AddField(sopnode.FieldStatus, field.TypeUint8, value)
+	}
+	if snu.mutation.StatusCleared() {
+		_spec.ClearField(sopnode.FieldStatus, field.TypeUint8)
+	}
+	if value, ok := snu.mutation.DeletedAt(); ok {
+		_spec.SetField(sopnode.FieldDeletedAt, field.TypeTime, value)
+	}
+	if snu.mutation.DeletedAtCleared() {
+		_spec.ClearField(sopnode.FieldDeletedAt, field.TypeTime)
+	}
+	if value, ok := snu.mutation.ParentID(); ok {
+		_spec.SetField(sopnode.FieldParentID, field.TypeInt, value)
+	}
+	if value, ok := snu.mutation.AddedParentID(); ok {
+		_spec.AddField(sopnode.FieldParentID, field.TypeInt, value)
+	}
+	if value, ok := snu.mutation.Name(); ok {
+		_spec.SetField(sopnode.FieldName, field.TypeString, value)
+	}
+	if value, ok := snu.mutation.ConditionType(); ok {
+		_spec.SetField(sopnode.FieldConditionType, field.TypeInt, value)
+	}
+	if value, ok := snu.mutation.AddedConditionType(); ok {
+		_spec.AddField(sopnode.FieldConditionType, field.TypeInt, value)
+	}
+	if value, ok := snu.mutation.ConditionList(); ok {
+		_spec.SetField(sopnode.FieldConditionList, field.TypeJSON, value)
+	}
+	if value, ok := snu.mutation.AppendedConditionList(); ok {
+		_spec.AddModifier(func(u *sql.UpdateBuilder) {
+			sqljson.Append(u, sopnode.FieldConditionList, value)
+		})
+	}
+	if value, ok := snu.mutation.ActionMessage(); ok {
+		_spec.SetField(sopnode.FieldActionMessage, field.TypeJSON, value)
+	}
+	if value, ok := snu.mutation.AppendedActionMessage(); ok {
+		_spec.AddModifier(func(u *sql.UpdateBuilder) {
+			sqljson.Append(u, sopnode.FieldActionMessage, value)
+		})
+	}
+	if value, ok := snu.mutation.ActionLabel(); ok {
+		_spec.SetField(sopnode.FieldActionLabel, field.TypeJSON, value)
+	}
+	if value, ok := snu.mutation.AppendedActionLabel(); ok {
+		_spec.AddModifier(func(u *sql.UpdateBuilder) {
+			sqljson.Append(u, sopnode.FieldActionLabel, value)
+		})
+	}
+	if snu.mutation.SopStageCleared() {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.M2O,
+			Inverse: true,
+			Table:   sopnode.SopStageTable,
+			Columns: []string{sopnode.SopStageColumn},
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: sqlgraph.NewFieldSpec(sopstage.FieldID, field.TypeUint64),
+			},
+		}
+		_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
+	}
+	if nodes := snu.mutation.SopStageIDs(); len(nodes) > 0 {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.M2O,
+			Inverse: true,
+			Table:   sopnode.SopStageTable,
+			Columns: []string{sopnode.SopStageColumn},
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: sqlgraph.NewFieldSpec(sopstage.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, snu.driver, _spec); err != nil {
+		if _, ok := err.(*sqlgraph.NotFoundError); ok {
+			err = &NotFoundError{sopnode.Label}
+		} else if sqlgraph.IsConstraintError(err) {
+			err = &ConstraintError{msg: err.Error(), wrap: err}
+		}
+		return 0, err
+	}
+	snu.mutation.done = true
+	return n, nil
+}
+
+// SopNodeUpdateOne is the builder for updating a single SopNode entity.
+type SopNodeUpdateOne struct {
+	config
+	fields   []string
+	hooks    []Hook
+	mutation *SopNodeMutation
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (snuo *SopNodeUpdateOne) SetUpdatedAt(t time.Time) *SopNodeUpdateOne {
+	snuo.mutation.SetUpdatedAt(t)
+	return snuo
+}
+
+// SetStatus sets the "status" field.
+func (snuo *SopNodeUpdateOne) SetStatus(u uint8) *SopNodeUpdateOne {
+	snuo.mutation.ResetStatus()
+	snuo.mutation.SetStatus(u)
+	return snuo
+}
+
+// SetNillableStatus sets the "status" field if the given value is not nil.
+func (snuo *SopNodeUpdateOne) SetNillableStatus(u *uint8) *SopNodeUpdateOne {
+	if u != nil {
+		snuo.SetStatus(*u)
+	}
+	return snuo
+}
+
+// AddStatus adds u to the "status" field.
+func (snuo *SopNodeUpdateOne) AddStatus(u int8) *SopNodeUpdateOne {
+	snuo.mutation.AddStatus(u)
+	return snuo
+}
+
+// ClearStatus clears the value of the "status" field.
+func (snuo *SopNodeUpdateOne) ClearStatus() *SopNodeUpdateOne {
+	snuo.mutation.ClearStatus()
+	return snuo
+}
+
+// SetDeletedAt sets the "deleted_at" field.
+func (snuo *SopNodeUpdateOne) SetDeletedAt(t time.Time) *SopNodeUpdateOne {
+	snuo.mutation.SetDeletedAt(t)
+	return snuo
+}
+
+// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil.
+func (snuo *SopNodeUpdateOne) SetNillableDeletedAt(t *time.Time) *SopNodeUpdateOne {
+	if t != nil {
+		snuo.SetDeletedAt(*t)
+	}
+	return snuo
+}
+
+// ClearDeletedAt clears the value of the "deleted_at" field.
+func (snuo *SopNodeUpdateOne) ClearDeletedAt() *SopNodeUpdateOne {
+	snuo.mutation.ClearDeletedAt()
+	return snuo
+}
+
+// SetStageID sets the "stage_id" field.
+func (snuo *SopNodeUpdateOne) SetStageID(u uint64) *SopNodeUpdateOne {
+	snuo.mutation.SetStageID(u)
+	return snuo
+}
+
+// SetNillableStageID sets the "stage_id" field if the given value is not nil.
+func (snuo *SopNodeUpdateOne) SetNillableStageID(u *uint64) *SopNodeUpdateOne {
+	if u != nil {
+		snuo.SetStageID(*u)
+	}
+	return snuo
+}
+
+// SetParentID sets the "parent_id" field.
+func (snuo *SopNodeUpdateOne) SetParentID(i int) *SopNodeUpdateOne {
+	snuo.mutation.ResetParentID()
+	snuo.mutation.SetParentID(i)
+	return snuo
+}
+
+// SetNillableParentID sets the "parent_id" field if the given value is not nil.
+func (snuo *SopNodeUpdateOne) SetNillableParentID(i *int) *SopNodeUpdateOne {
+	if i != nil {
+		snuo.SetParentID(*i)
+	}
+	return snuo
+}
+
+// AddParentID adds i to the "parent_id" field.
+func (snuo *SopNodeUpdateOne) AddParentID(i int) *SopNodeUpdateOne {
+	snuo.mutation.AddParentID(i)
+	return snuo
+}
+
+// SetName sets the "name" field.
+func (snuo *SopNodeUpdateOne) SetName(s string) *SopNodeUpdateOne {
+	snuo.mutation.SetName(s)
+	return snuo
+}
+
+// SetNillableName sets the "name" field if the given value is not nil.
+func (snuo *SopNodeUpdateOne) SetNillableName(s *string) *SopNodeUpdateOne {
+	if s != nil {
+		snuo.SetName(*s)
+	}
+	return snuo
+}
+
+// SetConditionType sets the "condition_type" field.
+func (snuo *SopNodeUpdateOne) SetConditionType(i int) *SopNodeUpdateOne {
+	snuo.mutation.ResetConditionType()
+	snuo.mutation.SetConditionType(i)
+	return snuo
+}
+
+// SetNillableConditionType sets the "condition_type" field if the given value is not nil.
+func (snuo *SopNodeUpdateOne) SetNillableConditionType(i *int) *SopNodeUpdateOne {
+	if i != nil {
+		snuo.SetConditionType(*i)
+	}
+	return snuo
+}
+
+// AddConditionType adds i to the "condition_type" field.
+func (snuo *SopNodeUpdateOne) AddConditionType(i int) *SopNodeUpdateOne {
+	snuo.mutation.AddConditionType(i)
+	return snuo
+}
+
+// SetConditionList sets the "condition_list" field.
+func (snuo *SopNodeUpdateOne) SetConditionList(ct []custom_types.Condition) *SopNodeUpdateOne {
+	snuo.mutation.SetConditionList(ct)
+	return snuo
+}
+
+// AppendConditionList appends ct to the "condition_list" field.
+func (snuo *SopNodeUpdateOne) AppendConditionList(ct []custom_types.Condition) *SopNodeUpdateOne {
+	snuo.mutation.AppendConditionList(ct)
+	return snuo
+}
+
+// SetActionMessage sets the "action_message" field.
+func (snuo *SopNodeUpdateOne) SetActionMessage(ct []custom_types.Action) *SopNodeUpdateOne {
+	snuo.mutation.SetActionMessage(ct)
+	return snuo
+}
+
+// AppendActionMessage appends ct to the "action_message" field.
+func (snuo *SopNodeUpdateOne) AppendActionMessage(ct []custom_types.Action) *SopNodeUpdateOne {
+	snuo.mutation.AppendActionMessage(ct)
+	return snuo
+}
+
+// SetActionLabel sets the "action_label" field.
+func (snuo *SopNodeUpdateOne) SetActionLabel(i []int) *SopNodeUpdateOne {
+	snuo.mutation.SetActionLabel(i)
+	return snuo
+}
+
+// AppendActionLabel appends i to the "action_label" field.
+func (snuo *SopNodeUpdateOne) AppendActionLabel(i []int) *SopNodeUpdateOne {
+	snuo.mutation.AppendActionLabel(i)
+	return snuo
+}
+
+// SetSopStageID sets the "sop_stage" edge to the SopStage entity by ID.
+func (snuo *SopNodeUpdateOne) SetSopStageID(id uint64) *SopNodeUpdateOne {
+	snuo.mutation.SetSopStageID(id)
+	return snuo
+}
+
+// SetSopStage sets the "sop_stage" edge to the SopStage entity.
+func (snuo *SopNodeUpdateOne) SetSopStage(s *SopStage) *SopNodeUpdateOne {
+	return snuo.SetSopStageID(s.ID)
+}
+
+// Mutation returns the SopNodeMutation object of the builder.
+func (snuo *SopNodeUpdateOne) Mutation() *SopNodeMutation {
+	return snuo.mutation
+}
+
+// ClearSopStage clears the "sop_stage" edge to the SopStage entity.
+func (snuo *SopNodeUpdateOne) ClearSopStage() *SopNodeUpdateOne {
+	snuo.mutation.ClearSopStage()
+	return snuo
+}
+
+// Where appends a list predicates to the SopNodeUpdate builder.
+func (snuo *SopNodeUpdateOne) Where(ps ...predicate.SopNode) *SopNodeUpdateOne {
+	snuo.mutation.Where(ps...)
+	return snuo
+}
+
+// Select allows selecting one or more fields (columns) of the returned entity.
+// The default is selecting all fields defined in the entity schema.
+func (snuo *SopNodeUpdateOne) Select(field string, fields ...string) *SopNodeUpdateOne {
+	snuo.fields = append([]string{field}, fields...)
+	return snuo
+}
+
+// Save executes the query and returns the updated SopNode entity.
+func (snuo *SopNodeUpdateOne) Save(ctx context.Context) (*SopNode, error) {
+	if err := snuo.defaults(); err != nil {
+		return nil, err
+	}
+	return withHooks(ctx, snuo.sqlSave, snuo.mutation, snuo.hooks)
+}
+
+// SaveX is like Save, but panics if an error occurs.
+func (snuo *SopNodeUpdateOne) SaveX(ctx context.Context) *SopNode {
+	node, err := snuo.Save(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return node
+}
+
+// Exec executes the query on the entity.
+func (snuo *SopNodeUpdateOne) Exec(ctx context.Context) error {
+	_, err := snuo.Save(ctx)
+	return err
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (snuo *SopNodeUpdateOne) ExecX(ctx context.Context) {
+	if err := snuo.Exec(ctx); err != nil {
+		panic(err)
+	}
+}
+
+// defaults sets the default values of the builder before save.
+func (snuo *SopNodeUpdateOne) defaults() error {
+	if _, ok := snuo.mutation.UpdatedAt(); !ok {
+		if sopnode.UpdateDefaultUpdatedAt == nil {
+			return fmt.Errorf("ent: uninitialized sopnode.UpdateDefaultUpdatedAt (forgotten import ent/runtime?)")
+		}
+		v := sopnode.UpdateDefaultUpdatedAt()
+		snuo.mutation.SetUpdatedAt(v)
+	}
+	return nil
+}
+
+// check runs all checks and user-defined validators on the builder.
+func (snuo *SopNodeUpdateOne) check() error {
+	if _, ok := snuo.mutation.SopStageID(); snuo.mutation.SopStageCleared() && !ok {
+		return errors.New(`ent: clearing a required unique edge "SopNode.sop_stage"`)
+	}
+	return nil
+}
+
+func (snuo *SopNodeUpdateOne) sqlSave(ctx context.Context) (_node *SopNode, err error) {
+	if err := snuo.check(); err != nil {
+		return _node, err
+	}
+	_spec := sqlgraph.NewUpdateSpec(sopnode.Table, sopnode.Columns, sqlgraph.NewFieldSpec(sopnode.FieldID, field.TypeUint64))
+	id, ok := snuo.mutation.ID()
+	if !ok {
+		return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "SopNode.id" for update`)}
+	}
+	_spec.Node.ID.Value = id
+	if fields := snuo.fields; len(fields) > 0 {
+		_spec.Node.Columns = make([]string, 0, len(fields))
+		_spec.Node.Columns = append(_spec.Node.Columns, sopnode.FieldID)
+		for _, f := range fields {
+			if !sopnode.ValidColumn(f) {
+				return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
+			}
+			if f != sopnode.FieldID {
+				_spec.Node.Columns = append(_spec.Node.Columns, f)
+			}
+		}
+	}
+	if ps := snuo.mutation.predicates; len(ps) > 0 {
+		_spec.Predicate = func(selector *sql.Selector) {
+			for i := range ps {
+				ps[i](selector)
+			}
+		}
+	}
+	if value, ok := snuo.mutation.UpdatedAt(); ok {
+		_spec.SetField(sopnode.FieldUpdatedAt, field.TypeTime, value)
+	}
+	if value, ok := snuo.mutation.Status(); ok {
+		_spec.SetField(sopnode.FieldStatus, field.TypeUint8, value)
+	}
+	if value, ok := snuo.mutation.AddedStatus(); ok {
+		_spec.AddField(sopnode.FieldStatus, field.TypeUint8, value)
+	}
+	if snuo.mutation.StatusCleared() {
+		_spec.ClearField(sopnode.FieldStatus, field.TypeUint8)
+	}
+	if value, ok := snuo.mutation.DeletedAt(); ok {
+		_spec.SetField(sopnode.FieldDeletedAt, field.TypeTime, value)
+	}
+	if snuo.mutation.DeletedAtCleared() {
+		_spec.ClearField(sopnode.FieldDeletedAt, field.TypeTime)
+	}
+	if value, ok := snuo.mutation.ParentID(); ok {
+		_spec.SetField(sopnode.FieldParentID, field.TypeInt, value)
+	}
+	if value, ok := snuo.mutation.AddedParentID(); ok {
+		_spec.AddField(sopnode.FieldParentID, field.TypeInt, value)
+	}
+	if value, ok := snuo.mutation.Name(); ok {
+		_spec.SetField(sopnode.FieldName, field.TypeString, value)
+	}
+	if value, ok := snuo.mutation.ConditionType(); ok {
+		_spec.SetField(sopnode.FieldConditionType, field.TypeInt, value)
+	}
+	if value, ok := snuo.mutation.AddedConditionType(); ok {
+		_spec.AddField(sopnode.FieldConditionType, field.TypeInt, value)
+	}
+	if value, ok := snuo.mutation.ConditionList(); ok {
+		_spec.SetField(sopnode.FieldConditionList, field.TypeJSON, value)
+	}
+	if value, ok := snuo.mutation.AppendedConditionList(); ok {
+		_spec.AddModifier(func(u *sql.UpdateBuilder) {
+			sqljson.Append(u, sopnode.FieldConditionList, value)
+		})
+	}
+	if value, ok := snuo.mutation.ActionMessage(); ok {
+		_spec.SetField(sopnode.FieldActionMessage, field.TypeJSON, value)
+	}
+	if value, ok := snuo.mutation.AppendedActionMessage(); ok {
+		_spec.AddModifier(func(u *sql.UpdateBuilder) {
+			sqljson.Append(u, sopnode.FieldActionMessage, value)
+		})
+	}
+	if value, ok := snuo.mutation.ActionLabel(); ok {
+		_spec.SetField(sopnode.FieldActionLabel, field.TypeJSON, value)
+	}
+	if value, ok := snuo.mutation.AppendedActionLabel(); ok {
+		_spec.AddModifier(func(u *sql.UpdateBuilder) {
+			sqljson.Append(u, sopnode.FieldActionLabel, value)
+		})
+	}
+	if snuo.mutation.SopStageCleared() {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.M2O,
+			Inverse: true,
+			Table:   sopnode.SopStageTable,
+			Columns: []string{sopnode.SopStageColumn},
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: sqlgraph.NewFieldSpec(sopstage.FieldID, field.TypeUint64),
+			},
+		}
+		_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
+	}
+	if nodes := snuo.mutation.SopStageIDs(); len(nodes) > 0 {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.M2O,
+			Inverse: true,
+			Table:   sopnode.SopStageTable,
+			Columns: []string{sopnode.SopStageColumn},
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: sqlgraph.NewFieldSpec(sopstage.FieldID, field.TypeUint64),
+			},
+		}
+		for _, k := range nodes {
+			edge.Target.Nodes = append(edge.Target.Nodes, k)
+		}
+		_spec.Edges.Add = append(_spec.Edges.Add, edge)
+	}
+	_node = &SopNode{config: snuo.config}
+	_spec.Assign = _node.assignValues
+	_spec.ScanValues = _node.scanValues
+	if err = sqlgraph.UpdateNode(ctx, snuo.driver, _spec); err != nil {
+		if _, ok := err.(*sqlgraph.NotFoundError); ok {
+			err = &NotFoundError{sopnode.Label}
+		} else if sqlgraph.IsConstraintError(err) {
+			err = &ConstraintError{msg: err.Error(), wrap: err}
+		}
+		return nil, err
+	}
+	snuo.mutation.done = true
+	return _node, nil
+}

+ 282 - 0
ent/sopstage.go

@@ -0,0 +1,282 @@
+// Code generated by ent, DO NOT EDIT.
+
+package ent
+
+import (
+	"encoding/json"
+	"fmt"
+	"strings"
+	"time"
+	"wechat-api/ent/custom_types"
+	"wechat-api/ent/sopstage"
+	"wechat-api/ent/soptask"
+
+	"entgo.io/ent"
+	"entgo.io/ent/dialect/sql"
+)
+
+// SopStage is the model entity for the SopStage schema.
+type SopStage 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"`
+	// Status 1: normal 2: ban | 状态 1 正常 2 禁用
+	Status uint8 `json:"status,omitempty"`
+	// Delete Time | 删除日期
+	DeletedAt time.Time `json:"deleted_at,omitempty"`
+	// SOP 任务 ID
+	TaskID uint64 `json:"task_id,omitempty"`
+	// 阶段名称
+	Name string `json:"name,omitempty"`
+	// 客群筛选条件类型  1 按标签筛选 2 按客户基本信息筛选
+	ConditionType int `json:"condition_type,omitempty"`
+	// 筛选条件关系  1 满足所有条件(and) 2 满足任意条件(or)
+	ConditionOperator int `json:"condition_operator,omitempty"`
+	// 筛选条件列表
+	ConditionList []custom_types.Condition `json:"condition_list,omitempty"`
+	// 命中后发送的消息内容
+	ActionMessage []custom_types.Action `json:"action_message,omitempty"`
+	// 命中后需要打的标签
+	ActionLabel []int `json:"action_label,omitempty"`
+	// 阶段顺序
+	IndexSort int `json:"index_sort,omitempty"`
+	// Edges holds the relations/edges for other nodes in the graph.
+	// The values are being populated by the SopStageQuery when eager-loading is set.
+	Edges        SopStageEdges `json:"edges"`
+	selectValues sql.SelectValues
+}
+
+// SopStageEdges holds the relations/edges for other nodes in the graph.
+type SopStageEdges struct {
+	// SopTask holds the value of the sop_task edge.
+	SopTask *SopTask `json:"sop_task,omitempty"`
+	// StageNodes holds the value of the stage_nodes edge.
+	StageNodes []*SopNode `json:"stage_nodes,omitempty"`
+	// loadedTypes holds the information for reporting if a
+	// type was loaded (or requested) in eager-loading or not.
+	loadedTypes [2]bool
+}
+
+// SopTaskOrErr returns the SopTask value or an error if the edge
+// was not loaded in eager-loading, or loaded but was not found.
+func (e SopStageEdges) SopTaskOrErr() (*SopTask, error) {
+	if e.SopTask != nil {
+		return e.SopTask, nil
+	} else if e.loadedTypes[0] {
+		return nil, &NotFoundError{label: soptask.Label}
+	}
+	return nil, &NotLoadedError{edge: "sop_task"}
+}
+
+// StageNodesOrErr returns the StageNodes value or an error if the edge
+// was not loaded in eager-loading.
+func (e SopStageEdges) StageNodesOrErr() ([]*SopNode, error) {
+	if e.loadedTypes[1] {
+		return e.StageNodes, nil
+	}
+	return nil, &NotLoadedError{edge: "stage_nodes"}
+}
+
+// scanValues returns the types for scanning values from sql.Rows.
+func (*SopStage) scanValues(columns []string) ([]any, error) {
+	values := make([]any, len(columns))
+	for i := range columns {
+		switch columns[i] {
+		case sopstage.FieldConditionList, sopstage.FieldActionMessage, sopstage.FieldActionLabel:
+			values[i] = new([]byte)
+		case sopstage.FieldID, sopstage.FieldStatus, sopstage.FieldTaskID, sopstage.FieldConditionType, sopstage.FieldConditionOperator, sopstage.FieldIndexSort:
+			values[i] = new(sql.NullInt64)
+		case sopstage.FieldName:
+			values[i] = new(sql.NullString)
+		case sopstage.FieldCreatedAt, sopstage.FieldUpdatedAt, sopstage.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 SopStage fields.
+func (ss *SopStage) 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 sopstage.FieldID:
+			value, ok := values[i].(*sql.NullInt64)
+			if !ok {
+				return fmt.Errorf("unexpected type %T for field id", value)
+			}
+			ss.ID = uint64(value.Int64)
+		case sopstage.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 {
+				ss.CreatedAt = value.Time
+			}
+		case sopstage.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 {
+				ss.UpdatedAt = value.Time
+			}
+		case sopstage.FieldStatus:
+			if value, ok := values[i].(*sql.NullInt64); !ok {
+				return fmt.Errorf("unexpected type %T for field status", values[i])
+			} else if value.Valid {
+				ss.Status = uint8(value.Int64)
+			}
+		case sopstage.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 {
+				ss.DeletedAt = value.Time
+			}
+		case sopstage.FieldTaskID:
+			if value, ok := values[i].(*sql.NullInt64); !ok {
+				return fmt.Errorf("unexpected type %T for field task_id", values[i])
+			} else if value.Valid {
+				ss.TaskID = uint64(value.Int64)
+			}
+		case sopstage.FieldName:
+			if value, ok := values[i].(*sql.NullString); !ok {
+				return fmt.Errorf("unexpected type %T for field name", values[i])
+			} else if value.Valid {
+				ss.Name = value.String
+			}
+		case sopstage.FieldConditionType:
+			if value, ok := values[i].(*sql.NullInt64); !ok {
+				return fmt.Errorf("unexpected type %T for field condition_type", values[i])
+			} else if value.Valid {
+				ss.ConditionType = int(value.Int64)
+			}
+		case sopstage.FieldConditionOperator:
+			if value, ok := values[i].(*sql.NullInt64); !ok {
+				return fmt.Errorf("unexpected type %T for field condition_operator", values[i])
+			} else if value.Valid {
+				ss.ConditionOperator = int(value.Int64)
+			}
+		case sopstage.FieldConditionList:
+			if value, ok := values[i].(*[]byte); !ok {
+				return fmt.Errorf("unexpected type %T for field condition_list", values[i])
+			} else if value != nil && len(*value) > 0 {
+				if err := json.Unmarshal(*value, &ss.ConditionList); err != nil {
+					return fmt.Errorf("unmarshal field condition_list: %w", err)
+				}
+			}
+		case sopstage.FieldActionMessage:
+			if value, ok := values[i].(*[]byte); !ok {
+				return fmt.Errorf("unexpected type %T for field action_message", values[i])
+			} else if value != nil && len(*value) > 0 {
+				if err := json.Unmarshal(*value, &ss.ActionMessage); err != nil {
+					return fmt.Errorf("unmarshal field action_message: %w", err)
+				}
+			}
+		case sopstage.FieldActionLabel:
+			if value, ok := values[i].(*[]byte); !ok {
+				return fmt.Errorf("unexpected type %T for field action_label", values[i])
+			} else if value != nil && len(*value) > 0 {
+				if err := json.Unmarshal(*value, &ss.ActionLabel); err != nil {
+					return fmt.Errorf("unmarshal field action_label: %w", err)
+				}
+			}
+		case sopstage.FieldIndexSort:
+			if value, ok := values[i].(*sql.NullInt64); !ok {
+				return fmt.Errorf("unexpected type %T for field index_sort", values[i])
+			} else if value.Valid {
+				ss.IndexSort = int(value.Int64)
+			}
+		default:
+			ss.selectValues.Set(columns[i], values[i])
+		}
+	}
+	return nil
+}
+
+// Value returns the ent.Value that was dynamically selected and assigned to the SopStage.
+// This includes values selected through modifiers, order, etc.
+func (ss *SopStage) Value(name string) (ent.Value, error) {
+	return ss.selectValues.Get(name)
+}
+
+// QuerySopTask queries the "sop_task" edge of the SopStage entity.
+func (ss *SopStage) QuerySopTask() *SopTaskQuery {
+	return NewSopStageClient(ss.config).QuerySopTask(ss)
+}
+
+// QueryStageNodes queries the "stage_nodes" edge of the SopStage entity.
+func (ss *SopStage) QueryStageNodes() *SopNodeQuery {
+	return NewSopStageClient(ss.config).QueryStageNodes(ss)
+}
+
+// Update returns a builder for updating this SopStage.
+// Note that you need to call SopStage.Unwrap() before calling this method if this SopStage
+// was returned from a transaction, and the transaction was committed or rolled back.
+func (ss *SopStage) Update() *SopStageUpdateOne {
+	return NewSopStageClient(ss.config).UpdateOne(ss)
+}
+
+// Unwrap unwraps the SopStage 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 (ss *SopStage) Unwrap() *SopStage {
+	_tx, ok := ss.config.driver.(*txDriver)
+	if !ok {
+		panic("ent: SopStage is not a transactional entity")
+	}
+	ss.config.driver = _tx.drv
+	return ss
+}
+
+// String implements the fmt.Stringer.
+func (ss *SopStage) String() string {
+	var builder strings.Builder
+	builder.WriteString("SopStage(")
+	builder.WriteString(fmt.Sprintf("id=%v, ", ss.ID))
+	builder.WriteString("created_at=")
+	builder.WriteString(ss.CreatedAt.Format(time.ANSIC))
+	builder.WriteString(", ")
+	builder.WriteString("updated_at=")
+	builder.WriteString(ss.UpdatedAt.Format(time.ANSIC))
+	builder.WriteString(", ")
+	builder.WriteString("status=")
+	builder.WriteString(fmt.Sprintf("%v", ss.Status))
+	builder.WriteString(", ")
+	builder.WriteString("deleted_at=")
+	builder.WriteString(ss.DeletedAt.Format(time.ANSIC))
+	builder.WriteString(", ")
+	builder.WriteString("task_id=")
+	builder.WriteString(fmt.Sprintf("%v", ss.TaskID))
+	builder.WriteString(", ")
+	builder.WriteString("name=")
+	builder.WriteString(ss.Name)
+	builder.WriteString(", ")
+	builder.WriteString("condition_type=")
+	builder.WriteString(fmt.Sprintf("%v", ss.ConditionType))
+	builder.WriteString(", ")
+	builder.WriteString("condition_operator=")
+	builder.WriteString(fmt.Sprintf("%v", ss.ConditionOperator))
+	builder.WriteString(", ")
+	builder.WriteString("condition_list=")
+	builder.WriteString(fmt.Sprintf("%v", ss.ConditionList))
+	builder.WriteString(", ")
+	builder.WriteString("action_message=")
+	builder.WriteString(fmt.Sprintf("%v", ss.ActionMessage))
+	builder.WriteString(", ")
+	builder.WriteString("action_label=")
+	builder.WriteString(fmt.Sprintf("%v", ss.ActionLabel))
+	builder.WriteString(", ")
+	builder.WriteString("index_sort=")
+	builder.WriteString(fmt.Sprintf("%v", ss.IndexSort))
+	builder.WriteByte(')')
+	return builder.String()
+}
+
+// SopStages is a parsable slice of SopStage.
+type SopStages []*SopStage

+ 203 - 0
ent/sopstage/sopstage.go

@@ -0,0 +1,203 @@
+// Code generated by ent, DO NOT EDIT.
+
+package sopstage
+
+import (
+	"time"
+
+	"entgo.io/ent"
+	"entgo.io/ent/dialect/sql"
+	"entgo.io/ent/dialect/sql/sqlgraph"
+)
+
+const (
+	// Label holds the string label denoting the sopstage type in the database.
+	Label = "sop_stage"
+	// 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"
+	// FieldStatus holds the string denoting the status field in the database.
+	FieldStatus = "status"
+	// FieldDeletedAt holds the string denoting the deleted_at field in the database.
+	FieldDeletedAt = "deleted_at"
+	// FieldTaskID holds the string denoting the task_id field in the database.
+	FieldTaskID = "task_id"
+	// FieldName holds the string denoting the name field in the database.
+	FieldName = "name"
+	// FieldConditionType holds the string denoting the condition_type field in the database.
+	FieldConditionType = "condition_type"
+	// FieldConditionOperator holds the string denoting the condition_operator field in the database.
+	FieldConditionOperator = "condition_operator"
+	// FieldConditionList holds the string denoting the condition_list field in the database.
+	FieldConditionList = "condition_list"
+	// FieldActionMessage holds the string denoting the action_message field in the database.
+	FieldActionMessage = "action_message"
+	// FieldActionLabel holds the string denoting the action_label field in the database.
+	FieldActionLabel = "action_label"
+	// FieldIndexSort holds the string denoting the index_sort field in the database.
+	FieldIndexSort = "index_sort"
+	// EdgeSopTask holds the string denoting the sop_task edge name in mutations.
+	EdgeSopTask = "sop_task"
+	// EdgeStageNodes holds the string denoting the stage_nodes edge name in mutations.
+	EdgeStageNodes = "stage_nodes"
+	// Table holds the table name of the sopstage in the database.
+	Table = "sop_stage"
+	// SopTaskTable is the table that holds the sop_task relation/edge.
+	SopTaskTable = "sop_stage"
+	// SopTaskInverseTable is the table name for the SopTask entity.
+	// It exists in this package in order to avoid circular dependency with the "soptask" package.
+	SopTaskInverseTable = "sop_task"
+	// SopTaskColumn is the table column denoting the sop_task relation/edge.
+	SopTaskColumn = "task_id"
+	// StageNodesTable is the table that holds the stage_nodes relation/edge.
+	StageNodesTable = "sop_node"
+	// StageNodesInverseTable is the table name for the SopNode entity.
+	// It exists in this package in order to avoid circular dependency with the "sopnode" package.
+	StageNodesInverseTable = "sop_node"
+	// StageNodesColumn is the table column denoting the stage_nodes relation/edge.
+	StageNodesColumn = "stage_id"
+)
+
+// Columns holds all SQL columns for sopstage fields.
+var Columns = []string{
+	FieldID,
+	FieldCreatedAt,
+	FieldUpdatedAt,
+	FieldStatus,
+	FieldDeletedAt,
+	FieldTaskID,
+	FieldName,
+	FieldConditionType,
+	FieldConditionOperator,
+	FieldConditionList,
+	FieldActionMessage,
+	FieldActionLabel,
+	FieldIndexSort,
+}
+
+// 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
+	// DefaultStatus holds the default value on creation for the "status" field.
+	DefaultStatus uint8
+	// DefaultName holds the default value on creation for the "name" field.
+	DefaultName string
+	// DefaultConditionType holds the default value on creation for the "condition_type" field.
+	DefaultConditionType int
+	// DefaultConditionOperator holds the default value on creation for the "condition_operator" field.
+	DefaultConditionOperator int
+	// DefaultIndexSort holds the default value on creation for the "index_sort" field.
+	DefaultIndexSort int
+)
+
+// OrderOption defines the ordering options for the SopStage 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()
+}
+
+// ByStatus orders the results by the status field.
+func ByStatus(opts ...sql.OrderTermOption) OrderOption {
+	return sql.OrderByField(FieldStatus, opts...).ToFunc()
+}
+
+// ByDeletedAt orders the results by the deleted_at field.
+func ByDeletedAt(opts ...sql.OrderTermOption) OrderOption {
+	return sql.OrderByField(FieldDeletedAt, opts...).ToFunc()
+}
+
+// ByTaskID orders the results by the task_id field.
+func ByTaskID(opts ...sql.OrderTermOption) OrderOption {
+	return sql.OrderByField(FieldTaskID, opts...).ToFunc()
+}
+
+// ByName orders the results by the name field.
+func ByName(opts ...sql.OrderTermOption) OrderOption {
+	return sql.OrderByField(FieldName, opts...).ToFunc()
+}
+
+// ByConditionType orders the results by the condition_type field.
+func ByConditionType(opts ...sql.OrderTermOption) OrderOption {
+	return sql.OrderByField(FieldConditionType, opts...).ToFunc()
+}
+
+// ByConditionOperator orders the results by the condition_operator field.
+func ByConditionOperator(opts ...sql.OrderTermOption) OrderOption {
+	return sql.OrderByField(FieldConditionOperator, opts...).ToFunc()
+}
+
+// ByIndexSort orders the results by the index_sort field.
+func ByIndexSort(opts ...sql.OrderTermOption) OrderOption {
+	return sql.OrderByField(FieldIndexSort, opts...).ToFunc()
+}
+
+// BySopTaskField orders the results by sop_task field.
+func BySopTaskField(field string, opts ...sql.OrderTermOption) OrderOption {
+	return func(s *sql.Selector) {
+		sqlgraph.OrderByNeighborTerms(s, newSopTaskStep(), sql.OrderByField(field, opts...))
+	}
+}
+
+// ByStageNodesCount orders the results by stage_nodes count.
+func ByStageNodesCount(opts ...sql.OrderTermOption) OrderOption {
+	return func(s *sql.Selector) {
+		sqlgraph.OrderByNeighborsCount(s, newStageNodesStep(), opts...)
+	}
+}
+
+// ByStageNodes orders the results by stage_nodes terms.
+func ByStageNodes(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption {
+	return func(s *sql.Selector) {
+		sqlgraph.OrderByNeighborTerms(s, newStageNodesStep(), append([]sql.OrderTerm{term}, terms...)...)
+	}
+}
+func newSopTaskStep() *sqlgraph.Step {
+	return sqlgraph.NewStep(
+		sqlgraph.From(Table, FieldID),
+		sqlgraph.To(SopTaskInverseTable, FieldID),
+		sqlgraph.Edge(sqlgraph.M2O, true, SopTaskTable, SopTaskColumn),
+	)
+}
+func newStageNodesStep() *sqlgraph.Step {
+	return sqlgraph.NewStep(
+		sqlgraph.From(Table, FieldID),
+		sqlgraph.To(StageNodesInverseTable, FieldID),
+		sqlgraph.Edge(sqlgraph.O2M, false, StageNodesTable, StageNodesColumn),
+	)
+}

+ 547 - 0
ent/sopstage/where.go

@@ -0,0 +1,547 @@
+// Code generated by ent, DO NOT EDIT.
+
+package sopstage
+
+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.SopStage {
+	return predicate.SopStage(sql.FieldEQ(FieldID, id))
+}
+
+// IDEQ applies the EQ predicate on the ID field.
+func IDEQ(id uint64) predicate.SopStage {
+	return predicate.SopStage(sql.FieldEQ(FieldID, id))
+}
+
+// IDNEQ applies the NEQ predicate on the ID field.
+func IDNEQ(id uint64) predicate.SopStage {
+	return predicate.SopStage(sql.FieldNEQ(FieldID, id))
+}
+
+// IDIn applies the In predicate on the ID field.
+func IDIn(ids ...uint64) predicate.SopStage {
+	return predicate.SopStage(sql.FieldIn(FieldID, ids...))
+}
+
+// IDNotIn applies the NotIn predicate on the ID field.
+func IDNotIn(ids ...uint64) predicate.SopStage {
+	return predicate.SopStage(sql.FieldNotIn(FieldID, ids...))
+}
+
+// IDGT applies the GT predicate on the ID field.
+func IDGT(id uint64) predicate.SopStage {
+	return predicate.SopStage(sql.FieldGT(FieldID, id))
+}
+
+// IDGTE applies the GTE predicate on the ID field.
+func IDGTE(id uint64) predicate.SopStage {
+	return predicate.SopStage(sql.FieldGTE(FieldID, id))
+}
+
+// IDLT applies the LT predicate on the ID field.
+func IDLT(id uint64) predicate.SopStage {
+	return predicate.SopStage(sql.FieldLT(FieldID, id))
+}
+
+// IDLTE applies the LTE predicate on the ID field.
+func IDLTE(id uint64) predicate.SopStage {
+	return predicate.SopStage(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.SopStage {
+	return predicate.SopStage(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.SopStage {
+	return predicate.SopStage(sql.FieldEQ(FieldUpdatedAt, v))
+}
+
+// Status applies equality check predicate on the "status" field. It's identical to StatusEQ.
+func Status(v uint8) predicate.SopStage {
+	return predicate.SopStage(sql.FieldEQ(FieldStatus, v))
+}
+
+// DeletedAt applies equality check predicate on the "deleted_at" field. It's identical to DeletedAtEQ.
+func DeletedAt(v time.Time) predicate.SopStage {
+	return predicate.SopStage(sql.FieldEQ(FieldDeletedAt, v))
+}
+
+// TaskID applies equality check predicate on the "task_id" field. It's identical to TaskIDEQ.
+func TaskID(v uint64) predicate.SopStage {
+	return predicate.SopStage(sql.FieldEQ(FieldTaskID, v))
+}
+
+// Name applies equality check predicate on the "name" field. It's identical to NameEQ.
+func Name(v string) predicate.SopStage {
+	return predicate.SopStage(sql.FieldEQ(FieldName, v))
+}
+
+// ConditionType applies equality check predicate on the "condition_type" field. It's identical to ConditionTypeEQ.
+func ConditionType(v int) predicate.SopStage {
+	return predicate.SopStage(sql.FieldEQ(FieldConditionType, v))
+}
+
+// ConditionOperator applies equality check predicate on the "condition_operator" field. It's identical to ConditionOperatorEQ.
+func ConditionOperator(v int) predicate.SopStage {
+	return predicate.SopStage(sql.FieldEQ(FieldConditionOperator, v))
+}
+
+// IndexSort applies equality check predicate on the "index_sort" field. It's identical to IndexSortEQ.
+func IndexSort(v int) predicate.SopStage {
+	return predicate.SopStage(sql.FieldEQ(FieldIndexSort, v))
+}
+
+// CreatedAtEQ applies the EQ predicate on the "created_at" field.
+func CreatedAtEQ(v time.Time) predicate.SopStage {
+	return predicate.SopStage(sql.FieldEQ(FieldCreatedAt, v))
+}
+
+// CreatedAtNEQ applies the NEQ predicate on the "created_at" field.
+func CreatedAtNEQ(v time.Time) predicate.SopStage {
+	return predicate.SopStage(sql.FieldNEQ(FieldCreatedAt, v))
+}
+
+// CreatedAtIn applies the In predicate on the "created_at" field.
+func CreatedAtIn(vs ...time.Time) predicate.SopStage {
+	return predicate.SopStage(sql.FieldIn(FieldCreatedAt, vs...))
+}
+
+// CreatedAtNotIn applies the NotIn predicate on the "created_at" field.
+func CreatedAtNotIn(vs ...time.Time) predicate.SopStage {
+	return predicate.SopStage(sql.FieldNotIn(FieldCreatedAt, vs...))
+}
+
+// CreatedAtGT applies the GT predicate on the "created_at" field.
+func CreatedAtGT(v time.Time) predicate.SopStage {
+	return predicate.SopStage(sql.FieldGT(FieldCreatedAt, v))
+}
+
+// CreatedAtGTE applies the GTE predicate on the "created_at" field.
+func CreatedAtGTE(v time.Time) predicate.SopStage {
+	return predicate.SopStage(sql.FieldGTE(FieldCreatedAt, v))
+}
+
+// CreatedAtLT applies the LT predicate on the "created_at" field.
+func CreatedAtLT(v time.Time) predicate.SopStage {
+	return predicate.SopStage(sql.FieldLT(FieldCreatedAt, v))
+}
+
+// CreatedAtLTE applies the LTE predicate on the "created_at" field.
+func CreatedAtLTE(v time.Time) predicate.SopStage {
+	return predicate.SopStage(sql.FieldLTE(FieldCreatedAt, v))
+}
+
+// UpdatedAtEQ applies the EQ predicate on the "updated_at" field.
+func UpdatedAtEQ(v time.Time) predicate.SopStage {
+	return predicate.SopStage(sql.FieldEQ(FieldUpdatedAt, v))
+}
+
+// UpdatedAtNEQ applies the NEQ predicate on the "updated_at" field.
+func UpdatedAtNEQ(v time.Time) predicate.SopStage {
+	return predicate.SopStage(sql.FieldNEQ(FieldUpdatedAt, v))
+}
+
+// UpdatedAtIn applies the In predicate on the "updated_at" field.
+func UpdatedAtIn(vs ...time.Time) predicate.SopStage {
+	return predicate.SopStage(sql.FieldIn(FieldUpdatedAt, vs...))
+}
+
+// UpdatedAtNotIn applies the NotIn predicate on the "updated_at" field.
+func UpdatedAtNotIn(vs ...time.Time) predicate.SopStage {
+	return predicate.SopStage(sql.FieldNotIn(FieldUpdatedAt, vs...))
+}
+
+// UpdatedAtGT applies the GT predicate on the "updated_at" field.
+func UpdatedAtGT(v time.Time) predicate.SopStage {
+	return predicate.SopStage(sql.FieldGT(FieldUpdatedAt, v))
+}
+
+// UpdatedAtGTE applies the GTE predicate on the "updated_at" field.
+func UpdatedAtGTE(v time.Time) predicate.SopStage {
+	return predicate.SopStage(sql.FieldGTE(FieldUpdatedAt, v))
+}
+
+// UpdatedAtLT applies the LT predicate on the "updated_at" field.
+func UpdatedAtLT(v time.Time) predicate.SopStage {
+	return predicate.SopStage(sql.FieldLT(FieldUpdatedAt, v))
+}
+
+// UpdatedAtLTE applies the LTE predicate on the "updated_at" field.
+func UpdatedAtLTE(v time.Time) predicate.SopStage {
+	return predicate.SopStage(sql.FieldLTE(FieldUpdatedAt, v))
+}
+
+// StatusEQ applies the EQ predicate on the "status" field.
+func StatusEQ(v uint8) predicate.SopStage {
+	return predicate.SopStage(sql.FieldEQ(FieldStatus, v))
+}
+
+// StatusNEQ applies the NEQ predicate on the "status" field.
+func StatusNEQ(v uint8) predicate.SopStage {
+	return predicate.SopStage(sql.FieldNEQ(FieldStatus, v))
+}
+
+// StatusIn applies the In predicate on the "status" field.
+func StatusIn(vs ...uint8) predicate.SopStage {
+	return predicate.SopStage(sql.FieldIn(FieldStatus, vs...))
+}
+
+// StatusNotIn applies the NotIn predicate on the "status" field.
+func StatusNotIn(vs ...uint8) predicate.SopStage {
+	return predicate.SopStage(sql.FieldNotIn(FieldStatus, vs...))
+}
+
+// StatusGT applies the GT predicate on the "status" field.
+func StatusGT(v uint8) predicate.SopStage {
+	return predicate.SopStage(sql.FieldGT(FieldStatus, v))
+}
+
+// StatusGTE applies the GTE predicate on the "status" field.
+func StatusGTE(v uint8) predicate.SopStage {
+	return predicate.SopStage(sql.FieldGTE(FieldStatus, v))
+}
+
+// StatusLT applies the LT predicate on the "status" field.
+func StatusLT(v uint8) predicate.SopStage {
+	return predicate.SopStage(sql.FieldLT(FieldStatus, v))
+}
+
+// StatusLTE applies the LTE predicate on the "status" field.
+func StatusLTE(v uint8) predicate.SopStage {
+	return predicate.SopStage(sql.FieldLTE(FieldStatus, v))
+}
+
+// StatusIsNil applies the IsNil predicate on the "status" field.
+func StatusIsNil() predicate.SopStage {
+	return predicate.SopStage(sql.FieldIsNull(FieldStatus))
+}
+
+// StatusNotNil applies the NotNil predicate on the "status" field.
+func StatusNotNil() predicate.SopStage {
+	return predicate.SopStage(sql.FieldNotNull(FieldStatus))
+}
+
+// DeletedAtEQ applies the EQ predicate on the "deleted_at" field.
+func DeletedAtEQ(v time.Time) predicate.SopStage {
+	return predicate.SopStage(sql.FieldEQ(FieldDeletedAt, v))
+}
+
+// DeletedAtNEQ applies the NEQ predicate on the "deleted_at" field.
+func DeletedAtNEQ(v time.Time) predicate.SopStage {
+	return predicate.SopStage(sql.FieldNEQ(FieldDeletedAt, v))
+}
+
+// DeletedAtIn applies the In predicate on the "deleted_at" field.
+func DeletedAtIn(vs ...time.Time) predicate.SopStage {
+	return predicate.SopStage(sql.FieldIn(FieldDeletedAt, vs...))
+}
+
+// DeletedAtNotIn applies the NotIn predicate on the "deleted_at" field.
+func DeletedAtNotIn(vs ...time.Time) predicate.SopStage {
+	return predicate.SopStage(sql.FieldNotIn(FieldDeletedAt, vs...))
+}
+
+// DeletedAtGT applies the GT predicate on the "deleted_at" field.
+func DeletedAtGT(v time.Time) predicate.SopStage {
+	return predicate.SopStage(sql.FieldGT(FieldDeletedAt, v))
+}
+
+// DeletedAtGTE applies the GTE predicate on the "deleted_at" field.
+func DeletedAtGTE(v time.Time) predicate.SopStage {
+	return predicate.SopStage(sql.FieldGTE(FieldDeletedAt, v))
+}
+
+// DeletedAtLT applies the LT predicate on the "deleted_at" field.
+func DeletedAtLT(v time.Time) predicate.SopStage {
+	return predicate.SopStage(sql.FieldLT(FieldDeletedAt, v))
+}
+
+// DeletedAtLTE applies the LTE predicate on the "deleted_at" field.
+func DeletedAtLTE(v time.Time) predicate.SopStage {
+	return predicate.SopStage(sql.FieldLTE(FieldDeletedAt, v))
+}
+
+// DeletedAtIsNil applies the IsNil predicate on the "deleted_at" field.
+func DeletedAtIsNil() predicate.SopStage {
+	return predicate.SopStage(sql.FieldIsNull(FieldDeletedAt))
+}
+
+// DeletedAtNotNil applies the NotNil predicate on the "deleted_at" field.
+func DeletedAtNotNil() predicate.SopStage {
+	return predicate.SopStage(sql.FieldNotNull(FieldDeletedAt))
+}
+
+// TaskIDEQ applies the EQ predicate on the "task_id" field.
+func TaskIDEQ(v uint64) predicate.SopStage {
+	return predicate.SopStage(sql.FieldEQ(FieldTaskID, v))
+}
+
+// TaskIDNEQ applies the NEQ predicate on the "task_id" field.
+func TaskIDNEQ(v uint64) predicate.SopStage {
+	return predicate.SopStage(sql.FieldNEQ(FieldTaskID, v))
+}
+
+// TaskIDIn applies the In predicate on the "task_id" field.
+func TaskIDIn(vs ...uint64) predicate.SopStage {
+	return predicate.SopStage(sql.FieldIn(FieldTaskID, vs...))
+}
+
+// TaskIDNotIn applies the NotIn predicate on the "task_id" field.
+func TaskIDNotIn(vs ...uint64) predicate.SopStage {
+	return predicate.SopStage(sql.FieldNotIn(FieldTaskID, vs...))
+}
+
+// NameEQ applies the EQ predicate on the "name" field.
+func NameEQ(v string) predicate.SopStage {
+	return predicate.SopStage(sql.FieldEQ(FieldName, v))
+}
+
+// NameNEQ applies the NEQ predicate on the "name" field.
+func NameNEQ(v string) predicate.SopStage {
+	return predicate.SopStage(sql.FieldNEQ(FieldName, v))
+}
+
+// NameIn applies the In predicate on the "name" field.
+func NameIn(vs ...string) predicate.SopStage {
+	return predicate.SopStage(sql.FieldIn(FieldName, vs...))
+}
+
+// NameNotIn applies the NotIn predicate on the "name" field.
+func NameNotIn(vs ...string) predicate.SopStage {
+	return predicate.SopStage(sql.FieldNotIn(FieldName, vs...))
+}
+
+// NameGT applies the GT predicate on the "name" field.
+func NameGT(v string) predicate.SopStage {
+	return predicate.SopStage(sql.FieldGT(FieldName, v))
+}
+
+// NameGTE applies the GTE predicate on the "name" field.
+func NameGTE(v string) predicate.SopStage {
+	return predicate.SopStage(sql.FieldGTE(FieldName, v))
+}
+
+// NameLT applies the LT predicate on the "name" field.
+func NameLT(v string) predicate.SopStage {
+	return predicate.SopStage(sql.FieldLT(FieldName, v))
+}
+
+// NameLTE applies the LTE predicate on the "name" field.
+func NameLTE(v string) predicate.SopStage {
+	return predicate.SopStage(sql.FieldLTE(FieldName, v))
+}
+
+// NameContains applies the Contains predicate on the "name" field.
+func NameContains(v string) predicate.SopStage {
+	return predicate.SopStage(sql.FieldContains(FieldName, v))
+}
+
+// NameHasPrefix applies the HasPrefix predicate on the "name" field.
+func NameHasPrefix(v string) predicate.SopStage {
+	return predicate.SopStage(sql.FieldHasPrefix(FieldName, v))
+}
+
+// NameHasSuffix applies the HasSuffix predicate on the "name" field.
+func NameHasSuffix(v string) predicate.SopStage {
+	return predicate.SopStage(sql.FieldHasSuffix(FieldName, v))
+}
+
+// NameEqualFold applies the EqualFold predicate on the "name" field.
+func NameEqualFold(v string) predicate.SopStage {
+	return predicate.SopStage(sql.FieldEqualFold(FieldName, v))
+}
+
+// NameContainsFold applies the ContainsFold predicate on the "name" field.
+func NameContainsFold(v string) predicate.SopStage {
+	return predicate.SopStage(sql.FieldContainsFold(FieldName, v))
+}
+
+// ConditionTypeEQ applies the EQ predicate on the "condition_type" field.
+func ConditionTypeEQ(v int) predicate.SopStage {
+	return predicate.SopStage(sql.FieldEQ(FieldConditionType, v))
+}
+
+// ConditionTypeNEQ applies the NEQ predicate on the "condition_type" field.
+func ConditionTypeNEQ(v int) predicate.SopStage {
+	return predicate.SopStage(sql.FieldNEQ(FieldConditionType, v))
+}
+
+// ConditionTypeIn applies the In predicate on the "condition_type" field.
+func ConditionTypeIn(vs ...int) predicate.SopStage {
+	return predicate.SopStage(sql.FieldIn(FieldConditionType, vs...))
+}
+
+// ConditionTypeNotIn applies the NotIn predicate on the "condition_type" field.
+func ConditionTypeNotIn(vs ...int) predicate.SopStage {
+	return predicate.SopStage(sql.FieldNotIn(FieldConditionType, vs...))
+}
+
+// ConditionTypeGT applies the GT predicate on the "condition_type" field.
+func ConditionTypeGT(v int) predicate.SopStage {
+	return predicate.SopStage(sql.FieldGT(FieldConditionType, v))
+}
+
+// ConditionTypeGTE applies the GTE predicate on the "condition_type" field.
+func ConditionTypeGTE(v int) predicate.SopStage {
+	return predicate.SopStage(sql.FieldGTE(FieldConditionType, v))
+}
+
+// ConditionTypeLT applies the LT predicate on the "condition_type" field.
+func ConditionTypeLT(v int) predicate.SopStage {
+	return predicate.SopStage(sql.FieldLT(FieldConditionType, v))
+}
+
+// ConditionTypeLTE applies the LTE predicate on the "condition_type" field.
+func ConditionTypeLTE(v int) predicate.SopStage {
+	return predicate.SopStage(sql.FieldLTE(FieldConditionType, v))
+}
+
+// ConditionOperatorEQ applies the EQ predicate on the "condition_operator" field.
+func ConditionOperatorEQ(v int) predicate.SopStage {
+	return predicate.SopStage(sql.FieldEQ(FieldConditionOperator, v))
+}
+
+// ConditionOperatorNEQ applies the NEQ predicate on the "condition_operator" field.
+func ConditionOperatorNEQ(v int) predicate.SopStage {
+	return predicate.SopStage(sql.FieldNEQ(FieldConditionOperator, v))
+}
+
+// ConditionOperatorIn applies the In predicate on the "condition_operator" field.
+func ConditionOperatorIn(vs ...int) predicate.SopStage {
+	return predicate.SopStage(sql.FieldIn(FieldConditionOperator, vs...))
+}
+
+// ConditionOperatorNotIn applies the NotIn predicate on the "condition_operator" field.
+func ConditionOperatorNotIn(vs ...int) predicate.SopStage {
+	return predicate.SopStage(sql.FieldNotIn(FieldConditionOperator, vs...))
+}
+
+// ConditionOperatorGT applies the GT predicate on the "condition_operator" field.
+func ConditionOperatorGT(v int) predicate.SopStage {
+	return predicate.SopStage(sql.FieldGT(FieldConditionOperator, v))
+}
+
+// ConditionOperatorGTE applies the GTE predicate on the "condition_operator" field.
+func ConditionOperatorGTE(v int) predicate.SopStage {
+	return predicate.SopStage(sql.FieldGTE(FieldConditionOperator, v))
+}
+
+// ConditionOperatorLT applies the LT predicate on the "condition_operator" field.
+func ConditionOperatorLT(v int) predicate.SopStage {
+	return predicate.SopStage(sql.FieldLT(FieldConditionOperator, v))
+}
+
+// ConditionOperatorLTE applies the LTE predicate on the "condition_operator" field.
+func ConditionOperatorLTE(v int) predicate.SopStage {
+	return predicate.SopStage(sql.FieldLTE(FieldConditionOperator, v))
+}
+
+// IndexSortEQ applies the EQ predicate on the "index_sort" field.
+func IndexSortEQ(v int) predicate.SopStage {
+	return predicate.SopStage(sql.FieldEQ(FieldIndexSort, v))
+}
+
+// IndexSortNEQ applies the NEQ predicate on the "index_sort" field.
+func IndexSortNEQ(v int) predicate.SopStage {
+	return predicate.SopStage(sql.FieldNEQ(FieldIndexSort, v))
+}
+
+// IndexSortIn applies the In predicate on the "index_sort" field.
+func IndexSortIn(vs ...int) predicate.SopStage {
+	return predicate.SopStage(sql.FieldIn(FieldIndexSort, vs...))
+}
+
+// IndexSortNotIn applies the NotIn predicate on the "index_sort" field.
+func IndexSortNotIn(vs ...int) predicate.SopStage {
+	return predicate.SopStage(sql.FieldNotIn(FieldIndexSort, vs...))
+}
+
+// IndexSortGT applies the GT predicate on the "index_sort" field.
+func IndexSortGT(v int) predicate.SopStage {
+	return predicate.SopStage(sql.FieldGT(FieldIndexSort, v))
+}
+
+// IndexSortGTE applies the GTE predicate on the "index_sort" field.
+func IndexSortGTE(v int) predicate.SopStage {
+	return predicate.SopStage(sql.FieldGTE(FieldIndexSort, v))
+}
+
+// IndexSortLT applies the LT predicate on the "index_sort" field.
+func IndexSortLT(v int) predicate.SopStage {
+	return predicate.SopStage(sql.FieldLT(FieldIndexSort, v))
+}
+
+// IndexSortLTE applies the LTE predicate on the "index_sort" field.
+func IndexSortLTE(v int) predicate.SopStage {
+	return predicate.SopStage(sql.FieldLTE(FieldIndexSort, v))
+}
+
+// HasSopTask applies the HasEdge predicate on the "sop_task" edge.
+func HasSopTask() predicate.SopStage {
+	return predicate.SopStage(func(s *sql.Selector) {
+		step := sqlgraph.NewStep(
+			sqlgraph.From(Table, FieldID),
+			sqlgraph.Edge(sqlgraph.M2O, true, SopTaskTable, SopTaskColumn),
+		)
+		sqlgraph.HasNeighbors(s, step)
+	})
+}
+
+// HasSopTaskWith applies the HasEdge predicate on the "sop_task" edge with a given conditions (other predicates).
+func HasSopTaskWith(preds ...predicate.SopTask) predicate.SopStage {
+	return predicate.SopStage(func(s *sql.Selector) {
+		step := newSopTaskStep()
+		sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
+			for _, p := range preds {
+				p(s)
+			}
+		})
+	})
+}
+
+// HasStageNodes applies the HasEdge predicate on the "stage_nodes" edge.
+func HasStageNodes() predicate.SopStage {
+	return predicate.SopStage(func(s *sql.Selector) {
+		step := sqlgraph.NewStep(
+			sqlgraph.From(Table, FieldID),
+			sqlgraph.Edge(sqlgraph.O2M, false, StageNodesTable, StageNodesColumn),
+		)
+		sqlgraph.HasNeighbors(s, step)
+	})
+}
+
+// HasStageNodesWith applies the HasEdge predicate on the "stage_nodes" edge with a given conditions (other predicates).
+func HasStageNodesWith(preds ...predicate.SopNode) predicate.SopStage {
+	return predicate.SopStage(func(s *sql.Selector) {
+		step := newStageNodesStep()
+		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.SopStage) predicate.SopStage {
+	return predicate.SopStage(sql.AndPredicates(predicates...))
+}
+
+// Or groups predicates with the OR operator between them.
+func Or(predicates ...predicate.SopStage) predicate.SopStage {
+	return predicate.SopStage(sql.OrPredicates(predicates...))
+}
+
+// Not applies the not operator on the given predicate.
+func Not(p predicate.SopStage) predicate.SopStage {
+	return predicate.SopStage(sql.NotPredicates(p))
+}

+ 1316 - 0
ent/sopstage_create.go

@@ -0,0 +1,1316 @@
+// Code generated by ent, DO NOT EDIT.
+
+package ent
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"time"
+	"wechat-api/ent/custom_types"
+	"wechat-api/ent/sopnode"
+	"wechat-api/ent/sopstage"
+	"wechat-api/ent/soptask"
+
+	"entgo.io/ent/dialect/sql"
+	"entgo.io/ent/dialect/sql/sqlgraph"
+	"entgo.io/ent/schema/field"
+)
+
+// SopStageCreate is the builder for creating a SopStage entity.
+type SopStageCreate struct {
+	config
+	mutation *SopStageMutation
+	hooks    []Hook
+	conflict []sql.ConflictOption
+}
+
+// SetCreatedAt sets the "created_at" field.
+func (ssc *SopStageCreate) SetCreatedAt(t time.Time) *SopStageCreate {
+	ssc.mutation.SetCreatedAt(t)
+	return ssc
+}
+
+// SetNillableCreatedAt sets the "created_at" field if the given value is not nil.
+func (ssc *SopStageCreate) SetNillableCreatedAt(t *time.Time) *SopStageCreate {
+	if t != nil {
+		ssc.SetCreatedAt(*t)
+	}
+	return ssc
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (ssc *SopStageCreate) SetUpdatedAt(t time.Time) *SopStageCreate {
+	ssc.mutation.SetUpdatedAt(t)
+	return ssc
+}
+
+// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil.
+func (ssc *SopStageCreate) SetNillableUpdatedAt(t *time.Time) *SopStageCreate {
+	if t != nil {
+		ssc.SetUpdatedAt(*t)
+	}
+	return ssc
+}
+
+// SetStatus sets the "status" field.
+func (ssc *SopStageCreate) SetStatus(u uint8) *SopStageCreate {
+	ssc.mutation.SetStatus(u)
+	return ssc
+}
+
+// SetNillableStatus sets the "status" field if the given value is not nil.
+func (ssc *SopStageCreate) SetNillableStatus(u *uint8) *SopStageCreate {
+	if u != nil {
+		ssc.SetStatus(*u)
+	}
+	return ssc
+}
+
+// SetDeletedAt sets the "deleted_at" field.
+func (ssc *SopStageCreate) SetDeletedAt(t time.Time) *SopStageCreate {
+	ssc.mutation.SetDeletedAt(t)
+	return ssc
+}
+
+// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil.
+func (ssc *SopStageCreate) SetNillableDeletedAt(t *time.Time) *SopStageCreate {
+	if t != nil {
+		ssc.SetDeletedAt(*t)
+	}
+	return ssc
+}
+
+// SetTaskID sets the "task_id" field.
+func (ssc *SopStageCreate) SetTaskID(u uint64) *SopStageCreate {
+	ssc.mutation.SetTaskID(u)
+	return ssc
+}
+
+// SetName sets the "name" field.
+func (ssc *SopStageCreate) SetName(s string) *SopStageCreate {
+	ssc.mutation.SetName(s)
+	return ssc
+}
+
+// SetNillableName sets the "name" field if the given value is not nil.
+func (ssc *SopStageCreate) SetNillableName(s *string) *SopStageCreate {
+	if s != nil {
+		ssc.SetName(*s)
+	}
+	return ssc
+}
+
+// SetConditionType sets the "condition_type" field.
+func (ssc *SopStageCreate) SetConditionType(i int) *SopStageCreate {
+	ssc.mutation.SetConditionType(i)
+	return ssc
+}
+
+// SetNillableConditionType sets the "condition_type" field if the given value is not nil.
+func (ssc *SopStageCreate) SetNillableConditionType(i *int) *SopStageCreate {
+	if i != nil {
+		ssc.SetConditionType(*i)
+	}
+	return ssc
+}
+
+// SetConditionOperator sets the "condition_operator" field.
+func (ssc *SopStageCreate) SetConditionOperator(i int) *SopStageCreate {
+	ssc.mutation.SetConditionOperator(i)
+	return ssc
+}
+
+// SetNillableConditionOperator sets the "condition_operator" field if the given value is not nil.
+func (ssc *SopStageCreate) SetNillableConditionOperator(i *int) *SopStageCreate {
+	if i != nil {
+		ssc.SetConditionOperator(*i)
+	}
+	return ssc
+}
+
+// SetConditionList sets the "condition_list" field.
+func (ssc *SopStageCreate) SetConditionList(ct []custom_types.Condition) *SopStageCreate {
+	ssc.mutation.SetConditionList(ct)
+	return ssc
+}
+
+// SetActionMessage sets the "action_message" field.
+func (ssc *SopStageCreate) SetActionMessage(ct []custom_types.Action) *SopStageCreate {
+	ssc.mutation.SetActionMessage(ct)
+	return ssc
+}
+
+// SetActionLabel sets the "action_label" field.
+func (ssc *SopStageCreate) SetActionLabel(i []int) *SopStageCreate {
+	ssc.mutation.SetActionLabel(i)
+	return ssc
+}
+
+// SetIndexSort sets the "index_sort" field.
+func (ssc *SopStageCreate) SetIndexSort(i int) *SopStageCreate {
+	ssc.mutation.SetIndexSort(i)
+	return ssc
+}
+
+// SetNillableIndexSort sets the "index_sort" field if the given value is not nil.
+func (ssc *SopStageCreate) SetNillableIndexSort(i *int) *SopStageCreate {
+	if i != nil {
+		ssc.SetIndexSort(*i)
+	}
+	return ssc
+}
+
+// SetID sets the "id" field.
+func (ssc *SopStageCreate) SetID(u uint64) *SopStageCreate {
+	ssc.mutation.SetID(u)
+	return ssc
+}
+
+// SetSopTaskID sets the "sop_task" edge to the SopTask entity by ID.
+func (ssc *SopStageCreate) SetSopTaskID(id uint64) *SopStageCreate {
+	ssc.mutation.SetSopTaskID(id)
+	return ssc
+}
+
+// SetSopTask sets the "sop_task" edge to the SopTask entity.
+func (ssc *SopStageCreate) SetSopTask(s *SopTask) *SopStageCreate {
+	return ssc.SetSopTaskID(s.ID)
+}
+
+// AddStageNodeIDs adds the "stage_nodes" edge to the SopNode entity by IDs.
+func (ssc *SopStageCreate) AddStageNodeIDs(ids ...uint64) *SopStageCreate {
+	ssc.mutation.AddStageNodeIDs(ids...)
+	return ssc
+}
+
+// AddStageNodes adds the "stage_nodes" edges to the SopNode entity.
+func (ssc *SopStageCreate) AddStageNodes(s ...*SopNode) *SopStageCreate {
+	ids := make([]uint64, len(s))
+	for i := range s {
+		ids[i] = s[i].ID
+	}
+	return ssc.AddStageNodeIDs(ids...)
+}
+
+// Mutation returns the SopStageMutation object of the builder.
+func (ssc *SopStageCreate) Mutation() *SopStageMutation {
+	return ssc.mutation
+}
+
+// Save creates the SopStage in the database.
+func (ssc *SopStageCreate) Save(ctx context.Context) (*SopStage, error) {
+	if err := ssc.defaults(); err != nil {
+		return nil, err
+	}
+	return withHooks(ctx, ssc.sqlSave, ssc.mutation, ssc.hooks)
+}
+
+// SaveX calls Save and panics if Save returns an error.
+func (ssc *SopStageCreate) SaveX(ctx context.Context) *SopStage {
+	v, err := ssc.Save(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return v
+}
+
+// Exec executes the query.
+func (ssc *SopStageCreate) Exec(ctx context.Context) error {
+	_, err := ssc.Save(ctx)
+	return err
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (ssc *SopStageCreate) ExecX(ctx context.Context) {
+	if err := ssc.Exec(ctx); err != nil {
+		panic(err)
+	}
+}
+
+// defaults sets the default values of the builder before save.
+func (ssc *SopStageCreate) defaults() error {
+	if _, ok := ssc.mutation.CreatedAt(); !ok {
+		if sopstage.DefaultCreatedAt == nil {
+			return fmt.Errorf("ent: uninitialized sopstage.DefaultCreatedAt (forgotten import ent/runtime?)")
+		}
+		v := sopstage.DefaultCreatedAt()
+		ssc.mutation.SetCreatedAt(v)
+	}
+	if _, ok := ssc.mutation.UpdatedAt(); !ok {
+		if sopstage.DefaultUpdatedAt == nil {
+			return fmt.Errorf("ent: uninitialized sopstage.DefaultUpdatedAt (forgotten import ent/runtime?)")
+		}
+		v := sopstage.DefaultUpdatedAt()
+		ssc.mutation.SetUpdatedAt(v)
+	}
+	if _, ok := ssc.mutation.Status(); !ok {
+		v := sopstage.DefaultStatus
+		ssc.mutation.SetStatus(v)
+	}
+	if _, ok := ssc.mutation.Name(); !ok {
+		v := sopstage.DefaultName
+		ssc.mutation.SetName(v)
+	}
+	if _, ok := ssc.mutation.ConditionType(); !ok {
+		v := sopstage.DefaultConditionType
+		ssc.mutation.SetConditionType(v)
+	}
+	if _, ok := ssc.mutation.ConditionOperator(); !ok {
+		v := sopstage.DefaultConditionOperator
+		ssc.mutation.SetConditionOperator(v)
+	}
+	if _, ok := ssc.mutation.IndexSort(); !ok {
+		v := sopstage.DefaultIndexSort
+		ssc.mutation.SetIndexSort(v)
+	}
+	return nil
+}
+
+// check runs all checks and user-defined validators on the builder.
+func (ssc *SopStageCreate) check() error {
+	if _, ok := ssc.mutation.CreatedAt(); !ok {
+		return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "SopStage.created_at"`)}
+	}
+	if _, ok := ssc.mutation.UpdatedAt(); !ok {
+		return &ValidationError{Name: "updated_at", err: errors.New(`ent: missing required field "SopStage.updated_at"`)}
+	}
+	if _, ok := ssc.mutation.TaskID(); !ok {
+		return &ValidationError{Name: "task_id", err: errors.New(`ent: missing required field "SopStage.task_id"`)}
+	}
+	if _, ok := ssc.mutation.Name(); !ok {
+		return &ValidationError{Name: "name", err: errors.New(`ent: missing required field "SopStage.name"`)}
+	}
+	if _, ok := ssc.mutation.ConditionType(); !ok {
+		return &ValidationError{Name: "condition_type", err: errors.New(`ent: missing required field "SopStage.condition_type"`)}
+	}
+	if _, ok := ssc.mutation.ConditionOperator(); !ok {
+		return &ValidationError{Name: "condition_operator", err: errors.New(`ent: missing required field "SopStage.condition_operator"`)}
+	}
+	if _, ok := ssc.mutation.ConditionList(); !ok {
+		return &ValidationError{Name: "condition_list", err: errors.New(`ent: missing required field "SopStage.condition_list"`)}
+	}
+	if _, ok := ssc.mutation.ActionMessage(); !ok {
+		return &ValidationError{Name: "action_message", err: errors.New(`ent: missing required field "SopStage.action_message"`)}
+	}
+	if _, ok := ssc.mutation.ActionLabel(); !ok {
+		return &ValidationError{Name: "action_label", err: errors.New(`ent: missing required field "SopStage.action_label"`)}
+	}
+	if _, ok := ssc.mutation.IndexSort(); !ok {
+		return &ValidationError{Name: "index_sort", err: errors.New(`ent: missing required field "SopStage.index_sort"`)}
+	}
+	if _, ok := ssc.mutation.SopTaskID(); !ok {
+		return &ValidationError{Name: "sop_task", err: errors.New(`ent: missing required edge "SopStage.sop_task"`)}
+	}
+	return nil
+}
+
+func (ssc *SopStageCreate) sqlSave(ctx context.Context) (*SopStage, error) {
+	if err := ssc.check(); err != nil {
+		return nil, err
+	}
+	_node, _spec := ssc.createSpec()
+	if err := sqlgraph.CreateNode(ctx, ssc.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)
+	}
+	ssc.mutation.id = &_node.ID
+	ssc.mutation.done = true
+	return _node, nil
+}
+
+func (ssc *SopStageCreate) createSpec() (*SopStage, *sqlgraph.CreateSpec) {
+	var (
+		_node = &SopStage{config: ssc.config}
+		_spec = sqlgraph.NewCreateSpec(sopstage.Table, sqlgraph.NewFieldSpec(sopstage.FieldID, field.TypeUint64))
+	)
+	_spec.OnConflict = ssc.conflict
+	if id, ok := ssc.mutation.ID(); ok {
+		_node.ID = id
+		_spec.ID.Value = id
+	}
+	if value, ok := ssc.mutation.CreatedAt(); ok {
+		_spec.SetField(sopstage.FieldCreatedAt, field.TypeTime, value)
+		_node.CreatedAt = value
+	}
+	if value, ok := ssc.mutation.UpdatedAt(); ok {
+		_spec.SetField(sopstage.FieldUpdatedAt, field.TypeTime, value)
+		_node.UpdatedAt = value
+	}
+	if value, ok := ssc.mutation.Status(); ok {
+		_spec.SetField(sopstage.FieldStatus, field.TypeUint8, value)
+		_node.Status = value
+	}
+	if value, ok := ssc.mutation.DeletedAt(); ok {
+		_spec.SetField(sopstage.FieldDeletedAt, field.TypeTime, value)
+		_node.DeletedAt = value
+	}
+	if value, ok := ssc.mutation.Name(); ok {
+		_spec.SetField(sopstage.FieldName, field.TypeString, value)
+		_node.Name = value
+	}
+	if value, ok := ssc.mutation.ConditionType(); ok {
+		_spec.SetField(sopstage.FieldConditionType, field.TypeInt, value)
+		_node.ConditionType = value
+	}
+	if value, ok := ssc.mutation.ConditionOperator(); ok {
+		_spec.SetField(sopstage.FieldConditionOperator, field.TypeInt, value)
+		_node.ConditionOperator = value
+	}
+	if value, ok := ssc.mutation.ConditionList(); ok {
+		_spec.SetField(sopstage.FieldConditionList, field.TypeJSON, value)
+		_node.ConditionList = value
+	}
+	if value, ok := ssc.mutation.ActionMessage(); ok {
+		_spec.SetField(sopstage.FieldActionMessage, field.TypeJSON, value)
+		_node.ActionMessage = value
+	}
+	if value, ok := ssc.mutation.ActionLabel(); ok {
+		_spec.SetField(sopstage.FieldActionLabel, field.TypeJSON, value)
+		_node.ActionLabel = value
+	}
+	if value, ok := ssc.mutation.IndexSort(); ok {
+		_spec.SetField(sopstage.FieldIndexSort, field.TypeInt, value)
+		_node.IndexSort = value
+	}
+	if nodes := ssc.mutation.SopTaskIDs(); len(nodes) > 0 {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.M2O,
+			Inverse: true,
+			Table:   sopstage.SopTaskTable,
+			Columns: []string{sopstage.SopTaskColumn},
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: sqlgraph.NewFieldSpec(soptask.FieldID, field.TypeUint64),
+			},
+		}
+		for _, k := range nodes {
+			edge.Target.Nodes = append(edge.Target.Nodes, k)
+		}
+		_node.TaskID = nodes[0]
+		_spec.Edges = append(_spec.Edges, edge)
+	}
+	if nodes := ssc.mutation.StageNodesIDs(); len(nodes) > 0 {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.O2M,
+			Inverse: false,
+			Table:   sopstage.StageNodesTable,
+			Columns: []string{sopstage.StageNodesColumn},
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: sqlgraph.NewFieldSpec(sopnode.FieldID, field.TypeUint64),
+			},
+		}
+		for _, k := range nodes {
+			edge.Target.Nodes = append(edge.Target.Nodes, k)
+		}
+		_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.SopStage.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.SopStageUpsert) {
+//			SetCreatedAt(v+v).
+//		}).
+//		Exec(ctx)
+func (ssc *SopStageCreate) OnConflict(opts ...sql.ConflictOption) *SopStageUpsertOne {
+	ssc.conflict = opts
+	return &SopStageUpsertOne{
+		create: ssc,
+	}
+}
+
+// OnConflictColumns calls `OnConflict` and configures the columns
+// as conflict target. Using this option is equivalent to using:
+//
+//	client.SopStage.Create().
+//		OnConflict(sql.ConflictColumns(columns...)).
+//		Exec(ctx)
+func (ssc *SopStageCreate) OnConflictColumns(columns ...string) *SopStageUpsertOne {
+	ssc.conflict = append(ssc.conflict, sql.ConflictColumns(columns...))
+	return &SopStageUpsertOne{
+		create: ssc,
+	}
+}
+
+type (
+	// SopStageUpsertOne is the builder for "upsert"-ing
+	//  one SopStage node.
+	SopStageUpsertOne struct {
+		create *SopStageCreate
+	}
+
+	// SopStageUpsert is the "OnConflict" setter.
+	SopStageUpsert struct {
+		*sql.UpdateSet
+	}
+)
+
+// SetUpdatedAt sets the "updated_at" field.
+func (u *SopStageUpsert) SetUpdatedAt(v time.Time) *SopStageUpsert {
+	u.Set(sopstage.FieldUpdatedAt, v)
+	return u
+}
+
+// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create.
+func (u *SopStageUpsert) UpdateUpdatedAt() *SopStageUpsert {
+	u.SetExcluded(sopstage.FieldUpdatedAt)
+	return u
+}
+
+// SetStatus sets the "status" field.
+func (u *SopStageUpsert) SetStatus(v uint8) *SopStageUpsert {
+	u.Set(sopstage.FieldStatus, v)
+	return u
+}
+
+// UpdateStatus sets the "status" field to the value that was provided on create.
+func (u *SopStageUpsert) UpdateStatus() *SopStageUpsert {
+	u.SetExcluded(sopstage.FieldStatus)
+	return u
+}
+
+// AddStatus adds v to the "status" field.
+func (u *SopStageUpsert) AddStatus(v uint8) *SopStageUpsert {
+	u.Add(sopstage.FieldStatus, v)
+	return u
+}
+
+// ClearStatus clears the value of the "status" field.
+func (u *SopStageUpsert) ClearStatus() *SopStageUpsert {
+	u.SetNull(sopstage.FieldStatus)
+	return u
+}
+
+// SetDeletedAt sets the "deleted_at" field.
+func (u *SopStageUpsert) SetDeletedAt(v time.Time) *SopStageUpsert {
+	u.Set(sopstage.FieldDeletedAt, v)
+	return u
+}
+
+// UpdateDeletedAt sets the "deleted_at" field to the value that was provided on create.
+func (u *SopStageUpsert) UpdateDeletedAt() *SopStageUpsert {
+	u.SetExcluded(sopstage.FieldDeletedAt)
+	return u
+}
+
+// ClearDeletedAt clears the value of the "deleted_at" field.
+func (u *SopStageUpsert) ClearDeletedAt() *SopStageUpsert {
+	u.SetNull(sopstage.FieldDeletedAt)
+	return u
+}
+
+// SetTaskID sets the "task_id" field.
+func (u *SopStageUpsert) SetTaskID(v uint64) *SopStageUpsert {
+	u.Set(sopstage.FieldTaskID, v)
+	return u
+}
+
+// UpdateTaskID sets the "task_id" field to the value that was provided on create.
+func (u *SopStageUpsert) UpdateTaskID() *SopStageUpsert {
+	u.SetExcluded(sopstage.FieldTaskID)
+	return u
+}
+
+// SetName sets the "name" field.
+func (u *SopStageUpsert) SetName(v string) *SopStageUpsert {
+	u.Set(sopstage.FieldName, v)
+	return u
+}
+
+// UpdateName sets the "name" field to the value that was provided on create.
+func (u *SopStageUpsert) UpdateName() *SopStageUpsert {
+	u.SetExcluded(sopstage.FieldName)
+	return u
+}
+
+// SetConditionType sets the "condition_type" field.
+func (u *SopStageUpsert) SetConditionType(v int) *SopStageUpsert {
+	u.Set(sopstage.FieldConditionType, v)
+	return u
+}
+
+// UpdateConditionType sets the "condition_type" field to the value that was provided on create.
+func (u *SopStageUpsert) UpdateConditionType() *SopStageUpsert {
+	u.SetExcluded(sopstage.FieldConditionType)
+	return u
+}
+
+// AddConditionType adds v to the "condition_type" field.
+func (u *SopStageUpsert) AddConditionType(v int) *SopStageUpsert {
+	u.Add(sopstage.FieldConditionType, v)
+	return u
+}
+
+// SetConditionOperator sets the "condition_operator" field.
+func (u *SopStageUpsert) SetConditionOperator(v int) *SopStageUpsert {
+	u.Set(sopstage.FieldConditionOperator, v)
+	return u
+}
+
+// UpdateConditionOperator sets the "condition_operator" field to the value that was provided on create.
+func (u *SopStageUpsert) UpdateConditionOperator() *SopStageUpsert {
+	u.SetExcluded(sopstage.FieldConditionOperator)
+	return u
+}
+
+// AddConditionOperator adds v to the "condition_operator" field.
+func (u *SopStageUpsert) AddConditionOperator(v int) *SopStageUpsert {
+	u.Add(sopstage.FieldConditionOperator, v)
+	return u
+}
+
+// SetConditionList sets the "condition_list" field.
+func (u *SopStageUpsert) SetConditionList(v []custom_types.Condition) *SopStageUpsert {
+	u.Set(sopstage.FieldConditionList, v)
+	return u
+}
+
+// UpdateConditionList sets the "condition_list" field to the value that was provided on create.
+func (u *SopStageUpsert) UpdateConditionList() *SopStageUpsert {
+	u.SetExcluded(sopstage.FieldConditionList)
+	return u
+}
+
+// SetActionMessage sets the "action_message" field.
+func (u *SopStageUpsert) SetActionMessage(v []custom_types.Action) *SopStageUpsert {
+	u.Set(sopstage.FieldActionMessage, v)
+	return u
+}
+
+// UpdateActionMessage sets the "action_message" field to the value that was provided on create.
+func (u *SopStageUpsert) UpdateActionMessage() *SopStageUpsert {
+	u.SetExcluded(sopstage.FieldActionMessage)
+	return u
+}
+
+// SetActionLabel sets the "action_label" field.
+func (u *SopStageUpsert) SetActionLabel(v []int) *SopStageUpsert {
+	u.Set(sopstage.FieldActionLabel, v)
+	return u
+}
+
+// UpdateActionLabel sets the "action_label" field to the value that was provided on create.
+func (u *SopStageUpsert) UpdateActionLabel() *SopStageUpsert {
+	u.SetExcluded(sopstage.FieldActionLabel)
+	return u
+}
+
+// SetIndexSort sets the "index_sort" field.
+func (u *SopStageUpsert) SetIndexSort(v int) *SopStageUpsert {
+	u.Set(sopstage.FieldIndexSort, v)
+	return u
+}
+
+// UpdateIndexSort sets the "index_sort" field to the value that was provided on create.
+func (u *SopStageUpsert) UpdateIndexSort() *SopStageUpsert {
+	u.SetExcluded(sopstage.FieldIndexSort)
+	return u
+}
+
+// AddIndexSort adds v to the "index_sort" field.
+func (u *SopStageUpsert) AddIndexSort(v int) *SopStageUpsert {
+	u.Add(sopstage.FieldIndexSort, v)
+	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.SopStage.Create().
+//		OnConflict(
+//			sql.ResolveWithNewValues(),
+//			sql.ResolveWith(func(u *sql.UpdateSet) {
+//				u.SetIgnore(sopstage.FieldID)
+//			}),
+//		).
+//		Exec(ctx)
+func (u *SopStageUpsertOne) UpdateNewValues() *SopStageUpsertOne {
+	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(sopstage.FieldID)
+		}
+		if _, exists := u.create.mutation.CreatedAt(); exists {
+			s.SetIgnore(sopstage.FieldCreatedAt)
+		}
+	}))
+	return u
+}
+
+// Ignore sets each column to itself in case of conflict.
+// Using this option is equivalent to using:
+//
+//	client.SopStage.Create().
+//	    OnConflict(sql.ResolveWithIgnore()).
+//	    Exec(ctx)
+func (u *SopStageUpsertOne) Ignore() *SopStageUpsertOne {
+	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 *SopStageUpsertOne) DoNothing() *SopStageUpsertOne {
+	u.create.conflict = append(u.create.conflict, sql.DoNothing())
+	return u
+}
+
+// Update allows overriding fields `UPDATE` values. See the SopStageCreate.OnConflict
+// documentation for more info.
+func (u *SopStageUpsertOne) Update(set func(*SopStageUpsert)) *SopStageUpsertOne {
+	u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) {
+		set(&SopStageUpsert{UpdateSet: update})
+	}))
+	return u
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (u *SopStageUpsertOne) SetUpdatedAt(v time.Time) *SopStageUpsertOne {
+	return u.Update(func(s *SopStageUpsert) {
+		s.SetUpdatedAt(v)
+	})
+}
+
+// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create.
+func (u *SopStageUpsertOne) UpdateUpdatedAt() *SopStageUpsertOne {
+	return u.Update(func(s *SopStageUpsert) {
+		s.UpdateUpdatedAt()
+	})
+}
+
+// SetStatus sets the "status" field.
+func (u *SopStageUpsertOne) SetStatus(v uint8) *SopStageUpsertOne {
+	return u.Update(func(s *SopStageUpsert) {
+		s.SetStatus(v)
+	})
+}
+
+// AddStatus adds v to the "status" field.
+func (u *SopStageUpsertOne) AddStatus(v uint8) *SopStageUpsertOne {
+	return u.Update(func(s *SopStageUpsert) {
+		s.AddStatus(v)
+	})
+}
+
+// UpdateStatus sets the "status" field to the value that was provided on create.
+func (u *SopStageUpsertOne) UpdateStatus() *SopStageUpsertOne {
+	return u.Update(func(s *SopStageUpsert) {
+		s.UpdateStatus()
+	})
+}
+
+// ClearStatus clears the value of the "status" field.
+func (u *SopStageUpsertOne) ClearStatus() *SopStageUpsertOne {
+	return u.Update(func(s *SopStageUpsert) {
+		s.ClearStatus()
+	})
+}
+
+// SetDeletedAt sets the "deleted_at" field.
+func (u *SopStageUpsertOne) SetDeletedAt(v time.Time) *SopStageUpsertOne {
+	return u.Update(func(s *SopStageUpsert) {
+		s.SetDeletedAt(v)
+	})
+}
+
+// UpdateDeletedAt sets the "deleted_at" field to the value that was provided on create.
+func (u *SopStageUpsertOne) UpdateDeletedAt() *SopStageUpsertOne {
+	return u.Update(func(s *SopStageUpsert) {
+		s.UpdateDeletedAt()
+	})
+}
+
+// ClearDeletedAt clears the value of the "deleted_at" field.
+func (u *SopStageUpsertOne) ClearDeletedAt() *SopStageUpsertOne {
+	return u.Update(func(s *SopStageUpsert) {
+		s.ClearDeletedAt()
+	})
+}
+
+// SetTaskID sets the "task_id" field.
+func (u *SopStageUpsertOne) SetTaskID(v uint64) *SopStageUpsertOne {
+	return u.Update(func(s *SopStageUpsert) {
+		s.SetTaskID(v)
+	})
+}
+
+// UpdateTaskID sets the "task_id" field to the value that was provided on create.
+func (u *SopStageUpsertOne) UpdateTaskID() *SopStageUpsertOne {
+	return u.Update(func(s *SopStageUpsert) {
+		s.UpdateTaskID()
+	})
+}
+
+// SetName sets the "name" field.
+func (u *SopStageUpsertOne) SetName(v string) *SopStageUpsertOne {
+	return u.Update(func(s *SopStageUpsert) {
+		s.SetName(v)
+	})
+}
+
+// UpdateName sets the "name" field to the value that was provided on create.
+func (u *SopStageUpsertOne) UpdateName() *SopStageUpsertOne {
+	return u.Update(func(s *SopStageUpsert) {
+		s.UpdateName()
+	})
+}
+
+// SetConditionType sets the "condition_type" field.
+func (u *SopStageUpsertOne) SetConditionType(v int) *SopStageUpsertOne {
+	return u.Update(func(s *SopStageUpsert) {
+		s.SetConditionType(v)
+	})
+}
+
+// AddConditionType adds v to the "condition_type" field.
+func (u *SopStageUpsertOne) AddConditionType(v int) *SopStageUpsertOne {
+	return u.Update(func(s *SopStageUpsert) {
+		s.AddConditionType(v)
+	})
+}
+
+// UpdateConditionType sets the "condition_type" field to the value that was provided on create.
+func (u *SopStageUpsertOne) UpdateConditionType() *SopStageUpsertOne {
+	return u.Update(func(s *SopStageUpsert) {
+		s.UpdateConditionType()
+	})
+}
+
+// SetConditionOperator sets the "condition_operator" field.
+func (u *SopStageUpsertOne) SetConditionOperator(v int) *SopStageUpsertOne {
+	return u.Update(func(s *SopStageUpsert) {
+		s.SetConditionOperator(v)
+	})
+}
+
+// AddConditionOperator adds v to the "condition_operator" field.
+func (u *SopStageUpsertOne) AddConditionOperator(v int) *SopStageUpsertOne {
+	return u.Update(func(s *SopStageUpsert) {
+		s.AddConditionOperator(v)
+	})
+}
+
+// UpdateConditionOperator sets the "condition_operator" field to the value that was provided on create.
+func (u *SopStageUpsertOne) UpdateConditionOperator() *SopStageUpsertOne {
+	return u.Update(func(s *SopStageUpsert) {
+		s.UpdateConditionOperator()
+	})
+}
+
+// SetConditionList sets the "condition_list" field.
+func (u *SopStageUpsertOne) SetConditionList(v []custom_types.Condition) *SopStageUpsertOne {
+	return u.Update(func(s *SopStageUpsert) {
+		s.SetConditionList(v)
+	})
+}
+
+// UpdateConditionList sets the "condition_list" field to the value that was provided on create.
+func (u *SopStageUpsertOne) UpdateConditionList() *SopStageUpsertOne {
+	return u.Update(func(s *SopStageUpsert) {
+		s.UpdateConditionList()
+	})
+}
+
+// SetActionMessage sets the "action_message" field.
+func (u *SopStageUpsertOne) SetActionMessage(v []custom_types.Action) *SopStageUpsertOne {
+	return u.Update(func(s *SopStageUpsert) {
+		s.SetActionMessage(v)
+	})
+}
+
+// UpdateActionMessage sets the "action_message" field to the value that was provided on create.
+func (u *SopStageUpsertOne) UpdateActionMessage() *SopStageUpsertOne {
+	return u.Update(func(s *SopStageUpsert) {
+		s.UpdateActionMessage()
+	})
+}
+
+// SetActionLabel sets the "action_label" field.
+func (u *SopStageUpsertOne) SetActionLabel(v []int) *SopStageUpsertOne {
+	return u.Update(func(s *SopStageUpsert) {
+		s.SetActionLabel(v)
+	})
+}
+
+// UpdateActionLabel sets the "action_label" field to the value that was provided on create.
+func (u *SopStageUpsertOne) UpdateActionLabel() *SopStageUpsertOne {
+	return u.Update(func(s *SopStageUpsert) {
+		s.UpdateActionLabel()
+	})
+}
+
+// SetIndexSort sets the "index_sort" field.
+func (u *SopStageUpsertOne) SetIndexSort(v int) *SopStageUpsertOne {
+	return u.Update(func(s *SopStageUpsert) {
+		s.SetIndexSort(v)
+	})
+}
+
+// AddIndexSort adds v to the "index_sort" field.
+func (u *SopStageUpsertOne) AddIndexSort(v int) *SopStageUpsertOne {
+	return u.Update(func(s *SopStageUpsert) {
+		s.AddIndexSort(v)
+	})
+}
+
+// UpdateIndexSort sets the "index_sort" field to the value that was provided on create.
+func (u *SopStageUpsertOne) UpdateIndexSort() *SopStageUpsertOne {
+	return u.Update(func(s *SopStageUpsert) {
+		s.UpdateIndexSort()
+	})
+}
+
+// Exec executes the query.
+func (u *SopStageUpsertOne) Exec(ctx context.Context) error {
+	if len(u.create.conflict) == 0 {
+		return errors.New("ent: missing options for SopStageCreate.OnConflict")
+	}
+	return u.create.Exec(ctx)
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (u *SopStageUpsertOne) 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 *SopStageUpsertOne) 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 *SopStageUpsertOne) IDX(ctx context.Context) uint64 {
+	id, err := u.ID(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return id
+}
+
+// SopStageCreateBulk is the builder for creating many SopStage entities in bulk.
+type SopStageCreateBulk struct {
+	config
+	err      error
+	builders []*SopStageCreate
+	conflict []sql.ConflictOption
+}
+
+// Save creates the SopStage entities in the database.
+func (sscb *SopStageCreateBulk) Save(ctx context.Context) ([]*SopStage, error) {
+	if sscb.err != nil {
+		return nil, sscb.err
+	}
+	specs := make([]*sqlgraph.CreateSpec, len(sscb.builders))
+	nodes := make([]*SopStage, len(sscb.builders))
+	mutators := make([]Mutator, len(sscb.builders))
+	for i := range sscb.builders {
+		func(i int, root context.Context) {
+			builder := sscb.builders[i]
+			builder.defaults()
+			var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
+				mutation, ok := m.(*SopStageMutation)
+				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, sscb.builders[i+1].mutation)
+				} else {
+					spec := &sqlgraph.BatchCreateSpec{Nodes: specs}
+					spec.OnConflict = sscb.conflict
+					// Invoke the actual operation on the latest mutation in the chain.
+					if err = sqlgraph.BatchCreate(ctx, sscb.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, sscb.builders[0].mutation); err != nil {
+			return nil, err
+		}
+	}
+	return nodes, nil
+}
+
+// SaveX is like Save, but panics if an error occurs.
+func (sscb *SopStageCreateBulk) SaveX(ctx context.Context) []*SopStage {
+	v, err := sscb.Save(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return v
+}
+
+// Exec executes the query.
+func (sscb *SopStageCreateBulk) Exec(ctx context.Context) error {
+	_, err := sscb.Save(ctx)
+	return err
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (sscb *SopStageCreateBulk) ExecX(ctx context.Context) {
+	if err := sscb.Exec(ctx); err != nil {
+		panic(err)
+	}
+}
+
+// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause
+// of the `INSERT` statement. For example:
+//
+//	client.SopStage.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.SopStageUpsert) {
+//			SetCreatedAt(v+v).
+//		}).
+//		Exec(ctx)
+func (sscb *SopStageCreateBulk) OnConflict(opts ...sql.ConflictOption) *SopStageUpsertBulk {
+	sscb.conflict = opts
+	return &SopStageUpsertBulk{
+		create: sscb,
+	}
+}
+
+// OnConflictColumns calls `OnConflict` and configures the columns
+// as conflict target. Using this option is equivalent to using:
+//
+//	client.SopStage.Create().
+//		OnConflict(sql.ConflictColumns(columns...)).
+//		Exec(ctx)
+func (sscb *SopStageCreateBulk) OnConflictColumns(columns ...string) *SopStageUpsertBulk {
+	sscb.conflict = append(sscb.conflict, sql.ConflictColumns(columns...))
+	return &SopStageUpsertBulk{
+		create: sscb,
+	}
+}
+
+// SopStageUpsertBulk is the builder for "upsert"-ing
+// a bulk of SopStage nodes.
+type SopStageUpsertBulk struct {
+	create *SopStageCreateBulk
+}
+
+// UpdateNewValues updates the mutable fields using the new values that
+// were set on create. Using this option is equivalent to using:
+//
+//	client.SopStage.Create().
+//		OnConflict(
+//			sql.ResolveWithNewValues(),
+//			sql.ResolveWith(func(u *sql.UpdateSet) {
+//				u.SetIgnore(sopstage.FieldID)
+//			}),
+//		).
+//		Exec(ctx)
+func (u *SopStageUpsertBulk) UpdateNewValues() *SopStageUpsertBulk {
+	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(sopstage.FieldID)
+			}
+			if _, exists := b.mutation.CreatedAt(); exists {
+				s.SetIgnore(sopstage.FieldCreatedAt)
+			}
+		}
+	}))
+	return u
+}
+
+// Ignore sets each column to itself in case of conflict.
+// Using this option is equivalent to using:
+//
+//	client.SopStage.Create().
+//		OnConflict(sql.ResolveWithIgnore()).
+//		Exec(ctx)
+func (u *SopStageUpsertBulk) Ignore() *SopStageUpsertBulk {
+	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 *SopStageUpsertBulk) DoNothing() *SopStageUpsertBulk {
+	u.create.conflict = append(u.create.conflict, sql.DoNothing())
+	return u
+}
+
+// Update allows overriding fields `UPDATE` values. See the SopStageCreateBulk.OnConflict
+// documentation for more info.
+func (u *SopStageUpsertBulk) Update(set func(*SopStageUpsert)) *SopStageUpsertBulk {
+	u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) {
+		set(&SopStageUpsert{UpdateSet: update})
+	}))
+	return u
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (u *SopStageUpsertBulk) SetUpdatedAt(v time.Time) *SopStageUpsertBulk {
+	return u.Update(func(s *SopStageUpsert) {
+		s.SetUpdatedAt(v)
+	})
+}
+
+// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create.
+func (u *SopStageUpsertBulk) UpdateUpdatedAt() *SopStageUpsertBulk {
+	return u.Update(func(s *SopStageUpsert) {
+		s.UpdateUpdatedAt()
+	})
+}
+
+// SetStatus sets the "status" field.
+func (u *SopStageUpsertBulk) SetStatus(v uint8) *SopStageUpsertBulk {
+	return u.Update(func(s *SopStageUpsert) {
+		s.SetStatus(v)
+	})
+}
+
+// AddStatus adds v to the "status" field.
+func (u *SopStageUpsertBulk) AddStatus(v uint8) *SopStageUpsertBulk {
+	return u.Update(func(s *SopStageUpsert) {
+		s.AddStatus(v)
+	})
+}
+
+// UpdateStatus sets the "status" field to the value that was provided on create.
+func (u *SopStageUpsertBulk) UpdateStatus() *SopStageUpsertBulk {
+	return u.Update(func(s *SopStageUpsert) {
+		s.UpdateStatus()
+	})
+}
+
+// ClearStatus clears the value of the "status" field.
+func (u *SopStageUpsertBulk) ClearStatus() *SopStageUpsertBulk {
+	return u.Update(func(s *SopStageUpsert) {
+		s.ClearStatus()
+	})
+}
+
+// SetDeletedAt sets the "deleted_at" field.
+func (u *SopStageUpsertBulk) SetDeletedAt(v time.Time) *SopStageUpsertBulk {
+	return u.Update(func(s *SopStageUpsert) {
+		s.SetDeletedAt(v)
+	})
+}
+
+// UpdateDeletedAt sets the "deleted_at" field to the value that was provided on create.
+func (u *SopStageUpsertBulk) UpdateDeletedAt() *SopStageUpsertBulk {
+	return u.Update(func(s *SopStageUpsert) {
+		s.UpdateDeletedAt()
+	})
+}
+
+// ClearDeletedAt clears the value of the "deleted_at" field.
+func (u *SopStageUpsertBulk) ClearDeletedAt() *SopStageUpsertBulk {
+	return u.Update(func(s *SopStageUpsert) {
+		s.ClearDeletedAt()
+	})
+}
+
+// SetTaskID sets the "task_id" field.
+func (u *SopStageUpsertBulk) SetTaskID(v uint64) *SopStageUpsertBulk {
+	return u.Update(func(s *SopStageUpsert) {
+		s.SetTaskID(v)
+	})
+}
+
+// UpdateTaskID sets the "task_id" field to the value that was provided on create.
+func (u *SopStageUpsertBulk) UpdateTaskID() *SopStageUpsertBulk {
+	return u.Update(func(s *SopStageUpsert) {
+		s.UpdateTaskID()
+	})
+}
+
+// SetName sets the "name" field.
+func (u *SopStageUpsertBulk) SetName(v string) *SopStageUpsertBulk {
+	return u.Update(func(s *SopStageUpsert) {
+		s.SetName(v)
+	})
+}
+
+// UpdateName sets the "name" field to the value that was provided on create.
+func (u *SopStageUpsertBulk) UpdateName() *SopStageUpsertBulk {
+	return u.Update(func(s *SopStageUpsert) {
+		s.UpdateName()
+	})
+}
+
+// SetConditionType sets the "condition_type" field.
+func (u *SopStageUpsertBulk) SetConditionType(v int) *SopStageUpsertBulk {
+	return u.Update(func(s *SopStageUpsert) {
+		s.SetConditionType(v)
+	})
+}
+
+// AddConditionType adds v to the "condition_type" field.
+func (u *SopStageUpsertBulk) AddConditionType(v int) *SopStageUpsertBulk {
+	return u.Update(func(s *SopStageUpsert) {
+		s.AddConditionType(v)
+	})
+}
+
+// UpdateConditionType sets the "condition_type" field to the value that was provided on create.
+func (u *SopStageUpsertBulk) UpdateConditionType() *SopStageUpsertBulk {
+	return u.Update(func(s *SopStageUpsert) {
+		s.UpdateConditionType()
+	})
+}
+
+// SetConditionOperator sets the "condition_operator" field.
+func (u *SopStageUpsertBulk) SetConditionOperator(v int) *SopStageUpsertBulk {
+	return u.Update(func(s *SopStageUpsert) {
+		s.SetConditionOperator(v)
+	})
+}
+
+// AddConditionOperator adds v to the "condition_operator" field.
+func (u *SopStageUpsertBulk) AddConditionOperator(v int) *SopStageUpsertBulk {
+	return u.Update(func(s *SopStageUpsert) {
+		s.AddConditionOperator(v)
+	})
+}
+
+// UpdateConditionOperator sets the "condition_operator" field to the value that was provided on create.
+func (u *SopStageUpsertBulk) UpdateConditionOperator() *SopStageUpsertBulk {
+	return u.Update(func(s *SopStageUpsert) {
+		s.UpdateConditionOperator()
+	})
+}
+
+// SetConditionList sets the "condition_list" field.
+func (u *SopStageUpsertBulk) SetConditionList(v []custom_types.Condition) *SopStageUpsertBulk {
+	return u.Update(func(s *SopStageUpsert) {
+		s.SetConditionList(v)
+	})
+}
+
+// UpdateConditionList sets the "condition_list" field to the value that was provided on create.
+func (u *SopStageUpsertBulk) UpdateConditionList() *SopStageUpsertBulk {
+	return u.Update(func(s *SopStageUpsert) {
+		s.UpdateConditionList()
+	})
+}
+
+// SetActionMessage sets the "action_message" field.
+func (u *SopStageUpsertBulk) SetActionMessage(v []custom_types.Action) *SopStageUpsertBulk {
+	return u.Update(func(s *SopStageUpsert) {
+		s.SetActionMessage(v)
+	})
+}
+
+// UpdateActionMessage sets the "action_message" field to the value that was provided on create.
+func (u *SopStageUpsertBulk) UpdateActionMessage() *SopStageUpsertBulk {
+	return u.Update(func(s *SopStageUpsert) {
+		s.UpdateActionMessage()
+	})
+}
+
+// SetActionLabel sets the "action_label" field.
+func (u *SopStageUpsertBulk) SetActionLabel(v []int) *SopStageUpsertBulk {
+	return u.Update(func(s *SopStageUpsert) {
+		s.SetActionLabel(v)
+	})
+}
+
+// UpdateActionLabel sets the "action_label" field to the value that was provided on create.
+func (u *SopStageUpsertBulk) UpdateActionLabel() *SopStageUpsertBulk {
+	return u.Update(func(s *SopStageUpsert) {
+		s.UpdateActionLabel()
+	})
+}
+
+// SetIndexSort sets the "index_sort" field.
+func (u *SopStageUpsertBulk) SetIndexSort(v int) *SopStageUpsertBulk {
+	return u.Update(func(s *SopStageUpsert) {
+		s.SetIndexSort(v)
+	})
+}
+
+// AddIndexSort adds v to the "index_sort" field.
+func (u *SopStageUpsertBulk) AddIndexSort(v int) *SopStageUpsertBulk {
+	return u.Update(func(s *SopStageUpsert) {
+		s.AddIndexSort(v)
+	})
+}
+
+// UpdateIndexSort sets the "index_sort" field to the value that was provided on create.
+func (u *SopStageUpsertBulk) UpdateIndexSort() *SopStageUpsertBulk {
+	return u.Update(func(s *SopStageUpsert) {
+		s.UpdateIndexSort()
+	})
+}
+
+// Exec executes the query.
+func (u *SopStageUpsertBulk) 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 SopStageCreateBulk instead", i)
+		}
+	}
+	if len(u.create.conflict) == 0 {
+		return errors.New("ent: missing options for SopStageCreateBulk.OnConflict")
+	}
+	return u.create.Exec(ctx)
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (u *SopStageUpsertBulk) ExecX(ctx context.Context) {
+	if err := u.create.Exec(ctx); err != nil {
+		panic(err)
+	}
+}

+ 88 - 0
ent/sopstage_delete.go

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

+ 680 - 0
ent/sopstage_query.go

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

+ 972 - 0
ent/sopstage_update.go

@@ -0,0 +1,972 @@
+// Code generated by ent, DO NOT EDIT.
+
+package ent
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"time"
+	"wechat-api/ent/custom_types"
+	"wechat-api/ent/predicate"
+	"wechat-api/ent/sopnode"
+	"wechat-api/ent/sopstage"
+	"wechat-api/ent/soptask"
+
+	"entgo.io/ent/dialect/sql"
+	"entgo.io/ent/dialect/sql/sqlgraph"
+	"entgo.io/ent/dialect/sql/sqljson"
+	"entgo.io/ent/schema/field"
+)
+
+// SopStageUpdate is the builder for updating SopStage entities.
+type SopStageUpdate struct {
+	config
+	hooks    []Hook
+	mutation *SopStageMutation
+}
+
+// Where appends a list predicates to the SopStageUpdate builder.
+func (ssu *SopStageUpdate) Where(ps ...predicate.SopStage) *SopStageUpdate {
+	ssu.mutation.Where(ps...)
+	return ssu
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (ssu *SopStageUpdate) SetUpdatedAt(t time.Time) *SopStageUpdate {
+	ssu.mutation.SetUpdatedAt(t)
+	return ssu
+}
+
+// SetStatus sets the "status" field.
+func (ssu *SopStageUpdate) SetStatus(u uint8) *SopStageUpdate {
+	ssu.mutation.ResetStatus()
+	ssu.mutation.SetStatus(u)
+	return ssu
+}
+
+// SetNillableStatus sets the "status" field if the given value is not nil.
+func (ssu *SopStageUpdate) SetNillableStatus(u *uint8) *SopStageUpdate {
+	if u != nil {
+		ssu.SetStatus(*u)
+	}
+	return ssu
+}
+
+// AddStatus adds u to the "status" field.
+func (ssu *SopStageUpdate) AddStatus(u int8) *SopStageUpdate {
+	ssu.mutation.AddStatus(u)
+	return ssu
+}
+
+// ClearStatus clears the value of the "status" field.
+func (ssu *SopStageUpdate) ClearStatus() *SopStageUpdate {
+	ssu.mutation.ClearStatus()
+	return ssu
+}
+
+// SetDeletedAt sets the "deleted_at" field.
+func (ssu *SopStageUpdate) SetDeletedAt(t time.Time) *SopStageUpdate {
+	ssu.mutation.SetDeletedAt(t)
+	return ssu
+}
+
+// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil.
+func (ssu *SopStageUpdate) SetNillableDeletedAt(t *time.Time) *SopStageUpdate {
+	if t != nil {
+		ssu.SetDeletedAt(*t)
+	}
+	return ssu
+}
+
+// ClearDeletedAt clears the value of the "deleted_at" field.
+func (ssu *SopStageUpdate) ClearDeletedAt() *SopStageUpdate {
+	ssu.mutation.ClearDeletedAt()
+	return ssu
+}
+
+// SetTaskID sets the "task_id" field.
+func (ssu *SopStageUpdate) SetTaskID(u uint64) *SopStageUpdate {
+	ssu.mutation.SetTaskID(u)
+	return ssu
+}
+
+// SetNillableTaskID sets the "task_id" field if the given value is not nil.
+func (ssu *SopStageUpdate) SetNillableTaskID(u *uint64) *SopStageUpdate {
+	if u != nil {
+		ssu.SetTaskID(*u)
+	}
+	return ssu
+}
+
+// SetName sets the "name" field.
+func (ssu *SopStageUpdate) SetName(s string) *SopStageUpdate {
+	ssu.mutation.SetName(s)
+	return ssu
+}
+
+// SetNillableName sets the "name" field if the given value is not nil.
+func (ssu *SopStageUpdate) SetNillableName(s *string) *SopStageUpdate {
+	if s != nil {
+		ssu.SetName(*s)
+	}
+	return ssu
+}
+
+// SetConditionType sets the "condition_type" field.
+func (ssu *SopStageUpdate) SetConditionType(i int) *SopStageUpdate {
+	ssu.mutation.ResetConditionType()
+	ssu.mutation.SetConditionType(i)
+	return ssu
+}
+
+// SetNillableConditionType sets the "condition_type" field if the given value is not nil.
+func (ssu *SopStageUpdate) SetNillableConditionType(i *int) *SopStageUpdate {
+	if i != nil {
+		ssu.SetConditionType(*i)
+	}
+	return ssu
+}
+
+// AddConditionType adds i to the "condition_type" field.
+func (ssu *SopStageUpdate) AddConditionType(i int) *SopStageUpdate {
+	ssu.mutation.AddConditionType(i)
+	return ssu
+}
+
+// SetConditionOperator sets the "condition_operator" field.
+func (ssu *SopStageUpdate) SetConditionOperator(i int) *SopStageUpdate {
+	ssu.mutation.ResetConditionOperator()
+	ssu.mutation.SetConditionOperator(i)
+	return ssu
+}
+
+// SetNillableConditionOperator sets the "condition_operator" field if the given value is not nil.
+func (ssu *SopStageUpdate) SetNillableConditionOperator(i *int) *SopStageUpdate {
+	if i != nil {
+		ssu.SetConditionOperator(*i)
+	}
+	return ssu
+}
+
+// AddConditionOperator adds i to the "condition_operator" field.
+func (ssu *SopStageUpdate) AddConditionOperator(i int) *SopStageUpdate {
+	ssu.mutation.AddConditionOperator(i)
+	return ssu
+}
+
+// SetConditionList sets the "condition_list" field.
+func (ssu *SopStageUpdate) SetConditionList(ct []custom_types.Condition) *SopStageUpdate {
+	ssu.mutation.SetConditionList(ct)
+	return ssu
+}
+
+// AppendConditionList appends ct to the "condition_list" field.
+func (ssu *SopStageUpdate) AppendConditionList(ct []custom_types.Condition) *SopStageUpdate {
+	ssu.mutation.AppendConditionList(ct)
+	return ssu
+}
+
+// SetActionMessage sets the "action_message" field.
+func (ssu *SopStageUpdate) SetActionMessage(ct []custom_types.Action) *SopStageUpdate {
+	ssu.mutation.SetActionMessage(ct)
+	return ssu
+}
+
+// AppendActionMessage appends ct to the "action_message" field.
+func (ssu *SopStageUpdate) AppendActionMessage(ct []custom_types.Action) *SopStageUpdate {
+	ssu.mutation.AppendActionMessage(ct)
+	return ssu
+}
+
+// SetActionLabel sets the "action_label" field.
+func (ssu *SopStageUpdate) SetActionLabel(i []int) *SopStageUpdate {
+	ssu.mutation.SetActionLabel(i)
+	return ssu
+}
+
+// AppendActionLabel appends i to the "action_label" field.
+func (ssu *SopStageUpdate) AppendActionLabel(i []int) *SopStageUpdate {
+	ssu.mutation.AppendActionLabel(i)
+	return ssu
+}
+
+// SetIndexSort sets the "index_sort" field.
+func (ssu *SopStageUpdate) SetIndexSort(i int) *SopStageUpdate {
+	ssu.mutation.ResetIndexSort()
+	ssu.mutation.SetIndexSort(i)
+	return ssu
+}
+
+// SetNillableIndexSort sets the "index_sort" field if the given value is not nil.
+func (ssu *SopStageUpdate) SetNillableIndexSort(i *int) *SopStageUpdate {
+	if i != nil {
+		ssu.SetIndexSort(*i)
+	}
+	return ssu
+}
+
+// AddIndexSort adds i to the "index_sort" field.
+func (ssu *SopStageUpdate) AddIndexSort(i int) *SopStageUpdate {
+	ssu.mutation.AddIndexSort(i)
+	return ssu
+}
+
+// SetSopTaskID sets the "sop_task" edge to the SopTask entity by ID.
+func (ssu *SopStageUpdate) SetSopTaskID(id uint64) *SopStageUpdate {
+	ssu.mutation.SetSopTaskID(id)
+	return ssu
+}
+
+// SetSopTask sets the "sop_task" edge to the SopTask entity.
+func (ssu *SopStageUpdate) SetSopTask(s *SopTask) *SopStageUpdate {
+	return ssu.SetSopTaskID(s.ID)
+}
+
+// AddStageNodeIDs adds the "stage_nodes" edge to the SopNode entity by IDs.
+func (ssu *SopStageUpdate) AddStageNodeIDs(ids ...uint64) *SopStageUpdate {
+	ssu.mutation.AddStageNodeIDs(ids...)
+	return ssu
+}
+
+// AddStageNodes adds the "stage_nodes" edges to the SopNode entity.
+func (ssu *SopStageUpdate) AddStageNodes(s ...*SopNode) *SopStageUpdate {
+	ids := make([]uint64, len(s))
+	for i := range s {
+		ids[i] = s[i].ID
+	}
+	return ssu.AddStageNodeIDs(ids...)
+}
+
+// Mutation returns the SopStageMutation object of the builder.
+func (ssu *SopStageUpdate) Mutation() *SopStageMutation {
+	return ssu.mutation
+}
+
+// ClearSopTask clears the "sop_task" edge to the SopTask entity.
+func (ssu *SopStageUpdate) ClearSopTask() *SopStageUpdate {
+	ssu.mutation.ClearSopTask()
+	return ssu
+}
+
+// ClearStageNodes clears all "stage_nodes" edges to the SopNode entity.
+func (ssu *SopStageUpdate) ClearStageNodes() *SopStageUpdate {
+	ssu.mutation.ClearStageNodes()
+	return ssu
+}
+
+// RemoveStageNodeIDs removes the "stage_nodes" edge to SopNode entities by IDs.
+func (ssu *SopStageUpdate) RemoveStageNodeIDs(ids ...uint64) *SopStageUpdate {
+	ssu.mutation.RemoveStageNodeIDs(ids...)
+	return ssu
+}
+
+// RemoveStageNodes removes "stage_nodes" edges to SopNode entities.
+func (ssu *SopStageUpdate) RemoveStageNodes(s ...*SopNode) *SopStageUpdate {
+	ids := make([]uint64, len(s))
+	for i := range s {
+		ids[i] = s[i].ID
+	}
+	return ssu.RemoveStageNodeIDs(ids...)
+}
+
+// Save executes the query and returns the number of nodes affected by the update operation.
+func (ssu *SopStageUpdate) Save(ctx context.Context) (int, error) {
+	if err := ssu.defaults(); err != nil {
+		return 0, err
+	}
+	return withHooks(ctx, ssu.sqlSave, ssu.mutation, ssu.hooks)
+}
+
+// SaveX is like Save, but panics if an error occurs.
+func (ssu *SopStageUpdate) SaveX(ctx context.Context) int {
+	affected, err := ssu.Save(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return affected
+}
+
+// Exec executes the query.
+func (ssu *SopStageUpdate) Exec(ctx context.Context) error {
+	_, err := ssu.Save(ctx)
+	return err
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (ssu *SopStageUpdate) ExecX(ctx context.Context) {
+	if err := ssu.Exec(ctx); err != nil {
+		panic(err)
+	}
+}
+
+// defaults sets the default values of the builder before save.
+func (ssu *SopStageUpdate) defaults() error {
+	if _, ok := ssu.mutation.UpdatedAt(); !ok {
+		if sopstage.UpdateDefaultUpdatedAt == nil {
+			return fmt.Errorf("ent: uninitialized sopstage.UpdateDefaultUpdatedAt (forgotten import ent/runtime?)")
+		}
+		v := sopstage.UpdateDefaultUpdatedAt()
+		ssu.mutation.SetUpdatedAt(v)
+	}
+	return nil
+}
+
+// check runs all checks and user-defined validators on the builder.
+func (ssu *SopStageUpdate) check() error {
+	if _, ok := ssu.mutation.SopTaskID(); ssu.mutation.SopTaskCleared() && !ok {
+		return errors.New(`ent: clearing a required unique edge "SopStage.sop_task"`)
+	}
+	return nil
+}
+
+func (ssu *SopStageUpdate) sqlSave(ctx context.Context) (n int, err error) {
+	if err := ssu.check(); err != nil {
+		return n, err
+	}
+	_spec := sqlgraph.NewUpdateSpec(sopstage.Table, sopstage.Columns, sqlgraph.NewFieldSpec(sopstage.FieldID, field.TypeUint64))
+	if ps := ssu.mutation.predicates; len(ps) > 0 {
+		_spec.Predicate = func(selector *sql.Selector) {
+			for i := range ps {
+				ps[i](selector)
+			}
+		}
+	}
+	if value, ok := ssu.mutation.UpdatedAt(); ok {
+		_spec.SetField(sopstage.FieldUpdatedAt, field.TypeTime, value)
+	}
+	if value, ok := ssu.mutation.Status(); ok {
+		_spec.SetField(sopstage.FieldStatus, field.TypeUint8, value)
+	}
+	if value, ok := ssu.mutation.AddedStatus(); ok {
+		_spec.AddField(sopstage.FieldStatus, field.TypeUint8, value)
+	}
+	if ssu.mutation.StatusCleared() {
+		_spec.ClearField(sopstage.FieldStatus, field.TypeUint8)
+	}
+	if value, ok := ssu.mutation.DeletedAt(); ok {
+		_spec.SetField(sopstage.FieldDeletedAt, field.TypeTime, value)
+	}
+	if ssu.mutation.DeletedAtCleared() {
+		_spec.ClearField(sopstage.FieldDeletedAt, field.TypeTime)
+	}
+	if value, ok := ssu.mutation.Name(); ok {
+		_spec.SetField(sopstage.FieldName, field.TypeString, value)
+	}
+	if value, ok := ssu.mutation.ConditionType(); ok {
+		_spec.SetField(sopstage.FieldConditionType, field.TypeInt, value)
+	}
+	if value, ok := ssu.mutation.AddedConditionType(); ok {
+		_spec.AddField(sopstage.FieldConditionType, field.TypeInt, value)
+	}
+	if value, ok := ssu.mutation.ConditionOperator(); ok {
+		_spec.SetField(sopstage.FieldConditionOperator, field.TypeInt, value)
+	}
+	if value, ok := ssu.mutation.AddedConditionOperator(); ok {
+		_spec.AddField(sopstage.FieldConditionOperator, field.TypeInt, value)
+	}
+	if value, ok := ssu.mutation.ConditionList(); ok {
+		_spec.SetField(sopstage.FieldConditionList, field.TypeJSON, value)
+	}
+	if value, ok := ssu.mutation.AppendedConditionList(); ok {
+		_spec.AddModifier(func(u *sql.UpdateBuilder) {
+			sqljson.Append(u, sopstage.FieldConditionList, value)
+		})
+	}
+	if value, ok := ssu.mutation.ActionMessage(); ok {
+		_spec.SetField(sopstage.FieldActionMessage, field.TypeJSON, value)
+	}
+	if value, ok := ssu.mutation.AppendedActionMessage(); ok {
+		_spec.AddModifier(func(u *sql.UpdateBuilder) {
+			sqljson.Append(u, sopstage.FieldActionMessage, value)
+		})
+	}
+	if value, ok := ssu.mutation.ActionLabel(); ok {
+		_spec.SetField(sopstage.FieldActionLabel, field.TypeJSON, value)
+	}
+	if value, ok := ssu.mutation.AppendedActionLabel(); ok {
+		_spec.AddModifier(func(u *sql.UpdateBuilder) {
+			sqljson.Append(u, sopstage.FieldActionLabel, value)
+		})
+	}
+	if value, ok := ssu.mutation.IndexSort(); ok {
+		_spec.SetField(sopstage.FieldIndexSort, field.TypeInt, value)
+	}
+	if value, ok := ssu.mutation.AddedIndexSort(); ok {
+		_spec.AddField(sopstage.FieldIndexSort, field.TypeInt, value)
+	}
+	if ssu.mutation.SopTaskCleared() {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.M2O,
+			Inverse: true,
+			Table:   sopstage.SopTaskTable,
+			Columns: []string{sopstage.SopTaskColumn},
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: sqlgraph.NewFieldSpec(soptask.FieldID, field.TypeUint64),
+			},
+		}
+		_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
+	}
+	if nodes := ssu.mutation.SopTaskIDs(); len(nodes) > 0 {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.M2O,
+			Inverse: true,
+			Table:   sopstage.SopTaskTable,
+			Columns: []string{sopstage.SopTaskColumn},
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: sqlgraph.NewFieldSpec(soptask.FieldID, field.TypeUint64),
+			},
+		}
+		for _, k := range nodes {
+			edge.Target.Nodes = append(edge.Target.Nodes, k)
+		}
+		_spec.Edges.Add = append(_spec.Edges.Add, edge)
+	}
+	if ssu.mutation.StageNodesCleared() {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.O2M,
+			Inverse: false,
+			Table:   sopstage.StageNodesTable,
+			Columns: []string{sopstage.StageNodesColumn},
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: sqlgraph.NewFieldSpec(sopnode.FieldID, field.TypeUint64),
+			},
+		}
+		_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
+	}
+	if nodes := ssu.mutation.RemovedStageNodesIDs(); len(nodes) > 0 && !ssu.mutation.StageNodesCleared() {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.O2M,
+			Inverse: false,
+			Table:   sopstage.StageNodesTable,
+			Columns: []string{sopstage.StageNodesColumn},
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: sqlgraph.NewFieldSpec(sopnode.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 := ssu.mutation.StageNodesIDs(); len(nodes) > 0 {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.O2M,
+			Inverse: false,
+			Table:   sopstage.StageNodesTable,
+			Columns: []string{sopstage.StageNodesColumn},
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: sqlgraph.NewFieldSpec(sopnode.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, ssu.driver, _spec); err != nil {
+		if _, ok := err.(*sqlgraph.NotFoundError); ok {
+			err = &NotFoundError{sopstage.Label}
+		} else if sqlgraph.IsConstraintError(err) {
+			err = &ConstraintError{msg: err.Error(), wrap: err}
+		}
+		return 0, err
+	}
+	ssu.mutation.done = true
+	return n, nil
+}
+
+// SopStageUpdateOne is the builder for updating a single SopStage entity.
+type SopStageUpdateOne struct {
+	config
+	fields   []string
+	hooks    []Hook
+	mutation *SopStageMutation
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (ssuo *SopStageUpdateOne) SetUpdatedAt(t time.Time) *SopStageUpdateOne {
+	ssuo.mutation.SetUpdatedAt(t)
+	return ssuo
+}
+
+// SetStatus sets the "status" field.
+func (ssuo *SopStageUpdateOne) SetStatus(u uint8) *SopStageUpdateOne {
+	ssuo.mutation.ResetStatus()
+	ssuo.mutation.SetStatus(u)
+	return ssuo
+}
+
+// SetNillableStatus sets the "status" field if the given value is not nil.
+func (ssuo *SopStageUpdateOne) SetNillableStatus(u *uint8) *SopStageUpdateOne {
+	if u != nil {
+		ssuo.SetStatus(*u)
+	}
+	return ssuo
+}
+
+// AddStatus adds u to the "status" field.
+func (ssuo *SopStageUpdateOne) AddStatus(u int8) *SopStageUpdateOne {
+	ssuo.mutation.AddStatus(u)
+	return ssuo
+}
+
+// ClearStatus clears the value of the "status" field.
+func (ssuo *SopStageUpdateOne) ClearStatus() *SopStageUpdateOne {
+	ssuo.mutation.ClearStatus()
+	return ssuo
+}
+
+// SetDeletedAt sets the "deleted_at" field.
+func (ssuo *SopStageUpdateOne) SetDeletedAt(t time.Time) *SopStageUpdateOne {
+	ssuo.mutation.SetDeletedAt(t)
+	return ssuo
+}
+
+// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil.
+func (ssuo *SopStageUpdateOne) SetNillableDeletedAt(t *time.Time) *SopStageUpdateOne {
+	if t != nil {
+		ssuo.SetDeletedAt(*t)
+	}
+	return ssuo
+}
+
+// ClearDeletedAt clears the value of the "deleted_at" field.
+func (ssuo *SopStageUpdateOne) ClearDeletedAt() *SopStageUpdateOne {
+	ssuo.mutation.ClearDeletedAt()
+	return ssuo
+}
+
+// SetTaskID sets the "task_id" field.
+func (ssuo *SopStageUpdateOne) SetTaskID(u uint64) *SopStageUpdateOne {
+	ssuo.mutation.SetTaskID(u)
+	return ssuo
+}
+
+// SetNillableTaskID sets the "task_id" field if the given value is not nil.
+func (ssuo *SopStageUpdateOne) SetNillableTaskID(u *uint64) *SopStageUpdateOne {
+	if u != nil {
+		ssuo.SetTaskID(*u)
+	}
+	return ssuo
+}
+
+// SetName sets the "name" field.
+func (ssuo *SopStageUpdateOne) SetName(s string) *SopStageUpdateOne {
+	ssuo.mutation.SetName(s)
+	return ssuo
+}
+
+// SetNillableName sets the "name" field if the given value is not nil.
+func (ssuo *SopStageUpdateOne) SetNillableName(s *string) *SopStageUpdateOne {
+	if s != nil {
+		ssuo.SetName(*s)
+	}
+	return ssuo
+}
+
+// SetConditionType sets the "condition_type" field.
+func (ssuo *SopStageUpdateOne) SetConditionType(i int) *SopStageUpdateOne {
+	ssuo.mutation.ResetConditionType()
+	ssuo.mutation.SetConditionType(i)
+	return ssuo
+}
+
+// SetNillableConditionType sets the "condition_type" field if the given value is not nil.
+func (ssuo *SopStageUpdateOne) SetNillableConditionType(i *int) *SopStageUpdateOne {
+	if i != nil {
+		ssuo.SetConditionType(*i)
+	}
+	return ssuo
+}
+
+// AddConditionType adds i to the "condition_type" field.
+func (ssuo *SopStageUpdateOne) AddConditionType(i int) *SopStageUpdateOne {
+	ssuo.mutation.AddConditionType(i)
+	return ssuo
+}
+
+// SetConditionOperator sets the "condition_operator" field.
+func (ssuo *SopStageUpdateOne) SetConditionOperator(i int) *SopStageUpdateOne {
+	ssuo.mutation.ResetConditionOperator()
+	ssuo.mutation.SetConditionOperator(i)
+	return ssuo
+}
+
+// SetNillableConditionOperator sets the "condition_operator" field if the given value is not nil.
+func (ssuo *SopStageUpdateOne) SetNillableConditionOperator(i *int) *SopStageUpdateOne {
+	if i != nil {
+		ssuo.SetConditionOperator(*i)
+	}
+	return ssuo
+}
+
+// AddConditionOperator adds i to the "condition_operator" field.
+func (ssuo *SopStageUpdateOne) AddConditionOperator(i int) *SopStageUpdateOne {
+	ssuo.mutation.AddConditionOperator(i)
+	return ssuo
+}
+
+// SetConditionList sets the "condition_list" field.
+func (ssuo *SopStageUpdateOne) SetConditionList(ct []custom_types.Condition) *SopStageUpdateOne {
+	ssuo.mutation.SetConditionList(ct)
+	return ssuo
+}
+
+// AppendConditionList appends ct to the "condition_list" field.
+func (ssuo *SopStageUpdateOne) AppendConditionList(ct []custom_types.Condition) *SopStageUpdateOne {
+	ssuo.mutation.AppendConditionList(ct)
+	return ssuo
+}
+
+// SetActionMessage sets the "action_message" field.
+func (ssuo *SopStageUpdateOne) SetActionMessage(ct []custom_types.Action) *SopStageUpdateOne {
+	ssuo.mutation.SetActionMessage(ct)
+	return ssuo
+}
+
+// AppendActionMessage appends ct to the "action_message" field.
+func (ssuo *SopStageUpdateOne) AppendActionMessage(ct []custom_types.Action) *SopStageUpdateOne {
+	ssuo.mutation.AppendActionMessage(ct)
+	return ssuo
+}
+
+// SetActionLabel sets the "action_label" field.
+func (ssuo *SopStageUpdateOne) SetActionLabel(i []int) *SopStageUpdateOne {
+	ssuo.mutation.SetActionLabel(i)
+	return ssuo
+}
+
+// AppendActionLabel appends i to the "action_label" field.
+func (ssuo *SopStageUpdateOne) AppendActionLabel(i []int) *SopStageUpdateOne {
+	ssuo.mutation.AppendActionLabel(i)
+	return ssuo
+}
+
+// SetIndexSort sets the "index_sort" field.
+func (ssuo *SopStageUpdateOne) SetIndexSort(i int) *SopStageUpdateOne {
+	ssuo.mutation.ResetIndexSort()
+	ssuo.mutation.SetIndexSort(i)
+	return ssuo
+}
+
+// SetNillableIndexSort sets the "index_sort" field if the given value is not nil.
+func (ssuo *SopStageUpdateOne) SetNillableIndexSort(i *int) *SopStageUpdateOne {
+	if i != nil {
+		ssuo.SetIndexSort(*i)
+	}
+	return ssuo
+}
+
+// AddIndexSort adds i to the "index_sort" field.
+func (ssuo *SopStageUpdateOne) AddIndexSort(i int) *SopStageUpdateOne {
+	ssuo.mutation.AddIndexSort(i)
+	return ssuo
+}
+
+// SetSopTaskID sets the "sop_task" edge to the SopTask entity by ID.
+func (ssuo *SopStageUpdateOne) SetSopTaskID(id uint64) *SopStageUpdateOne {
+	ssuo.mutation.SetSopTaskID(id)
+	return ssuo
+}
+
+// SetSopTask sets the "sop_task" edge to the SopTask entity.
+func (ssuo *SopStageUpdateOne) SetSopTask(s *SopTask) *SopStageUpdateOne {
+	return ssuo.SetSopTaskID(s.ID)
+}
+
+// AddStageNodeIDs adds the "stage_nodes" edge to the SopNode entity by IDs.
+func (ssuo *SopStageUpdateOne) AddStageNodeIDs(ids ...uint64) *SopStageUpdateOne {
+	ssuo.mutation.AddStageNodeIDs(ids...)
+	return ssuo
+}
+
+// AddStageNodes adds the "stage_nodes" edges to the SopNode entity.
+func (ssuo *SopStageUpdateOne) AddStageNodes(s ...*SopNode) *SopStageUpdateOne {
+	ids := make([]uint64, len(s))
+	for i := range s {
+		ids[i] = s[i].ID
+	}
+	return ssuo.AddStageNodeIDs(ids...)
+}
+
+// Mutation returns the SopStageMutation object of the builder.
+func (ssuo *SopStageUpdateOne) Mutation() *SopStageMutation {
+	return ssuo.mutation
+}
+
+// ClearSopTask clears the "sop_task" edge to the SopTask entity.
+func (ssuo *SopStageUpdateOne) ClearSopTask() *SopStageUpdateOne {
+	ssuo.mutation.ClearSopTask()
+	return ssuo
+}
+
+// ClearStageNodes clears all "stage_nodes" edges to the SopNode entity.
+func (ssuo *SopStageUpdateOne) ClearStageNodes() *SopStageUpdateOne {
+	ssuo.mutation.ClearStageNodes()
+	return ssuo
+}
+
+// RemoveStageNodeIDs removes the "stage_nodes" edge to SopNode entities by IDs.
+func (ssuo *SopStageUpdateOne) RemoveStageNodeIDs(ids ...uint64) *SopStageUpdateOne {
+	ssuo.mutation.RemoveStageNodeIDs(ids...)
+	return ssuo
+}
+
+// RemoveStageNodes removes "stage_nodes" edges to SopNode entities.
+func (ssuo *SopStageUpdateOne) RemoveStageNodes(s ...*SopNode) *SopStageUpdateOne {
+	ids := make([]uint64, len(s))
+	for i := range s {
+		ids[i] = s[i].ID
+	}
+	return ssuo.RemoveStageNodeIDs(ids...)
+}
+
+// Where appends a list predicates to the SopStageUpdate builder.
+func (ssuo *SopStageUpdateOne) Where(ps ...predicate.SopStage) *SopStageUpdateOne {
+	ssuo.mutation.Where(ps...)
+	return ssuo
+}
+
+// Select allows selecting one or more fields (columns) of the returned entity.
+// The default is selecting all fields defined in the entity schema.
+func (ssuo *SopStageUpdateOne) Select(field string, fields ...string) *SopStageUpdateOne {
+	ssuo.fields = append([]string{field}, fields...)
+	return ssuo
+}
+
+// Save executes the query and returns the updated SopStage entity.
+func (ssuo *SopStageUpdateOne) Save(ctx context.Context) (*SopStage, error) {
+	if err := ssuo.defaults(); err != nil {
+		return nil, err
+	}
+	return withHooks(ctx, ssuo.sqlSave, ssuo.mutation, ssuo.hooks)
+}
+
+// SaveX is like Save, but panics if an error occurs.
+func (ssuo *SopStageUpdateOne) SaveX(ctx context.Context) *SopStage {
+	node, err := ssuo.Save(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return node
+}
+
+// Exec executes the query on the entity.
+func (ssuo *SopStageUpdateOne) Exec(ctx context.Context) error {
+	_, err := ssuo.Save(ctx)
+	return err
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (ssuo *SopStageUpdateOne) ExecX(ctx context.Context) {
+	if err := ssuo.Exec(ctx); err != nil {
+		panic(err)
+	}
+}
+
+// defaults sets the default values of the builder before save.
+func (ssuo *SopStageUpdateOne) defaults() error {
+	if _, ok := ssuo.mutation.UpdatedAt(); !ok {
+		if sopstage.UpdateDefaultUpdatedAt == nil {
+			return fmt.Errorf("ent: uninitialized sopstage.UpdateDefaultUpdatedAt (forgotten import ent/runtime?)")
+		}
+		v := sopstage.UpdateDefaultUpdatedAt()
+		ssuo.mutation.SetUpdatedAt(v)
+	}
+	return nil
+}
+
+// check runs all checks and user-defined validators on the builder.
+func (ssuo *SopStageUpdateOne) check() error {
+	if _, ok := ssuo.mutation.SopTaskID(); ssuo.mutation.SopTaskCleared() && !ok {
+		return errors.New(`ent: clearing a required unique edge "SopStage.sop_task"`)
+	}
+	return nil
+}
+
+func (ssuo *SopStageUpdateOne) sqlSave(ctx context.Context) (_node *SopStage, err error) {
+	if err := ssuo.check(); err != nil {
+		return _node, err
+	}
+	_spec := sqlgraph.NewUpdateSpec(sopstage.Table, sopstage.Columns, sqlgraph.NewFieldSpec(sopstage.FieldID, field.TypeUint64))
+	id, ok := ssuo.mutation.ID()
+	if !ok {
+		return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "SopStage.id" for update`)}
+	}
+	_spec.Node.ID.Value = id
+	if fields := ssuo.fields; len(fields) > 0 {
+		_spec.Node.Columns = make([]string, 0, len(fields))
+		_spec.Node.Columns = append(_spec.Node.Columns, sopstage.FieldID)
+		for _, f := range fields {
+			if !sopstage.ValidColumn(f) {
+				return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
+			}
+			if f != sopstage.FieldID {
+				_spec.Node.Columns = append(_spec.Node.Columns, f)
+			}
+		}
+	}
+	if ps := ssuo.mutation.predicates; len(ps) > 0 {
+		_spec.Predicate = func(selector *sql.Selector) {
+			for i := range ps {
+				ps[i](selector)
+			}
+		}
+	}
+	if value, ok := ssuo.mutation.UpdatedAt(); ok {
+		_spec.SetField(sopstage.FieldUpdatedAt, field.TypeTime, value)
+	}
+	if value, ok := ssuo.mutation.Status(); ok {
+		_spec.SetField(sopstage.FieldStatus, field.TypeUint8, value)
+	}
+	if value, ok := ssuo.mutation.AddedStatus(); ok {
+		_spec.AddField(sopstage.FieldStatus, field.TypeUint8, value)
+	}
+	if ssuo.mutation.StatusCleared() {
+		_spec.ClearField(sopstage.FieldStatus, field.TypeUint8)
+	}
+	if value, ok := ssuo.mutation.DeletedAt(); ok {
+		_spec.SetField(sopstage.FieldDeletedAt, field.TypeTime, value)
+	}
+	if ssuo.mutation.DeletedAtCleared() {
+		_spec.ClearField(sopstage.FieldDeletedAt, field.TypeTime)
+	}
+	if value, ok := ssuo.mutation.Name(); ok {
+		_spec.SetField(sopstage.FieldName, field.TypeString, value)
+	}
+	if value, ok := ssuo.mutation.ConditionType(); ok {
+		_spec.SetField(sopstage.FieldConditionType, field.TypeInt, value)
+	}
+	if value, ok := ssuo.mutation.AddedConditionType(); ok {
+		_spec.AddField(sopstage.FieldConditionType, field.TypeInt, value)
+	}
+	if value, ok := ssuo.mutation.ConditionOperator(); ok {
+		_spec.SetField(sopstage.FieldConditionOperator, field.TypeInt, value)
+	}
+	if value, ok := ssuo.mutation.AddedConditionOperator(); ok {
+		_spec.AddField(sopstage.FieldConditionOperator, field.TypeInt, value)
+	}
+	if value, ok := ssuo.mutation.ConditionList(); ok {
+		_spec.SetField(sopstage.FieldConditionList, field.TypeJSON, value)
+	}
+	if value, ok := ssuo.mutation.AppendedConditionList(); ok {
+		_spec.AddModifier(func(u *sql.UpdateBuilder) {
+			sqljson.Append(u, sopstage.FieldConditionList, value)
+		})
+	}
+	if value, ok := ssuo.mutation.ActionMessage(); ok {
+		_spec.SetField(sopstage.FieldActionMessage, field.TypeJSON, value)
+	}
+	if value, ok := ssuo.mutation.AppendedActionMessage(); ok {
+		_spec.AddModifier(func(u *sql.UpdateBuilder) {
+			sqljson.Append(u, sopstage.FieldActionMessage, value)
+		})
+	}
+	if value, ok := ssuo.mutation.ActionLabel(); ok {
+		_spec.SetField(sopstage.FieldActionLabel, field.TypeJSON, value)
+	}
+	if value, ok := ssuo.mutation.AppendedActionLabel(); ok {
+		_spec.AddModifier(func(u *sql.UpdateBuilder) {
+			sqljson.Append(u, sopstage.FieldActionLabel, value)
+		})
+	}
+	if value, ok := ssuo.mutation.IndexSort(); ok {
+		_spec.SetField(sopstage.FieldIndexSort, field.TypeInt, value)
+	}
+	if value, ok := ssuo.mutation.AddedIndexSort(); ok {
+		_spec.AddField(sopstage.FieldIndexSort, field.TypeInt, value)
+	}
+	if ssuo.mutation.SopTaskCleared() {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.M2O,
+			Inverse: true,
+			Table:   sopstage.SopTaskTable,
+			Columns: []string{sopstage.SopTaskColumn},
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: sqlgraph.NewFieldSpec(soptask.FieldID, field.TypeUint64),
+			},
+		}
+		_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
+	}
+	if nodes := ssuo.mutation.SopTaskIDs(); len(nodes) > 0 {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.M2O,
+			Inverse: true,
+			Table:   sopstage.SopTaskTable,
+			Columns: []string{sopstage.SopTaskColumn},
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: sqlgraph.NewFieldSpec(soptask.FieldID, field.TypeUint64),
+			},
+		}
+		for _, k := range nodes {
+			edge.Target.Nodes = append(edge.Target.Nodes, k)
+		}
+		_spec.Edges.Add = append(_spec.Edges.Add, edge)
+	}
+	if ssuo.mutation.StageNodesCleared() {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.O2M,
+			Inverse: false,
+			Table:   sopstage.StageNodesTable,
+			Columns: []string{sopstage.StageNodesColumn},
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: sqlgraph.NewFieldSpec(sopnode.FieldID, field.TypeUint64),
+			},
+		}
+		_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
+	}
+	if nodes := ssuo.mutation.RemovedStageNodesIDs(); len(nodes) > 0 && !ssuo.mutation.StageNodesCleared() {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.O2M,
+			Inverse: false,
+			Table:   sopstage.StageNodesTable,
+			Columns: []string{sopstage.StageNodesColumn},
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: sqlgraph.NewFieldSpec(sopnode.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 := ssuo.mutation.StageNodesIDs(); len(nodes) > 0 {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.O2M,
+			Inverse: false,
+			Table:   sopstage.StageNodesTable,
+			Columns: []string{sopstage.StageNodesColumn},
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: sqlgraph.NewFieldSpec(sopnode.FieldID, field.TypeUint64),
+			},
+		}
+		for _, k := range nodes {
+			edge.Target.Nodes = append(edge.Target.Nodes, k)
+		}
+		_spec.Edges.Add = append(_spec.Edges.Add, edge)
+	}
+	_node = &SopStage{config: ssuo.config}
+	_spec.Assign = _node.assignValues
+	_spec.ScanValues = _node.scanValues
+	if err = sqlgraph.UpdateNode(ctx, ssuo.driver, _spec); err != nil {
+		if _, ok := err.(*sqlgraph.NotFoundError); ok {
+			err = &NotFoundError{sopstage.Label}
+		} else if sqlgraph.IsConstraintError(err) {
+			err = &ConstraintError{msg: err.Error(), wrap: err}
+		}
+		return nil, err
+	}
+	ssuo.mutation.done = true
+	return _node, nil
+}

+ 236 - 0
ent/soptask.go

@@ -0,0 +1,236 @@
+// Code generated by ent, DO NOT EDIT.
+
+package ent
+
+import (
+	"encoding/json"
+	"fmt"
+	"strings"
+	"time"
+	"wechat-api/ent/soptask"
+
+	"entgo.io/ent"
+	"entgo.io/ent/dialect/sql"
+)
+
+// SopTask is the model entity for the SopTask schema.
+type SopTask 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"`
+	// Status 1: normal 2: ban | 状态 1 正常 2 禁用
+	Status uint8 `json:"status,omitempty"`
+	// Delete Time | 删除日期
+	DeletedAt time.Time `json:"deleted_at,omitempty"`
+	// SOP 任务名称
+	Name string `json:"name,omitempty"`
+	// 机器人微信 id 列表
+	BotWxidList []string `json:"bot_wxid_list,omitempty"`
+	// 标签类型:1好友,2群组,3企业微信联系人
+	Type int `json:"type,omitempty"`
+	// 任务计划开始时间
+	PlanStartTime time.Time `json:"plan_start_time,omitempty"`
+	// 任务计划结束时间
+	PlanEndTime time.Time `json:"plan_end_time,omitempty"`
+	// 创建者 id
+	CreatorID string `json:"creator_id,omitempty"`
+	// Edges holds the relations/edges for other nodes in the graph.
+	// The values are being populated by the SopTaskQuery when eager-loading is set.
+	Edges        SopTaskEdges `json:"edges"`
+	selectValues sql.SelectValues
+}
+
+// SopTaskEdges holds the relations/edges for other nodes in the graph.
+type SopTaskEdges struct {
+	// TaskStages holds the value of the task_stages edge.
+	TaskStages []*SopStage `json:"task_stages,omitempty"`
+	// loadedTypes holds the information for reporting if a
+	// type was loaded (or requested) in eager-loading or not.
+	loadedTypes [1]bool
+}
+
+// TaskStagesOrErr returns the TaskStages value or an error if the edge
+// was not loaded in eager-loading.
+func (e SopTaskEdges) TaskStagesOrErr() ([]*SopStage, error) {
+	if e.loadedTypes[0] {
+		return e.TaskStages, nil
+	}
+	return nil, &NotLoadedError{edge: "task_stages"}
+}
+
+// scanValues returns the types for scanning values from sql.Rows.
+func (*SopTask) scanValues(columns []string) ([]any, error) {
+	values := make([]any, len(columns))
+	for i := range columns {
+		switch columns[i] {
+		case soptask.FieldBotWxidList:
+			values[i] = new([]byte)
+		case soptask.FieldID, soptask.FieldStatus, soptask.FieldType:
+			values[i] = new(sql.NullInt64)
+		case soptask.FieldName, soptask.FieldCreatorID:
+			values[i] = new(sql.NullString)
+		case soptask.FieldCreatedAt, soptask.FieldUpdatedAt, soptask.FieldDeletedAt, soptask.FieldPlanStartTime, soptask.FieldPlanEndTime:
+			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 SopTask fields.
+func (st *SopTask) 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 soptask.FieldID:
+			value, ok := values[i].(*sql.NullInt64)
+			if !ok {
+				return fmt.Errorf("unexpected type %T for field id", value)
+			}
+			st.ID = uint64(value.Int64)
+		case soptask.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 {
+				st.CreatedAt = value.Time
+			}
+		case soptask.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 {
+				st.UpdatedAt = value.Time
+			}
+		case soptask.FieldStatus:
+			if value, ok := values[i].(*sql.NullInt64); !ok {
+				return fmt.Errorf("unexpected type %T for field status", values[i])
+			} else if value.Valid {
+				st.Status = uint8(value.Int64)
+			}
+		case soptask.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 {
+				st.DeletedAt = value.Time
+			}
+		case soptask.FieldName:
+			if value, ok := values[i].(*sql.NullString); !ok {
+				return fmt.Errorf("unexpected type %T for field name", values[i])
+			} else if value.Valid {
+				st.Name = value.String
+			}
+		case soptask.FieldBotWxidList:
+			if value, ok := values[i].(*[]byte); !ok {
+				return fmt.Errorf("unexpected type %T for field bot_wxid_list", values[i])
+			} else if value != nil && len(*value) > 0 {
+				if err := json.Unmarshal(*value, &st.BotWxidList); err != nil {
+					return fmt.Errorf("unmarshal field bot_wxid_list: %w", err)
+				}
+			}
+		case soptask.FieldType:
+			if value, ok := values[i].(*sql.NullInt64); !ok {
+				return fmt.Errorf("unexpected type %T for field type", values[i])
+			} else if value.Valid {
+				st.Type = int(value.Int64)
+			}
+		case soptask.FieldPlanStartTime:
+			if value, ok := values[i].(*sql.NullTime); !ok {
+				return fmt.Errorf("unexpected type %T for field plan_start_time", values[i])
+			} else if value.Valid {
+				st.PlanStartTime = value.Time
+			}
+		case soptask.FieldPlanEndTime:
+			if value, ok := values[i].(*sql.NullTime); !ok {
+				return fmt.Errorf("unexpected type %T for field plan_end_time", values[i])
+			} else if value.Valid {
+				st.PlanEndTime = value.Time
+			}
+		case soptask.FieldCreatorID:
+			if value, ok := values[i].(*sql.NullString); !ok {
+				return fmt.Errorf("unexpected type %T for field creator_id", values[i])
+			} else if value.Valid {
+				st.CreatorID = value.String
+			}
+		default:
+			st.selectValues.Set(columns[i], values[i])
+		}
+	}
+	return nil
+}
+
+// Value returns the ent.Value that was dynamically selected and assigned to the SopTask.
+// This includes values selected through modifiers, order, etc.
+func (st *SopTask) Value(name string) (ent.Value, error) {
+	return st.selectValues.Get(name)
+}
+
+// QueryTaskStages queries the "task_stages" edge of the SopTask entity.
+func (st *SopTask) QueryTaskStages() *SopStageQuery {
+	return NewSopTaskClient(st.config).QueryTaskStages(st)
+}
+
+// Update returns a builder for updating this SopTask.
+// Note that you need to call SopTask.Unwrap() before calling this method if this SopTask
+// was returned from a transaction, and the transaction was committed or rolled back.
+func (st *SopTask) Update() *SopTaskUpdateOne {
+	return NewSopTaskClient(st.config).UpdateOne(st)
+}
+
+// Unwrap unwraps the SopTask 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 (st *SopTask) Unwrap() *SopTask {
+	_tx, ok := st.config.driver.(*txDriver)
+	if !ok {
+		panic("ent: SopTask is not a transactional entity")
+	}
+	st.config.driver = _tx.drv
+	return st
+}
+
+// String implements the fmt.Stringer.
+func (st *SopTask) String() string {
+	var builder strings.Builder
+	builder.WriteString("SopTask(")
+	builder.WriteString(fmt.Sprintf("id=%v, ", st.ID))
+	builder.WriteString("created_at=")
+	builder.WriteString(st.CreatedAt.Format(time.ANSIC))
+	builder.WriteString(", ")
+	builder.WriteString("updated_at=")
+	builder.WriteString(st.UpdatedAt.Format(time.ANSIC))
+	builder.WriteString(", ")
+	builder.WriteString("status=")
+	builder.WriteString(fmt.Sprintf("%v", st.Status))
+	builder.WriteString(", ")
+	builder.WriteString("deleted_at=")
+	builder.WriteString(st.DeletedAt.Format(time.ANSIC))
+	builder.WriteString(", ")
+	builder.WriteString("name=")
+	builder.WriteString(st.Name)
+	builder.WriteString(", ")
+	builder.WriteString("bot_wxid_list=")
+	builder.WriteString(fmt.Sprintf("%v", st.BotWxidList))
+	builder.WriteString(", ")
+	builder.WriteString("type=")
+	builder.WriteString(fmt.Sprintf("%v", st.Type))
+	builder.WriteString(", ")
+	builder.WriteString("plan_start_time=")
+	builder.WriteString(st.PlanStartTime.Format(time.ANSIC))
+	builder.WriteString(", ")
+	builder.WriteString("plan_end_time=")
+	builder.WriteString(st.PlanEndTime.Format(time.ANSIC))
+	builder.WriteString(", ")
+	builder.WriteString("creator_id=")
+	builder.WriteString(st.CreatorID)
+	builder.WriteByte(')')
+	return builder.String()
+}
+
+// SopTasks is a parsable slice of SopTask.
+type SopTasks []*SopTask

+ 170 - 0
ent/soptask/soptask.go

@@ -0,0 +1,170 @@
+// Code generated by ent, DO NOT EDIT.
+
+package soptask
+
+import (
+	"time"
+
+	"entgo.io/ent"
+	"entgo.io/ent/dialect/sql"
+	"entgo.io/ent/dialect/sql/sqlgraph"
+)
+
+const (
+	// Label holds the string label denoting the soptask type in the database.
+	Label = "sop_task"
+	// 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"
+	// FieldStatus holds the string denoting the status field in the database.
+	FieldStatus = "status"
+	// FieldDeletedAt holds the string denoting the deleted_at field in the database.
+	FieldDeletedAt = "deleted_at"
+	// FieldName holds the string denoting the name field in the database.
+	FieldName = "name"
+	// FieldBotWxidList holds the string denoting the bot_wxid_list field in the database.
+	FieldBotWxidList = "bot_wxid_list"
+	// FieldType holds the string denoting the type field in the database.
+	FieldType = "type"
+	// FieldPlanStartTime holds the string denoting the plan_start_time field in the database.
+	FieldPlanStartTime = "plan_start_time"
+	// FieldPlanEndTime holds the string denoting the plan_end_time field in the database.
+	FieldPlanEndTime = "plan_end_time"
+	// FieldCreatorID holds the string denoting the creator_id field in the database.
+	FieldCreatorID = "creator_id"
+	// EdgeTaskStages holds the string denoting the task_stages edge name in mutations.
+	EdgeTaskStages = "task_stages"
+	// Table holds the table name of the soptask in the database.
+	Table = "sop_task"
+	// TaskStagesTable is the table that holds the task_stages relation/edge.
+	TaskStagesTable = "sop_stage"
+	// TaskStagesInverseTable is the table name for the SopStage entity.
+	// It exists in this package in order to avoid circular dependency with the "sopstage" package.
+	TaskStagesInverseTable = "sop_stage"
+	// TaskStagesColumn is the table column denoting the task_stages relation/edge.
+	TaskStagesColumn = "task_id"
+)
+
+// Columns holds all SQL columns for soptask fields.
+var Columns = []string{
+	FieldID,
+	FieldCreatedAt,
+	FieldUpdatedAt,
+	FieldStatus,
+	FieldDeletedAt,
+	FieldName,
+	FieldBotWxidList,
+	FieldType,
+	FieldPlanStartTime,
+	FieldPlanEndTime,
+	FieldCreatorID,
+}
+
+// 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
+	// DefaultStatus holds the default value on creation for the "status" field.
+	DefaultStatus uint8
+	// NameValidator is a validator for the "name" field. It is called by the builders before save.
+	NameValidator func(string) error
+	// DefaultType holds the default value on creation for the "type" field.
+	DefaultType int
+)
+
+// OrderOption defines the ordering options for the SopTask 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()
+}
+
+// ByStatus orders the results by the status field.
+func ByStatus(opts ...sql.OrderTermOption) OrderOption {
+	return sql.OrderByField(FieldStatus, opts...).ToFunc()
+}
+
+// ByDeletedAt orders the results by the deleted_at field.
+func ByDeletedAt(opts ...sql.OrderTermOption) OrderOption {
+	return sql.OrderByField(FieldDeletedAt, opts...).ToFunc()
+}
+
+// ByName orders the results by the name field.
+func ByName(opts ...sql.OrderTermOption) OrderOption {
+	return sql.OrderByField(FieldName, opts...).ToFunc()
+}
+
+// ByType orders the results by the type field.
+func ByType(opts ...sql.OrderTermOption) OrderOption {
+	return sql.OrderByField(FieldType, opts...).ToFunc()
+}
+
+// ByPlanStartTime orders the results by the plan_start_time field.
+func ByPlanStartTime(opts ...sql.OrderTermOption) OrderOption {
+	return sql.OrderByField(FieldPlanStartTime, opts...).ToFunc()
+}
+
+// ByPlanEndTime orders the results by the plan_end_time field.
+func ByPlanEndTime(opts ...sql.OrderTermOption) OrderOption {
+	return sql.OrderByField(FieldPlanEndTime, opts...).ToFunc()
+}
+
+// ByCreatorID orders the results by the creator_id field.
+func ByCreatorID(opts ...sql.OrderTermOption) OrderOption {
+	return sql.OrderByField(FieldCreatorID, opts...).ToFunc()
+}
+
+// ByTaskStagesCount orders the results by task_stages count.
+func ByTaskStagesCount(opts ...sql.OrderTermOption) OrderOption {
+	return func(s *sql.Selector) {
+		sqlgraph.OrderByNeighborsCount(s, newTaskStagesStep(), opts...)
+	}
+}
+
+// ByTaskStages orders the results by task_stages terms.
+func ByTaskStages(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption {
+	return func(s *sql.Selector) {
+		sqlgraph.OrderByNeighborTerms(s, newTaskStagesStep(), append([]sql.OrderTerm{term}, terms...)...)
+	}
+}
+func newTaskStagesStep() *sqlgraph.Step {
+	return sqlgraph.NewStep(
+		sqlgraph.From(Table, FieldID),
+		sqlgraph.To(TaskStagesInverseTable, FieldID),
+		sqlgraph.Edge(sqlgraph.O2M, false, TaskStagesTable, TaskStagesColumn),
+	)
+}

+ 609 - 0
ent/soptask/where.go

@@ -0,0 +1,609 @@
+// Code generated by ent, DO NOT EDIT.
+
+package soptask
+
+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.SopTask {
+	return predicate.SopTask(sql.FieldEQ(FieldID, id))
+}
+
+// IDEQ applies the EQ predicate on the ID field.
+func IDEQ(id uint64) predicate.SopTask {
+	return predicate.SopTask(sql.FieldEQ(FieldID, id))
+}
+
+// IDNEQ applies the NEQ predicate on the ID field.
+func IDNEQ(id uint64) predicate.SopTask {
+	return predicate.SopTask(sql.FieldNEQ(FieldID, id))
+}
+
+// IDIn applies the In predicate on the ID field.
+func IDIn(ids ...uint64) predicate.SopTask {
+	return predicate.SopTask(sql.FieldIn(FieldID, ids...))
+}
+
+// IDNotIn applies the NotIn predicate on the ID field.
+func IDNotIn(ids ...uint64) predicate.SopTask {
+	return predicate.SopTask(sql.FieldNotIn(FieldID, ids...))
+}
+
+// IDGT applies the GT predicate on the ID field.
+func IDGT(id uint64) predicate.SopTask {
+	return predicate.SopTask(sql.FieldGT(FieldID, id))
+}
+
+// IDGTE applies the GTE predicate on the ID field.
+func IDGTE(id uint64) predicate.SopTask {
+	return predicate.SopTask(sql.FieldGTE(FieldID, id))
+}
+
+// IDLT applies the LT predicate on the ID field.
+func IDLT(id uint64) predicate.SopTask {
+	return predicate.SopTask(sql.FieldLT(FieldID, id))
+}
+
+// IDLTE applies the LTE predicate on the ID field.
+func IDLTE(id uint64) predicate.SopTask {
+	return predicate.SopTask(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.SopTask {
+	return predicate.SopTask(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.SopTask {
+	return predicate.SopTask(sql.FieldEQ(FieldUpdatedAt, v))
+}
+
+// Status applies equality check predicate on the "status" field. It's identical to StatusEQ.
+func Status(v uint8) predicate.SopTask {
+	return predicate.SopTask(sql.FieldEQ(FieldStatus, v))
+}
+
+// DeletedAt applies equality check predicate on the "deleted_at" field. It's identical to DeletedAtEQ.
+func DeletedAt(v time.Time) predicate.SopTask {
+	return predicate.SopTask(sql.FieldEQ(FieldDeletedAt, v))
+}
+
+// Name applies equality check predicate on the "name" field. It's identical to NameEQ.
+func Name(v string) predicate.SopTask {
+	return predicate.SopTask(sql.FieldEQ(FieldName, v))
+}
+
+// Type applies equality check predicate on the "type" field. It's identical to TypeEQ.
+func Type(v int) predicate.SopTask {
+	return predicate.SopTask(sql.FieldEQ(FieldType, v))
+}
+
+// PlanStartTime applies equality check predicate on the "plan_start_time" field. It's identical to PlanStartTimeEQ.
+func PlanStartTime(v time.Time) predicate.SopTask {
+	return predicate.SopTask(sql.FieldEQ(FieldPlanStartTime, v))
+}
+
+// PlanEndTime applies equality check predicate on the "plan_end_time" field. It's identical to PlanEndTimeEQ.
+func PlanEndTime(v time.Time) predicate.SopTask {
+	return predicate.SopTask(sql.FieldEQ(FieldPlanEndTime, v))
+}
+
+// CreatorID applies equality check predicate on the "creator_id" field. It's identical to CreatorIDEQ.
+func CreatorID(v string) predicate.SopTask {
+	return predicate.SopTask(sql.FieldEQ(FieldCreatorID, v))
+}
+
+// CreatedAtEQ applies the EQ predicate on the "created_at" field.
+func CreatedAtEQ(v time.Time) predicate.SopTask {
+	return predicate.SopTask(sql.FieldEQ(FieldCreatedAt, v))
+}
+
+// CreatedAtNEQ applies the NEQ predicate on the "created_at" field.
+func CreatedAtNEQ(v time.Time) predicate.SopTask {
+	return predicate.SopTask(sql.FieldNEQ(FieldCreatedAt, v))
+}
+
+// CreatedAtIn applies the In predicate on the "created_at" field.
+func CreatedAtIn(vs ...time.Time) predicate.SopTask {
+	return predicate.SopTask(sql.FieldIn(FieldCreatedAt, vs...))
+}
+
+// CreatedAtNotIn applies the NotIn predicate on the "created_at" field.
+func CreatedAtNotIn(vs ...time.Time) predicate.SopTask {
+	return predicate.SopTask(sql.FieldNotIn(FieldCreatedAt, vs...))
+}
+
+// CreatedAtGT applies the GT predicate on the "created_at" field.
+func CreatedAtGT(v time.Time) predicate.SopTask {
+	return predicate.SopTask(sql.FieldGT(FieldCreatedAt, v))
+}
+
+// CreatedAtGTE applies the GTE predicate on the "created_at" field.
+func CreatedAtGTE(v time.Time) predicate.SopTask {
+	return predicate.SopTask(sql.FieldGTE(FieldCreatedAt, v))
+}
+
+// CreatedAtLT applies the LT predicate on the "created_at" field.
+func CreatedAtLT(v time.Time) predicate.SopTask {
+	return predicate.SopTask(sql.FieldLT(FieldCreatedAt, v))
+}
+
+// CreatedAtLTE applies the LTE predicate on the "created_at" field.
+func CreatedAtLTE(v time.Time) predicate.SopTask {
+	return predicate.SopTask(sql.FieldLTE(FieldCreatedAt, v))
+}
+
+// UpdatedAtEQ applies the EQ predicate on the "updated_at" field.
+func UpdatedAtEQ(v time.Time) predicate.SopTask {
+	return predicate.SopTask(sql.FieldEQ(FieldUpdatedAt, v))
+}
+
+// UpdatedAtNEQ applies the NEQ predicate on the "updated_at" field.
+func UpdatedAtNEQ(v time.Time) predicate.SopTask {
+	return predicate.SopTask(sql.FieldNEQ(FieldUpdatedAt, v))
+}
+
+// UpdatedAtIn applies the In predicate on the "updated_at" field.
+func UpdatedAtIn(vs ...time.Time) predicate.SopTask {
+	return predicate.SopTask(sql.FieldIn(FieldUpdatedAt, vs...))
+}
+
+// UpdatedAtNotIn applies the NotIn predicate on the "updated_at" field.
+func UpdatedAtNotIn(vs ...time.Time) predicate.SopTask {
+	return predicate.SopTask(sql.FieldNotIn(FieldUpdatedAt, vs...))
+}
+
+// UpdatedAtGT applies the GT predicate on the "updated_at" field.
+func UpdatedAtGT(v time.Time) predicate.SopTask {
+	return predicate.SopTask(sql.FieldGT(FieldUpdatedAt, v))
+}
+
+// UpdatedAtGTE applies the GTE predicate on the "updated_at" field.
+func UpdatedAtGTE(v time.Time) predicate.SopTask {
+	return predicate.SopTask(sql.FieldGTE(FieldUpdatedAt, v))
+}
+
+// UpdatedAtLT applies the LT predicate on the "updated_at" field.
+func UpdatedAtLT(v time.Time) predicate.SopTask {
+	return predicate.SopTask(sql.FieldLT(FieldUpdatedAt, v))
+}
+
+// UpdatedAtLTE applies the LTE predicate on the "updated_at" field.
+func UpdatedAtLTE(v time.Time) predicate.SopTask {
+	return predicate.SopTask(sql.FieldLTE(FieldUpdatedAt, v))
+}
+
+// StatusEQ applies the EQ predicate on the "status" field.
+func StatusEQ(v uint8) predicate.SopTask {
+	return predicate.SopTask(sql.FieldEQ(FieldStatus, v))
+}
+
+// StatusNEQ applies the NEQ predicate on the "status" field.
+func StatusNEQ(v uint8) predicate.SopTask {
+	return predicate.SopTask(sql.FieldNEQ(FieldStatus, v))
+}
+
+// StatusIn applies the In predicate on the "status" field.
+func StatusIn(vs ...uint8) predicate.SopTask {
+	return predicate.SopTask(sql.FieldIn(FieldStatus, vs...))
+}
+
+// StatusNotIn applies the NotIn predicate on the "status" field.
+func StatusNotIn(vs ...uint8) predicate.SopTask {
+	return predicate.SopTask(sql.FieldNotIn(FieldStatus, vs...))
+}
+
+// StatusGT applies the GT predicate on the "status" field.
+func StatusGT(v uint8) predicate.SopTask {
+	return predicate.SopTask(sql.FieldGT(FieldStatus, v))
+}
+
+// StatusGTE applies the GTE predicate on the "status" field.
+func StatusGTE(v uint8) predicate.SopTask {
+	return predicate.SopTask(sql.FieldGTE(FieldStatus, v))
+}
+
+// StatusLT applies the LT predicate on the "status" field.
+func StatusLT(v uint8) predicate.SopTask {
+	return predicate.SopTask(sql.FieldLT(FieldStatus, v))
+}
+
+// StatusLTE applies the LTE predicate on the "status" field.
+func StatusLTE(v uint8) predicate.SopTask {
+	return predicate.SopTask(sql.FieldLTE(FieldStatus, v))
+}
+
+// StatusIsNil applies the IsNil predicate on the "status" field.
+func StatusIsNil() predicate.SopTask {
+	return predicate.SopTask(sql.FieldIsNull(FieldStatus))
+}
+
+// StatusNotNil applies the NotNil predicate on the "status" field.
+func StatusNotNil() predicate.SopTask {
+	return predicate.SopTask(sql.FieldNotNull(FieldStatus))
+}
+
+// DeletedAtEQ applies the EQ predicate on the "deleted_at" field.
+func DeletedAtEQ(v time.Time) predicate.SopTask {
+	return predicate.SopTask(sql.FieldEQ(FieldDeletedAt, v))
+}
+
+// DeletedAtNEQ applies the NEQ predicate on the "deleted_at" field.
+func DeletedAtNEQ(v time.Time) predicate.SopTask {
+	return predicate.SopTask(sql.FieldNEQ(FieldDeletedAt, v))
+}
+
+// DeletedAtIn applies the In predicate on the "deleted_at" field.
+func DeletedAtIn(vs ...time.Time) predicate.SopTask {
+	return predicate.SopTask(sql.FieldIn(FieldDeletedAt, vs...))
+}
+
+// DeletedAtNotIn applies the NotIn predicate on the "deleted_at" field.
+func DeletedAtNotIn(vs ...time.Time) predicate.SopTask {
+	return predicate.SopTask(sql.FieldNotIn(FieldDeletedAt, vs...))
+}
+
+// DeletedAtGT applies the GT predicate on the "deleted_at" field.
+func DeletedAtGT(v time.Time) predicate.SopTask {
+	return predicate.SopTask(sql.FieldGT(FieldDeletedAt, v))
+}
+
+// DeletedAtGTE applies the GTE predicate on the "deleted_at" field.
+func DeletedAtGTE(v time.Time) predicate.SopTask {
+	return predicate.SopTask(sql.FieldGTE(FieldDeletedAt, v))
+}
+
+// DeletedAtLT applies the LT predicate on the "deleted_at" field.
+func DeletedAtLT(v time.Time) predicate.SopTask {
+	return predicate.SopTask(sql.FieldLT(FieldDeletedAt, v))
+}
+
+// DeletedAtLTE applies the LTE predicate on the "deleted_at" field.
+func DeletedAtLTE(v time.Time) predicate.SopTask {
+	return predicate.SopTask(sql.FieldLTE(FieldDeletedAt, v))
+}
+
+// DeletedAtIsNil applies the IsNil predicate on the "deleted_at" field.
+func DeletedAtIsNil() predicate.SopTask {
+	return predicate.SopTask(sql.FieldIsNull(FieldDeletedAt))
+}
+
+// DeletedAtNotNil applies the NotNil predicate on the "deleted_at" field.
+func DeletedAtNotNil() predicate.SopTask {
+	return predicate.SopTask(sql.FieldNotNull(FieldDeletedAt))
+}
+
+// NameEQ applies the EQ predicate on the "name" field.
+func NameEQ(v string) predicate.SopTask {
+	return predicate.SopTask(sql.FieldEQ(FieldName, v))
+}
+
+// NameNEQ applies the NEQ predicate on the "name" field.
+func NameNEQ(v string) predicate.SopTask {
+	return predicate.SopTask(sql.FieldNEQ(FieldName, v))
+}
+
+// NameIn applies the In predicate on the "name" field.
+func NameIn(vs ...string) predicate.SopTask {
+	return predicate.SopTask(sql.FieldIn(FieldName, vs...))
+}
+
+// NameNotIn applies the NotIn predicate on the "name" field.
+func NameNotIn(vs ...string) predicate.SopTask {
+	return predicate.SopTask(sql.FieldNotIn(FieldName, vs...))
+}
+
+// NameGT applies the GT predicate on the "name" field.
+func NameGT(v string) predicate.SopTask {
+	return predicate.SopTask(sql.FieldGT(FieldName, v))
+}
+
+// NameGTE applies the GTE predicate on the "name" field.
+func NameGTE(v string) predicate.SopTask {
+	return predicate.SopTask(sql.FieldGTE(FieldName, v))
+}
+
+// NameLT applies the LT predicate on the "name" field.
+func NameLT(v string) predicate.SopTask {
+	return predicate.SopTask(sql.FieldLT(FieldName, v))
+}
+
+// NameLTE applies the LTE predicate on the "name" field.
+func NameLTE(v string) predicate.SopTask {
+	return predicate.SopTask(sql.FieldLTE(FieldName, v))
+}
+
+// NameContains applies the Contains predicate on the "name" field.
+func NameContains(v string) predicate.SopTask {
+	return predicate.SopTask(sql.FieldContains(FieldName, v))
+}
+
+// NameHasPrefix applies the HasPrefix predicate on the "name" field.
+func NameHasPrefix(v string) predicate.SopTask {
+	return predicate.SopTask(sql.FieldHasPrefix(FieldName, v))
+}
+
+// NameHasSuffix applies the HasSuffix predicate on the "name" field.
+func NameHasSuffix(v string) predicate.SopTask {
+	return predicate.SopTask(sql.FieldHasSuffix(FieldName, v))
+}
+
+// NameEqualFold applies the EqualFold predicate on the "name" field.
+func NameEqualFold(v string) predicate.SopTask {
+	return predicate.SopTask(sql.FieldEqualFold(FieldName, v))
+}
+
+// NameContainsFold applies the ContainsFold predicate on the "name" field.
+func NameContainsFold(v string) predicate.SopTask {
+	return predicate.SopTask(sql.FieldContainsFold(FieldName, v))
+}
+
+// BotWxidListIsNil applies the IsNil predicate on the "bot_wxid_list" field.
+func BotWxidListIsNil() predicate.SopTask {
+	return predicate.SopTask(sql.FieldIsNull(FieldBotWxidList))
+}
+
+// BotWxidListNotNil applies the NotNil predicate on the "bot_wxid_list" field.
+func BotWxidListNotNil() predicate.SopTask {
+	return predicate.SopTask(sql.FieldNotNull(FieldBotWxidList))
+}
+
+// TypeEQ applies the EQ predicate on the "type" field.
+func TypeEQ(v int) predicate.SopTask {
+	return predicate.SopTask(sql.FieldEQ(FieldType, v))
+}
+
+// TypeNEQ applies the NEQ predicate on the "type" field.
+func TypeNEQ(v int) predicate.SopTask {
+	return predicate.SopTask(sql.FieldNEQ(FieldType, v))
+}
+
+// TypeIn applies the In predicate on the "type" field.
+func TypeIn(vs ...int) predicate.SopTask {
+	return predicate.SopTask(sql.FieldIn(FieldType, vs...))
+}
+
+// TypeNotIn applies the NotIn predicate on the "type" field.
+func TypeNotIn(vs ...int) predicate.SopTask {
+	return predicate.SopTask(sql.FieldNotIn(FieldType, vs...))
+}
+
+// TypeGT applies the GT predicate on the "type" field.
+func TypeGT(v int) predicate.SopTask {
+	return predicate.SopTask(sql.FieldGT(FieldType, v))
+}
+
+// TypeGTE applies the GTE predicate on the "type" field.
+func TypeGTE(v int) predicate.SopTask {
+	return predicate.SopTask(sql.FieldGTE(FieldType, v))
+}
+
+// TypeLT applies the LT predicate on the "type" field.
+func TypeLT(v int) predicate.SopTask {
+	return predicate.SopTask(sql.FieldLT(FieldType, v))
+}
+
+// TypeLTE applies the LTE predicate on the "type" field.
+func TypeLTE(v int) predicate.SopTask {
+	return predicate.SopTask(sql.FieldLTE(FieldType, v))
+}
+
+// PlanStartTimeEQ applies the EQ predicate on the "plan_start_time" field.
+func PlanStartTimeEQ(v time.Time) predicate.SopTask {
+	return predicate.SopTask(sql.FieldEQ(FieldPlanStartTime, v))
+}
+
+// PlanStartTimeNEQ applies the NEQ predicate on the "plan_start_time" field.
+func PlanStartTimeNEQ(v time.Time) predicate.SopTask {
+	return predicate.SopTask(sql.FieldNEQ(FieldPlanStartTime, v))
+}
+
+// PlanStartTimeIn applies the In predicate on the "plan_start_time" field.
+func PlanStartTimeIn(vs ...time.Time) predicate.SopTask {
+	return predicate.SopTask(sql.FieldIn(FieldPlanStartTime, vs...))
+}
+
+// PlanStartTimeNotIn applies the NotIn predicate on the "plan_start_time" field.
+func PlanStartTimeNotIn(vs ...time.Time) predicate.SopTask {
+	return predicate.SopTask(sql.FieldNotIn(FieldPlanStartTime, vs...))
+}
+
+// PlanStartTimeGT applies the GT predicate on the "plan_start_time" field.
+func PlanStartTimeGT(v time.Time) predicate.SopTask {
+	return predicate.SopTask(sql.FieldGT(FieldPlanStartTime, v))
+}
+
+// PlanStartTimeGTE applies the GTE predicate on the "plan_start_time" field.
+func PlanStartTimeGTE(v time.Time) predicate.SopTask {
+	return predicate.SopTask(sql.FieldGTE(FieldPlanStartTime, v))
+}
+
+// PlanStartTimeLT applies the LT predicate on the "plan_start_time" field.
+func PlanStartTimeLT(v time.Time) predicate.SopTask {
+	return predicate.SopTask(sql.FieldLT(FieldPlanStartTime, v))
+}
+
+// PlanStartTimeLTE applies the LTE predicate on the "plan_start_time" field.
+func PlanStartTimeLTE(v time.Time) predicate.SopTask {
+	return predicate.SopTask(sql.FieldLTE(FieldPlanStartTime, v))
+}
+
+// PlanStartTimeIsNil applies the IsNil predicate on the "plan_start_time" field.
+func PlanStartTimeIsNil() predicate.SopTask {
+	return predicate.SopTask(sql.FieldIsNull(FieldPlanStartTime))
+}
+
+// PlanStartTimeNotNil applies the NotNil predicate on the "plan_start_time" field.
+func PlanStartTimeNotNil() predicate.SopTask {
+	return predicate.SopTask(sql.FieldNotNull(FieldPlanStartTime))
+}
+
+// PlanEndTimeEQ applies the EQ predicate on the "plan_end_time" field.
+func PlanEndTimeEQ(v time.Time) predicate.SopTask {
+	return predicate.SopTask(sql.FieldEQ(FieldPlanEndTime, v))
+}
+
+// PlanEndTimeNEQ applies the NEQ predicate on the "plan_end_time" field.
+func PlanEndTimeNEQ(v time.Time) predicate.SopTask {
+	return predicate.SopTask(sql.FieldNEQ(FieldPlanEndTime, v))
+}
+
+// PlanEndTimeIn applies the In predicate on the "plan_end_time" field.
+func PlanEndTimeIn(vs ...time.Time) predicate.SopTask {
+	return predicate.SopTask(sql.FieldIn(FieldPlanEndTime, vs...))
+}
+
+// PlanEndTimeNotIn applies the NotIn predicate on the "plan_end_time" field.
+func PlanEndTimeNotIn(vs ...time.Time) predicate.SopTask {
+	return predicate.SopTask(sql.FieldNotIn(FieldPlanEndTime, vs...))
+}
+
+// PlanEndTimeGT applies the GT predicate on the "plan_end_time" field.
+func PlanEndTimeGT(v time.Time) predicate.SopTask {
+	return predicate.SopTask(sql.FieldGT(FieldPlanEndTime, v))
+}
+
+// PlanEndTimeGTE applies the GTE predicate on the "plan_end_time" field.
+func PlanEndTimeGTE(v time.Time) predicate.SopTask {
+	return predicate.SopTask(sql.FieldGTE(FieldPlanEndTime, v))
+}
+
+// PlanEndTimeLT applies the LT predicate on the "plan_end_time" field.
+func PlanEndTimeLT(v time.Time) predicate.SopTask {
+	return predicate.SopTask(sql.FieldLT(FieldPlanEndTime, v))
+}
+
+// PlanEndTimeLTE applies the LTE predicate on the "plan_end_time" field.
+func PlanEndTimeLTE(v time.Time) predicate.SopTask {
+	return predicate.SopTask(sql.FieldLTE(FieldPlanEndTime, v))
+}
+
+// PlanEndTimeIsNil applies the IsNil predicate on the "plan_end_time" field.
+func PlanEndTimeIsNil() predicate.SopTask {
+	return predicate.SopTask(sql.FieldIsNull(FieldPlanEndTime))
+}
+
+// PlanEndTimeNotNil applies the NotNil predicate on the "plan_end_time" field.
+func PlanEndTimeNotNil() predicate.SopTask {
+	return predicate.SopTask(sql.FieldNotNull(FieldPlanEndTime))
+}
+
+// CreatorIDEQ applies the EQ predicate on the "creator_id" field.
+func CreatorIDEQ(v string) predicate.SopTask {
+	return predicate.SopTask(sql.FieldEQ(FieldCreatorID, v))
+}
+
+// CreatorIDNEQ applies the NEQ predicate on the "creator_id" field.
+func CreatorIDNEQ(v string) predicate.SopTask {
+	return predicate.SopTask(sql.FieldNEQ(FieldCreatorID, v))
+}
+
+// CreatorIDIn applies the In predicate on the "creator_id" field.
+func CreatorIDIn(vs ...string) predicate.SopTask {
+	return predicate.SopTask(sql.FieldIn(FieldCreatorID, vs...))
+}
+
+// CreatorIDNotIn applies the NotIn predicate on the "creator_id" field.
+func CreatorIDNotIn(vs ...string) predicate.SopTask {
+	return predicate.SopTask(sql.FieldNotIn(FieldCreatorID, vs...))
+}
+
+// CreatorIDGT applies the GT predicate on the "creator_id" field.
+func CreatorIDGT(v string) predicate.SopTask {
+	return predicate.SopTask(sql.FieldGT(FieldCreatorID, v))
+}
+
+// CreatorIDGTE applies the GTE predicate on the "creator_id" field.
+func CreatorIDGTE(v string) predicate.SopTask {
+	return predicate.SopTask(sql.FieldGTE(FieldCreatorID, v))
+}
+
+// CreatorIDLT applies the LT predicate on the "creator_id" field.
+func CreatorIDLT(v string) predicate.SopTask {
+	return predicate.SopTask(sql.FieldLT(FieldCreatorID, v))
+}
+
+// CreatorIDLTE applies the LTE predicate on the "creator_id" field.
+func CreatorIDLTE(v string) predicate.SopTask {
+	return predicate.SopTask(sql.FieldLTE(FieldCreatorID, v))
+}
+
+// CreatorIDContains applies the Contains predicate on the "creator_id" field.
+func CreatorIDContains(v string) predicate.SopTask {
+	return predicate.SopTask(sql.FieldContains(FieldCreatorID, v))
+}
+
+// CreatorIDHasPrefix applies the HasPrefix predicate on the "creator_id" field.
+func CreatorIDHasPrefix(v string) predicate.SopTask {
+	return predicate.SopTask(sql.FieldHasPrefix(FieldCreatorID, v))
+}
+
+// CreatorIDHasSuffix applies the HasSuffix predicate on the "creator_id" field.
+func CreatorIDHasSuffix(v string) predicate.SopTask {
+	return predicate.SopTask(sql.FieldHasSuffix(FieldCreatorID, v))
+}
+
+// CreatorIDIsNil applies the IsNil predicate on the "creator_id" field.
+func CreatorIDIsNil() predicate.SopTask {
+	return predicate.SopTask(sql.FieldIsNull(FieldCreatorID))
+}
+
+// CreatorIDNotNil applies the NotNil predicate on the "creator_id" field.
+func CreatorIDNotNil() predicate.SopTask {
+	return predicate.SopTask(sql.FieldNotNull(FieldCreatorID))
+}
+
+// CreatorIDEqualFold applies the EqualFold predicate on the "creator_id" field.
+func CreatorIDEqualFold(v string) predicate.SopTask {
+	return predicate.SopTask(sql.FieldEqualFold(FieldCreatorID, v))
+}
+
+// CreatorIDContainsFold applies the ContainsFold predicate on the "creator_id" field.
+func CreatorIDContainsFold(v string) predicate.SopTask {
+	return predicate.SopTask(sql.FieldContainsFold(FieldCreatorID, v))
+}
+
+// HasTaskStages applies the HasEdge predicate on the "task_stages" edge.
+func HasTaskStages() predicate.SopTask {
+	return predicate.SopTask(func(s *sql.Selector) {
+		step := sqlgraph.NewStep(
+			sqlgraph.From(Table, FieldID),
+			sqlgraph.Edge(sqlgraph.O2M, false, TaskStagesTable, TaskStagesColumn),
+		)
+		sqlgraph.HasNeighbors(s, step)
+	})
+}
+
+// HasTaskStagesWith applies the HasEdge predicate on the "task_stages" edge with a given conditions (other predicates).
+func HasTaskStagesWith(preds ...predicate.SopStage) predicate.SopTask {
+	return predicate.SopTask(func(s *sql.Selector) {
+		step := newTaskStagesStep()
+		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.SopTask) predicate.SopTask {
+	return predicate.SopTask(sql.AndPredicates(predicates...))
+}
+
+// Or groups predicates with the OR operator between them.
+func Or(predicates ...predicate.SopTask) predicate.SopTask {
+	return predicate.SopTask(sql.OrPredicates(predicates...))
+}
+
+// Not applies the not operator on the given predicate.
+func Not(p predicate.SopTask) predicate.SopTask {
+	return predicate.SopTask(sql.NotPredicates(p))
+}

+ 1202 - 0
ent/soptask_create.go

@@ -0,0 +1,1202 @@
+// Code generated by ent, DO NOT EDIT.
+
+package ent
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"time"
+	"wechat-api/ent/sopstage"
+	"wechat-api/ent/soptask"
+
+	"entgo.io/ent/dialect/sql"
+	"entgo.io/ent/dialect/sql/sqlgraph"
+	"entgo.io/ent/schema/field"
+)
+
+// SopTaskCreate is the builder for creating a SopTask entity.
+type SopTaskCreate struct {
+	config
+	mutation *SopTaskMutation
+	hooks    []Hook
+	conflict []sql.ConflictOption
+}
+
+// SetCreatedAt sets the "created_at" field.
+func (stc *SopTaskCreate) SetCreatedAt(t time.Time) *SopTaskCreate {
+	stc.mutation.SetCreatedAt(t)
+	return stc
+}
+
+// SetNillableCreatedAt sets the "created_at" field if the given value is not nil.
+func (stc *SopTaskCreate) SetNillableCreatedAt(t *time.Time) *SopTaskCreate {
+	if t != nil {
+		stc.SetCreatedAt(*t)
+	}
+	return stc
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (stc *SopTaskCreate) SetUpdatedAt(t time.Time) *SopTaskCreate {
+	stc.mutation.SetUpdatedAt(t)
+	return stc
+}
+
+// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil.
+func (stc *SopTaskCreate) SetNillableUpdatedAt(t *time.Time) *SopTaskCreate {
+	if t != nil {
+		stc.SetUpdatedAt(*t)
+	}
+	return stc
+}
+
+// SetStatus sets the "status" field.
+func (stc *SopTaskCreate) SetStatus(u uint8) *SopTaskCreate {
+	stc.mutation.SetStatus(u)
+	return stc
+}
+
+// SetNillableStatus sets the "status" field if the given value is not nil.
+func (stc *SopTaskCreate) SetNillableStatus(u *uint8) *SopTaskCreate {
+	if u != nil {
+		stc.SetStatus(*u)
+	}
+	return stc
+}
+
+// SetDeletedAt sets the "deleted_at" field.
+func (stc *SopTaskCreate) SetDeletedAt(t time.Time) *SopTaskCreate {
+	stc.mutation.SetDeletedAt(t)
+	return stc
+}
+
+// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil.
+func (stc *SopTaskCreate) SetNillableDeletedAt(t *time.Time) *SopTaskCreate {
+	if t != nil {
+		stc.SetDeletedAt(*t)
+	}
+	return stc
+}
+
+// SetName sets the "name" field.
+func (stc *SopTaskCreate) SetName(s string) *SopTaskCreate {
+	stc.mutation.SetName(s)
+	return stc
+}
+
+// SetBotWxidList sets the "bot_wxid_list" field.
+func (stc *SopTaskCreate) SetBotWxidList(s []string) *SopTaskCreate {
+	stc.mutation.SetBotWxidList(s)
+	return stc
+}
+
+// SetType sets the "type" field.
+func (stc *SopTaskCreate) SetType(i int) *SopTaskCreate {
+	stc.mutation.SetType(i)
+	return stc
+}
+
+// SetNillableType sets the "type" field if the given value is not nil.
+func (stc *SopTaskCreate) SetNillableType(i *int) *SopTaskCreate {
+	if i != nil {
+		stc.SetType(*i)
+	}
+	return stc
+}
+
+// SetPlanStartTime sets the "plan_start_time" field.
+func (stc *SopTaskCreate) SetPlanStartTime(t time.Time) *SopTaskCreate {
+	stc.mutation.SetPlanStartTime(t)
+	return stc
+}
+
+// SetNillablePlanStartTime sets the "plan_start_time" field if the given value is not nil.
+func (stc *SopTaskCreate) SetNillablePlanStartTime(t *time.Time) *SopTaskCreate {
+	if t != nil {
+		stc.SetPlanStartTime(*t)
+	}
+	return stc
+}
+
+// SetPlanEndTime sets the "plan_end_time" field.
+func (stc *SopTaskCreate) SetPlanEndTime(t time.Time) *SopTaskCreate {
+	stc.mutation.SetPlanEndTime(t)
+	return stc
+}
+
+// SetNillablePlanEndTime sets the "plan_end_time" field if the given value is not nil.
+func (stc *SopTaskCreate) SetNillablePlanEndTime(t *time.Time) *SopTaskCreate {
+	if t != nil {
+		stc.SetPlanEndTime(*t)
+	}
+	return stc
+}
+
+// SetCreatorID sets the "creator_id" field.
+func (stc *SopTaskCreate) SetCreatorID(s string) *SopTaskCreate {
+	stc.mutation.SetCreatorID(s)
+	return stc
+}
+
+// SetNillableCreatorID sets the "creator_id" field if the given value is not nil.
+func (stc *SopTaskCreate) SetNillableCreatorID(s *string) *SopTaskCreate {
+	if s != nil {
+		stc.SetCreatorID(*s)
+	}
+	return stc
+}
+
+// SetID sets the "id" field.
+func (stc *SopTaskCreate) SetID(u uint64) *SopTaskCreate {
+	stc.mutation.SetID(u)
+	return stc
+}
+
+// AddTaskStageIDs adds the "task_stages" edge to the SopStage entity by IDs.
+func (stc *SopTaskCreate) AddTaskStageIDs(ids ...uint64) *SopTaskCreate {
+	stc.mutation.AddTaskStageIDs(ids...)
+	return stc
+}
+
+// AddTaskStages adds the "task_stages" edges to the SopStage entity.
+func (stc *SopTaskCreate) AddTaskStages(s ...*SopStage) *SopTaskCreate {
+	ids := make([]uint64, len(s))
+	for i := range s {
+		ids[i] = s[i].ID
+	}
+	return stc.AddTaskStageIDs(ids...)
+}
+
+// Mutation returns the SopTaskMutation object of the builder.
+func (stc *SopTaskCreate) Mutation() *SopTaskMutation {
+	return stc.mutation
+}
+
+// Save creates the SopTask in the database.
+func (stc *SopTaskCreate) Save(ctx context.Context) (*SopTask, error) {
+	if err := stc.defaults(); err != nil {
+		return nil, err
+	}
+	return withHooks(ctx, stc.sqlSave, stc.mutation, stc.hooks)
+}
+
+// SaveX calls Save and panics if Save returns an error.
+func (stc *SopTaskCreate) SaveX(ctx context.Context) *SopTask {
+	v, err := stc.Save(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return v
+}
+
+// Exec executes the query.
+func (stc *SopTaskCreate) Exec(ctx context.Context) error {
+	_, err := stc.Save(ctx)
+	return err
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (stc *SopTaskCreate) ExecX(ctx context.Context) {
+	if err := stc.Exec(ctx); err != nil {
+		panic(err)
+	}
+}
+
+// defaults sets the default values of the builder before save.
+func (stc *SopTaskCreate) defaults() error {
+	if _, ok := stc.mutation.CreatedAt(); !ok {
+		if soptask.DefaultCreatedAt == nil {
+			return fmt.Errorf("ent: uninitialized soptask.DefaultCreatedAt (forgotten import ent/runtime?)")
+		}
+		v := soptask.DefaultCreatedAt()
+		stc.mutation.SetCreatedAt(v)
+	}
+	if _, ok := stc.mutation.UpdatedAt(); !ok {
+		if soptask.DefaultUpdatedAt == nil {
+			return fmt.Errorf("ent: uninitialized soptask.DefaultUpdatedAt (forgotten import ent/runtime?)")
+		}
+		v := soptask.DefaultUpdatedAt()
+		stc.mutation.SetUpdatedAt(v)
+	}
+	if _, ok := stc.mutation.Status(); !ok {
+		v := soptask.DefaultStatus
+		stc.mutation.SetStatus(v)
+	}
+	if _, ok := stc.mutation.GetType(); !ok {
+		v := soptask.DefaultType
+		stc.mutation.SetType(v)
+	}
+	return nil
+}
+
+// check runs all checks and user-defined validators on the builder.
+func (stc *SopTaskCreate) check() error {
+	if _, ok := stc.mutation.CreatedAt(); !ok {
+		return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "SopTask.created_at"`)}
+	}
+	if _, ok := stc.mutation.UpdatedAt(); !ok {
+		return &ValidationError{Name: "updated_at", err: errors.New(`ent: missing required field "SopTask.updated_at"`)}
+	}
+	if _, ok := stc.mutation.Name(); !ok {
+		return &ValidationError{Name: "name", err: errors.New(`ent: missing required field "SopTask.name"`)}
+	}
+	if v, ok := stc.mutation.Name(); ok {
+		if err := soptask.NameValidator(v); err != nil {
+			return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "SopTask.name": %w`, err)}
+		}
+	}
+	if _, ok := stc.mutation.GetType(); !ok {
+		return &ValidationError{Name: "type", err: errors.New(`ent: missing required field "SopTask.type"`)}
+	}
+	return nil
+}
+
+func (stc *SopTaskCreate) sqlSave(ctx context.Context) (*SopTask, error) {
+	if err := stc.check(); err != nil {
+		return nil, err
+	}
+	_node, _spec := stc.createSpec()
+	if err := sqlgraph.CreateNode(ctx, stc.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)
+	}
+	stc.mutation.id = &_node.ID
+	stc.mutation.done = true
+	return _node, nil
+}
+
+func (stc *SopTaskCreate) createSpec() (*SopTask, *sqlgraph.CreateSpec) {
+	var (
+		_node = &SopTask{config: stc.config}
+		_spec = sqlgraph.NewCreateSpec(soptask.Table, sqlgraph.NewFieldSpec(soptask.FieldID, field.TypeUint64))
+	)
+	_spec.OnConflict = stc.conflict
+	if id, ok := stc.mutation.ID(); ok {
+		_node.ID = id
+		_spec.ID.Value = id
+	}
+	if value, ok := stc.mutation.CreatedAt(); ok {
+		_spec.SetField(soptask.FieldCreatedAt, field.TypeTime, value)
+		_node.CreatedAt = value
+	}
+	if value, ok := stc.mutation.UpdatedAt(); ok {
+		_spec.SetField(soptask.FieldUpdatedAt, field.TypeTime, value)
+		_node.UpdatedAt = value
+	}
+	if value, ok := stc.mutation.Status(); ok {
+		_spec.SetField(soptask.FieldStatus, field.TypeUint8, value)
+		_node.Status = value
+	}
+	if value, ok := stc.mutation.DeletedAt(); ok {
+		_spec.SetField(soptask.FieldDeletedAt, field.TypeTime, value)
+		_node.DeletedAt = value
+	}
+	if value, ok := stc.mutation.Name(); ok {
+		_spec.SetField(soptask.FieldName, field.TypeString, value)
+		_node.Name = value
+	}
+	if value, ok := stc.mutation.BotWxidList(); ok {
+		_spec.SetField(soptask.FieldBotWxidList, field.TypeJSON, value)
+		_node.BotWxidList = value
+	}
+	if value, ok := stc.mutation.GetType(); ok {
+		_spec.SetField(soptask.FieldType, field.TypeInt, value)
+		_node.Type = value
+	}
+	if value, ok := stc.mutation.PlanStartTime(); ok {
+		_spec.SetField(soptask.FieldPlanStartTime, field.TypeTime, value)
+		_node.PlanStartTime = value
+	}
+	if value, ok := stc.mutation.PlanEndTime(); ok {
+		_spec.SetField(soptask.FieldPlanEndTime, field.TypeTime, value)
+		_node.PlanEndTime = value
+	}
+	if value, ok := stc.mutation.CreatorID(); ok {
+		_spec.SetField(soptask.FieldCreatorID, field.TypeString, value)
+		_node.CreatorID = value
+	}
+	if nodes := stc.mutation.TaskStagesIDs(); len(nodes) > 0 {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.O2M,
+			Inverse: false,
+			Table:   soptask.TaskStagesTable,
+			Columns: []string{soptask.TaskStagesColumn},
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: sqlgraph.NewFieldSpec(sopstage.FieldID, field.TypeUint64),
+			},
+		}
+		for _, k := range nodes {
+			edge.Target.Nodes = append(edge.Target.Nodes, k)
+		}
+		_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.SopTask.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.SopTaskUpsert) {
+//			SetCreatedAt(v+v).
+//		}).
+//		Exec(ctx)
+func (stc *SopTaskCreate) OnConflict(opts ...sql.ConflictOption) *SopTaskUpsertOne {
+	stc.conflict = opts
+	return &SopTaskUpsertOne{
+		create: stc,
+	}
+}
+
+// OnConflictColumns calls `OnConflict` and configures the columns
+// as conflict target. Using this option is equivalent to using:
+//
+//	client.SopTask.Create().
+//		OnConflict(sql.ConflictColumns(columns...)).
+//		Exec(ctx)
+func (stc *SopTaskCreate) OnConflictColumns(columns ...string) *SopTaskUpsertOne {
+	stc.conflict = append(stc.conflict, sql.ConflictColumns(columns...))
+	return &SopTaskUpsertOne{
+		create: stc,
+	}
+}
+
+type (
+	// SopTaskUpsertOne is the builder for "upsert"-ing
+	//  one SopTask node.
+	SopTaskUpsertOne struct {
+		create *SopTaskCreate
+	}
+
+	// SopTaskUpsert is the "OnConflict" setter.
+	SopTaskUpsert struct {
+		*sql.UpdateSet
+	}
+)
+
+// SetUpdatedAt sets the "updated_at" field.
+func (u *SopTaskUpsert) SetUpdatedAt(v time.Time) *SopTaskUpsert {
+	u.Set(soptask.FieldUpdatedAt, v)
+	return u
+}
+
+// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create.
+func (u *SopTaskUpsert) UpdateUpdatedAt() *SopTaskUpsert {
+	u.SetExcluded(soptask.FieldUpdatedAt)
+	return u
+}
+
+// SetStatus sets the "status" field.
+func (u *SopTaskUpsert) SetStatus(v uint8) *SopTaskUpsert {
+	u.Set(soptask.FieldStatus, v)
+	return u
+}
+
+// UpdateStatus sets the "status" field to the value that was provided on create.
+func (u *SopTaskUpsert) UpdateStatus() *SopTaskUpsert {
+	u.SetExcluded(soptask.FieldStatus)
+	return u
+}
+
+// AddStatus adds v to the "status" field.
+func (u *SopTaskUpsert) AddStatus(v uint8) *SopTaskUpsert {
+	u.Add(soptask.FieldStatus, v)
+	return u
+}
+
+// ClearStatus clears the value of the "status" field.
+func (u *SopTaskUpsert) ClearStatus() *SopTaskUpsert {
+	u.SetNull(soptask.FieldStatus)
+	return u
+}
+
+// SetDeletedAt sets the "deleted_at" field.
+func (u *SopTaskUpsert) SetDeletedAt(v time.Time) *SopTaskUpsert {
+	u.Set(soptask.FieldDeletedAt, v)
+	return u
+}
+
+// UpdateDeletedAt sets the "deleted_at" field to the value that was provided on create.
+func (u *SopTaskUpsert) UpdateDeletedAt() *SopTaskUpsert {
+	u.SetExcluded(soptask.FieldDeletedAt)
+	return u
+}
+
+// ClearDeletedAt clears the value of the "deleted_at" field.
+func (u *SopTaskUpsert) ClearDeletedAt() *SopTaskUpsert {
+	u.SetNull(soptask.FieldDeletedAt)
+	return u
+}
+
+// SetName sets the "name" field.
+func (u *SopTaskUpsert) SetName(v string) *SopTaskUpsert {
+	u.Set(soptask.FieldName, v)
+	return u
+}
+
+// UpdateName sets the "name" field to the value that was provided on create.
+func (u *SopTaskUpsert) UpdateName() *SopTaskUpsert {
+	u.SetExcluded(soptask.FieldName)
+	return u
+}
+
+// SetBotWxidList sets the "bot_wxid_list" field.
+func (u *SopTaskUpsert) SetBotWxidList(v []string) *SopTaskUpsert {
+	u.Set(soptask.FieldBotWxidList, v)
+	return u
+}
+
+// UpdateBotWxidList sets the "bot_wxid_list" field to the value that was provided on create.
+func (u *SopTaskUpsert) UpdateBotWxidList() *SopTaskUpsert {
+	u.SetExcluded(soptask.FieldBotWxidList)
+	return u
+}
+
+// ClearBotWxidList clears the value of the "bot_wxid_list" field.
+func (u *SopTaskUpsert) ClearBotWxidList() *SopTaskUpsert {
+	u.SetNull(soptask.FieldBotWxidList)
+	return u
+}
+
+// SetType sets the "type" field.
+func (u *SopTaskUpsert) SetType(v int) *SopTaskUpsert {
+	u.Set(soptask.FieldType, v)
+	return u
+}
+
+// UpdateType sets the "type" field to the value that was provided on create.
+func (u *SopTaskUpsert) UpdateType() *SopTaskUpsert {
+	u.SetExcluded(soptask.FieldType)
+	return u
+}
+
+// AddType adds v to the "type" field.
+func (u *SopTaskUpsert) AddType(v int) *SopTaskUpsert {
+	u.Add(soptask.FieldType, v)
+	return u
+}
+
+// SetPlanStartTime sets the "plan_start_time" field.
+func (u *SopTaskUpsert) SetPlanStartTime(v time.Time) *SopTaskUpsert {
+	u.Set(soptask.FieldPlanStartTime, v)
+	return u
+}
+
+// UpdatePlanStartTime sets the "plan_start_time" field to the value that was provided on create.
+func (u *SopTaskUpsert) UpdatePlanStartTime() *SopTaskUpsert {
+	u.SetExcluded(soptask.FieldPlanStartTime)
+	return u
+}
+
+// ClearPlanStartTime clears the value of the "plan_start_time" field.
+func (u *SopTaskUpsert) ClearPlanStartTime() *SopTaskUpsert {
+	u.SetNull(soptask.FieldPlanStartTime)
+	return u
+}
+
+// SetPlanEndTime sets the "plan_end_time" field.
+func (u *SopTaskUpsert) SetPlanEndTime(v time.Time) *SopTaskUpsert {
+	u.Set(soptask.FieldPlanEndTime, v)
+	return u
+}
+
+// UpdatePlanEndTime sets the "plan_end_time" field to the value that was provided on create.
+func (u *SopTaskUpsert) UpdatePlanEndTime() *SopTaskUpsert {
+	u.SetExcluded(soptask.FieldPlanEndTime)
+	return u
+}
+
+// ClearPlanEndTime clears the value of the "plan_end_time" field.
+func (u *SopTaskUpsert) ClearPlanEndTime() *SopTaskUpsert {
+	u.SetNull(soptask.FieldPlanEndTime)
+	return u
+}
+
+// SetCreatorID sets the "creator_id" field.
+func (u *SopTaskUpsert) SetCreatorID(v string) *SopTaskUpsert {
+	u.Set(soptask.FieldCreatorID, v)
+	return u
+}
+
+// UpdateCreatorID sets the "creator_id" field to the value that was provided on create.
+func (u *SopTaskUpsert) UpdateCreatorID() *SopTaskUpsert {
+	u.SetExcluded(soptask.FieldCreatorID)
+	return u
+}
+
+// ClearCreatorID clears the value of the "creator_id" field.
+func (u *SopTaskUpsert) ClearCreatorID() *SopTaskUpsert {
+	u.SetNull(soptask.FieldCreatorID)
+	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.SopTask.Create().
+//		OnConflict(
+//			sql.ResolveWithNewValues(),
+//			sql.ResolveWith(func(u *sql.UpdateSet) {
+//				u.SetIgnore(soptask.FieldID)
+//			}),
+//		).
+//		Exec(ctx)
+func (u *SopTaskUpsertOne) UpdateNewValues() *SopTaskUpsertOne {
+	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(soptask.FieldID)
+		}
+		if _, exists := u.create.mutation.CreatedAt(); exists {
+			s.SetIgnore(soptask.FieldCreatedAt)
+		}
+	}))
+	return u
+}
+
+// Ignore sets each column to itself in case of conflict.
+// Using this option is equivalent to using:
+//
+//	client.SopTask.Create().
+//	    OnConflict(sql.ResolveWithIgnore()).
+//	    Exec(ctx)
+func (u *SopTaskUpsertOne) Ignore() *SopTaskUpsertOne {
+	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 *SopTaskUpsertOne) DoNothing() *SopTaskUpsertOne {
+	u.create.conflict = append(u.create.conflict, sql.DoNothing())
+	return u
+}
+
+// Update allows overriding fields `UPDATE` values. See the SopTaskCreate.OnConflict
+// documentation for more info.
+func (u *SopTaskUpsertOne) Update(set func(*SopTaskUpsert)) *SopTaskUpsertOne {
+	u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) {
+		set(&SopTaskUpsert{UpdateSet: update})
+	}))
+	return u
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (u *SopTaskUpsertOne) SetUpdatedAt(v time.Time) *SopTaskUpsertOne {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.SetUpdatedAt(v)
+	})
+}
+
+// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create.
+func (u *SopTaskUpsertOne) UpdateUpdatedAt() *SopTaskUpsertOne {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.UpdateUpdatedAt()
+	})
+}
+
+// SetStatus sets the "status" field.
+func (u *SopTaskUpsertOne) SetStatus(v uint8) *SopTaskUpsertOne {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.SetStatus(v)
+	})
+}
+
+// AddStatus adds v to the "status" field.
+func (u *SopTaskUpsertOne) AddStatus(v uint8) *SopTaskUpsertOne {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.AddStatus(v)
+	})
+}
+
+// UpdateStatus sets the "status" field to the value that was provided on create.
+func (u *SopTaskUpsertOne) UpdateStatus() *SopTaskUpsertOne {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.UpdateStatus()
+	})
+}
+
+// ClearStatus clears the value of the "status" field.
+func (u *SopTaskUpsertOne) ClearStatus() *SopTaskUpsertOne {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.ClearStatus()
+	})
+}
+
+// SetDeletedAt sets the "deleted_at" field.
+func (u *SopTaskUpsertOne) SetDeletedAt(v time.Time) *SopTaskUpsertOne {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.SetDeletedAt(v)
+	})
+}
+
+// UpdateDeletedAt sets the "deleted_at" field to the value that was provided on create.
+func (u *SopTaskUpsertOne) UpdateDeletedAt() *SopTaskUpsertOne {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.UpdateDeletedAt()
+	})
+}
+
+// ClearDeletedAt clears the value of the "deleted_at" field.
+func (u *SopTaskUpsertOne) ClearDeletedAt() *SopTaskUpsertOne {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.ClearDeletedAt()
+	})
+}
+
+// SetName sets the "name" field.
+func (u *SopTaskUpsertOne) SetName(v string) *SopTaskUpsertOne {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.SetName(v)
+	})
+}
+
+// UpdateName sets the "name" field to the value that was provided on create.
+func (u *SopTaskUpsertOne) UpdateName() *SopTaskUpsertOne {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.UpdateName()
+	})
+}
+
+// SetBotWxidList sets the "bot_wxid_list" field.
+func (u *SopTaskUpsertOne) SetBotWxidList(v []string) *SopTaskUpsertOne {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.SetBotWxidList(v)
+	})
+}
+
+// UpdateBotWxidList sets the "bot_wxid_list" field to the value that was provided on create.
+func (u *SopTaskUpsertOne) UpdateBotWxidList() *SopTaskUpsertOne {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.UpdateBotWxidList()
+	})
+}
+
+// ClearBotWxidList clears the value of the "bot_wxid_list" field.
+func (u *SopTaskUpsertOne) ClearBotWxidList() *SopTaskUpsertOne {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.ClearBotWxidList()
+	})
+}
+
+// SetType sets the "type" field.
+func (u *SopTaskUpsertOne) SetType(v int) *SopTaskUpsertOne {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.SetType(v)
+	})
+}
+
+// AddType adds v to the "type" field.
+func (u *SopTaskUpsertOne) AddType(v int) *SopTaskUpsertOne {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.AddType(v)
+	})
+}
+
+// UpdateType sets the "type" field to the value that was provided on create.
+func (u *SopTaskUpsertOne) UpdateType() *SopTaskUpsertOne {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.UpdateType()
+	})
+}
+
+// SetPlanStartTime sets the "plan_start_time" field.
+func (u *SopTaskUpsertOne) SetPlanStartTime(v time.Time) *SopTaskUpsertOne {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.SetPlanStartTime(v)
+	})
+}
+
+// UpdatePlanStartTime sets the "plan_start_time" field to the value that was provided on create.
+func (u *SopTaskUpsertOne) UpdatePlanStartTime() *SopTaskUpsertOne {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.UpdatePlanStartTime()
+	})
+}
+
+// ClearPlanStartTime clears the value of the "plan_start_time" field.
+func (u *SopTaskUpsertOne) ClearPlanStartTime() *SopTaskUpsertOne {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.ClearPlanStartTime()
+	})
+}
+
+// SetPlanEndTime sets the "plan_end_time" field.
+func (u *SopTaskUpsertOne) SetPlanEndTime(v time.Time) *SopTaskUpsertOne {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.SetPlanEndTime(v)
+	})
+}
+
+// UpdatePlanEndTime sets the "plan_end_time" field to the value that was provided on create.
+func (u *SopTaskUpsertOne) UpdatePlanEndTime() *SopTaskUpsertOne {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.UpdatePlanEndTime()
+	})
+}
+
+// ClearPlanEndTime clears the value of the "plan_end_time" field.
+func (u *SopTaskUpsertOne) ClearPlanEndTime() *SopTaskUpsertOne {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.ClearPlanEndTime()
+	})
+}
+
+// SetCreatorID sets the "creator_id" field.
+func (u *SopTaskUpsertOne) SetCreatorID(v string) *SopTaskUpsertOne {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.SetCreatorID(v)
+	})
+}
+
+// UpdateCreatorID sets the "creator_id" field to the value that was provided on create.
+func (u *SopTaskUpsertOne) UpdateCreatorID() *SopTaskUpsertOne {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.UpdateCreatorID()
+	})
+}
+
+// ClearCreatorID clears the value of the "creator_id" field.
+func (u *SopTaskUpsertOne) ClearCreatorID() *SopTaskUpsertOne {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.ClearCreatorID()
+	})
+}
+
+// Exec executes the query.
+func (u *SopTaskUpsertOne) Exec(ctx context.Context) error {
+	if len(u.create.conflict) == 0 {
+		return errors.New("ent: missing options for SopTaskCreate.OnConflict")
+	}
+	return u.create.Exec(ctx)
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (u *SopTaskUpsertOne) 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 *SopTaskUpsertOne) 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 *SopTaskUpsertOne) IDX(ctx context.Context) uint64 {
+	id, err := u.ID(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return id
+}
+
+// SopTaskCreateBulk is the builder for creating many SopTask entities in bulk.
+type SopTaskCreateBulk struct {
+	config
+	err      error
+	builders []*SopTaskCreate
+	conflict []sql.ConflictOption
+}
+
+// Save creates the SopTask entities in the database.
+func (stcb *SopTaskCreateBulk) Save(ctx context.Context) ([]*SopTask, error) {
+	if stcb.err != nil {
+		return nil, stcb.err
+	}
+	specs := make([]*sqlgraph.CreateSpec, len(stcb.builders))
+	nodes := make([]*SopTask, len(stcb.builders))
+	mutators := make([]Mutator, len(stcb.builders))
+	for i := range stcb.builders {
+		func(i int, root context.Context) {
+			builder := stcb.builders[i]
+			builder.defaults()
+			var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
+				mutation, ok := m.(*SopTaskMutation)
+				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, stcb.builders[i+1].mutation)
+				} else {
+					spec := &sqlgraph.BatchCreateSpec{Nodes: specs}
+					spec.OnConflict = stcb.conflict
+					// Invoke the actual operation on the latest mutation in the chain.
+					if err = sqlgraph.BatchCreate(ctx, stcb.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, stcb.builders[0].mutation); err != nil {
+			return nil, err
+		}
+	}
+	return nodes, nil
+}
+
+// SaveX is like Save, but panics if an error occurs.
+func (stcb *SopTaskCreateBulk) SaveX(ctx context.Context) []*SopTask {
+	v, err := stcb.Save(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return v
+}
+
+// Exec executes the query.
+func (stcb *SopTaskCreateBulk) Exec(ctx context.Context) error {
+	_, err := stcb.Save(ctx)
+	return err
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (stcb *SopTaskCreateBulk) ExecX(ctx context.Context) {
+	if err := stcb.Exec(ctx); err != nil {
+		panic(err)
+	}
+}
+
+// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause
+// of the `INSERT` statement. For example:
+//
+//	client.SopTask.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.SopTaskUpsert) {
+//			SetCreatedAt(v+v).
+//		}).
+//		Exec(ctx)
+func (stcb *SopTaskCreateBulk) OnConflict(opts ...sql.ConflictOption) *SopTaskUpsertBulk {
+	stcb.conflict = opts
+	return &SopTaskUpsertBulk{
+		create: stcb,
+	}
+}
+
+// OnConflictColumns calls `OnConflict` and configures the columns
+// as conflict target. Using this option is equivalent to using:
+//
+//	client.SopTask.Create().
+//		OnConflict(sql.ConflictColumns(columns...)).
+//		Exec(ctx)
+func (stcb *SopTaskCreateBulk) OnConflictColumns(columns ...string) *SopTaskUpsertBulk {
+	stcb.conflict = append(stcb.conflict, sql.ConflictColumns(columns...))
+	return &SopTaskUpsertBulk{
+		create: stcb,
+	}
+}
+
+// SopTaskUpsertBulk is the builder for "upsert"-ing
+// a bulk of SopTask nodes.
+type SopTaskUpsertBulk struct {
+	create *SopTaskCreateBulk
+}
+
+// UpdateNewValues updates the mutable fields using the new values that
+// were set on create. Using this option is equivalent to using:
+//
+//	client.SopTask.Create().
+//		OnConflict(
+//			sql.ResolveWithNewValues(),
+//			sql.ResolveWith(func(u *sql.UpdateSet) {
+//				u.SetIgnore(soptask.FieldID)
+//			}),
+//		).
+//		Exec(ctx)
+func (u *SopTaskUpsertBulk) UpdateNewValues() *SopTaskUpsertBulk {
+	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(soptask.FieldID)
+			}
+			if _, exists := b.mutation.CreatedAt(); exists {
+				s.SetIgnore(soptask.FieldCreatedAt)
+			}
+		}
+	}))
+	return u
+}
+
+// Ignore sets each column to itself in case of conflict.
+// Using this option is equivalent to using:
+//
+//	client.SopTask.Create().
+//		OnConflict(sql.ResolveWithIgnore()).
+//		Exec(ctx)
+func (u *SopTaskUpsertBulk) Ignore() *SopTaskUpsertBulk {
+	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 *SopTaskUpsertBulk) DoNothing() *SopTaskUpsertBulk {
+	u.create.conflict = append(u.create.conflict, sql.DoNothing())
+	return u
+}
+
+// Update allows overriding fields `UPDATE` values. See the SopTaskCreateBulk.OnConflict
+// documentation for more info.
+func (u *SopTaskUpsertBulk) Update(set func(*SopTaskUpsert)) *SopTaskUpsertBulk {
+	u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) {
+		set(&SopTaskUpsert{UpdateSet: update})
+	}))
+	return u
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (u *SopTaskUpsertBulk) SetUpdatedAt(v time.Time) *SopTaskUpsertBulk {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.SetUpdatedAt(v)
+	})
+}
+
+// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create.
+func (u *SopTaskUpsertBulk) UpdateUpdatedAt() *SopTaskUpsertBulk {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.UpdateUpdatedAt()
+	})
+}
+
+// SetStatus sets the "status" field.
+func (u *SopTaskUpsertBulk) SetStatus(v uint8) *SopTaskUpsertBulk {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.SetStatus(v)
+	})
+}
+
+// AddStatus adds v to the "status" field.
+func (u *SopTaskUpsertBulk) AddStatus(v uint8) *SopTaskUpsertBulk {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.AddStatus(v)
+	})
+}
+
+// UpdateStatus sets the "status" field to the value that was provided on create.
+func (u *SopTaskUpsertBulk) UpdateStatus() *SopTaskUpsertBulk {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.UpdateStatus()
+	})
+}
+
+// ClearStatus clears the value of the "status" field.
+func (u *SopTaskUpsertBulk) ClearStatus() *SopTaskUpsertBulk {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.ClearStatus()
+	})
+}
+
+// SetDeletedAt sets the "deleted_at" field.
+func (u *SopTaskUpsertBulk) SetDeletedAt(v time.Time) *SopTaskUpsertBulk {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.SetDeletedAt(v)
+	})
+}
+
+// UpdateDeletedAt sets the "deleted_at" field to the value that was provided on create.
+func (u *SopTaskUpsertBulk) UpdateDeletedAt() *SopTaskUpsertBulk {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.UpdateDeletedAt()
+	})
+}
+
+// ClearDeletedAt clears the value of the "deleted_at" field.
+func (u *SopTaskUpsertBulk) ClearDeletedAt() *SopTaskUpsertBulk {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.ClearDeletedAt()
+	})
+}
+
+// SetName sets the "name" field.
+func (u *SopTaskUpsertBulk) SetName(v string) *SopTaskUpsertBulk {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.SetName(v)
+	})
+}
+
+// UpdateName sets the "name" field to the value that was provided on create.
+func (u *SopTaskUpsertBulk) UpdateName() *SopTaskUpsertBulk {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.UpdateName()
+	})
+}
+
+// SetBotWxidList sets the "bot_wxid_list" field.
+func (u *SopTaskUpsertBulk) SetBotWxidList(v []string) *SopTaskUpsertBulk {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.SetBotWxidList(v)
+	})
+}
+
+// UpdateBotWxidList sets the "bot_wxid_list" field to the value that was provided on create.
+func (u *SopTaskUpsertBulk) UpdateBotWxidList() *SopTaskUpsertBulk {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.UpdateBotWxidList()
+	})
+}
+
+// ClearBotWxidList clears the value of the "bot_wxid_list" field.
+func (u *SopTaskUpsertBulk) ClearBotWxidList() *SopTaskUpsertBulk {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.ClearBotWxidList()
+	})
+}
+
+// SetType sets the "type" field.
+func (u *SopTaskUpsertBulk) SetType(v int) *SopTaskUpsertBulk {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.SetType(v)
+	})
+}
+
+// AddType adds v to the "type" field.
+func (u *SopTaskUpsertBulk) AddType(v int) *SopTaskUpsertBulk {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.AddType(v)
+	})
+}
+
+// UpdateType sets the "type" field to the value that was provided on create.
+func (u *SopTaskUpsertBulk) UpdateType() *SopTaskUpsertBulk {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.UpdateType()
+	})
+}
+
+// SetPlanStartTime sets the "plan_start_time" field.
+func (u *SopTaskUpsertBulk) SetPlanStartTime(v time.Time) *SopTaskUpsertBulk {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.SetPlanStartTime(v)
+	})
+}
+
+// UpdatePlanStartTime sets the "plan_start_time" field to the value that was provided on create.
+func (u *SopTaskUpsertBulk) UpdatePlanStartTime() *SopTaskUpsertBulk {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.UpdatePlanStartTime()
+	})
+}
+
+// ClearPlanStartTime clears the value of the "plan_start_time" field.
+func (u *SopTaskUpsertBulk) ClearPlanStartTime() *SopTaskUpsertBulk {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.ClearPlanStartTime()
+	})
+}
+
+// SetPlanEndTime sets the "plan_end_time" field.
+func (u *SopTaskUpsertBulk) SetPlanEndTime(v time.Time) *SopTaskUpsertBulk {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.SetPlanEndTime(v)
+	})
+}
+
+// UpdatePlanEndTime sets the "plan_end_time" field to the value that was provided on create.
+func (u *SopTaskUpsertBulk) UpdatePlanEndTime() *SopTaskUpsertBulk {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.UpdatePlanEndTime()
+	})
+}
+
+// ClearPlanEndTime clears the value of the "plan_end_time" field.
+func (u *SopTaskUpsertBulk) ClearPlanEndTime() *SopTaskUpsertBulk {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.ClearPlanEndTime()
+	})
+}
+
+// SetCreatorID sets the "creator_id" field.
+func (u *SopTaskUpsertBulk) SetCreatorID(v string) *SopTaskUpsertBulk {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.SetCreatorID(v)
+	})
+}
+
+// UpdateCreatorID sets the "creator_id" field to the value that was provided on create.
+func (u *SopTaskUpsertBulk) UpdateCreatorID() *SopTaskUpsertBulk {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.UpdateCreatorID()
+	})
+}
+
+// ClearCreatorID clears the value of the "creator_id" field.
+func (u *SopTaskUpsertBulk) ClearCreatorID() *SopTaskUpsertBulk {
+	return u.Update(func(s *SopTaskUpsert) {
+		s.ClearCreatorID()
+	})
+}
+
+// Exec executes the query.
+func (u *SopTaskUpsertBulk) 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 SopTaskCreateBulk instead", i)
+		}
+	}
+	if len(u.create.conflict) == 0 {
+		return errors.New("ent: missing options for SopTaskCreateBulk.OnConflict")
+	}
+	return u.create.Exec(ctx)
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (u *SopTaskUpsertBulk) ExecX(ctx context.Context) {
+	if err := u.create.Exec(ctx); err != nil {
+		panic(err)
+	}
+}

+ 88 - 0
ent/soptask_delete.go

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

+ 605 - 0
ent/soptask_query.go

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

+ 840 - 0
ent/soptask_update.go

@@ -0,0 +1,840 @@
+// Code generated by ent, DO NOT EDIT.
+
+package ent
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"time"
+	"wechat-api/ent/predicate"
+	"wechat-api/ent/sopstage"
+	"wechat-api/ent/soptask"
+
+	"entgo.io/ent/dialect/sql"
+	"entgo.io/ent/dialect/sql/sqlgraph"
+	"entgo.io/ent/dialect/sql/sqljson"
+	"entgo.io/ent/schema/field"
+)
+
+// SopTaskUpdate is the builder for updating SopTask entities.
+type SopTaskUpdate struct {
+	config
+	hooks    []Hook
+	mutation *SopTaskMutation
+}
+
+// Where appends a list predicates to the SopTaskUpdate builder.
+func (stu *SopTaskUpdate) Where(ps ...predicate.SopTask) *SopTaskUpdate {
+	stu.mutation.Where(ps...)
+	return stu
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (stu *SopTaskUpdate) SetUpdatedAt(t time.Time) *SopTaskUpdate {
+	stu.mutation.SetUpdatedAt(t)
+	return stu
+}
+
+// SetStatus sets the "status" field.
+func (stu *SopTaskUpdate) SetStatus(u uint8) *SopTaskUpdate {
+	stu.mutation.ResetStatus()
+	stu.mutation.SetStatus(u)
+	return stu
+}
+
+// SetNillableStatus sets the "status" field if the given value is not nil.
+func (stu *SopTaskUpdate) SetNillableStatus(u *uint8) *SopTaskUpdate {
+	if u != nil {
+		stu.SetStatus(*u)
+	}
+	return stu
+}
+
+// AddStatus adds u to the "status" field.
+func (stu *SopTaskUpdate) AddStatus(u int8) *SopTaskUpdate {
+	stu.mutation.AddStatus(u)
+	return stu
+}
+
+// ClearStatus clears the value of the "status" field.
+func (stu *SopTaskUpdate) ClearStatus() *SopTaskUpdate {
+	stu.mutation.ClearStatus()
+	return stu
+}
+
+// SetDeletedAt sets the "deleted_at" field.
+func (stu *SopTaskUpdate) SetDeletedAt(t time.Time) *SopTaskUpdate {
+	stu.mutation.SetDeletedAt(t)
+	return stu
+}
+
+// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil.
+func (stu *SopTaskUpdate) SetNillableDeletedAt(t *time.Time) *SopTaskUpdate {
+	if t != nil {
+		stu.SetDeletedAt(*t)
+	}
+	return stu
+}
+
+// ClearDeletedAt clears the value of the "deleted_at" field.
+func (stu *SopTaskUpdate) ClearDeletedAt() *SopTaskUpdate {
+	stu.mutation.ClearDeletedAt()
+	return stu
+}
+
+// SetName sets the "name" field.
+func (stu *SopTaskUpdate) SetName(s string) *SopTaskUpdate {
+	stu.mutation.SetName(s)
+	return stu
+}
+
+// SetNillableName sets the "name" field if the given value is not nil.
+func (stu *SopTaskUpdate) SetNillableName(s *string) *SopTaskUpdate {
+	if s != nil {
+		stu.SetName(*s)
+	}
+	return stu
+}
+
+// SetBotWxidList sets the "bot_wxid_list" field.
+func (stu *SopTaskUpdate) SetBotWxidList(s []string) *SopTaskUpdate {
+	stu.mutation.SetBotWxidList(s)
+	return stu
+}
+
+// AppendBotWxidList appends s to the "bot_wxid_list" field.
+func (stu *SopTaskUpdate) AppendBotWxidList(s []string) *SopTaskUpdate {
+	stu.mutation.AppendBotWxidList(s)
+	return stu
+}
+
+// ClearBotWxidList clears the value of the "bot_wxid_list" field.
+func (stu *SopTaskUpdate) ClearBotWxidList() *SopTaskUpdate {
+	stu.mutation.ClearBotWxidList()
+	return stu
+}
+
+// SetType sets the "type" field.
+func (stu *SopTaskUpdate) SetType(i int) *SopTaskUpdate {
+	stu.mutation.ResetType()
+	stu.mutation.SetType(i)
+	return stu
+}
+
+// SetNillableType sets the "type" field if the given value is not nil.
+func (stu *SopTaskUpdate) SetNillableType(i *int) *SopTaskUpdate {
+	if i != nil {
+		stu.SetType(*i)
+	}
+	return stu
+}
+
+// AddType adds i to the "type" field.
+func (stu *SopTaskUpdate) AddType(i int) *SopTaskUpdate {
+	stu.mutation.AddType(i)
+	return stu
+}
+
+// SetPlanStartTime sets the "plan_start_time" field.
+func (stu *SopTaskUpdate) SetPlanStartTime(t time.Time) *SopTaskUpdate {
+	stu.mutation.SetPlanStartTime(t)
+	return stu
+}
+
+// SetNillablePlanStartTime sets the "plan_start_time" field if the given value is not nil.
+func (stu *SopTaskUpdate) SetNillablePlanStartTime(t *time.Time) *SopTaskUpdate {
+	if t != nil {
+		stu.SetPlanStartTime(*t)
+	}
+	return stu
+}
+
+// ClearPlanStartTime clears the value of the "plan_start_time" field.
+func (stu *SopTaskUpdate) ClearPlanStartTime() *SopTaskUpdate {
+	stu.mutation.ClearPlanStartTime()
+	return stu
+}
+
+// SetPlanEndTime sets the "plan_end_time" field.
+func (stu *SopTaskUpdate) SetPlanEndTime(t time.Time) *SopTaskUpdate {
+	stu.mutation.SetPlanEndTime(t)
+	return stu
+}
+
+// SetNillablePlanEndTime sets the "plan_end_time" field if the given value is not nil.
+func (stu *SopTaskUpdate) SetNillablePlanEndTime(t *time.Time) *SopTaskUpdate {
+	if t != nil {
+		stu.SetPlanEndTime(*t)
+	}
+	return stu
+}
+
+// ClearPlanEndTime clears the value of the "plan_end_time" field.
+func (stu *SopTaskUpdate) ClearPlanEndTime() *SopTaskUpdate {
+	stu.mutation.ClearPlanEndTime()
+	return stu
+}
+
+// SetCreatorID sets the "creator_id" field.
+func (stu *SopTaskUpdate) SetCreatorID(s string) *SopTaskUpdate {
+	stu.mutation.SetCreatorID(s)
+	return stu
+}
+
+// SetNillableCreatorID sets the "creator_id" field if the given value is not nil.
+func (stu *SopTaskUpdate) SetNillableCreatorID(s *string) *SopTaskUpdate {
+	if s != nil {
+		stu.SetCreatorID(*s)
+	}
+	return stu
+}
+
+// ClearCreatorID clears the value of the "creator_id" field.
+func (stu *SopTaskUpdate) ClearCreatorID() *SopTaskUpdate {
+	stu.mutation.ClearCreatorID()
+	return stu
+}
+
+// AddTaskStageIDs adds the "task_stages" edge to the SopStage entity by IDs.
+func (stu *SopTaskUpdate) AddTaskStageIDs(ids ...uint64) *SopTaskUpdate {
+	stu.mutation.AddTaskStageIDs(ids...)
+	return stu
+}
+
+// AddTaskStages adds the "task_stages" edges to the SopStage entity.
+func (stu *SopTaskUpdate) AddTaskStages(s ...*SopStage) *SopTaskUpdate {
+	ids := make([]uint64, len(s))
+	for i := range s {
+		ids[i] = s[i].ID
+	}
+	return stu.AddTaskStageIDs(ids...)
+}
+
+// Mutation returns the SopTaskMutation object of the builder.
+func (stu *SopTaskUpdate) Mutation() *SopTaskMutation {
+	return stu.mutation
+}
+
+// ClearTaskStages clears all "task_stages" edges to the SopStage entity.
+func (stu *SopTaskUpdate) ClearTaskStages() *SopTaskUpdate {
+	stu.mutation.ClearTaskStages()
+	return stu
+}
+
+// RemoveTaskStageIDs removes the "task_stages" edge to SopStage entities by IDs.
+func (stu *SopTaskUpdate) RemoveTaskStageIDs(ids ...uint64) *SopTaskUpdate {
+	stu.mutation.RemoveTaskStageIDs(ids...)
+	return stu
+}
+
+// RemoveTaskStages removes "task_stages" edges to SopStage entities.
+func (stu *SopTaskUpdate) RemoveTaskStages(s ...*SopStage) *SopTaskUpdate {
+	ids := make([]uint64, len(s))
+	for i := range s {
+		ids[i] = s[i].ID
+	}
+	return stu.RemoveTaskStageIDs(ids...)
+}
+
+// Save executes the query and returns the number of nodes affected by the update operation.
+func (stu *SopTaskUpdate) Save(ctx context.Context) (int, error) {
+	if err := stu.defaults(); err != nil {
+		return 0, err
+	}
+	return withHooks(ctx, stu.sqlSave, stu.mutation, stu.hooks)
+}
+
+// SaveX is like Save, but panics if an error occurs.
+func (stu *SopTaskUpdate) SaveX(ctx context.Context) int {
+	affected, err := stu.Save(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return affected
+}
+
+// Exec executes the query.
+func (stu *SopTaskUpdate) Exec(ctx context.Context) error {
+	_, err := stu.Save(ctx)
+	return err
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (stu *SopTaskUpdate) ExecX(ctx context.Context) {
+	if err := stu.Exec(ctx); err != nil {
+		panic(err)
+	}
+}
+
+// defaults sets the default values of the builder before save.
+func (stu *SopTaskUpdate) defaults() error {
+	if _, ok := stu.mutation.UpdatedAt(); !ok {
+		if soptask.UpdateDefaultUpdatedAt == nil {
+			return fmt.Errorf("ent: uninitialized soptask.UpdateDefaultUpdatedAt (forgotten import ent/runtime?)")
+		}
+		v := soptask.UpdateDefaultUpdatedAt()
+		stu.mutation.SetUpdatedAt(v)
+	}
+	return nil
+}
+
+// check runs all checks and user-defined validators on the builder.
+func (stu *SopTaskUpdate) check() error {
+	if v, ok := stu.mutation.Name(); ok {
+		if err := soptask.NameValidator(v); err != nil {
+			return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "SopTask.name": %w`, err)}
+		}
+	}
+	return nil
+}
+
+func (stu *SopTaskUpdate) sqlSave(ctx context.Context) (n int, err error) {
+	if err := stu.check(); err != nil {
+		return n, err
+	}
+	_spec := sqlgraph.NewUpdateSpec(soptask.Table, soptask.Columns, sqlgraph.NewFieldSpec(soptask.FieldID, field.TypeUint64))
+	if ps := stu.mutation.predicates; len(ps) > 0 {
+		_spec.Predicate = func(selector *sql.Selector) {
+			for i := range ps {
+				ps[i](selector)
+			}
+		}
+	}
+	if value, ok := stu.mutation.UpdatedAt(); ok {
+		_spec.SetField(soptask.FieldUpdatedAt, field.TypeTime, value)
+	}
+	if value, ok := stu.mutation.Status(); ok {
+		_spec.SetField(soptask.FieldStatus, field.TypeUint8, value)
+	}
+	if value, ok := stu.mutation.AddedStatus(); ok {
+		_spec.AddField(soptask.FieldStatus, field.TypeUint8, value)
+	}
+	if stu.mutation.StatusCleared() {
+		_spec.ClearField(soptask.FieldStatus, field.TypeUint8)
+	}
+	if value, ok := stu.mutation.DeletedAt(); ok {
+		_spec.SetField(soptask.FieldDeletedAt, field.TypeTime, value)
+	}
+	if stu.mutation.DeletedAtCleared() {
+		_spec.ClearField(soptask.FieldDeletedAt, field.TypeTime)
+	}
+	if value, ok := stu.mutation.Name(); ok {
+		_spec.SetField(soptask.FieldName, field.TypeString, value)
+	}
+	if value, ok := stu.mutation.BotWxidList(); ok {
+		_spec.SetField(soptask.FieldBotWxidList, field.TypeJSON, value)
+	}
+	if value, ok := stu.mutation.AppendedBotWxidList(); ok {
+		_spec.AddModifier(func(u *sql.UpdateBuilder) {
+			sqljson.Append(u, soptask.FieldBotWxidList, value)
+		})
+	}
+	if stu.mutation.BotWxidListCleared() {
+		_spec.ClearField(soptask.FieldBotWxidList, field.TypeJSON)
+	}
+	if value, ok := stu.mutation.GetType(); ok {
+		_spec.SetField(soptask.FieldType, field.TypeInt, value)
+	}
+	if value, ok := stu.mutation.AddedType(); ok {
+		_spec.AddField(soptask.FieldType, field.TypeInt, value)
+	}
+	if value, ok := stu.mutation.PlanStartTime(); ok {
+		_spec.SetField(soptask.FieldPlanStartTime, field.TypeTime, value)
+	}
+	if stu.mutation.PlanStartTimeCleared() {
+		_spec.ClearField(soptask.FieldPlanStartTime, field.TypeTime)
+	}
+	if value, ok := stu.mutation.PlanEndTime(); ok {
+		_spec.SetField(soptask.FieldPlanEndTime, field.TypeTime, value)
+	}
+	if stu.mutation.PlanEndTimeCleared() {
+		_spec.ClearField(soptask.FieldPlanEndTime, field.TypeTime)
+	}
+	if value, ok := stu.mutation.CreatorID(); ok {
+		_spec.SetField(soptask.FieldCreatorID, field.TypeString, value)
+	}
+	if stu.mutation.CreatorIDCleared() {
+		_spec.ClearField(soptask.FieldCreatorID, field.TypeString)
+	}
+	if stu.mutation.TaskStagesCleared() {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.O2M,
+			Inverse: false,
+			Table:   soptask.TaskStagesTable,
+			Columns: []string{soptask.TaskStagesColumn},
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: sqlgraph.NewFieldSpec(sopstage.FieldID, field.TypeUint64),
+			},
+		}
+		_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
+	}
+	if nodes := stu.mutation.RemovedTaskStagesIDs(); len(nodes) > 0 && !stu.mutation.TaskStagesCleared() {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.O2M,
+			Inverse: false,
+			Table:   soptask.TaskStagesTable,
+			Columns: []string{soptask.TaskStagesColumn},
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: sqlgraph.NewFieldSpec(sopstage.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 := stu.mutation.TaskStagesIDs(); len(nodes) > 0 {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.O2M,
+			Inverse: false,
+			Table:   soptask.TaskStagesTable,
+			Columns: []string{soptask.TaskStagesColumn},
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: sqlgraph.NewFieldSpec(sopstage.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, stu.driver, _spec); err != nil {
+		if _, ok := err.(*sqlgraph.NotFoundError); ok {
+			err = &NotFoundError{soptask.Label}
+		} else if sqlgraph.IsConstraintError(err) {
+			err = &ConstraintError{msg: err.Error(), wrap: err}
+		}
+		return 0, err
+	}
+	stu.mutation.done = true
+	return n, nil
+}
+
+// SopTaskUpdateOne is the builder for updating a single SopTask entity.
+type SopTaskUpdateOne struct {
+	config
+	fields   []string
+	hooks    []Hook
+	mutation *SopTaskMutation
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (stuo *SopTaskUpdateOne) SetUpdatedAt(t time.Time) *SopTaskUpdateOne {
+	stuo.mutation.SetUpdatedAt(t)
+	return stuo
+}
+
+// SetStatus sets the "status" field.
+func (stuo *SopTaskUpdateOne) SetStatus(u uint8) *SopTaskUpdateOne {
+	stuo.mutation.ResetStatus()
+	stuo.mutation.SetStatus(u)
+	return stuo
+}
+
+// SetNillableStatus sets the "status" field if the given value is not nil.
+func (stuo *SopTaskUpdateOne) SetNillableStatus(u *uint8) *SopTaskUpdateOne {
+	if u != nil {
+		stuo.SetStatus(*u)
+	}
+	return stuo
+}
+
+// AddStatus adds u to the "status" field.
+func (stuo *SopTaskUpdateOne) AddStatus(u int8) *SopTaskUpdateOne {
+	stuo.mutation.AddStatus(u)
+	return stuo
+}
+
+// ClearStatus clears the value of the "status" field.
+func (stuo *SopTaskUpdateOne) ClearStatus() *SopTaskUpdateOne {
+	stuo.mutation.ClearStatus()
+	return stuo
+}
+
+// SetDeletedAt sets the "deleted_at" field.
+func (stuo *SopTaskUpdateOne) SetDeletedAt(t time.Time) *SopTaskUpdateOne {
+	stuo.mutation.SetDeletedAt(t)
+	return stuo
+}
+
+// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil.
+func (stuo *SopTaskUpdateOne) SetNillableDeletedAt(t *time.Time) *SopTaskUpdateOne {
+	if t != nil {
+		stuo.SetDeletedAt(*t)
+	}
+	return stuo
+}
+
+// ClearDeletedAt clears the value of the "deleted_at" field.
+func (stuo *SopTaskUpdateOne) ClearDeletedAt() *SopTaskUpdateOne {
+	stuo.mutation.ClearDeletedAt()
+	return stuo
+}
+
+// SetName sets the "name" field.
+func (stuo *SopTaskUpdateOne) SetName(s string) *SopTaskUpdateOne {
+	stuo.mutation.SetName(s)
+	return stuo
+}
+
+// SetNillableName sets the "name" field if the given value is not nil.
+func (stuo *SopTaskUpdateOne) SetNillableName(s *string) *SopTaskUpdateOne {
+	if s != nil {
+		stuo.SetName(*s)
+	}
+	return stuo
+}
+
+// SetBotWxidList sets the "bot_wxid_list" field.
+func (stuo *SopTaskUpdateOne) SetBotWxidList(s []string) *SopTaskUpdateOne {
+	stuo.mutation.SetBotWxidList(s)
+	return stuo
+}
+
+// AppendBotWxidList appends s to the "bot_wxid_list" field.
+func (stuo *SopTaskUpdateOne) AppendBotWxidList(s []string) *SopTaskUpdateOne {
+	stuo.mutation.AppendBotWxidList(s)
+	return stuo
+}
+
+// ClearBotWxidList clears the value of the "bot_wxid_list" field.
+func (stuo *SopTaskUpdateOne) ClearBotWxidList() *SopTaskUpdateOne {
+	stuo.mutation.ClearBotWxidList()
+	return stuo
+}
+
+// SetType sets the "type" field.
+func (stuo *SopTaskUpdateOne) SetType(i int) *SopTaskUpdateOne {
+	stuo.mutation.ResetType()
+	stuo.mutation.SetType(i)
+	return stuo
+}
+
+// SetNillableType sets the "type" field if the given value is not nil.
+func (stuo *SopTaskUpdateOne) SetNillableType(i *int) *SopTaskUpdateOne {
+	if i != nil {
+		stuo.SetType(*i)
+	}
+	return stuo
+}
+
+// AddType adds i to the "type" field.
+func (stuo *SopTaskUpdateOne) AddType(i int) *SopTaskUpdateOne {
+	stuo.mutation.AddType(i)
+	return stuo
+}
+
+// SetPlanStartTime sets the "plan_start_time" field.
+func (stuo *SopTaskUpdateOne) SetPlanStartTime(t time.Time) *SopTaskUpdateOne {
+	stuo.mutation.SetPlanStartTime(t)
+	return stuo
+}
+
+// SetNillablePlanStartTime sets the "plan_start_time" field if the given value is not nil.
+func (stuo *SopTaskUpdateOne) SetNillablePlanStartTime(t *time.Time) *SopTaskUpdateOne {
+	if t != nil {
+		stuo.SetPlanStartTime(*t)
+	}
+	return stuo
+}
+
+// ClearPlanStartTime clears the value of the "plan_start_time" field.
+func (stuo *SopTaskUpdateOne) ClearPlanStartTime() *SopTaskUpdateOne {
+	stuo.mutation.ClearPlanStartTime()
+	return stuo
+}
+
+// SetPlanEndTime sets the "plan_end_time" field.
+func (stuo *SopTaskUpdateOne) SetPlanEndTime(t time.Time) *SopTaskUpdateOne {
+	stuo.mutation.SetPlanEndTime(t)
+	return stuo
+}
+
+// SetNillablePlanEndTime sets the "plan_end_time" field if the given value is not nil.
+func (stuo *SopTaskUpdateOne) SetNillablePlanEndTime(t *time.Time) *SopTaskUpdateOne {
+	if t != nil {
+		stuo.SetPlanEndTime(*t)
+	}
+	return stuo
+}
+
+// ClearPlanEndTime clears the value of the "plan_end_time" field.
+func (stuo *SopTaskUpdateOne) ClearPlanEndTime() *SopTaskUpdateOne {
+	stuo.mutation.ClearPlanEndTime()
+	return stuo
+}
+
+// SetCreatorID sets the "creator_id" field.
+func (stuo *SopTaskUpdateOne) SetCreatorID(s string) *SopTaskUpdateOne {
+	stuo.mutation.SetCreatorID(s)
+	return stuo
+}
+
+// SetNillableCreatorID sets the "creator_id" field if the given value is not nil.
+func (stuo *SopTaskUpdateOne) SetNillableCreatorID(s *string) *SopTaskUpdateOne {
+	if s != nil {
+		stuo.SetCreatorID(*s)
+	}
+	return stuo
+}
+
+// ClearCreatorID clears the value of the "creator_id" field.
+func (stuo *SopTaskUpdateOne) ClearCreatorID() *SopTaskUpdateOne {
+	stuo.mutation.ClearCreatorID()
+	return stuo
+}
+
+// AddTaskStageIDs adds the "task_stages" edge to the SopStage entity by IDs.
+func (stuo *SopTaskUpdateOne) AddTaskStageIDs(ids ...uint64) *SopTaskUpdateOne {
+	stuo.mutation.AddTaskStageIDs(ids...)
+	return stuo
+}
+
+// AddTaskStages adds the "task_stages" edges to the SopStage entity.
+func (stuo *SopTaskUpdateOne) AddTaskStages(s ...*SopStage) *SopTaskUpdateOne {
+	ids := make([]uint64, len(s))
+	for i := range s {
+		ids[i] = s[i].ID
+	}
+	return stuo.AddTaskStageIDs(ids...)
+}
+
+// Mutation returns the SopTaskMutation object of the builder.
+func (stuo *SopTaskUpdateOne) Mutation() *SopTaskMutation {
+	return stuo.mutation
+}
+
+// ClearTaskStages clears all "task_stages" edges to the SopStage entity.
+func (stuo *SopTaskUpdateOne) ClearTaskStages() *SopTaskUpdateOne {
+	stuo.mutation.ClearTaskStages()
+	return stuo
+}
+
+// RemoveTaskStageIDs removes the "task_stages" edge to SopStage entities by IDs.
+func (stuo *SopTaskUpdateOne) RemoveTaskStageIDs(ids ...uint64) *SopTaskUpdateOne {
+	stuo.mutation.RemoveTaskStageIDs(ids...)
+	return stuo
+}
+
+// RemoveTaskStages removes "task_stages" edges to SopStage entities.
+func (stuo *SopTaskUpdateOne) RemoveTaskStages(s ...*SopStage) *SopTaskUpdateOne {
+	ids := make([]uint64, len(s))
+	for i := range s {
+		ids[i] = s[i].ID
+	}
+	return stuo.RemoveTaskStageIDs(ids...)
+}
+
+// Where appends a list predicates to the SopTaskUpdate builder.
+func (stuo *SopTaskUpdateOne) Where(ps ...predicate.SopTask) *SopTaskUpdateOne {
+	stuo.mutation.Where(ps...)
+	return stuo
+}
+
+// Select allows selecting one or more fields (columns) of the returned entity.
+// The default is selecting all fields defined in the entity schema.
+func (stuo *SopTaskUpdateOne) Select(field string, fields ...string) *SopTaskUpdateOne {
+	stuo.fields = append([]string{field}, fields...)
+	return stuo
+}
+
+// Save executes the query and returns the updated SopTask entity.
+func (stuo *SopTaskUpdateOne) Save(ctx context.Context) (*SopTask, error) {
+	if err := stuo.defaults(); err != nil {
+		return nil, err
+	}
+	return withHooks(ctx, stuo.sqlSave, stuo.mutation, stuo.hooks)
+}
+
+// SaveX is like Save, but panics if an error occurs.
+func (stuo *SopTaskUpdateOne) SaveX(ctx context.Context) *SopTask {
+	node, err := stuo.Save(ctx)
+	if err != nil {
+		panic(err)
+	}
+	return node
+}
+
+// Exec executes the query on the entity.
+func (stuo *SopTaskUpdateOne) Exec(ctx context.Context) error {
+	_, err := stuo.Save(ctx)
+	return err
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (stuo *SopTaskUpdateOne) ExecX(ctx context.Context) {
+	if err := stuo.Exec(ctx); err != nil {
+		panic(err)
+	}
+}
+
+// defaults sets the default values of the builder before save.
+func (stuo *SopTaskUpdateOne) defaults() error {
+	if _, ok := stuo.mutation.UpdatedAt(); !ok {
+		if soptask.UpdateDefaultUpdatedAt == nil {
+			return fmt.Errorf("ent: uninitialized soptask.UpdateDefaultUpdatedAt (forgotten import ent/runtime?)")
+		}
+		v := soptask.UpdateDefaultUpdatedAt()
+		stuo.mutation.SetUpdatedAt(v)
+	}
+	return nil
+}
+
+// check runs all checks and user-defined validators on the builder.
+func (stuo *SopTaskUpdateOne) check() error {
+	if v, ok := stuo.mutation.Name(); ok {
+		if err := soptask.NameValidator(v); err != nil {
+			return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "SopTask.name": %w`, err)}
+		}
+	}
+	return nil
+}
+
+func (stuo *SopTaskUpdateOne) sqlSave(ctx context.Context) (_node *SopTask, err error) {
+	if err := stuo.check(); err != nil {
+		return _node, err
+	}
+	_spec := sqlgraph.NewUpdateSpec(soptask.Table, soptask.Columns, sqlgraph.NewFieldSpec(soptask.FieldID, field.TypeUint64))
+	id, ok := stuo.mutation.ID()
+	if !ok {
+		return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "SopTask.id" for update`)}
+	}
+	_spec.Node.ID.Value = id
+	if fields := stuo.fields; len(fields) > 0 {
+		_spec.Node.Columns = make([]string, 0, len(fields))
+		_spec.Node.Columns = append(_spec.Node.Columns, soptask.FieldID)
+		for _, f := range fields {
+			if !soptask.ValidColumn(f) {
+				return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
+			}
+			if f != soptask.FieldID {
+				_spec.Node.Columns = append(_spec.Node.Columns, f)
+			}
+		}
+	}
+	if ps := stuo.mutation.predicates; len(ps) > 0 {
+		_spec.Predicate = func(selector *sql.Selector) {
+			for i := range ps {
+				ps[i](selector)
+			}
+		}
+	}
+	if value, ok := stuo.mutation.UpdatedAt(); ok {
+		_spec.SetField(soptask.FieldUpdatedAt, field.TypeTime, value)
+	}
+	if value, ok := stuo.mutation.Status(); ok {
+		_spec.SetField(soptask.FieldStatus, field.TypeUint8, value)
+	}
+	if value, ok := stuo.mutation.AddedStatus(); ok {
+		_spec.AddField(soptask.FieldStatus, field.TypeUint8, value)
+	}
+	if stuo.mutation.StatusCleared() {
+		_spec.ClearField(soptask.FieldStatus, field.TypeUint8)
+	}
+	if value, ok := stuo.mutation.DeletedAt(); ok {
+		_spec.SetField(soptask.FieldDeletedAt, field.TypeTime, value)
+	}
+	if stuo.mutation.DeletedAtCleared() {
+		_spec.ClearField(soptask.FieldDeletedAt, field.TypeTime)
+	}
+	if value, ok := stuo.mutation.Name(); ok {
+		_spec.SetField(soptask.FieldName, field.TypeString, value)
+	}
+	if value, ok := stuo.mutation.BotWxidList(); ok {
+		_spec.SetField(soptask.FieldBotWxidList, field.TypeJSON, value)
+	}
+	if value, ok := stuo.mutation.AppendedBotWxidList(); ok {
+		_spec.AddModifier(func(u *sql.UpdateBuilder) {
+			sqljson.Append(u, soptask.FieldBotWxidList, value)
+		})
+	}
+	if stuo.mutation.BotWxidListCleared() {
+		_spec.ClearField(soptask.FieldBotWxidList, field.TypeJSON)
+	}
+	if value, ok := stuo.mutation.GetType(); ok {
+		_spec.SetField(soptask.FieldType, field.TypeInt, value)
+	}
+	if value, ok := stuo.mutation.AddedType(); ok {
+		_spec.AddField(soptask.FieldType, field.TypeInt, value)
+	}
+	if value, ok := stuo.mutation.PlanStartTime(); ok {
+		_spec.SetField(soptask.FieldPlanStartTime, field.TypeTime, value)
+	}
+	if stuo.mutation.PlanStartTimeCleared() {
+		_spec.ClearField(soptask.FieldPlanStartTime, field.TypeTime)
+	}
+	if value, ok := stuo.mutation.PlanEndTime(); ok {
+		_spec.SetField(soptask.FieldPlanEndTime, field.TypeTime, value)
+	}
+	if stuo.mutation.PlanEndTimeCleared() {
+		_spec.ClearField(soptask.FieldPlanEndTime, field.TypeTime)
+	}
+	if value, ok := stuo.mutation.CreatorID(); ok {
+		_spec.SetField(soptask.FieldCreatorID, field.TypeString, value)
+	}
+	if stuo.mutation.CreatorIDCleared() {
+		_spec.ClearField(soptask.FieldCreatorID, field.TypeString)
+	}
+	if stuo.mutation.TaskStagesCleared() {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.O2M,
+			Inverse: false,
+			Table:   soptask.TaskStagesTable,
+			Columns: []string{soptask.TaskStagesColumn},
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: sqlgraph.NewFieldSpec(sopstage.FieldID, field.TypeUint64),
+			},
+		}
+		_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
+	}
+	if nodes := stuo.mutation.RemovedTaskStagesIDs(); len(nodes) > 0 && !stuo.mutation.TaskStagesCleared() {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.O2M,
+			Inverse: false,
+			Table:   soptask.TaskStagesTable,
+			Columns: []string{soptask.TaskStagesColumn},
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: sqlgraph.NewFieldSpec(sopstage.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 := stuo.mutation.TaskStagesIDs(); len(nodes) > 0 {
+		edge := &sqlgraph.EdgeSpec{
+			Rel:     sqlgraph.O2M,
+			Inverse: false,
+			Table:   soptask.TaskStagesTable,
+			Columns: []string{soptask.TaskStagesColumn},
+			Bidi:    false,
+			Target: &sqlgraph.EdgeTarget{
+				IDSpec: sqlgraph.NewFieldSpec(sopstage.FieldID, field.TypeUint64),
+			},
+		}
+		for _, k := range nodes {
+			edge.Target.Nodes = append(edge.Target.Nodes, k)
+		}
+		_spec.Edges.Add = append(_spec.Edges.Add, edge)
+	}
+	_node = &SopTask{config: stuo.config}
+	_spec.Assign = _node.assignValues
+	_spec.ScanValues = _node.scanValues
+	if err = sqlgraph.UpdateNode(ctx, stuo.driver, _spec); err != nil {
+		if _, ok := err.(*sqlgraph.NotFoundError); ok {
+			err = &NotFoundError{soptask.Label}
+		} else if sqlgraph.IsConstraintError(err) {
+			err = &ConstraintError{msg: err.Error(), wrap: err}
+		}
+		return nil, err
+	}
+	stuo.mutation.done = true
+	return _node, nil
+}

+ 12 - 0
ent/tx.go

@@ -22,8 +22,16 @@ type Tx struct {
 	LabelRelationship *LabelRelationshipClient
 	// Message is the client for interacting with the Message builders.
 	Message *MessageClient
+	// MessageRecords is the client for interacting with the MessageRecords builders.
+	MessageRecords *MessageRecordsClient
 	// Server is the client for interacting with the Server builders.
 	Server *ServerClient
+	// SopNode is the client for interacting with the SopNode builders.
+	SopNode *SopNodeClient
+	// SopStage is the client for interacting with the SopStage builders.
+	SopStage *SopStageClient
+	// SopTask is the client for interacting with the SopTask builders.
+	SopTask *SopTaskClient
 	// Wx is the client for interacting with the Wx builders.
 	Wx *WxClient
 
@@ -161,7 +169,11 @@ func (tx *Tx) init() {
 	tx.Label = NewLabelClient(tx.config)
 	tx.LabelRelationship = NewLabelRelationshipClient(tx.config)
 	tx.Message = NewMessageClient(tx.config)
+	tx.MessageRecords = NewMessageRecordsClient(tx.config)
 	tx.Server = NewServerClient(tx.config)
+	tx.SopNode = NewSopNodeClient(tx.config)
+	tx.SopStage = NewSopStageClient(tx.config)
+	tx.SopTask = NewSopTaskClient(tx.config)
 	tx.Wx = NewWxClient(tx.config)
 }
 

+ 44 - 0
internal/handler/message_records/create_message_records_handler.go

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

+ 44 - 0
internal/handler/message_records/delete_message_records_handler.go

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

+ 44 - 0
internal/handler/message_records/get_message_records_by_id_handler.go

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

+ 44 - 0
internal/handler/message_records/get_message_records_list_handler.go

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

+ 44 - 0
internal/handler/message_records/update_message_records_handler.go

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

+ 140 - 0
internal/handler/routes.go

@@ -12,6 +12,10 @@ import (
 	contact "wechat-api/internal/handler/contact"
 	label "wechat-api/internal/handler/label"
 	label_relationship "wechat-api/internal/handler/label_relationship"
+	message_records "wechat-api/internal/handler/message_records"
+	sop_node "wechat-api/internal/handler/sop_node"
+	sop_stage "wechat-api/internal/handler/sop_stage"
+	sop_task "wechat-api/internal/handler/sop_task"
 	"wechat-api/internal/svc"
 
 	"github.com/zeromicro/go-zero/rest"
@@ -285,4 +289,140 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
 		),
 		rest.WithJwt(serverCtx.Config.Auth.AccessSecret),
 	)
+
+	server.AddRoutes(
+		rest.WithMiddlewares(
+			[]rest.Middleware{serverCtx.Authority},
+			[]rest.Route{
+				{
+					Method:  http.MethodPost,
+					Path:    "/sop_task/create",
+					Handler: sop_task.CreateSopTaskHandler(serverCtx),
+				},
+				{
+					Method:  http.MethodPost,
+					Path:    "/sop_task/update",
+					Handler: sop_task.UpdateSopTaskHandler(serverCtx),
+				},
+				{
+					Method:  http.MethodPost,
+					Path:    "/sop_task/delete",
+					Handler: sop_task.DeleteSopTaskHandler(serverCtx),
+				},
+				{
+					Method:  http.MethodPost,
+					Path:    "/sop_task/list",
+					Handler: sop_task.GetSopTaskListHandler(serverCtx),
+				},
+				{
+					Method:  http.MethodPost,
+					Path:    "/sop_task",
+					Handler: sop_task.GetSopTaskByIdHandler(serverCtx),
+				},
+			}...,
+		),
+		rest.WithJwt(serverCtx.Config.Auth.AccessSecret),
+	)
+
+	server.AddRoutes(
+		rest.WithMiddlewares(
+			[]rest.Middleware{serverCtx.Authority},
+			[]rest.Route{
+				{
+					Method:  http.MethodPost,
+					Path:    "/sop_stage/create",
+					Handler: sop_stage.CreateSopStageHandler(serverCtx),
+				},
+				{
+					Method:  http.MethodPost,
+					Path:    "/sop_stage/update",
+					Handler: sop_stage.UpdateSopStageHandler(serverCtx),
+				},
+				{
+					Method:  http.MethodPost,
+					Path:    "/sop_stage/delete",
+					Handler: sop_stage.DeleteSopStageHandler(serverCtx),
+				},
+				{
+					Method:  http.MethodPost,
+					Path:    "/sop_stage/list",
+					Handler: sop_stage.GetSopStageListHandler(serverCtx),
+				},
+				{
+					Method:  http.MethodPost,
+					Path:    "/sop_stage",
+					Handler: sop_stage.GetSopStageByIdHandler(serverCtx),
+				},
+			}...,
+		),
+		rest.WithJwt(serverCtx.Config.Auth.AccessSecret),
+	)
+
+	server.AddRoutes(
+		rest.WithMiddlewares(
+			[]rest.Middleware{serverCtx.Authority},
+			[]rest.Route{
+				{
+					Method:  http.MethodPost,
+					Path:    "/sop_node/create",
+					Handler: sop_node.CreateSopNodeHandler(serverCtx),
+				},
+				{
+					Method:  http.MethodPost,
+					Path:    "/sop_node/update",
+					Handler: sop_node.UpdateSopNodeHandler(serverCtx),
+				},
+				{
+					Method:  http.MethodPost,
+					Path:    "/sop_node/delete",
+					Handler: sop_node.DeleteSopNodeHandler(serverCtx),
+				},
+				{
+					Method:  http.MethodPost,
+					Path:    "/sop_node/list",
+					Handler: sop_node.GetSopNodeListHandler(serverCtx),
+				},
+				{
+					Method:  http.MethodPost,
+					Path:    "/sop_node",
+					Handler: sop_node.GetSopNodeByIdHandler(serverCtx),
+				},
+			}...,
+		),
+		rest.WithJwt(serverCtx.Config.Auth.AccessSecret),
+	)
+
+	server.AddRoutes(
+		rest.WithMiddlewares(
+			[]rest.Middleware{serverCtx.Authority},
+			[]rest.Route{
+				{
+					Method:  http.MethodPost,
+					Path:    "/message_records/create",
+					Handler: message_records.CreateMessageRecordsHandler(serverCtx),
+				},
+				{
+					Method:  http.MethodPost,
+					Path:    "/message_records/update",
+					Handler: message_records.UpdateMessageRecordsHandler(serverCtx),
+				},
+				{
+					Method:  http.MethodPost,
+					Path:    "/message_records/delete",
+					Handler: message_records.DeleteMessageRecordsHandler(serverCtx),
+				},
+				{
+					Method:  http.MethodPost,
+					Path:    "/message_records/list",
+					Handler: message_records.GetMessageRecordsListHandler(serverCtx),
+				},
+				{
+					Method:  http.MethodPost,
+					Path:    "/message_records",
+					Handler: message_records.GetMessageRecordsByIdHandler(serverCtx),
+				},
+			}...,
+		),
+		rest.WithJwt(serverCtx.Config.Auth.AccessSecret),
+	)
 }

+ 44 - 0
internal/handler/sop_node/create_sop_node_handler.go

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

+ 44 - 0
internal/handler/sop_node/delete_sop_node_handler.go

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

+ 44 - 0
internal/handler/sop_node/get_sop_node_by_id_handler.go

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

+ 44 - 0
internal/handler/sop_node/get_sop_node_list_handler.go

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

+ 44 - 0
internal/handler/sop_node/update_sop_node_handler.go

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

+ 44 - 0
internal/handler/sop_stage/create_sop_stage_handler.go

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

+ 44 - 0
internal/handler/sop_stage/delete_sop_stage_handler.go

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

+ 44 - 0
internal/handler/sop_stage/get_sop_stage_by_id_handler.go

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

+ 44 - 0
internal/handler/sop_stage/get_sop_stage_list_handler.go

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

+ 44 - 0
internal/handler/sop_stage/update_sop_stage_handler.go

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

+ 44 - 0
internal/handler/sop_task/create_sop_task_handler.go

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

+ 44 - 0
internal/handler/sop_task/delete_sop_task_handler.go

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

+ 44 - 0
internal/handler/sop_task/get_sop_task_by_id_handler.go

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

+ 44 - 0
internal/handler/sop_task/get_sop_task_list_handler.go

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

+ 44 - 0
internal/handler/sop_task/update_sop_task_handler.go

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

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

@@ -6,6 +6,254 @@ import (
 )
 
 func (l *InitDatabaseLogic) insertApiData() (err error) {
+	// MessageRecords
+
+	_, err = l.svcCtx.CoreRpc.CreateApi(l.ctx, &core.ApiInfo{
+		ServiceName: pointy.GetPointer("Wechat"),
+		Path:        pointy.GetPointer("/message_records/create"),
+		Description: pointy.GetPointer("apiDesc.createMessageRecords"),
+		ApiGroup:    pointy.GetPointer("message_records"),
+		Method:      pointy.GetPointer("POST"),
+	})
+
+	if err != nil {
+		return err
+	}
+
+	_, err = l.svcCtx.CoreRpc.CreateApi(l.ctx, &core.ApiInfo{
+		ServiceName: pointy.GetPointer("Wechat"),
+		Path:        pointy.GetPointer("/message_records/update"),
+		Description: pointy.GetPointer("apiDesc.updateMessageRecords"),
+		ApiGroup:    pointy.GetPointer("message_records"),
+		Method:      pointy.GetPointer("POST"),
+	})
+
+	if err != nil {
+		return err
+	}
+
+	_, err = l.svcCtx.CoreRpc.CreateApi(l.ctx, &core.ApiInfo{
+		ServiceName: pointy.GetPointer("Wechat"),
+		Path:        pointy.GetPointer("/message_records/delete"),
+		Description: pointy.GetPointer("apiDesc.deleteMessageRecords"),
+		ApiGroup:    pointy.GetPointer("message_records"),
+		Method:      pointy.GetPointer("POST"),
+	})
+
+	if err != nil {
+		return err
+	}
+
+	_, err = l.svcCtx.CoreRpc.CreateApi(l.ctx, &core.ApiInfo{
+		ServiceName: pointy.GetPointer("Wechat"),
+		Path:        pointy.GetPointer("/message_records/list"),
+		Description: pointy.GetPointer("apiDesc.getMessageRecordsList"),
+		ApiGroup:    pointy.GetPointer("message_records"),
+		Method:      pointy.GetPointer("POST"),
+	})
+
+	if err != nil {
+		return err
+	}
+
+	_, err = l.svcCtx.CoreRpc.CreateApi(l.ctx, &core.ApiInfo{
+		ServiceName: pointy.GetPointer("Wechat"),
+		Path:        pointy.GetPointer("/message_records"),
+		Description: pointy.GetPointer("apiDesc.getMessageRecordsById"),
+		ApiGroup:    pointy.GetPointer("message_records"),
+		Method:      pointy.GetPointer("POST"),
+	})
+
+	if err != nil {
+		return err
+	}
+
+	// SopNode
+
+	_, err = l.svcCtx.CoreRpc.CreateApi(l.ctx, &core.ApiInfo{
+		ServiceName: pointy.GetPointer("Wechat"),
+		Path:        pointy.GetPointer("/sop_node/create"),
+		Description: pointy.GetPointer("apiDesc.createSopNode"),
+		ApiGroup:    pointy.GetPointer("sop_node"),
+		Method:      pointy.GetPointer("POST"),
+	})
+
+	if err != nil {
+		return err
+	}
+
+	_, err = l.svcCtx.CoreRpc.CreateApi(l.ctx, &core.ApiInfo{
+		ServiceName: pointy.GetPointer("Wechat"),
+		Path:        pointy.GetPointer("/sop_node/update"),
+		Description: pointy.GetPointer("apiDesc.updateSopNode"),
+		ApiGroup:    pointy.GetPointer("sop_node"),
+		Method:      pointy.GetPointer("POST"),
+	})
+
+	if err != nil {
+		return err
+	}
+
+	_, err = l.svcCtx.CoreRpc.CreateApi(l.ctx, &core.ApiInfo{
+		ServiceName: pointy.GetPointer("Wechat"),
+		Path:        pointy.GetPointer("/sop_node/delete"),
+		Description: pointy.GetPointer("apiDesc.deleteSopNode"),
+		ApiGroup:    pointy.GetPointer("sop_node"),
+		Method:      pointy.GetPointer("POST"),
+	})
+
+	if err != nil {
+		return err
+	}
+
+	_, err = l.svcCtx.CoreRpc.CreateApi(l.ctx, &core.ApiInfo{
+		ServiceName: pointy.GetPointer("Wechat"),
+		Path:        pointy.GetPointer("/sop_node/list"),
+		Description: pointy.GetPointer("apiDesc.getSopNodeList"),
+		ApiGroup:    pointy.GetPointer("sop_node"),
+		Method:      pointy.GetPointer("POST"),
+	})
+
+	if err != nil {
+		return err
+	}
+
+	_, err = l.svcCtx.CoreRpc.CreateApi(l.ctx, &core.ApiInfo{
+		ServiceName: pointy.GetPointer("Wechat"),
+		Path:        pointy.GetPointer("/sop_node"),
+		Description: pointy.GetPointer("apiDesc.getSopNodeById"),
+		ApiGroup:    pointy.GetPointer("sop_node"),
+		Method:      pointy.GetPointer("POST"),
+	})
+
+	if err != nil {
+		return err
+	}
+
+	// SopStage
+
+	_, err = l.svcCtx.CoreRpc.CreateApi(l.ctx, &core.ApiInfo{
+		ServiceName: pointy.GetPointer("Wechat"),
+		Path:        pointy.GetPointer("/sop_stage/create"),
+		Description: pointy.GetPointer("apiDesc.createSopStage"),
+		ApiGroup:    pointy.GetPointer("sop_stage"),
+		Method:      pointy.GetPointer("POST"),
+	})
+
+	if err != nil {
+		return err
+	}
+
+	_, err = l.svcCtx.CoreRpc.CreateApi(l.ctx, &core.ApiInfo{
+		ServiceName: pointy.GetPointer("Wechat"),
+		Path:        pointy.GetPointer("/sop_stage/update"),
+		Description: pointy.GetPointer("apiDesc.updateSopStage"),
+		ApiGroup:    pointy.GetPointer("sop_stage"),
+		Method:      pointy.GetPointer("POST"),
+	})
+
+	if err != nil {
+		return err
+	}
+
+	_, err = l.svcCtx.CoreRpc.CreateApi(l.ctx, &core.ApiInfo{
+		ServiceName: pointy.GetPointer("Wechat"),
+		Path:        pointy.GetPointer("/sop_stage/delete"),
+		Description: pointy.GetPointer("apiDesc.deleteSopStage"),
+		ApiGroup:    pointy.GetPointer("sop_stage"),
+		Method:      pointy.GetPointer("POST"),
+	})
+
+	if err != nil {
+		return err
+	}
+
+	_, err = l.svcCtx.CoreRpc.CreateApi(l.ctx, &core.ApiInfo{
+		ServiceName: pointy.GetPointer("Wechat"),
+		Path:        pointy.GetPointer("/sop_stage/list"),
+		Description: pointy.GetPointer("apiDesc.getSopStageList"),
+		ApiGroup:    pointy.GetPointer("sop_stage"),
+		Method:      pointy.GetPointer("POST"),
+	})
+
+	if err != nil {
+		return err
+	}
+
+	_, err = l.svcCtx.CoreRpc.CreateApi(l.ctx, &core.ApiInfo{
+		ServiceName: pointy.GetPointer("Wechat"),
+		Path:        pointy.GetPointer("/sop_stage"),
+		Description: pointy.GetPointer("apiDesc.getSopStageById"),
+		ApiGroup:    pointy.GetPointer("sop_stage"),
+		Method:      pointy.GetPointer("POST"),
+	})
+
+	if err != nil {
+		return err
+	}
+
+	// SopTask
+
+	_, err = l.svcCtx.CoreRpc.CreateApi(l.ctx, &core.ApiInfo{
+		ServiceName: pointy.GetPointer("Wechat"),
+		Path:        pointy.GetPointer("/sop_task/create"),
+		Description: pointy.GetPointer("apiDesc.createSopTask"),
+		ApiGroup:    pointy.GetPointer("sop_task"),
+		Method:      pointy.GetPointer("POST"),
+	})
+
+	if err != nil {
+		return err
+	}
+
+	_, err = l.svcCtx.CoreRpc.CreateApi(l.ctx, &core.ApiInfo{
+		ServiceName: pointy.GetPointer("Wechat"),
+		Path:        pointy.GetPointer("/sop_task/update"),
+		Description: pointy.GetPointer("apiDesc.updateSopTask"),
+		ApiGroup:    pointy.GetPointer("sop_task"),
+		Method:      pointy.GetPointer("POST"),
+	})
+
+	if err != nil {
+		return err
+	}
+
+	_, err = l.svcCtx.CoreRpc.CreateApi(l.ctx, &core.ApiInfo{
+		ServiceName: pointy.GetPointer("Wechat"),
+		Path:        pointy.GetPointer("/sop_task/delete"),
+		Description: pointy.GetPointer("apiDesc.deleteSopTask"),
+		ApiGroup:    pointy.GetPointer("sop_task"),
+		Method:      pointy.GetPointer("POST"),
+	})
+
+	if err != nil {
+		return err
+	}
+
+	_, err = l.svcCtx.CoreRpc.CreateApi(l.ctx, &core.ApiInfo{
+		ServiceName: pointy.GetPointer("Wechat"),
+		Path:        pointy.GetPointer("/sop_task/list"),
+		Description: pointy.GetPointer("apiDesc.getSopTaskList"),
+		ApiGroup:    pointy.GetPointer("sop_task"),
+		Method:      pointy.GetPointer("POST"),
+	})
+
+	if err != nil {
+		return err
+	}
+
+	_, err = l.svcCtx.CoreRpc.CreateApi(l.ctx, &core.ApiInfo{
+		ServiceName: pointy.GetPointer("Wechat"),
+		Path:        pointy.GetPointer("/sop_task"),
+		Description: pointy.GetPointer("apiDesc.getSopTaskById"),
+		ApiGroup:    pointy.GetPointer("sop_task"),
+		Method:      pointy.GetPointer("POST"),
+	})
+
+	if err != nil {
+		return err
+	}
+
 	// LabelRelationship
 
 	_, err = l.svcCtx.CoreRpc.CreateApi(l.ctx, &core.ApiInfo{

+ 50 - 0
internal/logic/message_records/create_message_records_logic.go

@@ -0,0 +1,50 @@
+package message_records
+
+import (
+	"context"
+
+	"wechat-api/internal/svc"
+	"wechat-api/internal/types"
+	"wechat-api/internal/utils/dberrorhandler"
+
+    "github.com/suyuan32/simple-admin-common/msg/errormsg"
+    "github.com/suyuan32/simple-admin-common/utils/pointy"
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type CreateMessageRecordsLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewCreateMessageRecordsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateMessageRecordsLogic {
+	return &CreateMessageRecordsLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+func (l *CreateMessageRecordsLogic) CreateMessageRecords(req *types.MessageRecordsInfo) (*types.BaseMsgResp, error) {
+    _, err := l.svcCtx.DB.MessageRecords.Create().
+			SetNotNilStatus(req.Status).
+			SetNotNilBotWxid(req.BotWxid).
+			SetNotNilContactID(req.ContactId).
+			SetNotNilContactType(req.ContactType).
+			SetNotNilContactWxid(req.ContactWxid).
+			SetNotNilContentType(req.ContentType).
+			SetNotNilContent(req.Content).
+			SetNotNilErrorDetail(req.ErrorDetail).
+			SetNotNilSendTime(pointy.GetTimeMilliPointer(req.SendTime)).
+			SetNotNilSourceType(req.SourceType).
+			SetNotNilSourceID(req.SourceId).
+			SetNotNilSubSourceID(req.SubSourceId).
+			Save(l.ctx)
+
+    if err != nil {
+		return nil, dberrorhandler.DefaultEntError(l.Logger, err, req)
+	}
+
+    return &types.BaseMsgResp{Msg: errormsg.CreateSuccess}, nil
+}

+ 37 - 0
internal/logic/message_records/delete_message_records_logic.go

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

+ 62 - 0
internal/logic/message_records/get_message_records_by_id_logic.go

@@ -0,0 +1,62 @@
+package message_records
+
+import (
+	"context"
+
+	"wechat-api/internal/svc"
+	"wechat-api/internal/types"
+	"wechat-api/internal/utils/dberrorhandler"
+
+    "github.com/suyuan32/simple-admin-common/msg/errormsg"
+
+	"github.com/suyuan32/simple-admin-common/utils/pointy"
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type GetMessageRecordsByIdLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewGetMessageRecordsByIdLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetMessageRecordsByIdLogic {
+	return &GetMessageRecordsByIdLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+func (l *GetMessageRecordsByIdLogic) GetMessageRecordsById(req *types.IDReq) (*types.MessageRecordsInfoResp, error) {
+	data, err := l.svcCtx.DB.MessageRecords.Get(l.ctx, req.Id)
+	if err != nil {
+		return nil, dberrorhandler.DefaultEntError(l.Logger, err, req)
+	}
+
+	return &types.MessageRecordsInfoResp{
+	    BaseDataInfo: types.BaseDataInfo{
+            Code: 0,
+            Msg:  errormsg.Success,
+        },
+        Data: types.MessageRecordsInfo{
+            BaseIDInfo:    types.BaseIDInfo{
+				Id:          &data.ID,
+				CreatedAt:    pointy.GetPointer(data.CreatedAt.UnixMilli()),
+				UpdatedAt:    pointy.GetPointer(data.UpdatedAt.UnixMilli()),
+            },
+			Status:	&data.Status,
+			BotWxid:	&data.BotWxid,
+			ContactId:	&data.ContactID,
+			ContactType:	&data.ContactType,
+			ContactWxid:	&data.ContactWxid,
+			ContentType:	&data.ContentType,
+			Content:	&data.Content,
+			ErrorDetail:	&data.ErrorDetail,
+			SendTime:	pointy.GetUnixMilliPointer(data.SendTime.UnixMilli()),
+			SourceType:	&data.SourceType,
+			SourceId:	&data.SourceID,
+			SubSourceId:	&data.SubSourceID,
+        },
+	}, nil
+}
+

+ 77 - 0
internal/logic/message_records/get_message_records_list_logic.go

@@ -0,0 +1,77 @@
+package message_records
+
+import (
+	"context"
+
+	"wechat-api/ent/messagerecords"
+	"wechat-api/ent/predicate"
+	"wechat-api/internal/svc"
+	"wechat-api/internal/types"
+	"wechat-api/internal/utils/dberrorhandler"
+
+    "github.com/suyuan32/simple-admin-common/msg/errormsg"
+
+	"github.com/suyuan32/simple-admin-common/utils/pointy"
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type GetMessageRecordsListLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewGetMessageRecordsListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetMessageRecordsListLogic {
+	return &GetMessageRecordsListLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+func (l *GetMessageRecordsListLogic) GetMessageRecordsList(req *types.MessageRecordsListReq) (*types.MessageRecordsListResp, error) {
+	var predicates []predicate.MessageRecords
+	if req.BotWxid != nil {
+		predicates = append(predicates, messagerecords.BotWxidContains(*req.BotWxid))
+	}
+	if req.ContactWxid != nil {
+		predicates = append(predicates, messagerecords.ContactWxidContains(*req.ContactWxid))
+	}
+	if req.Content != nil {
+		predicates = append(predicates, messagerecords.ContentContains(*req.Content))
+	}
+	data, err := l.svcCtx.DB.MessageRecords.Query().Where(predicates...).Page(l.ctx, req.Page, req.PageSize)
+
+	if err != nil {
+		return nil, dberrorhandler.DefaultEntError(l.Logger, err, req)
+	}
+
+	resp := &types.MessageRecordsListResp{}
+	resp.Msg = errormsg.Success
+	resp.Data.Total = data.PageDetails.Total
+
+	for _, v := range data.List {
+		resp.Data.Data = append(resp.Data.Data,
+		types.MessageRecordsInfo{
+			BaseIDInfo:    types.BaseIDInfo{
+				Id:          &v.ID,
+				CreatedAt:    pointy.GetPointer(v.CreatedAt.UnixMilli()),
+				UpdatedAt:    pointy.GetPointer(v.UpdatedAt.UnixMilli()),
+            },
+			Status:	&v.Status,
+			BotWxid:	&v.BotWxid,
+			ContactId:	&v.ContactID,
+			ContactType:	&v.ContactType,
+			ContactWxid:	&v.ContactWxid,
+			ContentType:	&v.ContentType,
+			Content:	&v.Content,
+			ErrorDetail:	&v.ErrorDetail,
+			SendTime:	pointy.GetUnixMilliPointer(v.SendTime.UnixMilli()),
+			SourceType:	&v.SourceType,
+			SourceId:	&v.SourceID,
+			SubSourceId:	&v.SubSourceID,
+		})
+	}
+
+	return resp, nil
+}

+ 51 - 0
internal/logic/message_records/update_message_records_logic.go

@@ -0,0 +1,51 @@
+package message_records
+
+import (
+	"context"
+
+	"wechat-api/internal/svc"
+	"wechat-api/internal/types"
+	"wechat-api/internal/utils/dberrorhandler"
+
+
+	"github.com/suyuan32/simple-admin-common/msg/errormsg"
+	"github.com/suyuan32/simple-admin-common/utils/pointy"
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type UpdateMessageRecordsLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewUpdateMessageRecordsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateMessageRecordsLogic {
+	return &UpdateMessageRecordsLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+func (l *UpdateMessageRecordsLogic) UpdateMessageRecords(req *types.MessageRecordsInfo) (*types.BaseMsgResp, error) {
+    err := l.svcCtx.DB.MessageRecords.UpdateOneID(*req.Id).
+			SetNotNilStatus(req.Status).
+			SetNotNilBotWxid(req.BotWxid).
+			SetNotNilContactID(req.ContactId).
+			SetNotNilContactType(req.ContactType).
+			SetNotNilContactWxid(req.ContactWxid).
+			SetNotNilContentType(req.ContentType).
+			SetNotNilContent(req.Content).
+			SetNotNilErrorDetail(req.ErrorDetail).
+			SetNotNilSendTime(pointy.GetTimeMilliPointer(req.SendTime)).
+			SetNotNilSourceType(req.SourceType).
+			SetNotNilSourceID(req.SourceId).
+			SetNotNilSubSourceID(req.SubSourceId).
+			Exec(l.ctx)
+
+    if err != nil {
+		return nil, dberrorhandler.DefaultEntError(l.Logger, err, req)
+	}
+
+    return &types.BaseMsgResp{Msg: errormsg.UpdateSuccess}, nil
+}

+ 63 - 0
internal/logic/sop_node/create_sop_node_logic.go

@@ -0,0 +1,63 @@
+package sop_node
+
+import (
+	"context"
+	"wechat-api/ent/custom_types"
+
+	"wechat-api/internal/svc"
+	"wechat-api/internal/types"
+	"wechat-api/internal/utils/dberrorhandler"
+
+	"github.com/suyuan32/simple-admin-common/msg/errormsg"
+
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type CreateSopNodeLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewCreateSopNodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateSopNodeLogic {
+	return &CreateSopNodeLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+func (l *CreateSopNodeLogic) CreateSopNode(req *types.SopNodeInfo) (*types.BaseMsgResp, error) {
+	conditionList := make([]custom_types.Condition, len(req.ConditionList))
+	for i, condition := range req.ConditionList {
+		conditionList[i] = custom_types.Condition{
+			Equal:       condition.Equal,
+			LabelIdList: condition.LabelIdList,
+		}
+	}
+
+	actionMessage := make([]custom_types.Action, len(req.ActionMessage))
+	for i, condition := range req.ActionMessage {
+		actionMessage[i] = custom_types.Action{
+			Type:    condition.Type,
+			Content: condition.Content,
+		}
+	}
+
+	_, err := l.svcCtx.DB.SopNode.Create().
+		SetNotNilStatus(req.Status).
+		SetNotNilStageID(req.StageId).
+		SetNotNilParentID(req.ParentId).
+		SetNotNilName(req.Name).
+		SetNotNilConditionType(req.ConditionType).
+		SetNotNilConditionList(conditionList).
+		SetNotNilActionMessage(actionMessage).
+		SetNotNilActionLabel(req.ActionLabel).
+		Save(l.ctx)
+
+	if err != nil {
+		return nil, dberrorhandler.DefaultEntError(l.Logger, err, req)
+	}
+
+	return &types.BaseMsgResp{Msg: errormsg.CreateSuccess}, nil
+}

+ 37 - 0
internal/logic/sop_node/delete_sop_node_logic.go

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

+ 73 - 0
internal/logic/sop_node/get_sop_node_by_id_logic.go

@@ -0,0 +1,73 @@
+package sop_node
+
+import (
+	"context"
+
+	"wechat-api/internal/svc"
+	"wechat-api/internal/types"
+	"wechat-api/internal/utils/dberrorhandler"
+
+	"github.com/suyuan32/simple-admin-common/msg/errormsg"
+
+	"github.com/suyuan32/simple-admin-common/utils/pointy"
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type GetSopNodeByIdLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewGetSopNodeByIdLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetSopNodeByIdLogic {
+	return &GetSopNodeByIdLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+func (l *GetSopNodeByIdLogic) GetSopNodeById(req *types.IDReq) (*types.SopNodeInfoResp, error) {
+	data, err := l.svcCtx.DB.SopNode.Get(l.ctx, req.Id)
+	if err != nil {
+		return nil, dberrorhandler.DefaultEntError(l.Logger, err, req)
+	}
+
+	conditionList := make([]types.Condition, len(data.ConditionList))
+	for i, condition := range data.ConditionList {
+		conditionList[i] = types.Condition{
+			Equal:       condition.Equal,
+			LabelIdList: condition.LabelIdList,
+		}
+	}
+
+	actionMessage := make([]types.Action, len(data.ActionMessage))
+	for i, condition := range data.ActionMessage {
+		actionMessage[i] = types.Action{
+			Type:    condition.Type,
+			Content: condition.Content,
+		}
+	}
+
+	return &types.SopNodeInfoResp{
+		BaseDataInfo: types.BaseDataInfo{
+			Code: 0,
+			Msg:  errormsg.Success,
+		},
+		Data: types.SopNodeInfo{
+			BaseIDInfo: types.BaseIDInfo{
+				Id:        &data.ID,
+				CreatedAt: pointy.GetPointer(data.CreatedAt.UnixMilli()),
+				UpdatedAt: pointy.GetPointer(data.UpdatedAt.UnixMilli()),
+			},
+			Status:        &data.Status,
+			StageId:       &data.StageID,
+			ParentId:      &data.ParentID,
+			Name:          &data.Name,
+			ConditionType: &data.ConditionType,
+			ConditionList: conditionList,
+			ActionMessage: actionMessage,
+			ActionLabel:   data.ActionLabel,
+		},
+	}, nil
+}

+ 83 - 0
internal/logic/sop_node/get_sop_node_list_logic.go

@@ -0,0 +1,83 @@
+package sop_node
+
+import (
+	"context"
+
+	"wechat-api/ent/predicate"
+	"wechat-api/ent/sopnode"
+	"wechat-api/internal/svc"
+	"wechat-api/internal/types"
+	"wechat-api/internal/utils/dberrorhandler"
+
+	"github.com/suyuan32/simple-admin-common/msg/errormsg"
+
+	"github.com/suyuan32/simple-admin-common/utils/pointy"
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type GetSopNodeListLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewGetSopNodeListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetSopNodeListLogic {
+	return &GetSopNodeListLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+func (l *GetSopNodeListLogic) GetSopNodeList(req *types.SopNodeListReq) (*types.SopNodeListResp, error) {
+	var predicates []predicate.SopNode
+	if req.Name != nil {
+		predicates = append(predicates, sopnode.NameContains(*req.Name))
+	}
+	data, err := l.svcCtx.DB.SopNode.Query().Where(predicates...).Page(l.ctx, req.Page, req.PageSize)
+
+	if err != nil {
+		return nil, dberrorhandler.DefaultEntError(l.Logger, err, req)
+	}
+
+	resp := &types.SopNodeListResp{}
+	resp.Msg = errormsg.Success
+	resp.Data.Total = data.PageDetails.Total
+
+	for _, v := range data.List {
+		conditionList := make([]types.Condition, len(v.ConditionList))
+		for i, condition := range v.ConditionList {
+			conditionList[i] = types.Condition{
+				Equal:       condition.Equal,
+				LabelIdList: condition.LabelIdList,
+			}
+		}
+
+		actionMessage := make([]types.Action, len(v.ActionMessage))
+		for i, condition := range v.ActionMessage {
+			actionMessage[i] = types.Action{
+				Type:    condition.Type,
+				Content: condition.Content,
+			}
+		}
+
+		resp.Data.Data = append(resp.Data.Data,
+			types.SopNodeInfo{
+				BaseIDInfo: types.BaseIDInfo{
+					Id:        &v.ID,
+					CreatedAt: pointy.GetPointer(v.CreatedAt.UnixMilli()),
+					UpdatedAt: pointy.GetPointer(v.UpdatedAt.UnixMilli()),
+				},
+				Status:        &v.Status,
+				StageId:       &v.StageID,
+				ParentId:      &v.ParentID,
+				Name:          &v.Name,
+				ConditionType: &v.ConditionType,
+				ConditionList: conditionList,
+				ActionMessage: actionMessage,
+				ActionLabel:   v.ActionLabel,
+			})
+	}
+
+	return resp, nil
+}

+ 62 - 0
internal/logic/sop_node/update_sop_node_logic.go

@@ -0,0 +1,62 @@
+package sop_node
+
+import (
+	"context"
+	"wechat-api/ent/custom_types"
+
+	"wechat-api/internal/svc"
+	"wechat-api/internal/types"
+	"wechat-api/internal/utils/dberrorhandler"
+
+	"github.com/suyuan32/simple-admin-common/msg/errormsg"
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type UpdateSopNodeLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewUpdateSopNodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateSopNodeLogic {
+	return &UpdateSopNodeLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+func (l *UpdateSopNodeLogic) UpdateSopNode(req *types.SopNodeInfo) (*types.BaseMsgResp, error) {
+	conditionList := make([]custom_types.Condition, len(req.ConditionList))
+	for i, condition := range req.ConditionList {
+		conditionList[i] = custom_types.Condition{
+			Equal:       condition.Equal,
+			LabelIdList: condition.LabelIdList,
+		}
+	}
+
+	actionMessage := make([]custom_types.Action, len(req.ActionMessage))
+	for i, condition := range req.ActionMessage {
+		actionMessage[i] = custom_types.Action{
+			Type:    condition.Type,
+			Content: condition.Content,
+		}
+	}
+
+	err := l.svcCtx.DB.SopNode.UpdateOneID(*req.Id).
+		SetNotNilStatus(req.Status).
+		SetNotNilStageID(req.StageId).
+		SetNotNilParentID(req.ParentId).
+		SetNotNilName(req.Name).
+		SetNotNilConditionType(req.ConditionType).
+		SetNotNilConditionList(conditionList).
+		SetNotNilActionMessage(actionMessage).
+		SetNotNilActionLabel(req.ActionLabel).
+		Exec(l.ctx)
+
+	if err != nil {
+		return nil, dberrorhandler.DefaultEntError(l.Logger, err, req)
+	}
+
+	return &types.BaseMsgResp{Msg: errormsg.UpdateSuccess}, nil
+}

+ 64 - 0
internal/logic/sop_stage/create_sop_stage_logic.go

@@ -0,0 +1,64 @@
+package sop_stage
+
+import (
+	"context"
+	"wechat-api/ent/custom_types"
+
+	"wechat-api/internal/svc"
+	"wechat-api/internal/types"
+	"wechat-api/internal/utils/dberrorhandler"
+
+	"github.com/suyuan32/simple-admin-common/msg/errormsg"
+
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type CreateSopStageLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewCreateSopStageLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateSopStageLogic {
+	return &CreateSopStageLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+func (l *CreateSopStageLogic) CreateSopStage(req *types.SopStageInfo) (*types.BaseMsgResp, error) {
+	conditionList := make([]custom_types.Condition, len(req.ConditionList))
+	for i, condition := range req.ConditionList {
+		conditionList[i] = custom_types.Condition{
+			Equal:       condition.Equal,
+			LabelIdList: condition.LabelIdList,
+		}
+	}
+
+	actionMessage := make([]custom_types.Action, len(req.ActionMessage))
+	for i, condition := range req.ActionMessage {
+		actionMessage[i] = custom_types.Action{
+			Type:    condition.Type,
+			Content: condition.Content,
+		}
+	}
+
+	_, err := l.svcCtx.DB.SopStage.Create().
+		SetNotNilStatus(req.Status).
+		SetNotNilTaskID(req.TaskId).
+		SetNotNilName(req.Name).
+		SetNotNilConditionType(req.ConditionType).
+		SetNotNilConditionOperator(req.ConditionOperator).
+		SetNotNilConditionList(conditionList).
+		SetNotNilActionMessage(actionMessage).
+		SetNotNilActionLabel(req.ActionLabel).
+		SetNotNilIndexSort(req.IndexSort).
+		Save(l.ctx)
+
+	if err != nil {
+		return nil, dberrorhandler.DefaultEntError(l.Logger, err, req)
+	}
+
+	return &types.BaseMsgResp{Msg: errormsg.CreateSuccess}, nil
+}

+ 37 - 0
internal/logic/sop_stage/delete_sop_stage_logic.go

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

+ 74 - 0
internal/logic/sop_stage/get_sop_stage_by_id_logic.go

@@ -0,0 +1,74 @@
+package sop_stage
+
+import (
+	"context"
+
+	"wechat-api/internal/svc"
+	"wechat-api/internal/types"
+	"wechat-api/internal/utils/dberrorhandler"
+
+	"github.com/suyuan32/simple-admin-common/msg/errormsg"
+
+	"github.com/suyuan32/simple-admin-common/utils/pointy"
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type GetSopStageByIdLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewGetSopStageByIdLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetSopStageByIdLogic {
+	return &GetSopStageByIdLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+func (l *GetSopStageByIdLogic) GetSopStageById(req *types.IDReq) (*types.SopStageInfoResp, error) {
+	data, err := l.svcCtx.DB.SopStage.Get(l.ctx, req.Id)
+	if err != nil {
+		return nil, dberrorhandler.DefaultEntError(l.Logger, err, req)
+	}
+
+	conditionList := make([]types.Condition, len(data.ConditionList))
+	for i, condition := range data.ConditionList {
+		conditionList[i] = types.Condition{
+			Equal:       condition.Equal,
+			LabelIdList: condition.LabelIdList,
+		}
+	}
+
+	actionMessage := make([]types.Action, len(data.ActionMessage))
+	for i, condition := range data.ActionMessage {
+		actionMessage[i] = types.Action{
+			Type:    condition.Type,
+			Content: condition.Content,
+		}
+	}
+
+	return &types.SopStageInfoResp{
+		BaseDataInfo: types.BaseDataInfo{
+			Code: 0,
+			Msg:  errormsg.Success,
+		},
+		Data: types.SopStageInfo{
+			BaseIDInfo: types.BaseIDInfo{
+				Id:        &data.ID,
+				CreatedAt: pointy.GetPointer(data.CreatedAt.UnixMilli()),
+				UpdatedAt: pointy.GetPointer(data.UpdatedAt.UnixMilli()),
+			},
+			Status:            &data.Status,
+			TaskId:            &data.TaskID,
+			Name:              &data.Name,
+			ConditionType:     &data.ConditionType,
+			ConditionOperator: &data.ConditionOperator,
+			ConditionList:     conditionList,
+			ActionMessage:     actionMessage,
+			ActionLabel:       data.ActionLabel,
+			IndexSort:         &data.IndexSort,
+		},
+	}, nil
+}

+ 84 - 0
internal/logic/sop_stage/get_sop_stage_list_logic.go

@@ -0,0 +1,84 @@
+package sop_stage
+
+import (
+	"context"
+
+	"wechat-api/ent/predicate"
+	"wechat-api/ent/sopstage"
+	"wechat-api/internal/svc"
+	"wechat-api/internal/types"
+	"wechat-api/internal/utils/dberrorhandler"
+
+	"github.com/suyuan32/simple-admin-common/msg/errormsg"
+
+	"github.com/suyuan32/simple-admin-common/utils/pointy"
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type GetSopStageListLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewGetSopStageListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetSopStageListLogic {
+	return &GetSopStageListLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+func (l *GetSopStageListLogic) GetSopStageList(req *types.SopStageListReq) (*types.SopStageListResp, error) {
+	var predicates []predicate.SopStage
+	if req.Name != nil {
+		predicates = append(predicates, sopstage.NameContains(*req.Name))
+	}
+	data, err := l.svcCtx.DB.SopStage.Query().Where(predicates...).Page(l.ctx, req.Page, req.PageSize)
+
+	if err != nil {
+		return nil, dberrorhandler.DefaultEntError(l.Logger, err, req)
+	}
+
+	resp := &types.SopStageListResp{}
+	resp.Msg = errormsg.Success
+	resp.Data.Total = data.PageDetails.Total
+
+	for _, v := range data.List {
+		conditionList := make([]types.Condition, len(v.ConditionList))
+		for i, condition := range v.ConditionList {
+			conditionList[i] = types.Condition{
+				Equal:       condition.Equal,
+				LabelIdList: condition.LabelIdList,
+			}
+		}
+
+		actionMessage := make([]types.Action, len(v.ActionMessage))
+		for i, condition := range v.ActionMessage {
+			actionMessage[i] = types.Action{
+				Type:    condition.Type,
+				Content: condition.Content,
+			}
+		}
+
+		resp.Data.Data = append(resp.Data.Data,
+			types.SopStageInfo{
+				BaseIDInfo: types.BaseIDInfo{
+					Id:        &v.ID,
+					CreatedAt: pointy.GetPointer(v.CreatedAt.UnixMilli()),
+					UpdatedAt: pointy.GetPointer(v.UpdatedAt.UnixMilli()),
+				},
+				Status:            &v.Status,
+				TaskId:            &v.TaskID,
+				Name:              &v.Name,
+				ConditionType:     &v.ConditionType,
+				ConditionOperator: &v.ConditionOperator,
+				ConditionList:     conditionList,
+				ActionMessage:     actionMessage,
+				ActionLabel:       v.ActionLabel,
+				IndexSort:         &v.IndexSort,
+			})
+	}
+
+	return resp, nil
+}

+ 63 - 0
internal/logic/sop_stage/update_sop_stage_logic.go

@@ -0,0 +1,63 @@
+package sop_stage
+
+import (
+	"context"
+	"wechat-api/ent/custom_types"
+
+	"wechat-api/internal/svc"
+	"wechat-api/internal/types"
+	"wechat-api/internal/utils/dberrorhandler"
+
+	"github.com/suyuan32/simple-admin-common/msg/errormsg"
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type UpdateSopStageLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewUpdateSopStageLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateSopStageLogic {
+	return &UpdateSopStageLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+func (l *UpdateSopStageLogic) UpdateSopStage(req *types.SopStageInfo) (*types.BaseMsgResp, error) {
+	conditionList := make([]custom_types.Condition, len(req.ConditionList))
+	for i, condition := range req.ConditionList {
+		conditionList[i] = custom_types.Condition{
+			Equal:       condition.Equal,
+			LabelIdList: condition.LabelIdList,
+		}
+	}
+
+	actionMessage := make([]custom_types.Action, len(req.ActionMessage))
+	for i, condition := range req.ActionMessage {
+		actionMessage[i] = custom_types.Action{
+			Type:    condition.Type,
+			Content: condition.Content,
+		}
+	}
+
+	err := l.svcCtx.DB.SopStage.UpdateOneID(*req.Id).
+		SetNotNilStatus(req.Status).
+		SetNotNilTaskID(req.TaskId).
+		SetNotNilName(req.Name).
+		SetNotNilConditionType(req.ConditionType).
+		SetNotNilConditionOperator(req.ConditionOperator).
+		SetNotNilConditionList(conditionList).
+		SetNotNilActionMessage(actionMessage).
+		SetNotNilActionLabel(req.ActionLabel).
+		SetNotNilIndexSort(req.IndexSort).
+		Exec(l.ctx)
+
+	if err != nil {
+		return nil, dberrorhandler.DefaultEntError(l.Logger, err, req)
+	}
+
+	return &types.BaseMsgResp{Msg: errormsg.UpdateSuccess}, nil
+}

+ 47 - 0
internal/logic/sop_task/create_sop_task_logic.go

@@ -0,0 +1,47 @@
+package sop_task
+
+import (
+	"context"
+	"fmt"
+	"wechat-api/internal/svc"
+	"wechat-api/internal/types"
+	"wechat-api/internal/utils/dberrorhandler"
+
+	"github.com/suyuan32/simple-admin-common/msg/errormsg"
+	"github.com/suyuan32/simple-admin-common/utils/pointy"
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type CreateSopTaskLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewCreateSopTaskLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateSopTaskLogic {
+	return &CreateSopTaskLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+func (l *CreateSopTaskLogic) CreateSopTask(req *types.SopTaskInfo) (*types.BaseMsgResp, error) {
+	currentUserID, _ := l.ctx.Value("userId").(string)
+	fmt.Printf("ctx: %s\n\n", currentUserID)
+	_, err := l.svcCtx.DB.SopTask.Create().
+		SetNotNilStatus(req.Status).
+		SetNotNilName(req.Name).
+		SetNotNilBotWxidList(req.BotWxidList).
+		SetNotNilType(req.Type).
+		SetNotNilPlanStartTime(pointy.GetTimeMilliPointer(req.PlanStartTime)).
+		SetNotNilPlanEndTime(pointy.GetTimeMilliPointer(req.PlanEndTime)).
+		SetNotNilCreatorID(&currentUserID).
+		Save(l.ctx)
+
+	if err != nil {
+		return nil, dberrorhandler.DefaultEntError(l.Logger, err, req)
+	}
+
+	return &types.BaseMsgResp{Msg: errormsg.CreateSuccess}, nil
+}

+ 37 - 0
internal/logic/sop_task/delete_sop_task_logic.go

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

+ 60 - 0
internal/logic/sop_task/get_sop_task_by_id_logic.go

@@ -0,0 +1,60 @@
+package sop_task
+
+import (
+	"context"
+	"wechat-api/ent/soptask"
+
+	"wechat-api/internal/svc"
+	"wechat-api/internal/types"
+	"wechat-api/internal/utils/dberrorhandler"
+
+	"github.com/suyuan32/simple-admin-common/msg/errormsg"
+
+	"github.com/suyuan32/simple-admin-common/utils/pointy"
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type GetSopTaskByIdLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewGetSopTaskByIdLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetSopTaskByIdLogic {
+	return &GetSopTaskByIdLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+func (l *GetSopTaskByIdLogic) GetSopTaskById(req *types.IDReq) (*types.SopTaskInfoResp, error) {
+	data, err := l.svcCtx.DB.SopTask.Query().
+		Where(soptask.ID(req.Id)).
+		WithTaskStages().
+		Only(l.ctx)
+	if err != nil {
+		return nil, dberrorhandler.DefaultEntError(l.Logger, err, req)
+	}
+
+	return &types.SopTaskInfoResp{
+		BaseDataInfo: types.BaseDataInfo{
+			Code: 0,
+			Msg:  errormsg.Success,
+		},
+		Data: types.SopTaskInfo{
+			BaseIDInfo: types.BaseIDInfo{
+				Id:        &data.ID,
+				CreatedAt: pointy.GetPointer(data.CreatedAt.UnixMilli()),
+				UpdatedAt: pointy.GetPointer(data.UpdatedAt.UnixMilli()),
+			},
+			Status:        &data.Status,
+			Name:          &data.Name,
+			BotWxidList:   data.BotWxidList,
+			Type:          &data.Type,
+			PlanStartTime: pointy.GetUnixMilliPointer(data.PlanStartTime.UnixMilli()),
+			PlanEndTime:   pointy.GetUnixMilliPointer(data.PlanEndTime.UnixMilli()),
+			CreatorId:     &data.CreatorID,
+		},
+	}, nil
+}

+ 69 - 0
internal/logic/sop_task/get_sop_task_list_logic.go

@@ -0,0 +1,69 @@
+package sop_task
+
+import (
+	"context"
+
+	"wechat-api/ent/predicate"
+	"wechat-api/ent/soptask"
+	"wechat-api/internal/svc"
+	"wechat-api/internal/types"
+	"wechat-api/internal/utils/dberrorhandler"
+
+	"github.com/suyuan32/simple-admin-common/msg/errormsg"
+
+	"github.com/suyuan32/simple-admin-common/utils/pointy"
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type GetSopTaskListLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewGetSopTaskListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetSopTaskListLogic {
+	return &GetSopTaskListLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+func (l *GetSopTaskListLogic) GetSopTaskList(req *types.SopTaskListReq) (*types.SopTaskListResp, error) {
+	var predicates []predicate.SopTask
+	if req.Name != nil {
+		predicates = append(predicates, soptask.NameContains(*req.Name))
+	}
+	if req.CreatorId != nil {
+		predicates = append(predicates, soptask.CreatorIDContains(*req.CreatorId))
+	}
+	data, err := l.svcCtx.DB.SopTask.Query().Where(predicates...).Page(l.ctx, req.Page, req.PageSize)
+
+	if err != nil {
+		return nil, dberrorhandler.DefaultEntError(l.Logger, err, req)
+	}
+
+	resp := &types.SopTaskListResp{}
+	resp.Msg = errormsg.Success
+	resp.Data.Total = data.PageDetails.Total
+
+	for _, v := range data.List {
+		resp.Data.Data = append(resp.Data.Data,
+			types.SopTaskInfo{
+				BaseIDInfo: types.BaseIDInfo{
+					Id:        &v.ID,
+					CreatedAt: pointy.GetPointer(v.CreatedAt.UnixMilli()),
+					UpdatedAt: pointy.GetPointer(v.UpdatedAt.UnixMilli()),
+				},
+				Status:        &v.Status,
+				Name:          &v.Name,
+				BotWxidList:   v.BotWxidList,
+				Type:          &v.Type,
+				PlanStartTime: pointy.GetUnixMilliPointer(v.PlanStartTime.UnixMilli()),
+				PlanEndTime:   pointy.GetUnixMilliPointer(v.PlanEndTime.UnixMilli()),
+				CreatorId:     &v.CreatorID,
+			})
+	}
+
+	return resp, nil
+}

+ 46 - 0
internal/logic/sop_task/update_sop_task_logic.go

@@ -0,0 +1,46 @@
+package sop_task
+
+import (
+	"context"
+
+	"wechat-api/internal/svc"
+	"wechat-api/internal/types"
+	"wechat-api/internal/utils/dberrorhandler"
+
+
+	"github.com/suyuan32/simple-admin-common/msg/errormsg"
+	"github.com/suyuan32/simple-admin-common/utils/pointy"
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type UpdateSopTaskLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewUpdateSopTaskLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateSopTaskLogic {
+	return &UpdateSopTaskLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+func (l *UpdateSopTaskLogic) UpdateSopTask(req *types.SopTaskInfo) (*types.BaseMsgResp, error) {
+    err := l.svcCtx.DB.SopTask.UpdateOneID(*req.Id).
+			SetNotNilStatus(req.Status).
+			SetNotNilName(req.Name).
+			SetNotNilBotWxidList(req.BotWxidList).
+			SetNotNilType(req.Type).
+			SetNotNilPlanStartTime(pointy.GetTimeMilliPointer(req.PlanStartTime)).
+			SetNotNilPlanEndTime(pointy.GetTimeMilliPointer(req.PlanEndTime)).
+			SetNotNilCreatorID(req.CreatorId).
+			Exec(l.ctx)
+
+    if err != nil {
+		return nil, dberrorhandler.DefaultEntError(l.Logger, err, req)
+	}
+
+    return &types.BaseMsgResp{Msg: errormsg.UpdateSuccess}, nil
+}

+ 240 - 0
internal/types/types.go

@@ -220,6 +220,16 @@ type BaseUUIDInfo struct {
 	UpdatedAt *int64 `json:"updatedAt,optional"`
 }
 
+type Condition struct {
+	Equal       int   `json:"equal"`
+	LabelIdList []int `json:"labelIdList"`
+}
+
+type Action struct {
+	Type    int    `json:"type"`
+	Content string `json:"content"`
+}
+
 // The response data of server information | Server信息
 // swagger:model ServerInfo
 type ServerInfo struct {
@@ -662,3 +672,233 @@ type LabelSelectListInfo struct {
 	// value
 	Value *uint64 `json:"value,optional"`
 }
+
+// The response data of sop task information | SopTask信息
+// swagger:model SopTaskInfo
+type SopTaskInfo struct {
+	BaseIDInfo
+	// Status 1: normal 2: ban | 状态 1 正常 2 禁用
+	Status *uint8 `json:"status,optional"`
+	// SOP 任务名称
+	Name *string `json:"name,optional"`
+	// 机器人微信 id 列表
+	BotWxidList []string `json:"botWxidList,optional"`
+	// 标签类型:1好友,2群组,3企业微信联系人
+	Type *int `json:"type,optional"`
+	// 任务计划开始时间
+	PlanStartTime *int64 `json:"planStartTime,optional"`
+	// 任务计划结束时间
+	PlanEndTime *int64 `json:"planEndTime,optional"`
+	// 创建者 id
+	CreatorId *string `json:"creatorId,optional"`
+}
+
+// The response data of sop task list | SopTask列表数据
+// swagger:model SopTaskListResp
+type SopTaskListResp struct {
+	BaseDataInfo
+	// SopTask list data | SopTask列表数据
+	Data SopTaskListInfo `json:"data"`
+}
+
+// SopTask list data | SopTask列表数据
+// swagger:model SopTaskListInfo
+type SopTaskListInfo struct {
+	BaseListInfo
+	// The API list data | SopTask列表数据
+	Data []SopTaskInfo `json:"data"`
+}
+
+// Get sop task list request params | SopTask列表请求参数
+// swagger:model SopTaskListReq
+type SopTaskListReq struct {
+	PageInfo
+	// SOP 任务名称
+	Name *string `json:"name,optional"`
+	// 创建者 id
+	CreatorId *string `json:"creatorId,optional"`
+}
+
+// SopTask information response | SopTask信息返回体
+// swagger:model SopTaskInfoResp
+type SopTaskInfoResp struct {
+	BaseDataInfo
+	// SopTask information | SopTask数据
+	Data SopTaskInfo `json:"data"`
+}
+
+// The response data of sop stage information | SopStage信息
+// swagger:model SopStageInfo
+type SopStageInfo struct {
+	BaseIDInfo
+	// Status 1: normal 2: ban | 状态 1 正常 2 禁用
+	Status *uint8 `json:"status,optional"`
+	// SOP 任务 ID
+	TaskId *uint64 `json:"taskId,optional"`
+	// 阶段名称
+	Name *string `json:"name,optional"`
+	// 客群筛选条件类型  1 按标签筛选 2 按客户基本信息筛选
+	ConditionType *int `json:"conditionType,optional"`
+	// 筛选条件关系  1 满足所有条件(and) 2 满足任意条件(or)
+	ConditionOperator *int `json:"conditionOperator,optional"`
+	// 筛选条件列表
+	ConditionList []Condition `json:"conditionList,optional"`
+	// 命中后发送的消息内容
+	ActionMessage []Action `json:"actionMessage,optional"`
+	// 命中后需要打的标签
+	ActionLabel []int `json:"actionLabel,optional"`
+	// 阶段顺序
+	IndexSort *int `json:"indexSort,optional"`
+}
+
+// The response data of sop stage list | SopStage列表数据
+// swagger:model SopStageListResp
+type SopStageListResp struct {
+	BaseDataInfo
+	// SopStage list data | SopStage列表数据
+	Data SopStageListInfo `json:"data"`
+}
+
+// SopStage list data | SopStage列表数据
+// swagger:model SopStageListInfo
+type SopStageListInfo struct {
+	BaseListInfo
+	// The API list data | SopStage列表数据
+	Data []SopStageInfo `json:"data"`
+}
+
+// Get sop stage list request params | SopStage列表请求参数
+// swagger:model SopStageListReq
+type SopStageListReq struct {
+	PageInfo
+	// 阶段名称
+	Name *string `json:"name,optional"`
+}
+
+// SopStage information response | SopStage信息返回体
+// swagger:model SopStageInfoResp
+type SopStageInfoResp struct {
+	BaseDataInfo
+	// SopStage information | SopStage数据
+	Data SopStageInfo `json:"data"`
+}
+
+// The response data of sop node information | SopNode信息
+// swagger:model SopNodeInfo
+type SopNodeInfo struct {
+	BaseIDInfo
+	// Status 1: normal 2: ban | 状态 1 正常 2 禁用
+	Status *uint8 `json:"status,optional"`
+	// 阶段 ID
+	StageId *uint64 `json:"stageId,optional"`
+	// 父节点 ID
+	ParentId *int `json:"parentId,optional"`
+	// 节点名称
+	Name *string `json:"name,optional"`
+	// 触发条件类型 1 客户回复后触发 2 超时后触发
+	ConditionType *int `json:"conditionType,optional"`
+	// 触发语义列表 当为空时则代表用户回复任意内容后触发
+	ConditionList []Condition `json:"conditionList,optional"`
+	// 命中后发送的消息内容
+	ActionMessage []Action `json:"actionMessage,optional"`
+	// 命中后需要打的标签
+	ActionLabel []int `json:"actionLabel,optional"`
+}
+
+// The response data of sop node list | SopNode列表数据
+// swagger:model SopNodeListResp
+type SopNodeListResp struct {
+	BaseDataInfo
+	// SopNode list data | SopNode列表数据
+	Data SopNodeListInfo `json:"data"`
+}
+
+// SopNode list data | SopNode列表数据
+// swagger:model SopNodeListInfo
+type SopNodeListInfo struct {
+	BaseListInfo
+	// The API list data | SopNode列表数据
+	Data []SopNodeInfo `json:"data"`
+}
+
+// Get sop node list request params | SopNode列表请求参数
+// swagger:model SopNodeListReq
+type SopNodeListReq struct {
+	PageInfo
+	// 节点名称
+	Name *string `json:"name,optional"`
+}
+
+// SopNode information response | SopNode信息返回体
+// swagger:model SopNodeInfoResp
+type SopNodeInfoResp struct {
+	BaseDataInfo
+	// SopNode information | SopNode数据
+	Data SopNodeInfo `json:"data"`
+}
+
+// The response data of message records information | MessageRecords信息
+// swagger:model MessageRecordsInfo
+type MessageRecordsInfo struct {
+	BaseIDInfo
+	// Status 1: normal 2: ban | 状态 1 正常 2 禁用
+	Status *uint8 `json:"status,optional"`
+	// 机器人微信 id
+	BotWxid *string `json:"botWxid,optional"`
+	// 联系人 id
+	ContactId *int `json:"contactId,optional"`
+	// 类型:1好友,2群组,3企业微信联系人
+	ContactType *int `json:"contactType,optional"`
+	// 接收方微信 id
+	ContactWxid *string `json:"contactWxid,optional"`
+	// 内容类型 1 文本 2 文件
+	ContentType *int `json:"contentType,optional"`
+	// 发送内容
+	Content *string `json:"content,optional"`
+	// 异常原因
+	ErrorDetail *string `json:"errorDetail,optional"`
+	// 发送时间
+	SendTime *int64 `json:"sendTime,optional"`
+	// 源类型 1 点发 2 群发 3 SOP
+	SourceType *int `json:"sourceType,optional"`
+	// 源 ID
+	SourceId *int `json:"sourceId,optional"`
+	// 次源 ID
+	SubSourceId *int `json:"subSourceId,optional"`
+}
+
+// The response data of message records list | MessageRecords列表数据
+// swagger:model MessageRecordsListResp
+type MessageRecordsListResp struct {
+	BaseDataInfo
+	// MessageRecords list data | MessageRecords列表数据
+	Data MessageRecordsListInfo `json:"data"`
+}
+
+// MessageRecords list data | MessageRecords列表数据
+// swagger:model MessageRecordsListInfo
+type MessageRecordsListInfo struct {
+	BaseListInfo
+	// The API list data | MessageRecords列表数据
+	Data []MessageRecordsInfo `json:"data"`
+}
+
+// Get message records list request params | MessageRecords列表请求参数
+// swagger:model MessageRecordsListReq
+type MessageRecordsListReq struct {
+	PageInfo
+	// 机器人微信 id
+	BotWxid *string `json:"botWxid,optional"`
+	// 接收方微信 id
+	ContactWxid *string `json:"contactWxid,optional"`
+	// 发送内容
+	Content *string `json:"content,optional"`
+}
+
+// MessageRecords information response | MessageRecords信息返回体
+// swagger:model MessageRecordsInfoResp
+type MessageRecordsInfoResp struct {
+	BaseDataInfo
+	// MessageRecords information | MessageRecords数据
+	Data MessageRecordsInfo `json:"data"`
+}

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно