Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

database/gdb: fix #2119 #3559

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
99 changes: 99 additions & 0 deletions contrib/drivers/mysql/mysql_z_unit_issue_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1160,3 +1160,102 @@ func Test_Issue3238(t *testing.T) {
}
})
}

type RoleBase struct {
gmeta.Meta `orm:"table:sys_role"`
Name string `json:"name" description:"角色名称" `
Code string `json:"code" description:"角色 code" `
Description string `json:"description" description:"描述信息" `
Weight int `json:"weight" description:"排序" `
StatusId int `json:"statusId" description:"发布状态" `
CreatedAt *gtime.Time `json:"createdAt" description:"" `
UpdatedAt *gtime.Time `json:"updatedAt" description:"" `
}

type Role struct {
gmeta.Meta `orm:"table:sys_role"`
RoleBase
Id uint `json:"id" description:""`
Status *Status `json:"status" description:"发布状态" orm:"with:id=status_id" `
}

type StatusBase struct {
gmeta.Meta `orm:"table:sys_status"`
En string `json:"en" description:"英文名称" `
Cn string `json:"cn" description:"中文名称" `
Weight int `json:"weight" description:"排序权重" `
}

type Status struct {
gmeta.Meta `orm:"table:sys_status"`
StatusBase
Id uint `json:"id" description:""`
}

// https://github.com/gogf/gf/issues/2119
func Test_Issue2119(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
tables := []string{
"sys_role",
"sys_status",
}

defer dropTable(tables[0])
defer dropTable(tables[1])
_ = tables
array := gstr.SplitAndTrim(gtest.DataContent(`issue2119.sql`), ";")
for _, v := range array {
_, err := db.Exec(ctx, v)
t.AssertNil(err)
}
roles := make([]*Role, 0)
err := db.Ctx(context.Background()).Model(&Role{}).WithAll().Scan(&roles)
t.AssertNil(err)
expectStatus := []*Status{
{
StatusBase: StatusBase{
En: "undecided",
Cn: "未决定",
Weight: 800,
},
Id: 2,
},
{
StatusBase: StatusBase{
En: "on line",
Cn: "上线",
Weight: 900,
},
Id: 1,
},
{
StatusBase: StatusBase{
En: "on line",
Cn: "上线",
Weight: 900,
},
Id: 1,
},
{
StatusBase: StatusBase{
En: "on line",
Cn: "上线",
Weight: 900,
},
Id: 1,
},
{
StatusBase: StatusBase{
En: "on line",
Cn: "上线",
Weight: 900,
},
Id: 1,
},
}

for i := 0; i < len(roles); i++ {
t.Assert(roles[i].Status, expectStatus[i])
}
})
}
47 changes: 47 additions & 0 deletions contrib/drivers/mysql/testdata/issue2119.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for sys_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role` (
`id` int(0) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '||s',
`name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '角色名称||s,r',
`code` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '角色 code||s,r',
`description` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '描述信息|text',
`weight` int(0) UNSIGNED NOT NULL DEFAULT 0 COMMENT '排序||r|min:0#发布状态不能小于 0',
`status_id` int(0) UNSIGNED NOT NULL DEFAULT 1 COMMENT '发布状态|hasOne|f:status,fk:id',
`created_at` datetime(0) NULL DEFAULT NULL,
`updated_at` datetime(0) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
INDEX `code`(`code`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1091 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '系统角色表' ROW_FORMAT = Compact;

-- ----------------------------
-- Records of sys_role
-- ----------------------------
INSERT INTO `sys_role` VALUES (1, '开发人员', 'developer', '123123', 900, 2, '2022-09-03 21:25:03', '2022-09-09 23:35:23');
INSERT INTO `sys_role` VALUES (2, '管理员', 'admin', '', 800, 1, '2022-09-03 21:25:03', '2022-09-09 23:00:17');
INSERT INTO `sys_role` VALUES (3, '运营', 'operator', '', 700, 1, '2022-09-03 21:25:03', '2022-09-03 21:25:03');
INSERT INTO `sys_role` VALUES (4, '客服', 'service', '', 600, 1, '2022-09-03 21:25:03', '2022-09-03 21:25:03');
INSERT INTO `sys_role` VALUES (5, '收银', 'account', '', 500, 1, '2022-09-03 21:25:03', '2022-09-03 21:25:03');

-- ----------------------------
-- Table structure for sys_status
-- ----------------------------
DROP TABLE IF EXISTS `sys_status`;
CREATE TABLE `sys_status` (
`id` int(0) UNSIGNED NOT NULL AUTO_INCREMENT,
`en` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '英文名称',
`cn` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '中文名称',
`weight` int(0) UNSIGNED NOT NULL DEFAULT 0 COMMENT '排序权重',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '发布状态' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of sys_status
-- ----------------------------
INSERT INTO `sys_status` VALUES (1, 'on line', '上线', 900);
INSERT INTO `sys_status` VALUES (2, 'undecided', '未决定', 800);
INSERT INTO `sys_status` VALUES (3, 'off line', '下线', 700);
27 changes: 23 additions & 4 deletions os/gstructs/gstructs_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,35 @@

package gstructs

import "reflect"

// Signature returns a unique string as this type.
func (t Type) Signature() string {
return t.PkgPath() + "/" + t.String()
}

// FieldKeys returns the keys of current struct/map.
// FieldKeys returns the keys of current struct.
func (t Type) FieldKeys() []string {
keys := make([]string, t.NumField())
for i := 0; i < t.NumField(); i++ {
keys[i] = t.Field(i).Name
if t.Kind() != reflect.Struct {
return []string{}
}
return getStructFields(t.Type)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里不能直接改这个基础方法,根据issue描述,似乎是这里没有获取到匿名结构体的Field。这里有一个RecursiveOption的控制你可以研究一下呢?
image

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gqcn 这里获取的是最外层结构体,
123456

所以那里是无法获取到Status结构体的字段

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里不能直接改这个基础方法,根据issue描述,似乎是这里没有获取到匿名结构体的Field。这里有一个

倒是可以使用gstructs包的其他方法来解析结构体获取所有的字段,当时我看那个方法只有这里使用了,所以就做了改动。

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gqcn 这里获取的是最外层结构体, 123456

所以那里是无法获取到Status结构体的字段

那截图红框部分没有达到预期,预期是如果内嵌的结构体在没有tag时嵌套结构体的属性会作为Fields返回。可以研究下这个方法为什么失败了呢?

}

func getStructFields(structType reflect.Type) []string {
keys := make([]string, 0, structType.NumField())
for i := 0; i < structType.NumField(); i++ {
field := structType.Field(i)
if field.Anonymous {
if field.Type.Kind() == reflect.Ptr {
field.Type = field.Type.Elem()
}
if field.Type.Kind() == reflect.Struct {
keys = append(keys, getStructFields(field.Type)...)
continue
}
}
keys = append(keys, field.Name)
}
return keys
}
36 changes: 36 additions & 0 deletions os/gstructs/gstructs_z_unit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,42 @@ func TestType_FieldKeys(t *testing.T) {
t.AssertNil(err)
t.Assert(r.FieldKeys(), g.Slice{"Id", "Name"})
})

gtest.C(t, func(t *gtest.T) {
type A struct {
Age int
Score float64
}
type B struct {
A
Id int
Name string
}
r, err := gstructs.StructType(new(B))
t.AssertNil(err)
t.Assert(r.FieldKeys(), g.Slice{"Age", "Score", "Id", "Name"})
})

gtest.C(t, func(t *gtest.T) {
type Time struct {
CreatedAt string
UpdatedAt string
}
type A struct {
Age int
Score float64
}
type B struct {
*A
Time
Aa *A
Id int
Name string
}
r, err := gstructs.StructType(new(B))
t.AssertNil(err)
t.Assert(r.FieldKeys(), g.Slice{"Age", "Score", "CreatedAt", "UpdatedAt", "Aa", "Id", "Name"})
})
}

func TestType_TagMap(t *testing.T) {
Expand Down