diff --git a/README.md b/README.md
index dcdeb6cb..f6155a45 100644
--- a/README.md
+++ b/README.md
@@ -243,6 +243,10 @@ docker compose up -d
MAX API 采用分层架构:应用、SDK 或 Agent 请求经统一入口进入,依次经过路由、中间件、控制器和业务服务层,最终由中继层适配到对应上游供应商;数据层和缓存层为模型治理、Agent 令牌治理、计费、日志、审计和任务状态提供持久化与加速能力。
+
+
+以下简图展示核心请求链路:
+
```mermaid
flowchart LR
Client[客户端 / SDK / Agent] --> Router[Router
路由]
diff --git a/README.zh_CN.md b/README.zh_CN.md
index e5aa6558..1b9e16d6 100644
--- a/README.zh_CN.md
+++ b/README.zh_CN.md
@@ -241,6 +241,10 @@ docker compose up -d
MAX API 采用分层架构:应用、SDK 或 Agent 请求经统一入口进入,依次经过路由、中间件、控制器和业务服务层,最终由中继层适配到对应上游供应商;数据层和缓存层为模型治理、Agent 令牌治理、计费、日志、审计和任务状态提供持久化与加速能力。
+
+
+以下简图展示核心请求链路:
+
```mermaid
flowchart LR
Client[客户端 / SDK / Agent] --> Router[Router
路由]
diff --git "a/docs/images/MAX-API\346\236\266\346\236\204\345\233\276.png" "b/docs/images/MAX-API\346\236\266\346\236\204\345\233\276.png"
new file mode 100644
index 00000000..56dfd547
Binary files /dev/null and "b/docs/images/MAX-API\346\236\266\346\236\204\345\233\276.png" differ
diff --git a/model/channel_update_fields_test.go b/model/channel_update_fields_test.go
index df54753d..8d3de501 100644
--- a/model/channel_update_fields_test.go
+++ b/model/channel_update_fields_test.go
@@ -77,6 +77,63 @@ func installFailingAbilityDeleteTrigger(t *testing.T, db *gorm.DB) {
t.Cleanup(func() { _ = db.Exec("DROP TRIGGER IF EXISTS fail_ability_delete").Error })
}
+func populatedChannelForEditableUpdateMap() *Channel {
+ stringPtr := func(value string) *string { return &value }
+ priority := int64(1)
+ weight := uint(2)
+ autoBan := 1
+ return &Channel{
+ Type: 3,
+ Key: "key",
+ OpenAIOrganization: stringPtr("org"),
+ TestModel: stringPtr("gpt-test"),
+ Status: common.ChannelStatusEnabled,
+ Name: "schema-check-channel",
+ Weight: &weight,
+ BaseURL: stringPtr("https://example.com"),
+ Other: "{}",
+ Models: "gpt-test",
+ Group: "default",
+ ModelMapping: stringPtr("{}"),
+ StatusCodeMapping: stringPtr("{}"),
+ Priority: &priority,
+ AutoBan: &autoBan,
+ Tag: stringPtr("tag"),
+ Setting: stringPtr("{}"),
+ ParamOverride: stringPtr("{}"),
+ HeaderOverride: stringPtr("{}"),
+ Remark: stringPtr("remark"),
+ ChannelInfo: ChannelInfo{
+ IsMultiKey: true,
+ MultiKeySize: 1,
+ MultiKeyPollingIndex: 0,
+ },
+ OtherSettings: "{}",
+ }
+}
+
+func TestEditableUpdateMapUsesOnlyChannelSchemaColumns(t *testing.T) {
+ db, _ := setupChannelUpdateFieldsTestDB(t)
+
+ stmt := &gorm.Statement{DB: db}
+ require.NoError(t, stmt.Parse(&Channel{}))
+
+ schemaColumns := make(map[string]struct{}, len(stmt.Schema.DBNames))
+ for _, column := range stmt.Schema.DBNames {
+ schemaColumns[column] = struct{}{}
+ }
+
+ channel := populatedChannelForEditableUpdateMap()
+ for _, field := range channelEditableUpdateFields {
+ updates := channel.editableUpdateMap([]string{field})
+ require.NotEmpty(t, updates, "editable field %q is not handled by editableUpdateMap or the test fixture lacks its value", field)
+ for column := range updates {
+ require.Contains(t, schemaColumns, column, "editable field %q maps to unknown channel schema column %q", field, column)
+ require.True(t, db.Migrator().HasColumn(&Channel{}, column), "editable field %q maps to missing database column %q", field, column)
+ }
+ }
+}
+
func TestUpdateFieldsSkipsAbilityRebuildForChannelInfoOnly(t *testing.T) {
db, channel := setupChannelUpdateFieldsTestDB(t)
installFailingAbilityDeleteTrigger(t, db)