huangguangrong 9 месяцев назад
Сommit
c9086c1c57

+ 37 - 0
deploy/sql/slow_wild.sql

@@ -0,0 +1,37 @@
+CREATE TABLE `p_user` (
+  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户ID',
+  `nickname` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '昵称',
+  `username` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户名',
+  `phone` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '手机号',
+  `password` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'MD5密码',
+  `salt` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '盐值',
+  `status` tinyint NOT NULL DEFAULT '0' COMMENT '用户状态0-正常1-封禁中2-禁言中3-删除',
+  `avatar` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户头像',
+	`sex` tinyint NOT NULL DEFAULT '0' COMMENT '性别 0 女、1 男',
+	`like_count` int(10) NOT NULL DEFAULT '0' COMMENT '收获的点赞数量',
+	`tweet_count` int(10) NOT NULL DEFAULT '0' COMMENT '帖子数量',
+	`collection_count` int(10) NOT NULL DEFAULT '0' comment '收获的收藏数量',
+	`follow_count` int(10) NOT NULL DEFAULT '0' comment '关注数量',
+	`fans_count` int(10) NOT NULL DEFAULT '0' COMMENT '粉丝数量',
+	`last_login_ip` varchar(32) NOT NULL DEFAULT '0' COMMENT '最后登录的ip',
+	`created_on` bigint NOT NULL DEFAULT '0' COMMENT '创建时间',
+  `modified_on` bigint NOT NULL DEFAULT '0' COMMENT '修改时间',
+  `deleted_on` bigint NOT NULL DEFAULT '0' COMMENT '删除时间',
+  `is_del` tinyint NOT NULL DEFAULT '0' COMMENT '是否删除 0 为未删除、1 为已删除',
+  PRIMARY KEY (`id`) USING BTREE,
+  UNIQUE KEY `idx_user_username` (`username`) USING BTREE,
+  KEY `idx_user_phone` (`phone`) USING BTREE
+) ENGINE=InnoDB AUTO_INCREMENT=100060 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='用户';
+
+
+CREATE TABLE `p_user_follow` (
+  `id` bigint NOT NULL AUTO_INCREMENT,
+  `user_id` bigint NOT NULL DEFAULT '0' COMMENT '用户id',
+	`to_user_id` bigint NOT NULL DEFAULT '0' COMMENT '被关注的用户id',
+	`created_on` bigint NOT NULL DEFAULT '0' COMMENT '创建时间',
+  `modified_on` bigint NOT NULL DEFAULT '0' COMMENT '修改时间',
+  `deleted_on` bigint NOT NULL DEFAULT '0' COMMENT '删除时间',
+  `is_del` tinyint NOT NULL DEFAULT '0' COMMENT '是否删除 0 为未删除、1 为已删除',
+  PRIMARY KEY (`id`) USING BTREE,
+	KEY `idx_user_follow` (`user_id`,`to_user_id`,`is_del`)
+) ENGINE=InnoDB AUTO_INCREMENT=100060 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='用户关注表';

+ 6 - 0
etc/slowwild.yaml

@@ -0,0 +1,6 @@
+Name: slowwild
+Host: 0.0.0.0
+Port: 8888
+
+DB:
+  DataSource: root:123456@tcp(121.11.99.220:13306)/slow_wild?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai

+ 98 - 0
go.mod

@@ -0,0 +1,98 @@
+module slowwild
+
+go 1.22.0
+
+require (
+	git.banshen.xyz/huangguangrong/slow_wild_protobuff v0.0.7
+	github.com/zeromicro/go-zero v1.8.0
+	google.golang.org/grpc v1.70.0
+)
+
+require (
+	filippo.io/edwards25519 v1.1.0 // indirect
+	github.com/beorn7/perks v1.0.1 // indirect
+	github.com/cenkalti/backoff/v4 v4.3.0 // indirect
+	github.com/cespare/xxhash/v2 v2.3.0 // indirect
+	github.com/coreos/go-semver v0.3.1 // indirect
+	github.com/coreos/go-systemd/v22 v22.5.0 // indirect
+	github.com/davecgh/go-spew v1.1.1 // indirect
+	github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
+	github.com/emicklei/go-restful/v3 v3.11.0 // indirect
+	github.com/fatih/color v1.18.0 // indirect
+	github.com/go-logr/logr v1.4.2 // indirect
+	github.com/go-logr/stdr v1.2.2 // indirect
+	github.com/go-openapi/jsonpointer v0.19.6 // indirect
+	github.com/go-openapi/jsonreference v0.20.2 // indirect
+	github.com/go-openapi/swag v0.22.4 // indirect
+	github.com/go-sql-driver/mysql v1.8.1 // indirect
+	github.com/gogo/protobuf v1.3.2 // indirect
+	github.com/golang-jwt/jwt/v4 v4.5.1
+	github.com/golang/mock v1.6.0 // indirect
+	github.com/golang/protobuf v1.5.4 // indirect
+	github.com/google/gnostic-models v0.6.8 // indirect
+	github.com/google/go-cmp v0.6.0 // indirect
+	github.com/google/gofuzz v1.2.0 // indirect
+	github.com/google/uuid v1.6.0 // indirect
+	github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
+	github.com/jinzhu/inflection v1.0.0 // indirect
+	github.com/jinzhu/now v1.1.5 // indirect
+	github.com/josharian/intern v1.0.0 // indirect
+	github.com/json-iterator/go v1.1.12 // indirect
+	github.com/klauspost/compress v1.17.9 // indirect
+	github.com/mailru/easyjson v0.7.7 // indirect
+	github.com/mattn/go-colorable v0.1.13 // indirect
+	github.com/mattn/go-isatty v0.0.20 // indirect
+	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+	github.com/modern-go/reflect2 v1.0.2 // indirect
+	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
+	github.com/openzipkin/zipkin-go v0.4.3 // indirect
+	github.com/pelletier/go-toml/v2 v2.2.2 // indirect
+	github.com/prometheus/client_golang v1.20.5 // indirect
+	github.com/prometheus/client_model v0.6.1 // indirect
+	github.com/prometheus/common v0.55.0 // indirect
+	github.com/prometheus/procfs v0.15.1 // indirect
+	github.com/redis/go-redis/v9 v9.7.0 // indirect
+	github.com/spaolacci/murmur3 v1.1.0 // indirect
+	go.etcd.io/etcd/api/v3 v3.5.15 // indirect
+	go.etcd.io/etcd/client/pkg/v3 v3.5.15 // indirect
+	go.etcd.io/etcd/client/v3 v3.5.15 // indirect
+	go.opentelemetry.io/otel v1.32.0 // indirect
+	go.opentelemetry.io/otel/exporters/jaeger v1.17.0 // indirect
+	go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 // indirect
+	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0 // indirect
+	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0 // indirect
+	go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.24.0 // indirect
+	go.opentelemetry.io/otel/exporters/zipkin v1.24.0 // indirect
+	go.opentelemetry.io/otel/metric v1.32.0 // indirect
+	go.opentelemetry.io/otel/sdk v1.32.0 // indirect
+	go.opentelemetry.io/otel/trace v1.32.0 // indirect
+	go.opentelemetry.io/proto/otlp v1.3.1 // indirect
+	go.uber.org/atomic v1.10.0 // indirect
+	go.uber.org/automaxprocs v1.6.0 // indirect
+	go.uber.org/multierr v1.9.0 // indirect
+	go.uber.org/zap v1.24.0 // indirect
+	golang.org/x/net v0.34.0 // indirect
+	golang.org/x/oauth2 v0.24.0 // indirect
+	golang.org/x/sys v0.29.0 // indirect
+	golang.org/x/term v0.28.0 // indirect
+	golang.org/x/text v0.22.0 // indirect
+	golang.org/x/time v0.9.0 // indirect
+	google.golang.org/genproto/googleapis/api v0.0.0-20241202173237-19429a94021a // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a // indirect
+	google.golang.org/protobuf v1.36.5 // indirect
+	gopkg.in/inf.v0 v0.9.1 // indirect
+	gopkg.in/yaml.v2 v2.4.0 // indirect
+	gopkg.in/yaml.v3 v3.0.1 // indirect
+	gorm.io/driver/mysql v1.5.7 // indirect
+	gorm.io/gorm v1.25.12 // indirect
+	gorm.io/plugin/soft_delete v1.2.1
+	k8s.io/api v0.29.3 // indirect
+	k8s.io/apimachinery v0.29.4 // indirect
+	k8s.io/client-go v0.29.3 // indirect
+	k8s.io/klog/v2 v2.110.1 // indirect
+	k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect
+	k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect
+	sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
+	sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
+	sigs.k8s.io/yaml v1.3.0 // indirect
+)

+ 313 - 0
go.sum

@@ -0,0 +1,313 @@
+filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
+filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
+git.banshen.xyz/huangguangrong/slow_wild_protobuff v0.0.3 h1:0p6MNT4bEznu3l6z4ttI8mksY5YXsRYwIDoimGqXX64=
+git.banshen.xyz/huangguangrong/slow_wild_protobuff v0.0.3/go.mod h1:v8AzHCelFBbIkoY+gR4WIEuc6mG5okJ12IXbjvGJWHk=
+git.banshen.xyz/huangguangrong/slow_wild_protobuff v0.0.4 h1:MTsNXs5bgKdPNTrNdzOpwcsquCqOMqQe/e670EprMdA=
+git.banshen.xyz/huangguangrong/slow_wild_protobuff v0.0.4/go.mod h1:v8AzHCelFBbIkoY+gR4WIEuc6mG5okJ12IXbjvGJWHk=
+git.banshen.xyz/huangguangrong/slow_wild_protobuff v0.0.5 h1:o81TFudYw217MmRxSSTmInHRSH8GHtv4eQP5a3K9Vvw=
+git.banshen.xyz/huangguangrong/slow_wild_protobuff v0.0.5/go.mod h1:v8AzHCelFBbIkoY+gR4WIEuc6mG5okJ12IXbjvGJWHk=
+git.banshen.xyz/huangguangrong/slow_wild_protobuff v0.0.6 h1:5dwVwAFgpj83f3xcR1c+vGg6QId2B3fUFUjIBclq9qU=
+git.banshen.xyz/huangguangrong/slow_wild_protobuff v0.0.6/go.mod h1:v8AzHCelFBbIkoY+gR4WIEuc6mG5okJ12IXbjvGJWHk=
+git.banshen.xyz/huangguangrong/slow_wild_protobuff v0.0.7 h1:FvDzmIojdaAYzMOOq880nmSd1ZVLRGHYVSTE91gX3+E=
+git.banshen.xyz/huangguangrong/slow_wild_protobuff v0.0.7/go.mod h1:v8AzHCelFBbIkoY+gR4WIEuc6mG5okJ12IXbjvGJWHk=
+github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 h1:uvdUDbHQHO85qeSydJtItA4T55Pw6BtAejd0APRJOCE=
+github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
+github.com/alicebob/miniredis/v2 v2.34.0 h1:mBFWMaJSNL9RwdGRyEDoAAv8OQc5UlEhLDQggTglU/0=
+github.com/alicebob/miniredis/v2 v2.34.0/go.mod h1:kWShP4b58T1CW0Y5dViCd5ztzrDqRWqM3nksiyXk5s8=
+github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
+github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
+github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
+github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
+github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
+github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
+github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
+github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
+github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
+github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
+github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
+github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
+github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
+github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
+github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
+github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
+github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
+github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
+github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
+github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
+github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
+github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
+github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
+github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
+github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
+github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
+github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
+github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
+github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU=
+github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
+github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
+github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
+github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
+github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
+github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
+github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
+github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
+github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo=
+github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
+github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
+github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
+github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
+github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
+github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
+github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
+github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
+github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec=
+github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+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=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k=
+github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
+github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
+github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
+github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
+github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
+github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
+github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
+github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
+github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
+github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
+github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
+github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
+github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
+github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
+github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
+github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
+github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
+github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
+github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
+github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
+github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
+github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
+github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mattn/go-sqlite3 v1.14.3/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
+github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
+github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4=
+github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o=
+github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg=
+github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
+github.com/openzipkin/zipkin-go v0.4.3 h1:9EGwpqkgnwdEIJ+Od7QVSEIH+ocmm5nPat0G7sjsSdg=
+github.com/openzipkin/zipkin-go v0.4.3/go.mod h1:M9wCJZFWCo2RiY+o1eBCEMe0Dp2S5LDHcMZmk3RmK7c=
+github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
+github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
+github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
+github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
+github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
+github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
+github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
+github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
+github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
+github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
+github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
+github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E=
+github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw=
+github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
+github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
+github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
+github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
+github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
+github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
+github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
+github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M=
+github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
+github.com/zeromicro/go-zero v1.8.0 h1:4g/8VW+fOyM51HZYPeI3mXIZdEX+Fl6SsdYX2H5PYw4=
+github.com/zeromicro/go-zero v1.8.0/go.mod h1:xDBF+/iDzj30zPvu6HNUIbpz1J6+/g3Sx9D/DytJfss=
+go.etcd.io/etcd/api/v3 v3.5.15 h1:3KpLJir1ZEBrYuV2v+Twaa/e2MdDCEZ/70H+lzEiwsk=
+go.etcd.io/etcd/api/v3 v3.5.15/go.mod h1:N9EhGzXq58WuMllgH9ZvnEr7SI9pS0k0+DHZezGp7jM=
+go.etcd.io/etcd/client/pkg/v3 v3.5.15 h1:fo0HpWz/KlHGMCC+YejpiCmyWDEuIpnTDzpJLB5fWlA=
+go.etcd.io/etcd/client/pkg/v3 v3.5.15/go.mod h1:mXDI4NAOwEiszrHCb0aqfAYNCrZP4e9hRca3d1YK8EU=
+go.etcd.io/etcd/client/v3 v3.5.15 h1:23M0eY4Fd/inNv1ZfU3AxrbbOdW79r9V9Rl62Nm6ip4=
+go.etcd.io/etcd/client/v3 v3.5.15/go.mod h1:CLSJxrYjvLtHsrPKsy7LmZEE+DK2ktfd2bN4RhBMwlU=
+go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U=
+go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg=
+go.opentelemetry.io/otel/exporters/jaeger v1.17.0 h1:D7UpUy2Xc2wsi1Ras6V40q806WM07rqoCWzXu7Sqy+4=
+go.opentelemetry.io/otel/exporters/jaeger v1.17.0/go.mod h1:nPCqOnEH9rNLKqH/+rrUjiMzHJdV1BlpKcTwRTyKkKI=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 h1:t6wl9SPayj+c7lEIFgm4ooDBZVb01IhLB4InpomhRw8=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0/go.mod h1:iSDOcsnSA5INXzZtwaBPrKp/lWu/V14Dd+llD0oI2EA=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0 h1:Mw5xcxMwlqoJd97vwPxA8isEaIoxsta9/Q51+TTJLGE=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0/go.mod h1:CQNu9bj7o7mC6U7+CA/schKEYakYXWr79ucDHTMGhCM=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0 h1:Xw8U6u2f8DK2XAkGRFV7BBLENgnTGX9i4rQRxJf+/vs=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0/go.mod h1:6KW1Fm6R/s6Z3PGXwSJN2K4eT6wQB3vXX6CVnYX9NmM=
+go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.24.0 h1:s0PHtIkN+3xrbDOpt2M8OTG92cWqUESvzh2MxiR5xY8=
+go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.24.0/go.mod h1:hZlFbDbRt++MMPCCfSJfmhkGIWnX1h3XjkfxZUjLrIA=
+go.opentelemetry.io/otel/exporters/zipkin v1.24.0 h1:3evrL5poBuh1KF51D9gO/S+N/1msnm4DaBqs/rpXUqY=
+go.opentelemetry.io/otel/exporters/zipkin v1.24.0/go.mod h1:0EHgD8R0+8yRhUYJOGR8Hfg2dpiJQxDOszd5smVO9wM=
+go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M=
+go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8=
+go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4=
+go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU=
+go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
+go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
+go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM=
+go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8=
+go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
+go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
+go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
+go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
+go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
+go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
+go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
+go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
+go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
+go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
+go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
+go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
+golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
+golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
+golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE=
+golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
+golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
+golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
+golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
+golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
+golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
+golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
+golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
+golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/genproto/googleapis/api v0.0.0-20241202173237-19429a94021a h1:OAiGFfOiA0v9MRYsSidp3ubZaBnteRUyn3xB2ZQ5G/E=
+google.golang.org/genproto/googleapis/api v0.0.0-20241202173237-19429a94021a/go.mod h1:jehYqy3+AhJU9ve55aNOaSml7wUXjF9x6z2LcCfpAhY=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a h1:hgh8P4EuoxpsuKMXX/To36nOFD7vixReXgn8lPGnt+o=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU=
+google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
+google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
+google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
+google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+gopkg.in/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY=
+gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0=
+gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
+gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo=
+gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM=
+gorm.io/driver/sqlite v1.1.3/go.mod h1:AKDgRWk8lcSQSw+9kxCJnX/yySj8G3rdwYlU57cB45c=
+gorm.io/gorm v1.20.1/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
+gorm.io/gorm v1.23.0/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
+gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
+gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
+gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
+gorm.io/plugin/soft_delete v1.2.1 h1:qx9D/c4Xu6w5KT8LviX8DgLcB9hkKl6JC9f44Tj7cGU=
+gorm.io/plugin/soft_delete v1.2.1/go.mod h1:Zv7vQctOJTGOsJ/bWgrN1n3od0GBAZgnLjEx+cApLGk=
+k8s.io/api v0.29.3 h1:2ORfZ7+bGC3YJqGpV0KSDDEVf8hdGQ6A03/50vj8pmw=
+k8s.io/api v0.29.3/go.mod h1:y2yg2NTyHUUkIoTC+phinTnEa3KFM6RZ3szxt014a80=
+k8s.io/apimachinery v0.29.4 h1:RaFdJiDmuKs/8cm1M6Dh1Kvyh59YQFDcFuFTSmXes6Q=
+k8s.io/apimachinery v0.29.4/go.mod h1:i3FJVwhvSp/6n8Fl4K97PJEP8C+MM+aoDq4+ZJBf70Y=
+k8s.io/client-go v0.29.3 h1:R/zaZbEAxqComZ9FHeQwOh3Y1ZUs7FaHKZdQtIc2WZg=
+k8s.io/client-go v0.29.3/go.mod h1:tkDisCvgPfiRpxGnOORfkljmS+UrW+WtXAy2fTvXJB0=
+k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0=
+k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo=
+k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780=
+k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA=
+k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A=
+k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
+sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
+sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
+sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
+sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
+sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
+sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=

+ 10 - 0
internal/config/config.go

@@ -0,0 +1,10 @@
+package config
+
+import "github.com/zeromicro/go-zero/zrpc"
+
+type Config struct {
+	zrpc.RpcServerConf
+	DB struct {
+		DataSource string
+	}
+}

+ 32 - 0
internal/errorx/errcode.go

@@ -0,0 +1,32 @@
+package errorx
+
+type CodeError struct {
+	Code    int    `json:"code"`
+	Message string `json:"message"`
+}
+
+func (e *CodeError) Error() string {
+	return e.Message
+}
+
+func NewCodeError(code int, msg string) error {
+	return &CodeError{
+		Code:    code,
+		Message: msg,
+	}
+}
+
+var (
+	// 用户相关错误码 (10000-10099)
+	ErrUserNotFound    = NewCodeError(10001, "用户不存在")
+	ErrInvalidParam    = NewCodeError(10002, "无效的参数")
+	ErrUserQueryFailed = NewCodeError(10003, "查询用户失败")
+	ErrInvalidCode     = NewCodeError(10004, "验证码错误")
+	ErrInvalidPassword = NewCodeError(10005, "密码错误")
+	ErrGenerateToken   = NewCodeError(10006, "生成token失败")
+	ErrPhoneRegistered = NewCodeError(10007, "手机号已注册")
+	ErrCreateUser      = NewCodeError(10008, "创建用户失败")
+	ErrUnFollowFailed  = NewCodeError(10009, "取消关注失败")
+	ErrAlreadyFollowed = NewCodeError(10010, "已经关注该用户")
+	ErrFollowFailed    = NewCodeError(10011, "关注用户失败")
+)

+ 78 - 0
internal/logic/finduserlogic.go

@@ -0,0 +1,78 @@
+package logic
+
+import (
+	"context"
+
+	"slowwild/internal/svc"
+
+	"slowwild/internal/model"
+
+	"slowwild/internal/errorx"
+
+	"git.banshen.xyz/huangguangrong/slow_wild_protobuff/slowwild/slowwildserver"
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type FindUserLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewFindUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *FindUserLogic {
+	return &FindUserLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+// 查询用户信息
+func (l *FindUserLogic) FindUser(in *slowwildserver.FindUserReq) (*slowwildserver.FindUserResp, error) {
+	// 参数校验
+	if len(in.Ids) == 0 && in.Phone == "" && in.Username == "" {
+		return nil, errorx.ErrInvalidParam
+	}
+
+	var users []*model.User
+	var err error
+
+	if len(in.Ids) > 0 {
+		users, err = l.svcCtx.UserModel.FindByIds(l.ctx, in.Ids)
+	} else if in.Phone != "" {
+		user, err := l.svcCtx.UserModel.FindOneByPhone(l.ctx, in.Phone)
+		if err == model.ErrNotFound {
+			return nil, errorx.ErrUserNotFound
+		}
+		if err == nil && user != nil {
+			users = append(users, user)
+		}
+	} else if in.Username != "" {
+		user, err := l.svcCtx.UserModel.FindOneByUsername(l.ctx, in.Username)
+		if err == model.ErrNotFound {
+			return nil, errorx.ErrUserNotFound
+		}
+		if err == nil && user != nil {
+			users = append(users, user)
+		}
+	}
+
+	if err != nil {
+		return nil, errorx.ErrUserQueryFailed
+	}
+
+	// 转换为响应对象
+	resp := &slowwildserver.FindUserResp{
+		UserList: make([]*slowwildserver.UserInfo, 0, len(users)),
+	}
+
+	for _, user := range users {
+		resp.UserList = append(resp.UserList, &slowwildserver.UserInfo{
+			Id:       user.ID,
+			Avatar:   user.Avatar,
+			Sex:      int32(user.Sex),
+			Nickname: user.Nickname,
+		})
+	}
+	return resp, nil
+}

+ 77 - 0
internal/logic/getfanslogic.go

@@ -0,0 +1,77 @@
+package logic
+
+import (
+	"context"
+
+	"slowwild/internal/svc"
+
+	"git.banshen.xyz/huangguangrong/slow_wild_protobuff/slowwild/slowwildserver"
+
+	"slowwild/internal/errorx"
+
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type GetFansLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewGetFansLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetFansLogic {
+	return &GetFansLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+// 获取我的粉丝
+func (l *GetFansLogic) GetFans(in *slowwildserver.GetFollowingReq) (*slowwildserver.GetFollowingRes, error) {
+	if in.UserId <= 0 || in.Page <= 0 || in.PageSize <= 0 {
+		return nil, errorx.ErrInvalidParam
+	}
+
+	// 获取粉丝关注记录
+	follows, err := l.svcCtx.UserFollowModel.GetFansList(l.ctx, in.QueryUserId, int(in.Page), int(in.PageSize))
+	if err != nil {
+		return nil, errorx.ErrUserQueryFailed
+	}
+
+	// 获取所有粉丝的用户ID
+	userIds := make([]int64, 0, len(follows))
+	for _, follow := range follows {
+		userIds = append(userIds, follow.UserId)
+	}
+
+	// 查询粉丝用户信息
+	users, err := l.svcCtx.UserModel.FindByIds(l.ctx, userIds)
+	if err != nil {
+		return nil, errorx.ErrUserQueryFailed
+	}
+
+	// 查询当前用户是否关注了这些粉丝(互关检查)
+	mutualFollows, err := l.svcCtx.UserFollowModel.CheckMutualFollows(l.ctx, in.UserId, userIds)
+	if err != nil {
+		return nil, errorx.ErrUserQueryFailed
+	}
+
+	// 构建响应
+	resp := &slowwildserver.GetFollowingRes{
+		List: make([]*slowwildserver.FollowUserInfo, 0, len(users)),
+	}
+
+	for _, user := range users {
+		resp.List = append(resp.List, &slowwildserver.FollowUserInfo{
+			User: &slowwildserver.UserInfo{
+				Id:       user.ID,
+				Nickname: user.Nickname,
+				Avatar:   user.Avatar,
+				Sex:      int32(user.Sex),
+			},
+			IsMutualFollow: mutualFollows[user.ID], // 设置是否互关
+		})
+	}
+
+	return resp, nil
+}

+ 77 - 0
internal/logic/getfollowslogic.go

@@ -0,0 +1,77 @@
+package logic
+
+import (
+	"context"
+
+	"slowwild/internal/svc"
+
+	"git.banshen.xyz/huangguangrong/slow_wild_protobuff/slowwild/slowwildserver"
+
+	"slowwild/internal/errorx"
+
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type GetFollowsLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewGetFollowsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetFollowsLogic {
+	return &GetFollowsLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+// 查询我的关注
+func (l *GetFollowsLogic) GetFollows(in *slowwildserver.GetFollowingReq) (*slowwildserver.GetFollowingRes, error) {
+	if in.UserId <= 0 || in.Page <= 0 || in.PageSize <= 0 {
+		return nil, errorx.ErrInvalidParam
+	}
+
+	// 获取关注记录
+	follows, err := l.svcCtx.UserFollowModel.GetFollowsList(l.ctx, in.QueryUserId, int(in.Page), int(in.PageSize))
+	if err != nil {
+		return nil, errorx.ErrUserQueryFailed
+	}
+
+	// 获取所有关注的用户ID
+	userIds := make([]int64, 0, len(follows))
+	for _, follow := range follows {
+		userIds = append(userIds, follow.ToUserId) // 注意这里使用 ToUserId
+	}
+
+	// 查询关注的用户信息
+	users, err := l.svcCtx.UserModel.FindByIds(l.ctx, userIds)
+	if err != nil {
+		return nil, errorx.ErrUserQueryFailed
+	}
+
+	// 查询这些用户是否也关注了当前用户(互关检查)
+	mutualFollows, err := l.svcCtx.UserFollowModel.CheckMutualFollows(l.ctx, in.UserId, userIds)
+	if err != nil {
+		return nil, errorx.ErrUserQueryFailed
+	}
+
+	// 构建响应
+	resp := &slowwildserver.GetFollowingRes{
+		List: make([]*slowwildserver.FollowUserInfo, 0, len(users)),
+	}
+
+	for _, user := range users {
+		resp.List = append(resp.List, &slowwildserver.FollowUserInfo{
+			User: &slowwildserver.UserInfo{
+				Id:       user.ID,
+				Nickname: user.Nickname,
+				Avatar:   user.Avatar,
+				Sex:      int32(user.Sex),
+			},
+			IsMutualFollow: mutualFollows[user.ID], // 设置是否互关
+		})
+	}
+
+	return resp, nil
+}

+ 57 - 0
internal/logic/getuserinfologic.go

@@ -0,0 +1,57 @@
+package logic
+
+import (
+	"context"
+
+	"slowwild/internal/svc"
+
+	"slowwild/internal/errorx"
+	"slowwild/internal/model"
+
+	"git.banshen.xyz/huangguangrong/slow_wild_protobuff/slowwild/slowwildserver"
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type GetUserInfoLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewGetUserInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUserInfoLogic {
+	return &GetUserInfoLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+// 获取用户信息
+func (l *GetUserInfoLogic) GetUserInfo(in *slowwildserver.GetUserInfoReq) (*slowwildserver.GetUserInfoResp, error) {
+	if in.UserId <= 0 || in.QueryUserId <= 0 {
+		return nil, errorx.ErrInvalidParam
+	}
+
+	// 查询用户基本信息
+	user, err := l.svcCtx.UserModel.FindOneById(l.ctx, in.QueryUserId)
+	if err == model.ErrNotFound {
+		return nil, errorx.ErrUserNotFound
+	}
+	if err != nil {
+		return nil, errorx.ErrUserQueryFailed
+	}
+
+	// 查询是否关注了该用户
+	mutualFollows, err := l.svcCtx.UserFollowModel.CheckMutualFollows(l.ctx, in.UserId, []int64{in.QueryUserId})
+	if err != nil {
+		return nil, errorx.ErrUserQueryFailed
+	}
+
+	return &slowwildserver.GetUserInfoResp{
+		Id:       user.ID,
+		Nickname: user.Nickname,
+		Avatar:   user.Avatar,
+		Sex:      int32(user.Sex),
+		IsFollow: mutualFollows[in.QueryUserId],
+	}, nil
+}

+ 59 - 0
internal/logic/getuserprofilelogic.go

@@ -0,0 +1,59 @@
+package logic
+
+import (
+	"context"
+
+	"slowwild/internal/svc"
+
+	"slowwild/internal/errorx"
+	"slowwild/internal/model"
+
+	"git.banshen.xyz/huangguangrong/slow_wild_protobuff/slowwild/slowwildserver"
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type GetUserProfileLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewGetUserProfileLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUserProfileLogic {
+	return &GetUserProfileLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+// 查询用户的详细信息
+func (l *GetUserProfileLogic) GetUserProfile(in *slowwildserver.GetUserProfileReq) (*slowwildserver.GetUserProfileRes, error) {
+	if in.UserId <= 0 {
+		return nil, errorx.ErrInvalidParam
+	}
+
+	// 查询用户详细信息
+	user, err := l.svcCtx.UserModel.FindOneById(l.ctx, in.UserId)
+	if err == model.ErrNotFound {
+		return nil, errorx.ErrUserNotFound
+	}
+	if err != nil {
+		return nil, errorx.ErrUserQueryFailed
+	}
+
+	return &slowwildserver.GetUserProfileRes{
+		Id:                 user.ID,
+		Username:           user.Username,
+		Phone:              user.Phone,
+		Nickname:           user.Nickname,
+		Avatar:             user.Avatar,
+		Sex:                int32(user.Sex),
+		Status:             user.Status,
+		GetLikesCount:      int32(user.LikeCount),
+		TweetCount:         int32(user.TweetCount),
+		GetCollectionCount: int32(user.CollectionCount),
+		Follows:            int64(user.FollowCount),
+		Fans:               int64(user.FansCount),
+		CreatedOn:          user.CreatedOn,
+	}, nil
+}

+ 89 - 0
internal/logic/loginlogic.go

@@ -0,0 +1,89 @@
+package logic
+
+import (
+	"context"
+
+	"slowwild/internal/svc"
+
+	"slowwild/internal/errorx"
+	"slowwild/internal/model"
+	"slowwild/internal/utils"
+
+	"git.banshen.xyz/huangguangrong/slow_wild_protobuff/slowwild/slowwildserver"
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type LoginLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LoginLogic {
+	return &LoginLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+// 用户登录
+func (l *LoginLogic) Login(in *slowwildserver.LoginReq) (*slowwildserver.LoginAndRegisterRsp, error) {
+	if in.Phone == "" {
+		return nil, errorx.ErrInvalidParam
+	}
+
+	var user *model.User
+	var err error
+
+	// 查询用户是否存在
+	user, err = l.svcCtx.UserModel.FindOneByPhone(l.ctx, in.Phone)
+	if err == model.ErrNotFound {
+		return nil, errorx.ErrUserNotFound
+	}
+	if err != nil {
+		return nil, errorx.ErrUserQueryFailed
+	}
+
+	// 根据登录方式验证
+	if in.LoginType == 1 { // 验证码登录
+		if in.Code == "" {
+			return nil, errorx.ErrInvalidParam
+		}
+		// TODO: 验证验证码
+		// if !verifyCode(in.Phone, in.Code) {
+		// 	return nil, errorx.NewCodeError(10004, "验证码错误")
+		// }
+	} else if in.LoginType == 2 { // 密码登录
+		if in.Password == "" {
+			return nil, errorx.ErrInvalidParam
+		}
+		// 验证密码
+		if !verifyPassword(user.Password, user.Salt, in.Password) {
+			return nil, errorx.NewCodeError(10005, "密码错误")
+		}
+	} else {
+		return nil, errorx.ErrInvalidParam
+	}
+
+	// 生成token
+	token, err := utils.GenerateToken(user.ID)
+	if err != nil {
+		return nil, errorx.NewCodeError(10006, "生成token失败")
+	}
+
+	// 更新最后登录IP
+	err = l.svcCtx.UserModel.UpdateLoginIP(l.ctx, user.ID, in.LoginIp)
+	if err != nil {
+		l.Logger.Errorf("更新登录IP失败: %v", err)
+	}
+
+	return &slowwildserver.LoginAndRegisterRsp{
+		Token: token,
+	}, nil
+}
+
+// 验证密码
+func verifyPassword(dbPassword, salt, inputPassword string) bool {
+	return dbPassword == utils.EncryptPassword(inputPassword, salt)
+}

+ 82 - 0
internal/logic/registerlogic.go

@@ -0,0 +1,82 @@
+package logic
+
+import (
+	"context"
+	"time"
+
+	"slowwild/internal/errorx"
+	"slowwild/internal/model"
+	"slowwild/internal/svc"
+	"slowwild/internal/utils"
+
+	"git.banshen.xyz/huangguangrong/slow_wild_protobuff/slowwild/slowwildserver"
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type RegisterLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewRegisterLogic(ctx context.Context, svcCtx *svc.ServiceContext) *RegisterLogic {
+	return &RegisterLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+// 用户注册
+func (l *RegisterLogic) Register(in *slowwildserver.RegisterReq) (*slowwildserver.LoginAndRegisterRsp, error) {
+	// 参数校验
+	if in.Phone == "" || in.Password == "" || in.Code == "" {
+		return nil, errorx.ErrInvalidParam
+	}
+
+	// 检查手机号是否已注册
+	_, err := l.svcCtx.UserModel.FindOneByPhone(l.ctx, in.Phone)
+	if err != nil && err != model.ErrNotFound {
+		return nil, errorx.ErrUserQueryFailed
+	}
+	if err == nil {
+		return nil, errorx.NewCodeError(10007, "手机号已注册")
+	}
+
+	// TODO: 验证验证码
+	// if !verifyCode(in.Phone, in.Code) {
+	// 	return nil, errorx.ErrInvalidCode
+	// }
+
+	// 生成盐值
+	salt := utils.GenerateSalt()
+	// 加密密码
+	encryptedPassword := utils.EncryptPassword(in.Password, salt)
+
+	// 创建用户
+	user := &model.User{
+		Phone:      in.Phone,
+		Password:   encryptedPassword,
+		Salt:       salt,
+		Nickname:   in.Nickname,
+		Sex:        int(in.Sex),
+		Status:     1, // 正常状态
+		CreateTime: time.Now(),
+		UpdateTime: time.Now(),
+	}
+
+	err = l.svcCtx.UserModel.Create(l.ctx, user)
+	if err != nil {
+		return nil, errorx.NewCodeError(10008, "创建用户失败")
+	}
+
+	// 生成token
+	token, err := utils.GenerateToken(user.ID)
+	if err != nil {
+		return nil, errorx.ErrGenerateToken
+	}
+
+	return &slowwildserver.LoginAndRegisterRsp{
+		Token: token,
+	}, nil
+}

+ 68 - 0
internal/logic/searchusernamelogic.go

@@ -0,0 +1,68 @@
+package logic
+
+import (
+	"context"
+
+	"slowwild/internal/errorx"
+	"slowwild/internal/svc"
+
+	"git.banshen.xyz/huangguangrong/slow_wild_protobuff/slowwild/slowwildserver"
+
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type SearchUsernameLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewSearchUsernameLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SearchUsernameLogic {
+	return &SearchUsernameLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+// 搜索用户名称
+func (l *SearchUsernameLogic) SearchUsername(in *slowwildserver.SearchUsernameReq) (*slowwildserver.SearchUsernameRes, error) {
+	if in.Keyword == "" || in.Page <= 0 || in.PageSize <= 0 || in.UserId <= 0 {
+		return nil, errorx.ErrInvalidParam
+	}
+
+	// 搜索用户
+	users, err := l.svcCtx.UserModel.SearchByNickname(l.ctx, in.Keyword, int(in.Page), int(in.PageSize))
+	if err != nil {
+		return nil, errorx.ErrUserQueryFailed
+	}
+
+	// 获取所有搜索结果的用户ID
+	userIds := make([]int64, 0, len(users))
+	for _, user := range users {
+		userIds = append(userIds, user.ID)
+	}
+
+	// 查询当前用户是否关注了这些用户
+	followMap, err := l.svcCtx.UserFollowModel.CheckMutualFollows(l.ctx, in.UserId, userIds)
+	if err != nil {
+		return nil, errorx.ErrUserQueryFailed
+	}
+
+	// 构建响应
+	resp := &slowwildserver.SearchUsernameRes{
+		List: make([]*slowwildserver.SearchUsernameItem, 0, len(users)),
+	}
+
+	for _, user := range users {
+		resp.List = append(resp.List, &slowwildserver.SearchUsernameItem{
+			Id:       user.ID,
+			Nickname: user.Nickname,
+			Avatar:   user.Avatar,
+			Sex:      int32(user.Sex),
+			IsFollow: followMap[user.ID], // 添加关注状态
+		})
+	}
+
+	return resp, nil
+}

+ 66 - 0
internal/logic/unuserfollowlogic.go

@@ -0,0 +1,66 @@
+package logic
+
+import (
+	"context"
+
+	"slowwild/internal/errorx"
+	"slowwild/internal/model"
+	"slowwild/internal/svc"
+
+	"git.banshen.xyz/huangguangrong/slow_wild_protobuff/slowwild/slowwildserver"
+	"github.com/zeromicro/go-zero/core/logx"
+	"gorm.io/gorm"
+)
+
+type UnUserFollowLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewUnUserFollowLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UnUserFollowLogic {
+	return &UnUserFollowLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+// 取消关注用户
+func (l *UnUserFollowLogic) UnUserFollow(in *slowwildserver.UnFollowUserReq) (*slowwildserver.UnFollowUserRes, error) {
+	if in.UserId <= 0 || in.UnFollowUserId <= 0 {
+		return nil, errorx.ErrInvalidParam
+	}
+
+	err := l.svcCtx.UserModel.Transaction(l.ctx, func(tx *gorm.DB) error {
+		userModel := model.NewUserModel(tx)
+		userFollowModel := model.NewUserFollowModel(tx)
+
+		// 取消关注
+		err := userFollowModel.UnFollow(l.ctx, in.UserId, in.UnFollowUserId)
+		if err != nil {
+			return err
+		}
+
+		// 减少当前用户的关注数
+		err = userModel.DecrementFollowCount(l.ctx, in.UserId)
+		if err != nil {
+			return err
+		}
+
+		// 减少目标用户的粉丝数
+		err = userModel.DecrementFansCount(l.ctx, in.UnFollowUserId)
+		if err != nil {
+			return err
+		}
+
+		return nil
+	})
+
+	if err != nil {
+		l.Logger.Errorf("取消关注失败: %v", err)
+		return nil, errorx.NewCodeError(10009, "取消关注失败")
+	}
+
+	return &slowwildserver.UnFollowUserRes{}, nil
+}

+ 77 - 0
internal/logic/userfollowlogic.go

@@ -0,0 +1,77 @@
+package logic
+
+import (
+	"context"
+
+	"slowwild/internal/svc"
+
+	"git.banshen.xyz/huangguangrong/slow_wild_protobuff/slowwild/slowwildserver"
+	"github.com/zeromicro/go-zero/core/logx"
+	"gorm.io/gorm"
+
+	"slowwild/internal/errorx"
+	"slowwild/internal/model"
+)
+
+type UserFollowLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewUserFollowLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserFollowLogic {
+	return &UserFollowLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+// 关注用户
+func (l *UserFollowLogic) UserFollow(in *slowwildserver.FollowUserReq) (*slowwildserver.FollowUserRes, error) {
+	if in.UserId <= 0 || in.FollowUserId <= 0 {
+		return nil, errorx.ErrInvalidParam
+	}
+
+	// 检查是否已经关注
+	followMap, err := l.svcCtx.UserFollowModel.CheckMutualFollows(l.ctx, in.UserId, []int64{in.FollowUserId})
+	if err != nil {
+		return nil, errorx.ErrUserQueryFailed
+	}
+	if followMap[in.FollowUserId] {
+		return nil, errorx.NewCodeError(10010, "已经关注该用户")
+	}
+
+	// 开启事务
+	err = l.svcCtx.UserModel.Transaction(l.ctx, func(tx *gorm.DB) error {
+		userModel := model.NewUserModel(tx)
+		userFollowModel := model.NewUserFollowModel(tx)
+
+		// 创建关注关系
+		err := userFollowModel.Follow(l.ctx, in.UserId, in.FollowUserId)
+		if err != nil {
+			return err
+		}
+
+		// 增加当前用户的关注数
+		err = userModel.IncrementFollowCount(l.ctx, in.UserId)
+		if err != nil {
+			return err
+		}
+
+		// 增加目标用户的粉丝数
+		err = userModel.IncrementFansCount(l.ctx, in.FollowUserId)
+		if err != nil {
+			return err
+		}
+
+		return nil
+	})
+
+	if err != nil {
+		l.Logger.Errorf("关注用户失败: %v", err)
+		return nil, errorx.NewCodeError(10011, "关注用户失败")
+	}
+
+	return &slowwildserver.FollowUserRes{}, nil
+}

+ 33 - 0
internal/model/base_model.go

@@ -0,0 +1,33 @@
+package model
+
+import (
+	"time"
+
+	"gorm.io/gorm"
+	"gorm.io/plugin/soft_delete"
+)
+
+// Model 公共Model
+type Model struct {
+	ID         int64                 `gorm:"primary_key" json:"id"`
+	CreatedOn  int64                 `json:"created_on"`
+	ModifiedOn int64                 `json:"modified_on"`
+	DeletedOn  int64                 `json:"deleted_on"`
+	IsDel      soft_delete.DeletedAt `gorm:"softDelete:flag" json:"is_del"`
+}
+
+func (m *Model) BeforeCreate(tx *gorm.DB) (err error) {
+	nowTime := time.Now().Unix()
+
+	tx.Statement.SetColumn("created_on", nowTime)
+	tx.Statement.SetColumn("modified_on", nowTime)
+	return
+}
+
+func (m *Model) BeforeUpdate(tx *gorm.DB) (err error) {
+	if !tx.Statement.Changed("modified_on") {
+		tx.Statement.SetColumn("modified_on", time.Now().Unix())
+	}
+
+	return
+}

+ 106 - 0
internal/model/user_follow.go

@@ -0,0 +1,106 @@
+package model
+
+import (
+	"context"
+
+	"gorm.io/gorm"
+)
+
+type UserFollow struct {
+	*Model
+	UserId   int64 `gorm:"column:user_id;default:0;NOT NULL"`    // 用户id
+	ToUserId int64 `gorm:"column:to_user_id;default:0;NOT NULL"` // 被关注的用户id
+}
+
+func (m *UserFollow) TableName() string {
+	return "p_user_follow"
+}
+
+type UserFollowModel struct {
+	conn *gorm.DB
+}
+
+func NewUserFollowModel(conn *gorm.DB) UserFollowModel {
+	return UserFollowModel{
+		conn: conn,
+	}
+}
+
+// GetFansList 获取用户的粉丝列表
+func (m *UserFollowModel) GetFansList(ctx context.Context, userId int64, page, pageSize int) ([]*UserFollow, error) {
+	var follows []*UserFollow
+	offset := (page - 1) * pageSize
+
+	err := m.conn.WithContext(ctx).
+		Where("to_user_id = ? AND is_del = 0", userId).
+		Offset(offset).
+		Limit(pageSize).
+		Order("created_on desc").
+		Find(&follows).Error
+
+	if err != nil {
+		return nil, err
+	}
+	return follows, nil
+}
+
+// GetFollowsList 获取用户的关注列表
+func (m *UserFollowModel) GetFollowsList(ctx context.Context, userId int64, page, pageSize int) ([]*UserFollow, error) {
+	var follows []*UserFollow
+	offset := (page - 1) * pageSize
+
+	err := m.conn.WithContext(ctx).
+		Where("user_id = ? AND is_del = 0", userId).
+		Offset(offset).
+		Limit(pageSize).
+		Order("created_on desc").
+		Find(&follows).Error
+
+	if err != nil {
+		return nil, err
+	}
+	return follows, nil
+}
+
+// CheckMutualFollows 批量检查用户之间的关注关系
+func (m *UserFollowModel) CheckMutualFollows(ctx context.Context, userId int64, targetUserIds []int64) (map[int64]bool, error) {
+	var follows []*UserFollow
+	result := make(map[int64]bool)
+
+	// 初始化结果map
+	for _, targetId := range targetUserIds {
+		result[targetId] = false
+	}
+
+	// 查询当前用户是否关注了目标用户们
+	err := m.conn.WithContext(ctx).
+		Where("user_id = ? AND to_user_id IN ? AND is_del = 0", userId, targetUserIds).
+		Find(&follows).Error
+
+	if err != nil {
+		return nil, err
+	}
+
+	// 标记已关注的用户
+	for _, follow := range follows {
+		result[follow.ToUserId] = true
+	}
+
+	return result, nil
+}
+
+// UnFollow 取消关注
+func (m *UserFollowModel) UnFollow(ctx context.Context, userId, toUserId int64) error {
+	return m.conn.WithContext(ctx).
+		Model(&UserFollow{}).
+		Where("user_id = ? AND to_user_id = ? AND is_del = 0", userId, toUserId).
+		Update("is_del", 1).Error
+}
+
+// Follow 关注用户
+func (m *UserFollowModel) Follow(ctx context.Context, userId, toUserId int64) error {
+	return m.conn.WithContext(ctx).Create(&UserFollow{
+		UserId:   userId,
+		ToUserId: toUserId,
+	}).Error
+}

+ 163 - 0
internal/model/user_model.go

@@ -0,0 +1,163 @@
+package model
+
+import (
+	"context"
+	"time"
+
+	"gorm.io/gorm"
+)
+
+// User 用户模型
+type User struct {
+	*Model
+	Username        string    `gorm:"column:username;type:varchar(32);not null"`
+	Phone           string    `gorm:"column:phone;type:varchar(11);not null"`
+	Password        string    `gorm:"column:password;type:varchar(128);not null"`
+	Avatar          string    `gorm:"column:avatar;type:varchar(255)"`
+	Gender          int32     `gorm:"column:gender;type:tinyint(1);default:0"` // 0:未知 1:男 2:女
+	Status          int32     `gorm:"column:status;type:tinyint(1);default:1"` // 1:正常 2:禁用
+	LastLoginIp     string    `gorm:"column:last_login_ip;type:varchar(15)"`
+	CreateTime      time.Time `gorm:"column:create_time;autoCreateTime"`
+	UpdateTime      time.Time `gorm:"column:update_time;autoUpdateTime"`
+	Nickname        string    `gorm:"column:nickname;NOT NULL"`                   // 昵称
+	Salt            string    `gorm:"column:salt;NOT NULL"`                       // 盐值
+	Sex             int       `gorm:"column:sex;default:0;NOT NULL"`              // 性别 0 女、1 男
+	LikeCount       int       `gorm:"column:like_count;default:0;NOT NULL"`       // 收获的点赞数量
+	TweetCount      int       `gorm:"column:tweet_count;default:0;NOT NULL"`      // 帖子数量
+	CollectionCount int       `gorm:"column:collection_count;default:0;NOT NULL"` // 收获的收藏数量
+	FollowCount     int       `gorm:"column:follow_count;default:0;NOT NULL"`     // 关注数量
+	FansCount       int       `gorm:"column:fans_count;default:0;NOT NULL"`       // 粉丝数量
+}
+
+// TableName 表名
+func (User) TableName() string {
+	return "user"
+}
+
+// FindByIds 通过ID列表批量查询用户
+func (m *UserModel) FindByIds(ctx context.Context, ids []int64) ([]*User, error) {
+	var users []*User
+	err := m.conn.WithContext(ctx).Where("id IN ?", ids).Find(&users).Error
+	if err != nil {
+		return nil, err
+	}
+	return users, nil
+}
+
+// FindOneByPhone 通过手机号查询用户
+func (m *UserModel) FindOneByPhone(ctx context.Context, phone string) (*User, error) {
+	var user User
+	err := m.conn.WithContext(ctx).Where("phone = ?", phone).First(&user).Error
+	if err != nil {
+		if err == gorm.ErrRecordNotFound {
+			return nil, ErrNotFound
+		}
+		return nil, err
+	}
+	return &user, nil
+}
+
+// FindOneByUsername 通过用户名查询用户
+func (m *UserModel) FindOneByUsername(ctx context.Context, username string) (*User, error) {
+	var user User
+	err := m.conn.WithContext(ctx).Where("username = ?", username).First(&user).Error
+	if err != nil {
+		if err == gorm.ErrRecordNotFound {
+			return nil, ErrNotFound
+		}
+		return nil, err
+	}
+	return &user, nil
+}
+
+// FindOneById 通过ID查询用户
+func (m *UserModel) FindOneById(ctx context.Context, id int64) (*User, error) {
+	var user User
+	err := m.conn.WithContext(ctx).Where("id = ?", id).First(&user).Error
+	if err != nil {
+		if err == gorm.ErrRecordNotFound {
+			return nil, ErrNotFound
+		}
+		return nil, err
+	}
+	return &user, nil
+}
+
+// UpdateLoginIP 更新用户最后登录IP
+func (m *UserModel) UpdateLoginIP(ctx context.Context, userId int64, ip string) error {
+	return m.conn.WithContext(ctx).Model(&User{}).
+		Where("id = ?", userId).
+		Update("last_login_ip", ip).Error
+}
+
+// Create 创建用户
+func (m *UserModel) Create(ctx context.Context, user *User) error {
+	return m.conn.WithContext(ctx).Create(user).Error
+}
+
+// SearchByNickname 通过昵称搜索用户
+func (m *UserModel) SearchByNickname(ctx context.Context, nickname string, page, pageSize int) ([]*User, error) {
+	var users []*User
+	offset := (page - 1) * pageSize
+
+	err := m.conn.WithContext(ctx).
+		Where("nickname LIKE ? AND status = 1", "%"+nickname+"%").
+		Offset(offset).
+		Limit(pageSize).
+		Order("id desc").
+		Find(&users).Error
+
+	if err != nil {
+		return nil, err
+	}
+	return users, nil
+}
+
+// DecrementFansCount 减少粉丝数
+func (m *UserModel) DecrementFansCount(ctx context.Context, userId int64) error {
+	return m.conn.WithContext(ctx).
+		Model(&User{}).
+		Where("id = ?", userId).
+		UpdateColumn("fans_count", gorm.Expr("fans_count - ?", 1)).Error
+}
+
+// DecrementFollowCount 减少关注数
+func (m *UserModel) DecrementFollowCount(ctx context.Context, userId int64) error {
+	return m.conn.WithContext(ctx).
+		Model(&User{}).
+		Where("id = ?", userId).
+		UpdateColumn("follow_count", gorm.Expr("follow_count - ?", 1)).Error
+}
+
+// IncrementFansCount 增加粉丝数
+func (m *UserModel) IncrementFansCount(ctx context.Context, userId int64) error {
+	return m.conn.WithContext(ctx).
+		Model(&User{}).
+		Where("id = ?", userId).
+		UpdateColumn("fans_count", gorm.Expr("fans_count + ?", 1)).Error
+}
+
+// IncrementFollowCount 增加关注数
+func (m *UserModel) IncrementFollowCount(ctx context.Context, userId int64) error {
+	return m.conn.WithContext(ctx).
+		Model(&User{}).
+		Where("id = ?", userId).
+		UpdateColumn("follow_count", gorm.Expr("follow_count + ?", 1)).Error
+}
+
+// Transaction 执行数据库事务
+func (m *UserModel) Transaction(ctx context.Context, fn func(tx *gorm.DB) error) error {
+	return m.conn.WithContext(ctx).Transaction(fn)
+}
+
+type UserModel struct {
+	conn *gorm.DB
+}
+
+var ErrNotFound = gorm.ErrRecordNotFound
+
+func NewUserModel(conn *gorm.DB) UserModel {
+	return UserModel{
+		conn: conn,
+	}
+}

+ 82 - 0
internal/server/slowwildserverserver.go

@@ -0,0 +1,82 @@
+// Code generated by goctl. DO NOT EDIT.
+// Source: slowwild.proto
+
+package server
+
+import (
+	"context"
+	"git.banshen.xyz/huangguangrong/slow_wild_protobuff/slowwild/slowwildserver"
+	"slowwild/internal/logic"
+	"slowwild/internal/svc"
+)
+
+type SlowWildServerServer struct {
+	svcCtx *svc.ServiceContext
+	slowwildserver.UnimplementedSlowWildServerServer
+}
+
+func NewSlowWildServerServer(svcCtx *svc.ServiceContext) *SlowWildServerServer {
+	return &SlowWildServerServer{
+		svcCtx: svcCtx,
+	}
+}
+
+// 用户登录
+func (s *SlowWildServerServer) Login(ctx context.Context, in *slowwildserver.LoginReq) (*slowwildserver.LoginAndRegisterRsp, error) {
+	l := logic.NewLoginLogic(ctx, s.svcCtx)
+	return l.Login(in)
+}
+
+// 用户注册
+func (s *SlowWildServerServer) Register(ctx context.Context, in *slowwildserver.RegisterReq) (*slowwildserver.LoginAndRegisterRsp, error) {
+	l := logic.NewRegisterLogic(ctx, s.svcCtx)
+	return l.Register(in)
+}
+
+// 获取用户信息
+func (s *SlowWildServerServer) GetUserInfo(ctx context.Context, in *slowwildserver.GetUserInfoReq) (*slowwildserver.GetUserInfoResp, error) {
+	l := logic.NewGetUserInfoLogic(ctx, s.svcCtx)
+	return l.GetUserInfo(in)
+}
+
+// 查询用户信息
+func (s *SlowWildServerServer) FindUser(ctx context.Context, in *slowwildserver.FindUserReq) (*slowwildserver.FindUserResp, error) {
+	l := logic.NewFindUserLogic(ctx, s.svcCtx)
+	return l.FindUser(in)
+}
+
+// 关注用户
+func (s *SlowWildServerServer) UserFollow(ctx context.Context, in *slowwildserver.FollowUserReq) (*slowwildserver.FollowUserRes, error) {
+	l := logic.NewUserFollowLogic(ctx, s.svcCtx)
+	return l.UserFollow(in)
+}
+
+// 取消关注用户
+func (s *SlowWildServerServer) UnUserFollow(ctx context.Context, in *slowwildserver.UnFollowUserReq) (*slowwildserver.UnFollowUserRes, error) {
+	l := logic.NewUnUserFollowLogic(ctx, s.svcCtx)
+	return l.UnUserFollow(in)
+}
+
+// 获取我得粉丝
+func (s *SlowWildServerServer) GetFans(ctx context.Context, in *slowwildserver.GetFollowingReq) (*slowwildserver.GetFollowingRes, error) {
+	l := logic.NewGetFansLogic(ctx, s.svcCtx)
+	return l.GetFans(in)
+}
+
+// 查询我的关注
+func (s *SlowWildServerServer) GetFollows(ctx context.Context, in *slowwildserver.GetFollowingReq) (*slowwildserver.GetFollowingRes, error) {
+	l := logic.NewGetFollowsLogic(ctx, s.svcCtx)
+	return l.GetFollows(in)
+}
+
+// 查询用户的详细信息
+func (s *SlowWildServerServer) GetUserProfile(ctx context.Context, in *slowwildserver.GetUserProfileReq) (*slowwildserver.GetUserProfileRes, error) {
+	l := logic.NewGetUserProfileLogic(ctx, s.svcCtx)
+	return l.GetUserProfile(in)
+}
+
+// 搜索用户名称
+func (s *SlowWildServerServer) SearchUsername(ctx context.Context, in *slowwildserver.SearchUsernameReq) (*slowwildserver.SearchUsernameRes, error) {
+	l := logic.NewSearchUsernameLogic(ctx, s.svcCtx)
+	return l.SearchUsername(in)
+}

+ 28 - 0
internal/svc/servicecontext.go

@@ -0,0 +1,28 @@
+package svc
+
+import (
+	"slowwild/internal/config"
+	"slowwild/internal/model"
+
+	"gorm.io/driver/mysql"
+	"gorm.io/gorm"
+)
+
+type ServiceContext struct {
+	Config          config.Config
+	UserModel       model.UserModel
+	UserFollowModel model.UserFollowModel
+}
+
+func NewServiceContext(c config.Config) *ServiceContext {
+	db, err := gorm.Open(mysql.Open(c.DB.DataSource), &gorm.Config{})
+	if err != nil {
+		panic(err)
+	}
+
+	return &ServiceContext{
+		Config:          c,
+		UserModel:       model.NewUserModel(db),
+		UserFollowModel: model.NewUserFollowModel(db),
+	}
+}

+ 24 - 0
internal/utils/encrypt.go

@@ -0,0 +1,24 @@
+package utils
+
+import (
+	"crypto/md5"
+	"encoding/hex"
+	"math/rand"
+	"time"
+)
+
+// EncryptPassword 密码加密
+func EncryptPassword(password, salt string) string {
+	h := md5.New()
+	h.Write([]byte(password + salt))
+	return hex.EncodeToString(h.Sum(nil))
+}
+
+// GenerateSalt 生成随机盐值
+func GenerateSalt() string {
+	bytes := make([]byte, 16)
+	if _, err := rand.Read(bytes); err != nil {
+		return time.Now().Format("20060102150405")
+	}
+	return hex.EncodeToString(bytes)
+}

+ 30 - 0
internal/utils/jwt.go

@@ -0,0 +1,30 @@
+package utils
+
+import (
+	"time"
+
+	"github.com/golang-jwt/jwt/v4"
+)
+
+type Claims struct {
+	UserId int64
+	jwt.RegisteredClaims
+}
+
+const TokenExpireDuration = time.Hour * 24 * 7 // token有效期7天
+
+var Secret = []byte("your-secret-key") // 实际使用时应该从配置文件读取
+
+// GenerateToken 生成JWT
+func GenerateToken(userId int64) (string, error) {
+	claims := Claims{
+		UserId: userId,
+		RegisteredClaims: jwt.RegisteredClaims{
+			ExpiresAt: jwt.NewNumericDate(time.Now().Add(TokenExpireDuration)),
+			IssuedAt:  jwt.NewNumericDate(time.Now()),
+			NotBefore: jwt.NewNumericDate(time.Now()),
+		},
+	}
+	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
+	return token.SignedString(Secret)
+}

+ 39 - 0
slowwild.go

@@ -0,0 +1,39 @@
+package main
+
+import (
+	"flag"
+	"fmt"
+
+	"slowwild/internal/config"
+	"slowwild/internal/server"
+	"slowwild/internal/svc"
+
+	"git.banshen.xyz/huangguangrong/slow_wild_protobuff/slowwild/slowwildserver"
+	"github.com/zeromicro/go-zero/core/conf"
+	"github.com/zeromicro/go-zero/core/service"
+	"github.com/zeromicro/go-zero/zrpc"
+	"google.golang.org/grpc"
+	"google.golang.org/grpc/reflection"
+)
+
+var configFile = flag.String("f", "etc/slowwild.yaml", "the config file")
+
+func main() {
+	flag.Parse()
+
+	var c config.Config
+	conf.MustLoad(*configFile, &c)
+	ctx := svc.NewServiceContext(c)
+
+	s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) {
+		slowwildserver.RegisterSlowWildServerServer(grpcServer, server.NewSlowWildServerServer(ctx))
+
+		if c.Mode == service.DevMode || c.Mode == service.TestMode {
+			reflection.Register(grpcServer)
+		}
+	})
+	defer s.Stop()
+
+	fmt.Printf("Starting rpc server at %s...\n", c.ListenOn)
+	s.Start()
+}

+ 128 - 0
slowwildserverclient/slowwildserver.go

@@ -0,0 +1,128 @@
+// Code generated by goctl. DO NOT EDIT.
+// Source: slowwild.proto
+
+package slowwildserverclient
+
+import (
+	"context"
+	"github.com/zeromicro/go-zero/zrpc"
+	"google.golang.org/grpc"
+	"git.banshen.xyz/huangguangrong/slow_wild_protobuff/slowwild/slowwildserver"
+)
+
+type (
+	FindUserReq         = slowwildserver.FindUserReq
+	FindUserResp        = slowwildserver.FindUserResp
+	FollowUserReq       = slowwildserver.FollowUserReq
+	FollowUserRes       = slowwildserver.FollowUserRes
+	GetFollowReq        = slowwildserver.GetFollowReq
+	GetFollowRes        = slowwildserver.GetFollowRes
+	GetFollowingReq     = slowwildserver.GetFollowingReq
+	GetFollowingRes     = slowwildserver.GetFollowingRes
+	GetUserInfoReq      = slowwildserver.GetUserInfoReq
+	GetUserInfoResp     = slowwildserver.GetUserInfoResp
+	GetUserProfileReq   = slowwildserver.GetUserProfileReq
+	GetUserProfileRes   = slowwildserver.GetUserProfileRes
+	LoginAndRegisterRsp = slowwildserver.LoginAndRegisterRsp
+	LoginReq            = slowwildserver.LoginReq
+	RegisterReq         = slowwildserver.RegisterReq
+	SearchUsernameItem  = slowwildserver.SearchUsernameItem
+	SearchUsernameReq   = slowwildserver.SearchUsernameReq
+	SearchUsernameRes   = slowwildserver.SearchUsernameRes
+	UnFollowUserReq     = slowwildserver.UnFollowUserReq
+	UnFollowUserRes     = slowwildserver.UnFollowUserRes
+	UserEntity          = slowwildserver.UserEntity
+
+	SlowWildServer interface {
+		// 用户登录
+		Login(ctx context.Context, in *LoginReq, opts ...grpc.CallOption) (*LoginAndRegisterRsp, error)
+		// 用户注册
+		Register(ctx context.Context, in *RegisterReq, opts ...grpc.CallOption) (*LoginAndRegisterRsp, error)
+		// 获取用户信息
+		GetUserInfo(ctx context.Context, in *GetUserInfoReq, opts ...grpc.CallOption) (*GetUserInfoResp, error)
+		// 查询用户信息
+		FindUser(ctx context.Context, in *FindUserReq, opts ...grpc.CallOption) (*FindUserResp, error)
+		// 关注用户
+		UserFollow(ctx context.Context, in *FollowUserReq, opts ...grpc.CallOption) (*FollowUserRes, error)
+		// 取消关注用户
+		UnUserFollow(ctx context.Context, in *UnFollowUserReq, opts ...grpc.CallOption) (*UnFollowUserRes, error)
+		// 获取我得粉丝
+		GetFans(ctx context.Context, in *GetFollowingReq, opts ...grpc.CallOption) (*GetFollowingRes, error)
+		// 查询我的关注
+		GetFollows(ctx context.Context, in *GetFollowingReq, opts ...grpc.CallOption) (*GetFollowingRes, error)
+		// 查询用户的详细信息
+		GetUserProfile(ctx context.Context, in *GetUserProfileReq, opts ...grpc.CallOption) (*GetUserProfileRes, error)
+		// 搜索用户名称
+		SearchUsername(ctx context.Context, in *SearchUsernameReq, opts ...grpc.CallOption) (*SearchUsernameRes, error)
+	}
+
+	defaultSlowWildServer struct {
+		cli zrpc.Client
+	}
+)
+
+func NewSlowWildServer(cli zrpc.Client) SlowWildServer {
+	return &defaultSlowWildServer{
+		cli: cli,
+	}
+}
+
+// 用户登录
+func (m *defaultSlowWildServer) Login(ctx context.Context, in *LoginReq, opts ...grpc.CallOption) (*LoginAndRegisterRsp, error) {
+	client := slowwildserver.NewSlowWildServerClient(m.cli.Conn())
+	return client.Login(ctx, in, opts...)
+}
+
+// 用户注册
+func (m *defaultSlowWildServer) Register(ctx context.Context, in *RegisterReq, opts ...grpc.CallOption) (*LoginAndRegisterRsp, error) {
+	client := slowwildserver.NewSlowWildServerClient(m.cli.Conn())
+	return client.Register(ctx, in, opts...)
+}
+
+// 获取用户信息
+func (m *defaultSlowWildServer) GetUserInfo(ctx context.Context, in *GetUserInfoReq, opts ...grpc.CallOption) (*GetUserInfoResp, error) {
+	client := slowwildserver.NewSlowWildServerClient(m.cli.Conn())
+	return client.GetUserInfo(ctx, in, opts...)
+}
+
+// 查询用户信息
+func (m *defaultSlowWildServer) FindUser(ctx context.Context, in *FindUserReq, opts ...grpc.CallOption) (*FindUserResp, error) {
+	client := slowwildserver.NewSlowWildServerClient(m.cli.Conn())
+	return client.FindUser(ctx, in, opts...)
+}
+
+// 关注用户
+func (m *defaultSlowWildServer) UserFollow(ctx context.Context, in *FollowUserReq, opts ...grpc.CallOption) (*FollowUserRes, error) {
+	client := slowwildserver.NewSlowWildServerClient(m.cli.Conn())
+	return client.UserFollow(ctx, in, opts...)
+}
+
+// 取消关注用户
+func (m *defaultSlowWildServer) UnUserFollow(ctx context.Context, in *UnFollowUserReq, opts ...grpc.CallOption) (*UnFollowUserRes, error) {
+	client := slowwildserver.NewSlowWildServerClient(m.cli.Conn())
+	return client.UnUserFollow(ctx, in, opts...)
+}
+
+// 获取我得粉丝
+func (m *defaultSlowWildServer) GetFans(ctx context.Context, in *GetFollowingReq, opts ...grpc.CallOption) (*GetFollowingRes, error) {
+	client := slowwildserver.NewSlowWildServerClient(m.cli.Conn())
+	return client.GetFans(ctx, in, opts...)
+}
+
+// 查询我的关注
+func (m *defaultSlowWildServer) GetFollows(ctx context.Context, in *GetFollowingReq, opts ...grpc.CallOption) (*GetFollowingRes, error) {
+	client := slowwildserver.NewSlowWildServerClient(m.cli.Conn())
+	return client.GetFollows(ctx, in, opts...)
+}
+
+// 查询用户的详细信息
+func (m *defaultSlowWildServer) GetUserProfile(ctx context.Context, in *GetUserProfileReq, opts ...grpc.CallOption) (*GetUserProfileRes, error) {
+	client := slowwildserver.NewSlowWildServerClient(m.cli.Conn())
+	return client.GetUserProfile(ctx, in, opts...)
+}
+
+// 搜索用户名称
+func (m *defaultSlowWildServer) SearchUsername(ctx context.Context, in *SearchUsernameReq, opts ...grpc.CallOption) (*SearchUsernameRes, error) {
+	client := slowwildserver.NewSlowWildServerClient(m.cli.Conn())
+	return client.SearchUsername(ctx, in, opts...)
+}