已开启
feat: 消息全部功能实现 #23
jiax0274创建于 2025年11月28日
feat: 消息全部功能实现 #23
已开启
共 508 个文件变更+19732-823415
| @@ -1,29 +0,0 @@ | |||
| 1 | -docker/data/mysql/ | ||
| 2 | -docker/data/mysql/* | ||
| 3 | -docker/data/grafana/ | ||
| 4 | -docker/data/grafana/* | ||
| 5 | -docker/data/prometheus/ | ||
| 6 | -docker/data/prometheus/* | ||
| 7 | -docker/data/redis/ | ||
| 8 | -docker/data/redis/* | ||
| 9 | -docker/data/etcd/ | ||
| 10 | -docker/data/etcd/* | ||
| 11 | -docker/data/elasticsearch/ | ||
| 12 | -docker/data/elasticsearch/* | ||
| 13 | -docker/data/kafka/ | ||
| 14 | -docker/data/kafka/* | ||
| 15 | -docker/data/kibana/ | ||
| 16 | -docker/data/kibana/* | ||
| 17 | - | ||
| 18 | -.git | ||
| 19 | -.gitignore | ||
| 20 | -.env | ||
| 21 | -*.md | ||
| 22 | -.editorconfig | ||
| 23 | -.golangci.yml | ||
| 24 | -docker-compose.yml | ||
| 25 | -docker-compose.ci.yml | ||
| 26 | -README.md | ||
| 27 | -/test | ||
| 28 | -/.github | ||
| 29 | -*.log | ||
| @@ -1,18 +0,0 @@ | |||
| 1 | -#### 这个 PR 的类型是什么? | ||
| 2 | -<!-- | ||
| 3 | -添加以下类型的一种: | ||
| 4 | - | ||
| 5 | -build: 影响构建系统或外部依赖项的更改 (常用 scope: gulp, broccoli, npm) | ||
| 6 | -ci: 更改我们的 CI 配置文件和脚本 (常用 scope: Travis, Circle, BrowserStack, SauceLabs) | ||
| 7 | -docs: 只包含文档的更改 | ||
| 8 | -feat: 一个新的特性 | ||
| 9 | -optimize: 对已有代码的优化 | ||
| 10 | -fix: 修正 bug | ||
| 11 | -perf: 对代码的性能提升 | ||
| 12 | -refactor: 重构,或代码更改既没有修复错误也没有添加功能 | ||
| 13 | -style: 不影响代码含义的更改 (空白行/空格, 格式优化, 缺失的分号, etc.) | ||
| 14 | -test: 添加缺失的测试或更正现有的测试 | ||
| 15 | -chore: 构建过程或辅助工具和库(如文档生成)的变更 | ||
| 16 | ---> | ||
| 17 | - | ||
| 18 | - | ||
| @@ -1,42 +0,0 @@ | |||
| 1 | -name: Build and Push Docker Image | ||
| 2 | - | ||
| 3 | -on: | ||
| 4 | - workflow_dispatch: | ||
| 5 | - inputs: | ||
| 6 | - service: | ||
| 7 | - description: "Select the service to build" | ||
| 8 | - required: true | ||
| 9 | - type: choice | ||
| 10 | - options: | ||
| 11 | - - gateway | ||
| 12 | - - user | ||
| 13 | - - assistant | ||
| 14 | - - commodity | ||
| 15 | - - cart | ||
| 16 | - - order | ||
| 17 | - - payment | ||
| 18 | - | ||
| 19 | -jobs: | ||
| 20 | - build: | ||
| 21 | - runs-on: ubuntu-latest | ||
| 22 | - | ||
| 23 | - steps: | ||
| 24 | - - name: Check out the code | ||
| 25 | - uses: actions/checkout@v4 | ||
| 26 | - | ||
| 27 | - - name: Log in to Alibaba Cloud Docker Registry | ||
| 28 | - uses: docker/login-action@v3 | ||
| 29 | - with: | ||
| 30 | - registry: ${{ secrets.ALIYUN_DOCKER_REGISTRY }} | ||
| 31 | - username: ${{ secrets.ALIYUN_DOCKER_USER }} | ||
| 32 | - password: ${{ secrets.ALIYUN_DOCKER_PASSWORD }} | ||
| 33 | - | ||
| 34 | - - name: Ensure build script is executable | ||
| 35 | - run: chmod +x ./hack/image-build-and-push.sh | ||
| 36 | - | ||
| 37 | - - name: Convert service name to lowercase | ||
| 38 | - run: echo "SERVICE=$(echo '${{ github.event.inputs.service }}' | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV | ||
| 39 | - | ||
| 40 | - - name: Build and Push Docker Image | ||
| 41 | - run: | | ||
| 42 | - cd $GITHUB_WORKSPACE && bash ./hack/image-build-and-push.sh $SERVICE | ||
| @@ -1,64 +0,0 @@ | |||
| 1 | -name: CI | ||
| 2 | - | ||
| 3 | -on: | ||
| 4 | - push: | ||
| 5 | - branches: | ||
| 6 | - - main | ||
| 7 | - - dev | ||
| 8 | - pull_request: | ||
| 9 | - branches: | ||
| 10 | - - main | ||
| 11 | - - dev | ||
| 12 | - | ||
| 13 | -permissions: | ||
| 14 | - contents: read | ||
| 15 | - | ||
| 16 | -jobs: | ||
| 17 | - fmt: | ||
| 18 | - name: Format | ||
| 19 | - runs-on: ubuntu-latest | ||
| 20 | - steps: | ||
| 21 | - - uses: actions/checkout@v4 | ||
| 22 | - - uses: actions/setup-go@v5 | ||
| 23 | - with: | ||
| 24 | - go-version-file: go.mod | ||
| 25 | - cache: false | ||
| 26 | - - name: Check Go modules | ||
| 27 | - run: | | ||
| 28 | - go mod tidy && git add go.* && | ||
| 29 | - git diff --cached --exit-code || (echo 'Please run "go mod tidy" to sync Go modules' && exit 1); | ||
| 30 | - - name: Verify gofumpt | ||
| 31 | - run: | | ||
| 32 | - echo "refer to https://github.com/mvdan/gofumpt for detailed info" && | ||
| 33 | - GO111MODULE=on go install mvdan.cc/gofumpt@latest && | ||
| 34 | - make fmt && git add pkg cmd && | ||
| 35 | - git diff --cached --exit-code || (echo 'Please run "make fmt" to verify fmt' && exit 1); | ||
| 36 | - | ||
| 37 | - vet: | ||
| 38 | - name: Vet | ||
| 39 | - runs-on: ubuntu-latest | ||
| 40 | - steps: | ||
| 41 | - - uses: actions/checkout@v4 | ||
| 42 | - - uses: actions/setup-go@v5 | ||
| 43 | - with: | ||
| 44 | - go-version-file: go.mod | ||
| 45 | - cache: false | ||
| 46 | - - name: Verify govet | ||
| 47 | - run: | | ||
| 48 | - make vet && git add pkg cmd && | ||
| 49 | - git diff --cached --exit-code || (echo 'Please run "make vet" to verify govet' && exit 1); | ||
| 50 | - | ||
| 51 | - lint: | ||
| 52 | - name: lint | ||
| 53 | - runs-on: ubuntu-latest | ||
| 54 | - steps: | ||
| 55 | - - uses: actions/checkout@v4 | ||
| 56 | - - uses: actions/setup-go@v5 | ||
| 57 | - with: | ||
| 58 | - go-version-file: go.mod | ||
| 59 | - cache: false | ||
| 60 | - - name: golangci-lint | ||
| 61 | - uses: golangci/golangci-lint-action@v6 | ||
| 62 | - with: | ||
| 63 | - version: latest | ||
| 64 | - args: --timeout=10m | ||
| @@ -1,43 +0,0 @@ | |||
| 1 | -name: "CodeQL" | ||
| 2 | - | ||
| 3 | -on: | ||
| 4 | - push: | ||
| 5 | - branches: | ||
| 6 | - - main | ||
| 7 | - - dev | ||
| 8 | - pull_request: | ||
| 9 | - branches: | ||
| 10 | - - main | ||
| 11 | - - dev | ||
| 12 | - | ||
| 13 | -jobs: | ||
| 14 | - analyze: | ||
| 15 | - name: Analyze | ||
| 16 | - runs-on: ubuntu-latest | ||
| 17 | - timeout-minutes: 360 | ||
| 18 | - permissions: | ||
| 19 | - actions: read | ||
| 20 | - contents: read | ||
| 21 | - security-events: write | ||
| 22 | - | ||
| 23 | - strategy: | ||
| 24 | - fail-fast: false | ||
| 25 | - matrix: | ||
| 26 | - language: [ 'go' ] | ||
| 27 | - | ||
| 28 | - steps: | ||
| 29 | - - name: Checkout infrastructure | ||
| 30 | - uses: actions/checkout@v4 | ||
| 31 | - | ||
| 32 | - - name: Initialize CodeQL | ||
| 33 | - uses: github/codeql-action/init@v3 | ||
| 34 | - with: | ||
| 35 | - languages: ${{ matrix.language }} | ||
| 36 | - | ||
| 37 | - - name: Autobuild | ||
| 38 | - uses: github/codeql-action/autobuild@v3 | ||
| 39 | - | ||
| 40 | - - name: Perform CodeQL Analysis | ||
| 41 | - uses: github/codeql-action/analyze@v3 | ||
| 42 | - with: | ||
| 43 | - category: "/language:${{matrix.language}}" | ||
| @@ -1,36 +0,0 @@ | |||
| 1 | -name: Run govulncheck | ||
| 2 | - | ||
| 3 | -on: | ||
| 4 | - push: | ||
| 5 | - branches: | ||
| 6 | - - develop | ||
| 7 | - paths: | ||
| 8 | - - "**" | ||
| 9 | - - "!**.md" | ||
| 10 | - pull_request: | ||
| 11 | - paths: | ||
| 12 | - - "**" | ||
| 13 | - - "!**.md" | ||
| 14 | - | ||
| 15 | -jobs: | ||
| 16 | - vulncheck-check: | ||
| 17 | - runs-on: ubuntu-latest | ||
| 18 | - env: | ||
| 19 | - GO111MODULE: on | ||
| 20 | - steps: | ||
| 21 | - - name: Fetch Repository | ||
| 22 | - uses: actions/checkout@v4 | ||
| 23 | - | ||
| 24 | - - name: Install Go | ||
| 25 | - uses: actions/setup-go@v5 | ||
| 26 | - with: | ||
| 27 | - go-version: stable | ||
| 28 | - check-latest: true | ||
| 29 | - cache: false | ||
| 30 | - | ||
| 31 | - - name: Install Govulncheck | ||
| 32 | - run: go install golang.org/x/vuln/cmd/govulncheck@latest | ||
| 33 | - | ||
| 34 | - - name: Run Govulncheck | ||
| 35 | - run: govulncheck ./... | ||
| 36 | - | ||
| @@ -1,50 +1,45 @@ | |||
| 1 | -*.o | 1 | +# Miscellaneous |
| 2 | -*.a | 2 | +*.class |
| 3 | -*.so | ||
| 4 | -_obj | ||
| 5 | -config/config.yaml | ||
| 6 | -config/config.yaml.bak | ||
| 7 | -_test | ||
| 8 | -*.[568vq] | ||
| 9 | -[568vq].out | ||
| 10 | -*.cgo1.go | ||
| 11 | -*.cgo2.c | ||
| 12 | -src/storage/ | ||
| 13 | -_cgo_defun.c | ||
| 14 | -_cgo_gotypes.go | ||
| 15 | -_cgo_export.* | ||
| 16 | -_testmain.go | ||
| 17 | -*.exe | ||
| 18 | -*.exe~ | ||
| 19 | -*.test | ||
| 20 | -*.prof | ||
| 21 | -*.rar | ||
| 22 | -*.zip | ||
| 23 | -*.gz | ||
| 24 | -*.psd | ||
| 25 | -*.bmd | ||
| 26 | -*.cfg | ||
| 27 | -*.pptx | ||
| 28 | *.log | 3 | *.log |
| 29 | -*nohup.out | 4 | +*.pyc |
| 30 | -*settings.pyc | 5 | +*.swp |
| 31 | -*.sublime-project | ||
| 32 | -*.sublime-workspace | ||
| 33 | -!.gitkeep.DS_Store | ||
| 34 | -/.idea | ||
| 35 | -/.vscode | ||
| 36 | -/output | ||
| 37 | -*.local.yml | ||
| 38 | -dumped_hertz_remote_config.json | ||
| 39 | -docker/data/ | ||
| 40 | -docker/data/mysql/ | ||
| 41 | -docker/data/mysql/* | ||
| 42 | -node_modules/ | ||
| 43 | -dist/ | ||
| 44 | .DS_Store | 6 | .DS_Store |
| 45 | -.env.local | 7 | +.atom/ |
| 46 | -.env.*.local | 8 | +.build/ |
| 47 | -npm-debug.log* | 9 | +.buildlog/ |
| 48 | -yarn-debug.log* | 10 | +.history |
| 49 | -yarn-error.log* | 11 | +.svn/ |
| 50 | -.env | 12 | +.swiftpm/ |
| 13 | +migrate_working_dir/ | ||
| 14 | + | ||
| 15 | +# IntelliJ related | ||
| 16 | +*.iml | ||
| 17 | +*.ipr | ||
| 18 | +*.iws | ||
| 19 | +.idea/ | ||
| 20 | + | ||
| 21 | +# The .vscode folder contains launch configuration and tasks you configure in | ||
| 22 | +# VS Code which you may wish to be included in version control, so this line | ||
| 23 | +# is commented out by default. | ||
| 24 | +#.vscode/ | ||
| 25 | + | ||
| 26 | +# Flutter/Dart/Pub related | ||
| 27 | +**/doc/api/ | ||
| 28 | +**/ios/Flutter/.last_build_id | ||
| 29 | +.dart_tool/ | ||
| 30 | +.flutter-plugins-dependencies | ||
| 31 | +.pub-cache/ | ||
| 32 | +.pub/ | ||
| 33 | +/build/ | ||
| 34 | +/coverage/ | ||
| 35 | + | ||
| 36 | +# Symbolication related | ||
| 37 | +app.*.symbols | ||
| 38 | + | ||
| 39 | +# Obfuscation related | ||
| 40 | +app.*.map.json | ||
| 41 | + | ||
| 42 | +# Android Studio will place build artifacts here | ||
| 43 | +/android/app/debug | ||
| 44 | +/android/app/profile | ||
| 45 | +/android/app/release | ||
| @@ -1,223 +0,0 @@ | |||
| 1 | -# options for analysis running | ||
| 2 | -run: | ||
| 3 | - # default concurrency is the available CPU number | ||
| 4 | - concurrency: 16 | ||
| 5 | - # timeout for analysis, e.g. 30s, 5m, default is 1m | ||
| 6 | - timeout: 10m | ||
| 7 | - # exit code when at least one issue was found, default is 1 | ||
| 8 | - issues-exit-code: 1 | ||
| 9 | - # include test files or not, default is true | ||
| 10 | - tests: true | ||
| 11 | - # which dirs to skip: they won't be analyzed; | ||
| 12 | - # can use regexp here: generated.*, regexp is applied on full path; | ||
| 13 | - # default value is empty list, but next dirs are always skipped independently | ||
| 14 | - # from this option's value: | ||
| 15 | - # third_party$, testdata$, examples$, Godeps$, builtin$ | ||
| 16 | - # skip-dirs: | ||
| 17 | - | ||
| 18 | -# output configuration options | ||
| 19 | -output: | ||
| 20 | - # print lines of code with issue, default is true | ||
| 21 | - print-issued-lines: true | ||
| 22 | - # print linter name in the end of issue text, default is true | ||
| 23 | - print-linter-name: true | ||
| 24 | - | ||
| 25 | - | ||
| 26 | -# all available settings of specific linters | ||
| 27 | -linters-settings: | ||
| 28 | - nakedret: | ||
| 29 | - # Make an issue if func has more lines of code than this setting, and it has naked returns. | ||
| 30 | - # Default: 30 | ||
| 31 | - max-func-lines: 31 | ||
| 32 | - gofmt: | ||
| 33 | - # simplify code: gofmt with `-s` option, true by default | ||
| 34 | - simplify: true | ||
| 35 | - goimports: | ||
| 36 | - # put imports beginning with prefix after 3rd-party packages; | ||
| 37 | - # it's a comma-separated list of prefixes | ||
| 38 | - local-prefixes: github.com/west2-online | ||
| 39 | - gci: | ||
| 40 | - # Section configuration to compare against. | ||
| 41 | - # Section names are case-insensitive and may contain parameters in (). | ||
| 42 | - # The default order of sections is `standard > default > custom > blank > dot > alias > localmodule`, | ||
| 43 | - # If `custom-order` is `true`, it follows the order of `sections` option. | ||
| 44 | - # Default: ["standard", "default"] | ||
| 45 | - sections: | ||
| 46 | - - standard # Standard section: captures all standard packages. | ||
| 47 | - - default # Default section: contains all imports that could not be matched to another section type. | ||
| 48 | - - prefix(github.com/west2-online) # Custom section: groups all imports with the specified Prefix. | ||
| 49 | - # - blank # Blank section: contains all blank imports. This section is not present unless explicitly enabled. | ||
| 50 | - # - dot # Dot section: contains all dot imports. This section is not present unless explicitly enabled. | ||
| 51 | - # - alias # Alias section: contains all alias imports. This section is not present unless explicitly enabled. | ||
| 52 | - # - localmodule # Local module section: contains all local packages. This section is not present unless explicitly enabled. | ||
| 53 | - # Skip generated files. | ||
| 54 | - # Default: true | ||
| 55 | - skip-generated: true | ||
| 56 | - # Enable custom order of sections. | ||
| 57 | - # If `true`, make the section order the same as the order of `sections`. | ||
| 58 | - # Default: false | ||
| 59 | - custom-order: true | ||
| 60 | - # Drops lexical ordering for custom sections. | ||
| 61 | - # Default: false | ||
| 62 | - no-lex-order: true | ||
| 63 | - nilnil: | ||
| 64 | - # List of return types to check. | ||
| 65 | - # Default: ["ptr", "func", "iface", "map", "chan", "uintptr", "unsafeptr"] | ||
| 66 | - checked-types: | ||
| 67 | - - ptr | ||
| 68 | - - func | ||
| 69 | - - iface | ||
| 70 | - - map | ||
| 71 | - - chan | ||
| 72 | - - uintptr | ||
| 73 | - - unsafeptr | ||
| 74 | - errorlint: | ||
| 75 | - # Check whether fmt.Errorf uses the %w verb for formatting errors. | ||
| 76 | - # See the https://github.com/polyfloyd/go-errorlint for caveats. | ||
| 77 | - errorf: true | ||
| 78 | - # Permit more than 1 %w verb, valid per Go 1.20 (Requires errorf:true) | ||
| 79 | - # Default: true | ||
| 80 | - errorf-multi: true | ||
| 81 | - # Check for plain type assertions and type switches. | ||
| 82 | - # Default: true | ||
| 83 | - asserts: true | ||
| 84 | - # Check for plain error comparisons. | ||
| 85 | - # Default: true | ||
| 86 | - comparison: true | ||
| 87 | - # Allowed errors. | ||
| 88 | - # Default: [] | ||
| 89 | - allowed-errors: | ||
| 90 | - - err: "io.EOF" | ||
| 91 | - fun: "example.com/pkg.Read" | ||
| 92 | - # Allowed error "wildcards". | ||
| 93 | - # Default: [] | ||
| 94 | - allowed-errors-wildcard: | ||
| 95 | - - err: "github.com/west2-online/domtok/pkg/errno" | ||
| 96 | - fun: "github.com/west2-online/domtok/pkg/errno" | ||
| 97 | - misspell: | ||
| 98 | - # Correct spellings using locale preferences for US or UK. | ||
| 99 | - # Default is to use a neutral variety of English. | ||
| 100 | - # Setting locale to US will correct the British spelling of 'colour' to 'color'. | ||
| 101 | - locale: US | ||
| 102 | - staticcheck: | ||
| 103 | - checks: | ||
| 104 | - - all | ||
| 105 | - - -SA1019 | ||
| 106 | - lll: | ||
| 107 | - # Max line length, lines longer will be reported. | ||
| 108 | - # '\t' is counted as 1 character by default, and can be changed with the tab-width option. | ||
| 109 | - # Default: 120. | ||
| 110 | - line-length: 160 | ||
| 111 | - # Tab width in spaces. | ||
| 112 | - # Default: 1 | ||
| 113 | - tab-width: 1 | ||
| 114 | - mnd: | ||
| 115 | - # List of enabled checks, see https://github.com/tommy-muehle/go-mnd/#checks for description. | ||
| 116 | - # Default: ["argument", "case", "condition", "operation", "return", "assign"] | ||
| 117 | - checks: | ||
| 118 | - - argument | ||
| 119 | - - case | ||
| 120 | - - condition | ||
| 121 | - - operation | ||
| 122 | - - return | ||
| 123 | - - assign | ||
| 124 | - # List of numbers to exclude from analysis. | ||
| 125 | - # The numbers should be written as string. | ||
| 126 | - # Values always ignored: "1", "1.0", "0" and "0.0" | ||
| 127 | - # Default: [] | ||
| 128 | - ignored-numbers: | ||
| 129 | - - '0666' | ||
| 130 | - - '0755' | ||
| 131 | - - '42' | ||
| 132 | - # List of file patterns to exclude from analysis. | ||
| 133 | - # Values always ignored: `.+_test.go` | ||
| 134 | - # Default: [] | ||
| 135 | - ignored-files: | ||
| 136 | - - 'magic1_.+\.go$' | ||
| 137 | - # List of function patterns to exclude from analysis. | ||
| 138 | - # Following functions are always ignored: `time.Date`, | ||
| 139 | - # `strconv.FormatInt`, `strconv.FormatUint`, `strconv.FormatFloat`, | ||
| 140 | - # `strconv.ParseInt`, `strconv.ParseUint`, `strconv.ParseFloat`. | ||
| 141 | - # Default: [] | ||
| 142 | - ignored-functions: | ||
| 143 | - - '^math\.' | ||
| 144 | - - '^http\.StatusText$' | ||
| 145 | - makezero: | ||
| 146 | - # Allow only slices initialized with a length of zero. | ||
| 147 | - # Default: false | ||
| 148 | - always: false | ||
| 149 | - nestif: | ||
| 150 | - # Minimal complexity of if statements to report. | ||
| 151 | - # Default: 5 | ||
| 152 | - min-complexity: 6 | ||
| 153 | - tagalign: | ||
| 154 | - # Align and sort can be used together or separately. | ||
| 155 | - # | ||
| 156 | - # Whether enable align. If true, the struct tags will be aligned. | ||
| 157 | - # e.g.: | ||
| 158 | - # type FooBar struct { | ||
| 159 | - # Bar string `json:"bar" validate:"required"` | ||
| 160 | - # FooFoo int8 `json:"foo_foo" validate:"required"` | ||
| 161 | - # } | ||
| 162 | - # will be formatted to: | ||
| 163 | - # type FooBar struct { | ||
| 164 | - # Bar string `json:"bar" validate:"required"` | ||
| 165 | - # FooFoo int8 `json:"foo_foo" validate:"required"` | ||
| 166 | - # } | ||
| 167 | - # Default: true. | ||
| 168 | - align: true | ||
| 169 | - # Whether enable tags sort. | ||
| 170 | - # If true, the tags will be sorted by name in ascending order. | ||
| 171 | - # e.g.: `xml:"bar" json:"bar" validate:"required"` -> `json:"bar" validate:"required" xml:"bar"` | ||
| 172 | - # Default: true | ||
| 173 | - sort: true | ||
| 174 | - # Specify the order of tags, the other tags will be sorted by name. | ||
| 175 | - # This option will be ignored if `sort` is false. | ||
| 176 | - # Default: [] | ||
| 177 | - order: | ||
| 178 | - - json | ||
| 179 | - - yaml | ||
| 180 | - - yml | ||
| 181 | - - toml | ||
| 182 | - - mapstructure | ||
| 183 | - - binding | ||
| 184 | - - validate | ||
| 185 | - # Whether enable strict style. | ||
| 186 | - # In this style, the tags will be sorted and aligned in the dictionary order, | ||
| 187 | - # and the tags with the same name will be aligned together. | ||
| 188 | - # Note: This option will be ignored if 'align' or 'sort' is false. | ||
| 189 | - # Default: false | ||
| 190 | - strict: true | ||
| 191 | - | ||
| 192 | -linters: | ||
| 193 | - fast: false | ||
| 194 | - disable-all: true | ||
| 195 | - enable: | ||
| 196 | - - errcheck # 错误检查 | ||
| 197 | - - staticcheck # 静态检查 | ||
| 198 | - - unused # 检查未使用的变量、常量、函数和类型 | ||
| 199 | - - misspell # 检查常见的拼写错误的英文单词 | ||
| 200 | - - nolintlint # 报告不正确或不充分的 nolint 指令 | ||
| 201 | - - goimports # 检查包导入是否按 goimport 命令格式化 | ||
| 202 | - - nakedret # 检查具有裸返回(func with naked returns)的函数包体是否过大 | ||
| 203 | - - unconvert # 删除不必要的类型转换 | ||
| 204 | - - whitespace # 检查不必要的换行符 | ||
| 205 | - - govet # 与 go vet 大致相同 | ||
| 206 | - - gosimple # 简化代码 | ||
| 207 | - - ineffassign # 检测现有变量的赋值何时未被使用 | ||
| 208 | - - gocritic # 提供诊断功能,检查错误、性能和样式问题 | ||
| 209 | - - importas # 强制使用一致的包导入别名 | ||
| 210 | - - gci # 控制包导入顺序 | ||
| 211 | - - errorlint # 检查错误处理代码合规性 | ||
| 212 | - - nilerr # 检查当产生错误时仍然返回 nil 的情况 | ||
| 213 | - - asciicheck # 检查非 ASCII 字符 | ||
| 214 | - - copyloopvar # 检查循环变量是否在闭包中被复制 | ||
| 215 | - - decorder # 检查类型、常量、变量和函数的声明顺序和数量 | ||
| 216 | - - forcetypeassert # 查找强制类型断言 | ||
| 217 | - - lll # 检查行的长度 | ||
| 218 | - - mnd # 检查魔数,魔数应该尽量放置在常量中 | ||
| 219 | - - inamedparam # 检查函数参数的命名 | ||
| 220 | - - makezero # 检查是否存在非零长初始化的切片 | ||
| 221 | - - mirror # 报告 bytes/strings 的错误 mirror 用法 | ||
| 222 | - - nestif # 检查是否存在深度嵌套的 if | ||
| 223 | - - tagalign # 检查结构体标签是否对齐,支持自动修正 | ||
| @@ -1,6 +0,0 @@ | |||
| 1 | -// Code generated by hz. DO NOT EDIT. | ||
| 2 | - | ||
| 3 | -hz version: v0.9.7 | ||
| 4 | -handlerDir: ./app/gateway/handler | ||
| 5 | -modelDir: ./app/gateway/model | ||
| 6 | -routerDir: ./app/gateway/router | ||
| @@ -0,0 +1,30 @@ | |||
| 1 | +# This file tracks properties of this Flutter project. | ||
| 2 | +# Used by Flutter tool to assess capabilities and perform upgrades etc. | ||
| 3 | +# | ||
| 4 | +# This file should be version controlled and should not be manually edited. | ||
| 5 | + | ||
| 6 | +version: | ||
| 7 | + revision: "adc901062556672b4138e18a4dc62a4be8f4b3c2" | ||
| 8 | + channel: "stable" | ||
| 9 | + | ||
| 10 | +project_type: app | ||
| 11 | + | ||
| 12 | +# Tracks metadata for the flutter migrate command | ||
| 13 | +migration: | ||
| 14 | + platforms: | ||
| 15 | + - platform: root | ||
| 16 | + create_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2 | ||
| 17 | + base_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2 | ||
| 18 | + - platform: android | ||
| 19 | + create_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2 | ||
| 20 | + base_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2 | ||
| 21 | + | ||
| 22 | + # User provided section | ||
| 23 | + | ||
| 24 | + # List of Local paths (relative to this file) that should be | ||
| 25 | + # ignored by the migrate tool. | ||
| 26 | + # | ||
| 27 | + # Files that are not part of the templates will be ignored by default. | ||
| 28 | + unmanaged_files: | ||
| 29 | + - 'lib/main.dart' | ||
| 30 | + - 'ios/Runner.xcodeproj/project.pbxproj' | ||
| @@ -1,178 +0,0 @@ | |||
| 1 | -# 辅助工具安装列表 | ||
| 2 | -# 执行 go install github.com/cloudwego/hertz/cmd/hz@latest | ||
| 3 | -# 执行 go install github.com/cloudwego/kitex/tool/cmd/kitex@latest | ||
| 4 | -# 执行 go install golang.org/x/tools/cmd/goimports@latest | ||
| 5 | -# 执行 go install golang.org/x/vuln/cmd/govulncheck@latest | ||
| 6 | -# 执行 go install mvdan.cc/gofumpt@latest | ||
| 7 | -# 访问 https://golangci-lint.run/welcome/install/ 以查看安装 golangci-lint 的方法 | ||
| 8 | - | ||
| 9 | -# 默认输出帮助信息 | ||
| 10 | -.DEFAULT_GOAL := help | ||
| 11 | -# 检查 tmux 是否存在 | ||
| 12 | -TMUX_EXISTS := $(shell command -v tmux) | ||
| 13 | -# 项目 MODULE 名 | ||
| 14 | -MODULE = github.com/yxrxy/videoHub | ||
| 15 | -# 当前架构 | ||
| 16 | -ARCH := $(shell uname -m) | ||
| 17 | -PREFIX = "[Makefile]" | ||
| 18 | -# 目录相关 | ||
| 19 | -DIR = $(shell pwd) | ||
| 20 | -CMD = $(DIR)/cmd | ||
| 21 | -CONFIG_PATH = $(DIR)/config | ||
| 22 | -IDL_PATH = $(DIR)/idl | ||
| 23 | -OUTPUT_PATH = $(DIR)/output | ||
| 24 | - | ||
| 25 | -# 服务名 | ||
| 26 | -SERVICES := gateway user social interaction video note | ||
| 27 | -service = $(word 1, $@) | ||
| 28 | - | ||
| 29 | -all: | ||
| 30 | - @make user | ||
| 31 | - @make social | ||
| 32 | - @make interaction | ||
| 33 | - @make video | ||
| 34 | - @make gateway | ||
| 35 | - | ||
| 36 | -.PHONY: help | ||
| 37 | -help: | ||
| 38 | - @echo "Available targets:" | ||
| 39 | - @echo " {service name} : Build a specific service (e.g., make social). use BUILD_ONLY=1 to avoid auto bootstrap." | ||
| 40 | - @echo " Available service list: [${SERVICES}]" | ||
| 41 | - @echo " env-up : Start the docker-compose environment." | ||
| 42 | - @echo " env-down : Stop the docker-compose environment." | ||
| 43 | - @echo " kitex-gen-% : Generate Kitex service code for a specific service." | ||
| 44 | - @echo " kitex-update-% : Update Kitex generated code for a specific service." | ||
| 45 | - @echo " hertz-gen-api : Generate Hertz scaffold based on the API IDL." | ||
| 46 | - @echo " test : Run unit tests for the project." | ||
| 47 | - @echo " clean : Remove the 'output' directories and related binaries." | ||
| 48 | - | ||
| 49 | -# 启动必要的环境,比如 etcd、mysql | ||
| 50 | -.PHONY: env-up | ||
| 51 | -env-up: | ||
| 52 | - @ docker compose -f ./docker/docker-compose.yml up -d videohub-mysql videohub-redis videohub-etcd kafka otel-collector elasticsearch kibana jaeger prometheus grafana | ||
| 53 | - @echo "$(PREFIX) - Jaeger UI: http://localhost:16686" | ||
| 54 | - @echo "$(PREFIX) - Prometheus: http://localhost:9090" | ||
| 55 | - @echo "$(PREFIX) - Grafana: http://localhost:3080 (admin/admin)" | ||
| 56 | - @echo "$(PREFIX) - Node Exporter: http://localhost:9100" | ||
| 57 | - @echo "$(PREFIX) - AlertManager: http://localhost:9093" | ||
| 58 | - | ||
| 59 | -# 关闭必要的环境,但不清理 data(位于 docker/data 目录中) | ||
| 60 | -.PHONY: env-down | ||
| 61 | -env-down: | ||
| 62 | - @ cd ./docker && docker compose down | ||
| 63 | - | ||
| 64 | -# 基于 idl 生成相关的 go 语言描述文件 | ||
| 65 | -.PHONY: kitex-gen-% | ||
| 66 | -kitex-gen-%: | ||
| 67 | - @ kitex -module "${MODULE}" \ | ||
| 68 | - -thrift no_default_serdes \ | ||
| 69 | - ${IDL_PATH}/$*.thrift | ||
| 70 | - @ go mod tidy | ||
| 71 | - | ||
| 72 | -# 生成基于 Hertz 的脚手架 | ||
| 73 | -.PHONY: hz-% | ||
| 74 | -hz-%: | ||
| 75 | - hz update -idl ${IDL_PATH}/api/$*.thrift | ||
| 76 | - | ||
| 77 | -# 构建指定对象,构建后在没有给 BUILD_ONLY 参的情况下会自动运行,需要熟悉 tmux 环境 | ||
| 78 | -.PHONY: $(SERVICES) | ||
| 79 | -$(SERVICES): | ||
| 80 | - @if [ -z "$(TMUX_EXISTS)" ]; then \ | ||
| 81 | - echo "$(PREFIX) tmux is not installed. Please install tmux first."; \ | ||
| 82 | - exit 1; \ | ||
| 83 | - fi | ||
| 84 | - @if [ -z "$$TMUX" ]; then \ | ||
| 85 | - echo "$(PREFIX) you are not in tmux, press ENTER to start tmux environment."; \ | ||
| 86 | - read -r; \ | ||
| 87 | - if tmux has-session -t videohub 2>/dev/null; then \ | ||
| 88 | - echo "$(PREFIX) Tmux session 'videohub' already exists. Attaching to session and running command."; \ | ||
| 89 | - tmux attach-session -t videohub; \ | ||
| 90 | - tmux send-keys -t videohub "make $(service)" C-m; \ | ||
| 91 | - else \ | ||
| 92 | - echo "$(PREFIX) No tmux session found. Creating a new session."; \ | ||
| 93 | - tmux new-session -s videohub "make $(service); $$SHELL"; \ | ||
| 94 | - fi; \ | ||
| 95 | - else \ | ||
| 96 | - echo "$(PREFIX) Build $(service) target..."; \ | ||
| 97 | - mkdir -p output; \ | ||
| 98 | - bash $(DIR)/docker/script/build.sh $(service); \ | ||
| 99 | - echo "$(PREFIX) Build $(service) target completed"; \ | ||
| 100 | - fi | ||
| 101 | -ifndef BUILD_ONLY | ||
| 102 | - @echo "$(PREFIX) Automatic run server" | ||
| 103 | - @if tmux list-windows -F '#{window_name}' | grep -q "^videohub-$(service)$$"; then \ | ||
| 104 | - echo "$(PREFIX) Window 'videohub-$(service)' already exists. Reusing the window."; \ | ||
| 105 | - tmux select-window -t "videohub-$(service)"; \ | ||
| 106 | - else \ | ||
| 107 | - echo "$(PREFIX) Window 'videohub-$(service)' does not exist. Creating a new window."; \ | ||
| 108 | - tmux new-window -n "videohub-$(service)"; \ | ||
| 109 | - tmux split-window -h ; \ | ||
| 110 | - tmux select-layout -t "videohub-$(service)" even-horizontal; \ | ||
| 111 | - fi | ||
| 112 | - @echo "$(PREFIX) Running $(service) service in tmux..." | ||
| 113 | - @tmux send-keys -t videohub-$(service).0 'export SERVICE=$(service) && bash ./docker/script/entrypoint.sh' C-m | ||
| 114 | - @tmux select-pane -t videohub-$(service).1 | ||
| 115 | -endif | ||
| 116 | - | ||
| 117 | -# 清除所有的构建产物 | ||
| 118 | -.PHONY: clean | ||
| 119 | -clean: | ||
| 120 | - @find . -type d -name "output" -exec rm -rf {} + -print | ||
| 121 | - | ||
| 122 | -# 清除所有构建产物、compose 环境和它的数据 | ||
| 123 | -.PHONY: clean-all | ||
| 124 | -clean-all: clean | ||
| 125 | - @echo "$(PREFIX) Checking if docker-compose services are running..." | ||
| 126 | - @docker-compose -f ./docker/docker-compose.yml ps -q | grep '.' && docker-compose -f ./docker/docker-compose.yml down || echo "$(PREFIX) No services are running." | ||
| 127 | - @echo "$(PREFIX) Removing docker data..." | ||
| 128 | - rm -rf ./docker/data | ||
| 129 | - | ||
| 130 | -# 格式化代码,我们使用 gofumpt,是 fmt 的严格超集 | ||
| 131 | -.PHONY: fmt | ||
| 132 | -fmt: | ||
| 133 | - gofumpt -l -w . | ||
| 134 | - | ||
| 135 | -# 优化 import 顺序结构 | ||
| 136 | -.PHONY: import | ||
| 137 | -import: | ||
| 138 | - goimports -w -local github.com/west2-online . | ||
| 139 | - | ||
| 140 | -# 检查可能的错误 | ||
| 141 | -.PHONY: vet | ||
| 142 | -vet: | ||
| 143 | - go vet ./... | ||
| 144 | - | ||
| 145 | -# 代码格式校验 | ||
| 146 | -.PHONY: lint | ||
| 147 | -lint: | ||
| 148 | - golangci-lint run --config=./.golangci.yml --tests --allow-parallel-runners --show-stats --print-resources-usage | ||
| 149 | - | ||
| 150 | -# 检查依赖漏洞 | ||
| 151 | -.PHONY: vulncheck | ||
| 152 | -vulncheck: | ||
| 153 | - govulncheck ./... | ||
| 154 | - | ||
| 155 | -.PHONY: tidy | ||
| 156 | -tidy: | ||
| 157 | - go mod tidy | ||
| 158 | - | ||
| 159 | -# 前端服务相关命令 | ||
| 160 | -.PHONY: frontend frontend-stop frontend-build | ||
| 161 | - | ||
| 162 | -# 启动前端服务 | ||
| 163 | -frontend: | ||
| 164 | - @echo "$(PREFIX) Building and starting frontend service..." | ||
| 165 | - @cd docker && docker compose up -d frontend | ||
| 166 | - @echo "$(PREFIX) Frontend service is running at http://localhost:80" | ||
| 167 | - | ||
| 168 | -# 停止前端服务 | ||
| 169 | -frontend-stop: | ||
| 170 | - @echo "$(PREFIX) Stopping frontend service..." | ||
| 171 | - @cd docker && docker compose stop frontend && docker compose rm -f frontend | ||
| 172 | - @echo "$(PREFIX) Frontend service has been stopped" | ||
| 173 | - | ||
| 174 | -# 构建前端服务 | ||
| 175 | -frontend-build: | ||
| 176 | - @echo "$(PREFIX) Building frontend service..." | ||
| 177 | - @cd docker && docker compose build --no-cache frontend | ||
| 178 | - @echo "$(PREFIX) Frontend service build completed" | ||
| @@ -1,484 +1,31 @@ | |||
| 1 | -# VideoHub | 1 | +# ZiTok Frontend |
| 2 | 2 | ||
| 3 | -API文档: https://2pr5l6um26.apifox.cn | 3 | +## 主题管理系统 |
| 4 | 4 | ||
| 5 | -## 服务接口列表 | 5 | +本项目已采用统一的主题管理架构: |
| 6 | 6 | ||
| 7 | -### 1. 用户服务 (UserService) | 7 | +- 主题配置:`lib/shared/styles/app_colors.dart` 与 `lib/shared/styles/app_theme.dart` |
| 8 | -- Register | 8 | +- 状态管理:`lib/shared/providers/app_provider.dart` |
| 9 | -- Login | 9 | +- 设置页面集成:`lib/features/user/presentation/pages/settingsPage.dart` |
| 10 | -- GetUserInfo | ||
| 11 | -- UploadAvatar | ||
| 12 | -- RefreshToken | ||
| 13 | 10 | ||
| 14 | -### 2. 视频服务 (VideoService) | 11 | +### 使用方式 |
| 15 | -- Publish | ||
| 16 | -- List | ||
| 17 | -- Detail | ||
| 18 | -- GetHotVideos | ||
| 19 | -- Delete | ||
| 20 | -- IncrementVisitCount | ||
| 21 | -- IncrementLikeCount | ||
| 22 | -- Search | ||
| 23 | -- SemanticSearch | ||
| 24 | 12 | ||
| 25 | -### 3. 互动服务 (InteractionService) | 13 | +- 在 `MaterialApp` 中使用: |
| 26 | -- Like | 14 | + ```dart |
| 27 | -- GetLikes | 15 | + MaterialApp( |
| 28 | -- Comment | 16 | + theme: context.watch<AppProvider>().currentTheme, |
| 29 | -- GetComments | 17 | + ) |
| 30 | -- DeleteComment | 18 | + ``` |
| 31 | -- LikeComment | 19 | +- 切换主题: |
| 20 | + ```dart | ||
| 21 | + context.read<AppProvider>().setThemeMode(AppThemeMode.dark); // 或 light/system | ||
| 22 | + ``` | ||
| 23 | +- 主题切换按钮示例:`lib/shared/widgets/theme_toggle.dart` | ||
| 32 | 24 | ||
| 33 | -### 4. 社交服务 (SocialService) | 25 | +### 持久化 |
| 34 | -- PrivateChat | ||
| 35 | -- RoomChat | ||
| 36 | -- CreateChatRoom | ||
| 37 | 26 | ||
| 38 | -## 技术栈 | 27 | +主题与语言设置通过 `SharedPreferences` 持久化,实现于 `lib/core/storage/app_storage.dart`。 |
| 39 | 28 | ||
| 40 | -### 后端 | 29 | +### 测试 |
| 41 | -- **语言与框架**: Go 1.21, Kitex(RPC), Hertz(HTTP), Thrift(IDL) | ||
| 42 | -- **数据存储**: MySQL(GORM), Redis, Elasticsearch | ||
| 43 | -- **中间件**: Kafka, etcd, JWT | ||
| 44 | -- **AI与搜索**: OpenAI API, 向量检索 | ||
| 45 | -- **监控**: OpenTelemetry | ||
| 46 | -- **部署**: Docker, Docker Compose | ||
| 47 | 30 | ||
| 48 | -### 前端 | 31 | +主题切换的Widget测试位于 `test/theme_test.dart`,验证 `MaterialApp` 的 `ThemeData` 会随 `AppProvider` 的模式更新。 |
| 49 | -- **框架**: Vue 3 | ||
| 50 | -- **构建工具**: Vite | ||
| 51 | -- **UI组件库**: Element Plus | ||
| 52 | -- **状态管理**: Pinia | ||
| 53 | -- **路由**: Vue Router | ||
| 54 | -- **HTTP客户端**: Axios | ||
| 55 | -- **样式**: SCSS | ||
| 56 | -- **包管理器**: npm | ||
| 57 | - | ||
| 58 | -## 系统架构 | ||
| 59 | - | ||
| 60 | -基于微服务架构,划分为用户、视频、互动和社交四个核心服务,通过Gateway统一入口处理请求。服务间通过Kitex RPC框架通信,使用etcd进行服务发现。 | ||
| 61 | - | ||
| 62 | -## 核心实现 | ||
| 63 | - | ||
| 64 | -### 视频服务 | ||
| 65 | -- 基于OpenAI的语义搜索功能 | ||
| 66 | -- 视频元数据提取和标签管理 | ||
| 67 | -- 热门视频排行榜(访问量、点赞数) | ||
| 68 | - | ||
| 69 | -### 缓存策略 | ||
| 70 | -- 热门视频使用Redis缓存 | ||
| 71 | -- 用户令牌缓存 | ||
| 72 | -- 点赞、访问量等计数器使用Redis实现 | ||
| 73 | -- 热门排行榜使用Redis Sorted Set | ||
| 74 | -- 分服务的Redis DB隔离(用户服务DB0、视频服务DB1、社交服务DB2) | ||
| 75 | - | ||
| 76 | -### 异步处理 | ||
| 77 | -- 视频上传后通过Kafka消息队列异步处理 | ||
| 78 | -- 视频封面生成、元数据提取(时长、分辨率) | ||
| 79 | -- 异步数据写入与统计更新 | ||
| 80 | -- 异步更新Elasticsearch索引和向量数据库 | ||
| 81 | - | ||
| 82 | -### 社交功能 | ||
| 83 | -- 基于WebSocket的实时聊天 | ||
| 84 | -- 支持私聊消息和聊天室 | ||
| 85 | -- 聊天记录持久化存储 | ||
| 86 | - | ||
| 87 | -### 搜索功能 | ||
| 88 | -- 基于Elasticsearch的全文搜索 | ||
| 89 | -- 集成OpenAI API的语义搜索 | ||
| 90 | -- 支持多字段复合查询 | ||
| 91 | -- 搜索结果缓存优化 | ||
| 92 | - | ||
| 93 | -### 安全性 | ||
| 94 | -- JWT用户认证 | ||
| 95 | -- API访问频率限制 | ||
| 96 | -- 视频内容安全检查 | ||
| @@ -0,0 +1 @@ | |||
| 1 | +include: package:flutter_lints/flutter.yaml | ||
| @@ -0,0 +1,14 @@ | |||
| 1 | +gradle-wrapper.jar | ||
| 2 | +/.gradle | ||
| 3 | +/captures/ | ||
| 4 | +/gradlew | ||
| 5 | +/gradlew.bat | ||
| 6 | +/local.properties | ||
| 7 | +GeneratedPluginRegistrant.java | ||
| 8 | +.cxx/ | ||
| 9 | + | ||
| 10 | +# Remember to never publicly share your keystore. | ||
| 11 | +# See https://flutter.dev/to/reference-keystore | ||
| 12 | +key.properties | ||
| 13 | +**/*.keystore | ||
| 14 | +**/*.jks | ||
| @@ -0,0 +1,7 @@ | |||
| 1 | +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> | ||
| 2 | + <!-- The INTERNET permission is required for development. Specifically, | ||
| 3 | + the Flutter tool needs it to communicate with the running application | ||
| 4 | + to allow setting breakpoints, to provide hot reload, etc. | ||
| 5 | + --> | ||
| 6 | + <uses-permission android:name="android.permission.INTERNET"/> | ||
| 7 | +</manifest> | ||
| @@ -0,0 +1,12 @@ | |||
| 1 | +<?xml version="1.0" encoding="utf-8"?> | ||
| 2 | +<!-- Modify this file to customize your launch splash screen --> | ||
| 3 | +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> | ||
| 4 | + <item android:drawable="?android:colorBackground" /> | ||
| 5 | + | ||
| 6 | + <!-- You can insert your own image assets here --> | ||
| 7 | + <!-- <item> | ||
| 8 | + <bitmap | ||
| 9 | + android:gravity="center" | ||
| 10 | + android:src="@mipmap/launch_image" /> | ||
| 11 | + </item> --> | ||
| 12 | +</layer-list> | ||
| @@ -0,0 +1,12 @@ | |||
| 1 | +<?xml version="1.0" encoding="utf-8"?> | ||
| 2 | +<!-- Modify this file to customize your launch splash screen --> | ||
| 3 | +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> | ||
| 4 | + <item android:drawable="@android:color/white" /> | ||
| 5 | + | ||
| 6 | + <!-- You can insert your own image assets here --> | ||
| 7 | + <!-- <item> | ||
| 8 | + <bitmap | ||
| 9 | + android:gravity="center" | ||
| 10 | + android:src="@mipmap/launch_image" /> | ||
| 11 | + </item> --> | ||
| 12 | +</layer-list> | ||
| @@ -0,0 +1,18 @@ | |||
| 1 | +<?xml version="1.0" encoding="utf-8"?> | ||
| 2 | +<resources> | ||
| 3 | + <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on --> | ||
| 4 | + <style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar"> | ||
| 5 | + <!-- Show a splash screen on the activity. Automatically removed when | ||
| 6 | + the Flutter engine draws its first frame --> | ||
| 7 | + <item name="android:windowBackground">@drawable/launch_background</item> | ||
| 8 | + </style> | ||
| 9 | + <!-- Theme applied to the Android Window as soon as the process has started. | ||
| 10 | + This theme determines the color of the Android Window while your | ||
| 11 | + Flutter UI initializes, as well as behind your Flutter UI while its | ||
| 12 | + running. | ||
| 13 | + | ||
| 14 | + This Theme is only used starting with V2 of Flutter's Android embedding. --> | ||
| 15 | + <style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar"> | ||
| 16 | + <item name="android:windowBackground">?android:colorBackground</item> | ||
| 17 | + </style> | ||
| 18 | +</resources> | ||
| @@ -0,0 +1,18 @@ | |||
| 1 | +<?xml version="1.0" encoding="utf-8"?> | ||
| 2 | +<resources> | ||
| 3 | + <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off --> | ||
| 4 | + <style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar"> | ||
| 5 | + <!-- Show a splash screen on the activity. Automatically removed when | ||
| 6 | + the Flutter engine draws its first frame --> | ||
| 7 | + <item name="android:windowBackground">@drawable/launch_background</item> | ||
| 8 | + </style> | ||
| 9 | + <!-- Theme applied to the Android Window as soon as the process has started. | ||
| 10 | + This theme determines the color of the Android Window while your | ||
| 11 | + Flutter UI initializes, as well as behind your Flutter UI while its | ||
| 12 | + running. | ||
| 13 | + | ||
| 14 | + This Theme is only used starting with V2 of Flutter's Android embedding. --> | ||
| 15 | + <style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar"> | ||
| 16 | + <item name="android:windowBackground">?android:colorBackground</item> | ||
| 17 | + </style> | ||
| 18 | +</resources> | ||
| @@ -0,0 +1,7 @@ | |||
| 1 | +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> | ||
| 2 | + <!-- The INTERNET permission is required for development. Specifically, | ||
| 3 | + the Flutter tool needs it to communicate with the running application | ||
| 4 | + to allow setting breakpoints, to provide hot reload, etc. | ||
| 5 | + --> | ||
| 6 | + <uses-permission android:name="android.permission.INTERNET"/> | ||
| 7 | +</manifest> | ||
| @@ -0,0 +1,24 @@ | |||
| 1 | +allprojects { | ||
| 2 | + repositories { | ||
| 3 | + google() | ||
| 4 | + mavenCentral() | ||
| 5 | + } | ||
| 6 | +} | ||
| 7 | + | ||
| 8 | +val newBuildDir: Directory = | ||
| 9 | + rootProject.layout.buildDirectory | ||
| 10 | + .dir("../../build") | ||
| 11 | + .get() | ||
| 12 | +rootProject.layout.buildDirectory.value(newBuildDir) | ||
| 13 | + | ||
| 14 | +subprojects { | ||
| 15 | + val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name) | ||
| 16 | + project.layout.buildDirectory.value(newSubprojectBuildDir) | ||
| 17 | +} | ||
| 18 | +subprojects { | ||
| 19 | + project.evaluationDependsOn(":app") | ||
| 20 | +} | ||
| 21 | + | ||
| 22 | +tasks.register<Delete>("clean") { | ||
| 23 | + delete(rootProject.layout.buildDirectory) | ||
| 24 | +} | ||
| @@ -0,0 +1,3 @@ | |||
| 1 | +org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError | ||
| 2 | +android.useAndroidX=true | ||
| 3 | +android.enableJetifier=true | ||
| @@ -0,0 +1,5 @@ | |||
| 1 | +distributionBase=GRADLE_USER_HOME | ||
| 2 | +distributionPath=wrapper/dists | ||
| 3 | +zipStoreBase=GRADLE_USER_HOME | ||
| 4 | +zipStorePath=wrapper/dists | ||
| 5 | +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip | ||
| @@ -1,149 +0,0 @@ | |||
| 1 | -// Code generated by hertz generator. | ||
| 2 | - | ||
| 3 | -package interaction | ||
| 4 | - | ||
| 5 | -import ( | ||
| 6 | - "context" | ||
| 7 | - | ||
| 8 | - "github.com/cloudwego/hertz/pkg/app" | ||
| 9 | - api "github.com/yxrxy/videoHub/app/gateway/model/interaction" | ||
| 10 | - "github.com/yxrxy/videoHub/app/gateway/pack" | ||
| 11 | - "github.com/yxrxy/videoHub/app/gateway/rpc" | ||
| 12 | - "github.com/yxrxy/videoHub/kitex_gen/interaction" | ||
| 13 | - "github.com/yxrxy/videoHub/pkg/errno" | ||
| 14 | -) | ||
| 15 | - | ||
| 16 | -// Like . | ||
| 17 | -// @router /api/v1/video/:video_id/like [POST] | ||
| 18 | -func Like(ctx context.Context, c *app.RequestContext) { | ||
| 19 | - var err error | ||
| 20 | - var req api.LikeRequest | ||
| 21 | - err = c.BindAndValidate(&req) | ||
| 22 | - if err != nil { | ||
| 23 | - pack.RespError(c, errno.ParamVerifyError.WithError(err)) | ||
| 24 | - return | ||
| 25 | - } | ||
| 26 | - err = rpc.LikeVideoRPC(ctx, &interaction.LikeRequest{ | ||
| 27 | - VideoId: req.VideoID, | ||
| 28 | - }) | ||
| 29 | - if err != nil { | ||
| 30 | - pack.RespError(c, err) | ||
| 31 | - return | ||
| 32 | - } | ||
| 33 | - pack.RespSuccess(c) | ||
| 34 | -} | ||
| 35 | - | ||
| 36 | -// GetLikes . | ||
| 37 | -// @router /api/v1/video/:video_id/likes [GET] | ||
| 38 | -func GetLikes(ctx context.Context, c *app.RequestContext) { | ||
| 39 | - var err error | ||
| 40 | - var req api.GetLikesRequest | ||
| 41 | - err = c.BindAndValidate(&req) | ||
| 42 | - if err != nil { | ||
| 43 | - pack.RespError(c, errno.ParamVerifyError.WithError(err)) | ||
| 44 | - return | ||
| 45 | - } | ||
| 46 | - | ||
| 47 | - resp, _, err := rpc.GetLikesRPC(ctx, &interaction.GetLikesRequest{ | ||
| 48 | - VideoId: req.VideoID, | ||
| 49 | - Page: req.Page, | ||
| 50 | - Size: req.Size, | ||
| 51 | - }) | ||
| 52 | - if err != nil { | ||
| 53 | - pack.RespError(c, err) | ||
| 54 | - return | ||
| 55 | - } | ||
| 56 | - pack.RespData(c, resp) | ||
| 57 | -} | ||
| 58 | - | ||
| 59 | -// Comment . | ||
| 60 | -// @router /api/v1/video/:video_id/comment [POST] | ||
| 61 | -func Comment(ctx context.Context, c *app.RequestContext) { | ||
| 62 | - var err error | ||
| 63 | - var req api.CommentRequest | ||
| 64 | - err = c.BindAndValidate(&req) | ||
| 65 | - if err != nil { | ||
| 66 | - pack.RespError(c, errno.ParamVerifyError.WithError(err)) | ||
| 67 | - return | ||
| 68 | - } | ||
| 69 | - if req.ParentID == nil { | ||
| 70 | - defaultValue := int64(-1) | ||
| 71 | - req.ParentID = &defaultValue | ||
| 72 | - } | ||
| 73 | - err = rpc.CommentVideoRPC(ctx, &interaction.CommentRequest{ | ||
| 74 | - VideoId: req.VideoID, | ||
| 75 | - Content: req.Content, | ||
| 76 | - ParentId: req.ParentID, | ||
| 77 | - }) | ||
| 78 | - if err != nil { | ||
| 79 | - pack.RespError(c, err) | ||
| 80 | - return | ||
| 81 | - } | ||
| 82 | - pack.RespSuccess(c) | ||
| 83 | -} | ||
| 84 | - | ||
| 85 | -// GetComments . | ||
| 86 | -// @router /api/v1/video/:video_id/comments [GET] | ||
| 87 | -func GetComments(ctx context.Context, c *app.RequestContext) { | ||
| 88 | - var err error | ||
| 89 | - var req api.GetCommentsRequest | ||
| 90 | - err = c.BindAndValidate(&req) | ||
| 91 | - if err != nil { | ||
| 92 | - pack.RespError(c, errno.ParamVerifyError.WithError(err)) | ||
| 93 | - return | ||
| 94 | - } | ||
| 95 | - | ||
| 96 | - resp, _, err := rpc.GetCommentsRPC(ctx, &interaction.GetCommentsRequest{ | ||
| 97 | - VideoId: req.VideoID, | ||
| 98 | - Page: req.Page, | ||
| 99 | - Size: req.Size, | ||
| 100 | - CommentId: req.CommentID, | ||
| 101 | - }) | ||
| 102 | - if err != nil { | ||
| 103 | - pack.RespError(c, err) | ||
| 104 | - return | ||
| 105 | - } | ||
| 106 | - pack.RespData(c, resp) | ||
| 107 | -} | ||
| 108 | - | ||
| 109 | -// DeleteComment . | ||
| 110 | -// @router /api/v1/comment/:comment_id [DELETE] | ||
| 111 | -func DeleteComment(ctx context.Context, c *app.RequestContext) { | ||
| 112 | - var err error | ||
| 113 | - var req api.DeleteCommentRequest | ||
| 114 | - err = c.BindAndValidate(&req) | ||
| 115 | - if err != nil { | ||
| 116 | - pack.RespError(c, errno.ParamVerifyError.WithError(err)) | ||
| 117 | - return | ||
| 118 | - } | ||
| 119 | - | ||
| 120 | - err = rpc.DeleteCommentRPC(ctx, &interaction.DeleteCommentRequest{ | ||
| 121 | - CommentId: req.CommentID, | ||
| 122 | - }) | ||
| 123 | - if err != nil { | ||
| 124 | - pack.RespError(c, err) | ||
| 125 | - return | ||
| 126 | - } | ||
| 127 | - pack.RespSuccess(c) | ||
| 128 | -} | ||
| 129 | - | ||
| 130 | -// LikeComment . | ||
| 131 | -// @router /api/v1/comment/:comment_id/like [POST] | ||
| 132 | -func LikeComment(ctx context.Context, c *app.RequestContext) { | ||
| 133 | - var err error | ||
| 134 | - var req api.LikeCommentRequest | ||
| 135 | - err = c.BindAndValidate(&req) | ||
| 136 | - if err != nil { | ||
| 137 | - pack.RespError(c, errno.ParamVerifyError.WithError(err)) | ||
| 138 | - return | ||
| 139 | - } | ||
| 140 | - | ||
| 141 | - err = rpc.LikeCommentRPC(ctx, &interaction.LikeCommentRequest{ | ||
| 142 | - CommentId: req.CommentID, | ||
| 143 | - }) | ||
| 144 | - if err != nil { | ||
| 145 | - pack.RespError(c, err) | ||
| 146 | - return | ||
| 147 | - } | ||
| 148 | - pack.RespSuccess(c) | ||
| 149 | -} | ||
| @@ -1,189 +0,0 @@ | |||
| 1 | -package notes | ||
| 2 | - | ||
| 3 | -import ( | ||
| 4 | - "context" | ||
| 5 | - "strconv" | ||
| 6 | - | ||
| 7 | - "github.com/cloudwego/hertz/pkg/app" | ||
| 8 | - | ||
| 9 | - "github.com/yxrxy/videoHub/app/gateway/pack" | ||
| 10 | - "github.com/yxrxy/videoHub/app/gateway/rpc" | ||
| 11 | - "github.com/yxrxy/videoHub/kitex_gen/notes" | ||
| 12 | - "github.com/yxrxy/videoHub/pkg/errno" | ||
| 13 | -) | ||
| 14 | - | ||
| 15 | -// CreateNote 创建笔记 | ||
| 16 | -// @router /api/v1/note/create [POST] | ||
| 17 | -func CreateNote(ctx context.Context, c *app.RequestContext) { | ||
| 18 | - var req notes.CreateNoteRequest | ||
| 19 | - err := c.BindAndValidate(&req) | ||
| 20 | - if err != nil { | ||
| 21 | - pack.RespError(c, errno.ParamVerifyError.WithError(err)) | ||
| 22 | - return | ||
| 23 | - } | ||
| 24 | - | ||
| 25 | - // 获取用户ID | ||
| 26 | - userID, exists := c.Get("userID") | ||
| 27 | - if !exists { | ||
| 28 | - pack.RespError(c, errno.AuthInvalid) | ||
| 29 | - return | ||
| 30 | - } | ||
| 31 | - req.UserId = userID.(int64) | ||
| 32 | - | ||
| 33 | - resp, err := rpc.CreateNoteRPC(ctx, &req) | ||
| 34 | - if err != nil { | ||
| 35 | - pack.RespError(c, err) | ||
| 36 | - return | ||
| 37 | - } | ||
| 38 | - | ||
| 39 | - pack.RespData(c, map[string]interface{}{ | ||
| 40 | - "note_id": resp.NoteId, | ||
| 41 | - }) | ||
| 42 | -} | ||
| 43 | - | ||
| 44 | -// DeleteNote 删除笔记 | ||
| 45 | -// @router /api/v1/note/delete [POST] | ||
| 46 | -func DeleteNote(ctx context.Context, c *app.RequestContext) { | ||
| 47 | - var req notes.DeleteNoteRequest | ||
| 48 | - err := c.BindAndValidate(&req) | ||
| 49 | - if err != nil { | ||
| 50 | - pack.RespError(c, errno.ParamVerifyError.WithError(err)) | ||
| 51 | - return | ||
| 52 | - } | ||
| 53 | - | ||
| 54 | - // 获取用户ID | ||
| 55 | - userID, exists := c.Get("userID") | ||
| 56 | - if !exists { | ||
| 57 | - pack.RespError(c, errno.AuthInvalid) | ||
| 58 | - return | ||
| 59 | - } | ||
| 60 | - req.UserId = userID.(int64) | ||
| 61 | - | ||
| 62 | - _, err = rpc.DeleteNoteRPC(ctx, &req) | ||
| 63 | - if err != nil { | ||
| 64 | - pack.RespError(c, err) | ||
| 65 | - return | ||
| 66 | - } | ||
| 67 | - | ||
| 68 | - pack.RespSuccess(c) | ||
| 69 | -} | ||
| 70 | - | ||
| 71 | -// GetVideoNotes 获取视频的笔记列表 | ||
| 72 | -// @router /api/v1/note/video [GET] | ||
| 73 | -func GetVideoNotes(ctx context.Context, c *app.RequestContext) { | ||
| 74 | - videoIDStr := c.Query("videoId") | ||
| 75 | - videoID, err := strconv.ParseInt(videoIDStr, 10, 64) | ||
| 76 | - if err != nil { | ||
| 77 | - pack.RespError(c, errno.ParamVerifyError.WithError(err)) | ||
| 78 | - return | ||
| 79 | - } | ||
| 80 | - | ||
| 81 | - pageStr := c.DefaultQuery("page", "1") | ||
| 82 | - page, _ := strconv.Atoi(pageStr) | ||
| 83 | - pageSizeStr := c.DefaultQuery("pageSize", "10") | ||
| 84 | - pageSize, _ := strconv.Atoi(pageSizeStr) | ||
| 85 | - | ||
| 86 | - pageInt32 := int32(page) | ||
| 87 | - pageSizeInt32 := int32(pageSize) | ||
| 88 | - | ||
| 89 | - req := ¬es.GetNotesRequest{ | ||
| 90 | - VideoId: videoID, | ||
| 91 | - Page: &pageInt32, | ||
| 92 | - Size: &pageSizeInt32, | ||
| 93 | - } | ||
| 94 | - | ||
| 95 | - resp, err := rpc.GetNotesRPC(ctx, req) | ||
| 96 | - if err != nil { | ||
| 97 | - pack.RespError(c, err) | ||
| 98 | - return | ||
| 99 | - } | ||
| 100 | - | ||
| 101 | - pack.RespData(c, resp) | ||
| 102 | -} | ||
| 103 | - | ||
| 104 | -// GetUserNotes 获取用户的笔记列表 | ||
| 105 | -// @router /api/v1/note/user [GET] | ||
| 106 | -func GetUserNotes(ctx context.Context, c *app.RequestContext) { | ||
| 107 | - userIDStr := c.Query("userId") | ||
| 108 | - userID, err := strconv.ParseInt(userIDStr, 10, 64) | ||
| 109 | - if err != nil { | ||
| 110 | - pack.RespError(c, errno.ParamVerifyError.WithError(err)) | ||
| 111 | - return | ||
| 112 | - } | ||
| 113 | - | ||
| 114 | - pageStr := c.DefaultQuery("page", "1") | ||
| 115 | - page, _ := strconv.Atoi(pageStr) | ||
| 116 | - pageSizeStr := c.DefaultQuery("pageSize", "10") | ||
| 117 | - pageSize, _ := strconv.Atoi(pageSizeStr) | ||
| 118 | - | ||
| 119 | - pageInt32 := int32(page) | ||
| 120 | - pageSizeInt32 := int32(pageSize) | ||
| 121 | - | ||
| 122 | - req := ¬es.GetUserNotesRequest{ | ||
| 123 | - UserId: userID, | ||
| 124 | - Page: &pageInt32, | ||
| 125 | - Size: &pageSizeInt32, | ||
| 126 | - } | ||
| 127 | - | ||
| 128 | - resp, err := rpc.GetUserNotesRPC(ctx, req) | ||
| 129 | - if err != nil { | ||
| 130 | - pack.RespError(c, err) | ||
| 131 | - return | ||
| 132 | - } | ||
| 133 | - | ||
| 134 | - pack.RespData(c, resp) | ||
| 135 | -} | ||
| 136 | - | ||
| 137 | -// LikeNote 点赞笔记 | ||
| 138 | -// @router /api/v1/note/like [POST] | ||
| 139 | -func LikeNote(ctx context.Context, c *app.RequestContext) { | ||
| 140 | - var req notes.LikeNoteRequest | ||
| 141 | - err := c.BindAndValidate(&req) | ||
| 142 | - if err != nil { | ||
| 143 | - pack.RespError(c, errno.ParamVerifyError.WithError(err)) | ||
| 144 | - return | ||
| 145 | - } | ||
| 146 | - | ||
| 147 | - // 获取用户ID | ||
| 148 | - userID, exists := c.Get("userID") | ||
| 149 | - if !exists { | ||
| 150 | - pack.RespError(c, errno.AuthInvalid) | ||
| 151 | - return | ||
| 152 | - } | ||
| 153 | - req.UserId = userID.(int64) | ||
| 154 | - | ||
| 155 | - _, err = rpc.LikeNoteRPC(ctx, &req) | ||
| 156 | - if err != nil { | ||
| 157 | - pack.RespError(c, err) | ||
| 158 | - return | ||
| 159 | - } | ||
| 160 | - | ||
| 161 | - pack.RespSuccess(c) | ||
| 162 | -} | ||
| 163 | - | ||
| 164 | -// FavoriteNote 收藏笔记 | ||
| 165 | -// @router /api/v1/note/favorite [POST] | ||
| 166 | -func FavoriteNote(ctx context.Context, c *app.RequestContext) { | ||
| 167 | - var req notes.FavoriteNoteRequest | ||
| 168 | - err := c.BindAndValidate(&req) | ||
| 169 | - if err != nil { | ||
| 170 | - pack.RespError(c, errno.ParamVerifyError.WithError(err)) | ||
| 171 | - return | ||
| 172 | - } | ||
| 173 | - | ||
| 174 | - // 获取用户ID | ||
| 175 | - userID, exists := c.Get("userID") | ||
| 176 | - if !exists { | ||
| 177 | - pack.RespError(c, errno.AuthInvalid) | ||
| 178 | - return | ||
| 179 | - } | ||
| 180 | - req.UserId = userID.(int64) | ||
| 181 | - | ||
| 182 | - _, err = rpc.FavoriteNoteRPC(ctx, &req) | ||
| 183 | - if err != nil { | ||
| 184 | - pack.RespError(c, err) | ||
| 185 | - return | ||
| 186 | - } | ||
| 187 | - | ||
| 188 | - pack.RespSuccess(c) | ||
| 189 | -} | ||
| @@ -1,226 +0,0 @@ | |||
| 1 | -// Code generated by hertz generator. | ||
| 2 | - | ||
| 3 | -package user | ||
| 4 | - | ||
| 5 | -import ( | ||
| 6 | - "context" | ||
| 7 | - "io" | ||
| 8 | - | ||
| 9 | - "github.com/cloudwego/hertz/pkg/app" | ||
| 10 | - | ||
| 11 | - api "github.com/yxrxy/videoHub/app/gateway/model/user" | ||
| 12 | - "github.com/yxrxy/videoHub/app/gateway/pack" | ||
| 13 | - "github.com/yxrxy/videoHub/app/gateway/rpc" | ||
| 14 | - "github.com/yxrxy/videoHub/kitex_gen/user" | ||
| 15 | - "github.com/yxrxy/videoHub/pkg/errno" | ||
| 16 | -) | ||
| 17 | - | ||
| 18 | -// Register . | ||
| 19 | -// @router /api/v1/user/register [POST] | ||
| 20 | -func Register(ctx context.Context, c *app.RequestContext) { | ||
| 21 | - var err error | ||
| 22 | - var req api.RegisterRequest | ||
| 23 | - err = c.BindAndValidate(&req) | ||
| 24 | - if err != nil { | ||
| 25 | - pack.RespError(c, errno.ParamVerifyError.WithError(err)) | ||
| 26 | - return | ||
| 27 | - } | ||
| 28 | - | ||
| 29 | - resp, err := rpc.RegisterRPC(ctx, &user.RegisterRequest{ | ||
| 30 | - Username: req.Username, | ||
| 31 | - Password: req.Password, | ||
| 32 | - Email: req.Email, // 新增字段 | ||
| 33 | - }) | ||
| 34 | - if err != nil { | ||
| 35 | - pack.RespError(c, err) | ||
| 36 | - return | ||
| 37 | - } | ||
| 38 | - pack.RespData(c, map[string]any{ | ||
| 39 | - "user_id": resp, | ||
| 40 | - }) | ||
| 41 | -} | ||
| 42 | - | ||
| 43 | -// Login . | ||
| 44 | -// @router /api/v1/user/login [POST] | ||
| 45 | -func Login(ctx context.Context, c *app.RequestContext) { | ||
| 46 | - var err error | ||
| 47 | - var req api.LoginRequest | ||
| 48 | - err = c.BindAndValidate(&req) | ||
| 49 | - if err != nil { | ||
| 50 | - pack.RespError(c, errno.ParamVerifyError.WithError(err)) | ||
| 51 | - return | ||
| 52 | - } | ||
| 53 | - resp, err := rpc.LoginRPC(ctx, &user.LoginRequest{ | ||
| 54 | - Username: req.Username, | ||
| 55 | - Password: req.Password, | ||
| 56 | - }) | ||
| 57 | - if err != nil { | ||
| 58 | - pack.RespError(c, err) | ||
| 59 | - return | ||
| 60 | - } | ||
| 61 | - c.Header("Authorization", "Bearer "+resp.Token) | ||
| 62 | - c.Header("RefreshToken", resp.RefreshToken) | ||
| 63 | - | ||
| 64 | - pack.RespData(c, map[string]any{ | ||
| 65 | - "user_id": resp.UserId, | ||
| 66 | - "token": resp.Token, | ||
| 67 | - "refresh_token": resp.RefreshToken, | ||
| 68 | - }) | ||
| 69 | -} | ||
| 70 | - | ||
| 71 | -// RefreshToken . | ||
| 72 | -// @router /api/v1/user/token/refresh [POST] | ||
| 73 | -func RefreshToken(ctx context.Context, c *app.RequestContext) { | ||
| 74 | - var err error | ||
| 75 | - var req api.RefreshTokenRequest | ||
| 76 | - err = c.BindAndValidate(&req) | ||
| 77 | - if err != nil { | ||
| 78 | - pack.RespError(c, errno.ParamVerifyError.WithError(err)) | ||
| 79 | - return | ||
| 80 | - } | ||
| 81 | - | ||
| 82 | - // Extract refresh token from header | ||
| 83 | - refreshToken := req.RefreshToken | ||
| 84 | - if refreshToken == "" { | ||
| 85 | - pack.RespError(c, errno.NewErrNo(errno.AuthNoTokenCode, "未提供刷新令牌")) | ||
| 86 | - return | ||
| 87 | - } | ||
| 88 | - | ||
| 89 | - resp, err := rpc.RefreshTokenRPC(ctx, &user.RefreshTokenRequest{ | ||
| 90 | - UserId: req.UserID, | ||
| 91 | - RefreshToken: refreshToken, | ||
| 92 | - }) | ||
| 93 | - if err != nil { | ||
| 94 | - pack.RespError(c, err) | ||
| 95 | - return | ||
| 96 | - } | ||
| 97 | - | ||
| 98 | - // Set tokens in response headers | ||
| 99 | - c.Header("Authorization", "Bearer "+resp.Token) | ||
| 100 | - c.Header("RefreshToken", resp.RefreshToken) | ||
| 101 | - | ||
| 102 | - pack.RespData(c, resp) | ||
| 103 | -} | ||
| 104 | - | ||
| 105 | -// GetUserInfo . | ||
| 106 | -// @router /api/v1/user/info/:user_id [GET] | ||
| 107 | -func GetUserInfo(ctx context.Context, c *app.RequestContext) { | ||
| 108 | - var err error | ||
| 109 | - var req api.UserInfoRequest | ||
| 110 | - err = c.BindAndValidate(&req) | ||
| 111 | - if err != nil { | ||
| 112 | - pack.RespError(c, errno.ParamVerifyError.WithError(err)) | ||
| 113 | - return | ||
| 114 | - } | ||
| 115 | - resp, err := rpc.GetUserInfoRPC(ctx, &user.UserInfoRequest{ | ||
| 116 | - UserId: req.UserID, | ||
| 117 | - }) | ||
| 118 | - if err != nil { | ||
| 119 | - pack.RespError(c, err) | ||
| 120 | - return | ||
| 121 | - } | ||
| 122 | - pack.RespData(c, resp) | ||
| 123 | -} | ||
| 124 | - | ||
| 125 | -// UploadAvatar .. | ||
| 126 | -// @router /api/v1/user/avatar [POST] | ||
| 127 | -func UploadAvatar(ctx context.Context, c *app.RequestContext) { | ||
| 128 | - var err error | ||
| 129 | - | ||
| 130 | - fileHeader, err := c.FormFile("avatar_data") | ||
| 131 | - if err != nil { | ||
| 132 | - pack.RespError(c, errno.ParamVerifyError.WithError(err)) | ||
| 133 | - return | ||
| 134 | - } | ||
| 135 | - | ||
| 136 | - file, err := fileHeader.Open() | ||
| 137 | - if err != nil { | ||
| 138 | - pack.RespError(c, errno.ParamVerifyError.WithError(err)) | ||
| 139 | - return | ||
| 140 | - } | ||
| 141 | - defer file.Close() | ||
| 142 | - | ||
| 143 | - avatarData, err := io.ReadAll(file) | ||
| 144 | - if err != nil { | ||
| 145 | - pack.RespError(c, errno.ParamVerifyError.WithError(err)) | ||
| 146 | - return | ||
| 147 | - } | ||
| 148 | - | ||
| 149 | - resp, err := rpc.UploadAvatarRPC(ctx, &user.UploadAvatarRequest{ | ||
| 150 | - AvatarData: avatarData, | ||
| 151 | - ContentType: fileHeader.Header.Get("Content-Type"), | ||
| 152 | - }) | ||
| 153 | - if err != nil { | ||
| 154 | - pack.RespError(c, err) | ||
| 155 | - return | ||
| 156 | - } | ||
| 157 | - | ||
| 158 | - pack.RespData(c, resp) | ||
| 159 | -} | ||
| 160 | - | ||
| 161 | -// UpdateUserInfo . | ||
| 162 | -// @router /api/v1/user/info [PUT] | ||
| 163 | -func UpdateUserInfo(ctx context.Context, c *app.RequestContext) { | ||
| 164 | - var err error | ||
| 165 | - var req api.UpdateUserInfoRequest | ||
| 166 | - err = c.BindAndValidate(&req) | ||
| 167 | - if err != nil { | ||
| 168 | - pack.RespError(c, errno.ParamVerifyError.WithError(err)) | ||
| 169 | - return | ||
| 170 | - } | ||
| 171 | - | ||
| 172 | - // 调用 RPC 更新信息 | ||
| 173 | - resp, err := rpc.UpdateUserInfoRPC(ctx, &user.UpdateUserInfoRequest{ | ||
| 174 | - Username: req.Username, | ||
| 175 | - Password: req.Password, | ||
| 176 | - Signature: req.Signature, | ||
| 177 | - Gender: req.Gender, | ||
| 178 | - Birthday: req.Birthday, | ||
| 179 | - Email: req.Email, | ||
| 180 | - }) | ||
| 181 | - | ||
| 182 | - if err != nil { | ||
| 183 | - pack.RespError(c, err) | ||
| 184 | - return | ||
| 185 | - } | ||
| 186 | - | ||
| 187 | - pack.RespData(c, resp) // 或 resp.Base | ||
| 188 | -} | ||
| 189 | - | ||
| 190 | -// UploadBackground . | ||
| 191 | -// @router /api/v1/user/background [POST] | ||
| 192 | -func UploadBackground(ctx context.Context, c *app.RequestContext) { | ||
| 193 | - var err error | ||
| 194 | - | ||
| 195 | - // 与 UploadAvatar 逻辑类似,读取背景图文件 | ||
| 196 | - fileHeader, err := c.FormFile("background_data") | ||
| 197 | - if err != nil { | ||
| 198 | - pack.RespError(c, errno.ParamVerifyError.WithError(err)) | ||
| 199 | - return | ||
| 200 | - } | ||
| 201 | - | ||
| 202 | - file, err := fileHeader.Open() | ||
| 203 | - if err != nil { | ||
| 204 | - pack.RespError(c, errno.ParamVerifyError.WithError(err)) | ||
| 205 | - return | ||
| 206 | - } | ||
| 207 | - defer file.Close() | ||
| 208 | - | ||
| 209 | - backgroundData, err := io.ReadAll(file) | ||
| 210 | - if err != nil { | ||
| 211 | - pack.RespError(c, errno.ParamVerifyError.WithError(err)) | ||
| 212 | - return | ||
| 213 | - } | ||
| 214 | - | ||
| 215 | - // 调用 RPC | ||
| 216 | - resp, err := rpc.UploadBackgroundRPC(ctx, &user.UploadBackgroundRequest{ | ||
| 217 | - BackgroundData: backgroundData, | ||
| 218 | - ContentType: fileHeader.Header.Get("Content-Type"), | ||
| 219 | - }) | ||
| 220 | - if err != nil { | ||
| 221 | - pack.RespError(c, err) | ||
| 222 | - return | ||
| 223 | - } | ||
| 224 | - | ||
| 225 | - pack.RespData(c, resp) // 返回更新后的 User | ||
| 226 | -} | ||