package main import ( "encoding/json" "fmt" "net/http" "net/http/httptest" "path/filepath" "strconv" "strings" "testing" "github.com/gin-gonic/gin" "github.com/glebarez/sqlite" "gorm.io/gorm" businessbiz "kra/internal/biz/business" "kra/internal/biz/system" "kra/internal/config" businessdata "kra/internal/data/business" systemdata "kra/internal/data/system" "kra/internal/server/handler" "kra/internal/server/router" businessservice "kra/internal/service/business" systemservice "kra/internal/service/system" ) type nativeUserHTTPData struct { businessHTTPData runtime *config.Store } func (d nativeUserHTTPData) Database(name string) (*gorm.DB, error) { if name == "" { return d.db, nil } return nil, fmt.Errorf("未配置") } func (d nativeUserHTTPData) DatabaseReady() bool { return true } func (d nativeUserHTTPData) Runtime() *config.Store { return d.runtime } func TestNativeUserEnableAndBusinessStatusOverHTTP(t *testing.T) { db, err := gorm.Open(sqlite.Open(filepath.Join(t.TempDir(), "native-status.db")), &gorm.Config{}) if err != nil { t.Fatal(err) } pool, _ := db.DB() t.Cleanup(func() { _ = pool.Close() }) for _, step := range append(systemdata.Migrations(), businessdata.Migrations()...) { if err := step.Migrate(db); err != nil { t.Fatal(err) } } if err := db.Exec("INSERT INTO sys_users(id,uuid,username,nick_name,authority_id,level,id_path,enable,business_status) VALUES(1,'root','root','root',888,0,'0',1,0),(2,'normal','normal','normal',901,1,'0',1,0),(3,'pending','pending','pending',903,3,'0,2',0,3),(4,'rejected','rejected','rejected',903,3,'0,2',0,2)").Error; err != nil { t.Fatal(err) } provider := nativeUserHTTPData{businessHTTPData{db}, config.NewStore(&config.Config{Admin: &config.Admin{}})} users := handler.NewUser(systemservice.NewUserService(system.NewUserUsecase(systemdata.NewUserRepo(provider)), nil), nil) accounts := handler.NewBusiness(nil, businessservice.NewAccountService(businessbiz.NewAccountUsecase(businessdata.NewAccountRepo(provider))), nil) engine := gin.New() engine.Use(func(c *gin.Context) { id := uint64(1) if raw := c.GetHeader("X-Test-User"); raw != "" { id, _ = strconv.ParseUint(raw, 10, 64) } aid := uint(903) if id == 1 { aid = 888 } c.Set("admin_claims", &system.AuthClaims{ID: uint(id), AuthorityID: aid}) c.Request = c.Request.WithContext(system.NewActorContext(c.Request.Context(), system.Actor{UserID: uint(id), AuthorityID: aid})) c.Next() }) router.RegisterUser(engine.Group(""), users) engine.GET("/sw/business/:userId", accounts.Account) server := httptest.NewServer(engine) defer server.Close() call := func(user int, method, path, body string, expected int) map[string]any { t.Helper() req, _ := http.NewRequest(method, server.URL+path, strings.NewReader(body)) req.Header.Set("Content-Type", "application/json") req.Header.Set("X-Test-User", strconv.Itoa(user)) res, err := server.Client().Do(req) if err != nil { t.Fatal(err) } defer res.Body.Close() var result map[string]any if err := json.NewDecoder(res.Body).Decode(&result); err != nil { t.Fatal(err) } if result["code"] != float64(expected) { t.Fatalf("%s %s 返回错误: %+v", method, path, result) } return result["data"].(map[string]any) } check := func(id int, status string, enable int) { t.Helper() data := call(1, "GET", fmt.Sprintf("/sw/business/%d", id), "", 0) var actual int db.Table("sys_users").Select("enable").Where("id = ?", id).Scan(&actual) if data["status"] != status || actual != enable { t.Fatalf("账号%d状态不同步: %+v enable=%d", id, data, actual) } } call(1, "PUT", "/user/setUserInfo", `{"ID":2,"nickName":"停用后","enable":2}`, 0) check(2, "1", 2) call(1, "PUT", "/user/setUserInfo", `{"ID":2,"nickName":"启用后","enable":1}`, 0) check(2, "0", 1) call(1, "PUT", "/user/setUserInfo", `{"ID":3,"nickName":"修改待审资料","enable":0}`, 0) check(3, "3", 0) call(1, "PUT", "/user/setUserInfo", `{"ID":4,"nickName":"修改驳回资料","enable":2}`, 0) check(4, "2", 2) call(1, "PUT", "/user/setUserInfo", `{"ID":3,"nickName":"审核后启用","enable":1}`, 0) check(3, "0", 1) // 模拟鉴权之后管理员又修改状态的竞争,个人资料不能用 enable 绕过审核。 call(4, "PUT", "/user/setSelfInfo", `{"enable":1,"nickName":"试图自行启用"}`, 7) check(4, "2", 2) call(2, "PUT", "/user/setSelfInfo", `{"enable":2}`, 0) check(2, "1", 2) call(2, "PUT", "/user/setSelfInfo", `{"enable":1}`, 7) check(2, "1", 2) call(1, "PUT", "/user/setUserInfo", `{"ID":99999,"enable":1}`, 7) }