重构: User 默认实现移到 req/userx,消除 req 对 contracts 的循环依赖

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 的函数别名,保持
兼容不用改调用方。
This commit is contained in:
2026-08-20 18:00:40 +08:00
parent 632c5476a0
commit 3c757d53c5
4 changed files with 116 additions and 37 deletions
+22 -22
View File
@@ -7,12 +7,12 @@ import (
"testing" "testing"
"time" "time"
"git.fsdpf.net/go/contracts/base"
"git.fsdpf.net/go/db" "git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/engine" "git.fsdpf.net/go/db/engine"
"git.fsdpf.net/go/reflux" "git.fsdpf.net/go/reflux"
"git.fsdpf.net/go/req" "git.fsdpf.net/go/req"
"git.fsdpf.net/go/req/resx" "git.fsdpf.net/go/req/resx"
"git.fsdpf.net/go/req/userx"
"github.com/DATA-DOG/go-sqlmock" "github.com/DATA-DOG/go-sqlmock"
"github.com/samber/do/v2" "github.com/samber/do/v2"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
@@ -84,14 +84,14 @@ func (t *hooksTest) TestOnBefore_RolesCondition_Cond() {
// 实际生成的 SQL // 实际生成的 SQL
// SELECT * FROM `users` AS `User` WHERE (`owned_user` = '00000000-0000-0000-0000-000000000000') // 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, "owned_user")
t.Contains(sql, "00000000-0000-0000-0000-000000000000") t.Contains(sql, "00000000-0000-0000-0000-000000000000")
mock.ExpectQuery("SELECT"). mock.ExpectQuery("SELECT").
WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).AddRow(1, "张三")) 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.NoError(err)
t.Len(rows, 1) t.Len(rows, 1)
t.NoError(mock.ExpectationsWereMet()) t.NoError(mock.ExpectationsWereMet())
@@ -111,13 +111,13 @@ func (t *hooksTest) TestOnBefore_RolesCondition_Sub() {
// 实际生成的 SQL // 实际生成的 SQL
// SELECT * FROM (SELECT * FROM `users` AS `User` WHERE (`owned_user` = '00000000-0000-0000-0000-000000000000')) AS `User` // 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") t.Contains(sql, "owned_user")
mock.ExpectQuery("SELECT"). mock.ExpectQuery("SELECT").
WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).AddRow(2, "李四")) 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.NoError(err)
t.Len(rows, 1) t.Len(rows, 1)
t.NoError(mock.ExpectationsWereMet()) t.NoError(mock.ExpectationsWereMet())
@@ -153,7 +153,7 @@ func (t *hooksTest) TestOnBefore_NeedsCapture_True() {
mock.ExpectExec("UPDATE").WillReturnResult(sqlmock.NewResult(0, 1)) mock.ExpectExec("UPDATE").WillReturnResult(sqlmock.NewResult(0, 1))
mock.ExpectCommit() 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": "新名字"}). Set(db.Record{"name": "新名字"}).
Where(db.C("id").Eq(1)). Where(db.C("id").Eq(1)).
Executor().Exec() Executor().Exec()
@@ -185,7 +185,7 @@ func (t *hooksTest) TestOnBefore_NeedsCapture_False() {
// 实际生成的 SQLUPDATE `users` AS `User` SET `name`='新名字' WHERE (`id` = 1) // 实际生成的 SQLUPDATE `users` AS `User` SET `name`='新名字' WHERE (`id` = 1)
mock.ExpectExec("UPDATE").WillReturnResult(sqlmock.NewResult(0, 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": "新名字"}). Set(db.Record{"name": "新名字"}).
Where(db.C("id").Eq(1)). Where(db.C("id").Eq(1)).
Executor().Exec() Executor().Exec()
@@ -220,7 +220,7 @@ func (t *hooksTest) TestOnAfter_RowsContent_Insert() {
mock.ExpectExec("INSERT").WillReturnResult(sqlmock.NewResult(10, 1)) mock.ExpectExec("INSERT").WillReturnResult(sqlmock.NewResult(10, 1))
mock.ExpectCommit() 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": "张三"}). Rows(db.Record{"name": "张三"}).
Executor().Exec() Executor().Exec()
t.NoError(err) t.NoError(err)
@@ -256,7 +256,7 @@ func (t *hooksTest) TestOnAfter_UnconditionalOnWrite() {
// 实际生成的 SQLDELETE FROM `users` AS `User` WHERE (`id` = 1) // 实际生成的 SQLDELETE FROM `users` AS `User` WHERE (`id` = 1)
mock.ExpectExec("DELETE").WillReturnResult(sqlmock.NewResult(0, 3)) 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)). Where(db.C("id").Eq(1)).
Executor().Exec() Executor().Exec()
t.NoError(err) t.NoError(err)
@@ -300,7 +300,7 @@ func (t *hooksTest) TestInsertFromQuery_FetchesInsertedRowsAfterward() {
) )
mock.ExpectCommit() 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(err)
t.NoError(mock.ExpectationsWereMet()) t.NoError(mock.ExpectationsWereMet())
@@ -335,7 +335,7 @@ func (t *hooksTest) TestBatchInsertIds_Mysql() {
mock.ExpectExec("INSERT").WillReturnResult(sqlmock.NewResult(100, 3)) mock.ExpectExec("INSERT").WillReturnResult(sqlmock.NewResult(100, 3))
mock.ExpectCommit() 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"}, db.Record{"name": "A"}, db.Record{"name": "B"}, db.Record{"name": "C"},
).Executor().Exec() ).Executor().Exec()
t.NoError(err) 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"}, db.Record{"name": "A"}, db.Record{"name": "B"}, db.Record{"name": "C"},
).Executor().Exec() ).Executor().Exec()
t.Require().NoError(err) t.Require().NoError(err)
@@ -413,7 +413,7 @@ func (t *hooksTest) TestUpdate_RolesConditionSub() {
// 实际生成的 SQL // 实际生成的 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'))))) // 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": "新名字"}). Set(db.Record{"name": "新名字"}).
Where(db.C("id").Eq(1)). Where(db.C("id").Eq(1)).
Executor().ToSQL() Executor().ToSQL()
@@ -438,7 +438,7 @@ func (t *hooksTest) TestDelete_RolesConditionSub() {
// 实际生成的 SQL // 实际生成的 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'))))) // 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)). Where(db.C("id").Eq(1)).
Executor().ToSQL() Executor().ToSQL()
@@ -474,7 +474,7 @@ func (t *hooksTest) TestDelete_CapturesSnapshot() {
mock.ExpectExec("DELETE").WillReturnResult(sqlmock.NewResult(0, 1)) mock.ExpectExec("DELETE").WillReturnResult(sqlmock.NewResult(0, 1))
mock.ExpectCommit() 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)). Where(db.C("id").Eq(5)).
Executor().Exec() Executor().Exec()
t.NoError(err) t.NoError(err)
@@ -510,7 +510,7 @@ func (t *hooksTest) TestInsertColsVals_Capture() {
mock.ExpectExec("INSERT").WillReturnResult(sqlmock.NewResult(1, 1)) mock.ExpectExec("INSERT").WillReturnResult(sqlmock.NewResult(1, 1))
mock.ExpectCommit() 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{"赵六"}, db.Vals{"赵六"},
).Executor().Exec() ).Executor().Exec()
t.NoError(err) t.NoError(err)
@@ -528,7 +528,7 @@ func (t *hooksTest) TestInsertColsVals_DoesNotStripSystemColumns() {
// 实际生成的 SQLcreated_user 没有被剔除/改写): // 实际生成的 SQLcreated_user 没有被剔除/改写):
// INSERT INTO `users` (`name`, `created_user`, `owned_user`) VALUES ('赵六', 'attacker-uuid', '00000000-0000-0000-0000-000000000000') // 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"}, db.Vals{"赵六", "attacker-uuid"},
).Executor().ToSQL() ).Executor().ToSQL()
@@ -572,7 +572,7 @@ func (t *hooksTest) TestVirtualResource_SkipsRolesConditionAndOnAfter() {
// 实际生成的 SQL(虚拟资源拿子查询拼表名,且没有 owned_user 权限条件): // 实际生成的 SQL(虚拟资源拿子查询拼表名,且没有 owned_user 权限条件):
// UPDATE (SELECT * FROM `users`) AS `User` SET `name`='新名字' WHERE (`id` = 1) // 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": "新名字"}). Set(db.Record{"name": "新名字"}).
Where(db.C("id").Eq(1)). Where(db.C("id").Eq(1)).
Executor().ToSQL() Executor().ToSQL()
@@ -580,7 +580,7 @@ func (t *hooksTest) TestVirtualResource_SkipsRolesConditionAndOnAfter() {
// 没有安排 Begin:虚拟资源在 UseTx 里直接短路,不会自动开事务 // 没有安排 Begin:虚拟资源在 UseTx 里直接短路,不会自动开事务
mock.ExpectExec("UPDATE").WillReturnResult(sqlmock.NewResult(0, 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": "新名字"}). Set(db.Record{"name": "新名字"}).
Where(db.C("id").Eq(1)). Where(db.C("id").Eq(1)).
Executor().Exec() Executor().Exec()
@@ -620,7 +620,7 @@ func (t *hooksTest) TestBuildChanges_ResolvesNonPlaceholderLiteral() {
mock.ExpectExec("UPDATE").WillReturnResult(sqlmock.NewResult(0, 1)) mock.ExpectExec("UPDATE").WillReturnResult(sqlmock.NewResult(0, 1))
mock.ExpectCommit() 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()")}). Set(db.Record{"name": db.L("NOW()")}).
Where(db.C("id").Eq(1)). Where(db.C("id").Eq(1)).
Executor().Exec() Executor().Exec()
@@ -650,7 +650,7 @@ func (t *hooksTest) TestUseTx_RollsBackOnChangeError() {
mock.ExpectExec("INSERT").WillReturnResult(sqlmock.NewResult(1, 1)) mock.ExpectExec("INSERT").WillReturnResult(sqlmock.NewResult(1, 1))
mock.ExpectRollback() 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": "赵六"}). Rows(db.Record{"name": "赵六"}).
Executor().Exec() Executor().Exec()
@@ -684,7 +684,7 @@ func (t *hooksTest) TestUseTx_ReusesCallerProvidedTx() {
// 这里不再有第二次 ExpectBegin:证明 UseTx 发现 dp.tx 已经非 nil 就不会再自己开一个 // 这里不再有第二次 ExpectBegin:证明 UseTx 发现 dp.tx 已经非 nil 就不会再自己开一个
mock.ExpectExec("INSERT").WillReturnResult(sqlmock.NewResult(1, 1)) 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": "赵六"}). Rows(db.Record{"name": "赵六"}).
Executor().Exec() Executor().Exec()
t.NoError(err) t.NoError(err)
+11 -11
View File
@@ -4,11 +4,11 @@ import (
"strings" "strings"
"testing" "testing"
"git.fsdpf.net/go/contracts/base"
"git.fsdpf.net/go/db" "git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/engine" "git.fsdpf.net/go/db/engine"
"git.fsdpf.net/go/req" "git.fsdpf.net/go/req"
"git.fsdpf.net/go/req/resx" "git.fsdpf.net/go/req/resx"
"git.fsdpf.net/go/req/userx"
"github.com/DATA-DOG/go-sqlmock" "github.com/DATA-DOG/go-sqlmock"
"github.com/samber/do/v2" "github.com/samber/do/v2"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
@@ -56,7 +56,7 @@ func (t *normalizeTest) TestStripsSystemFields() {
resx.NewResField("name", "", resx.FieldWithName("姓名"), resx.FieldWithDataType(req.ResString)), 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{ db.Record{
"id": 999, "id": 999,
"name": "张三", "name": "张三",
@@ -83,7 +83,7 @@ func (t *normalizeTest) TestDropsUndeclaredFields() {
resx.NewResField("name", "", resx.FieldWithName("姓名"), resx.FieldWithDataType(req.ResString)), 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"}, db.Record{"name": "李四", "hacker_field": "evil"},
).Executor().ToSQL() ).Executor().ToSQL()
@@ -99,7 +99,7 @@ func (t *normalizeTest) TestExpressionValuePassesThroughEvenIfUndeclared() {
resx.NewResField("name", "", resx.FieldWithName("姓名"), resx.FieldWithDataType(req.ResString)), 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()")}, db.Record{"name": "王五", "login_at": db.L("NOW()")},
).Executor().ToSQL() ).Executor().ToSQL()
@@ -114,7 +114,7 @@ func (t *normalizeTest) TestJSONFieldNilUsesFieldDefault() {
resx.NewResField("fields", "", resx.FieldWithName("字段"), resx.FieldWithDefault("[]"), resx.FieldWithDataType(req.ResJson)), 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}, db.Record{"name": "A", "fields": nil},
).Executor().ToSQL() ).Executor().ToSQL()
@@ -131,7 +131,7 @@ func (t *normalizeTest) TestJSONFieldDefaultFilledWhenOmitted() {
resx.NewResField("fields", "", resx.FieldWithName("字段"), resx.FieldWithDefault("{}"), resx.FieldWithDataType(req.ResJson)), 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"}, db.Record{"name": "B"},
).Executor().ToSQL() ).Executor().ToSQL()
@@ -146,7 +146,7 @@ func (t *normalizeTest) TestNonJSONFieldDefaultNotAutoFilled() {
resx.NewResField("name", "", resx.FieldWithName("姓名"), resx.FieldWithDefault("匿名"), resx.FieldWithDataType(req.ResString)), 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{}, db.Record{},
).Executor().ToSQL() ).Executor().ToSQL()
@@ -161,7 +161,7 @@ func (t *normalizeTest) TestOwnedUserDefaultsToCurrentUserWhenUndeclared() {
resx.NewResField("name", "", resx.FieldWithName("姓名"), resx.FieldWithDataType(req.ResString)), 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"}, db.Record{"name": "C", "owned_user": "someone-else-uuid"},
).Executor().ToSQL() ).Executor().ToSQL()
@@ -178,7 +178,7 @@ func (t *normalizeTest) TestOwnedUserPreservedWhenDeclaredAsField() {
resx.NewResField("owned_user", "", resx.FieldWithName("拥有者"), resx.FieldWithDataType(req.ResString)), 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"}, db.Record{"name": "D", "owned_user": "someone-else-uuid"},
).Executor().ToSQL() ).Executor().ToSQL()
@@ -193,7 +193,7 @@ func (t *normalizeTest) TestCreatedUserAlwaysForcedToCurrentUser() {
resx.NewResField("created_user", "", resx.FieldWithName("创建者"), resx.FieldWithDataType(req.ResString)), 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"}, db.Record{"name": "E", "created_user": "someone-else-uuid"},
).Executor().ToSQL() ).Executor().ToSQL()
@@ -212,7 +212,7 @@ func (t *normalizeTest) TestNormalizeSaveValue_Update() {
resx.NewResField("fields", "", resx.FieldWithName("字段"), resx.FieldWithDefault("{}"), resx.FieldWithDataType(req.ResJson)), 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{ Set(db.Record{
"id": 1, "id": 1,
"name": "F", "name": "F",
+4 -4
View File
@@ -3,11 +3,11 @@ package resx_test
import ( import (
"testing" "testing"
"git.fsdpf.net/go/contracts/base"
"git.fsdpf.net/go/db" "git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/engine" "git.fsdpf.net/go/db/engine"
"git.fsdpf.net/go/req" "git.fsdpf.net/go/req"
"git.fsdpf.net/go/req/resx" "git.fsdpf.net/go/req/resx"
"git.fsdpf.net/go/req/userx"
"github.com/DATA-DOG/go-sqlmock" "github.com/DATA-DOG/go-sqlmock"
"github.com/samber/do/v2" "github.com/samber/do/v2"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
@@ -56,7 +56,7 @@ func (t *resourceTest) TestInsertRows() {
result := "INSERT INTO `users` (`created_user`, `fields`, `name`, `owned_user`) " + result := "INSERT INTO `users` (`created_user`, `fields`, `name`, `owned_user`) " +
"VALUES ('00000000-0000-0000-0000-000000000000', '{\\\"aa\\\":1}', '张三', '00000000-0000-0000-0000-000000000000')" "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}}, db.Record{"name": "张三", "id": 1, "fields": map[string]any{"aa": 1}},
).Executor().ToSQL() ).Executor().ToSQL()
t.Equal(result, sql) t.Equal(result, sql)
@@ -64,7 +64,7 @@ func (t *resourceTest) TestInsertRows() {
// 注意:db-v2 的 exp.NewRecordFromStruct 传入指针 struct 时会 panic // 注意:db-v2 的 exp.NewRecordFromStruct 传入指针 struct 时会 panic
// util.SafeGetFieldByIndex 未对指针做 reflect.Indirect),此处暂不测试指针形式 // 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}}, Temp{Name: "张三", Fields: map[string]any{"aa": 1}},
).Executor().ToSQL() ).Executor().ToSQL()
t.Equal(result, sql) 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\\\":1}', '00000000-0000-0000-0000-000000000000', '00000000-0000-0000-0000-000000000000'), " +
"('王五', '{\\\"aa\\\":3}', '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": 1}},
db.Vals{"王五", map[string]any{"aa": 3}}, db.Vals{"王五", map[string]any{"aa": 3}},
).Executor().ToSQL() ).Executor().ToSQL()
+79
View File
@@ -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...),
}
}