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:
@@ -3,6 +3,7 @@ package applogic
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/csv"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
@@ -13,6 +14,7 @@ import (
|
||||
"aijianzhan/platform/internal/meta"
|
||||
"aijianzhan/platform/internal/types"
|
||||
|
||||
"github.com/lib/pq"
|
||||
"github.com/xuri/excelize/v2"
|
||||
)
|
||||
|
||||
@@ -24,8 +26,10 @@ func (l *CrudLogic) ImportRows(slug, resource, filename string, r io.Reader) (*t
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Z9f:读路径内存兜底(与 Z9e 扫库双保险)
|
||||
ensureImportOpInMemory(ref)
|
||||
if !hasOp(ref, "import") {
|
||||
return nil, fmt.Errorf("operation import not allowed")
|
||||
return nil, fmt.Errorf("蓝图未开启 import(operations 缺 import)。请管理员执行「一键开启全部业务表导入」或再发布模块;与账号权限 row.import 无关")
|
||||
}
|
||||
|
||||
ext := strings.ToLower(filepath.Ext(filename))
|
||||
@@ -86,7 +90,11 @@ func (l *CrudLogic) ImportRows(slug, resource, filename string, r io.Reader) (*t
|
||||
if _, err := l.svcCtx.CRUD.Create(l.ctx, ref, tenantID, userID, body); err != nil {
|
||||
resp.Skipped++
|
||||
if len(resp.Errors) < 100 {
|
||||
resp.Errors = append(resp.Errors, fmt.Sprintf("row %d: %v", rowNum+1, err))
|
||||
msg := err.Error()
|
||||
if isUndefinedColumnErr(err) {
|
||||
msg = fmt.Sprintf("%v(蓝图字段与库表不一致,请重新发布模块以自动补列,或缩小 Excel 表头至库已有列)", err)
|
||||
}
|
||||
resp.Errors = append(resp.Errors, fmt.Sprintf("row %d: %s", rowNum+1, msg))
|
||||
}
|
||||
continue
|
||||
}
|
||||
@@ -107,8 +115,9 @@ func (l *CrudLogic) ExportExcel(slug, resource string) ([]byte, string, error) {
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
ensureImportOpInMemory(ref)
|
||||
if !hasOp(ref, "export") {
|
||||
return nil, "", fmt.Errorf("operation export not allowed")
|
||||
return nil, "", fmt.Errorf("蓝图未开启 export(operations 缺 export)。请管理员执行「一键开启全部业务表导入」或再发布模块")
|
||||
}
|
||||
items, _, err := l.svcCtx.CRUD.List(l.ctx, ref, tenantID, 1, 5000, nil, "")
|
||||
if err != nil {
|
||||
@@ -147,8 +156,9 @@ func (l *CrudLogic) ExportCSV(slug, resource string) ([]byte, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ensureImportOpInMemory(ref)
|
||||
if !hasOp(ref, "export") {
|
||||
return nil, fmt.Errorf("operation export not allowed")
|
||||
return nil, fmt.Errorf("蓝图未开启 export(operations 缺 export)。请管理员执行「一键开启全部业务表导入」或再发布模块")
|
||||
}
|
||||
items, _, err := l.svcCtx.CRUD.List(l.ctx, ref, tenantID, 1, 5000, nil, "")
|
||||
if err != nil {
|
||||
@@ -237,6 +247,9 @@ func rowEmpty(rec []string) bool {
|
||||
}
|
||||
|
||||
func hasOp(ref *meta.ResourceRef, op string) bool {
|
||||
if ref == nil {
|
||||
return false
|
||||
}
|
||||
for _, o := range ref.Resource.Operations {
|
||||
if o == op {
|
||||
return true
|
||||
@@ -245,6 +258,37 @@ func hasOp(ref *meta.ResourceRef, op string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// ensureImportOpInMemory Z9f:对已加载蓝图做与 EnsureDefaultImportExport 相同的内存补齐,并刷新 ref.Resource。
|
||||
func ensureImportOpInMemory(ref *meta.ResourceRef) {
|
||||
if ref == nil || ref.App == nil || ref.App.Blueprint == nil {
|
||||
return
|
||||
}
|
||||
ref.App.Blueprint.EnsureDefaultImportExport()
|
||||
want := strings.TrimSpace(ref.Resource.Path)
|
||||
if len(want) > 0 && want[0] == '/' {
|
||||
want = want[1:]
|
||||
}
|
||||
for _, r := range ref.App.Blueprint.Apis.Resources {
|
||||
path := r.Path
|
||||
if len(path) > 0 && path[0] == '/' {
|
||||
path = path[1:]
|
||||
}
|
||||
if path == want {
|
||||
ref.Resource = r
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func isUndefinedColumnErr(err error) bool {
|
||||
var pqErr *pq.Error
|
||||
if errors.As(err, &pqErr) && pqErr.Code == "42703" {
|
||||
return true
|
||||
}
|
||||
msg := err.Error()
|
||||
return strings.Contains(msg, "42703") || strings.Contains(msg, "字段不存在") || strings.Contains(msg, "does not exist")
|
||||
}
|
||||
|
||||
func mapHeaderToField(ref *meta.ResourceRef, header string) string {
|
||||
h := strings.TrimSpace(header)
|
||||
if h == "" {
|
||||
|
||||
Reference in New Issue
Block a user