From a017d93dbfe9301f9e7c1d4a63b36f6f9734864e Mon Sep 17 00:00:00 2001 From: Yvan <8574526@qq,com> Date: Wed, 7 Jan 2026 11:53:56 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BB=BB=E5=8A=A1=E4=B8=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/gen/main.go | 42 +- go.mod | 24 +- go.sum | 48 +-- internal/biz/biz.go | 7 +- internal/biz/system/user.go | 233 +++++++++++ internal/data/model/sys_users.gen.go | 2 + internal/data/query/sys_users.gen.go | 185 ++++++++- internal/data/system/user.go | 100 ++--- internal/service/service.go | 2 +- internal/service/system/user.go | 558 +++++++++++++++++++++++++++ 10 files changed, 1082 insertions(+), 119 deletions(-) create mode 100644 internal/biz/system/user.go create mode 100644 internal/service/system/user.go diff --git a/cmd/gen/main.go b/cmd/gen/main.go index 4f23349..73e11e5 100644 --- a/cmd/gen/main.go +++ b/cmd/gen/main.go @@ -7,6 +7,7 @@ import ( "gorm.io/driver/mysql" "gorm.io/gen" + "gorm.io/gen/field" "gorm.io/gorm" ) @@ -39,12 +40,45 @@ func main() { // 字段名映射,避免与方法名冲突 fieldOpts := []gen.ModelOpt{ gen.FieldRename("table_name", "TblName"), - gen.FieldRename("table", "TblName"), // 避免与Table()方法冲突 + gen.FieldRename("table", "TblName"), } - // 生成所有表的model和query - allTables := g.GenerateAllTable(fieldOpts...) - g.ApplyBasic(allTables...) + // SysUser 关联配置 + sysUserOpts := append(fieldOpts, + // Authority: belongs to SysAuthority + gen.FieldRelate(field.HasOne, "Authority", g.GenerateModel("sys_authorities"), + &field.RelateConfig{ + GORMTag: field.GormTag{ + "foreignKey": []string{"AuthorityID"}, + "references": []string{"AuthorityID"}, + }, + }), + // Authorities: many2many through sys_user_authority + gen.FieldRelate(field.Many2Many, "Authorities", g.GenerateModel("sys_authorities"), + &field.RelateConfig{ + GORMTag: field.GormTag{ + "many2many": []string{"sys_user_authority"}, + "joinForeignKey": []string{"SysUserID"}, + "joinReferences": []string{"SysAuthorityAuthorityID"}, + }, + }), + ) + + // 生成带关联的SysUser + sysUser := g.GenerateModel("sys_users", sysUserOpts...) + + // 生成其他表(不含sys_users) + tables, _ := db.Migrator().GetTables() + var models []interface{} + for _, table := range tables { + if table == "sys_users" { + continue + } + models = append(models, g.GenerateModel(table, fieldOpts...)) + } + + g.ApplyBasic(sysUser) + g.ApplyBasic(models...) // 执行生成 g.Execute() diff --git a/go.mod b/go.mod index 3f8b414..d24084f 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,6 @@ go 1.24.6 toolchain go1.24.7 require ( - entgo.io/ent v0.14.5 github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible github.com/aws/aws-sdk-go-v2 v1.41.0 github.com/aws/aws-sdk-go-v2/config v1.32.6 @@ -13,7 +12,6 @@ require ( github.com/aws/aws-sdk-go-v2/service/s3 v1.95.0 github.com/casbin/casbin/v2 v2.103.0 github.com/casbin/gorm-adapter/v3 v3.32.0 - github.com/go-kratos/aip-go/ents v0.0.0-20251213081434-74ffa1fc1588 github.com/go-kratos/kratos/v2 v2.8.0 github.com/go-sql-driver/mysql v1.8.1 github.com/golang-jwt/jwt/v5 v5.2.2 @@ -26,25 +24,26 @@ require ( github.com/redis/go-redis/v9 v9.7.0 github.com/robfig/cron/v3 v3.0.1 github.com/tencentyun/cos-go-sdk-v5 v0.7.60 + github.com/xuri/excelize/v2 v2.9.0 go.einride.tech/aip v0.78.0 go.uber.org/automaxprocs v1.6.0 + golang.org/x/crypto v0.38.0 google.golang.org/genproto/googleapis/api v0.0.0-20251213004720-97cd9d5aeac2 google.golang.org/grpc v1.74.2 google.golang.org/protobuf v1.36.10 gorm.io/driver/mysql v1.5.7 gorm.io/driver/postgres v1.5.11 gorm.io/driver/sqlite v1.6.0 + gorm.io/gen v0.3.27 gorm.io/gorm v1.30.0 + gorm.io/plugin/dbresolver v1.6.2 ) require ( - ariga.io/atlas v0.32.1-0.20250325101103-175b25e1c1b9 // indirect dario.cat/mergo v1.0.0 // indirect filippo.io/edwards25519 v1.1.0 // indirect github.com/BurntSushi/toml v1.3.2 // indirect - github.com/agext/levenshtein v1.2.3 // indirect github.com/alex-ant/gomath v0.0.0-20160516115720-89013a210a82 // indirect - github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.16 // indirect @@ -60,7 +59,6 @@ require ( github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.12 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.41.5 // indirect github.com/aws/smithy-go v1.24.0 // indirect - github.com/bmatcuk/doublestar v1.3.4 // indirect github.com/bmatcuk/doublestar/v4 v4.6.1 // indirect github.com/casbin/govaluate v1.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect @@ -75,19 +73,16 @@ require ( github.com/go-kratos/aegis v0.2.0 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-openapi/inflect v0.19.0 // indirect github.com/go-playground/form/v4 v4.2.0 // indirect github.com/goccy/go-json v0.10.4 // indirect github.com/gofrs/flock v0.8.1 // indirect github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect github.com/golang-sql/sqlexp v0.1.0 // indirect github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect - github.com/google/go-cmp v0.7.0 // indirect github.com/google/go-querystring v1.0.0 // indirect github.com/gorilla/mux v1.8.1 // indirect - github.com/hashicorp/hcl/v2 v2.18.1 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect - github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect + github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 // indirect github.com/jackc/pgx/v5 v5.5.5 // indirect github.com/jackc/puddle/v2 v2.2.1 // indirect github.com/jinzhu/inflection v1.0.0 // indirect @@ -98,7 +93,6 @@ require ( github.com/mattn/go-sqlite3 v1.14.22 // indirect github.com/microsoft/go-mssqldb v1.6.0 // indirect github.com/minio/md5-simd v1.1.2 // indirect - github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/mozillazg/go-httpheader v0.2.1 // indirect @@ -107,15 +101,11 @@ require ( github.com/richardlehane/msoleps v1.0.4 // indirect github.com/rs/xid v1.6.0 // indirect github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d // indirect - github.com/xuri/excelize/v2 v2.9.0 // indirect github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7 // indirect - github.com/zclconf/go-cty v1.14.4 // indirect - github.com/zclconf/go-cty-yaml v1.1.0 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/otel v1.36.0 // indirect go.opentelemetry.io/otel/metric v1.36.0 // indirect go.opentelemetry.io/otel/trace v1.36.0 // indirect - golang.org/x/crypto v0.38.0 // indirect golang.org/x/image v0.23.0 // indirect golang.org/x/mod v0.23.0 // indirect golang.org/x/net v0.40.0 // indirect @@ -123,10 +113,12 @@ require ( golang.org/x/sys v0.33.0 // indirect golang.org/x/text v0.25.0 // indirect golang.org/x/time v0.14.0 // indirect + golang.org/x/tools v0.30.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + gorm.io/datatypes v1.2.4 // indirect gorm.io/driver/sqlserver v1.5.3 // indirect - gorm.io/plugin/dbresolver v1.6.2 // indirect + gorm.io/hints v1.1.0 // indirect modernc.org/fileutil v1.0.0 // indirect modernc.org/libc v1.22.5 // indirect modernc.org/mathutil v1.5.0 // indirect diff --git a/go.sum b/go.sum index df61463..73681aa 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,7 @@ -ariga.io/atlas v0.32.1-0.20250325101103-175b25e1c1b9 h1:E0wvcUXTkgyN4wy4LGtNzMNGMytJN8afmIWXJVMi4cc= -ariga.io/atlas v0.32.1-0.20250325101103-175b25e1c1b9/go.mod h1:Oe1xWPuu5q9LzyrWfbZmEZxFYeu4BHTyzfjeW2aZp/w= cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY= cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= -entgo.io/ent v0.14.5 h1:Rj2WOYJtCkWyFo6a+5wB3EfBRP0rnx1fMk6gGA0UUe4= -entgo.io/ent v0.14.5/go.mod h1:zTzLmWtPvGpmSwtkaayM2cm5m819NdM7z7tYPq3vN0U= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.4.0/go.mod h1:ON4tFdPTwRcgWEaVDrN3584Ef+b7GgSJaXxe5fW9t4M= @@ -28,17 +24,11 @@ github.com/AzureAD/microsoft-authentication-library-for-go v1.1.0 h1:HCc0+LpPfpC github.com/AzureAD/microsoft-authentication-library-for-go v1.1.0/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= -github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM= -github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= -github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/alex-ant/gomath v0.0.0-20160516115720-89013a210a82 h1:7dONQ3WNZ1zy960TmkxJPuwoolZwL7xKtpcM04MBnt4= github.com/alex-ant/gomath v0.0.0-20160516115720-89013a210a82/go.mod h1:nLnM0KdK1CmygvjpDUO6m1TjSsiQtL61juhNsvV/JVI= github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible h1:8psS8a+wKfiLt1iVDX79F7Y6wUM49Lcha2FMXt4UM8g= github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= -github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY= -github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4= github.com/aws/aws-sdk-go-v2 v1.41.0 h1:tNvqh1s+v0vFYdA1xq0aOJH+Y5cRyZ5upu6roPgPKd4= github.com/aws/aws-sdk-go-v2 v1.41.0/go.mod h1:MayyLB8y+buD9hZqkCW3kX1AKq07Y5pXxtgB+rRFhz0= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4 h1:489krEF9xIGkOaaX3CE/Be2uWjiXrkCH6gUX+bZA/BU= @@ -77,8 +67,6 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.41.5 h1:SciGFVNZ4mHdm7gpD1dgZYnCuVdX github.com/aws/aws-sdk-go-v2/service/sts v1.41.5/go.mod h1:iW40X4QBmUxdP+fZNOpfmkdMZqsovezbAeO+Ubiv2pk= github.com/aws/smithy-go v1.24.0 h1:LpilSUItNPFr1eY85RYgTIg5eIEPtvFbskaFcmmIUnk= github.com/aws/smithy-go v1.24.0/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0= -github.com/bmatcuk/doublestar v1.3.4 h1:gPypJ5xD31uhX6Tf54sDPUOBXTqKH4c9aPY66CyQrS0= -github.com/bmatcuk/doublestar v1.3.4/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE= github.com/bmatcuk/doublestar/v4 v4.6.1 h1:FH9SifrbvJhnlQpztAx++wlkk70QBf0iBWDwNy7PA4I= github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= @@ -125,8 +113,6 @@ github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-kratos/aegis v0.2.0 h1:dObzCDWn3XVjUkgxyBp6ZeWtx/do0DPZ7LY3yNSJLUQ= github.com/go-kratos/aegis v0.2.0/go.mod h1:v0R2m73WgEEYB3XYu6aE2WcMwsZkJ/Rzuf5eVccm7bI= -github.com/go-kratos/aip-go/ents v0.0.0-20251213081434-74ffa1fc1588 h1:e0dWyNWFeTgGCH7cRMROahTwMaQYtmHce/6fxVmA6yI= -github.com/go-kratos/aip-go/ents v0.0.0-20251213081434-74ffa1fc1588/go.mod h1:ifKMm4eJmaQz5WNKKhfClpCng8UxUB3sArof8wjwG78= github.com/go-kratos/kratos/v2 v2.8.0 h1:qr27WRTRrI3o4jzJzNKf4XVVoMYIqnQD+4ws1C46yhM= github.com/go-kratos/kratos/v2 v2.8.0/go.mod h1:+Vfe3FzF0d+BfMdajA11jT0rAyJWublRE/seZQNZVxE= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -134,8 +120,6 @@ github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-openapi/inflect v0.19.0 h1:9jCH9scKIbHeV9m12SmPilScz6krDxKRasNNSNPXu/4= -github.com/go-openapi/inflect v0.19.0/go.mod h1:lHpZVlpIQqLyKwJ4N+YSc9hchQy/i12fJykb83CRBH4= github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/form/v4 v4.2.0 h1:N1wh+Goz61e6w66vo8vJkQt+uwZSoLz50kZPJWR8eic= @@ -148,8 +132,6 @@ github.com/go-playground/validator/v10 v10.7.0/go.mod h1:xm76BBt941f7yWdGnI2DVPF github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= -github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= -github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM= github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= @@ -191,15 +173,13 @@ github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+ github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/hcl/v2 v2.18.1 h1:6nxnOJFku1EuSawSD81fuviYUV8DxFr3fp2dUi3ZYSo= -github.com/hashicorp/hcl/v2 v2.18.1/go.mod h1:ThLC89FV4p9MPW804KVbe/cEXoQ8NZEh+JtMeeGErHE= github.com/huaweicloud/huaweicloud-sdk-go-obs v3.24.9+incompatible h1:XQVXdk+WAJ4fSNB6mMRuYNvFWou7BZs6SZB925hPrnk= github.com/huaweicloud/huaweicloud-sdk-go-obs v3.24.9+incompatible/go.mod h1:l7VUhRbTKCzdOacdT4oWCwATKyvZqUOlOqr0Ous3k4s= github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA= +github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw= github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= @@ -213,6 +193,7 @@ github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJk github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= @@ -237,6 +218,7 @@ github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/microsoft/go-mssqldb v1.6.0 h1:mM3gYdVwEPFrlg/Dvr2DNVEgYFG7L42l+dGc67NNNpc= @@ -245,8 +227,6 @@ github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= github.com/minio/minio-go/v7 v7.0.84 h1:D1HVmAF8JF8Bpi6IU4V9vIEj+8pc+xU88EWMs2yed0E= github.com/minio/minio-go/v7 v7.0.84/go.mod h1:57YXpvc5l3rjPdhqNrDsvVlY0qPI6UTk1bflAe+9doY= -github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= -github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= @@ -289,8 +269,6 @@ github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= -github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= -github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -316,10 +294,6 @@ github.com/xuri/excelize/v2 v2.9.0/go.mod h1:uqey4QBZ9gdMeWApPLdhm9x+9o2lq4iVmji github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7 h1:hPVCafDV85blFTabnqKgNhDCkJX25eik94Si9cTER4A= github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/zclconf/go-cty v1.14.4 h1:uXXczd9QDGsgu0i/QFR/hzI5NYCHLf6NQw/atrbnhq8= -github.com/zclconf/go-cty v1.14.4/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= -github.com/zclconf/go-cty-yaml v1.1.0 h1:nP+jp0qPHv2IhUVqmQSzjvqAWcObN0KBkUl2rWBdig0= -github.com/zclconf/go-cty-yaml v1.1.0/go.mod h1:9YLUH4g7lOhVWqUbctnVlZ5KLpg7JAprQNgxSZ1Gyxs= go.einride.tech/aip v0.78.0 h1:nbnM/OuGt5Pyz/r2KOxB6Hp+ey2e0+MNnfIPBtY45pY= go.einride.tech/aip v0.78.0/go.mod h1:E8+wdTApA70odnpFzJgsGogHozC2JCIhFJBKPr8bVig= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= @@ -442,6 +416,8 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY= +golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/genproto v0.0.0-20240711142825-46eb208f015d h1:/hmn0Ku5kWij/kjGsrcJeC1T/MrJi2iNWwgAqrihFwc= google.golang.org/genproto v0.0.0-20240711142825-46eb208f015d/go.mod h1:FfBgJBJg9GcpPvKIuHSZ/aE1g2ecGL74upMzGZjiGEY= @@ -466,21 +442,27 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/datatypes v1.2.4 h1:uZmGAcK/QZ0uyfCuVg0VQY1ZmV9h1fuG0tMwKByO1z4= +gorm.io/datatypes v1.2.4/go.mod h1:f4BsLcFAX67szSv8svwLRjklArSHAvHLeE3pXAS5DZI= gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo= gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM= gorm.io/driver/postgres v1.5.11 h1:ubBVAfbKEUld/twyKZ0IYn9rSQh448EdelLYk9Mv314= gorm.io/driver/postgres v1.5.11/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI= +gorm.io/driver/sqlite v1.1.6/go.mod h1:W8LmC/6UvVbHKah0+QOC7Ja66EaZXHwUTjgXY8YNWX8= gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ= gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8= gorm.io/driver/sqlserver v1.5.3 h1:rjupPS4PVw+rjJkfvr8jn2lJ8BMhT4UW5FwuJY0P3Z0= gorm.io/driver/sqlserver v1.5.3/go.mod h1:B+CZ0/7oFJ6tAlefsKoyxdgDCXJKSgwS2bMOQZT0I00= +gorm.io/gen v0.3.27 h1:ziocAFLpE7e0g4Rum69pGfB9S6DweTxK8gAun7cU8as= +gorm.io/gen v0.3.27/go.mod h1:9zquz2xD1f3Eb/eHq4oLn2z6vDVvQlCY5S3uMBLv4EA= +gorm.io/gorm v1.21.15/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= +gorm.io/gorm v1.22.2/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= gorm.io/gorm v1.25.7-0.20240204074919-46816ad31dde/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= -gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ= gorm.io/gorm v1.30.0 h1:qbT5aPv1UH8gI99OsRlvDToLxW5zR7FzS9acZDOZcgs= gorm.io/gorm v1.30.0/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE= -gorm.io/plugin/dbresolver v1.5.3 h1:wFwINGZZmttuu9h7XpvbDHd8Lf9bb8GNzp/NpAMV2wU= -gorm.io/plugin/dbresolver v1.5.3/go.mod h1:TSrVhaUg2DZAWP3PrHlDlITEJmNOkL0tFTjvTEsQ4XE= +gorm.io/hints v1.1.0 h1:Lp4z3rxREufSdxn4qmkK3TLDltrM10FLTHiuqwDPvXw= +gorm.io/hints v1.1.0/go.mod h1:lKQ0JjySsPBj3uslFzY3JhYDtqEwzm+G1hv8rWujB6Y= gorm.io/plugin/dbresolver v1.6.2 h1:F4b85TenghUeITqe3+epPSUtHH7RIk3fXr5l83DF8Pc= gorm.io/plugin/dbresolver v1.6.2/go.mod h1:tctw63jdrOezFR9HmrKnPkmig3m5Edem9fdxk9bQSzM= gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= diff --git a/internal/biz/biz.go b/internal/biz/biz.go index 9eb395f..ad9111b 100644 --- a/internal/biz/biz.go +++ b/internal/biz/biz.go @@ -1,6 +1,9 @@ package biz -import "github.com/google/wire" +import ( + "github.com/google/wire" + "kra/internal/biz/system" +) // ProviderSet is biz providers. -var ProviderSet = wire.NewSet(NewAdminUsecase) +var ProviderSet = wire.NewSet(system.NewUserUsecase) diff --git a/internal/biz/system/user.go b/internal/biz/system/user.go new file mode 100644 index 0000000..cab0c75 --- /dev/null +++ b/internal/biz/system/user.go @@ -0,0 +1,233 @@ +package system + +import ( + "context" + "encoding/json" + "errors" + "time" + + "github.com/google/uuid" + "golang.org/x/crypto/bcrypt" +) + +var ( + ErrUserNotFound = errors.New("用户不存在") + ErrUserAlreadyExists = errors.New("用户名已注册") + ErrPasswordWrong = errors.New("密码错误") + ErrOldPasswordWrong = errors.New("原密码错误") + ErrUserNoAuthority = errors.New("该用户无此角色") + ErrDefaultRouterEmpty = errors.New("找不到默认路由,无法切换本角色") +) + +// User 用户实体 +type User struct { + ID uint + UUID uuid.UUID + Username string + Password string + NickName string + SideMode string + HeaderImg string + BaseColor string + AuthorityId uint + Phone string + Email string + Enable int + OriginSetting json.RawMessage + Authority *Authority + Authorities []*Authority + CreatedAt time.Time + UpdatedAt time.Time +} + +// Authority 角色实体 +type Authority struct { + AuthorityId uint + AuthorityName string + ParentId *uint + DefaultRouter string +} + +// UserRepo 用户仓储接口 +type UserRepo interface { + Create(ctx context.Context, user *User) error + Update(ctx context.Context, user *User) error + UpdateSelf(ctx context.Context, user *User) error + UpdateSelfSetting(ctx context.Context, id uint, setting json.RawMessage) error + Delete(ctx context.Context, id uint) error + FindByID(ctx context.Context, id uint) (*User, error) + FindByIDWithAuthorities(ctx context.Context, id uint) (*User, error) + FindByUUID(ctx context.Context, uuid string) (*User, error) + FindByUUIDWithAuthorities(ctx context.Context, uuid string) (*User, error) + FindByUsername(ctx context.Context, username string) (*User, error) + FindByUsernameWithAuthorities(ctx context.Context, username string) (*User, error) + List(ctx context.Context, page, pageSize int, filters map[string]string) ([]*User, int64, error) + UpdatePassword(ctx context.Context, id uint, password string) error + UpdateAuthorityID(ctx context.Context, id uint, authorityId uint) error + SetUserAuthorities(ctx context.Context, adminAuthorityID, userId uint, authorityIds []uint) error + CheckUserHasAuthority(ctx context.Context, userId, authorityId uint) (bool, error) + GetAuthorityMenuRouters(ctx context.Context, authorityId uint) ([]string, error) + GetAuthorityDefaultRouter(ctx context.Context, authorityId uint) (string, error) +} + +// UserUsecase 用户用例 +type UserUsecase struct { + repo UserRepo +} + +// NewUserUsecase 创建用户用例 +func NewUserUsecase(repo UserRepo) *UserUsecase { + return &UserUsecase{repo: repo} +} + +// Register 用户注册 +func (uc *UserUsecase) Register(ctx context.Context, user *User) (*User, error) { + // 检查用户名是否已存在 + existing, _ := uc.repo.FindByUsername(ctx, user.Username) + if existing != nil { + return nil, ErrUserAlreadyExists + } + + // 密码加密 + hashedPassword, err := bcrypt.GenerateFromPassword([]byte(user.Password), bcrypt.DefaultCost) + if err != nil { + return nil, err + } + user.Password = string(hashedPassword) + user.UUID = uuid.New() + + if err := uc.repo.Create(ctx, user); err != nil { + return nil, err + } + return user, nil +} + +// Login 用户登录(预加载Authorities) +func (uc *UserUsecase) Login(ctx context.Context, username, password string) (*User, error) { + user, err := uc.repo.FindByUsernameWithAuthorities(ctx, username) + if err != nil { + return nil, ErrUserNotFound + } + + if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)); err != nil { + return nil, ErrPasswordWrong + } + + return user, nil +} + +// ChangePassword 修改密码 +func (uc *UserUsecase) ChangePassword(ctx context.Context, id uint, oldPassword, newPassword string) error { + user, err := uc.repo.FindByID(ctx, id) + if err != nil { + return ErrUserNotFound + } + + if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(oldPassword)); err != nil { + return ErrOldPasswordWrong + } + + hashedPassword, err := bcrypt.GenerateFromPassword([]byte(newPassword), bcrypt.DefaultCost) + if err != nil { + return err + } + + return uc.repo.UpdatePassword(ctx, id, string(hashedPassword)) +} + +// ResetPassword 重置密码 +func (uc *UserUsecase) ResetPassword(ctx context.Context, id uint, newPassword string) error { + hashedPassword, err := bcrypt.GenerateFromPassword([]byte(newPassword), bcrypt.DefaultCost) + if err != nil { + return err + } + return uc.repo.UpdatePassword(ctx, id, string(hashedPassword)) +} + +// GetUserInfo 获取用户信息(预加载Authorities) +func (uc *UserUsecase) GetUserInfo(ctx context.Context, uuid string) (*User, error) { + return uc.repo.FindByUUIDWithAuthorities(ctx, uuid) +} + +// GetUserByID 通过ID获取用户 +func (uc *UserUsecase) GetUserByID(ctx context.Context, id uint) (*User, error) { + return uc.repo.FindByID(ctx, id) +} + +// FindUserById 通过ID获取用户(GVA兼容) +func (uc *UserUsecase) FindUserById(ctx context.Context, id uint) (*User, error) { + return uc.repo.FindByID(ctx, id) +} + +// FindUserByUuid 通过UUID获取用户(GVA兼容) +func (uc *UserUsecase) FindUserByUuid(ctx context.Context, uuid string) (*User, error) { + return uc.repo.FindByUUID(ctx, uuid) +} + +// GetUserList 获取用户列表 +func (uc *UserUsecase) GetUserList(ctx context.Context, page, pageSize int, filters map[string]string) ([]*User, int64, error) { + return uc.repo.List(ctx, page, pageSize, filters) +} + +// SetUserInfo 设置用户信息(管理员用) +func (uc *UserUsecase) SetUserInfo(ctx context.Context, user *User) error { + return uc.repo.Update(ctx, user) +} + +// SetSelfInfo 设置自己的信息(用户用) +func (uc *UserUsecase) SetSelfInfo(ctx context.Context, user *User) error { + return uc.repo.UpdateSelf(ctx, user) +} + +// SetSelfSetting 设置自己的配置 +func (uc *UserUsecase) SetSelfSetting(ctx context.Context, id uint, setting json.RawMessage) error { + return uc.repo.UpdateSelfSetting(ctx, id, setting) +} + +// DeleteUser 删除用户 +func (uc *UserUsecase) DeleteUser(ctx context.Context, id uint) error { + return uc.repo.Delete(ctx, id) +} + +// SetUserAuthority 设置用户角色(切换角色) +func (uc *UserUsecase) SetUserAuthority(ctx context.Context, id uint, authorityId uint) error { + // 检查用户是否拥有该角色 + hasAuthority, err := uc.repo.CheckUserHasAuthority(ctx, id, authorityId) + if err != nil { + return err + } + if !hasAuthority { + return ErrUserNoAuthority + } + + // 获取角色的默认路由 + defaultRouter, err := uc.repo.GetAuthorityDefaultRouter(ctx, authorityId) + if err != nil { + return err + } + + // 获取角色的菜单路由列表 + menuRouters, err := uc.repo.GetAuthorityMenuRouters(ctx, authorityId) + if err != nil { + return err + } + + // 检查默认路由是否在菜单中 + hasMenu := false + for _, router := range menuRouters { + if router == defaultRouter { + hasMenu = true + break + } + } + if !hasMenu { + return ErrDefaultRouterEmpty + } + + return uc.repo.UpdateAuthorityID(ctx, id, authorityId) +} + +// SetUserAuthorities 设置用户多角色 +func (uc *UserUsecase) SetUserAuthorities(ctx context.Context, adminAuthorityID, userId uint, authorityIds []uint) error { + return uc.repo.SetUserAuthorities(ctx, adminAuthorityID, userId, authorityIds) +} diff --git a/internal/data/model/sys_users.gen.go b/internal/data/model/sys_users.gen.go index 0b56a17..0a1a0b5 100644 --- a/internal/data/model/sys_users.gen.go +++ b/internal/data/model/sys_users.gen.go @@ -28,6 +28,8 @@ type SysUser struct { Email *string `gorm:"column:email;type:varchar(191);comment:用户邮箱" json:"email"` // 用户邮箱 Enable *int64 `gorm:"column:enable;type:bigint(20);default:1;comment:用户是否被冻结 1正常 2冻结" json:"enable"` // 用户是否被冻结 1正常 2冻结 OriginSetting *string `gorm:"column:origin_setting;type:text;comment:配置" json:"origin_setting"` // 配置 + Authority SysAuthority `gorm:"foreignKey:AuthorityID;references:AuthorityID" json:"authority"` + Authorities []SysAuthority `gorm:"joinForeignKey:SysUserID;joinReferences:SysAuthorityAuthorityID;many2many:sys_user_authority" json:"authorities"` } // TableName SysUser's table name diff --git a/internal/data/query/sys_users.gen.go b/internal/data/query/sys_users.gen.go index b6dba80..ef6cf46 100644 --- a/internal/data/query/sys_users.gen.go +++ b/internal/data/query/sys_users.gen.go @@ -42,6 +42,17 @@ func newSysUser(db *gorm.DB, opts ...gen.DOOption) sysUser { _sysUser.Email = field.NewString(tableName, "email") _sysUser.Enable = field.NewInt64(tableName, "enable") _sysUser.OriginSetting = field.NewString(tableName, "origin_setting") + _sysUser.Authority = sysUserHasOneAuthority{ + db: db.Session(&gorm.Session{}), + + RelationField: field.NewRelation("Authority", "model.SysAuthority"), + } + + _sysUser.Authorities = sysUserManyToManyAuthorities{ + db: db.Session(&gorm.Session{}), + + RelationField: field.NewRelation("Authorities", "model.SysAuthority"), + } _sysUser.fillFieldMap() @@ -66,6 +77,9 @@ type sysUser struct { Email field.String // 用户邮箱 Enable field.Int64 // 用户是否被冻结 1正常 2冻结 OriginSetting field.String // 配置 + Authority sysUserHasOneAuthority + + Authorities sysUserManyToManyAuthorities fieldMap map[string]field.Expr } @@ -120,7 +134,7 @@ func (s *sysUser) GetFieldByName(fieldName string) (field.OrderExpr, bool) { } func (s *sysUser) fillFieldMap() { - s.fieldMap = make(map[string]field.Expr, 14) + s.fieldMap = make(map[string]field.Expr, 16) s.fieldMap["id"] = s.ID s.fieldMap["created_at"] = s.CreatedAt s.fieldMap["updated_at"] = s.UpdatedAt @@ -135,18 +149,187 @@ func (s *sysUser) fillFieldMap() { s.fieldMap["email"] = s.Email s.fieldMap["enable"] = s.Enable s.fieldMap["origin_setting"] = s.OriginSetting + } func (s sysUser) clone(db *gorm.DB) sysUser { s.sysUserDo.ReplaceConnPool(db.Statement.ConnPool) + s.Authority.db = db.Session(&gorm.Session{Initialized: true}) + s.Authority.db.Statement.ConnPool = db.Statement.ConnPool + s.Authorities.db = db.Session(&gorm.Session{Initialized: true}) + s.Authorities.db.Statement.ConnPool = db.Statement.ConnPool return s } func (s sysUser) replaceDB(db *gorm.DB) sysUser { s.sysUserDo.ReplaceDB(db) + s.Authority.db = db.Session(&gorm.Session{}) + s.Authorities.db = db.Session(&gorm.Session{}) return s } +type sysUserHasOneAuthority struct { + db *gorm.DB + + field.RelationField +} + +func (a sysUserHasOneAuthority) Where(conds ...field.Expr) *sysUserHasOneAuthority { + if len(conds) == 0 { + return &a + } + + exprs := make([]clause.Expression, 0, len(conds)) + for _, cond := range conds { + exprs = append(exprs, cond.BeCond().(clause.Expression)) + } + a.db = a.db.Clauses(clause.Where{Exprs: exprs}) + return &a +} + +func (a sysUserHasOneAuthority) WithContext(ctx context.Context) *sysUserHasOneAuthority { + a.db = a.db.WithContext(ctx) + return &a +} + +func (a sysUserHasOneAuthority) Session(session *gorm.Session) *sysUserHasOneAuthority { + a.db = a.db.Session(session) + return &a +} + +func (a sysUserHasOneAuthority) Model(m *model.SysUser) *sysUserHasOneAuthorityTx { + return &sysUserHasOneAuthorityTx{a.db.Model(m).Association(a.Name())} +} + +func (a sysUserHasOneAuthority) Unscoped() *sysUserHasOneAuthority { + a.db = a.db.Unscoped() + return &a +} + +type sysUserHasOneAuthorityTx struct{ tx *gorm.Association } + +func (a sysUserHasOneAuthorityTx) Find() (result *model.SysAuthority, err error) { + return result, a.tx.Find(&result) +} + +func (a sysUserHasOneAuthorityTx) Append(values ...*model.SysAuthority) (err error) { + targetValues := make([]interface{}, len(values)) + for i, v := range values { + targetValues[i] = v + } + return a.tx.Append(targetValues...) +} + +func (a sysUserHasOneAuthorityTx) Replace(values ...*model.SysAuthority) (err error) { + targetValues := make([]interface{}, len(values)) + for i, v := range values { + targetValues[i] = v + } + return a.tx.Replace(targetValues...) +} + +func (a sysUserHasOneAuthorityTx) Delete(values ...*model.SysAuthority) (err error) { + targetValues := make([]interface{}, len(values)) + for i, v := range values { + targetValues[i] = v + } + return a.tx.Delete(targetValues...) +} + +func (a sysUserHasOneAuthorityTx) Clear() error { + return a.tx.Clear() +} + +func (a sysUserHasOneAuthorityTx) Count() int64 { + return a.tx.Count() +} + +func (a sysUserHasOneAuthorityTx) Unscoped() *sysUserHasOneAuthorityTx { + a.tx = a.tx.Unscoped() + return &a +} + +type sysUserManyToManyAuthorities struct { + db *gorm.DB + + field.RelationField +} + +func (a sysUserManyToManyAuthorities) Where(conds ...field.Expr) *sysUserManyToManyAuthorities { + if len(conds) == 0 { + return &a + } + + exprs := make([]clause.Expression, 0, len(conds)) + for _, cond := range conds { + exprs = append(exprs, cond.BeCond().(clause.Expression)) + } + a.db = a.db.Clauses(clause.Where{Exprs: exprs}) + return &a +} + +func (a sysUserManyToManyAuthorities) WithContext(ctx context.Context) *sysUserManyToManyAuthorities { + a.db = a.db.WithContext(ctx) + return &a +} + +func (a sysUserManyToManyAuthorities) Session(session *gorm.Session) *sysUserManyToManyAuthorities { + a.db = a.db.Session(session) + return &a +} + +func (a sysUserManyToManyAuthorities) Model(m *model.SysUser) *sysUserManyToManyAuthoritiesTx { + return &sysUserManyToManyAuthoritiesTx{a.db.Model(m).Association(a.Name())} +} + +func (a sysUserManyToManyAuthorities) Unscoped() *sysUserManyToManyAuthorities { + a.db = a.db.Unscoped() + return &a +} + +type sysUserManyToManyAuthoritiesTx struct{ tx *gorm.Association } + +func (a sysUserManyToManyAuthoritiesTx) Find() (result []*model.SysAuthority, err error) { + return result, a.tx.Find(&result) +} + +func (a sysUserManyToManyAuthoritiesTx) Append(values ...*model.SysAuthority) (err error) { + targetValues := make([]interface{}, len(values)) + for i, v := range values { + targetValues[i] = v + } + return a.tx.Append(targetValues...) +} + +func (a sysUserManyToManyAuthoritiesTx) Replace(values ...*model.SysAuthority) (err error) { + targetValues := make([]interface{}, len(values)) + for i, v := range values { + targetValues[i] = v + } + return a.tx.Replace(targetValues...) +} + +func (a sysUserManyToManyAuthoritiesTx) Delete(values ...*model.SysAuthority) (err error) { + targetValues := make([]interface{}, len(values)) + for i, v := range values { + targetValues[i] = v + } + return a.tx.Delete(targetValues...) +} + +func (a sysUserManyToManyAuthoritiesTx) Clear() error { + return a.tx.Clear() +} + +func (a sysUserManyToManyAuthoritiesTx) Count() int64 { + return a.tx.Count() +} + +func (a sysUserManyToManyAuthoritiesTx) Unscoped() *sysUserManyToManyAuthoritiesTx { + a.tx = a.tx.Unscoped() + return &a +} + type sysUserDo struct{ gen.DO } type ISysUserDo interface { diff --git a/internal/data/system/user.go b/internal/data/system/user.go index 316d628..a0d54f7 100644 --- a/internal/data/system/user.go +++ b/internal/data/system/user.go @@ -73,11 +73,9 @@ func (r *userRepo) UpdateSelfSetting(ctx context.Context, id uint, setting json. func (r *userRepo) Delete(ctx context.Context, id uint) error { return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { - // 删除用户 if err := tx.Where("id = ?", id).Delete(&model.SysUser{}).Error; err != nil { return err } - // 删除用户角色关联 if err := tx.Where("sys_user_id = ?", id).Delete(&model.SysUserAuthority{}).Error; err != nil { return err } @@ -94,16 +92,15 @@ func (r *userRepo) FindByID(ctx context.Context, id uint) (*system.User, error) } func (r *userRepo) FindByIDWithAuthorities(ctx context.Context, id uint) (*system.User, error) { - var m model.SysUser - err := r.db.WithContext(ctx). - Preload("Authorities"). - Preload("Authority"). - Where("id = ?", id). - First(&m).Error + m, err := query.SysUser.WithContext(ctx). + Preload(query.SysUser.Authority). + Preload(query.SysUser.Authorities). + Where(query.SysUser.ID.Eq(int64(id))). + First() if err != nil { return nil, err } - return toModelUserWithAuthorities(&m), nil + return toModelUserWithAuthorities(m), nil } func (r *userRepo) FindByUUID(ctx context.Context, uuidStr string) (*system.User, error) { @@ -115,16 +112,15 @@ func (r *userRepo) FindByUUID(ctx context.Context, uuidStr string) (*system.User } func (r *userRepo) FindByUUIDWithAuthorities(ctx context.Context, uuidStr string) (*system.User, error) { - var m model.SysUser - err := r.db.WithContext(ctx). - Preload("Authorities"). - Preload("Authority"). - Where("uuid = ?", uuidStr). - First(&m).Error + m, err := query.SysUser.WithContext(ctx). + Preload(query.SysUser.Authority). + Preload(query.SysUser.Authorities). + Where(query.SysUser.UUID.Eq(uuidStr)). + First() if err != nil { return nil, err } - return toModelUserWithAuthorities(&m), nil + return toModelUserWithAuthorities(m), nil } func (r *userRepo) FindByUsername(ctx context.Context, username string) (*system.User, error) { @@ -136,49 +132,48 @@ func (r *userRepo) FindByUsername(ctx context.Context, username string) (*system } func (r *userRepo) FindByUsernameWithAuthorities(ctx context.Context, username string) (*system.User, error) { - var m model.SysUser - err := r.db.WithContext(ctx). - Preload("Authorities"). - Preload("Authority"). - Where("username = ?", username). - First(&m).Error + m, err := query.SysUser.WithContext(ctx). + Preload(query.SysUser.Authority). + Preload(query.SysUser.Authorities). + Where(query.SysUser.Username.Eq(username)). + First() if err != nil { return nil, err } - return toModelUserWithAuthorities(&m), nil + return toModelUserWithAuthorities(m), nil } func (r *userRepo) List(ctx context.Context, page, pageSize int, filters map[string]string) ([]*system.User, int64, error) { - db := r.db.WithContext(ctx).Model(&model.SysUser{}) + u := query.SysUser + q := u.WithContext(ctx) if v, ok := filters["username"]; ok && v != "" { - db = db.Where("username LIKE ?", "%"+v+"%") + q = q.Where(u.Username.Like("%" + v + "%")) } if v, ok := filters["nick_name"]; ok && v != "" { - db = db.Where("nick_name LIKE ?", "%"+v+"%") + q = q.Where(u.NickName.Like("%" + v + "%")) } if v, ok := filters["phone"]; ok && v != "" { - db = db.Where("phone LIKE ?", "%"+v+"%") + q = q.Where(u.Phone.Like("%" + v + "%")) } if v, ok := filters["email"]; ok && v != "" { - db = db.Where("email LIKE ?", "%"+v+"%") + q = q.Where(u.Email.Like("%" + v + "%")) } - var total int64 - if err := db.Count(&total).Error; err != nil { + total, err := q.Count() + if err != nil { return nil, 0, err } - var list []model.SysUser offset := (page - 1) * pageSize - err := db.Preload("Authorities").Preload("Authority").Offset(offset).Limit(pageSize).Find(&list).Error + list, err := q.Preload(u.Authority).Preload(u.Authorities).Offset(offset).Limit(pageSize).Find() if err != nil { return nil, 0, err } users := make([]*system.User, len(list)) for i := range list { - users[i] = toModelUserWithAuthorities(&list[i]) + users[i] = toModelUserWithAuthorities(list[i]) } return users, total, nil } @@ -193,20 +188,16 @@ func (r *userRepo) UpdateAuthorityID(ctx context.Context, id uint, authorityId u func (r *userRepo) SetUserAuthorities(ctx context.Context, adminAuthorityID, userId uint, authorityIds []uint) error { return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { - // 查询用户是否存在 var user model.SysUser if err := tx.Where("id = ?", userId).First(&user).Error; err != nil { return errors.New("查询用户数据失败") } - // 删除旧的用户角色关联 if err := tx.Where("sys_user_id = ?", userId).Delete(&model.SysUserAuthority{}).Error; err != nil { return err } - // 检查管理员权限并创建新的用户角色关联 for _, aid := range authorityIds { - // 检查管理员是否有权限分配该角色 if err := checkAuthorityIDAuth(tx, adminAuthorityID, aid); err != nil { return err } @@ -219,7 +210,6 @@ func (r *userRepo) SetUserAuthorities(ctx context.Context, adminAuthorityID, use } } - // 更新用户默认角色 if len(authorityIds) > 0 { if err := tx.Model(&model.SysUser{}).Where("id = ?", userId).Update("authority_id", authorityIds[0]).Error; err != nil { return err @@ -229,13 +219,10 @@ func (r *userRepo) SetUserAuthorities(ctx context.Context, adminAuthorityID, use }) } -// checkAuthorityIDAuth 检查管理员是否有权限分配该角色 func checkAuthorityIDAuth(tx *gorm.DB, adminAuthorityID, targetAuthorityID uint) error { - // 如果是超级管理员(888),直接通过 if adminAuthorityID == 888 { return nil } - // 检查目标角色是否是管理员的子角色 var authority model.SysAuthority if err := tx.Where("authority_id = ? AND parent_id = ?", targetAuthorityID, adminAuthorityID).First(&authority).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { @@ -255,7 +242,6 @@ func (r *userRepo) CheckUserHasAuthority(ctx context.Context, userId, authorityI } func (r *userRepo) GetAuthorityMenuRouters(ctx context.Context, authorityId uint) ([]string, error) { - // 获取角色的菜单ID列表 var authorityMenus []model.SysAuthorityMenu if err := r.db.WithContext(ctx).Where("sys_authority_authority_id = ?", authorityId).Find(&authorityMenus).Error; err != nil { return nil, err @@ -265,13 +251,11 @@ func (r *userRepo) GetAuthorityMenuRouters(ctx context.Context, authorityId uint return []string{}, nil } - // 获取菜单ID - menuIDs := make([]string, len(authorityMenus)) + menuIDs := make([]int64, len(authorityMenus)) for i, am := range authorityMenus { - menuIDs[i] = am.MenuID + menuIDs[i] = am.SysBaseMenuID } - // 获取菜单的name(路由) var menus []model.SysBaseMenu if err := r.db.WithContext(ctx).Where("id IN ?", menuIDs).Find(&menus).Error; err != nil { return nil, err @@ -337,15 +321,9 @@ func toModelUser(m *model.SysUser) *system.User { if m.NickName != nil { u.NickName = *m.NickName } - if m.SideMode != nil { - u.SideMode = *m.SideMode - } if m.HeaderImg != nil { u.HeaderImg = *m.HeaderImg } - if m.BaseColor != nil { - u.BaseColor = *m.BaseColor - } if m.AuthorityID != nil { u.AuthorityId = uint(*m.AuthorityID) } @@ -374,16 +352,14 @@ func toModelUserWithAuthorities(m *model.SysUser) *system.User { u := toModelUser(m) // 转换Authority - if m.Authority != nil { - u.Authority = &system.Authority{ - AuthorityId: uint(m.Authority.AuthorityID), - AuthorityName: safeString(m.Authority.AuthorityName), - DefaultRouter: safeString(m.Authority.DefaultRouter), - } - if m.Authority.ParentID != nil { - pid := uint(*m.Authority.ParentID) - u.Authority.ParentId = &pid - } + u.Authority = &system.Authority{ + AuthorityId: uint(m.Authority.AuthorityID), + AuthorityName: safeString(m.Authority.AuthorityName), + DefaultRouter: safeString(m.Authority.DefaultRouter), + } + if m.Authority.ParentID != nil { + pid := uint(*m.Authority.ParentID) + u.Authority.ParentId = &pid } // 转换Authorities diff --git a/internal/service/service.go b/internal/service/service.go index d4313fb..1a3a55a 100644 --- a/internal/service/service.go +++ b/internal/service/service.go @@ -3,4 +3,4 @@ package service import "github.com/google/wire" // ProviderSet is service providers. -var ProviderSet = wire.NewSet(NewAdminService) +var ProviderSet = wire.NewSet() diff --git a/internal/service/system/user.go b/internal/service/system/user.go new file mode 100644 index 0000000..e61f8fb --- /dev/null +++ b/internal/service/system/user.go @@ -0,0 +1,558 @@ +package system + +import ( + "context" + "encoding/json" + + "kra/internal/biz/system" + "kra/internal/server/middleware" + "kra/pkg/jwt" + + "github.com/go-kratos/kratos/v2/errors" + "github.com/go-kratos/kratos/v2/transport/http" +) + +// UserService 用户服务 +type UserService struct { + uc *system.UserUsecase + jwtPkg *jwt.JWT +} + +// NewUserService 创建用户服务 +func NewUserService(uc *system.UserUsecase, jwtPkg *jwt.JWT) *UserService { + return &UserService{uc: uc, jwtPkg: jwtPkg} +} + +// LoginRequest 登录请求 +type LoginRequest struct { + Username string `json:"username"` + Password string `json:"password"` + Captcha string `json:"captcha"` + CaptchaId string `json:"captchaId"` +} + +// LoginResponse 登录响应 +type LoginResponse struct { + User *UserInfo `json:"user"` + Token string `json:"token"` + ExpiresAt int64 `json:"expiresAt"` +} + +// UserInfo 用户信息 +type UserInfo struct { + ID uint `json:"id"` + UUID string `json:"uuid"` + Username string `json:"username"` + NickName string `json:"nickName"` + SideMode string `json:"sideMode"` + HeaderImg string `json:"headerImg"` + BaseColor string `json:"baseColor"` + AuthorityId uint `json:"authorityId"` + Phone string `json:"phone"` + Email string `json:"email"` + Enable int `json:"enable"` + OriginSetting json.RawMessage `json:"originSetting,omitempty"` + Authority *AuthorityInfo `json:"authority,omitempty"` + Authorities []*AuthorityInfo `json:"authorities,omitempty"` +} + +// AuthorityInfo 角色信息 +type AuthorityInfo struct { + AuthorityId uint `json:"authorityId"` + AuthorityName string `json:"authorityName"` + ParentId *uint `json:"parentId,omitempty"` + DefaultRouter string `json:"defaultRouter"` +} + +// Login 用户登录 +func (s *UserService) Login(ctx context.Context, req *LoginRequest) (*LoginResponse, error) { + user, err := s.uc.Login(ctx, req.Username, req.Password) + if err != nil { + return nil, errors.Unauthorized("LOGIN_FAILED", err.Error()) + } + + if user.Enable != 1 { + return nil, errors.Forbidden("USER_DISABLED", "用户被禁止登录") + } + + // 生成 JWT token + claims := s.jwtPkg.CreateClaims(jwt.BaseClaims{ + UUID: user.UUID.String(), + ID: uint(user.ID), + Username: user.Username, + NickName: user.NickName, + AuthorityID: user.AuthorityId, + }) + token, err := s.jwtPkg.CreateToken(claims.BaseClaims) + if err != nil { + return nil, errors.InternalServer("TOKEN_ERROR", "生成token失败") + } + + return &LoginResponse{ + User: toUserInfo(user), + Token: token, + ExpiresAt: claims.ExpiresAt.UnixMilli(), + }, nil +} + +// RegisterRequest 注册请求 +type RegisterRequest struct { + Username string `json:"username"` + Password string `json:"password"` + NickName string `json:"nickName"` + HeaderImg string `json:"headerImg"` + AuthorityId uint `json:"authorityId"` + AuthorityIds []uint `json:"authorityIds"` + Phone string `json:"phone"` + Email string `json:"email"` + Enable int `json:"enable"` +} + +// Register 用户注册 +func (s *UserService) Register(ctx context.Context, req *RegisterRequest) (*UserInfo, error) { + user := &system.User{ + Username: req.Username, + Password: req.Password, + NickName: req.NickName, + HeaderImg: req.HeaderImg, + AuthorityId: req.AuthorityId, + Phone: req.Phone, + Email: req.Email, + Enable: req.Enable, + } + + created, err := s.uc.Register(ctx, user) + if err != nil { + return nil, errors.BadRequest("REGISTER_FAILED", err.Error()) + } + + return toUserInfo(created), nil +} + +// ChangePasswordRequest 修改密码请求 +type ChangePasswordRequest struct { + Password string `json:"password"` + NewPassword string `json:"newPassword"` +} + +// ChangePassword 修改密码 +func (s *UserService) ChangePassword(ctx context.Context, userID uint, req *ChangePasswordRequest) error { + return s.uc.ChangePassword(ctx, userID, req.Password, req.NewPassword) +} + +// ResetPasswordRequest 重置密码请求 +type ResetPasswordRequest struct { + ID uint `json:"id"` + Password string `json:"password"` +} + +// ResetPassword 重置密码 +func (s *UserService) ResetPassword(ctx context.Context, req *ResetPasswordRequest) error { + return s.uc.ResetPassword(ctx, req.ID, req.Password) +} + +// GetUserInfo 获取用户信息 +func (s *UserService) GetUserInfo(ctx context.Context, uuid string) (*UserInfo, error) { + user, err := s.uc.GetUserInfo(ctx, uuid) + if err != nil { + return nil, errors.NotFound("USER_NOT_FOUND", err.Error()) + } + return toUserInfo(user), nil +} + +// GetUserListRequest 获取用户列表请求 +type GetUserListRequest struct { + Page int `json:"page"` + PageSize int `json:"pageSize"` + Username string `json:"username"` + NickName string `json:"nickName"` + Phone string `json:"phone"` + Email string `json:"email"` +} + +// GetUserListResponse 获取用户列表响应 +type GetUserListResponse struct { + List []*UserInfo `json:"list"` + Total int64 `json:"total"` + Page int `json:"page"` + PageSize int `json:"pageSize"` +} + +// GetUserList 获取用户列表 +func (s *UserService) GetUserList(ctx context.Context, req *GetUserListRequest) (*GetUserListResponse, error) { + filters := make(map[string]string) + if req.Username != "" { + filters["username"] = req.Username + } + if req.NickName != "" { + filters["nick_name"] = req.NickName + } + if req.Phone != "" { + filters["phone"] = req.Phone + } + if req.Email != "" { + filters["email"] = req.Email + } + + users, total, err := s.uc.GetUserList(ctx, req.Page, req.PageSize, filters) + if err != nil { + return nil, errors.InternalServer("LIST_ERROR", err.Error()) + } + + list := make([]*UserInfo, len(users)) + for i, u := range users { + list[i] = toUserInfo(u) + } + + return &GetUserListResponse{ + List: list, + Total: total, + Page: req.Page, + PageSize: req.PageSize, + }, nil +} + +// SetUserInfoRequest 设置用户信息请求(管理员用) +type SetUserInfoRequest struct { + ID uint `json:"id"` + NickName string `json:"nickName"` + HeaderImg string `json:"headerImg"` + Phone string `json:"phone"` + Email string `json:"email"` + Enable int `json:"enable"` + AuthorityIds []uint `json:"authorityIds"` +} + +// SetUserInfo 设置用户信息 +func (s *UserService) SetUserInfo(ctx context.Context, req *SetUserInfoRequest) error { + user := &system.User{ + ID: req.ID, + NickName: req.NickName, + HeaderImg: req.HeaderImg, + Phone: req.Phone, + Email: req.Email, + Enable: req.Enable, + } + return s.uc.SetUserInfo(ctx, user) +} + +// SetSelfInfoRequest 设置自己信息请求(用户用) +type SetSelfInfoRequest struct { + NickName string `json:"nickName"` + HeaderImg string `json:"headerImg"` + Phone string `json:"phone"` + Email string `json:"email"` + SideMode string `json:"sideMode"` + BaseColor string `json:"baseColor"` +} + +// SetSelfInfo 设置自己的信息 +func (s *UserService) SetSelfInfo(ctx context.Context, userID uint, req *SetSelfInfoRequest) error { + user := &system.User{ + ID: userID, + NickName: req.NickName, + HeaderImg: req.HeaderImg, + Phone: req.Phone, + Email: req.Email, + SideMode: req.SideMode, + BaseColor: req.BaseColor, + } + return s.uc.SetSelfInfo(ctx, user) +} + +// SetSelfSetting 设置自己的配置 +func (s *UserService) SetSelfSetting(ctx context.Context, userID uint, setting json.RawMessage) error { + return s.uc.SetSelfSetting(ctx, userID, setting) +} + +// DeleteUser 删除用户 +func (s *UserService) DeleteUser(ctx context.Context, id uint) error { + return s.uc.DeleteUser(ctx, id) +} + +// SetUserAuthorityRequest 设置用户角色请求 +type SetUserAuthorityRequest struct { + AuthorityId uint `json:"authorityId"` +} + +// SetUserAuthority 设置用户角色(切换角色) +func (s *UserService) SetUserAuthority(ctx context.Context, userID uint, req *SetUserAuthorityRequest) error { + return s.uc.SetUserAuthority(ctx, userID, req.AuthorityId) +} + +// SetUserAuthoritiesRequest 设置用户多角色请求 +type SetUserAuthoritiesRequest struct { + ID uint `json:"id"` + AuthorityIds []uint `json:"authorityIds"` +} + +// SetUserAuthorities 设置用户多角色 +func (s *UserService) SetUserAuthorities(ctx context.Context, adminAuthorityID uint, req *SetUserAuthoritiesRequest) error { + return s.uc.SetUserAuthorities(ctx, adminAuthorityID, req.ID, req.AuthorityIds) +} + +// 转换函数 +func toUserInfo(u *system.User) *UserInfo { + info := &UserInfo{ + ID: u.ID, + UUID: u.UUID.String(), + Username: u.Username, + NickName: u.NickName, + SideMode: u.SideMode, + HeaderImg: u.HeaderImg, + BaseColor: u.BaseColor, + AuthorityId: u.AuthorityId, + Phone: u.Phone, + Email: u.Email, + Enable: u.Enable, + OriginSetting: u.OriginSetting, + } + + if u.Authority != nil { + info.Authority = &AuthorityInfo{ + AuthorityId: u.Authority.AuthorityId, + AuthorityName: u.Authority.AuthorityName, + ParentId: u.Authority.ParentId, + DefaultRouter: u.Authority.DefaultRouter, + } + } + + if len(u.Authorities) > 0 { + info.Authorities = make([]*AuthorityInfo, len(u.Authorities)) + for i, a := range u.Authorities { + info.Authorities[i] = &AuthorityInfo{ + AuthorityId: a.AuthorityId, + AuthorityName: a.AuthorityName, + ParentId: a.ParentId, + DefaultRouter: a.DefaultRouter, + } + } + } + + return info +} + +// RegisterRoutes 注册路由 +func (s *UserService) RegisterRoutes(srv *http.Server) { + r := srv.Route("/") + + // 公开接口 + r.POST("/base/login", s.handleLogin) + + // 需要认证的接口 + r.POST("/user/register", s.handleRegister) + r.POST("/user/changePassword", s.handleChangePassword) + r.POST("/user/resetPassword", s.handleResetPassword) + r.GET("/user/getUserInfo", s.handleGetUserInfo) + r.POST("/user/getUserList", s.handleGetUserList) + r.PUT("/user/setUserInfo", s.handleSetUserInfo) + r.PUT("/user/setSelfInfo", s.handleSetSelfInfo) + r.PUT("/user/setSelfSetting", s.handleSetSelfSetting) + r.DELETE("/user/deleteUser", s.handleDeleteUser) + r.POST("/user/setUserAuthority", s.handleSetUserAuthority) + r.POST("/user/setUserAuthorities", s.handleSetUserAuthorities) +} + +// HTTP Handlers +func (s *UserService) handleLogin(ctx http.Context) error { + var req LoginRequest + if err := ctx.Bind(&req); err != nil { + return err + } + resp, err := s.Login(ctx, &req) + if err != nil { + return err + } + return ctx.Result(200, map[string]any{ + "code": 0, + "msg": "登录成功", + "data": resp, + }) +} + +func (s *UserService) handleRegister(ctx http.Context) error { + var req RegisterRequest + if err := ctx.Bind(&req); err != nil { + return err + } + resp, err := s.Register(ctx, &req) + if err != nil { + return err + } + return ctx.Result(200, map[string]any{ + "code": 0, + "msg": "注册成功", + "data": resp, + }) +} + +func (s *UserService) handleChangePassword(ctx http.Context) error { + var req ChangePasswordRequest + if err := ctx.Bind(&req); err != nil { + return err + } + userID := middleware.GetUserID(ctx) + if userID == 0 { + return errors.Unauthorized("UNAUTHORIZED", "请先登录") + } + if err := s.ChangePassword(ctx, userID, &req); err != nil { + return err + } + return ctx.Result(200, map[string]any{ + "code": 0, + "msg": "修改成功", + }) +} + +func (s *UserService) handleResetPassword(ctx http.Context) error { + var req ResetPasswordRequest + if err := ctx.Bind(&req); err != nil { + return err + } + if err := s.ResetPassword(ctx, &req); err != nil { + return err + } + return ctx.Result(200, map[string]any{ + "code": 0, + "msg": "重置成功", + }) +} + +func (s *UserService) handleGetUserInfo(ctx http.Context) error { + claims, ok := middleware.GetClaims(ctx) + if !ok { + return errors.Unauthorized("UNAUTHORIZED", "请先登录") + } + resp, err := s.GetUserInfo(ctx, claims.UUID) + if err != nil { + return err + } + return ctx.Result(200, map[string]any{ + "code": 0, + "msg": "获取成功", + "data": map[string]any{"userInfo": resp}, + }) +} + +func (s *UserService) handleGetUserList(ctx http.Context) error { + var req GetUserListRequest + if err := ctx.Bind(&req); err != nil { + return err + } + resp, err := s.GetUserList(ctx, &req) + if err != nil { + return err + } + return ctx.Result(200, map[string]any{ + "code": 0, + "msg": "获取成功", + "data": resp, + }) +} + +func (s *UserService) handleSetUserInfo(ctx http.Context) error { + var req SetUserInfoRequest + if err := ctx.Bind(&req); err != nil { + return err + } + if err := s.SetUserInfo(ctx, &req); err != nil { + return err + } + return ctx.Result(200, map[string]any{ + "code": 0, + "msg": "设置成功", + }) +} + +func (s *UserService) handleSetSelfInfo(ctx http.Context) error { + var req SetSelfInfoRequest + if err := ctx.Bind(&req); err != nil { + return err + } + userID := middleware.GetUserID(ctx) + if userID == 0 { + return errors.Unauthorized("UNAUTHORIZED", "请先登录") + } + if err := s.SetSelfInfo(ctx, userID, &req); err != nil { + return err + } + return ctx.Result(200, map[string]any{ + "code": 0, + "msg": "设置成功", + }) +} + +func (s *UserService) handleSetSelfSetting(ctx http.Context) error { + var req json.RawMessage + if err := ctx.Bind(&req); err != nil { + return err + } + userID := middleware.GetUserID(ctx) + if userID == 0 { + return errors.Unauthorized("UNAUTHORIZED", "请先登录") + } + if err := s.SetSelfSetting(ctx, userID, req); err != nil { + return err + } + return ctx.Result(200, map[string]any{ + "code": 0, + "msg": "设置成功", + }) +} + +func (s *UserService) handleDeleteUser(ctx http.Context) error { + var req struct { + ID uint `json:"id"` + } + if err := ctx.Bind(&req); err != nil { + return err + } + // 不能删除自己 + userID := middleware.GetUserID(ctx) + if userID == req.ID { + return errors.BadRequest("DELETE_SELF", "删除失败, 无法删除自己") + } + if err := s.DeleteUser(ctx, req.ID); err != nil { + return err + } + return ctx.Result(200, map[string]any{ + "code": 0, + "msg": "删除成功", + }) +} + +func (s *UserService) handleSetUserAuthority(ctx http.Context) error { + var req SetUserAuthorityRequest + if err := ctx.Bind(&req); err != nil { + return err + } + userID := middleware.GetUserID(ctx) + if userID == 0 { + return errors.Unauthorized("UNAUTHORIZED", "请先登录") + } + if err := s.SetUserAuthority(ctx, userID, &req); err != nil { + return err + } + return ctx.Result(200, map[string]any{ + "code": 0, + "msg": "修改成功", + }) +} + +func (s *UserService) handleSetUserAuthorities(ctx http.Context) error { + var req SetUserAuthoritiesRequest + if err := ctx.Bind(&req); err != nil { + return err + } + adminAuthorityID := middleware.GetAuthorityID(ctx) + if adminAuthorityID == 0 { + return errors.Unauthorized("UNAUTHORIZED", "请先登录") + } + if err := s.SetUserAuthorities(ctx, adminAuthorityID, &req); err != nil { + return err + } + return ctx.Result(200, map[string]any{ + "code": 0, + "msg": "修改成功", + }) +}