Browse Source

new create

master
hejl 1 month ago
parent
commit
309b941c2a
  1. 85
      gofaster/backend/cmd/server/main.go
  2. 72
      gofaster/backend/go.mod
  3. 161
      gofaster/backend/go.sum
  4. 56
      gofaster/backend/internal/config/config.go
  5. 47
      gofaster/backend/internal/controller/user.go
  6. 27
      gofaster/backend/internal/middleware/auth.go
  7. 52
      gofaster/backend/internal/middleware/logger.go
  8. 26
      gofaster/backend/internal/middleware/permission.go
  9. 14
      gofaster/backend/internal/model/base.go
  10. 16
      gofaster/backend/internal/model/log.go
  11. 9
      gofaster/backend/internal/model/permission.go
  12. 8
      gofaster/backend/internal/model/role.go
  13. 11
      gofaster/backend/internal/model/user.go
  14. 42
      gofaster/backend/internal/model/workflow.go
  15. 72
      gofaster/backend/internal/repository/action_log.go
  16. 29
      gofaster/backend/internal/repository/base.go
  17. 59
      gofaster/backend/internal/repository/permission.go
  18. 61
      gofaster/backend/internal/repository/role.go
  19. 61
      gofaster/backend/internal/repository/user.go
  20. 128
      gofaster/backend/internal/repository/workflow.go
  21. 34
      gofaster/backend/internal/service/user.go
  22. 34
      gofaster/backend/internal/service/workflow.go
  23. 44
      gofaster/backend/pkg/auth/jwt.go
  24. 22
      gofaster/backend/pkg/database/db.go
  25. 45
      gofaster/frontend/.gitignore
  26. 30
      gofaster/frontend/.metadata
  27. 16
      gofaster/frontend/README.md
  28. 28
      gofaster/frontend/analysis_options.yaml
  29. 29
      gofaster/frontend/lib/api/dio.dart
  30. 122
      gofaster/frontend/lib/main.dart
  31. 41
      gofaster/frontend/lib/pages/users/list.dart
  32. 23
      gofaster/frontend/lib/services/user_service.dart
  33. 277
      gofaster/frontend/pubspec.lock
  34. 92
      gofaster/frontend/pubspec.yaml
  35. 30
      gofaster/frontend/test/widget_test.dart
  36. BIN
      gofaster/frontend/web/favicon.png
  37. BIN
      gofaster/frontend/web/icons/Icon-192.png
  38. BIN
      gofaster/frontend/web/icons/Icon-512.png
  39. BIN
      gofaster/frontend/web/icons/Icon-maskable-192.png
  40. BIN
      gofaster/frontend/web/icons/Icon-maskable-512.png
  41. 38
      gofaster/frontend/web/index.html
  42. 35
      gofaster/frontend/web/manifest.json

85
gofaster/backend/cmd/server/main.go

@ -0,0 +1,85 @@ @@ -0,0 +1,85 @@
package main
import (
"gofaster/internal/config"
"gofaster/internal/controller"
"gofaster/internal/middleware"
"gofaster/internal/model"
"gofaster/internal/repository"
"gofaster/internal/service"
"gofaster/pkg/database"
"gofaster/pkg/logger"
"gofaster/routes"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
"gorm.io/gorm"
)
func main() {
// 加载配置
cfg := config.LoadConfig()
// 初始化日志
log := logger.NewLogger(cfg.Log.Level, cfg.Log.Path)
defer log.Sync()
// 初始化数据库
db, err := database.NewDB(&cfg.DB)
if err != nil {
log.Fatal("Failed to connect database", zap.Error(err))
}
// 自动迁移
if err := autoMigrate(db); err != nil {
log.Fatal("Failed to migrate database", zap.Error(err))
}
// 初始化Redis
redisClient := database.NewRedisClient(&cfg.Redis)
// 初始化服务层
userRepo := repository.NewUserRepo(db)
userService := service.NewUserService(userRepo)
// 初始化控制器
userCtrl := controller.NewUserController(userService)
// 初始化Gin
app := gin.New()
// 中间件
app.Use(middleware.LoggerMiddleware(log, db))
app.Use(middleware.RecoveryMiddleware(log))
app.Use(middleware.CORSMiddleware())
// 路由
api := app.Group("/api")
routes.RegisterUserRoutes(api, userCtrl)
// 认证路由
authApi := api.Group("")
authApi.Use(middleware.AuthMiddleware(cfg.JWT.Secret))
{
// 需要认证的路由
}
// 启动服务器
log.Info("Starting server", zap.String("port", cfg.Server.Port))
if err := app.Run(":" + cfg.Server.Port); err != nil {
log.Fatal("Failed to start server", zap.Error(err))
}
}
func autoMigrate(db *gorm.DB) error {
return db.AutoMigrate(
&model.User{},
&model.Role{},
&model.Permission{},
&model.ActionLog{},
&model.Workflow{},
&model.WorkflowNode{},
&model.WorkflowInstance{},
&model.WorkflowTask{},
)
}

72
gofaster/backend/go.mod

@ -0,0 +1,72 @@ @@ -0,0 +1,72 @@
module gofaster
go 1.24.3
require github.com/spf13/viper v1.20.1
require (
github.com/KyleBanks/depth v1.2.1 // indirect
github.com/PuerkitoBio/purell v1.2.1 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/bytedance/sonic v1.14.0 // indirect
github.com/bytedance/sonic/loader v0.3.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
github.com/cloudwego/base64x v0.1.5 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/fsnotify/fsnotify v1.8.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.9 // indirect
github.com/gin-contrib/sse v1.1.0 // indirect
github.com/gin-gonic/gin v1.10.1 // indirect
github.com/go-openapi/jsonpointer v0.21.1 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect
github.com/go-openapi/spec v0.21.0 // indirect
github.com/go-openapi/swag v0.23.1 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.27.0 // indirect
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/golang-jwt/jwt/v5 v5.2.3
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/pgx/v5 v5.7.5 // indirect
github.com/jackc/puddle/v2 v2.2.2 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/mailru/easyjson v0.9.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/redis/go-redis/v9 v9.11.0 // indirect
github.com/sagikazarmark/locafero v0.7.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.12.0 // indirect
github.com/spf13/cast v1.7.1 // indirect
github.com/spf13/pflag v1.0.6 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/swaggo/gin-swagger v1.6.0 // indirect
github.com/swaggo/swag v1.16.5 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.3.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/arch v0.19.0 // indirect
golang.org/x/crypto v0.40.0 // indirect
golang.org/x/mod v0.26.0 // indirect
golang.org/x/net v0.42.0 // indirect
golang.org/x/sync v0.16.0 // indirect
golang.org/x/sys v0.34.0 // indirect
golang.org/x/text v0.27.0 // indirect
golang.org/x/tools v0.35.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gorm.io/driver/postgres v1.6.0 // indirect
gorm.io/gorm v1.30.1 // indirect
)

161
gofaster/backend/go.sum

@ -0,0 +1,161 @@ @@ -0,0 +1,161 @@
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
github.com/PuerkitoBio/purell v1.2.1 h1:QsZ4TjvwiMpat6gBCBxEQI0rcS9ehtkKtSpiUnd9N28=
github.com/PuerkitoBio/purell v1.2.1/go.mod h1:ZwHcC/82TOaovDi//J/804umJFFmbOHPngi8iYYv/Eo=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
github.com/bytedance/sonic v1.14.0 h1:/OfKt8HFw0kh2rj8N0F6C/qPGRESq0BbaNZgcNXXzQQ=
github.com/bytedance/sonic v1.14.0/go.mod h1:WoEbx8WTcFJfzCe0hbmyTGrfjt8PzNEBdxlNUO24NhA=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA=
github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0=
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA=
github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ=
github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
github.com/go-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic=
github.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk=
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY=
github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk=
github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU=
github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4=
github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/golang-jwt/jwt/v5 v5.2.3 h1:kkGXqQOBSDDWRhWNXTFpqGSCMyh/PLnqUvMGJPDJDs0=
github.com/golang-jwt/jwt/v5 v5.2.3/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
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-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.7.5 h1:JHGfMnQY+IEtGM63d+NGMjoRpysB2JBwDr5fsngwmJs=
github.com/jackc/pgx/v5 v5.7.5/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M=
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
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.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/redis/go-redis/v9 v9.11.0 h1:E3S08Gl/nJNn5vkxd2i78wZxWAPNZgUNTp8WIJUAiIs=
github.com/redis/go-redis/v9 v9.11.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw=
github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo=
github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs=
github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4=
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4=
github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4=
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=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/swaggo/gin-swagger v1.6.0 h1:y8sxvQ3E20/RCyrXeFfg60r6H0Z+SwpTjMYsMm+zy8M=
github.com/swaggo/gin-swagger v1.6.0/go.mod h1:BG00cCEy294xtVpyIAHG6+e2Qzj/xKlRdOqDkvq0uzo=
github.com/swaggo/swag v1.16.5 h1:nMf2fEV1TetMTJb4XzD0Lz7jFfKJmJKGTygEey8NSxM=
github.com/swaggo/swag v1.16.5/go.mod h1:ngP2etMK5a0P3QBizic5MEwpRmluJZPHjXcMoj4Xesg=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA=
github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.19.0 h1:LmbDQUodHThXE+htjrnmVD73M//D9GTH6wFZjyDkjyU=
golang.org/x/arch v0.19.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg=
golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ=
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0=
golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4=
gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo=
gorm.io/gorm v1.30.1 h1:lSHg33jJTBxs2mgJRfRZeLDG+WZaHYCk3Wtfl6Ngzo4=
gorm.io/gorm v1.30.1/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE=
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

56
gofaster/backend/internal/config/config.go

@ -0,0 +1,56 @@ @@ -0,0 +1,56 @@
package config
import (
"log"
"github.com/spf13/viper"
)
type Config struct {
DB DBConfig
Server ServerConfig
Redis RedisConfig
JWT JWTConfig
}
type DBConfig struct {
Host string
Port string
User string
Password string
Name string
}
type ServerConfig struct {
Port string
}
type RedisConfig struct {
Host string
Port string
Password string
DB int
}
type JWTConfig struct {
Secret string
Expire int // 小时
}
func LoadConfig() *Config {
viper.SetConfigName("config")
viper.SetConfigType("yaml")
viper.AddConfigPath(".")
viper.AutomaticEnv()
if err := viper.ReadInConfig(); err != nil {
log.Fatalf("Error reading config file: %v", err)
}
var cfg Config
if err := viper.Unmarshal(&cfg); err != nil {
log.Fatalf("Unable to decode into struct: %v", err)
}
return &cfg
}

47
gofaster/backend/internal/controller/user.go

@ -0,0 +1,47 @@ @@ -0,0 +1,47 @@
package controller
import (
"net/http"
"gofaster/internal/model"
"gofaster/internal/service"
"github.com/gin-gonic/gin"
)
type UserController struct {
userService *service.UserService
}
func NewUserController(userService *service.UserService) *UserController {
return &UserController{userService: userService}
}
func (c *UserController) RegisterRoutes(r *gin.RouterGroup) {
r.GET("/users", c.ListUsers)
r.POST("/users", c.CreateUser)
r.GET("/users/:id", c.GetUser)
r.PUT("/users/:id", c.UpdateUser)
r.DELETE("/users/:id", c.DeleteUser)
}
func (c *UserController) ListUsers(ctx *gin.Context) {
// 实现分页查询
}
func (c *UserController) CreateUser(ctx *gin.Context) {
var user model.User
if err := ctx.ShouldBindJSON(&user); err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if err := c.userService.CreateUser(&user); err != nil {
ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
ctx.JSON(http.StatusCreated, user)
}
// 其他方法实现...

27
gofaster/backend/internal/middleware/auth.go

@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
package middleware
import (
"gofaster/pkg/auth"
"net/http"
"github.com/gin-gonic/gin"
)
func AuthMiddleware(jwtSecret string) gin.HandlerFunc {
return func(c *gin.Context) {
token := c.GetHeader("Authorization")
if token == "" {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "未提供认证令牌"})
return
}
claims, err := auth.ParseToken(token, jwtSecret)
if err != nil {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "无效的认证令牌"})
return
}
c.Set("user_id", claims.UserID)
c.Next()
}
}

52
gofaster/backend/internal/middleware/logger.go

@ -0,0 +1,52 @@ @@ -0,0 +1,52 @@
package middleware
import (
"bytes"
"gofaster/internal/model"
"io"
"time"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
"gorm.io/gorm"
)
func LoggerMiddleware(logger *zap.Logger, db *gorm.DB) gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
path := c.Request.URL.Path
query := c.Request.URL.RawQuery
// 记录请求体
var requestBody string
if c.Request.Body != nil {
bodyBytes, _ := io.ReadAll(c.Request.Body)
c.Request.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
requestBody = string(bodyBytes)
}
// 处理请求
c.Next()
// 记录日志
latency := time.Since(start).Milliseconds()
actionLog := model.ActionLog{
UserID: getUserIdFromContext(c),
Action: getActionFromPath(path),
IP: c.ClientIP(),
UserAgent: c.Request.UserAgent(),
Path: path,
Method: c.Request.Method,
Request: requestBody,
Status: c.Writer.Status(),
Latency: latency,
}
// 异步保存日志
go func() {
if err := db.Create(&actionLog).Error; err != nil {
logger.Error("保存操作日志失败", zap.Error(err))
}
}()
}
}

26
gofaster/backend/internal/middleware/permission.go

@ -0,0 +1,26 @@ @@ -0,0 +1,26 @@
package middleware
import (
"net/http"
"github.com/gin-gonic/gin"
)
func PermissionMiddleware(permission string) gin.HandlerFunc {
return func(c *gin.Context) {
userID, exists := c.Get("user_id")
if !exists {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "未认证"})
return
}
// 检查用户是否有该权限
hasPerm := checkPermission(userID.(uint), permission)
if !hasPerm {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "没有权限"})
return
}
c.Next()
}
}

14
gofaster/backend/internal/model/base.go

@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
package model
import (
"time"
"gorm.io/gorm"
)
type BaseModel struct {
ID uint `gorm:"primarykey" json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
}

16
gofaster/backend/internal/model/log.go

@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
package model
type ActionLog struct {
BaseModel
UserID uint `json:"user_id"`
Username string `json:"username"`
Action string `json:"action"`
IP string `json:"ip"`
UserAgent string `json:"user_agent"`
Path string `json:"path"`
Method string `json:"method"`
Request string `json:"request" gorm:"type:text"`
Response string `json:"response" gorm:"type:text"`
Status int `json:"status"`
Latency int64 `json:"latency"` // 毫秒
}

9
gofaster/backend/internal/model/permission.go

@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
package model
type Permission struct {
BaseModel
Name string `gorm:"uniqueIndex;size:50" json:"name"`
Description string `gorm:"size:200" json:"description"`
Resource string `gorm:"size:100" json:"resource"`
Action string `gorm:"size:50" json:"action"` // create, read, update, delete等
}

8
gofaster/backend/internal/model/role.go

@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
package model
type Role struct {
BaseModel
Name string `gorm:"uniqueIndex;size:50" json:"name"`
Description string `gorm:"size:200" json:"description"`
Permissions []Permission `gorm:"many2many:role_permissions;" json:"permissions"`
}

11
gofaster/backend/internal/model/user.go

@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
package model
type User struct {
BaseModel
Username string `gorm:"uniqueIndex;size:50" json:"username"`
Password string `gorm:"size:100" json:"-"`
Email string `gorm:"size:100" json:"email"`
Phone string `gorm:"size:20" json:"phone"`
Status int `gorm:"default:1" json:"status"` // 1-正常 2-禁用
Roles []Role `gorm:"many2many:user_roles;" json:"roles"`
}

42
gofaster/backend/internal/model/workflow.go

@ -0,0 +1,42 @@ @@ -0,0 +1,42 @@
package model
import "time"
type Workflow struct {
BaseModel
Name string `json:"name"`
Description string `json:"description"`
Status int `json:"status"` // 1-启用 2-禁用
Nodes []WorkflowNode `json:"nodes"`
}
type WorkflowNode struct {
BaseModel
WorkflowID uint `json:"workflow_id"`
Name string `json:"name"`
Type string `json:"type"` // start, end, task, approval等
Handler string `json:"handler"` // 处理函数
NextNodes string `json:"next_nodes"` // 逗号分隔的下个节点ID
Position int `json:"position"`
}
type WorkflowInstance struct {
BaseModel
WorkflowID uint `json:"workflow_id"`
Status string `json:"status"` // running, completed, canceled
CurrentNodeID uint `json:"current_node_id"`
Data string `json:"data" gorm:"type:text"` // JSON格式的业务数据
Tasks []WorkflowTask `json:"tasks"`
}
type WorkflowTask struct {
BaseModel
InstanceID uint `json:"instance_id"`
NodeID uint `json:"node_id"`
Status string `json:"status"` // pending, processing, completed, canceled
Result string `json:"result" gorm:"type:text"` // JSON格式的处理结果
Handler string `json:"handler"`
Assignee uint `json:"assignee"` // 处理人
StartTime time.Time `json:"start_time"`
EndTime time.Time `json:"end_time"`
}

72
gofaster/backend/internal/repository/action_log.go

@ -0,0 +1,72 @@ @@ -0,0 +1,72 @@
package repository
import (
"gofaster/internal/model"
"gorm.io/gorm"
)
type ActionLogRepo struct {
BaseRepo
}
func NewActionLogRepo(db *gorm.DB) *ActionLogRepo {
return &ActionLogRepo{BaseRepo{db: db}}
}
func (r *ActionLogRepo) Create(log *model.ActionLog) error {
return r.db.Create(log).Error
}
func (r *ActionLogRepo) GetByID(id uint) (*model.ActionLog, error) {
var log model.ActionLog
err := r.db.First(&log, id).Error
return &log, err
}
func (r *ActionLogRepo) ListByUser(userID uint, page, pageSize int) ([]model.ActionLog, int64, error) {
var logs []model.ActionLog
var count int64
err := r.db.Model(&model.ActionLog{}).Where("user_id = ?", userID).Count(&count).Error
if err != nil {
return nil, 0, err
}
offset := (page - 1) * pageSize
err = r.db.Where("user_id = ?", userID).Offset(offset).Limit(pageSize).Order("created_at DESC").Find(&logs).Error
return logs, count, err
}
func (r *ActionLogRepo) List(page, pageSize int) ([]model.ActionLog, int64, error) {
var logs []model.ActionLog
var count int64
err := r.db.Model(&model.ActionLog{}).Count(&count).Error
if err != nil {
return nil, 0, err
}
offset := (page - 1) * pageSize
err = r.db.Offset(offset).Limit(pageSize).Order("created_at DESC").Find(&logs).Error
return logs, count, err
}
func (r *ActionLogRepo) Search(query string, page, pageSize int) ([]model.ActionLog, int64, error) {
var logs []model.ActionLog
var count int64
searchQuery := "%" + query + "%"
countQuery := r.db.Model(&model.ActionLog{}).
Where("username LIKE ? OR action LIKE ? OR path LIKE ? OR method LIKE ?",
searchQuery, searchQuery, searchQuery, searchQuery)
err := countQuery.Count(&count).Error
if err != nil {
return nil, 0, err
}
offset := (page - 1) * pageSize
err = countQuery.Offset(offset).Limit(pageSize).Order("created_at DESC").Find(&logs).Error
return logs, count, err
}

29
gofaster/backend/internal/repository/base.go

@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
package repository
import (
"gorm.io/gorm"
)
type BaseRepo struct {
db *gorm.DB
}
func NewBaseRepo(db *gorm.DB) *BaseRepo {
return &BaseRepo{db: db}
}
func (r *BaseRepo) DB() *gorm.DB {
return r.db
}
func (r *BaseRepo) Begin() *gorm.DB {
return r.db.Begin()
}
func (r *BaseRepo) Commit(tx *gorm.DB) error {
return tx.Commit().Error
}
func (r *BaseRepo) Rollback(tx *gorm.DB) error {
return tx.Rollback().Error
}

59
gofaster/backend/internal/repository/permission.go

@ -0,0 +1,59 @@ @@ -0,0 +1,59 @@
package repository
import (
"gofaster/internal/model"
"gorm.io/gorm"
)
type PermissionRepo struct {
BaseRepo
}
func NewPermissionRepo(db *gorm.DB) *PermissionRepo {
return &PermissionRepo{BaseRepo{db: db}}
}
func (r *PermissionRepo) Create(permission *model.Permission) error {
return r.db.Create(permission).Error
}
func (r *PermissionRepo) GetByID(id uint) (*model.Permission, error) {
var permission model.Permission
err := r.db.First(&permission, id).Error
return &permission, err
}
func (r *PermissionRepo) GetByName(name string) (*model.Permission, error) {
var permission model.Permission
err := r.db.Where("name = ?", name).First(&permission).Error
return &permission, err
}
func (r *PermissionRepo) Update(permission *model.Permission) error {
return r.db.Save(permission).Error
}
func (r *PermissionRepo) Delete(id uint) error {
return r.db.Delete(&model.Permission{}, id).Error
}
func (r *PermissionRepo) List(page, pageSize int) ([]model.Permission, int64, error) {
var permissions []model.Permission
var count int64
err := r.db.Model(&model.Permission{}).Count(&count).Error
if err != nil {
return nil, 0, err
}
offset := (page - 1) * pageSize
err = r.db.Offset(offset).Limit(pageSize).Find(&permissions).Error
return permissions, count, err
}
func (r *PermissionRepo) GetByResourceAction(resource, action string) (*model.Permission, error) {
var permission model.Permission
err := r.db.Where("resource = ? AND action = ?", resource, action).First(&permission).Error
return &permission, err
}

61
gofaster/backend/internal/repository/role.go

@ -0,0 +1,61 @@ @@ -0,0 +1,61 @@
package repository
import (
"gofaster/internal/model"
"gorm.io/gorm"
)
type RoleRepo struct {
BaseRepo
}
func NewRoleRepo(db *gorm.DB) *RoleRepo {
return &RoleRepo{BaseRepo{db: db}}
}
func (r *RoleRepo) Create(role *model.Role) error {
return r.db.Create(role).Error
}
func (r *RoleRepo) GetByID(id uint) (*model.Role, error) {
var role model.Role
err := r.db.Preload("Permissions").First(&role, id).Error
return &role, err
}
func (r *RoleRepo) GetByName(name string) (*model.Role, error) {
var role model.Role
err := r.db.Preload("Permissions").Where("name = ?", name).First(&role).Error
return &role, err
}
func (r *RoleRepo) Update(role *model.Role) error {
return r.db.Save(role).Error
}
func (r *RoleRepo) Delete(id uint) error {
return r.db.Delete(&model.Role{}, id).Error
}
func (r *RoleRepo) List(page, pageSize int) ([]model.Role, int64, error) {
var roles []model.Role
var count int64
err := r.db.Model(&model.Role{}).Count(&count).Error
if err != nil {
return nil, 0, err
}
offset := (page - 1) * pageSize
err = r.db.Preload("Permissions").Offset(offset).Limit(pageSize).Find(&roles).Error
return roles, count, err
}
func (r *RoleRepo) AssignPermissions(roleID uint, permissionIDs []uint) error {
var permissions []model.Permission
if err := r.db.Find(&permissions, permissionIDs).Error; err != nil {
return err
}
return r.db.Model(&model.Role{BaseModel: model.BaseModel{ID: roleID}}).Association("Permissions").Replace(permissions)
}

61
gofaster/backend/internal/repository/user.go

@ -0,0 +1,61 @@ @@ -0,0 +1,61 @@
package repository
import (
"gofaster/internal/model"
"gorm.io/gorm"
)
type UserRepo struct {
BaseRepo
}
func NewUserRepo(db *gorm.DB) *UserRepo {
return &UserRepo{BaseRepo{db: db}}
}
func (r *UserRepo) Create(user *model.User) error {
return r.db.Create(user).Error
}
func (r *UserRepo) GetByID(id uint) (*model.User, error) {
var user model.User
err := r.db.Preload("Roles.Permissions").First(&user, id).Error
return &user, err
}
func (r *UserRepo) GetByUsername(username string) (*model.User, error) {
var user model.User
err := r.db.Preload("Roles.Permissions").Where("username = ?", username).First(&user).Error
return &user, err
}
func (r *UserRepo) Update(user *model.User) error {
return r.db.Save(user).Error
}
func (r *UserRepo) Delete(id uint) error {
return r.db.Delete(&model.User{}, id).Error
}
func (r *UserRepo) List(page, pageSize int) ([]model.User, int64, error) {
var users []model.User
var count int64
err := r.db.Model(&model.User{}).Count(&count).Error
if err != nil {
return nil, 0, err
}
offset := (page - 1) * pageSize
err = r.db.Preload("Roles").Offset(offset).Limit(pageSize).Find(&users).Error
return users, count, err
}
func (r *UserRepo) AssignRole(userID uint, roleIDs []uint) error {
var roles []model.Role
if err := r.db.Find(&roles, roleIDs).Error; err != nil {
return err
}
return r.db.Model(&model.User{BaseModel: model.BaseModel{ID: userID}}).Association("Roles").Replace(roles)
}

128
gofaster/backend/internal/repository/workflow.go

@ -0,0 +1,128 @@ @@ -0,0 +1,128 @@
package repository
import (
"gofaster/internal/model"
"gorm.io/gorm"
)
type WorkflowRepo struct {
BaseRepo
}
func NewWorkflowRepo(db *gorm.DB) *WorkflowRepo {
return &WorkflowRepo{BaseRepo{db: db}}
}
// Workflow CRUD
func (r *WorkflowRepo) CreateWorkflow(workflow *model.Workflow) error {
return r.db.Create(workflow).Error
}
func (r *WorkflowRepo) GetWorkflowByID(id uint) (*model.Workflow, error) {
var workflow model.Workflow
err := r.db.Preload("Nodes").First(&workflow, id).Error
return &workflow, err
}
func (r *WorkflowRepo) UpdateWorkflow(workflow *model.Workflow) error {
return r.db.Save(workflow).Error
}
func (r *WorkflowRepo) DeleteWorkflow(id uint) error {
return r.db.Delete(&model.Workflow{}, id).Error
}
func (r *WorkflowRepo) ListWorkflows(page, pageSize int) ([]model.Workflow, int64, error) {
var workflows []model.Workflow
var count int64
err := r.db.Model(&model.Workflow{}).Count(&count).Error
if err != nil {
return nil, 0, err
}
offset := (page - 1) * pageSize
err = r.db.Preload("Nodes").Offset(offset).Limit(pageSize).Find(&workflows).Error
return workflows, count, err
}
// WorkflowNode operations
func (r *WorkflowRepo) AddNode(node *model.WorkflowNode) error {
return r.db.Create(node).Error
}
func (r *WorkflowRepo) UpdateNode(node *model.WorkflowNode) error {
return r.db.Save(node).Error
}
func (r *WorkflowRepo) DeleteNode(id uint) error {
return r.db.Delete(&model.WorkflowNode{}, id).Error
}
func (r *WorkflowRepo) GetNodeByID(id uint) (*model.WorkflowNode, error) {
var node model.WorkflowNode
err := r.db.First(&node, id).Error
return &node, err
}
// WorkflowInstance operations
func (r *WorkflowRepo) CreateInstance(instance *model.WorkflowInstance) error {
return r.db.Create(instance).Error
}
func (r *WorkflowRepo) GetInstanceByID(id uint) (*model.WorkflowInstance, error) {
var instance model.WorkflowInstance
err := r.db.Preload("Tasks").First(&instance, id).Error
return &instance, err
}
func (r *WorkflowRepo) UpdateInstance(instance *model.WorkflowInstance) error {
return r.db.Save(instance).Error
}
func (r *WorkflowRepo) ListInstances(workflowID uint, page, pageSize int) ([]model.WorkflowInstance, int64, error) {
var instances []model.WorkflowInstance
var count int64
query := r.db.Model(&model.WorkflowInstance{})
if workflowID != 0 {
query = query.Where("workflow_id = ?", workflowID)
}
err := query.Count(&count).Error
if err != nil {
return nil, 0, err
}
offset := (page - 1) * pageSize
err = query.Preload("Tasks").Offset(offset).Limit(pageSize).Order("created_at DESC").Find(&instances).Error
return instances, count, err
}
// WorkflowTask operations
func (r *WorkflowRepo) CreateTask(task *model.WorkflowTask) error {
return r.db.Create(task).Error
}
func (r *WorkflowRepo) GetTaskByID(id uint) (*model.WorkflowTask, error) {
var task model.WorkflowTask
err := r.db.First(&task, id).Error
return &task, err
}
func (r *WorkflowRepo) UpdateTask(task *model.WorkflowTask) error {
return r.db.Save(task).Error
}
func (r *WorkflowRepo) ListTasksByInstance(instanceID uint) ([]model.WorkflowTask, error) {
var tasks []model.WorkflowTask
err := r.db.Where("instance_id = ?", instanceID).Order("created_at").Find(&tasks).Error
return tasks, err
}
func (r *WorkflowRepo) ListPendingTasks() ([]model.WorkflowTask, error) {
var tasks []model.WorkflowTask
err := r.db.Where("status = ?", "pending").Order("created_at").Find(&tasks).Error
return tasks, err
}

34
gofaster/backend/internal/service/user.go

@ -0,0 +1,34 @@ @@ -0,0 +1,34 @@
package service
import (
"gofaster/internal/model"
"gofaster/internal/repository"
)
type UserService struct {
repo *repository.UserRepo
}
func NewUserService(repo *repository.UserRepo) *UserService {
return &UserService{repo: repo}
}
func (s *UserService) CreateUser(user *model.User) error {
return s.repo.Create(user)
}
func (s *UserService) GetUserByID(id uint) (*model.User, error) {
return s.repo.GetByID(id)
}
func (s *UserService) UpdateUser(user *model.User) error {
return s.repo.Update(user)
}
func (s *UserService) DeleteUser(id uint) error {
return s.repo.Delete(id)
}
func (s *UserService) ListUsers(page, pageSize int) ([]*model.User, int64, error) {
return s.repo.List(page, pageSize)
}

34
gofaster/backend/internal/service/workflow.go

@ -0,0 +1,34 @@ @@ -0,0 +1,34 @@
package service
import (
"gofaster/internal/model"
"gorm.io/gorm"
)
type WorkflowEngine struct {
db *gorm.DB
}
func NewWorkflowEngine(db *gorm.DB) *WorkflowEngine {
return &WorkflowEngine{db: db}
}
func (e *WorkflowEngine) StartWorkflow(workflowID uint, data map[string]interface{}) (*model.WorkflowInstance, error) {
// 创建工作流实例
// 找到开始节点
// 创建第一个任务
// 返回实例
}
func (e *WorkflowEngine) ProcessTask(taskID uint, userID uint, result map[string]interface{}) error {
// 处理任务
// 根据节点类型执行不同逻辑
// 更新任务状态
// 根据结果决定下一个节点
// 创建下一个任务
}
func (e *WorkflowEngine) GetUserTasks(userID uint, status string) ([]*model.WorkflowTask, error) {
// 获取用户的任务列表
}

44
gofaster/backend/pkg/auth/jwt.go

@ -0,0 +1,44 @@ @@ -0,0 +1,44 @@
package auth
import (
"time"
"github.com/golang-jwt/jwt/v5"
)
type Claims struct {
UserID uint `json:"user_id"`
jwt.RegisteredClaims
}
func GenerateToken(userID uint, secret string, expireHours int) (string, error) {
expireTime := time.Now().Add(time.Hour * time.Duration(expireHours))
claims := Claims{
UserID: userID,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(expireTime),
IssuedAt: jwt.NewNumericDate(time.Now()),
Issuer: "go-admin-platform",
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString([]byte(secret))
}
func ParseToken(tokenString, secret string) (*Claims, error) {
token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
return []byte(secret), nil
})
if err != nil {
return nil, err
}
if claims, ok := token.Claims.(*Claims); ok && token.Valid {
return claims, nil
}
return nil, jwt.ErrInvalidKey
}

22
gofaster/backend/pkg/database/db.go

@ -0,0 +1,22 @@ @@ -0,0 +1,22 @@
package database
import (
"fmt"
"gofaster/internal/config"
"gorm.io/driver/postgres"
"gorm.io/gorm"
)
func NewDB(cfg *config.DBConfig) (*gorm.DB, error) {
dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%s sslmode=disable TimeZone=Asia/Shanghai",
cfg.Host, cfg.User, cfg.Password, cfg.Name, cfg.Port)
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil {
return nil, err
}
// 自动迁移将在业务模块中单独处理
return db, nil
}

45
gofaster/frontend/.gitignore vendored

@ -0,0 +1,45 @@ @@ -0,0 +1,45 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.build/
.buildlog/
.history
.svn/
.swiftpm/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.pub-cache/
.pub/
/build/
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release

30
gofaster/frontend/.metadata

@ -0,0 +1,30 @@ @@ -0,0 +1,30 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: "ea121f8859e4b13e47a8f845e4586164519588bc"
channel: "stable"
project_type: app
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: ea121f8859e4b13e47a8f845e4586164519588bc
base_revision: ea121f8859e4b13e47a8f845e4586164519588bc
- platform: web
create_revision: ea121f8859e4b13e47a8f845e4586164519588bc
base_revision: ea121f8859e4b13e47a8f845e4586164519588bc
# User provided section
# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'

16
gofaster/frontend/README.md

@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
# frontend
A new Flutter project.
## Getting Started
This project is a starting point for a Flutter application.
A few resources to get you started if this is your first Flutter project:
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
For help getting started with Flutter development, view the
[online documentation](https://docs.flutter.dev/), which offers tutorials,
samples, guidance on mobile development, and a full API reference.

28
gofaster/frontend/analysis_options.yaml

@ -0,0 +1,28 @@ @@ -0,0 +1,28 @@
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.
# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml
linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at https://dart.dev/lints.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options

29
gofaster/frontend/lib/api/dio.dart

@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
import 'package:dio/dio.dart';
class ApiClient {
final Dio _dio;
final String baseUrl;
ApiClient({required this.baseUrl}) : _dio = Dio(BaseOptions(baseUrl: baseUrl)) {
_dio.interceptors.add(InterceptorsWrapper(
onRequest: (options, handler) {
// token等
return handler.next(options);
},
onError: (error, handler) {
//
return handler.next(error);
},
));
}
Future<Response> get(String path, {Map<String, dynamic>? params}) async {
return _dio.get(path, queryParameters: params);
}
Future<Response> post(String path, dynamic data) async {
return _dio.post(path, data: data);
}
// HTTP方法...
}

122
gofaster/frontend/lib/main.dart

@ -0,0 +1,122 @@ @@ -0,0 +1,122 @@
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// TRY THIS: Try running your application with "flutter run". You'll see
// the application has a purple toolbar. Then, without quitting the app,
// try changing the seedColor in the colorScheme below to Colors.green
// and then invoke "hot reload" (save your changes or press the "hot
// reload" button in a Flutter-supported IDE, or press "r" if you used
// the command line to start the app).
//
// Notice that the counter didn't reset back to zero; the application
// state is not lost during the reload. To reset the state, use hot
// restart instead.
//
// This works for code too, not just values: Most code changes can be
// tested with just a hot reload.
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
_counter++;
});
}
@override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
appBar: AppBar(
// TRY THIS: Try changing the color here to a specific color (to
// Colors.amber, perhaps?) and trigger a hot reload to see the AppBar
// change color while the other colors stay the same.
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
// Column is also a layout widget. It takes a list of children and
// arranges them vertically. By default, it sizes itself to fit its
// children horizontally, and tries to be as tall as its parent.
//
// Column has various properties to control how it sizes itself and
// how it positions its children. Here we use mainAxisAlignment to
// center the children vertically; the main axis here is the vertical
// axis because Columns are vertical (the cross axis would be
// horizontal).
//
// TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint"
// action in the IDE, or press "p" in the console), to see the
// wireframe for each widget.
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text('You have pushed the button this many times:'),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}

41
gofaster/frontend/lib/pages/users/list.dart

@ -0,0 +1,41 @@ @@ -0,0 +1,41 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class UserListPage extends ConsumerWidget {
const UserListPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final usersAsync = ref.watch(usersProvider);
return Scaffold(
appBar: AppBar(title: const Text('用户管理')),
body: usersAsync.when(
loading: () => const Center(child: CircularProgressIndicator()),
error: (error, stack) => Center(child: Text('Error: $error')),
data: (users) => ListView.builder(
itemCount: users.length,
itemBuilder: (context, index) {
final user = users[index];
return ListTile(
title: Text(user['username']),
subtitle: Text(user['email']),
trailing: IconButton(
icon: const Icon(Icons.edit),
onPressed: () {
//
},
),
);
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
//
},
child: const Icon(Icons.add),
),
);
}
}

23
gofaster/frontend/lib/services/user_service.dart

@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../api/client.dart';
class UserService {
final ApiClient _client;
UserService(this._client);
Future<Map<String, dynamic>> listUsers({int page = 1, int pageSize = 10}) async {
final response = await _client.get('/users', params: {
'page': page,
'pageSize': pageSize,
});
return response.data;
}
// ...
}
final userServiceProvider = Provider<UserService>((ref) {
final client = ref.read(apiClientProvider);
return UserService(client);
});

277
gofaster/frontend/pubspec.lock

@ -0,0 +1,277 @@ @@ -0,0 +1,277 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
async:
dependency: transitive
description:
name: async
sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.12.0"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.2"
characters:
dependency: transitive
description:
name: characters
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.4.0"
clock:
dependency: transitive
description:
name: clock
sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.2"
collection:
dependency: transitive
description:
name: collection
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.19.1"
cupertino_icons:
dependency: "direct main"
description:
name: cupertino_icons
sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.8"
dio:
dependency: "direct main"
description:
name: dio
sha256: "253a18bbd4851fecba42f7343a1df3a9a4c1d31a2c1b37e221086b4fa8c8dbc9"
url: "https://pub.flutter-io.cn"
source: hosted
version: "5.8.0+1"
dio_web_adapter:
dependency: transitive
description:
name: dio_web_adapter
sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.1"
fake_async:
dependency: transitive
description:
name: fake_async
sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.3.2"
flutter:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_lints:
dependency: "direct dev"
description:
name: flutter_lints
sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1"
url: "https://pub.flutter-io.cn"
source: hosted
version: "5.0.0"
flutter_riverpod:
dependency: "direct main"
description:
name: flutter_riverpod
sha256: "9532ee6db4a943a1ed8383072a2e3eeda041db5657cdf6d2acecf3c21ecbe7e1"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.6.1"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
http_parser:
dependency: transitive
description:
name: http_parser
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.1.2"
leak_tracker:
dependency: transitive
description:
name: leak_tracker
sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec
url: "https://pub.flutter-io.cn"
source: hosted
version: "10.0.8"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.9"
leak_tracker_testing:
dependency: transitive
description:
name: leak_tracker_testing
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.1"
lints:
dependency: transitive
description:
name: lints
sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7
url: "https://pub.flutter-io.cn"
source: hosted
version: "5.1.1"
matcher:
dependency: transitive
description:
name: matcher
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.12.17"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.11.1"
meta:
dependency: transitive
description:
name: meta
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.16.0"
path:
dependency: transitive
description:
name: path
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.9.1"
riverpod:
dependency: "direct main"
description:
name: riverpod
sha256: "59062512288d3056b2321804332a13ffdd1bf16df70dcc8e506e411280a72959"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.6.1"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
source_span:
dependency: transitive
description:
name: source_span
sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.10.1"
stack_trace:
dependency: transitive
description:
name: stack_trace
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.12.1"
state_notifier:
dependency: transitive
description:
name: state_notifier
sha256: b8677376aa54f2d7c58280d5a007f9e8774f1968d1fb1c096adcb4792fba29bb
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.0"
stream_channel:
dependency: transitive
description:
name: stream_channel
sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.4"
string_scanner:
dependency: transitive
description:
name: string_scanner
sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.4.1"
term_glyph:
dependency: transitive
description:
name: term_glyph
sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.2.2"
test_api:
dependency: transitive
description:
name: test_api
sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.7.4"
typed_data:
dependency: transitive
description:
name: typed_data
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.4.0"
vector_math:
dependency: transitive
description:
name: vector_math
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.4"
vm_service:
dependency: transitive
description:
name: vm_service
sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14"
url: "https://pub.flutter-io.cn"
source: hosted
version: "14.3.1"
web:
dependency: transitive
description:
name: web
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.1"
sdks:
dart: ">=3.7.2 <4.0.0"
flutter: ">=3.18.0-18.0.pre.54"

92
gofaster/frontend/pubspec.yaml

@ -0,0 +1,92 @@ @@ -0,0 +1,92 @@
name: frontend
description: "A new Flutter project."
# The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively.
# In Android, build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
version: 1.0.0+1
environment:
sdk: ^3.7.2
# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
# consider running `flutter pub upgrade --major-versions`. Alternatively,
# dependencies can be manually updated by changing the version numbers below to
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.8
dio: ^5.8.0+1
riverpod: ^2.6.1
flutter_riverpod: ^2.6.1
dev_dependencies:
flutter_test:
sdk: flutter
# The "flutter_lints" package below contains a set of recommended lints to
# encourage good coding practices. The lint set provided by the package is
# activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^5.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter packages.
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
# To add assets to your application, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/to/resolution-aware-images
# For details regarding adding assets from package dependencies, see
# https://flutter.dev/to/asset-from-package
# To add custom fonts to your application, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts from package dependencies,
# see https://flutter.dev/to/font-from-package

30
gofaster/frontend/test/widget_test.dart

@ -0,0 +1,30 @@ @@ -0,0 +1,30 @@
// This is a basic Flutter widget test.
//
// To perform an interaction with a widget in your test, use the WidgetTester
// utility in the flutter_test package. For example, you can send tap and scroll
// gestures. You can also use WidgetTester to find child widgets in the widget
// tree, read text, and verify that the values of widget properties are correct.
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:frontend/main.dart';
void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(const MyApp());
// Verify that our counter starts at 0.
expect(find.text('0'), findsOneWidget);
expect(find.text('1'), findsNothing);
// Tap the '+' icon and trigger a frame.
await tester.tap(find.byIcon(Icons.add));
await tester.pump();
// Verify that our counter has incremented.
expect(find.text('0'), findsNothing);
expect(find.text('1'), findsOneWidget);
});
}

BIN
gofaster/frontend/web/favicon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 917 B

BIN
gofaster/frontend/web/icons/Icon-192.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

BIN
gofaster/frontend/web/icons/Icon-512.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

BIN
gofaster/frontend/web/icons/Icon-maskable-192.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

BIN
gofaster/frontend/web/icons/Icon-maskable-512.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

38
gofaster/frontend/web/index.html

@ -0,0 +1,38 @@ @@ -0,0 +1,38 @@
<!DOCTYPE html>
<html>
<head>
<!--
If you are serving your web app in a path other than the root, change the
href value below to reflect the base path you are serving from.
The path provided below has to start and end with a slash "/" in order for
it to work correctly.
For more details:
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
This is a placeholder for base href that will be replaced by the value of
the `--base-href` argument provided to `flutter build`.
-->
<base href="$FLUTTER_BASE_HREF">
<meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="A new Flutter project.">
<!-- iOS meta tags & icons -->
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="frontend">
<link rel="apple-touch-icon" href="icons/Icon-192.png">
<!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png"/>
<title>frontend</title>
<link rel="manifest" href="manifest.json">
</head>
<body>
<script src="flutter_bootstrap.js" async></script>
</body>
</html>

35
gofaster/frontend/web/manifest.json

@ -0,0 +1,35 @@ @@ -0,0 +1,35 @@
{
"name": "frontend",
"short_name": "frontend",
"start_url": ".",
"display": "standalone",
"background_color": "#0175C2",
"theme_color": "#0175C2",
"description": "A new Flutter project.",
"orientation": "portrait-primary",
"prefer_related_applications": false,
"icons": [
{
"src": "icons/Icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "icons/Icon-512.png",
"sizes": "512x512",
"type": "image/png"
},
{
"src": "icons/Icon-maskable-192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "icons/Icon-maskable-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
]
}
Loading…
Cancel
Save