refactor: upgrade to Go 1.26 and modernize code idioms
All checks were successful
Build Backend / build (push) Successful in 3m51s
Build Backend / build-docker (push) Successful in 1m0s

- Replace `interface{}` with `any` type alias across all packages
- Use built-in `min()`/`max()` for parameter clamping
- Use `slices.SortFunc`, `slices.Min`, `slices.Max` for cleaner code
- Use `strings.Cut()` for simpler string parsing in auth middleware
- Use `errors.Is()` for proper error comparison in handlers
- Update dependencies: golang.org/x/image 0.37.0 -> 0.38.0
- Add Wire code generation guidelines to ARCHITECTURE.md
- Disable Go cache in CI build workflow
This commit is contained in:
lafay
2026-03-30 04:49:35 +08:00
parent a69b2026f4
commit b640c9a249
47 changed files with 368 additions and 352 deletions

View File

@@ -24,7 +24,7 @@ jobs:
uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
cache: true
cache: false
- name: Go cache info
run: |

View File

@@ -351,3 +351,46 @@ type LogFilter struct {
| Model | `internal/model/` |
| Wire | `internal/wire/` |
| Cache | `internal/cache/` |
---
## 11. Wire 生成的代码规范
### 11.1 禁止直接修改生成的文件
以下文件由 Wire 工具自动生成,**禁止直接修改**
- `cmd/server/wire_gen.go`
- `cmd/server/wire.go` 中的 Provider 函数签名由 Wire 维护
如果需要修改这些文件:
1. **修改源文件**(如 `wire.go` 中的结构)
2. **运行 `wire` 命令**重新生成
### 11.2 正确的修改流程
```bash
# 1. 修改 wire.go 或 wire/*.go
# 2. 运行 wire 重新生成
go run -mod=mod github.com/google/wire/cmd/wire ./cmd/server
```
### 11.3 Provider 依赖添加
新增依赖时,必须同步修改:
1. `wire/service.go` 中的 Provider 函数
2. `wire/handler.go` 中的 Handler Provider 函数
3. `cmd/server/wire.go` 中的 `ProvideRouter` 函数
4. `wire/repository.go`(如需新增 Repository
5. 重新运行 `wire ./cmd/server` 生成 `wire_gen.go`
### 11.4 生成文件识别
```
// Code generated by Wire. DO NOT EDIT.
//go:generate go run -mod=mod github.com/google/wire/cmd/wire
//go:build !wireinject
```
带以上标记的文件均为自动生成文件。

18
go.mod
View File

@@ -1,6 +1,6 @@
module carrot_bbs
go 1.25.7
go 1.26
require (
github.com/alicebob/miniredis/v2 v2.37.0
@@ -18,7 +18,7 @@ require (
github.com/spf13/viper v1.21.0
go.uber.org/zap v1.27.1
golang.org/x/crypto v0.49.0
golang.org/x/image v0.37.0
golang.org/x/image v0.38.0
google.golang.org/grpc v1.79.3
google.golang.org/protobuf v1.36.11
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
@@ -32,14 +32,14 @@ require (
github.com/bmatcuk/doublestar/v4 v4.10.0 // indirect
github.com/bytedance/gopkg v0.1.4 // indirect
github.com/bytedance/sonic v1.15.0 // indirect
github.com/bytedance/sonic/loader v0.5.0 // indirect
github.com/bytedance/sonic/loader v0.5.1 // indirect
github.com/casbin/govaluate v1.10.0 // indirect
github.com/cloudwego/base64x v0.1.6 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.13 // indirect
github.com/gin-contrib/sse v1.1.0 // indirect
github.com/gin-contrib/sse v1.1.1 // indirect
github.com/glebarez/go-sqlite v1.22.0 // indirect
github.com/glebarez/sqlite v1.11.0 // indirect
github.com/go-ini/ini v1.67.0 // indirect
@@ -54,12 +54,12 @@ require (
github.com/golang-sql/sqlexp v0.1.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/pgx/v5 v5.8.0 // indirect
github.com/jackc/pgx/v5 v5.9.1 // 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/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.18.4 // indirect
github.com/klauspost/compress v1.18.5 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/klauspost/crc32 v1.3.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
@@ -71,7 +71,7 @@ require (
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/ncruces/go-strftime v1.0.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/pelletier/go-toml/v2 v2.3.0 // indirect
github.com/philhofer/fwd v1.2.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/quic-go/qpack v0.6.0 // indirect
@@ -101,7 +101,7 @@ require (
golang.org/x/sys v0.42.0 // indirect
golang.org/x/text v0.35.0 // indirect
golang.org/x/tools v0.43.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260316180232-0b37fe3546d5 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260319201613-d00831a3d3e7 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gorm.io/driver/mysql v1.6.0 // indirect
gorm.io/driver/sqlserver v1.6.3 // indirect
@@ -109,5 +109,5 @@ require (
modernc.org/libc v1.70.0 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.11.0 // indirect
modernc.org/sqlite v1.47.0 // indirect
modernc.org/sqlite v1.48.0 // indirect
)

34
go.sum
View File

@@ -37,8 +37,8 @@ github.com/bytedance/gopkg v0.1.4 h1:oZnQwnX82KAIWb7033bEwtxvTqXcYMxDBaQxo5JJHWM
github.com/bytedance/gopkg v0.1.4/go.mod h1:v1zWfPm21Fb+OsyXN2VAHdL6TBb2L88anLQgdyje6R4=
github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSE=
github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k=
github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE=
github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
github.com/bytedance/sonic/loader v0.5.1 h1:Ygpfa9zwRCCKSlrp5bBP/b/Xzc3VxsAW+5NIYXrOOpI=
github.com/bytedance/sonic/loader v0.5.1/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
github.com/casbin/casbin/v3 v3.10.0 h1:039ORla55vCeIZWd0LfzWFt1yiEA5X4W41xBW2bQuHs=
github.com/casbin/casbin/v3 v3.10.0/go.mod h1:5rJbQr2e6AuuDDNxnPc5lQlC9nIgg6nS1zYwKXhpHC8=
github.com/casbin/gorm-adapter/v3 v3.41.0 h1:Xhpi0tfRP9aKPDWDf6dgBxHZ9UM6IophxxPIEGWqCNM=
@@ -68,8 +68,8 @@ github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM=
github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
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-contrib/sse v1.1.1 h1:uGYpNwTacv5R68bSGMapo62iLTRa9l5zxGCps4hK6ko=
github.com/gin-contrib/sse v1.1.1/go.mod h1:QXzuVkA0YO7o/gun03UI1Q+FTI8ZV/n5t03kIQAI89s=
github.com/gin-gonic/gin v1.12.0 h1:b3YAbrZtnf8N//yjKeU2+MQsh2mY5htkZidOM7O0wG8=
github.com/gin-gonic/gin v1.12.0/go.mod h1:VxccKfsSllpKshkBWgVgRniFFAzFb9csfngsqANjnLc=
github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ=
@@ -115,8 +115,6 @@ github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
github.com/google/subcommands v1.2.0 h1:vWQspBTo2nEqTUFita5/KeEWlUL8kQObDFbub/EN9oE=
github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@@ -134,8 +132,8 @@ github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsI
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.8.0 h1:TYPDoleBBme0xGSAX3/+NujXXtpZn9HBONkQC7IEZSo=
github.com/jackc/pgx/v5 v5.8.0/go.mod h1:QVeDInX2m9VyzvNeiCJVjCkNFqzsNb43204HshNSZKw=
github.com/jackc/pgx/v5 v5.9.1 h1:uwrxJXBnx76nyISkhr33kQLlUqjv7et7b9FjCen/tdc=
github.com/jackc/pgx/v5 v5.9.1/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4=
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/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
@@ -150,8 +148,8 @@ github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
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/compress v1.18.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c=
github.com/klauspost/compress v1.18.4/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE=
github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
github.com/klauspost/cpuid/v2 v2.0.1/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=
@@ -192,8 +190,8 @@ github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3P
github.com/montanaflynn/stats v0.7.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
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/pelletier/go-toml/v2 v2.3.0 h1:k59bC/lIZREW0/iVaQR8nDHxVq8OVlIzYCOJf421CaM=
github.com/pelletier/go-toml/v2 v2.3.0/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM=
github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM=
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
@@ -301,8 +299,8 @@ golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5D
golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.37.0 h1:ZiRjArKI8GwxZOoEtUfhrBtaCN+4b/7709dlT6SSnQA=
golang.org/x/image v0.37.0/go.mod h1:/3f6vaXC+6CEanU4KJxbcUZyEePbyKbaLoDOe4ehFYY=
golang.org/x/image v0.38.0 h1:5l+q+Y9JDC7mBOMjo4/aPhMDcxEptsX+Tt3GgRQRPuE=
golang.org/x/image v0.38.0/go.mod h1:/3f6vaXC+6CEanU4KJxbcUZyEePbyKbaLoDOe4ehFYY=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
@@ -401,8 +399,8 @@ golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260316180232-0b37fe3546d5 h1:aJmi6DVGGIStN9Mobk/tZOOQUBbj0BPjZjjnOdoZKts=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260316180232-0b37fe3546d5/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260319201613-d00831a3d3e7 h1:ndE4FoJqsIceKP2oYSnUZqhTdYufCYYkqwtFzfrhI7w=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
@@ -456,8 +454,8 @@ modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
modernc.org/sqlite v1.47.0 h1:R1XyaNpoW4Et9yly+I2EeX7pBza/w+pmYee/0HJDyKk=
modernc.org/sqlite v1.47.0/go.mod h1:hWjRO6Tj/5Ik8ieqxQybiEOUXy0NJFNp2tpvVpKlvig=
modernc.org/sqlite v1.48.0 h1:ElZyLop3Q2mHYk5IFPPXADejZrlHu7APbpB0sF78bq4=
modernc.org/sqlite v1.48.0/go.mod h1:hWjRO6Tj/5Ik8ieqxQybiEOUXy0NJFNp2tpvVpKlvig=
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=

View File

@@ -17,9 +17,9 @@ var ErrKeyNotFound = errors.New("key not found")
// Cache 缓存接口
type Cache interface {
// Set 设置缓存值支持TTL
Set(key string, value interface{}, ttl time.Duration)
Set(key string, value any, ttl time.Duration)
// Get 获取缓存值
Get(key string) (interface{}, bool)
Get(key string) (any, bool)
// Delete 删除缓存
Delete(key string)
// DeleteByPrefix 根据前缀删除缓存
@@ -35,13 +35,13 @@ type Cache interface {
// ==================== Hash 操作 ====================
// HSet 设置 Hash 字段
HSet(ctx context.Context, key string, field string, value interface{}) error
HSet(ctx context.Context, key string, field string, value any) error
// HMSet 批量设置 Hash 字段
HMSet(ctx context.Context, key string, values map[string]interface{}) error
HMSet(ctx context.Context, key string, values map[string]any) error
// HGet 获取 Hash 字段值
HGet(ctx context.Context, key string, field string) (string, error)
// HMGet 批量获取 Hash 字段值
HMGet(ctx context.Context, key string, fields ...string) ([]interface{}, error)
HMGet(ctx context.Context, key string, fields ...string) ([]any, error)
// HGetAll 获取 Hash 所有字段
HGetAll(ctx context.Context, key string) (map[string]string, error)
// HDel 删除 Hash 字段
@@ -59,7 +59,7 @@ type Cache interface {
// ZReplaceSortedSet 原子替换整个 ZSET先删后批量写入
ZReplaceSortedSet(ctx context.Context, key string, members []redis.Z) error
// ZRem 删除 Sorted Set 成员
ZRem(ctx context.Context, key string, members ...interface{}) error
ZRem(ctx context.Context, key string, members ...any) error
// ZCard 获取 Sorted Set 成员数量
ZCard(ctx context.Context, key string) (int64, error)
@@ -145,7 +145,7 @@ func normalizePrefix(prefix string) string {
return cmp.Or(settings.KeyPrefix, "") + ":" + prefix
}
func SetWithJitter(c Cache, key string, value interface{}, ttl time.Duration, jitterRatio float64) {
func SetWithJitter(c Cache, key string, value any, ttl time.Duration, jitterRatio float64) {
if !settings.Enabled {
return
}

View File

@@ -26,7 +26,7 @@ func NewRedisCache(client *redisPkg.Client) *RedisCache {
}
// Set 设置缓存值
func (c *RedisCache) Set(key string, value interface{}, ttl time.Duration) {
func (c *RedisCache) Set(key string, value any, ttl time.Duration) {
key = normalizeKey(key)
// 将值序列化为JSON
data, err := json.Marshal(value)
@@ -49,7 +49,7 @@ func (c *RedisCache) Set(key string, value interface{}, ttl time.Duration) {
}
// Get 获取缓存值
func (c *RedisCache) Get(key string) (interface{}, bool) {
func (c *RedisCache) Get(key string) (any, bool) {
key = normalizeKey(key)
data, err := c.client.Get(c.ctx, key)
if err != nil {
@@ -164,13 +164,13 @@ func (c *RedisCache) IncrementBy(key string, value int64) int64 {
// ==================== RedisCache Hash 操作 ====================
// HSet 设置 Hash 字段
func (c *RedisCache) HSet(ctx context.Context, key string, field string, value interface{}) error {
func (c *RedisCache) HSet(ctx context.Context, key string, field string, value any) error {
key = normalizeKey(key)
return c.client.HSet(ctx, key, field, value)
}
// HMSet 批量设置 Hash 字段
func (c *RedisCache) HMSet(ctx context.Context, key string, values map[string]interface{}) error {
func (c *RedisCache) HMSet(ctx context.Context, key string, values map[string]any) error {
key = normalizeKey(key)
return c.client.HMSet(ctx, key, values)
}
@@ -182,7 +182,7 @@ func (c *RedisCache) HGet(ctx context.Context, key string, field string) (string
}
// HMGet 批量获取 Hash 字段值
func (c *RedisCache) HMGet(ctx context.Context, key string, fields ...string) ([]interface{}, error) {
func (c *RedisCache) HMGet(ctx context.Context, key string, fields ...string) ([]any, error) {
key = normalizeKey(key)
return c.client.HMGet(ctx, key, fields...)
}
@@ -239,7 +239,7 @@ func (c *RedisCache) ZReplaceSortedSet(ctx context.Context, key string, members
}
// ZRem 删除 Sorted Set 成员
func (c *RedisCache) ZRem(ctx context.Context, key string, members ...interface{}) error {
func (c *RedisCache) ZRem(ctx context.Context, key string, members ...any) error {
key = normalizeKey(key)
return c.client.ZRem(ctx, key, members...)
}

View File

@@ -153,7 +153,7 @@ func Load(configPath string) (*Config, error) {
viper.SetDefault("hot_rank.recent_fetch_limit", 500)
viper.SetDefault("hot_rank.candidate_cap", 800)
// WebRTC 默认值Google 公共 STUN 服务器)
viper.SetDefault("webrtc.ice_servers", []map[string]interface{}{
viper.SetDefault("webrtc.ice_servers", []map[string]any{
{"urls": []string{"stun:stun.l.google.com:19302"}},
})
// Report 默认值

View File

@@ -516,15 +516,15 @@ type PushRecordListResponse struct {
// SystemMessageResponse 系统消息响应
type SystemMessageResponse struct {
ID string `json:"id"`
SenderID string `json:"sender_id"`
ReceiverID string `json:"receiver_id"`
Content string `json:"content"`
Category string `json:"category"`
SystemType string `json:"system_type"`
ExtraData map[string]interface{} `json:"extra_data,omitempty"`
IsRead bool `json:"is_read"`
CreatedAt time.Time `json:"created_at"`
ID string `json:"id"`
SenderID string `json:"sender_id"`
ReceiverID string `json:"receiver_id"`
Content string `json:"content"`
Category string `json:"category"`
SystemType string `json:"system_type"`
ExtraData map[string]any `json:"extra_data,omitempty"`
IsRead bool `json:"is_read"`
CreatedAt time.Time `json:"created_at"`
}
// SystemMessageListResponse 系统消息列表响应
@@ -905,10 +905,10 @@ type VoteResultDTO struct {
// WSResponse WebSocket响应结构体
type WSResponse struct {
Success bool `json:"success"` // 是否成功
Action string `json:"action"` // 响应原action
Data interface{} `json:"data,omitempty"` // 响应数据
Error string `json:"error,omitempty"` // 错误信息
Success bool `json:"success"` // 是否成功
Action string `json:"action"` // 响应原action
Data any `json:"data,omitempty"` // 响应数据
Error string `json:"error,omitempty"` // 错误信息
}
// ==================== Admin Post DTOs ====================
@@ -1212,13 +1212,13 @@ type CursorPageRequest struct {
}
// CursorPageResponse 游标分页响应(泛型版本)
// 注意:由于 Go 的限制,这里使用 interface{} 作为 Items 类型
// 注意:由于 Go 的限制,这里使用 any 作为 Items 类型
// 实际使用时可以创建特定类型的响应结构体
type CursorPageResponse struct {
Items interface{} `json:"list"`
NextCursor string `json:"next_cursor,omitempty"` // 下一页游标
PrevCursor string `json:"prev_cursor,omitempty"` // 上一页游标(可选)
HasMore bool `json:"has_more"` // 是否有更多数据
Items any `json:"list"`
NextCursor string `json:"next_cursor,omitempty"` // 下一页游标
PrevCursor string `json:"prev_cursor,omitempty"` // 上一页游标(可选)
HasMore bool `json:"has_more"` // 是否有更多数据
}
// CursorPageMeta 分页元信息

View File

@@ -54,7 +54,7 @@ func SystemMessageToResponse(msg *model.Message) *SystemMessageResponse {
CreatedAt: msg.CreatedAt,
}
if msg.ExtraData != nil {
resp.ExtraData = map[string]interface{}{
resp.ExtraData = map[string]any{
"actor_id": msg.ExtraData.ActorID,
"actor_name": msg.ExtraData.ActorName,
"avatar_url": msg.ExtraData.AvatarURL,
@@ -92,7 +92,7 @@ func SystemNotificationToResponse(n *model.SystemNotification) *SystemMessageRes
CreatedAt: n.CreatedAt,
}
if n.ExtraData != nil {
resp.ExtraData = map[string]interface{}{
resp.ExtraData = map[string]any{
"actor_id": n.ExtraData.ActorID,
"actor_id_str": n.ExtraData.ActorIDStr,
"actor_name": n.ExtraData.ActorName,

View File

@@ -9,7 +9,7 @@ import (
)
// ParseSegmentData 解析Segment数据到目标结构体
func ParseSegmentData(segment model.MessageSegment, target interface{}) error {
func ParseSegmentData(segment model.MessageSegment, target any) error {
dataBytes, err := json.Marshal(segment.Data)
if err != nil {
return err
@@ -21,7 +21,7 @@ func ParseSegmentData(segment model.MessageSegment, target interface{}) error {
func NewTextSegment(content string) model.MessageSegment {
return model.MessageSegment{
Type: string(SegmentTypeText),
Data: map[string]interface{}{"text": content},
Data: map[string]any{"text": content},
}
}
@@ -29,7 +29,7 @@ func NewTextSegment(content string) model.MessageSegment {
func NewImageSegment(url string, width, height int, thumbnailURL string) model.MessageSegment {
return model.MessageSegment{
Type: string(SegmentTypeImage),
Data: map[string]interface{}{
Data: map[string]any{
"url": url,
"width": width,
"height": height,
@@ -42,7 +42,7 @@ func NewImageSegment(url string, width, height int, thumbnailURL string) model.M
func NewImageSegmentWithSize(url string, width, height int, thumbnailURL string, fileSize int64) model.MessageSegment {
return model.MessageSegment{
Type: string(SegmentTypeImage),
Data: map[string]interface{}{
Data: map[string]any{
"url": url,
"width": width,
"height": height,
@@ -56,7 +56,7 @@ func NewImageSegmentWithSize(url string, width, height int, thumbnailURL string,
func NewVoiceSegment(url string, duration int, fileSize int64) model.MessageSegment {
return model.MessageSegment{
Type: string(SegmentTypeVoice),
Data: map[string]interface{}{
Data: map[string]any{
"url": url,
"duration": duration,
"file_size": fileSize,
@@ -68,7 +68,7 @@ func NewVoiceSegment(url string, duration int, fileSize int64) model.MessageSegm
func NewVideoSegment(url string, width, height, duration int, thumbnailURL string, fileSize int64) model.MessageSegment {
return model.MessageSegment{
Type: string(SegmentTypeVideo),
Data: map[string]interface{}{
Data: map[string]any{
"url": url,
"width": width,
"height": height,
@@ -83,7 +83,7 @@ func NewVideoSegment(url string, width, height, duration int, thumbnailURL strin
func NewFileSegment(url, name string, size int64, mimeType string) model.MessageSegment {
return model.MessageSegment{
Type: string(SegmentTypeFile),
Data: map[string]interface{}{
Data: map[string]any{
"url": url,
"name": name,
"size": size,
@@ -96,7 +96,7 @@ func NewFileSegment(url, name string, size int64, mimeType string) model.Message
func NewAtSegment(userID string) model.MessageSegment {
return model.MessageSegment{
Type: string(SegmentTypeAt),
Data: map[string]interface{}{
Data: map[string]any{
"user_id": userID,
},
}
@@ -106,7 +106,7 @@ func NewAtSegment(userID string) model.MessageSegment {
func NewAtAllSegment() model.MessageSegment {
return model.MessageSegment{
Type: string(SegmentTypeAt),
Data: map[string]interface{}{
Data: map[string]any{
"user_id": "all",
},
}
@@ -116,7 +116,7 @@ func NewAtAllSegment() model.MessageSegment {
func NewReplySegment(messageID string) model.MessageSegment {
return model.MessageSegment{
Type: string(SegmentTypeReply),
Data: map[string]interface{}{"id": messageID},
Data: map[string]any{"id": messageID},
}
}
@@ -124,7 +124,7 @@ func NewReplySegment(messageID string) model.MessageSegment {
func NewFaceSegment(id int, name, url string) model.MessageSegment {
return model.MessageSegment{
Type: string(SegmentTypeFace),
Data: map[string]interface{}{
Data: map[string]any{
"id": id,
"name": name,
"url": url,
@@ -136,7 +136,7 @@ func NewFaceSegment(id int, name, url string) model.MessageSegment {
func NewLinkSegment(url, title, description, image string) model.MessageSegment {
return model.MessageSegment{
Type: string(SegmentTypeLink),
Data: map[string]interface{}{
Data: map[string]any{
"url": url,
"title": title,
"description": description,

View File

@@ -236,13 +236,13 @@ func (h *RunnerHub) GetAvailableRunner(taskType runner.TaskType) (*RunnerConnect
}
// GetAllRunners 获取所有已连接的 Runner 信息
func (h *RunnerHub) GetAllRunners() []map[string]interface{} {
func (h *RunnerHub) GetAllRunners() []map[string]any {
h.mu.RLock()
defer h.mu.RUnlock()
result := make([]map[string]interface{}, 0, len(h.runners))
result := make([]map[string]any, 0, len(h.runners))
for id, conn := range h.runners {
result = append(result, map[string]interface{}{
result = append(result, map[string]any{
"id": id,
"version": conn.Version,
"capabilities": conn.Capabilities,

View File

@@ -43,7 +43,7 @@ func NewTaskManager(hub *RunnerHub, timeout time.Duration, logger *zap.Logger) *
}
// SubmitTask 提交任务并等待结果
func (tm *TaskManager) SubmitTask(ctx context.Context, taskType runner.TaskType, payload interface{}, callback TaskCallback) (*runner.Task, error) {
func (tm *TaskManager) SubmitTask(ctx context.Context, taskType runner.TaskType, payload any, callback TaskCallback) (*runner.Task, error) {
payloadBytes, err := json.Marshal(payload)
if err != nil {
return nil, fmt.Errorf("failed to marshal payload: %w", err)
@@ -89,7 +89,7 @@ func (tm *TaskManager) SubmitTask(ctx context.Context, taskType runner.TaskType,
}
// SubmitTaskSync 同步提交任务并等待结果
func (tm *TaskManager) SubmitTaskSync(ctx context.Context, taskType runner.TaskType, payload interface{}) (*runner.TaskResult, error) {
func (tm *TaskManager) SubmitTaskSync(ctx context.Context, taskType runner.TaskType, payload any) (*runner.TaskResult, error) {
resultChan := make(chan *runner.TaskResult, 1)
task, err := tm.SubmitTask(ctx, taskType, payload, func(result *runner.TaskResult) {

View File

@@ -36,14 +36,9 @@ func (h *AdminDashboardHandler) GetStats(c *gin.Context) {
// GetUserActivityTrend 获取用户活跃度趋势
// GET /api/v1/admin/dashboard/user-activity
func (h *AdminDashboardHandler) GetUserActivityTrend(c *gin.Context) {
// 解析天数参数默认7天
// 解析天数参数默认7天限制在1-30天
days, _ := strconv.Atoi(c.DefaultQuery("days", "7"))
if days <= 0 {
days = 7
}
if days > 30 {
days = 30
}
days = min(max(days, 1), 30)
trend, err := h.dashboardService.GetUserActivityTrend(c.Request.Context(), days)
if err != nil {
@@ -69,14 +64,9 @@ func (h *AdminDashboardHandler) GetContentStats(c *gin.Context) {
// GetPendingContent 获取待审核内容
// GET /api/v1/admin/dashboard/pending-content
func (h *AdminDashboardHandler) GetPendingContent(c *gin.Context) {
// 解析limit参数默认10条
// 解析limit参数默认10条限制在1-50条
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "10"))
if limit <= 0 {
limit = 10
}
if limit > 50 {
limit = 50
}
limit = min(max(limit, 1), 50)
content, err := h.dashboardService.GetPendingContent(c.Request.Context(), limit)
if err != nil {

View File

@@ -1,6 +1,7 @@
package handler
import (
"errors"
"strconv"
"github.com/gin-gonic/gin"
@@ -90,7 +91,7 @@ func (h *GroupHandler) GetGroup(c *gin.Context) {
group, err := h.groupService.GetGroupByID(groupID)
if err != nil {
if err == service.ErrGroupNotFound {
if errors.Is(err, service.ErrGroupNotFound) {
response.NotFound(c, "群组不存在")
return
}
@@ -129,7 +130,7 @@ func (h *GroupHandler) UpdateGroup(c *gin.Context) {
return
}
updates := make(map[string]interface{})
updates := make(map[string]any)
if req.Name != "" {
updates["name"] = req.Name
}
@@ -145,7 +146,7 @@ func (h *GroupHandler) UpdateGroup(c *gin.Context) {
response.Forbidden(c, "没有权限修改群组信息")
return
}
if err == service.ErrGroupNotFound {
if errors.Is(err, service.ErrGroupNotFound) {
response.NotFound(c, "群组不存在")
return
}
@@ -178,7 +179,7 @@ func (h *GroupHandler) DissolveGroup(c *gin.Context) {
response.Forbidden(c, "只有群主可以解散群组")
return
}
if err == service.ErrGroupNotFound {
if errors.Is(err, service.ErrGroupNotFound) {
response.NotFound(c, "群组不存在")
return
}
@@ -215,7 +216,7 @@ func (h *GroupHandler) TransferOwner(c *gin.Context) {
response.Forbidden(c, "只有群主可以转让群主")
return
}
if err == service.ErrGroupNotFound {
if errors.Is(err, service.ErrGroupNotFound) {
response.NotFound(c, "群组不存在")
return
}
@@ -279,7 +280,7 @@ func (h *GroupHandler) InviteMembers(c *gin.Context) {
response.Forbidden(c, "只有群成员可以邀请他人")
return
}
if err == service.ErrGroupNotFound {
if errors.Is(err, service.ErrGroupNotFound) {
response.NotFound(c, "群组不存在")
return
}
@@ -314,7 +315,7 @@ func (h *GroupHandler) JoinGroup(c *gin.Context) {
response.Forbidden(c, "该群不允许加入")
return
}
if err == service.ErrGroupNotFound {
if errors.Is(err, service.ErrGroupNotFound) {
response.NotFound(c, "群组不存在")
return
}
@@ -353,7 +354,7 @@ func (h *GroupHandler) LeaveGroup(c *gin.Context) {
response.BadRequest(c, "不是群成员")
return
}
if err == service.ErrGroupNotFound {
if errors.Is(err, service.ErrGroupNotFound) {
response.NotFound(c, "群组不存在")
return
}
@@ -390,7 +391,7 @@ func (h *GroupHandler) RemoveMember(c *gin.Context) {
response.Forbidden(c, "只有群主或管理员可以移除成员")
return
}
if err == service.ErrGroupNotFound {
if errors.Is(err, service.ErrGroupNotFound) {
response.NotFound(c, "群组不存在")
return
}
@@ -429,7 +430,7 @@ func (h *GroupHandler) GetMembers(c *gin.Context) {
members, total, err := h.groupService.GetMembers(groupID, page, pageSize)
if err != nil {
if err == service.ErrGroupNotFound {
if errors.Is(err, service.ErrGroupNotFound) {
response.NotFound(c, "群组不存在")
return
}
@@ -517,7 +518,7 @@ func (h *GroupHandler) HandleGetMyMemberInfo(c *gin.Context) {
// 获取群组信息
group, err := h.groupService.GetGroupByID(groupID)
if err != nil {
if err == service.ErrGroupNotFound {
if errors.Is(err, service.ErrGroupNotFound) {
response.NotFound(c, "群组不存在")
return
}
@@ -542,7 +543,7 @@ func (h *GroupHandler) HandleGetMyMemberInfo(c *gin.Context) {
}
// 添加群组禁言状态信息
response.Success(c, map[string]interface{}{
response.Success(c, map[string]any{
"member": memberResp,
"mute_all": group.MuteAll,
"is_muted": member.Muted || group.MuteAll,
@@ -570,7 +571,7 @@ func (h *GroupHandler) HandleDissolveGroup(c *gin.Context) {
response.Forbidden(c, "只有群主可以解散群组")
return
}
if err == service.ErrGroupNotFound {
if errors.Is(err, service.ErrGroupNotFound) {
response.NotFound(c, "群组不存在")
return
}
@@ -612,7 +613,7 @@ func (h *GroupHandler) HandleTransferOwner(c *gin.Context) {
response.Forbidden(c, "只有群主可以转让群主")
return
}
if err == service.ErrGroupNotFound {
if errors.Is(err, service.ErrGroupNotFound) {
response.NotFound(c, "群组不存在")
return
}
@@ -657,7 +658,7 @@ func (h *GroupHandler) HandleInviteMembers(c *gin.Context) {
response.Forbidden(c, "只有群主或管理员可以邀请他人")
return
}
if err == service.ErrGroupNotFound {
if errors.Is(err, service.ErrGroupNotFound) {
response.NotFound(c, "群组不存在")
return
}
@@ -696,7 +697,7 @@ func (h *GroupHandler) HandleJoinGroup(c *gin.Context) {
response.Forbidden(c, "该群不允许加入")
return
}
if err == service.ErrGroupNotFound {
if errors.Is(err, service.ErrGroupNotFound) {
response.NotFound(c, "群组不存在")
return
}
@@ -741,7 +742,7 @@ func (h *GroupHandler) HandleSetNickname(c *gin.Context) {
response.BadRequest(c, "不是群成员")
return
}
if err == service.ErrGroupNotFound {
if errors.Is(err, service.ErrGroupNotFound) {
response.NotFound(c, "群组不存在")
return
}
@@ -778,7 +779,7 @@ func (h *GroupHandler) HandleSetJoinType(c *gin.Context) {
response.Forbidden(c, "只有群主可以设置加群方式")
return
}
if err == service.ErrGroupNotFound {
if errors.Is(err, service.ErrGroupNotFound) {
response.NotFound(c, "群组不存在")
return
}
@@ -824,7 +825,7 @@ func (h *GroupHandler) HandleCreateAnnouncement(c *gin.Context) {
response.Forbidden(c, "只有群主或管理员可以发布公告")
return
}
if err == service.ErrGroupNotFound {
if errors.Is(err, service.ErrGroupNotFound) {
response.NotFound(c, "群组不存在")
return
}
@@ -855,7 +856,7 @@ func (h *GroupHandler) HandleGetAnnouncements(c *gin.Context) {
announcements, total, err := h.groupService.GetAnnouncements(groupID, page, pageSize)
if err != nil {
if err == service.ErrGroupNotFound {
if errors.Is(err, service.ErrGroupNotFound) {
response.NotFound(c, "群组不存在")
return
}
@@ -916,7 +917,7 @@ func (h *GroupHandler) GetMyMemberInfo(c *gin.Context) {
// 获取群组信息
group, err := h.groupService.GetGroupByID(groupID)
if err != nil {
if err == service.ErrGroupNotFound {
if errors.Is(err, service.ErrGroupNotFound) {
response.NotFound(c, "群组不存在")
return
}
@@ -941,7 +942,7 @@ func (h *GroupHandler) GetMyMemberInfo(c *gin.Context) {
}
// 添加群组禁言状态信息
response.Success(c, map[string]interface{}{
response.Success(c, map[string]any{
"member": memberResp,
"mute_all": group.MuteAll,
"is_muted": member.Muted || group.MuteAll,
@@ -981,7 +982,7 @@ func (h *GroupHandler) SetMemberRole(c *gin.Context) {
response.Forbidden(c, "只有群主可以设置成员角色")
return
}
if err == service.ErrGroupNotFound {
if errors.Is(err, service.ErrGroupNotFound) {
response.NotFound(c, "群组不存在")
return
}
@@ -1022,7 +1023,7 @@ func (h *GroupHandler) SetNickname(c *gin.Context) {
response.BadRequest(c, "不是群成员")
return
}
if err == service.ErrGroupNotFound {
if errors.Is(err, service.ErrGroupNotFound) {
response.NotFound(c, "群组不存在")
return
}
@@ -1065,7 +1066,7 @@ func (h *GroupHandler) MuteMember(c *gin.Context) {
response.Forbidden(c, "只有群主或管理员可以禁言成员")
return
}
if err == service.ErrGroupNotFound {
if errors.Is(err, service.ErrGroupNotFound) {
response.NotFound(c, "群组不存在")
return
}
@@ -1116,7 +1117,7 @@ func (h *GroupHandler) SetMuteAll(c *gin.Context) {
response.Forbidden(c, "只有群主可以设置全员禁言")
return
}
if err == service.ErrGroupNotFound {
if errors.Is(err, service.ErrGroupNotFound) {
response.NotFound(c, "群组不存在")
return
}
@@ -1157,7 +1158,7 @@ func (h *GroupHandler) SetJoinType(c *gin.Context) {
response.Forbidden(c, "只有群主可以设置加群方式")
return
}
if err == service.ErrGroupNotFound {
if errors.Is(err, service.ErrGroupNotFound) {
response.NotFound(c, "群组不存在")
return
}
@@ -1205,7 +1206,7 @@ func (h *GroupHandler) CreateAnnouncement(c *gin.Context) {
response.Forbidden(c, "只有群主或管理员可以发布公告")
return
}
if err == service.ErrGroupNotFound {
if errors.Is(err, service.ErrGroupNotFound) {
response.NotFound(c, "群组不存在")
return
}
@@ -1236,7 +1237,7 @@ func (h *GroupHandler) GetAnnouncements(c *gin.Context) {
announcements, total, err := h.groupService.GetAnnouncements(groupID, page, pageSize)
if err != nil {
if err == service.ErrGroupNotFound {
if errors.Is(err, service.ErrGroupNotFound) {
response.NotFound(c, "群组不存在")
return
}
@@ -1363,7 +1364,7 @@ func (h *GroupHandler) GetMembersByCursor(c *gin.Context) {
// 调用游标分页服务
result, err := h.groupService.GetMembersByCursor(c.Request.Context(), groupID, req)
if err != nil {
if err == service.ErrGroupNotFound {
if errors.Is(err, service.ErrGroupNotFound) {
response.NotFound(c, "群组不存在")
return
}
@@ -1406,7 +1407,7 @@ func (h *GroupHandler) GetAnnouncementsByCursor(c *gin.Context) {
// 调用游标分页服务
result, err := h.groupService.GetAnnouncementsByCursor(c.Request.Context(), groupID, req)
if err != nil {
if err == service.ErrGroupNotFound {
if errors.Is(err, service.ErrGroupNotFound) {
response.NotFound(c, "群组不存在")
return
}
@@ -1472,7 +1473,7 @@ func (h *GroupHandler) HandleSetGroupKick(c *gin.Context) {
response.Forbidden(c, "只有群主或管理员可以移除成员")
return
}
if err == service.ErrGroupNotFound {
if errors.Is(err, service.ErrGroupNotFound) {
response.NotFound(c, "群组不存在")
return
}
@@ -1543,7 +1544,7 @@ func (h *GroupHandler) HandleSetGroupBan(c *gin.Context) {
response.Forbidden(c, "只有群主或管理员可以禁言成员")
return
}
if err == service.ErrGroupNotFound {
if errors.Is(err, service.ErrGroupNotFound) {
response.NotFound(c, "群组不存在")
return
}
@@ -1593,7 +1594,7 @@ func (h *GroupHandler) HandleSetGroupWholeBan(c *gin.Context) {
response.Forbidden(c, "只有群主可以设置全员禁言")
return
}
if err == service.ErrGroupNotFound {
if errors.Is(err, service.ErrGroupNotFound) {
response.NotFound(c, "群组不存在")
return
}
@@ -1646,7 +1647,7 @@ func (h *GroupHandler) HandleSetGroupAdmin(c *gin.Context) {
response.Forbidden(c, "只有群主可以设置管理员")
return
}
if err == service.ErrGroupNotFound {
if errors.Is(err, service.ErrGroupNotFound) {
response.NotFound(c, "群组不存在")
return
}
@@ -1691,7 +1692,7 @@ func (h *GroupHandler) HandleSetGroupName(c *gin.Context) {
return
}
updates := map[string]interface{}{
updates := map[string]any{
"name": params.GroupName,
}
@@ -1701,7 +1702,7 @@ func (h *GroupHandler) HandleSetGroupName(c *gin.Context) {
response.Forbidden(c, "没有权限修改群组信息")
return
}
if err == service.ErrGroupNotFound {
if errors.Is(err, service.ErrGroupNotFound) {
response.NotFound(c, "群组不存在")
return
}
@@ -1740,7 +1741,7 @@ func (h *GroupHandler) HandleSetGroupAvatar(c *gin.Context) {
return
}
updates := map[string]interface{}{
updates := map[string]any{
"avatar": params.Avatar,
}
@@ -1750,7 +1751,7 @@ func (h *GroupHandler) HandleSetGroupAvatar(c *gin.Context) {
response.Forbidden(c, "没有权限修改群组信息")
return
}
if err == service.ErrGroupNotFound {
if errors.Is(err, service.ErrGroupNotFound) {
response.NotFound(c, "群组不存在")
return
}
@@ -1784,7 +1785,7 @@ func (h *GroupHandler) HandleSetGroupLeave(c *gin.Context) {
response.BadRequest(c, "不是群成员")
return
}
if err == service.ErrGroupNotFound {
if errors.Is(err, service.ErrGroupNotFound) {
response.NotFound(c, "群组不存在")
return
}
@@ -1907,7 +1908,7 @@ func (h *GroupHandler) HandleGetGroupInfo(c *gin.Context) {
group, err := h.groupService.GetGroupByID(groupID)
if err != nil {
if err == service.ErrGroupNotFound {
if errors.Is(err, service.ErrGroupNotFound) {
response.NotFound(c, "群组不存在")
return
}
@@ -1945,7 +1946,7 @@ func (h *GroupHandler) HandleGetGroupMemberList(c *gin.Context) {
members, total, err := h.groupService.GetMembers(groupID, page, pageSize)
if err != nil {
if err == service.ErrGroupNotFound {
if errors.Is(err, service.ErrGroupNotFound) {
response.NotFound(c, "群组不存在")
return
}

View File

@@ -1,6 +1,7 @@
package handler
import (
"errors"
"strconv"
"carrot_bbs/internal/dto"
@@ -39,21 +40,21 @@ type subjectResponse struct {
}
type materialResponse struct {
ID string `json:"id"`
SubjectID string `json:"subject_id"`
Title string `json:"title"`
Description string `json:"description,omitempty"`
FileType string `json:"file_type"`
FileSize int64 `json:"file_size"`
FileURL string `json:"file_url"`
FileName string `json:"file_name"`
DownloadCount int `json:"download_count"`
AuthorID string `json:"author_id,omitempty"`
Tags []string `json:"tags,omitempty"`
Status string `json:"status"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
Subject *subjectResponse `json:"subject,omitempty"`
ID string `json:"id"`
SubjectID string `json:"subject_id"`
Title string `json:"title"`
Description string `json:"description,omitempty"`
FileType string `json:"file_type"`
FileSize int64 `json:"file_size"`
FileURL string `json:"file_url"`
FileName string `json:"file_name"`
DownloadCount int `json:"download_count"`
AuthorID string `json:"author_id,omitempty"`
Tags []string `json:"tags,omitempty"`
Status string `json:"status"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
Subject *subjectResponse `json:"subject,omitempty"`
}
// =====================================================
@@ -170,11 +171,11 @@ func (h *MaterialHandler) ListMaterials(c *gin.Context) {
}
response.Success(c, gin.H{
"list": result,
"total": total,
"page": page,
"page_size": pageSize,
"has_more": int64(page*pageSize) < total,
"list": result,
"total": total,
"page": page,
"page_size": pageSize,
"has_more": int64(page*pageSize) < total,
})
}
@@ -305,7 +306,7 @@ func (h *MaterialHandler) AdminUpdateSubject(c *gin.Context) {
func (h *MaterialHandler) AdminDeleteSubject(c *gin.Context) {
id := c.Param("id")
if err := h.materialService.DeleteSubject(id); err != nil {
if err == service.ErrSubjectHasMaterials {
if errors.Is(err, service.ErrSubjectHasMaterials) {
response.BadRequest(c, "学科下存在资料,无法删除")
return
}

View File

@@ -1,6 +1,7 @@
package handler
import (
"errors"
"net/http"
"github.com/gin-gonic/gin"
@@ -81,11 +82,11 @@ func (h *StickerHandler) AddSticker(c *gin.Context) {
sticker, err := h.stickerService.AddSticker(userID, req.URL, req.Width, req.Height)
if err != nil {
if err == service.ErrStickerAlreadyExists {
if errors.Is(err, service.ErrStickerAlreadyExists) {
response.Error(c, http.StatusConflict, "sticker already exists")
return
}
if err == service.ErrInvalidStickerURL {
if errors.Is(err, service.ErrInvalidStickerURL) {
response.BadRequest(c, "invalid sticker url, only http/https is allowed")
return
}

View File

@@ -67,9 +67,9 @@ func (h *WSHandler) HandleWebSocket(c *gin.Context) {
if token == "" {
authHeader := c.GetHeader("Authorization")
if authHeader != "" {
parts := strings.SplitN(authHeader, " ", 2)
if len(parts) == 2 && parts[0] == "Bearer" {
token = parts[1]
prefix, rest, found := strings.Cut(authHeader, " ")
if found && prefix == "Bearer" {
token = rest
}
}
}
@@ -489,7 +489,7 @@ func (h *WSHandler) handleCallInvite(ctx context.Context, client *ws.Client, pay
EventID: h.wsHub.NextID(),
Type: "call_invited",
TS: time.Now().UnixMilli(),
Payload: map[string]interface{}{
Payload: map[string]any{
"call_id": call.ID,
"conversation_id": req.ConversationID,
"callee_id": req.CalleeID,
@@ -528,7 +528,7 @@ func (h *WSHandler) handleCallAnswer(ctx context.Context, client *ws.Client, pay
EventID: h.wsHub.NextID(),
Type: "call_accepted",
TS: time.Now().UnixMilli(),
Payload: map[string]interface{}{
Payload: map[string]any{
"call_id": call.ID,
"started_at": call.StartedAt,
},
@@ -582,7 +582,7 @@ func (h *WSHandler) handleCallSDP(ctx context.Context, client *ws.Client, payloa
return
}
signalPayload, _ := json.Marshal(map[string]interface{}{
signalPayload, _ := json.Marshal(map[string]any{
"sdp_type": req.SDPType,
"sdp": json.RawMessage(req.SDP),
})
@@ -602,7 +602,7 @@ func (h *WSHandler) handleCallICE(ctx context.Context, client *ws.Client, payloa
return
}
signalPayload, _ := json.Marshal(map[string]interface{}{
signalPayload, _ := json.Marshal(map[string]any{
"candidate": json.RawMessage(req.Candidate),
})
if err := h.callService.RelaySignal(ctx, req.CallID, client.UserID, "call_ice", signalPayload); err != nil {
@@ -631,7 +631,7 @@ func (h *WSHandler) handleCallEnd(ctx context.Context, client *ws.Client, payloa
EventID: h.wsHub.NextID(),
Type: "call_ended",
TS: time.Now().UnixMilli(),
Payload: map[string]interface{}{
Payload: map[string]any{
"call_id": call.ID,
"duration": call.Duration,
},

View File

@@ -21,15 +21,13 @@ func Auth(jwtService *service.JWTService) gin.HandlerFunc {
}
// 提取Token
parts := strings.SplitN(authHeader, " ", 2)
if len(parts) != 2 || parts[0] != "Bearer" {
prefix, token, found := strings.Cut(authHeader, " ")
if !found || prefix != "Bearer" {
response.Unauthorized(c, "invalid authorization header format")
c.Abort()
return
}
token := parts[1]
// 验证Token
claims, err := jwtService.ParseToken(token)
if err != nil {
@@ -56,14 +54,12 @@ func OptionalAuth(jwtService *service.JWTService) gin.HandlerFunc {
}
// 提取Token
parts := strings.SplitN(authHeader, " ", 2)
if len(parts) != 2 || parts[0] != "Bearer" {
prefix, token, found := strings.Cut(authHeader, " ")
if !found || prefix != "Bearer" {
c.Next()
return
}
token := parts[1]
// 验证Token
claims, err := jwtService.ParseToken(token)
if err != nil {

View File

@@ -66,7 +66,7 @@ func (a StringArray) Value() (driver.Value, error) {
}
// Scan 实现 sql.Scanner 接口
func (a *StringArray) Scan(value interface{}) error {
func (a *StringArray) Scan(value any) error {
if value == nil {
*a = nil
return nil
@@ -80,20 +80,20 @@ func (a *StringArray) Scan(value interface{}) error {
// MaterialFile 文件资料
type MaterialFile struct {
ID string `json:"id" gorm:"type:varchar(36);primaryKey"`
SubjectID string `json:"subject_id" gorm:"type:varchar(36);index;not null"`
Title string `json:"title" gorm:"type:varchar(200);not null"`
Description string `json:"description" gorm:"type:text"`
ID string `json:"id" gorm:"type:varchar(36);primaryKey"`
SubjectID string `json:"subject_id" gorm:"type:varchar(36);index;not null"`
Title string `json:"title" gorm:"type:varchar(200);not null"`
Description string `json:"description" gorm:"type:text"`
FileType MaterialFileType `json:"file_type" gorm:"type:varchar(20);not null;index"`
FileSize int64 `json:"file_size" gorm:"default:0"` // 文件大小(字节)
FileURL string `json:"file_url" gorm:"type:text;not null"` // 文件URL
FileName string `json:"file_name" gorm:"type:varchar(255)"` // 原始文件名
DownloadCount int `json:"download_count" gorm:"default:0"` // 下载次数
AuthorID string `json:"author_id" gorm:"type:varchar(36);index"` // 上传者ID
Tags StringArray `json:"tags" gorm:"type:json"` // 标签
Status MaterialStatus `json:"status" gorm:"type:varchar(20);default:active;index"` // 状态
CreatedAt time.Time `json:"created_at" gorm:"autoCreateTime;index"`
UpdatedAt time.Time `json:"updated_at" gorm:"autoUpdateTime"`
FileSize int64 `json:"file_size" gorm:"default:0"` // 文件大小(字节)
FileURL string `json:"file_url" gorm:"type:text;not null"` // 文件URL
FileName string `json:"file_name" gorm:"type:varchar(255)"` // 原始文件名
DownloadCount int `json:"download_count" gorm:"default:0"` // 下载次数
AuthorID string `json:"author_id" gorm:"type:varchar(36);index"` // 上传者ID
Tags StringArray `json:"tags" gorm:"type:json"` // 标签
Status MaterialStatus `json:"status" gorm:"type:varchar(20);default:active;index"` // 状态
CreatedAt time.Time `json:"created_at" gorm:"autoCreateTime;index"`
UpdatedAt time.Time `json:"updated_at" gorm:"autoUpdateTime"`
// 关联
Subject *MaterialSubject `json:"subject,omitempty" gorm:"foreignKey:SubjectID"`

View File

@@ -97,7 +97,7 @@ func (e ExtraData) Value() (driver.Value, error) {
}
// Scan 实现sql.Scanner接口用于数据库读取
func (e *ExtraData) Scan(value interface{}) error {
func (e *ExtraData) Scan(value any) error {
if value == nil {
return nil
}
@@ -109,7 +109,7 @@ func (e *ExtraData) Scan(value interface{}) error {
}
// MessageSegmentData 单个消息段的数据
type MessageSegmentData map[string]interface{}
type MessageSegmentData map[string]any
// MessageSegment 消息段
type MessageSegment struct {
@@ -126,7 +126,7 @@ func (s MessageSegments) Value() (driver.Value, error) {
}
// Scan 实现sql.Scanner接口用于数据库读取
func (s *MessageSegments) Scan(value interface{}) error {
func (s *MessageSegments) Scan(value any) error {
if value == nil {
*s = nil
return nil
@@ -148,11 +148,11 @@ type Message struct {
Seq int64 `gorm:"not null;index:idx_msg_conversation_seq,priority:2" json:"seq"` // 会话内序号,用于排序和增量同步
// 消息内容字段
Segments MessageSegments `gorm:"-" json:"segments"` // 消息链(内存中使用,不映射到数据库)
SegmentsEncrypted string `gorm:"column:segments;type:text" json:"-"` // 加密后的消息链(存储在数据库)
Segments MessageSegments `gorm:"-" json:"segments"` // 消息链(内存中使用,不映射到数据库)
SegmentsEncrypted string `gorm:"column:segments;type:text" json:"-"` // 加密后的消息链(存储在数据库)
SegmentsKeyVersion int `gorm:"column:segments_key_version;default:0" json:"segments_key_version"` // 密钥版本,用于密钥轮换
ReplyToID *string `json:"reply_to_id,omitempty"` // 回复的消息IDstring类型
ReplyToID *string `json:"reply_to_id,omitempty"` // 回复的消息IDstring类型
Status MessageStatus `gorm:"type:varchar(20);default:'normal'" json:"status"` // 消息状态
// 新增字段:消息分类和系统消息类型

View File

@@ -75,7 +75,7 @@ func (e SystemNotificationExtra) Value() (driver.Value, error) {
}
// Scan 实现sql.Scanner接口
func (e *SystemNotificationExtra) Scan(value interface{}) error {
func (e *SystemNotificationExtra) Scan(value any) error {
if value == nil {
return nil
}

View File

@@ -86,7 +86,7 @@ func (e *MessageEncryptor) DecryptMessageSegments(ciphertext string) ([]byte, er
// TryParseMessageSegments 尝试解析消息段
// 先尝试解密,如果失败则尝试直接解析(兼容未加密的旧数据)
func TryParseMessageSegments(ciphertext string, segments interface{}) error {
func TryParseMessageSegments(ciphertext string, segments any) error {
if ciphertext == "" {
return nil
}

View File

@@ -93,14 +93,14 @@ func (r *PageRequest) IsBackward() bool {
// PageResponse 游标分页响应结构
type PageResponse struct {
Items interface{} `json:"items"`
NextCursor string `json:"next_cursor"`
PrevCursor string `json:"prev_cursor,omitempty"`
HasMore bool `json:"has_more"`
Items any `json:"items"`
NextCursor string `json:"next_cursor"`
PrevCursor string `json:"prev_cursor,omitempty"`
HasMore bool `json:"has_more"`
}
// NewPageResponse 创建分页响应
func NewPageResponse(items interface{}, nextCursor, prevCursor string, hasMore bool) *PageResponse {
func NewPageResponse(items any, nextCursor, prevCursor string, hasMore bool) *PageResponse {
return &PageResponse{
Items: items,
NextCursor: nextCursor,

View File

@@ -103,7 +103,7 @@ func (m *Metrics) GetHookMetrics() []MetricSnapshot {
}
func parseKey(key string) (HookType, string) {
for i := 0; i < len(key); i++ {
for i := range len(key) {
if key[i] == ':' {
return HookType(key[:i]), key[i+1:]
}

View File

@@ -210,8 +210,8 @@ type responseFormatConfig struct {
}
type chatMessage struct {
Role string `json:"role"`
Content interface{} `json:"content"`
Role string `json:"role"`
Content any `json:"content"`
}
type contentPart struct {

View File

@@ -73,7 +73,7 @@ func (c *Client) Get(ctx context.Context, key string) (string, error) {
}
// Set 设置值
func (c *Client) Set(ctx context.Context, key string, value interface{}, expiration time.Duration) error {
func (c *Client) Set(ctx context.Context, key string, value any, expiration time.Duration) error {
return c.rdb.Set(ctx, key, value, expiration).Err()
}
@@ -121,12 +121,12 @@ func (c *Client) IsMiniRedis() bool {
// ==================== Hash 操作 ====================
// HSet 设置 Hash 字段
func (c *Client) HSet(ctx context.Context, key string, field string, value interface{}) error {
func (c *Client) HSet(ctx context.Context, key string, field string, value any) error {
return c.rdb.HSet(ctx, key, field, value).Err()
}
// HMSet 批量设置 Hash 字段
func (c *Client) HMSet(ctx context.Context, key string, values map[string]interface{}) error {
func (c *Client) HMSet(ctx context.Context, key string, values map[string]any) error {
return c.rdb.HMSet(ctx, key, values).Err()
}
@@ -136,7 +136,7 @@ func (c *Client) HGet(ctx context.Context, key string, field string) (string, er
}
// HMGet 批量获取 Hash 字段值
func (c *Client) HMGet(ctx context.Context, key string, fields ...string) ([]interface{}, error) {
func (c *Client) HMGet(ctx context.Context, key string, fields ...string) ([]any, error) {
return c.rdb.HMGet(ctx, key, fields...).Result()
}
@@ -203,7 +203,7 @@ func (c *Client) ZRevRange(ctx context.Context, key string, start, stop int64) (
}
// ZRem 删除 Sorted Set 成员
func (c *Client) ZRem(ctx context.Context, key string, members ...interface{}) error {
func (c *Client) ZRem(ctx context.Context, key string, members ...any) error {
return c.rdb.ZRem(ctx, key, members...).Err()
}

View File

@@ -10,20 +10,20 @@ import (
// Response 统一响应结构
type Response struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data,omitempty"`
Code int `json:"code"`
Message string `json:"message"`
Data any `json:"data,omitempty"`
}
// ResponseSnakeCase 统一响应结构(snake_case)
type ResponseSnakeCase struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data,omitempty"`
Code int `json:"code"`
Message string `json:"message"`
Data any `json:"data,omitempty"`
}
// Success 成功响应
func Success(c *gin.Context, data interface{}) {
func Success(c *gin.Context, data any) {
c.JSON(http.StatusOK, Response{
Code: 0,
Message: "success",
@@ -32,7 +32,7 @@ func Success(c *gin.Context, data interface{}) {
}
// SuccessWithMessage 成功响应带消息
func SuccessWithMessage(c *gin.Context, message string, data interface{}) {
func SuccessWithMessage(c *gin.Context, message string, data any) {
c.JSON(http.StatusOK, Response{
Code: 0,
Message: message,
@@ -95,15 +95,15 @@ func InternalServerError(c *gin.Context, message string) {
// PaginatedResponse 分页响应
type PaginatedResponse struct {
List interface{} `json:"list"`
Total int64 `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
TotalPages int `json:"total_pages"`
List any `json:"list"`
Total int64 `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
TotalPages int `json:"total_pages"`
}
// Paginated 分页成功响应
func Paginated(c *gin.Context, list interface{}, total int64, page, pageSize int) {
func Paginated(c *gin.Context, list any, total int64, page, pageSize int) {
totalPages := int(total) / pageSize
if int(total)%pageSize > 0 {
totalPages++

View File

@@ -16,11 +16,11 @@ const (
// Event 表示一个WebSocket事件
type Event struct {
ID uint64 `json:"event_id"`
Type string `json:"type"`
Event string `json:"event"` // 事件名称(兼容多种协议)
TS int64 `json:"ts"`
Payload interface{} `json:"payload"`
ID uint64 `json:"event_id"`
Type string `json:"type"`
Event string `json:"event"` // 事件名称(兼容多种协议)
TS int64 `json:"ts"`
Payload any `json:"payload"`
}
// Client 表示一个WebSocket客户端连接
@@ -39,10 +39,10 @@ type Message struct {
// ResponseMessage 表示发送给客户端的消息
type ResponseMessage struct {
EventID uint64 `json:"event_id"`
Type string `json:"type"`
TS int64 `json:"ts"`
Payload interface{} `json:"payload,omitempty"`
EventID uint64 `json:"event_id"`
Type string `json:"type"`
TS int64 `json:"ts"`
Payload any `json:"payload,omitempty"`
}
// ErrorMessage 表示错误消息
@@ -76,11 +76,11 @@ type Hub struct {
seq uint64
mu sync.RWMutex
clients map[string]map[uint64]*Client // userID -> clientID -> Client
history map[string][]Event // userID -> []Event (用于断线重连历史回放)
clients map[string]map[uint64]*Client // userID -> clientID -> Client
history map[string][]Event // userID -> []Event (用于断线重连历史回放)
subscribers map[string]map[uint64]*subscriber // userID -> subscriberID -> subscriber
disconnectHandlers []DisconnectHandler // 断开连接事件处理器列表
connectHandlers []ConnectHandler // 连接事件处理器列表
disconnectHandlers []DisconnectHandler // 断开连接事件处理器列表
connectHandlers []ConnectHandler // 连接事件处理器列表
}
// NewHub 创建WebSocket Hub
@@ -213,7 +213,7 @@ func (h *Hub) GetClientCount(userID string) int {
}
// PublishToUser 向指定用户发送事件
func (h *Hub) PublishToUser(userID string, eventType string, payload interface{}) Event {
func (h *Hub) PublishToUser(userID string, eventType string, payload any) Event {
ev := Event{
ID: h.NextID(),
Type: eventType,
@@ -225,7 +225,7 @@ func (h *Hub) PublishToUser(userID string, eventType string, payload interface{}
}
// PublishToUsers 向多个用户发送事件
func (h *Hub) PublishToUsers(userIDs []string, eventType string, payload interface{}) {
func (h *Hub) PublishToUsers(userIDs []string, eventType string, payload any) {
for _, uid := range userIDs {
h.PublishToUser(uid, eventType, payload)
}
@@ -233,7 +233,7 @@ func (h *Hub) PublishToUsers(userIDs []string, eventType string, payload interfa
// PublishToUserOnline 仅在用户在线时发送事件,不存入历史回放
// 适用于通话信令等实时性消息,避免断线重连时重放过期消息
func (h *Hub) PublishToUserOnline(userID string, eventType string, payload interface{}) bool {
func (h *Hub) PublishToUserOnline(userID string, eventType string, payload any) bool {
h.mu.RLock()
targets := make([]*Client, 0, len(h.clients[userID]))
for _, c := range h.clients[userID] {
@@ -269,7 +269,7 @@ func (h *Hub) PublishToUserOnline(userID string, eventType string, payload inter
// PublishToUserOnlineReliable 可靠地发送事件给在线用户(阻塞发送)
// 用于重要的信令消息(如 SDP, ICE candidate确保消息送达
// 如果用户不在线,返回 false
func (h *Hub) PublishToUserOnlineReliable(userID string, eventType string, payload interface{}) bool {
func (h *Hub) PublishToUserOnlineReliable(userID string, eventType string, payload any) bool {
h.mu.RLock()
targets := make([]*Client, 0, len(h.clients[userID]))
for _, c := range h.clients[userID] {
@@ -376,7 +376,7 @@ func (h *Hub) SendError(client *Client, code string, message string) {
}
// Broadcast 广播消息给所有连接
func (h *Hub) Broadcast(eventType string, payload interface{}) {
func (h *Hub) Broadcast(eventType string, payload any) {
h.mu.RLock()
userIDs := make([]string, 0, len(h.clients))
for uid := range h.clients {

View File

@@ -94,7 +94,7 @@ func (r *commentRepository) Delete(id string) error {
// 减少帖子的评论数并同步热度分
// 评论数属于统计字段不应影响帖子内容更新时间updated_at
if err := tx.Model(&model.Post{}).Where("id = ?", comment.PostID).
UpdateColumns(map[string]interface{}{
UpdateColumns(map[string]any{
"comments_count": gorm.Expr("comments_count - 1"),
"updated_at": gorm.Expr("updated_at"),
}).Error; err != nil {

View File

@@ -125,7 +125,7 @@ func (r *deviceTokenRepository) Upsert(token *model.DeviceToken) error {
}
// 更新现有记录
return r.db.Model(&existing).Updates(map[string]interface{}{
return r.db.Model(&existing).Updates(map[string]any{
"push_token": token.PushToken,
"is_active": true,
"device_name": token.DeviceName,
@@ -151,7 +151,7 @@ func (r *deviceTokenRepository) Deactivate(deviceID string) error {
func (r *deviceTokenRepository) Activate(deviceID string) error {
return r.db.Model(&model.DeviceToken{}).
Where("device_id = ?", deviceID).
Updates(map[string]interface{}{
Updates(map[string]any{
"is_active": true,
"last_used_at": time.Now(),
}).Error

View File

@@ -91,7 +91,7 @@ func (r *messageRepository) CreateConversationWithParticipants(conv *model.Conve
participant := model.ConversationParticipant{
ConversationID: conv.ID,
UserID: userID,
LastReadSeq: 0,
LastReadSeq: 0,
}
if err := tx.Create(&participant).Error; err != nil {
return err
@@ -291,7 +291,7 @@ func (r *messageRepository) UpdateLastReadSeq(conversationID string, userID stri
{Name: "conversation_id"},
{Name: "user_id"},
},
DoUpdates: clause.Assignments(map[string]interface{}{
DoUpdates: clause.Assignments(map[string]any{
"last_read_seq": lastReadSeq,
"updated_at": gorm.Expr("CURRENT_TIMESTAMP"),
}),
@@ -324,7 +324,7 @@ func (r *messageRepository) UpdatePinned(conversationID string, userID string, i
{Name: "conversation_id"},
{Name: "user_id"},
},
DoUpdates: clause.Assignments(map[string]interface{}{
DoUpdates: clause.Assignments(map[string]any{
"is_pinned": isPinned,
"updated_at": gorm.Expr("CURRENT_TIMESTAMP"),
}),
@@ -358,7 +358,7 @@ func (r *messageRepository) GetUnreadCount(conversationID string, userID string)
func (r *messageRepository) UpdateConversationLastSeq(conversationID string, seq int64) error {
return r.db.Model(&model.Conversation{}).
Where("id = ?", conversationID).
Updates(map[string]interface{}{
Updates(map[string]any{
"last_seq": seq,
"last_msg_time": gorm.Expr("CURRENT_TIMESTAMP"),
}).Error
@@ -393,7 +393,7 @@ func (r *messageRepository) CreateMessageWithSeq(msg *model.Message) error {
// 更新会话的last_seq
if err := tx.Model(&model.Conversation{}).
Where("id = ?", msg.ConversationID).
Updates(map[string]interface{}{
Updates(map[string]any{
"last_seq": msg.Seq,
"last_msg_time": gorm.Expr("CURRENT_TIMESTAMP"),
}).Error; err != nil {
@@ -455,7 +455,7 @@ func (r *messageRepository) UpdateMessageStatus(messageID string, status model.M
func (r *messageRepository) RecallMessage(messageID string, userID string) error {
return r.db.Model(&model.Message{}).
Where("id = ? AND sender_id = ?", messageID, userID).
Updates(map[string]interface{}{
Updates(map[string]any{
"status": model.MessageStatusRecalled,
"segments": model.MessageSegments{},
}).Error
@@ -526,7 +526,7 @@ func (r *messageRepository) MarkAllSystemMessagesAsRead(userID string) error {
{Name: "conversation_id"},
{Name: "user_id"},
},
DoUpdates: clause.Assignments(map[string]interface{}{
DoUpdates: clause.Assignments(map[string]any{
"last_read_seq": maxSeq,
"updated_at": gorm.Expr("CURRENT_TIMESTAMP"),
}),
@@ -653,7 +653,7 @@ func (r *messageRepository) BatchUpdateParticipants(ctx context.Context, updates
var cases []string
var whereClauses []string
var args []interface{}
var args []any
for _, u := range updates {
cases = append(cases, "WHEN (conversation_id = ? AND user_id = ?) THEN ?")
@@ -678,7 +678,7 @@ func (r *messageRepository) UpdateConversationLastSeqWithContext(ctx context.Con
return r.db.WithContext(ctx).
Model(&model.Conversation{}).
Where("id = ?", convID).
Updates(map[string]interface{}{
Updates(map[string]any{
"last_seq": lastSeq,
"last_msg_time": lastMsgTime,
"updated_at": time.Now(),
@@ -701,7 +701,7 @@ func (r *messageRepository) BatchUpdateParticipantsWithTx(tx *gorm.DB, updates [
var cases []string
var whereClauses []string
var args []interface{}
var args []any
for _, u := range updates {
cases = append(cases, "WHEN (conversation_id = ? AND user_id = ?) THEN ?")
@@ -725,7 +725,7 @@ func (r *messageRepository) BatchUpdateParticipantsWithTx(tx *gorm.DB, updates [
func (r *messageRepository) UpdateConversationLastSeqWithTx(tx *gorm.DB, convID string, lastSeq int64, lastMsgTime time.Time) error {
return tx.Model(&model.Conversation{}).
Where("id = ?", convID).
Updates(map[string]interface{}{
Updates(map[string]any{
"last_seq": lastSeq,
"last_msg_time": lastMsgTime,
"updated_at": time.Now(),

View File

@@ -166,7 +166,7 @@ func (r *postRepository) UpdateWithImages(post *model.Post, images *[]string) er
// UpdateModerationStatus 更新帖子审核状态
func (r *postRepository) UpdateModerationStatus(postID string, status model.PostStatus, rejectReason string, reviewedBy string) error {
// 审核状态更新属于管理操作不应影响帖子内容更新时间updated_at
updates := map[string]interface{}{
updates := map[string]any{
"status": status,
"reviewed_at": gorm.Expr("CURRENT_TIMESTAMP"),
"reviewed_by": reviewedBy,
@@ -455,7 +455,7 @@ func (r *postRepository) IsFavoritedBatch(postIDs []string, userID string) map[s
// IncrementViews 增加帖子观看量
func (r *postRepository) IncrementViews(postID string) error {
return r.db.Model(&model.Post{}).Where("id = ?", postID).
UpdateColumns(map[string]interface{}{
UpdateColumns(map[string]any{
"views_count": gorm.Expr("views_count + 1"),
"updated_at": gorm.Expr("updated_at"),
}).Error
@@ -467,7 +467,7 @@ func (r *postRepository) IncrementShares(postID string) (int, error) {
err := r.db.Transaction(func(tx *gorm.DB) error {
res := tx.Model(&model.Post{}).
Where("id = ? AND status = ?", postID, model.PostStatusPublished).
Updates(map[string]interface{}{
Updates(map[string]any{
"shares_count": gorm.Expr("shares_count + 1"),
"updated_at": gorm.Expr("updated_at"),
})
@@ -909,11 +909,11 @@ func (r *postRepository) BatchUpdateStatus(ids []string, status model.PostStatus
result := r.db.Model(&model.Post{}).
Where("id IN ?", ids).
Updates(map[string]any{
"status": status,
"reviewed_at": gorm.Expr("CURRENT_TIMESTAMP"),
"reviewed_by": reviewedBy,
"status": status,
"reviewed_at": gorm.Expr("CURRENT_TIMESTAMP"),
"reviewed_by": reviewedBy,
"reject_reason": "",
"updated_at": gorm.Expr("updated_at"),
"updated_at": gorm.Expr("updated_at"),
})
if result.Error != nil {

View File

@@ -113,7 +113,7 @@ func (r *pushRecordRepository) BatchUpdateStatus(ids []int64, status model.PushS
if len(ids) == 0 {
return nil
}
updates := map[string]interface{}{
updates := map[string]any{
"push_status": status,
}
if status == model.PushStatusPushed {
@@ -126,7 +126,7 @@ func (r *pushRecordRepository) BatchUpdateStatus(ids []int64, status model.PushS
// UpdateStatus 更新单条记录状态
func (r *pushRecordRepository) UpdateStatus(id int64, status model.PushStatus) error {
updates := map[string]interface{}{
updates := map[string]any{
"push_status": status,
}
if status == model.PushStatusPushed {
@@ -141,7 +141,7 @@ func (r *pushRecordRepository) UpdateStatus(id int64, status model.PushStatus) e
func (r *pushRecordRepository) MarkAsFailed(id int64, errMsg string) error {
return r.db.Model(&model.PushRecord{}).
Where("id = ?", id).
Updates(map[string]interface{}{
Updates(map[string]any{
"push_status": model.PushStatusFailed,
"error_message": errMsg,
"retry_count": gorm.Expr("retry_count + 1"),
@@ -152,7 +152,7 @@ func (r *pushRecordRepository) MarkAsFailed(id int64, errMsg string) error {
func (r *pushRecordRepository) MarkAsDelivered(id int64) error {
return r.db.Model(&model.PushRecord{}).
Where("id = ?", id).
Updates(map[string]interface{}{
Updates(map[string]any{
"push_status": model.PushStatusDelivered,
"delivered_at": time.Now(),
}).Error

View File

@@ -164,7 +164,7 @@ func (r *reportRepository) GetPendingReportsByTarget(targetType model.ReportTarg
// UpdateStatus 更新举报状态
func (r *reportRepository) UpdateStatus(id string, status model.ReportStatus, handledBy string, result string) error {
updates := map[string]interface{}{
updates := map[string]any{
"status": status,
"handled_by": handledBy,
"result": result,
@@ -174,7 +174,7 @@ func (r *reportRepository) UpdateStatus(id string, status model.ReportStatus, ha
// UpdateStatusWithContext 使用上下文更新举报状态
func (r *reportRepository) UpdateStatusWithContext(ctx context.Context, id string, status model.ReportStatus, handledBy string, result string) error {
updates := map[string]interface{}{
updates := map[string]any{
"status": status,
"handled_by": handledBy,
"result": result,
@@ -184,7 +184,7 @@ func (r *reportRepository) UpdateStatusWithContext(ctx context.Context, id strin
// BatchUpdateStatus 批量更新举报状态
func (r *reportRepository) BatchUpdateStatus(ids []string, status model.ReportStatus, handledBy string, result string) (int64, error) {
updates := map[string]interface{}{
updates := map[string]any{
"status": status,
"handled_by": handledBy,
"result": result,
@@ -195,7 +195,7 @@ func (r *reportRepository) BatchUpdateStatus(ids []string, status model.ReportSt
// BatchUpdateStatusWithContext 使用上下文批量更新举报状态
func (r *reportRepository) BatchUpdateStatusWithContext(ctx context.Context, ids []string, status model.ReportStatus, handledBy string, result string) (int64, error) {
updates := map[string]interface{}{
updates := map[string]any{
"status": status,
"handled_by": handledBy,
"result": result,

View File

@@ -91,7 +91,7 @@ func (r *systemNotificationRepository) MarkAsRead(id int64, receiverID string) e
now := model.SystemNotification{}.UpdatedAt
return r.db.Model(&model.SystemNotification{}).
Where("id = ? AND receiver_id = ?", id, receiverID).
Updates(map[string]interface{}{
Updates(map[string]any{
"is_read": true,
"read_at": now,
}).Error
@@ -102,7 +102,7 @@ func (r *systemNotificationRepository) MarkAllAsRead(receiverID string) error {
now := model.SystemNotification{}.UpdatedAt
return r.db.Model(&model.SystemNotification{}).
Where("receiver_id = ? AND is_read = ?", receiverID, false).
Updates(map[string]interface{}{
Updates(map[string]any{
"is_read": true,
"read_at": now,
}).Error

View File

@@ -173,12 +173,7 @@ func (s *adminDashboardServiceImpl) GetStats(ctx context.Context) (*dto.Dashboar
// GetUserActivityTrend 获取用户活跃度趋势
func (s *adminDashboardServiceImpl) GetUserActivityTrend(ctx context.Context, days int) ([]dto.UserActivityTrendResponse, error) {
if days <= 0 {
days = 7
}
if days > 30 {
days = 30
}
days = min(max(days, 1), 30)
now := time.Now()
results := make([]dto.UserActivityTrendResponse, days)
@@ -282,12 +277,7 @@ func (s *adminDashboardServiceImpl) GetContentStats(ctx context.Context) (*dto.C
// GetPendingContent 获取待审核内容
func (s *adminDashboardServiceImpl) GetPendingContent(ctx context.Context, limit int) ([]dto.PendingContentResponse, error) {
if limit <= 0 {
limit = 10
}
if limit > 50 {
limit = 50
}
limit = min(max(limit, 1), 50)
results := make([]dto.PendingContentResponse, 0)

View File

@@ -205,7 +205,7 @@ func (m *AsyncLogManager) flushDataChangeLogs(logs []*model.DataChangeLog, repo
}
// handleError 处理写入错误
func (m *AsyncLogManager) handleError(logType string, logs interface{}, err error) {
func (m *AsyncLogManager) handleError(logType string, logs any, err error) {
m.logger.Error("failed to batch write logs",
zap.String("log_type", logType),
zap.Error(err),
@@ -219,7 +219,7 @@ func (m *AsyncLogManager) handleError(logType string, logs interface{}, err erro
}
// writeToFallback 写入降级文件
func (m *AsyncLogManager) writeToFallback(logType string, logs interface{}) error {
func (m *AsyncLogManager) writeToFallback(logType string, logs any) error {
return nil
}

View File

@@ -35,7 +35,7 @@ type ActiveCall struct {
MediaType string // voice 或 video
CreatedAt time.Time
StartedAt *time.Time
Duration int64 // 通话时长(秒)
Duration int64 // 通话时长(秒)
// 参与者状态
Participants map[string]*ActiveParticipant
// ICE Servers
@@ -228,7 +228,7 @@ func (s *callService) Invite(ctx context.Context, callerID, calleeID, conversati
calleeOnline := s.hub.HasClients(calleeID)
// 发送来电通知
payload := map[string]interface{}{
payload := map[string]any{
"call_id": call.ID,
"conversation_id": conversationID,
"caller_id": callerID,
@@ -286,14 +286,14 @@ func (s *callService) Accept(ctx context.Context, callID, userID string) (*Activ
s.mu.Unlock()
// 通知拨打方
s.hub.PublishToUserOnline(call.CallerID, "call_accepted", map[string]interface{}{
s.hub.PublishToUserOnline(call.CallerID, "call_accepted", map[string]any{
"call_id": callID,
"started_at": now.UnixMilli(),
"ice_servers": s.config.WebRTC.ICEServers,
})
// 通知被叫方其他设备
s.hub.PublishToUserOnline(userID, "call_answered_elsewhere", map[string]interface{}{
s.hub.PublishToUserOnline(userID, "call_answered_elsewhere", map[string]any{
"call_id": callID,
"reason": "answered_on_another_device",
})
@@ -328,7 +328,7 @@ func (s *callService) Reject(ctx context.Context, callID, userID string) error {
s.saveCallHistory(call, model.CallStatusRejected, 0)
// 通知拨打方
s.hub.PublishToUserOnline(call.CallerID, "call_rejected", map[string]interface{}{
s.hub.PublishToUserOnline(call.CallerID, "call_rejected", map[string]any{
"call_id": callID,
"reason": "rejected",
})
@@ -362,7 +362,7 @@ func (s *callService) Busy(ctx context.Context, callID, userID string) error {
s.saveCallHistory(call, model.CallStatusMissed, 0)
// 通知拨打方
s.hub.PublishToUserOnline(call.CallerID, "call_busy", map[string]interface{}{
s.hub.PublishToUserOnline(call.CallerID, "call_busy", map[string]any{
"call_id": callID,
})
@@ -420,7 +420,7 @@ func (s *callService) End(ctx context.Context, callID, userID string, reason str
// 通知其他参与者
for pUserID := range call.Participants {
if pUserID != userID {
s.hub.PublishToUserOnline(pUserID, "call_ended", map[string]interface{}{
s.hub.PublishToUserOnline(pUserID, "call_ended", map[string]any{
"call_id": callID,
"ended_by": userID,
"reason": reason,
@@ -452,7 +452,7 @@ func (s *callService) RelaySignal(ctx context.Context, callID, fromUserID string
for pUserID := range call.Participants {
if pUserID != fromUserID {
s.hub.PublishToUserOnlineReliable(pUserID, signalType, map[string]interface{}{
s.hub.PublishToUserOnlineReliable(pUserID, signalType, map[string]any{
"call_id": callID,
"from_id": fromUserID,
"payload": json.RawMessage(payload),
@@ -481,7 +481,7 @@ func (s *callService) SetMuted(ctx context.Context, callID, userID string, muted
for pUserID := range call.Participants {
if pUserID != userID {
s.hub.PublishToUserOnline(pUserID, "call_peer_muted", map[string]interface{}{
s.hub.PublishToUserOnline(pUserID, "call_peer_muted", map[string]any{
"call_id": callID,
"user_id": userID,
"muted": muted,

View File

@@ -97,7 +97,7 @@ func NewChatService(
}
}
func (s *chatServiceImpl) publishToUsers(userIDs []string, event string, payload interface{}) {
func (s *chatServiceImpl) publishToUsers(userIDs []string, event string, payload any) {
if s.wsHub == nil || len(userIDs) == 0 {
return
}
@@ -329,7 +329,7 @@ func (s *chatServiceImpl) SendMessage(ctx context.Context, senderID string, conv
if conv.Type == model.ConversationTypeGroup {
detailType = "group"
}
s.publishToUsers(targetIDs, "chat_message", map[string]interface{}{
s.publishToUsers(targetIDs, "chat_message", map[string]any{
"detail_type": detailType,
"message": dto.ConvertMessageToResponse(message),
})
@@ -342,7 +342,7 @@ func (s *chatServiceImpl) SendMessage(ctx context.Context, senderID string, conv
s.conversationCache.InvalidateUnreadCount(p.UserID, conversationID)
}
if totalUnread, uErr := s.repo.GetAllUnreadCount(p.UserID); uErr == nil {
s.publishToUsers([]string{p.UserID}, "conversation_unread", map[string]interface{}{
s.publishToUsers([]string{p.UserID}, "conversation_unread", map[string]any{
"conversation_id": conversationID,
"total_unread": totalUnread,
})
@@ -490,7 +490,7 @@ func (s *chatServiceImpl) MarkAsRead(ctx context.Context, conversationID string,
for _, p := range participants {
targetIDs = append(targetIDs, p.UserID)
}
s.publishToUsers(targetIDs, "message_read", map[string]interface{}{
s.publishToUsers(targetIDs, "message_read", map[string]any{
"detail_type": detailType,
"conversation_id": conversationID,
"group_id": groupID,
@@ -499,7 +499,7 @@ func (s *chatServiceImpl) MarkAsRead(ctx context.Context, conversationID string,
})
}
if totalUnread, uErr := s.repo.GetAllUnreadCount(userID); uErr == nil {
s.publishToUsers([]string{userID}, "conversation_unread", map[string]interface{}{
s.publishToUsers([]string{userID}, "conversation_unread", map[string]any{
"conversation_id": conversationID,
"total_unread": totalUnread,
})
@@ -579,7 +579,7 @@ func (s *chatServiceImpl) RecallMessage(ctx context.Context, messageID string, u
for _, p := range participants {
targetIDs = append(targetIDs, p.UserID)
}
s.publishToUsers(targetIDs, "message_recall", map[string]interface{}{
s.publishToUsers(targetIDs, "message_recall", map[string]any{
"detail_type": detailType,
"conversation_id": message.ConversationID,
"group_id": groupID,
@@ -652,7 +652,7 @@ func (s *chatServiceImpl) SendTyping(ctx context.Context, senderID string, conve
continue
}
if s.wsHub != nil {
s.wsHub.PublishToUser(p.UserID, "typing", map[string]interface{}{
s.wsHub.PublishToUser(p.UserID, "typing", map[string]any{
"detail_type": detailType,
"conversation_id": conversationID,
"user_id": senderID,

View File

@@ -135,7 +135,7 @@ func (s *CommentService) reviewCommentAsync(
Images: imageURLs,
AuthorID: authorID,
ParentID: parentID,
}, map[string]interface{}{
}, map[string]any{
"result": result,
})

View File

@@ -56,7 +56,7 @@ type GroupService interface {
// 群组管理
CreateGroup(ownerID string, name string, description string, memberIDs []string) (*model.Group, error)
GetGroupByID(id string) (*model.Group, error)
UpdateGroup(userID string, groupID string, updates map[string]interface{}) error
UpdateGroup(userID string, groupID string, updates map[string]any) error
DissolveGroup(userID string, groupID string) error
TransferOwner(userID string, groupID string, newOwnerID string) error
GetUserGroups(userID string, page, pageSize int) ([]model.Group, int64, error)
@@ -300,7 +300,7 @@ func (s *groupService) GetMemberCount(groupID string) (int, error) {
}
// UpdateGroup 更新群组信息
func (s *groupService) UpdateGroup(userID string, groupID string, updates map[string]interface{}) error {
func (s *groupService) UpdateGroup(userID string, groupID string, updates map[string]any) error {
// 检查群组是否存在
group, err := s.groupRepo.GetByID(groupID)
if err != nil {
@@ -448,7 +448,7 @@ func (s *groupService) broadcastMemberJoinNotice(groupID string, targetUserID st
ConversationID: conv.ID,
SenderID: model.SystemSenderIDStr,
Segments: model.MessageSegments{
{Type: "text", Data: map[string]interface{}{"text": noticeContent}},
{Type: "text", Data: map[string]any{"text": noticeContent}},
},
Status: model.MessageStatusNormal,
Category: model.CategoryNotification,
@@ -1360,7 +1360,7 @@ func (s *groupService) MuteMember(userID string, groupID string, targetUserID st
ConversationID: conv.ID,
SenderID: model.SystemSenderIDStr,
Segments: model.MessageSegments{
{Type: "text", Data: map[string]interface{}{"text": noticeContent}},
{Type: "text", Data: map[string]any{"text": noticeContent}},
},
Status: model.MessageStatusNormal,
Category: model.CategoryNotification,

View File

@@ -1,10 +1,11 @@
package service
import (
"cmp"
"context"
"errors"
"math"
"sort"
"slices"
"sync"
"time"
@@ -103,9 +104,7 @@ func (w *HotRankWorker) Refresh(ctx context.Context) error {
if topN <= 0 {
topN = 100
}
if topN > 500 {
topN = 500
}
topN = min(topN, 500)
recentHours := w.cfg.HotRank.RecentWindowHours
if recentHours <= 0 {
recentHours = 168
@@ -176,15 +175,8 @@ func (w *HotRankWorker) Refresh(ctx context.Context) error {
raw[i] = base / denom
}
minR, maxR := raw[0], raw[0]
for _, v := range raw[1:] {
if v < minR {
minR = v
}
if v > maxR {
maxR = v
}
}
minR := slices.Min(raw)
maxR := slices.Max(raw)
ranked = make([]scored, len(snapshots))
for i := range snapshots {
@@ -196,11 +188,16 @@ func (w *HotRankWorker) Refresh(ctx context.Context) error {
}
ranked[i] = scored{idx: i, norm: norm}
}
sort.Slice(ranked, func(a, b int) bool {
if ranked[a].norm != ranked[b].norm {
return ranked[a].norm > ranked[b].norm
slices.SortFunc(ranked, func(a, b scored) int {
if a.norm != b.norm {
return cmp.Compare(b.norm, a.norm) // 降序
}
return snapshots[ranked[a].idx].CreatedAt.After(snapshots[ranked[b].idx].CreatedAt)
if snapshots[a.idx].CreatedAt.After(snapshots[b.idx].CreatedAt) {
return -1
} else if snapshots[a.idx].CreatedAt.Before(snapshots[b.idx].CreatedAt) {
return 1
}
return 0
})
}

View File

@@ -198,7 +198,7 @@ func NewLogService(
}
// GetLogCleanupServiceFromApp 从应用中获取日志清理服务
func GetLogCleanupServiceFromApp(app interface{}) LogCleanupService {
func GetLogCleanupServiceFromApp(app any) LogCleanupService {
// 这里需要根据实际的app结构来获取
// 暂时返回nil实际使用时需要实现
return nil

View File

@@ -66,8 +66,7 @@ type PostService interface {
IncrementViews(ctx context.Context, postID, userID string) error
// RecordShare 记录分享(仅已发布帖子计数 +1返回最新 shares_count
RecordShare(ctx context.Context, postID, userID string) (sharesCount int, err error)
}
}
// postServiceImpl 帖子服务实现
type postServiceImpl struct {
@@ -103,9 +102,9 @@ func (s *postServiceImpl) Create(ctx context.Context, userID, title, content str
post := &model.Post{
UserID: userID,
ChannelID: channelID,
Title: title,
Content: content,
Status: model.PostStatusPending,
Title: title,
Content: content,
Status: model.PostStatusPending,
}
err := s.postRepo.Create(post, images)
@@ -166,7 +165,7 @@ func (s *postServiceImpl) reviewPostAsync(postID, userID, title, content string,
Content: content,
Images: images,
AuthorID: authorID,
}, map[string]interface{}{
}, map[string]any{
"result": result,
})

View File

@@ -41,7 +41,7 @@ type PushService interface {
PushToUser(ctx context.Context, userID string, message *model.Message, priority int) error
// 系统消息推送
PushSystemMessage(ctx context.Context, userID string, msgType, title, content string, data map[string]interface{}) error
PushSystemMessage(ctx context.Context, userID string, msgType, title, content string, data map[string]any) error
// 系统通知推送(新接口,使用独立的 SystemNotification 模型)
PushSystemNotification(ctx context.Context, userID string, notification *model.SystemNotification) error
@@ -148,17 +148,17 @@ func (s *pushServiceImpl) pushViaWebSocket(ctx context.Context, userID string, m
// 从 segments 中提取文本内容
content := dto.ExtractTextContentFromModel(message.Segments)
notification := map[string]interface{}{
notification := map[string]any{
"id": fmt.Sprintf("%s", message.ID),
"type": string(message.SystemType),
"content": content,
"extra": map[string]interface{}{},
"extra": map[string]any{},
"created_at": message.CreatedAt.UnixMilli(),
}
// 填充额外数据
if message.ExtraData != nil {
extra := notification["extra"].(map[string]interface{})
extra := notification["extra"].(map[string]any)
extra["actor_id"] = message.ExtraData.ActorID
extra["actor_name"] = message.ExtraData.ActorName
extra["avatar_url"] = message.ExtraData.AvatarURL
@@ -167,7 +167,7 @@ func (s *pushServiceImpl) pushViaWebSocket(ctx context.Context, userID string, m
extra["action_url"] = message.ExtraData.ActionURL
extra["action_time"] = message.ExtraData.ActionTime
if message.ExtraData.ActorID > 0 {
notification["trigger_user"] = map[string]interface{}{
notification["trigger_user"] = map[string]any{
"id": fmt.Sprintf("%d", message.ExtraData.ActorID),
"username": message.ExtraData.ActorName,
"avatar": message.ExtraData.AvatarURL,
@@ -200,7 +200,7 @@ func (s *pushServiceImpl) pushViaWebSocket(ctx context.Context, userID string, m
SenderID: message.SenderID,
}
s.wsHub.PublishToUser(userID, "chat_message", map[string]interface{}{
s.wsHub.PublishToUser(userID, "chat_message", map[string]any{
"detail_type": detailType,
"message": event,
})
@@ -432,7 +432,7 @@ func (s *pushServiceImpl) doRetry() {
}
// PushSystemMessage 推送系统消息
func (s *pushServiceImpl) PushSystemMessage(ctx context.Context, userID string, msgType, title, content string, data map[string]interface{}) error {
func (s *pushServiceImpl) PushSystemMessage(ctx context.Context, userID string, msgType, title, content string, data map[string]any) error {
// 首先尝试WebSocket推送
if s.pushSystemViaWebSocket(ctx, userID, msgType, title, content, data) {
return nil
@@ -444,12 +444,12 @@ func (s *pushServiceImpl) PushSystemMessage(ctx context.Context, userID string,
}
// pushSystemViaWebSocket 通过WebSocket推送系统消息
func (s *pushServiceImpl) pushSystemViaWebSocket(ctx context.Context, userID string, msgType, title, content string, data map[string]interface{}) bool {
func (s *pushServiceImpl) pushSystemViaWebSocket(ctx context.Context, userID string, msgType, title, content string, data map[string]any) bool {
if s.wsHub == nil || !s.wsHub.HasClients(userID) {
return false
}
sysMsg := map[string]interface{}{
sysMsg := map[string]any{
"type": msgType,
"title": title,
"content": content,
@@ -477,18 +477,18 @@ func (s *pushServiceImpl) pushSystemNotificationViaWebSocket(ctx context.Context
return false
}
wsNotification := map[string]interface{}{
wsNotification := map[string]any{
"id": fmt.Sprintf("%d", notification.ID),
"type": string(notification.Type),
"title": notification.Title,
"content": notification.Content,
"extra": map[string]interface{}{},
"extra": map[string]any{},
"created_at": notification.CreatedAt.UnixMilli(),
}
// 填充额外数据
if notification.ExtraData != nil {
extra := wsNotification["extra"].(map[string]interface{})
extra := wsNotification["extra"].(map[string]any)
extra["actor_id_str"] = notification.ExtraData.ActorIDStr
extra["actor_name"] = notification.ExtraData.ActorName
extra["avatar_url"] = notification.ExtraData.AvatarURL
@@ -499,7 +499,7 @@ func (s *pushServiceImpl) pushSystemNotificationViaWebSocket(ctx context.Context
// 设置触发用户信息
if notification.ExtraData.ActorIDStr != "" {
wsNotification["trigger_user"] = map[string]interface{}{
wsNotification["trigger_user"] = map[string]any{
"id": notification.ExtraData.ActorIDStr,
"username": notification.ExtraData.ActorName,
"avatar": notification.ExtraData.AvatarURL,

View File

@@ -54,7 +54,7 @@ type QRCodeSession struct {
// QRCodeLoginService 二维码登录服务
type QRCodeLoginService struct {
redis *redis.Client
wsHub *ws.Hub
wsHub *ws.Hub
jwtService *JWTService
userService UserService
activityService UserActivityService
@@ -65,7 +65,7 @@ type QRCodeLoginService struct {
func NewQRCodeLoginService(redis *redis.Client, wsHub *ws.Hub, jwtService *JWTService, userService UserService, activityService UserActivityService, logService *LogService) *QRCodeLoginService {
return &QRCodeLoginService{
redis: redis,
wsHub: wsHub,
wsHub: wsHub,
jwtService: jwtService,
userService: userService,
activityService: activityService,
@@ -87,7 +87,7 @@ func (s *QRCodeLoginService) CreateSession(ctx context.Context) (*QRCodeSession,
}
key := qrcodeSessionPrefix + sessionID
data := map[string]interface{}{
data := map[string]any{
"status": string(session.Status),
"user_id": "",
"created_at": session.CreatedAt,
@@ -275,7 +275,7 @@ func (s *QRCodeLoginService) Cancel(ctx context.Context, sessionID, userID strin
}
// 推送取消事件
s.wsHub.PublishToUser(sessionID, "cancelled", map[string]interface{}{})
s.wsHub.PublishToUser(sessionID, "cancelled", map[string]any{})
return nil
}

View File

@@ -389,7 +389,7 @@ func (s *sensitiveServiceImpl) AddWord(ctx context.Context, word string, categor
// 同步到 Redis
if s.redis != nil && s.config.RedisKeyPrefix != "" {
key := fmt.Sprintf("%s:%s", s.config.RedisKeyPrefix, word)
data := map[string]interface{}{
data := map[string]any{
"word": word,
"category": category,
"level": level,
@@ -498,7 +498,7 @@ func (s *sensitiveServiceImpl) loadFromRedis(ctx context.Context) error {
continue
}
var wordData map[string]interface{}
var wordData map[string]any
if err := json.Unmarshal([]byte(data), &wordData); err != nil {
continue
}