Skip to content

Commit d60b71f

Browse files
committed
feat:新增数组字段 --story=128495448 字段类型: 数组
1 parent cc45f93 commit d60b71f

10 files changed

+1812
-0
lines changed
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package metadata
2+
3+
import (
4+
"configcenter/src/common/util"
5+
"encoding/json"
6+
"fmt"
7+
"math"
8+
9+
"github.com/tidwall/gjson"
10+
"go.mongodb.org/mongo-driver/bson"
11+
)
12+
13+
type ArrayOption[T any] struct {
14+
Len int `bson:"len" json:"len" `
15+
Cap int `bson:"cap" json:"cap" `
16+
Option T `bson:"option" json:"option" `
17+
}
18+
19+
func (a *ArrayOption[T]) Valid() error {
20+
if a.Len < 0 || a.Len > a.Cap {
21+
return fmt.Errorf("invalid array option,len:%d cap:%d", a.Len, a.Cap)
22+
}
23+
return nil
24+
}
25+
26+
func ParseArrayOption[T any](option any) (ArrayOption[T], error) {
27+
if option == nil || option == "" {
28+
return ArrayOption[T]{Len: math.MaxInt, Cap: math.MaxInt}, nil
29+
}
30+
31+
var result ArrayOption[T]
32+
33+
var optMap map[string]interface{}
34+
switch value := option.(type) {
35+
case ArrayOption[T]:
36+
return value, nil
37+
case bson.M:
38+
optMap = value
39+
case map[string]interface{}:
40+
optMap = value
41+
default:
42+
marshal, err := json.Marshal(option)
43+
if err != nil {
44+
return result, fmt.Errorf("invalid array option,type:%v,value:%v,err:%w", option, option, err)
45+
}
46+
lenItem := gjson.GetBytes(marshal, "len")
47+
capItem := gjson.GetBytes(marshal, "cap")
48+
if !lenItem.Exists() || !lenItem.Exists() {
49+
return result, fmt.Errorf("invalid array option,type:%v,value:%v,err: not exist len or cap", option, option)
50+
}
51+
result.Len = int(capItem.Int())
52+
result.Cap = int(capItem.Int())
53+
return result, result.Valid()
54+
}
55+
lenn, lenOk := optMap["len"]
56+
capp, capOk := optMap["cap"]
57+
if !lenOk || !capOk {
58+
return result, fmt.Errorf("invalid array option,type:%v,value:%v,err: not exist len or cap", option, option)
59+
}
60+
capOpt, err := util.GetIntByInterface(capp)
61+
if err != nil {
62+
return result, err
63+
}
64+
result.Cap = capOpt
65+
lenOpt, err := util.GetIntByInterface(lenn)
66+
if err != nil {
67+
return result, err
68+
}
69+
result.Len = lenOpt
70+
return result, result.Valid()
71+
}
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
package metadata
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"configcenter/src/common"
8+
"configcenter/src/common/blog"
9+
"configcenter/src/common/mapstr"
10+
"configcenter/src/common/util"
11+
"configcenter/src/common/valid/attribute/manager/register"
12+
)
13+
14+
func init() {
15+
// Register the arrayBool attribute type
16+
register.Register(arrayBool{})
17+
}
18+
19+
type arrayBool struct {
20+
}
21+
22+
// Name returns the name of the arrayBool attribute.
23+
func (a arrayBool) Name() string {
24+
return "array_bool"
25+
}
26+
27+
// DisplayName returns the display name for user.
28+
func (a arrayBool) DisplayName() string {
29+
return "布尔数组"
30+
}
31+
32+
// RealType returns the db type of the arrayBool attribute.
33+
// Flattened array uses LongChar as storage type
34+
func (a arrayBool) RealType() string {
35+
return common.FieldTypeLongChar
36+
}
37+
38+
// Info returns the tips for user.
39+
func (a arrayBool) Info() string {
40+
return "布尔值的扁平化数组字段,存储多个true/false值"
41+
}
42+
43+
// Validate validates the arrayBool attribute value
44+
func (a arrayBool) Validate(ctx context.Context, objID string, propertyType string, required bool, option interface{}, value interface{}) error {
45+
46+
rid := util.ExtractRequestIDFromContext(ctx)
47+
48+
if value == nil {
49+
if required {
50+
blog.Errorf("arrayBool attribute %s.%s value is required but got nil, rid: %s", objID, propertyType, rid)
51+
return fmt.Errorf("arrayBool attribute %s.%s value is required but got nil", objID, propertyType)
52+
}
53+
return nil
54+
}
55+
opts, err := ParseArrayOption[any](option)
56+
if err != nil {
57+
blog.Errorf("array_bool parse option failed: %v, rid: %s", err, rid)
58+
return fmt.Errorf("array_bool invalid option: %v", err)
59+
}
60+
61+
// Validate that value is a slice of any
62+
boolArray, ok := value.([]interface{})
63+
if !ok {
64+
blog.Errorf("arrayBool attribute %s.%s value must be []interface{}, got %T, rid: %s", objID, propertyType, value, rid)
65+
return fmt.Errorf("arrayBool attribute %s.%s value must be []interface{}, got %T", objID, propertyType, value)
66+
}
67+
if opts.Cap > len(boolArray) {
68+
return fmt.Errorf("array_bool invalid cap %d, rid: %s", opts.Cap, rid)
69+
}
70+
// Validate each item in the array is a boolean
71+
for i, item := range boolArray {
72+
if _, ok := item.(bool); !ok {
73+
blog.Errorf("arrayBool attribute %s.%s array item [%d] type %T is not bool, rid: %s", objID, propertyType, i, item, rid)
74+
return fmt.Errorf("arrayBool attribute %s.%s array item [%v] type %T is not bool", objID, propertyType, item, item)
75+
}
76+
}
77+
78+
return nil
79+
}
80+
81+
// FillLostValue fills the lost value with default value
82+
func (a arrayBool) FillLostValue(ctx context.Context, valData mapstr.MapStr, propertyId string, defaultValue, option interface{}) error {
83+
84+
rid := util.ExtractRequestIDFromContext(ctx)
85+
86+
valData[propertyId] = nil
87+
if defaultValue == nil {
88+
return nil
89+
}
90+
91+
// Validate default value
92+
defaultArray, ok := defaultValue.([]interface{})
93+
if !ok {
94+
blog.Errorf("arrayBool default value must be []interface{}, got %T, rid: %s", defaultValue, rid)
95+
return fmt.Errorf("arrayBool default value must be []interface{}, got %T", defaultValue)
96+
}
97+
98+
// Validate each item in default array
99+
for i, item := range defaultArray {
100+
if _, ok := item.(bool); !ok {
101+
blog.Errorf("arrayBool default value array item [%d] type %T is not bool, rid: %s", i, item, rid)
102+
return fmt.Errorf("arrayBool default value array item [%d] type %T is not bool", i, item)
103+
}
104+
}
105+
106+
valData[propertyId] = defaultArray
107+
return nil
108+
}
109+
110+
// ValidateOption validates the option field
111+
func (a arrayBool) ValidateOption(ctx context.Context, option interface{}, defaultVal interface{}) error {
112+
113+
rid := util.ExtractRequestIDFromContext(ctx)
114+
115+
_, err := ParseArrayOption[any](option)
116+
if err != nil {
117+
return err
118+
}
119+
if defaultVal == nil {
120+
return nil
121+
}
122+
123+
// Validate default value
124+
defaultArray, ok := defaultVal.([]interface{})
125+
if !ok {
126+
blog.Errorf("arrayBool default value must be []interface{}, got %T, rid: %s", defaultVal, rid)
127+
return fmt.Errorf("arrayBool default value must be []interface{}, got %T", defaultVal)
128+
}
129+
130+
// Validate each item in default array
131+
for i, item := range defaultArray {
132+
if _, ok := item.(bool); !ok {
133+
blog.Errorf("arrayBool default value array item [%d] type %T is not bool, rid: %s", i, item, rid)
134+
return fmt.Errorf("arrayBool default value array item [%d] type %T is not bool", i, item)
135+
}
136+
}
137+
138+
return nil
139+
}
140+
141+
var _ register.AttributeTypeI = &arrayBool{}
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
package metadata
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"configcenter/src/common"
8+
"configcenter/src/common/blog"
9+
"configcenter/src/common/mapstr"
10+
"configcenter/src/common/util"
11+
"configcenter/src/common/valid/attribute/manager/register"
12+
)
13+
14+
func init() {
15+
// Register the arrayDate attribute type
16+
register.Register(arrayDate{})
17+
}
18+
19+
type arrayDate struct {
20+
}
21+
22+
// Name returns the name of the arrayDate attribute.
23+
func (a arrayDate) Name() string {
24+
return "array_date"
25+
}
26+
27+
// DisplayName returns the display name for user.
28+
func (a arrayDate) DisplayName() string {
29+
return "日期数组"
30+
}
31+
32+
// RealType returns the db type of the arrayDate attribute.
33+
// Flattened array uses LongChar as storage type
34+
func (a arrayDate) RealType() string {
35+
return common.FieldTypeLongChar
36+
}
37+
38+
// Info returns the tips for user.
39+
func (a arrayDate) Info() string {
40+
return "日期的扁平化数组字段,格式为YYYY-MM-DD"
41+
}
42+
43+
// Validate validates the arrayDate attribute value
44+
func (a arrayDate) Validate(ctx context.Context, objID string, propertyType string, required bool, option interface{}, value interface{}) error {
45+
rid := util.ExtractRequestIDFromContext(ctx)
46+
47+
if value == nil {
48+
if required {
49+
blog.Errorf("arrayDate attribute %s.%s value is required but got nil, rid: %s", objID, propertyType, rid)
50+
return fmt.Errorf("arrayDate attribute %s.%s value is required but got nil", objID, propertyType)
51+
}
52+
return nil
53+
}
54+
55+
// Validate that value is a slice of any
56+
dateArray, ok := value.([]interface{})
57+
if !ok {
58+
blog.Errorf("arrayDate attribute %s.%s value must be []interface{}, got %T, rid: %s", objID, propertyType, value, rid)
59+
return fmt.Errorf("arrayDate attribute %s.%s value must be []interface{}, got %T", objID, propertyType, value)
60+
}
61+
opts, err := ParseArrayOption[any](option)
62+
if err != nil {
63+
blog.Errorf("array_date parse option failed: %v, rid: %s", err, rid)
64+
return fmt.Errorf("array_date invalid option: %v", err)
65+
}
66+
if opts.Cap > len(dateArray) {
67+
return fmt.Errorf("array_float invalid cap %d, rid: %s", opts.Cap, rid)
68+
}
69+
// Validate each item in the array
70+
for i, item := range dateArray {
71+
// Validate date format
72+
if !util.IsDate(item) {
73+
blog.Errorf("arrayDate attribute %s.%s array item [%d] type %T is not a valid date, rid: %s", objID, propertyType, i, item, rid)
74+
return fmt.Errorf("arrayDate attribute %s.%s array item [%d] is not a valid date", objID, propertyType, i)
75+
}
76+
}
77+
78+
return nil
79+
}
80+
81+
// FillLostValue fills the lost value with default value
82+
func (a arrayDate) FillLostValue(ctx context.Context, valData mapstr.MapStr, propertyId string, defaultValue, option interface{}) error {
83+
rid := util.ExtractRequestIDFromContext(ctx)
84+
85+
valData[propertyId] = nil
86+
if defaultValue == nil {
87+
return nil
88+
}
89+
90+
// Validate default value
91+
defaultArray, ok := defaultValue.([]interface{})
92+
if !ok {
93+
blog.Errorf("arrayDate default value must be []interface{}, got %T, rid: %s", defaultValue, rid)
94+
return fmt.Errorf("arrayDate default value must be []interface{}, got %T", defaultValue)
95+
}
96+
97+
// Validate each item in default array
98+
for i, item := range defaultArray {
99+
if !util.IsDate(item) {
100+
blog.Errorf("arrayDate default value array item [%d] type %T is not a valid date, rid: %s", i, item, rid)
101+
return fmt.Errorf("arrayDate default value array item [%d] is not a valid date", i)
102+
}
103+
}
104+
105+
valData[propertyId] = defaultArray
106+
return nil
107+
}
108+
109+
// ValidateOption validates the option field
110+
func (a arrayDate) ValidateOption(ctx context.Context, option interface{}, defaultVal interface{}) error {
111+
rid := util.ExtractRequestIDFromContext(ctx)
112+
113+
_, err := ParseArrayOption[any](option)
114+
if err != nil {
115+
return err
116+
}
117+
if defaultVal == nil {
118+
return nil
119+
}
120+
121+
// Validate default value
122+
defaultArray, ok := defaultVal.([]interface{})
123+
if !ok {
124+
blog.Errorf("arrayDate default value must be []interface{}, got %T, rid: %s", defaultVal, rid)
125+
return fmt.Errorf("arrayDate default value must be []interface{}, got %T", defaultVal)
126+
}
127+
128+
// Validate each item in default array
129+
for i, item := range defaultArray {
130+
if !util.IsDate(item) {
131+
blog.Errorf("arrayDate default value array item [%d] type %T is not a valid date, rid: %s", i, item, rid)
132+
return fmt.Errorf("arrayDate default value array item [%v] is not a valid date", item)
133+
}
134+
}
135+
136+
return nil
137+
}
138+
139+
var _ register.AttributeTypeI = &arrayDate{}

0 commit comments

Comments
 (0)