feat: add Z12/Z13 bind APIs, stock import, and sync docs
Enable auto default sync channels on agent activate, bind-code/phone confirm flows, publish ALTER, and align admin/yuheng docs with the production bind path.
This commit is contained in:
92
platform/internal/schema/alter.go
Normal file
92
platform/internal/schema/alter.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package schema
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"aijianzhan/platform/internal/blueprint"
|
||||
)
|
||||
|
||||
// BuildPostgresAlterDDL 对比蓝图与已有表,生成 ADD COLUMN IF NOT EXISTS(Z11a)。
|
||||
// db 可为 nil:则只返回空(调用方应先 CREATE TABLE)。
|
||||
func BuildPostgresAlterDDL(ctx context.Context, db *sql.DB, bp *blueprint.Blueprint) ([]string, error) {
|
||||
if bp == nil || bp.Storage.SchemaName == "" {
|
||||
return nil, fmt.Errorf("schema_name empty")
|
||||
}
|
||||
if db == nil {
|
||||
return nil, nil
|
||||
}
|
||||
schemaName := bp.Storage.SchemaName
|
||||
var stmts []string
|
||||
for _, e := range bp.Entities {
|
||||
existing, err := listTableColumns(ctx, db, schemaName, e.Table)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if existing == nil {
|
||||
// 表尚不存在:由 CREATE TABLE IF NOT EXISTS 处理
|
||||
continue
|
||||
}
|
||||
for _, f := range e.Fields {
|
||||
if _, ok := existing[f.Name]; ok {
|
||||
continue
|
||||
}
|
||||
sqlType, err := mapType(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 存量表补列一律可空,避免非空约束导致迁库失败
|
||||
stmts = append(stmts, fmt.Sprintf(
|
||||
`ALTER TABLE %s.%s ADD COLUMN IF NOT EXISTS %s %s`,
|
||||
quoteIdent(schemaName), quoteIdent(e.Table), quoteIdent(f.Name), sqlType,
|
||||
))
|
||||
}
|
||||
}
|
||||
return stmts, nil
|
||||
}
|
||||
|
||||
func listTableColumns(ctx context.Context, db *sql.DB, schemaName, table string) (map[string]struct{}, error) {
|
||||
const q = `
|
||||
SELECT column_name FROM information_schema.columns
|
||||
WHERE table_schema = $1 AND table_name = $2`
|
||||
rows, err := db.QueryContext(ctx, q, schemaName, table)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
out := map[string]struct{}{}
|
||||
found := false
|
||||
for rows.Next() {
|
||||
found = true
|
||||
var name string
|
||||
if err := rows.Scan(&name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out[name] = struct{}{}
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !found {
|
||||
return nil, nil
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// MergeDDL 将 alter 语句追加到 create ddl 之后。
|
||||
func MergeDDL(create, alter []string) []string {
|
||||
if len(alter) == 0 {
|
||||
return create
|
||||
}
|
||||
out := make([]string, 0, len(create)+len(alter))
|
||||
out = append(out, create...)
|
||||
out = append(out, alter...)
|
||||
return out
|
||||
}
|
||||
|
||||
// Quote for tests
|
||||
func normalizeSchemaTable(s string) string {
|
||||
return strings.TrimSpace(s)
|
||||
}
|
||||
Reference in New Issue
Block a user