From 3c757d53c5032bc00629153c6662149686d73ba5 Mon Sep 17 00:00:00 2001 From: what Date: Thu, 20 Aug 2026 18:00:40 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84:=20User=20=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E7=A7=BB=E5=88=B0=20req/userx=EF=BC=8C?= =?UTF-8?q?=E6=B6=88=E9=99=A4=20req=20=E5=AF=B9=20contracts=20=E7=9A=84?= =?UTF-8?q?=E5=BE=AA=E7=8E=AF=E4=BE=9D=E8=B5=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GetAnonymous/GetSystemUser 原来在 contracts/base 里实现,req 自己的测试 又要用它们构造测试用户,形成 req(测试)依赖 contracts、contracts(正式 代码)依赖 req 的循环——两边 go.mod 只能靠指向本地兄弟目录的 replace 绕过, 没法各自独立解析成真实版本号。 跟 resx 之于 req.Resource 一个思路:接口留在 req 包,默认实现挪到子包 req/userx,req 主包保持不引入实现细节所需的依赖(这里是 samber/lo)。 req 自己的测试文件改用 req/userx,不用触碰 contracts 了;contracts/base 里的 GetAnonymous/GetSystemUser 改成指向 req/userx 的函数别名,保持 兼容不用改调用方。 --- resx/res_interceptor_test.go | 44 ++++++++++---------- resx/res_normalize_test.go | 22 +++++----- resx/resource_test.go | 8 ++-- userx/user.go | 79 ++++++++++++++++++++++++++++++++++++ 4 files changed, 116 insertions(+), 37 deletions(-) create mode 100644 userx/user.go diff --git a/resx/res_interceptor_test.go b/resx/res_interceptor_test.go index f6ef7ed..d0f83fe 100644 --- a/resx/res_interceptor_test.go +++ b/resx/res_interceptor_test.go @@ -7,12 +7,12 @@ import ( "testing" "time" - "git.fsdpf.net/go/contracts/base" "git.fsdpf.net/go/db" "git.fsdpf.net/go/db/engine" "git.fsdpf.net/go/reflux" "git.fsdpf.net/go/req" "git.fsdpf.net/go/req/resx" + "git.fsdpf.net/go/req/userx" "github.com/DATA-DOG/go-sqlmock" "github.com/samber/do/v2" "github.com/stretchr/testify/suite" @@ -84,14 +84,14 @@ func (t *hooksTest) TestOnBefore_RolesCondition_Cond() { // 实际生成的 SQL: // SELECT * FROM `users` AS `User` WHERE (`owned_user` = '00000000-0000-0000-0000-000000000000') - sql, _, _ := res.GetDBTable(base.GetAnonymous(), req.WithPermission(req.ResAll)).Select(db.Star()).Executor().ToSQL() + sql, _, _ := res.GetDBTable(userx.GetAnonymous(), req.WithPermission(req.ResAll)).Select(db.Star()).Executor().ToSQL() t.Contains(sql, "owned_user") t.Contains(sql, "00000000-0000-0000-0000-000000000000") mock.ExpectQuery("SELECT"). WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).AddRow(1, "张三")) - rows, err := res.GetDBTable(base.GetAnonymous(), req.WithPermission(req.ResAll)).Select(db.Star()).Executor().GetRecords() + rows, err := res.GetDBTable(userx.GetAnonymous(), req.WithPermission(req.ResAll)).Select(db.Star()).Executor().GetRecords() t.NoError(err) t.Len(rows, 1) t.NoError(mock.ExpectationsWereMet()) @@ -111,13 +111,13 @@ func (t *hooksTest) TestOnBefore_RolesCondition_Sub() { // 实际生成的 SQL: // SELECT * FROM (SELECT * FROM `users` AS `User` WHERE (`owned_user` = '00000000-0000-0000-0000-000000000000')) AS `User` - sql, _, _ := res.GetDBTable(base.GetAnonymous(), req.WithPermission(req.ResAll)).Select(db.Star()).Executor().ToSQL() + sql, _, _ := res.GetDBTable(userx.GetAnonymous(), req.WithPermission(req.ResAll)).Select(db.Star()).Executor().ToSQL() t.Contains(sql, "owned_user") mock.ExpectQuery("SELECT"). WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).AddRow(2, "李四")) - rows, err := res.GetDBTable(base.GetAnonymous(), req.WithPermission(req.ResAll)).Select(db.Star()).Executor().GetRecords() + rows, err := res.GetDBTable(userx.GetAnonymous(), req.WithPermission(req.ResAll)).Select(db.Star()).Executor().GetRecords() t.NoError(err) t.Len(rows, 1) t.NoError(mock.ExpectationsWereMet()) @@ -153,7 +153,7 @@ func (t *hooksTest) TestOnBefore_NeedsCapture_True() { mock.ExpectExec("UPDATE").WillReturnResult(sqlmock.NewResult(0, 1)) mock.ExpectCommit() - _, err := res.GetDBTable(base.GetAnonymous(), req.WithPermission(req.ResAll)).Update(). + _, err := res.GetDBTable(userx.GetAnonymous(), req.WithPermission(req.ResAll)).Update(). Set(db.Record{"name": "新名字"}). Where(db.C("id").Eq(1)). Executor().Exec() @@ -185,7 +185,7 @@ func (t *hooksTest) TestOnBefore_NeedsCapture_False() { // 实际生成的 SQL:UPDATE `users` AS `User` SET `name`='新名字' WHERE (`id` = 1) mock.ExpectExec("UPDATE").WillReturnResult(sqlmock.NewResult(0, 1)) - _, err := res.GetDBTable(base.GetAnonymous(), req.WithPermission(req.ResAll)).Update(). + _, err := res.GetDBTable(userx.GetAnonymous(), req.WithPermission(req.ResAll)).Update(). Set(db.Record{"name": "新名字"}). Where(db.C("id").Eq(1)). Executor().Exec() @@ -220,7 +220,7 @@ func (t *hooksTest) TestOnAfter_RowsContent_Insert() { mock.ExpectExec("INSERT").WillReturnResult(sqlmock.NewResult(10, 1)) mock.ExpectCommit() - _, err := res.GetDBTable(base.GetAnonymous(), req.WithPermission(req.ResAll)).Insert(). + _, err := res.GetDBTable(userx.GetAnonymous(), req.WithPermission(req.ResAll)).Insert(). Rows(db.Record{"name": "张三"}). Executor().Exec() t.NoError(err) @@ -256,7 +256,7 @@ func (t *hooksTest) TestOnAfter_UnconditionalOnWrite() { // 实际生成的 SQL:DELETE FROM `users` AS `User` WHERE (`id` = 1) mock.ExpectExec("DELETE").WillReturnResult(sqlmock.NewResult(0, 3)) - _, err := res.GetDBTable(base.GetAnonymous(), req.WithPermission(req.ResAll)).Delete(). + _, err := res.GetDBTable(userx.GetAnonymous(), req.WithPermission(req.ResAll)).Delete(). Where(db.C("id").Eq(1)). Executor().Exec() t.NoError(err) @@ -300,7 +300,7 @@ func (t *hooksTest) TestInsertFromQuery_FetchesInsertedRowsAfterward() { ) mock.ExpectCommit() - _, err := res.GetDBTable(base.GetAnonymous(), req.WithPermission(req.ResAll)).Insert().FromQuery(sub).Executor().Exec() + _, err := res.GetDBTable(userx.GetAnonymous(), req.WithPermission(req.ResAll)).Insert().FromQuery(sub).Executor().Exec() t.NoError(err) t.NoError(mock.ExpectationsWereMet()) @@ -335,7 +335,7 @@ func (t *hooksTest) TestBatchInsertIds_Mysql() { mock.ExpectExec("INSERT").WillReturnResult(sqlmock.NewResult(100, 3)) mock.ExpectCommit() - _, err := res.GetDBTable(base.GetAnonymous(), req.WithPermission(req.ResAll)).Insert().Rows( + _, err := res.GetDBTable(userx.GetAnonymous(), req.WithPermission(req.ResAll)).Insert().Rows( db.Record{"name": "A"}, db.Record{"name": "B"}, db.Record{"name": "C"}, ).Executor().Exec() t.NoError(err) @@ -385,7 +385,7 @@ func (t *hooksTest) TestBatchInsertIds_LocalDB() { ), ) - _, err := res.GetDBTable(base.GetAnonymous(), req.WithPermission(req.ResAll)).Insert().Rows( + _, err := res.GetDBTable(userx.GetAnonymous(), req.WithPermission(req.ResAll)).Insert().Rows( db.Record{"name": "A"}, db.Record{"name": "B"}, db.Record{"name": "C"}, ).Executor().Exec() t.Require().NoError(err) @@ -413,7 +413,7 @@ func (t *hooksTest) TestUpdate_RolesConditionSub() { // 实际生成的 SQL: // UPDATE `users` AS `User` SET `name`='新名字' WHERE ((`id` = 1) AND (`id` IN ((SELECT `User`.`id` FROM `users` AS `User` WHERE (`owned_user` = '00000000-0000-0000-0000-000000000000'))))) - sql, _, _ := res.GetDBTable(base.GetAnonymous(), req.WithPermission(req.ResAll)).Update(). + sql, _, _ := res.GetDBTable(userx.GetAnonymous(), req.WithPermission(req.ResAll)).Update(). Set(db.Record{"name": "新名字"}). Where(db.C("id").Eq(1)). Executor().ToSQL() @@ -438,7 +438,7 @@ func (t *hooksTest) TestDelete_RolesConditionSub() { // 实际生成的 SQL: // DELETE FROM `users` AS `User` WHERE ((`id` = 1) AND (`id` IN ((SELECT `User`.`id` FROM `users` AS `User` WHERE (`owned_user` = '00000000-0000-0000-0000-000000000000'))))) - sql, _, _ := res.GetDBTable(base.GetAnonymous(), req.WithPermission(req.ResAll)).Delete(). + sql, _, _ := res.GetDBTable(userx.GetAnonymous(), req.WithPermission(req.ResAll)).Delete(). Where(db.C("id").Eq(1)). Executor().ToSQL() @@ -474,7 +474,7 @@ func (t *hooksTest) TestDelete_CapturesSnapshot() { mock.ExpectExec("DELETE").WillReturnResult(sqlmock.NewResult(0, 1)) mock.ExpectCommit() - _, err := res.GetDBTable(base.GetAnonymous(), req.WithPermission(req.ResAll)).Delete(). + _, err := res.GetDBTable(userx.GetAnonymous(), req.WithPermission(req.ResAll)).Delete(). Where(db.C("id").Eq(5)). Executor().Exec() t.NoError(err) @@ -510,7 +510,7 @@ func (t *hooksTest) TestInsertColsVals_Capture() { mock.ExpectExec("INSERT").WillReturnResult(sqlmock.NewResult(1, 1)) mock.ExpectCommit() - _, err := res.GetDBTable(base.GetAnonymous(), req.WithPermission(req.ResAll)).Insert().Cols("name").Vals( + _, err := res.GetDBTable(userx.GetAnonymous(), req.WithPermission(req.ResAll)).Insert().Cols("name").Vals( db.Vals{"赵六"}, ).Executor().Exec() t.NoError(err) @@ -528,7 +528,7 @@ func (t *hooksTest) TestInsertColsVals_DoesNotStripSystemColumns() { // 实际生成的 SQL(created_user 没有被剔除/改写): // INSERT INTO `users` (`name`, `created_user`, `owned_user`) VALUES ('赵六', 'attacker-uuid', '00000000-0000-0000-0000-000000000000') - sql, _, _ := res.GetDBTable(base.GetAnonymous(), req.WithPermission(req.ResAll)).Insert().Cols("name", "created_user").Vals( + sql, _, _ := res.GetDBTable(userx.GetAnonymous(), req.WithPermission(req.ResAll)).Insert().Cols("name", "created_user").Vals( db.Vals{"赵六", "attacker-uuid"}, ).Executor().ToSQL() @@ -572,7 +572,7 @@ func (t *hooksTest) TestVirtualResource_SkipsRolesConditionAndOnAfter() { // 实际生成的 SQL(虚拟资源拿子查询拼表名,且没有 owned_user 权限条件): // UPDATE (SELECT * FROM `users`) AS `User` SET `name`='新名字' WHERE (`id` = 1) - sql, _, _ := res.GetDBTable(base.GetAnonymous(), req.WithPermission(req.ResAll)).Update(). + sql, _, _ := res.GetDBTable(userx.GetAnonymous(), req.WithPermission(req.ResAll)).Update(). Set(db.Record{"name": "新名字"}). Where(db.C("id").Eq(1)). Executor().ToSQL() @@ -580,7 +580,7 @@ func (t *hooksTest) TestVirtualResource_SkipsRolesConditionAndOnAfter() { // 没有安排 Begin:虚拟资源在 UseTx 里直接短路,不会自动开事务 mock.ExpectExec("UPDATE").WillReturnResult(sqlmock.NewResult(0, 1)) - _, err = res.GetDBTable(base.GetAnonymous(), req.WithPermission(req.ResAll)).Update(). + _, err = res.GetDBTable(userx.GetAnonymous(), req.WithPermission(req.ResAll)).Update(). Set(db.Record{"name": "新名字"}). Where(db.C("id").Eq(1)). Executor().Exec() @@ -620,7 +620,7 @@ func (t *hooksTest) TestBuildChanges_ResolvesNonPlaceholderLiteral() { mock.ExpectExec("UPDATE").WillReturnResult(sqlmock.NewResult(0, 1)) mock.ExpectCommit() - _, err := res.GetDBTable(base.GetAnonymous(), req.WithPermission(req.ResAll)).Update(). + _, err := res.GetDBTable(userx.GetAnonymous(), req.WithPermission(req.ResAll)).Update(). Set(db.Record{"name": db.L("NOW()")}). Where(db.C("id").Eq(1)). Executor().Exec() @@ -650,7 +650,7 @@ func (t *hooksTest) TestUseTx_RollsBackOnChangeError() { mock.ExpectExec("INSERT").WillReturnResult(sqlmock.NewResult(1, 1)) mock.ExpectRollback() - _, err := res.GetDBTable(base.GetAnonymous(), req.WithPermission(req.ResAll)).Insert(). + _, err := res.GetDBTable(userx.GetAnonymous(), req.WithPermission(req.ResAll)).Insert(). Rows(db.Record{"name": "赵六"}). Executor().Exec() @@ -684,7 +684,7 @@ func (t *hooksTest) TestUseTx_ReusesCallerProvidedTx() { // 这里不再有第二次 ExpectBegin:证明 UseTx 发现 dp.tx 已经非 nil 就不会再自己开一个 mock.ExpectExec("INSERT").WillReturnResult(sqlmock.NewResult(1, 1)) - _, err = res.GetDBTable(base.GetAnonymous(), req.WithTx(tx), req.WithPermission(req.ResAll)).Insert(). + _, err = res.GetDBTable(userx.GetAnonymous(), req.WithTx(tx), req.WithPermission(req.ResAll)).Insert(). Rows(db.Record{"name": "赵六"}). Executor().Exec() t.NoError(err) diff --git a/resx/res_normalize_test.go b/resx/res_normalize_test.go index f732bdb..4ef84f9 100644 --- a/resx/res_normalize_test.go +++ b/resx/res_normalize_test.go @@ -4,11 +4,11 @@ import ( "strings" "testing" - "git.fsdpf.net/go/contracts/base" "git.fsdpf.net/go/db" "git.fsdpf.net/go/db/engine" "git.fsdpf.net/go/req" "git.fsdpf.net/go/req/resx" + "git.fsdpf.net/go/req/userx" "github.com/DATA-DOG/go-sqlmock" "github.com/samber/do/v2" "github.com/stretchr/testify/suite" @@ -56,7 +56,7 @@ func (t *normalizeTest) TestStripsSystemFields() { resx.NewResField("name", "", resx.FieldWithName("姓名"), resx.FieldWithDataType(req.ResString)), ) - sql, _, _ := res.GetDBTable(base.GetAnonymous()).Insert().Rows( + sql, _, _ := res.GetDBTable(userx.GetAnonymous()).Insert().Rows( db.Record{ "id": 999, "name": "张三", @@ -83,7 +83,7 @@ func (t *normalizeTest) TestDropsUndeclaredFields() { resx.NewResField("name", "", resx.FieldWithName("姓名"), resx.FieldWithDataType(req.ResString)), ) - sql, _, _ := res.GetDBTable(base.GetAnonymous()).Insert().Rows( + sql, _, _ := res.GetDBTable(userx.GetAnonymous()).Insert().Rows( db.Record{"name": "李四", "hacker_field": "evil"}, ).Executor().ToSQL() @@ -99,7 +99,7 @@ func (t *normalizeTest) TestExpressionValuePassesThroughEvenIfUndeclared() { resx.NewResField("name", "", resx.FieldWithName("姓名"), resx.FieldWithDataType(req.ResString)), ) - sql, _, _ := res.GetDBTable(base.GetAnonymous()).Insert().Rows( + sql, _, _ := res.GetDBTable(userx.GetAnonymous()).Insert().Rows( db.Record{"name": "王五", "login_at": db.L("NOW()")}, ).Executor().ToSQL() @@ -114,7 +114,7 @@ func (t *normalizeTest) TestJSONFieldNilUsesFieldDefault() { resx.NewResField("fields", "", resx.FieldWithName("字段"), resx.FieldWithDefault("[]"), resx.FieldWithDataType(req.ResJson)), ) - sql, _, _ := res.GetDBTable(base.GetAnonymous()).Insert().Rows( + sql, _, _ := res.GetDBTable(userx.GetAnonymous()).Insert().Rows( db.Record{"name": "A", "fields": nil}, ).Executor().ToSQL() @@ -131,7 +131,7 @@ func (t *normalizeTest) TestJSONFieldDefaultFilledWhenOmitted() { resx.NewResField("fields", "", resx.FieldWithName("字段"), resx.FieldWithDefault("{}"), resx.FieldWithDataType(req.ResJson)), ) - sql, _, _ := res.GetDBTable(base.GetAnonymous()).Insert().Rows( + sql, _, _ := res.GetDBTable(userx.GetAnonymous()).Insert().Rows( db.Record{"name": "B"}, ).Executor().ToSQL() @@ -146,7 +146,7 @@ func (t *normalizeTest) TestNonJSONFieldDefaultNotAutoFilled() { resx.NewResField("name", "", resx.FieldWithName("姓名"), resx.FieldWithDefault("匿名"), resx.FieldWithDataType(req.ResString)), ) - sql, _, _ := res.GetDBTable(base.GetAnonymous()).Insert().Rows( + sql, _, _ := res.GetDBTable(userx.GetAnonymous()).Insert().Rows( db.Record{}, ).Executor().ToSQL() @@ -161,7 +161,7 @@ func (t *normalizeTest) TestOwnedUserDefaultsToCurrentUserWhenUndeclared() { resx.NewResField("name", "", resx.FieldWithName("姓名"), resx.FieldWithDataType(req.ResString)), ) - sql, _, _ := res.GetDBTable(base.GetAnonymous()).Insert().Rows( + sql, _, _ := res.GetDBTable(userx.GetAnonymous()).Insert().Rows( db.Record{"name": "C", "owned_user": "someone-else-uuid"}, ).Executor().ToSQL() @@ -178,7 +178,7 @@ func (t *normalizeTest) TestOwnedUserPreservedWhenDeclaredAsField() { resx.NewResField("owned_user", "", resx.FieldWithName("拥有者"), resx.FieldWithDataType(req.ResString)), ) - sql, _, _ := res.GetDBTable(base.GetAnonymous()).Insert().Rows( + sql, _, _ := res.GetDBTable(userx.GetAnonymous()).Insert().Rows( db.Record{"name": "D", "owned_user": "someone-else-uuid"}, ).Executor().ToSQL() @@ -193,7 +193,7 @@ func (t *normalizeTest) TestCreatedUserAlwaysForcedToCurrentUser() { resx.NewResField("created_user", "", resx.FieldWithName("创建者"), resx.FieldWithDataType(req.ResString)), ) - sql, _, _ := res.GetDBTable(base.GetAnonymous()).Insert().Rows( + sql, _, _ := res.GetDBTable(userx.GetAnonymous()).Insert().Rows( db.Record{"name": "E", "created_user": "someone-else-uuid"}, ).Executor().ToSQL() @@ -212,7 +212,7 @@ func (t *normalizeTest) TestNormalizeSaveValue_Update() { resx.NewResField("fields", "", resx.FieldWithName("字段"), resx.FieldWithDefault("{}"), resx.FieldWithDataType(req.ResJson)), ) - sql, _, _ := res.GetDBTable(base.GetAnonymous()).Update(). + sql, _, _ := res.GetDBTable(userx.GetAnonymous()).Update(). Set(db.Record{ "id": 1, "name": "F", diff --git a/resx/resource_test.go b/resx/resource_test.go index 40f6822..771e85d 100644 --- a/resx/resource_test.go +++ b/resx/resource_test.go @@ -3,11 +3,11 @@ package resx_test import ( "testing" - "git.fsdpf.net/go/contracts/base" "git.fsdpf.net/go/db" "git.fsdpf.net/go/db/engine" "git.fsdpf.net/go/req" "git.fsdpf.net/go/req/resx" + "git.fsdpf.net/go/req/userx" "github.com/DATA-DOG/go-sqlmock" "github.com/samber/do/v2" "github.com/stretchr/testify/suite" @@ -56,7 +56,7 @@ func (t *resourceTest) TestInsertRows() { result := "INSERT INTO `users` (`created_user`, `fields`, `name`, `owned_user`) " + "VALUES ('00000000-0000-0000-0000-000000000000', '{\\\"aa\\\":1}', '张三', '00000000-0000-0000-0000-000000000000')" - sql, _, _ := t.res.GetDBTable(base.GetAnonymous()).Insert().Rows( + sql, _, _ := t.res.GetDBTable(userx.GetAnonymous()).Insert().Rows( db.Record{"name": "张三", "id": 1, "fields": map[string]any{"aa": 1}}, ).Executor().ToSQL() t.Equal(result, sql) @@ -64,7 +64,7 @@ func (t *resourceTest) TestInsertRows() { // 注意:db-v2 的 exp.NewRecordFromStruct 传入指针 struct 时会 panic // (util.SafeGetFieldByIndex 未对指针做 reflect.Indirect),此处暂不测试指针形式 - sql, _, _ = t.res.GetDBTable(base.GetAnonymous()).Insert().Rows( + sql, _, _ = t.res.GetDBTable(userx.GetAnonymous()).Insert().Rows( Temp{Name: "张三", Fields: map[string]any{"aa": 1}}, ).Executor().ToSQL() t.Equal(result, sql) @@ -74,7 +74,7 @@ func (t *resourceTest) TestInsertColsVals() { "('李四', '{\\\"aa\\\":1}', '00000000-0000-0000-0000-000000000000', '00000000-0000-0000-0000-000000000000'), " + "('王五', '{\\\"aa\\\":3}', '00000000-0000-0000-0000-000000000000', '00000000-0000-0000-0000-000000000000')" - sql, _, _ := t.res.GetDBTable(base.GetAnonymous()).Insert().Cols("name", "fields").Vals( + sql, _, _ := t.res.GetDBTable(userx.GetAnonymous()).Insert().Cols("name", "fields").Vals( db.Vals{"李四", map[string]any{"aa": 1}}, db.Vals{"王五", map[string]any{"aa": 3}}, ).Executor().ToSQL() diff --git a/userx/user.go b/userx/user.go new file mode 100644 index 0000000..07b5eba --- /dev/null +++ b/userx/user.go @@ -0,0 +1,79 @@ +// Package userx 提供 req.User 的默认实现,跟 resx 之于 req.Resource 是同一个思路:接口留在 +// req 包里保持依赖最小,具体实现(包括内置的匿名用户/系统用户)放在子包里,避免 req 包本身 +// 引入实现所需的依赖(这里是 github.com/samber/lo)。 +package userx + +import ( + "github.com/samber/lo" + + "git.fsdpf.net/go/req" +) + +type user struct { + id int64 + uuid string + username string + nickname string + roles []string + runtime req.UserRuntime +} + +func (this user) ID() int64 { + return this.id +} + +func (this user) Uuid() string { + return this.uuid +} + +func (this user) Username() string { + return this.username +} + +func (this user) Nickname() string { + return this.nickname +} + +func (this user) GetUserInfo() map[string]any { + return nil +} + +func (this user) Roles() (roles []string) { + return this.roles +} + +func (this user) HasUserRoles(roles ...string) bool { + return lo.Contains(this.Roles(), "ffffffff-ffff-ffff-ffff-ffffffffffff") || len(lo.Intersect(this.Roles(), roles)) > 0 +} + +func (this user) IsAnonymous() bool { + return this.id == 0 +} + +func (this user) Runtime() req.UserRuntime { + return this.runtime +} + +// GetAnonymous 获取匿名用户 +func GetAnonymous(opts ...req.UserRuntimeOption) req.User { + return user{ + id: 0, + uuid: "00000000-0000-0000-0000-000000000000", + username: "anonymous", + nickname: "匿名者", + roles: []string{"00000000-0000-0000-0000-000000000000"}, + runtime: req.NewUserRuntime(opts...), + } +} + +// GetSystemUser 系统用户 +func GetSystemUser(opts ...req.UserRuntimeOption) req.User { + return user{ + id: -1, + uuid: "ffffffff-ffff-ffff-ffff-ffffffffffff", + username: "system", + nickname: "系统", + roles: []string{"ffffffff-ffff-ffff-ffff-ffffffffffff"}, + runtime: req.NewUserRuntime(opts...), + } +}